目次
前
次
モータ制御
2個のモータを、PWM(Pulse Width Modulation)で制御します。
PWMは、モータの回転速度を、パルス幅で制御する方式です。
1周期の中で、電圧がHとLになっている時間を可変にして
モータに与える平均電力を変えていきます。
H8のITU(Integrated Timer Unit)には、PWM用モジュールが
含まれていますが、データシートの説明がわかりにくいので
タイマー割込みを利用した単純な方法にしました。
PWMの原理は、非常に簡単です。
0〜99までカウンタで数えて、レジスタの値と比較します。
レジスタの値がNで、カウンタの値がTとすると、次の処理を
します。
N < T → 1出力
N >= T → 0出力
この動作を、タイマー割込みの中で実行します。
Cのコードで記述すると、以下です。
/* handling counter */
pwm_cnt++ ;
if ( pwm_cnt == 100 ) { pwm_cnt = 0 ; }
/* compare */
LEFT_OUT = RIGHT_OUT = ON ;
if ( pwm_cnt >= left_duty ) { LEFT_OUT = OFF ; }
if ( pwm_cnt >= right_duty ) { RIGHT_OUT = OFF ; }
/* out */
タイマー割込みの中で、上の動作をすればよいので
タイマー割込みの種類選択と周期を決めれば、制御できます。
今回は、H8にあるITU1を利用し、モータを制御します。
タイマー割込みが使えるワンチップマイコンであれば、大抵
用意されているコンペアマッチ割込みを使います。
コンペアマッチ割込み
普通のワンチップマイコンの内蔵タイマー/カウンタには
カウンタとプリスケーラが用意されています。
カウンタは、8ビットか16ビットになっていることが多い
ですが、H8のITUは16ビットです。
カウンタの他に、GRA、GRBという2個の16ビットレジスタ
があります。
これらのレジスタの値とカウンタの値を比較し、値が一致
したとき、割込みを発生するのが、コンペアマッチ割込み
です。コンペアマッチ割込みで、比較する値を入れる所が
2つあるのがH8のITUです。値が一致すると、カウンタを0
にできます。
利用しているH8/3048Fのボードは、16MHzのシステムクロック
になっているので、プリスケーラの分周比を1:1としカウンタ
に16MHzを入力します。
PWM用のカウンタは、100まで数えて0に戻すので、PWMの
1周期を400kHzに設定します。
16MHzから400kHzを生成したいので、16000kHz/400kHz=40を
GRAに設定します。
ITU1の初期化処理は、次の関数で定義しました。
#define ITU1_AREG 40
#define MASKFFFF 0xffff
void init_timer1(void)
{
/* stop timer */
ITU.TSTR.BIT.STR1 = OFF ;
ITU.TOER.BYTE = 0 ;
/* TIOR : Timer I/O Control Register
7 **** -> 0
6 IOB2 -> 0 GRB is not output compare match register
5 IOB1 -> 0
4 IOB0 -> 0
3 **** -> 0
2 IOA2 -> 0 GRA is not output compare match register
1 IOA1 -> 0
0 IOA0 -> 0
*/
ITU1.TIOR.BYTE = 0 ;
/* TCR : Timer Control Register
7 **** -> 0
6 CCLR1 -> 0 clear TCNT if GRA = TCNT
5 CCLR0 -> 1
4 CKEG1 -> 0 rising edge
3 CKEG0 -> 0
2 TPSC2 -> 0 φ利用
1 TPSC1 -> 0
0 TPSC0 -> 0
*/
ITU1.TCR.BYTE = 0x20 ;
/* TIER : Timer Interrupt Enable Register
7 **** -> 0
6 *** -> 0
5 *** -> 0
4 *** -> 0
3 *** -> 0
2 OVIE -> 0
1 IMIEB -> 0
0 IMIEA -> 1 select compare match interrupt
*/
ITU1.TIER.BIT.IMIEA = ON ;
/* reference */
ITU1.GRA = ITU1_AREG ;
ITU1.GRB = MASKFFFF ;
/* counter */
ITU0.TCNT = 0 ;
}
割込み中の処理
割込みが発生した場合、次のシーケンスを実行します。
- 割込みフラグリード
- 割込みフラグクリア
- カウンタインクリメントと初期化
- カウンタとレジスタの値を比較
- ポートへ論理値出力
割込み中の処理(ハンドラ)は、次の関数で定義しました。
void int_imia1(void)
{
UBYTE dummy ;
/* clear flag */
dummy = ITU1.TSR.BIT.IMFA ;
ITU1.TSR.BIT.IMFA = OFF ;
/* increment */
pwm_cnt++ ;
if ( pwm_cnt == 100 ) { pwm_cnt = 0 ; }
/* judge logical level */
LEFT_MOTOR = RIGHT_MOTOR = OFF ;
if ( pwm_cnt < left_duty ) { LEFT_MOTOR = ON ; }
if ( pwm_cnt < right_duty ) { RIGHT_MOTOR = ON ; }
/* put logical level */
PB.DR.BIT.B2 = LEFT_MOTOR ;
PB.DR.BIT.B0 = RIGHT_MOTOR ;
}
回転開始と停止
必要ない場合に、モータを回転させないように
開始と停止を担当する関数を定義します。
パルスを与えなければよいので、カウンタにクロックを
与えるか与えないかと、ポートへ出力する論理値で回転
の開始、停止を実行します。
回転開始は、カウンタにクロックを与えます。
次のように定義しました。
void start_pwm(void)
{
ITU.TSTR.BIT.STR1 = ON;
}
回転停止は、カウンタに与えるクロックを切ると同時に
出力論理値を0にします。
void stop_pwm(void)
{
ITU.TSTR.BIT.STR1 = OFF;
/* set logical level */
PB.DR.BIT.B0 = OFF ;
PB.DR.BIT.B2 = OFF ;
}
速度制御
左右のモータのDuty比を変更できるようにします。
グローバル変数left_duty、right_dutyを用意し、
これらの値を更新する関数で実現しました。
UBYTE left_duty;
UBYTE right_duty;
void update_motor(UBYTE dl,UBYTE dr)
{
left_duty = dl ;
right_duty = dr ;
}
目次
前
次