macOSからPi Picoを使う - その4 [Pi Pico]
うまくいけば仕事と趣味とで20台近くあるRaspberry Piのうち半分はPi Picoに置き換えられそうな気がしてきた。僕にとってはモデルとしてはPi PicoとPi 4Bの両端があればいい、ということになりそう。あとはそれぞれが順にパフォーマンスを上げてくれればありがたい....
16ビットカウンタはTOPというレジスタにある値までクロックをカウントしていく。TOPの値になると0に戻ってカウントを繰り返す。CCレジスタというのがあって、カウンタの値とCCレジスタの値を比較してCCの方が大きいとき1を出力してカウンタ値のほうが大きいと0を出力することでPWMを作る。CCレジスタは32ビットで下位と上位がそれぞれA出力とB出力のCCの値になっている。CCはデフォルトで0が入っているのでデフォルトでの出力は定数0になる。
CCの値を変えるとデューティが変わるけど、そのときに位相がずれるのがまずい(パルス幅に関わらず立ち上がり位置が同じになるので、基本波の位相はデューティを変えるとずれる)場合にはカウンタをアップダウンにできる。ただしその場合周期は倍になる。またTOPやCCレジスタはダブルバッファになっていて周期がひと段落するまで切り替わらないようになっている。
分周器はADCと同じように1以下の値(n/16の形の有理数)も設定できるので、クロックに対して中途半端な周期を平均的にではあるけど作ることができる。またB出力はクロックの代わりにも使えるので、長い周期のPWMも出力できるようになっている。
スライスごとにIRQ出力を持っているので、割り込みがかけられる。DMAも起動できる。
複数のスライスをカウンタ初期値を決めて同時にスタートすることができて、そうすることで複数のスライスの位相を制御できる。また「オンザフライ(On-the-fly)」、つまり走らせている途中で位相を調整することもできる。
データシートから読めるのはこんなところか。たかがPWMというかもしれないけど、案外ちょっとした細かい配慮がいっぱい詰め込まれていることがわかってそういうところは僕は嬉しくなる。
まずピンをPWMに使うことを指定する。
を使って、ピンpigoをGPIO_FUNC_PWMに設定する。この関数はpwmモジュールのものではない。
GPIOのピンは特定のPWMスライスと結びついているので
ピンがどのスライスのABどちらの出力に対応しているかを調べる。対応表はデータシートにあるのでわかっていれば必要ない。
PWMの基本設定は
で、wrapというのはTOPレジスタの値、levelというのはCCレジスタの値である。出力2つをいっぺんに設定したりgpioピン番号から設定したりというのもある。
これだけあればとりあえず
とすると信号が出る。このままだとシステムクロックのままなので分周器の設定
整数部と小数点以下で指定するか浮動小数点で指定するかが選べる。小数点以下は1/16の倍数を指定する(16以上だと設定そのものが無視される)。浮動小数点では1/16の倍数に丸められる。
もちろん小数点以下がある場合、基本波より低い周波数成分も現れる。この場合、基本波の1/16の周波数以下の周波数成分が現れるかどうか(パターンがランダムかどうか)ってPWMの後ろにアナログ回路をくっつけるときに問題になること(音として出力するような場合)があるかもしれない。どうなってるかはソースを見ないとわからないので、それが問題になる人は確認してほしい。
いくつかのモードがあったけど、これで指定する。
pwm_set_phase_correct()はノコギリ波にするか三角波にするかの選択で、pwm_set_output_polarity()ではtrueだと出力の論理を反転させる。出力ピンのあとにトランジスタのバッファを追加したときpolarity関数を呼ぶだけですむのは小さな親切。
位相の調整は
get_counter()はカウンタの瞬時値を読んで、set_counter()は値をセットする。advance_count()とretard_count()は1だけ進めたり遅らせたりする。
いちいち関数を呼ぶのが面倒な場合は設定用の構造体
があってそれに対して
なんかで構造体を作って、
で一気に設定する。startはfalseだと設定するだけで、信号は出ない。
そのほかに、割り込み処理用に
なんかがある。
7 PWM
Pi PicoにはA/DはあるけどD/Aはない。A/DがENOB=9.5なのでそれと同程度でいいとすればPWMのローパスでいいじゃん、ということなんだろう。それに応えようとしているのか、本家より高いベースクロックが設定されている。7.1 PWMのハードウェア
データシートの中でスライス(slice)と呼んでいるPWMブロックが8つあって、それぞれが2つの出力を持っている。データシートの中にあるひとつのスライスのブロックダイアグラムがこれ カウンタは16ビットで、アップカウントとアップダウンができる、つまりノコギリ状と三角波とができる、レジスタがダブルバッファになっていてデューティなどを変えたときにグリッチがでない、などとデータシートには書いてある。16ビットカウンタはTOPというレジスタにある値までクロックをカウントしていく。TOPの値になると0に戻ってカウントを繰り返す。CCレジスタというのがあって、カウンタの値とCCレジスタの値を比較してCCの方が大きいとき1を出力してカウンタ値のほうが大きいと0を出力することでPWMを作る。CCレジスタは32ビットで下位と上位がそれぞれA出力とB出力のCCの値になっている。CCはデフォルトで0が入っているのでデフォルトでの出力は定数0になる。
CCの値を変えるとデューティが変わるけど、そのときに位相がずれるのがまずい(パルス幅に関わらず立ち上がり位置が同じになるので、基本波の位相はデューティを変えるとずれる)場合にはカウンタをアップダウンにできる。ただしその場合周期は倍になる。またTOPやCCレジスタはダブルバッファになっていて周期がひと段落するまで切り替わらないようになっている。
分周器はADCと同じように1以下の値(n/16の形の有理数)も設定できるので、クロックに対して中途半端な周期を平均的にではあるけど作ることができる。またB出力はクロックの代わりにも使えるので、長い周期のPWMも出力できるようになっている。
スライスごとにIRQ出力を持っているので、割り込みがかけられる。DMAも起動できる。
複数のスライスをカウンタ初期値を決めて同時にスタートすることができて、そうすることで複数のスライスの位相を制御できる。また「オンザフライ(On-the-fly)」、つまり走らせている途中で位相を調整することもできる。
データシートから読めるのはこんなところか。たかがPWMというかもしれないけど、案外ちょっとした細かい配慮がいっぱい詰め込まれていることがわかってそういうところは僕は嬉しくなる。
7.2 headrware_pwm
sdkにPWMのためのheadrware_pwmモジュールがある。ヘッダは#include "hardware/pwm.h"
enum gpio_function { GPIO_FUNC_XIP = 0, GPIO_FUNC_SPI = 1, GPIO_FUNC_UART = 2, GPIO_FUNC_I2C = 3, GPIO_FUNC_PWM = 4, GPIO_FUNC_SIO = 5, GPIO_FUNC_PIO0 = 6, GPIO_FUNC_PIO1 = 7, GPIO_FUNC_GPCK = 8, GPIO_FUNC_USB = 9, GPIO_FUNC_NULL = 0xf }; void gpio_set_function(uint gpio, enum gpio_function fn);
GPIOのピンは特定のPWMスライスと結びついているので
static inline uint pwm_gpio_to_slice_num(uint gpio); enum pwm_chan { PWM_CHAN_A = 0, PWM_CHAN_B = 1 }; static inline uint pwm_gpio_to_channel(uint gpio);
PWMの基本設定は
static inline void pwm_set_wrap(uint slice_num, uint16_t wrap); static inline void pwm_set_chan_level(uint slice_num, uint chan, uint16_t level); static inline void pwm_set_both_levels(uint slice_num, uint16_t level_a, uint16_t level_b); static inline void pwm_set_gpio_level(uint gpio, uint16_t level);
これだけあればとりあえず
static inline void pwm_set_enabled(uint slice_num, bool enabled);
static inline void pwm_set_clkdiv_int_frac(uint slice_num, uint8_t integer, uint8_t fract); static inline void pwm_set_clkdiv(uint slice_num, float divider);
もちろん小数点以下がある場合、基本波より低い周波数成分も現れる。この場合、基本波の1/16の周波数以下の周波数成分が現れるかどうか(パターンがランダムかどうか)ってPWMの後ろにアナログ回路をくっつけるときに問題になること(音として出力するような場合)があるかもしれない。どうなってるかはソースを見ないとわからないので、それが問題になる人は確認してほしい。
いくつかのモードがあったけど、これで指定する。
enum pwm_clkdiv_mode{ PWM_DIV_FREE_RUNNING = 0, ///< Free-running counting at rate dictated by fractional divider PWM_DIV_B_HIGH = 1, ///< Fractional divider is gated by the PWM B pin PWM_DIV_B_RISING = 2, ///< Fractional divider advances with each rising edge of the PWM B pin PWM_DIV_B_FALLING = 3 ///< Fractional divider advances with each falling edge of the PWM B pin }; static inline void pwm_set_clkdiv_mode(uint slice_num, enum pwm_clkdiv_mode mode); static inline void pwm_set_phase_correct(uint slice_num, bool phase_correct); static inline void pwm_set_output_polarity(uint slice_num, bool a, bool b);
位相の調整は
static inline uint16_t pwm_get_counter(uint slice_num); static inline void pwm_set_counter(uint slice_num, uint16_t c); static inline void pwm_advance_count(uint slice_num); static inline void pwm_retard_count(uint slice_num);
いちいち関数を呼ぶのが面倒な場合は設定用の構造体
typedef struct { uint32_t csr; uint32_t div; uint32_t top; } pwm_config;
static inline pwm_config pwm_get_default_config(void); tatic inline void pwm_config_set_phase_correct(pwm_config *c, bool phase_correct); static inline void pwm_config_set_clkdiv(pwm_config *c, float div); static inline void pwm_config_set_clkdiv_int(pwm_config *c, uint div); static inline void pwm_config_set_clkdiv_mode(pwm_config *c, enum pwm_clkdiv_mode mode); static inline void pwm_config_set_output_polarity(pwm_config *c, bool a, bool b); static inline void pwm_config_set_wrap(pwm_config *c, uint16_t wrap);
static inline void pwm_init(uint slice_num, pwm_config *c, bool start);
そのほかに、割り込み処理用に
static inline void pwm_set_irq_enabled(uint slice_num, bool enabled); static inline void pwm_set_irq_mask_enabled(uint32_t slice_mask, bool enabled); static inline void pwm_clear_irq(uint slice_num); static inline uint32_t pwm_get_irq_status_mask(void); static inline void pwm_force_irq(uint slice_num);
2021-05-23 21:40
nice!(0)
コメント(0)
コメント 0