Raspberry pi で AP のレジスタをC言語で直接変更して PWM を操作する
やること
Raspberry Pi 用の特別なライブラリとドライバを利用せずにPWMをC言語で操作する。
注意点
Linux のシステムコールを含む(mmap関数)ので、root 権限が必要なため sudo コマンドを利用する必要があります。 プログラム名を sample.c とするならば、
$ gcc sample.c $ sudo ./a.out
のように実行してください。
main()
IC 18pin (RaspberryPi の端子だと 12pin) をALT5モードにして、PWMを発生させる。 PWM のサブモードは
- ある期間(M, RNG)内で、バランスよく N(N, DAT) 回 Highとなる
- サイトによっては balanced mode と記載あり
- 仕様書にはPWM algorithm
- ある期間(M, RNG)内で、最初に N(N, DAT) 回 Highとなる
- サイトによっては legacy mode と記載あり
- 仕様書には M/S transmission
の2つあります。 正式な名称は不明で、完全な仕様書を読むと記載があるのかもしれません。
実際の波形
上記のモードだけを切り替えた波形を参考に添付します。 他の設定値は同じです。
balanced mode
legacy mode
サンプルコードではクロックのソースを Oscillator(19.2MHz)にして これをもとに 192 分周して100kHzにしています。 また、PWM algorithm( balanced mode ) です。
void main() { //------------------------------------ // set GPIO18 mode to ALT5(PWM0) //------------------------------------ gpio = io_mapping(GPIO_BASE); *(gpio + 0x4/4) = (*(gpio + 0x4/4) & ~(0x7 << 24 )) | (0b010 << 24); //------------------------------------ // set source clock //------------------------------------ clk = io_mapping(CLK_BASE); clk += 0xA0/4; // Oscillator 19.2MHz *clk = CLK_PASSWD + (0x1<<5); *(clk+0x4/4) = CLK_PASSWD + (192 << 12); *clk = CLK_PASSWD + 0x1 + (0x1 << 4); //------------------------------------ // set pwm //------------------------------------ pwm = io_mapping(PWM_BASE); *pwm = 0; // Disable usleep(10); // wait for stable *(pwm + 0x10/4) = 4; // set RNG *(pwm + 0x14/4) = 2; // set DAT *pwm = 0x1; // Enable }
物理アドレスのアクセス用マッピング
過去記事を参照してください。
使っているレジスタ
Clock 関連
ADDRESS | Name | Description | Size | Read/Write |
---|---|---|---|---|
0x3F10_1000 | CM_PWMCTL | PWM Clock Control | 32 | R/W |
0x3F10_1004 | CM_PWMDIV | PWM Clock Divisors | 32 | R/W |
CM_PWMCTL
名前は類推で、記載を見つけられず WiringPi ソースコードから該当アドレスを確認しました。 ただし、CM_GPnCTL レジスタの記述があり、レジスタの操作は仕様書が参考になります。
Clock 系のレジスタへの書き込みはbit[31:24]に0x5a を同時に書き込む必要があります。
使っている bit field は
SRC bit[3:0]
- 0 : GND
- 1 : oscillator
- 4 : PLL
- 他にも色々ある
ENAB bit[4]
- 1 : Start
- 0 : Stop
KILL bit[5]
- 1 : stop and reset
- 0 : no action
です。
CM_PWMDIV
名前は類推で、記載を見つけられず WiringPi ソースコードから該当アドレスを確認しました。 ただし、CM_GPnDIV レジスタの記述があり、レジスタの操作は仕様書が参考になります。
クロックの分周を決めるレジスタです。
使っている bit field は
- DIVI bit[23:12]
- Integer part of divisor
PWM 設定関連
ADDRESS | Name | Description | Size | Read/Write |
---|---|---|---|---|
0x3F20_C000 | CTL | PWM Channel1 Range | 32 | R/W |
0x3F20_C010 | RNG1 | PWM Channel1 Range | 32 | R/W |
0x3F20_C014 | DAT1 | PWM Channel1 Data | 32 | R/W |
CTL
仕様書からの抜粋です。 初期値はどれも 0x0 で、 bit0(Enable) と bit7(PWMのモード切り替え) を使えばほぼ大丈夫なはず。
bit# | Name | Description | Read/Write |
---|---|---|---|
7 | MSEN1 |
Channel 1 M/S Enable 0: PWM algorithm is used 1: M/S transmission is used. |
RW |
4 | POLA1 |
Channel 1 Polarity 0 : 0=low 1=high 1: 1=low 0=high |
RW |
1 | MODE1 |
Channel 1 Mode 0: PWM mode 1: Serialiser mode |
RW |
0 | PWEN1 |
Channel 1 Enable 0: Channel is disabled 1: Channel is enabled RW 0x0 |
RW |
RNGn
PWM の期間をこのレジスタ値で分割します。 32 未満の値で設定してください。
DATn
RNGn で設定した期間のうちHiとする回数を設定できます。