こんにちは、稲垣@Cerevoです。今回はまたMSP430の話題です。
※一応、使っているのはMSP430F247であると断わっておきます。
MSP430には二つのタイマが入っていて、PWMを自動で (CPUが割り込みの中で操作しなくても) やってくれます。今回は普通にタイマでPWMをする方法を紹介し、さらに頑張って普通はPWMに使えないポートでも半自動でPWMをやってみたいと思います。
タイマの概要 (コンペアモード)
タイマにはタイマレジスタとキャプチャ・コンペアブロック0から2 (もしくは0から7) があり、大体以下のように動作します:
- タイマレジスタ
- 値が0になるときに割り込みを発生させる (オーバーフロー割り込み)
- タイマAでは16ビット幅で固定、タイマBでは16/12/10/8ビット幅で可変
- 三つのタイマモード
- ビット幅全てを使ってカウントアップ (連続モード)
- コンペアレジスタ0の値までカウントアップ (アップモード)
- コンペアレジスタ0の値までカウントアップ、さらに0までカウントダウン (アップダウンモード)
- キャプチャ・コンペアブロック
- キャプチャ・コンペアレジスタ (コンペアレジスタと略します) と出力ビットがある
- コンペアレジスタの値がタイマレジスタの値と等しくなるとき割り込み (コンペア割り込み)
- 同時に出力ビットをセット・リセットする
- キャプチャ・コンペアブロック (0以外)
- タイマレジスタの値がコンペアレジスタ0の値と等しくなるときに (すなわちコンペア割り込み0のタイミングで)、出力ビットをセット・リセット・トグルする (出力モードによる)
キャプチャ・コンペアブロック (0以外) には、出力ビットの変化するタイミングが
- 自分のコンペア割り込み
- ブロック0のコンペア割り込み
の二つあることに気づかれましたか。変化のタイミングが二つあるので、各ブロックごとに異なったデューティー比のPWM出力ができるわけです。つまり、普通のPWMとしての使い方は次のようです:
- アップモード
- コンペアレジスタ0: 変調周波数を決める
- コンペアレジスタ0以外: デューティー比を決め、適当な出力モードでPWM出力
キャプチャ・コンペアブロック0でもPWM
上記のような特性から、ブロック0ではPWM出力はできません (パルス幅が0固定のPWMと言えなくもないのですが)。かといって、連続モードに設定して、割り込みが発生するたびにコンペアレジスタを設定しながらGPIOを叩くのもやや無駄な話です。そういうのは各ブロックで別々の割り込み周期を使いたい場合にすることです。もうちょっとCPUがサボりながらPWM出力をする方法があります。ハードウェアに足りない機能だけ、ソフトウェアで実現するべきです。
PWMのキモはこの特徴です:
コンペア割り込みのタイミングで出力ビットをセット・リセット・反転
これでデューティー比が決まるわけです。この特徴は全てのキャプチャ・コンペアブロックに存在します。活用しましょう。ただし、キャプチャ・コンペアブロック0で活用するには、連続モードにしなければいけません。代償として変調周波数は自由に設定できなくなります。
一方、キャプチャ・コンペアブロック0には、この機能がありません:
ブロック0のコンペア割り込みタイミングで出力ビットをセット・リセットする
当然ですね。この部分をオーバーフロー割り込みのタイミングでソフトウェアで処理すれば、キャプチャ・コンペアブロック0でもPWMができます。つまりオーバーフロー割り込みで、出力ビットを0または1に設定すればいいのです。
なお、出力ビットは、通常の出力モードでは値を設定できません。手動設定のモードに切り替えてから設定し、また通常のモードに戻すことになります。なかなか普通じゃない感があります。
そして自由な変調周波数
先に、「代償として変調周波数は自由に設定できなくなります」と書きましたが、自由にする方法が無いわけではありません。というのは、タイマAのタイマレジスタはビット幅が16ビット固定で、連続モードに設定すると周期がかなり長くなってしまうのです。通常の1MHzのクロックだとおよそ16Hz、8MHzのクロックを使ってもおよそ128Hzです。省電力な低速クロックではさらに遅くなりますし、そもそも16HzなんてPWMでLEDを光らせるには遅すぎます。
解決方法は簡単で、周期を短くしたければ、オーバーフロー割り込みの中でタイマレジスタの値を適当に大きくすればいいのです。例えば、0xff00を代入すれば、カウンタは8ビット幅になったも同然。こうして自由な変調周波数を手に入れることができます。コンペアレジスタの値も対応する値にしておきましょう。
まとめ
まとめると次のようにタイマを設定することになります:
- 初期設定
- 連続モード
- コンペアレジスタ0にデューティー比を設定 (必要なら、(0x10000 – 周期 + デューティー比) の値にする)
- 出力モードはトグル
- オーバーフロー割り込み
- 出力モードを手動に設定し、出力ビットを0 (ないし1) に初期化
- 出力モードをトグルに設定し直す
- 必要ならタイマレジスタの値を (0x10000 – 周期) の値に設定し直す
ちなみに、タイマのクロックがCPUのクロックと同期していない場合についてデータシートには色々と注意書きがありますが、今回のような使い方では特に気にするべきことはないようです。
おしまい
実は事の発端はタイマBの出力0にLEDをつなげてしまったことだったりします。プリント基板を作る前にチップの仕様をよく確認しないと、ソフトにしわ寄せがくるようですね (寄せることができるともいう)。
ともあれ、Happy hacking!
著者プロフィール
最近の投稿
- 02. ソフトウェア2016.12.15[15日目] 不具合を発見したときのテストレポートの心得
- 03. 組み込み2012.09.19H.263からSparkへのトランスコーディング(上)
- 03. 組み込み2012.09.09H.263からSparkへのトランスコーディング(下)
- 03. 組み込み2009.09.02GIOChannelの使い方