P-ROBO(ピーロボ)
割り込み処理
P−ROBOの割り込みでは、大きく分けて3つの処理を行いました。
以下にてこれらの解説を行います。
- モータ
- ユーザLED
- タイマ
モータ
モータ部分では、動作に強弱(速さの調節)を行うためにPWM制御をしています。
※PWM制御とは?
一定周期の中で出力のON時間とOFF時間の割合を変えて、出力電圧を制御すること。
- モータ部の処理は、割り込みが30回発生して1PWM周期とする。
- 1PWM周期中で、割り込み何回分モータをオンにするかで生成する。
※例 1PWM周期(割り込みが30回発生)中に、割り込み15回分だけモータをオンにする。上記のような処理を行うことにより、モータ出力の50%を生成することが出来ます。モータ出力の25%を生成したい場合は、1PWM周期中に割り込み7,8回分だけモータをオンにすることにより生成することが出来ます。
では、実際のプログラムを見ながら確認することにします。
if(iPwmCycle > 0){
iPwmCycle--; // PWMサイクルカウンタをデクリメントする
/*** 右モータ制御 ***/
if(iR_MotorCount > 0){
iR_MotorCount--; // 右モータカウントをデクリメントする
}else{
iActPortA |= 0x03; // 右モータを停止する
}
/*** 左モータ制御 ***/
if(iL_MotorCount > 0){
iL_MotorCount--; // 左モータカウントをデクリメントする
}else{
iActPortA |= 0x0c; // 左モータを停止する
}
}else{
iPwmCycle = PWM_CYCLE; // PWMサイクルカウントをセットする
iR_MotorCount = iR_MotorSpeed; // 右モータカウントをセットする
iL_MotorCount = iL_MotorSpeed; // 左モータカウントをセットする
iActPortA &= 0x10; // モータ動作をリセットする
iActPortA |= (iMotorData & 0x0f); // モータ動作をセットする
}
まずは変数の解説から・・・
- iPwmCycle:
- 1PWM周期を作成するための値「 30 」が最初に格納されます。
- iR_MotorCount:
- 右モータを、モータ部の処理1周期の内、割り込み何回分動作させるかを設定する値が格納されます。
- iL_MotorCount:
- 左モータを、モータ部の処理1周期の内、割り込み何回分動作させるかを設定する値が格納されます。
- iActPortA:
- ポートAに設定する値が格納されます。
- iR_MotorSpeed:
- 割り込み関数の外で設定された、右モータの設定値が格納されています。
- iL_MotorSpeed:
- 割り込み関数の外で設定された、左モータの設定値が格納されています。
- iMotorData:
- 割り込み関数の外で設定された、モータの動作設定値が格納されています。
続いて処理の解説を行います。
始めにPWMサイクルカウントが0かチェックしています。
PWMサイクルカウントが0で無い場合は、カウントをデクリメントしています。
右モータの制御を行います。
右モータカウントが0かチェックしています。
カウントが0で無い場合は、カウントをデクリメントしています。
カウントが0の場合は、ポートAに設定する値が格納されている変数【iActPortA】に0x03をor演算して、右モータの動作をフリーに変更しています。
左モータの制御を行います。
制御内容は右モータと同様ですが、カウントが0の場合に設定する値が0x0cとなっています。
これは左のモータの動作をフリーに設定するためです。
次にPWMサイクルカウントが0の場合の処理を行います。
処理内容は、初期化処理のような内容になります。
PWMサイクルカウンタにPWMサイクルカウント(30)を設定する。
割り込み変数外で設定された右モータ設定値を、右モータのカウントに設定する。
割り込み変数外で設定された左モータ設定値を、左モータのカウントに設定する。
モータ動作をリセットし、割り込み関数外で設定されたモータ動作を、ポートAに設定する変数に設定します。
以上でモータ部の処理が終了しました。各モータ部のカウント値を変更することにより、モータ動作に強弱をつけることが出来るようになりました。
ユーザLED
ユーザLEDのオン/オフの処理を行っています。処理内容は単純なもので、ポートAに設定する変数のユーザLEDのビットを一度リセットし、再度ユーザLEDビットのみを設定します。
プログラムを見て確認しましょう。
iActPortA &= 0x0f; // ユーザLEDをリセットする
iActPortA |= iUserLedData; // ユーザLEDをセットする
とても簡単です。
まずはポートAに設定する変数【iActPortA】のユーザLEDビットをリセットします。
割り込み処理外で設定されたユーザLEDの設定値が格納されている変数【iUserLedData】がありますので、ポートA設定変数へor演算しています。
以上でユーザLEDの処理は終了です。
ポートAの設定
ここまででポートAに設定する情報はそろいました。後はポートAの設定を行うだけですが、実際に設定する方法はとても簡単です。 プログラムを見ながら確認しましょう。
iPORTA = iActPortA; // データを出力する
たったこれだけで終了です。レジスタの設定でiPORTA変数にはポートAのアドレスが割り当てられているので、iPORTA変数に設定値を格納するだけでよいのです。
タイマ
今回使用しているタイマには2種類あります。1つは電源が投入され、割り込み処理が許可された時点から停止することなく動作を続けるインターバルタイマです。10msごとにフラグが立ちます。その他のタイマとしてユーザが時間を設定して使用するタイマを3つ作成しました。設定時間になると対応するフラグが立ちます。
プログラムを見ながら確認しましょう。
/*** 10MSec Interval Timer ***/
if(uiTmr10MSec != 0){ // 10msタイマカウンタをチェックする
uiTmr10MSec--; // 10msタイマカウンタが0以外の場合は、
10msタイマカウンタをデクリメントする
}else{ // 以下、10msタイマカウンタが0の場合
iFlag |= 0x01; // 10msタイマのタイムアウトフラグをセットする
uiTmr10MSec = INTERVAL_10ms; // 10msタイマカウンタを設定する
/*** Timer Count Down ***/
for(i = 0; i < MAX_TIMER; i++){ // タイマ数分ループする
if(uiTimerCnt[i] != 0){ // タイマカウンタをチェック
uiTimerCnt[i]--; // タイマカウンタが以外の場合はデクリメントする
if(uiTimerCnt[i] == 0){
/*** Timer End Check ***/
switch(i){
case 0:
iFlag |= 0x02; // タイマ0タイムアウトフラグをセットする
break;
case 1:
iFlag |= 0x04; // タイマ1タイムアウトフラグをセットする
break;
case 2:
iFlag |= 0x08; // タイマ2タイムアウトフラグをセットする
break;
default:
break;
}
}
}
}/*** Timer Count Down Loop End ***/
始めに10msタイマカウントが0かチェックしています。
10msタイマカウントが0で無い場合
10msタイマカウントをデクリメントします。
10msタイマカウントが0の場合
3つあるタイマをループしながら1つずつチェックします。
各タイマが0かチェックし、0の場合は何もしません。
タイマ1
カウントが0で無い場合はタイマカウントをデクリメントする。
タイマ1のカウントが0かチェックする。
カウントが0の場合は、対応するフラグを立てる。
タイマ2
カウントが0で無い場合はタイマカウントをデクリメントする。
タイマ1のカウントが0かチェックする。
カウントが0の場合は、対応するフラグを立てる。
タイマ3
カウントが0で無い場合はタイマカウントをデクリメントする。
タイマ1のカウントが0かチェックする。
カウントが0の場合は、対応するフラグを立てる。
上記から分かるように、タイマ1カウントの時間は10msになります。
タイマを1秒タイマとして使用する場合は、タイマカウンタに100を設定します。
割り込み関数
割り込み関数のプログラムです。
__interrupt()
{
int i; // ループカウンタ
iINTCON &= 0xfb; // TMR0オーバーフロー割り込みフラグをクリア
uiTMR0 = 255; // 8ビットカウンタの設定
/*** モータ処理 ***/
if(iPwmCycle > 0){
iPwmCycle--; // PWMサイクルカウンタをデクリメントする
/*** 右モータ制御 ***/
if(iR_MotorCount > 0){
iR_MotorCount--; // 右モータカウントをデクリメントする
}else{
iActPortA |= 0x03; // 右モータを停止する
}
/*** 左モータ制御 ***/
if(iL_MotorCount > 0){
iL_MotorCount--; // 左モータカウントをデクリメントする
}else{
iActPortA |= 0x0c; // 左モータを停止する
}
}else{
iPwmCycle = PWM_CYCLE; // PWMサイクルカウントをセットする
iR_MotorCount = iR_MotorSpeed; // 右モータカウントをセットする
iL_MotorCount = iL_MotorSpeed; // 左モータカウントをセットする
iActPortA &= 0x10; // モータ動作をリセットする
iActPortA |= (iMotorData & 0x0f); // モータ動作をセットする
}
/*** ユーザLED処理 ***/
iActPortA &= 0x0f; // ユーザLEDをリセットする
iActPortA |= iUserLedData; // ユーザLEDをセットする
/*** データ出力 ***/
iPORTA = iActPortA; // データを出力する
/*** タイマ処理 ***/
/*** 10MSec Interval Timer ***/
if(uiTmr10MSec != 0){ // 10msタイマカウンタをチェックする
uiTmr10MSec--; // 10msタイマカウンタが0以外の場合は、
10msタイマカウンタをデクリメントする
}else{ // 以下、10msタイマカウンタが0の場合
iFlag |= 0x01; // 10msタイマのタイムアウトフラグをセットする
uiTmr10MSec = INTERVAL_10ms; // 10msタイマカウンタを設定する
/*** Timer Count Down ***/
for(i = 0; i < MAX_TIMER; i++){ // タイマ数分ループする
if(uiTimerCnt[i] != 0){ // タイマカウンタをチェック
uiTimerCnt[i]--; // タイマカウンタが以外の場合はデクリメントする
if(uiTimerCnt[i] == 0){
/*** Timer End Check ***/
switch(i){
case 0:
iFlag |= 0x02; // タイマ0タイムアウトフラグをセットする
break;
case 1:
iFlag |= 0x04; // タイマ1タイムアウトフラグをセットする
break;
case 2:
iFlag |= 0x08; // タイマ2タイムアウトフラグをセットする
break;
default:
break;
}
}
}
}/*** Timer Count Down Loop End ***/
}/*** 10MSec Interval Timer Loop End ***/
}