2014年08月19日

Raspberry PiをEWARM使ってBareMetalで動かす(13)

Raspberry PiのPWMをBareMetalで動かす

組み込みマイコンの中には、タイマーと組み合わせてPWMを出力するタイプのものもありますが、BCM2835のPWMはシンプルで、動作クロックに基づいて、設定されたPWM波形を出力していくだけです。

ここでは、Raspberry Piの心臓部であるBCM2835のPWMの設定方法を調べ、Raspberry Piを使って実際に波形を出して確認してみます。

以降では、1.PWMを動かすための手順を説明し、その中で使用するレジスタの説明を行います。
そして、2.PWMの波形設定例をいくつか示します。
さらに、3.実験で、それらの設定をC言語でプログラミングしてEmbedded Workbenchを用いてコンパイル&実行し、
実際にどのような波形が出るかを調べました。
Embedded Workbenchのプロジェクト一式を最後に添付しています。

1.PWMを動かす手順
PWMの説明は、BCM2835の仕様書[1]のp138〜に記述があります。

PWMを動かすには、
(1)PWMの出力ピンを設定する
(2)PWMの動作クロックを設定する
(3)PWMのモードを設定する
(4)PWM出力する波形を設定する
の手順となります。

以降、順番に説明します。

(1)PWMの出力ピンを設定する
PWM出力ピンはGPIOピンを兼用しています。
どのGPIOピンをどの(GPIOとは別の)機能として使うかは、CPFSEL0〜5のレジスタで機能選択することで指定できます。
PWM出力兼用ピンの割り当て状況は、BCM2835の仕様書[1]のp102〜を見ると次の通りです。

            ALT0
GPIO12 PWM0
GPIO13 PWM1

            ALT5
GPIO18 PWM0
GPIO19 PWM1

            ALT0
GPIO40 PWM0
GPIO41 PWM1
GPIO45 PWM1

ここではRaspberry Piのピンヘッダーに出ているGPIO18を使ってPWM0を出力させることにします。
GPIO18に対応する機能設定はGPFSEL1のbit24-26を使います。

GPFSEL1  0x7e200004 (物理アドレス 0x20200004)

このレジスタのbit24-26に010をセットすることでALT5の機能設定になり、GPIO18にPWM0が出力されるようになります。
プログラムでは、他のピンの設定を変更しないよう、一旦32bitの変数xに値を取り込んでからマスクして設定します。
--------------------------------------------------------------------
  uint32_t x;
  x = GPFSEL1;
  GPFSEL1 = (x & 0xf8ffffff) | 0x02000000; // GPIO18->PWM0 (ALT5)
--------------------------------------------------------------------
 
(2)PWMの動作クロックを設定する
PWMの動作クロックの設定方法は、BCM2835の仕様書[1]にはほとんど書かれていません。
しかし、BCM2835のレジスタ一覧のページ[3]を見ると、"CM"(Clock Manager)というRegister Regionの中に、設定レジスタが埋もれていることがわかります。

CM_PWMCTL  0x7e1010a0 (物理アドレス 0x201010a0)
CM_PWMDIV  0x7e1010a4 (物理アドレス 0x201010a4)

これらのレジスタの使い方は、実は、BCM2835の仕様書[1]のp107、108にあるCM_GP0CTLやCM_GP0DIVと同じです。
使い方は同じでも、CM_GP0CTLやCM_GP0DIVは、PWMとは関係のないレジスタで、後ほど動かし方を説明する「汎用クロック出力」の動作クロックを設定するためのレジスタです。
"CM"というRegister Regionには、このようなCM_xxxCTL、CM_xxxDIVという名前のレジスタが並んでおり、xxxがクロック供給先の各ペリフェラル機能(PWMや汎用クロック出力など)を表しています。
CM_xxxCTLやCM_xxxDIVを使うにはパスワードの指定が必要で、レジスタの上位8bitは必ず"0x5a"にする必要があります。

CM_xxxCTLレジスタ
CM_xxxCTLレジスタで主に設定/参照するのは次のフィールドです。
・SRC: クロックソースの選択
・ENAB: クロック供給のenableフラグ
・KILL: クロックジェネレータの強制リセットフラグ
・BUSY: クロックジェネレータの動作中を表すフラグ
・MASH: ノイズ除去フィルタの設定
・PASSWD: 0x5aを指定

クロックソース(SRC)で意味のあるのは、
・oscillator: BCM2835の外部クロック。Raspberry Piでは19.2MHz
・PLLA: PLL出力。(一説によると 650MHz)
・PLLC: PLL出力。(一説によると 200MHz)
・PLLD: PLL出力。(一説によると 500MHz)
です。
各PLL出力の周波数は公式なものは見つからず、実測値が出回っているので参考までに記載しました。

CM_xxxDIVレジスタ
CMxxxDIVは分周比を設定します。
Fractional分周器になっており、整数部と小数部をそれぞれ指定できます。

PWMの動作クロック設定手順
このあとの実験では、19.2MHzのoscillatorを選択、1/1920に分周して 10KHzにしてPWMに与えることにします。
MASHは使わない設定にします。

次のような処理になります。
・CM_PWMCTLのENAB=0、KILL=1 でクロックジェネレータをリセット
・CM_PWMDIVの分周比(1920)を設定
・CM_PWMCTLのSRCおよびMASHを指定しENAB=1 にしてクロック供給開始

これをプログラムにすると次の通りです。
--------------------------------------------------------------------
  CM_PWMCTL = 0x5a000021; // src=osc, en=0, kill=1
  CM_PWMDIV = 0x5a000000 | (1920 << 12); // 19.2MHz/1920=10KHz
  CM_PWMCTL = 0x5a000211; // src=osc, en=1, mash=1(none)
--------------------------------------------------------------------

(3)PWMのモードを設定する
BCM2835のPWMの説明は、BCM2835の仕様書[1]のp138〜にあります。
PWMには3つのモードがあります。
・PWMモード(MSEN=0)
・PWMモード(MSEN=1)
・serializerモード

大きくは、PWMモードとserializerモードの2つがあり、これは後述するCTLレジスタのMODEビットで指定します。
PWMモードは、duty比を指定するモードで、serializerモードは、与えられたビット列を順次出力するモードです。

PWMモードでMSEN=1のときは、PWMの動作クロックを、後述するRNGレジスタに指定した数だけカウントして1周期のPWM波形を作ります。
このPWM波形は、後述するDATレジスタに指定した数だけHの期間があり、残りがLの期間となります。
RNGをもとに波形の周期が決まり、DATをもとにduty比が決まるPWMを出力します。

一方、PWMモードでMSEN=0のときは少し複雑です。
MSEN=1のときの波形はPWM波形としては分かりやすいですが、Hの期間やLの期間が(PWMの動作クロックと比べ)長く続くものなので、この出力を、CとRからなるLPFを使ってアナログ値に変換するにはカットオフ周波数を決めづらく不都合です。
そのため、Hの期間やLの期間を動作クロック周期単位でばらし、RNGの範囲内に分散させた波形を出力するのがMSEN=0の動作です。
例えば、RNG=8でDAT=4の場合、MSEN=1では、Hが4サイクル続いた後、Lが4サイクル続く8サイクル分の波形になりますが、
MSEN=0では、4サイクル続くH、Lを、1サイクルのH×4個、1サイクルのL×4個にばらして、8サイクルの中に均等に分散させます。
結果、8サイクルという周期は分かりにくくなり、1サイクル毎にH→Lが繰り返される波形に見えます。

3つめのSerializerモードは、DATに与えられたビット列の中から、RNGで指定された数だけのビットを上位から順番に取り出して、1サイクル単位で出力します。
PWMというよりはparallel-serial変換に近い動作です。

PWMで設定するレジスタ群
PWMのレジスタ群は、BCM2835の仕様書[1]のp141〜に記載されていますが、実は、レジスタの置かれているベースアドレスの記載が漏れています。
BCM2835のエラッタ[2]にそのことが書かれていますので参照必須です。
http://elinux.org/BCM2835_datasheet_errata#p141

BCM2835のPWMは2つあり、どちらの設定も、

CTL  0x7e20c000 (物理アドレス 0x2020c000)

のレジスタで行います。

PWMのCTLレジスタ
CTLレジスタで主に設定/参照するのは次のフィールドです。
いずれもPWM0、PWM1のそれぞれに用意されています。
・PWEN: PWM出力のイネーブル
・MODE: PWMモード/serializerモードの切り替え
・MSEN: PWMモード時のH/L期間分散化の有無の指定

PWMのモード設定例
一番わかりやすいPWMモード(MSEN=1)の設定は次の通りです。
--------------------------------------------------------------------
  PWM_CTL = 0x00000081; //PWM0=enable, MODE=PWM, MSEN=1
--------------------------------------------------------------------

また、PWMモード(MSEN=0)の設定は次の通り。
--------------------------------------------------------------------
  PWM_CTL = 0x00000001; //PWM0=enable, MODE=PWM, MSEN=0
--------------------------------------------------------------------

Serializerモードを使うときには次のように設定します。
--------------------------------------------------------------------
  PWM_CTL = 0x00000003; //PWM0=enable, MODE=serializer
--------------------------------------------------------------------

(4)PWM出力する波形を設定する
前述の説明で登場したRNGやDATは、PWM0、PWM1のそれぞれに用意されています。
RNG1  0x7e20c010 (物理アドレス 0x2020c010)
DAT1  0x7e20c014 (物理アドレス 0x2020c014)
RNG2  0x7e20c020 (物理アドレス 0x2020c020)
DAT2  0x7e20c024 (物理アドレス 0x2020c024)

この他、今回の実験では使いませんが、FIFOを使ってデータを渡すこともできます。
FIFOは、最終目標である「BareMetalでFMトランスミッターを動かす」ときに使います。

(つづく)


ラベル:EWARM Raspberry Pi ARM11
posted by EWRL78JP at 20:00| Comment(0) | TrackBack(0) | Raspberry Pi | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。

この記事へのトラックバック
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。