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 のサブモードは

  1. ある期間(M, RNG)内で、バランスよく N(N, DAT) 回 Highとなる
    • サイトによっては balanced mode と記載あり
    • 仕様書にはPWM algorithm
  2. ある期間(M, RNG)内で、最初に N(N, DAT) 回 Highとなる
    • サイトによっては legacy mode と記載あり
    • 仕様書には M/S transmission

の2つあります。 正式な名称は不明で、完全な仕様書を読むと記載があるのかもしれません。

実際の波形

上記のモードだけを切り替えた波形を参考に添付します。 他の設定値は同じです。

balanced mode

f:id:katogiso-tech:20171112170346p:plain

legacy mode

f:id:katogiso-tech:20171112170350p:plain

サンプルコードではクロックのソースを 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 
}

物理アドレスのアクセス用マッピング

過去記事を参照してください。

katogiso-tech.hatenablog.com

使っているレジスタ

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とする回数を設定できます。

全容

RaspberryPi pwm sample