CMTの使い方
ここでは,SH7085が内蔵するCMTの使い方を学ぶ.
CMT とは
CMT は,Compare Match Timer の略であり,コンペア(比較)し,マッチ(一致)したら何かが起こるタイマである.実際には,クロックをカウントするカウンタと,ユーザが設定する値とを比較していて,一致するとレジスタの特定ビットが1になったり,割込みを発生させることもできる.
タイマと呼ばれるものは,時間をカウントするもので,マイコンの世界では,頻繁に使われており,ソフトウェアタイマと大別できる.
ソフトウェアタイマとは,簡単に言えば,次のようなプログラムである.
for ( i = 0; i < 10000; i++) ;
上記の場合,待ち時間は,「出たとこ勝負」である.もちろん(ビルドしてマシン語に変換された)命令の実行時間やハードウェア性能などから正確な時間を計算することも可能ではあるが,現実的ではない.
ハードウェアを使用したタイマは,正確な周期の信号をカウントするので,カウント値と時間の関係は一定であり,正確な時間を知る(作る)ことができるのである.
マイコンには,このような用途のカウンタ機能が組込まれていることが多く,SH7085の場合には,ここで解説するCMT と,さらに多機能なMTU2 などのタイマ機能も組込まれている.
モジュールスタンバイ
SH7085の内蔵モジュールは,初期状態でスタンバイモードになっているものが多い.今回使用するCMT も初期状態では,スタンバイモードになっているモジュールの一つである.
これは,SH7085 の消費電力を抑えるためである.SH7085 内部のモジュールは,カウントや通信などを行わずにじっとしている場合にも,クロックが供給されているだけで,一定の電力を消費してしまう.特に,バッテリ駆動の機器の場合などは無駄な電力消費は大きな問題となる.そこで,不要なモジュールはクロックを断ってしまうのである.
CMT モジュールを動作可能な状態にするには,モジュールのスタンバイモードを解除する必要がある.そのためには,スタンバイコントロールレジスタ4(STB.CR4)の ビット5 を使う.このビットを0にすることで,クロックが供給されて,CMT は動作可能状態になる(図1).
図1:モジュールスタンバイの解除
図1は,ハードウェアマニュアルに記載されているビットの名称である.しかし,このビットの名称は,直感的ではないので,HEW の標準で生成される iodefine.h では,下記のように構造体と共用体が宣言されている.
struct st_stb { union { unsigned char BYTE; struct { unsigned char STBY:1; } BIT; } CR1; ~ 中略 ~ union { /* STBCR4 */ unsigned char BYTE; /* Byte Access */ struct { /* Bit Access */ unsigned char _MTU2S:1; /* MTU2S */ unsigned char _MTU2 :1; /* MTU2 */ unsigned char _CMT :1; /* CMT */ unsigned char :2; unsigned char _AD2 :1; /* A/D2 */ unsigned char _AD1 :1; /* A/D1 */ unsigned char _AD0 :1; /* A/D0 */ } BIT; } CR4; ~ 中略 ~ }; #define STB (*(volatile struct st_stb *)0xFFFFE802)/* STB Address*/
モジュールスタンバイレジスタにアクセスするには,下記のようなプログラムになる.
STB.CR4.BIT._CMT = 0; /* モジュールスタンバイの解除 */ STB.CR4.BYTE &= 0xDF; /* モジュールスタンバイの解除 */
CMT の構成
CMT には,2チャネルのカウンタが内蔵されており,それぞれ,CMT0(CMT チャネル0) とCMT1(CMT チャネル1) と呼ぶ.CMT のチャネル0 の概念図を図2に示す.なお,チャネル0 もチャネル1 も同じ構成になっている.
図2:CMT の概念図
周辺バスクロック(Pφ) を 20MHzとする.
1.1/8 ,1/32 ,1/128 ,1/512 に分周し,いずれかの分周比を選択し,そのクロックをカウントし,カウント結果がCMCNT レジスタへ格納される.クロックの分周比は,CMCSR レジスタのCKS ビット(2ビット)で選択する.初期値は 00 (1/8)である.
2.CMCOR レジスタは,CMCNT レジスタと比較する値が格納されたレジスタである.このCMCOR レジスタに格納された値とCMCNT レジスタの値が比較され,一致すれば CMCSR レジスタのビット7 が1 になる.同時にCMCNT レジスタは,0 にクリアされ,再びカウントを開始する.
3.カウントの開始と停止を行うレジスタがCMSTR レジスタである.このレジスタのSTR0 と STR1 ビットでカウントの開始と停止を行う.
4.CMCSR レジスタのビット7のように,判定の結果を示すビットをフラグビットと呼ぶ.フラグが1になることを「フラグが立つ」と言うことが多い.逆にフラグビットが0になることを「フラグが倒れる」などと言うことがある.
Pφ の分周比を1/8 に設定した場合,20[MHz] × 1 / 8 = 2.5 [MHz] をカウントすることになる(図3).この場合,1周期は,0.4[us](400[ns])であるから,CMCNT レジスタの値は,0.4 us ごとに1づつ増加する.CMCNT レジスタは,16ビットレジスタなので,0x0000 〜 0xFFFF までをカウントできる.0xFFFF に1 を加えると 0x0000 に戻る.10進数では, 0 〜 65535 までである.
CMT0.CMCSR.WORD = 0x0000; /* デフォルト値を設定 */ CMT1.CMCSR.BIT.CKS = 0x1; /* チャネル1の分周比 1/32 を選択 */
CMSTR レジスタの構成を図6に示す.
図6:CMSTR レジスタの構成
CMSTR レジスタは,タイマ動作の開始と停止の設定を行うレジスタである.このレジスタは,チャネル0とチャネル1で別々になっておらず,1つのレジスタ内のビット0とビット1に0または1を設定することで,タイマ動作の開始と停止を行なっている.
実際にレジスタにアクセスするには,下記のようなプログラムになる.
CMT.CMSTR.BIT.STR0 = 1; /* CMTのチャネル0スタート */ CMT.CMSTR.WORD |= 0x0002 /* CMTのチャネル1スタート */
簡単な使用例
まず,単純な使用例でCMTを動かしてみる.
下記よりワークスペースをダウンロードし,展開する.その後,CMT.hws をダブルクリックして,HEW でワークスペースを開く.
ここでは,CMT_prog1 というプロジェクトをアクティブプロジェクトとする.ソース・プログラムは,CMT_prog1.c である.CMT はチャネル0を使用している.
このプログラムを何も変更せずに,ビルドして,hterm を使い,ミームスにダウンロードして実行すると,LED5 とLED6 が高速で点滅する.この点滅間隔がCMTのカウント機能を使ってコンペアマッチが行われている時間である.
この点滅間隔は,図4にあるように,およそ,26.215[ms] である.
動画1:CMT_prog1 の実行結果
コンペアマッチ周期の変更(その1)
CMCSR レジスタ内のCKSビットを変更し分周比を変えることによって点滅間隔を変えることができる.現在は,初期値の1/8 分周になっているが,これを 1/32 や 1/128 ,1/512 分周にすることで点滅間隔を大きくすることができる.
演習問題(その1)
1.各分周比においてカウントできる最大値を計算し,下記の表を完成させよ.但し,Pφ = 20MHz とする.
CKS設定値 | 分周比 | 周期 | 最大値 |
0 | 1/8 | 0.4[us] | 26.2144[ms] |
1 | 1/32 | ||
2 | 1/128 | ||
3 | 1/512 |
2.CMT_prog1.c のCKS の設定部分を変更し,上記の計算した間隔でLED が点滅することを確認せよ.但し,厳密に測定するにはオシロスコープが必要になるので,ここでは,分周比を変化させることで点滅間隔が「速くなる」「遅くなる」ということを確認すればよい.
コンペアマッチ周期の変更(その2)
上記のその1でCKS の設定で大雑把にコンペアマッチの時間が変化することが確認できた.次は,CMCOR レジスタに設定する値を変化させてユーザが決めた値で点滅するようなプログラムの作成を考える.
分周比を1/8 ,Pφ = 20[MHz] であるとする.分周比が1/8 なので,実際にカウントする周波数は,下記のように計算できる.
この周波数 f の周期 T は下記のように計算できる.
カウントする数を N とし,N カウントしコンペアマッチする時間を t とすると,t は,N を使って下記のように計算できる.
上記の式をカウントする回数の N について解くと,下記のような計算により N が計算できることがわかる.
上記の式によって求められる N の値を CMCOR レジスタに設定するが,ここで,考えなければならないことがある.前述したが,実際には,0 からカウントを開始するので,実際には,計算より求まった値より1少なくする必要がある.
これは,例えば,N = 5 のとき 1〜5までの5カウントとするのが普通であるが,カウントの開始は,0からなので,実際には,0〜5までの6カウントとなる(図7).
図7:カウントの開始とCMCORへの設定値
従って,計算により求まった値から1を引き算した結果をCMCOR レジスタに設定することとなる.つまり,正しくは下記の式である.
上の式を一般的に書き直すと,N をカウントする回数,t をコンペアマッチする時間[s],分周比を d (1/8, 1/32, 1/128, 1/512 のいずれか),周辺バスクロックを Pφ [Hz]とすると,一般的には下記の式で N を求めることができる.
上の式を使って,例えば,t = 1[ms] ,分周比 d = 1/8 ,Pφ = 20[MHz] とすると,N は,下記のように計算できる.
つまり,CMCOR に設定すべき値は,2499 であることが計算により求められる.
スピーカから音を出す
これまでは,CMTを使ってLED の点滅を制御するプログラムを作成してきた.ここでは,LED の替りにスピーカから音を出すことを行なってみる.
ミームスには,圧電スピーカが実装されており,パスル信号を与えることにより音を出すことができる.図8 に接続の様子を示す.
図8:SH7085 と圧電スピーカの接続図
PE0 から任意のパルスを出力することで任意の音を発生させることができるようになる.CMT_prog2 プロジェクトをアクティブプロジェクトに設定し,ビルドして,ダウンロードし,実行すると圧電スピーカから1kHz の音が発生するので,まず実行し,確認すること
動画2:CMT_prog2 の実行結果
※音が出ますので,パソコンで音が聞こえる状態にしてから再生してください.
演習問題
1.CMT_prog1 のCMCOR レジスタの値とCKS の値を計算によって求め,点灯と消灯が 50ms, 400ms, 1s で繰り返されるような3種類のプログラムを作成せよ.ただし,1s については,割り切れないため,2s の値を求めてそれを半分にした値を使用すること.
動画3:50ms,400ms,1s 間隔で点滅するプログラムの実行結果
2.CMT_prog2 を改造して,SW6 でCMT のカウントをスタートさせ,SW5 でカウントを一時停止させる.再びSW6 を押すことカウントを再開するプログラムを作成せよ.スピーカへの出力は,1kHz とする.なお,SW については,押したときに動作するようにせよ.
動画4:SW によるCMT の開始と停止制御
※音が出ますので,パソコンで音が聞こえる状態にしてから再生してください.
3.CMT_prog2 を改造して,1秒毎に断続的に1kHz の音が鳴るようにせよ.なお,1kHz の周期の作成には,CMTのチャネル0を使用し,1秒のタイミングはCMTのチャネル1を使用すること.
動画5:CMT の2チャネルを使用したプログラム例
※音が出ますので,パソコンで音が聞こえる状態にしてから再生してください.
【難問に挑戦】
7セグメントLED を使って,100ms 毎にカウントアップするストップウォッチプログラムを作成せよ.7セグメントは,U16を10秒の位,U17を1秒の位,U18を100msの位とする.ただし,99.9秒になったら00.0秒に戻るようにすること.また,ストップウォッチなので,スタートスイッチは SW6 とし,ストップスイッチは SW5 とする.さらに,SW4 で 00.0 秒にクリアするようにすること.
動画6:99.9秒まで計測可能なストップウォッチの実行例