stop_patternのアイコン画像
stop_pattern 2024年01月29日作成
セットアップや使用方法 セットアップや使用方法 閲覧数 216
stop_pattern 2024年01月29日作成 セットアップや使用方法 セットアップや使用方法 閲覧数 216

SpresenseのPWM周りを触った話

SpresenseのPWM周りを触った話

何がしたいか

SpresenseからPWMを出力して適当な抵抗でVI変換することで電流計を自由に動かしたかったです。
Spresenseの拡張ボードを通した時の話です。
備忘録程度、大した情報はないです。

普通にやってみる

Spresenseから出力したPWMをそのまま電流計につないでみました。

PWMの解像度の謎

電流計の指針を細かに制御できると気持ちがいいので、PWMの解像度はなるべく高いほうがいいです。
しかしながらArduinoを通してanalogWrite()とかすると最大値が255で解像度が8Bitなので気持ちよくなれません。
調べてみるとドキュメントにこんなこと[1]が書いてありました。

Spresenseでは拡張ボードにPWM出力に使用できる端子が4系統用意されています。
インターフェース電圧は5Vまたは3.3Vになります。
周波数は最大で6.5MHz、分解能は15ビットとなります。

なるほど、15Bitは気持ちよくなれそう。
でもどう実装すればいいのかわからないのでソースや偉大な先人の残した記録を読みます。
ちなみに手元の環境ではピンが先人の残した記録と違うようでした、なぜ?

nuttx/timers/pwm.h

struct pwm_info_s { uint32_t frequency; /* Frequency of the pulse train */ #ifdef CONFIG_PWM_MULTICHAN /* Per-channel output state */ struct pwm_chan_s channels[CONFIG_PWM_NCHANNELS]; #else ub16_t duty; /* Duty of the pulse train, "1"-to-"0" duration. * Maximum: 65535/65536 (0x0000ffff) * Minimum: 1/65536 (0x00000001) */ # ifdef CONFIG_PWM_PULSECOUNT uint32_t count; /* The number of pulse to generate. 0 means to * generate an indefinite number of pulses */ # endif #endif /* CONFIG_PWM_MULTICHAN */ };

ドキュメントには15Bitの解像度で出力可能と書いてありますが、実際にコードを読むと65535までみたいなことが書いてあるので16Bitかもしれません。
Arduino用にラップされた部分のソースを読むと、analogWrite()はその引数を255で割って65535をかけて出力に設定していました。

cores/spresense/wiring_analog.c

... static void pwm_write(uint8_t pin, uint32_t pulse_width, uint32_t freq) { int slot = pwm_pin2slot(pin); if (slot < 0) { return; } if (pulse_width==0){ if (s_pwm_timers[slot].running){ pwm_stop(pin); } return; } if (s_pwm_timers[slot].running && s_pwm_timers[slot].pulse_width == pulse_width) { return; } pwm_prepare_timer(pin); pwm_set_timer_info(pin, pulse_width, freq); pwm_start(pin); } void analog_stop(uint8_t pin) { if (pin == PIN_PWM_0 || pin == PIN_PWM_1 || pin == PIN_PWM_2 || pin == PIN_PWM_3) { pwm_stop(pin); } else { sim_stop(pin); } } void analog_write(uint8_t pin, uint32_t pulse_width, uint32_t freq) { if (pin == PIN_PWM_0 || pin == PIN_PWM_1 || pin == PIN_PWM_2 || pin == PIN_PWM_3) { pwm_write(pin, pulse_width, freq); } else { sim_write(pin, pulse_width, freq); } } ... void analogWriteFreq(uint8_t pin, int value, uint32_t freq) { value = value < 0 ? 0 : value > 255 ? 255 : value; analog_write(pin, GET_ON_DURATION(value, freq), freq); } void analogWrite(uint8_t pin, int value) { analogWriteFreq(pin, value, default_pwm_freq); }

で、気持ちよくなるにはどうすればいいのか。
次の手順で操作を行うとよいようです。

  1. 周波数とデューティーを設定したpwm_info_sの変数を用意する
  2. /dev/pwm?ioctlを通して1の内容を書き込む
  3. ioctlを通してPWMIOC_STARTを書き込む

するとうまくいったようでいい感じの波形が出たようでが電流計がいい感じに動いてました。(波形自体は確認していません)
しかし、リセット後の初期状態などではHiが出力され、Lo出力時にも数mA流れてしまうことがわかりました。
何かがおかしい...

拡張ボードのレベル変換回路

ドキュメントを読むとこんなこと[2]が書いてありました。

Spresenseでは拡張ボードのデジタルピン入出力に下記の構造のようなレベルシフタを使用して、自動的方向の切り換えと電圧変換を行っています。
このレベルシフタはピンソケット側が1kΩで5Vまたは3.3Vに常にプルアップされています。
したがってピンソケットに接続する回路が低いインピーダンスを持つ回路である場合には、CPUの検出する閾値が変動してしまう事から正しく論理を検出できなくなります。

そりゃプルアップしてるんだから無信号時はHiが出て、Loの時もI=V/Rで少し電流流れますよね...

ではメインボードから直接ならどうか

直接ならいけるだろと思ったら、メインボードのピンは一部のみでPWMのピンは4ピンとも出ていませんでした。
この方法でやるならドキュメントにある通り片方向のバッファを用意してやる必要がありそうです。
そんなものは買わないとありません、おわり。

参考文献など

サムネ画像: https://developer.sony.com/spresense/development-guides/images/overview_hardware_both.jpg


  1. 公式ドキュメント ↩︎

  2. 公式ドキュメント ↩︎

stop_patternのアイコン画像
twitter:@stop_pattern
ログインしてコメントを投稿する