PICで電飾 割り込み処理 その2 タイマ0の設定方法
タイマの種類
割り込みにはタイマという機能を使います。このブログで使っているPIC12F683と16F1827には
タイマ0、タイマ1、タイマ2
という3種類のタイマ機能があり、それぞれ微妙に機能が異なっていて、設定方法も違います。
機能の違いに関しては、電飾のために割り込み処理をさせたいというだけなら、知らなくても影響ありません…と私は思っていたのですが、色々試してみると、微妙に違うようです。ここで説明しているボタンの受付ならどれでも同じなのですが、PWM調光ということになるとタイマ2がおすすめです。
ボタン入力の受付にタイマ0や1を使い、PWM調光にタイマ2を使う、というように使い分けるのがいいかもしれません。
ともあれ、タイマ0から説明します。
タイマと割り込み
タイマで割り込み処理を実現するには、前回述べたようにメインの仕事と、割り込みさせたい仕事を分けて、メイン関数と割り込み関数に入れておくのがまず第一の作業です。
今回もプログラムは一番最後に載せています。前回のとほぼ同じですが違いとしてまず第一に
・ボタン入力の受付が、割り込み関数としてメイン関数から分離していること
が違います。下掲のプログラムを見て下さい。メイン関数は
void main(void)
{
・・・・
}
のところで、ここでLEDを点滅させたりしています。
割り込み関数はその下の
void __interrupt() xxx(void)
{
・・・・
}
のところで、ここでボタン入力の受付をやっています。
タイマと割り込み処理を実現する呪文
前回とプログラムが違う点の第2が
・その割り込み関数を機能させるのに必要な文言が加えられていること
です。プログラムの中で、赤字にしているのがそのタイマ0の割り込み機能を使うにあたって必要な文言です。
一応意味はコメントとして//の後ろに書いていますが、もうこれは機能を呼び出すための呪文のようなものですから、機械的に書くしかありません。
自分もやってみようという方は、この赤字の文言をコピーして、メイン関数のタイマー設定のところと、割り込み処理の前後に適宜はめ込んでください。
割り込み周期を決める
プログラムの中で背景色が水色のところが3箇所あります。これらは
どれくらいの周期で割り込みをするかを決めるパラメーター
です。これは必要に応じて作った人がきめる必要があります。
今回はボタンが押されたことを確認する作業を20ミリ秒に一回、させることにします。
1秒間に50回ボタンが押されたか確認しているわけですから、人間の時間感覚で言えば、どの瞬間でも確認していると言っていいですよね。
タイマ0では3つの要素で周期が決まります。
①クロック数
②プリスケール
③TMR0初期値
です。周期を決める計算式があるわけなのですが、面倒なので少し前に紹介した「PICとは」のページに頼ることにしましょう。
ページの半ばにこういうフォームがあります。
クロック数(青丸)はいじらず、プリスケール(赤丸)とTMR0 初期値(緑丸)だけ数字をいじって、最終的なタイマ周期を20ミリ秒にします(オレンジ矢印)。
クロック数を8MHZ、プリスケールを256、TMR0初期値を99にすると、20ミリ秒に一回割り込みが発生するわけですね。(完全にぴったりじゃないですけど…)
ではこの設定をプログラムに落とし込みます。また下掲のプログラムを見てください。
①クロック数の設定
クロック数の設定はレジスタ設定の
OSCCON = 0b01110010 ;//①内部クロック8MHz
ここでやっています。水色の背景の3桁の数字を書き換えることでクロック数が変わります。
実はこれまでの工作は全て8Mhzでやってきています。多分模型電飾ならもっと低いクロックでぜんぜん大丈夫だと思うのですが、モーター制御が関わってくるとそうでも無いかもです。とりあえず8Mhzでいきます。
②プリスケールの設定
プリスケールの設定は、OPTION_REGの下3桁の数字を変えることで設定します。
OPTION_REG = 0b00000111;//内部プルアップ抵抗を使う、②タイマ0のプリスケール256
プリスケールの値と、下三桁の値の対応は下の表のとおりです。
今回は256なので、下三桁は111なわけですね。
③TMR0初期値
TMR0初期値は、フォームで導かれた99という数値をそのまま
TMR0=99;//③タイマ0初期値初期値99
と書いておけばOKです。
これで20ミリ秒に一回、割り込みが生じ、PICは割り込み関数に行ってボタンが押されてるかどうかチェックします。下掲のプログラムをコピペしてビルドして書き込みしてみてください。
どの瞬間でもRB3のボタンを押せば、フラグが立って、RA1のLEDが光るようになったはずです。
ただし、押した瞬間からRA1が光り始めるわけではありません。押した瞬間にフラグは立てるのですが、RB6,RB7を光らせて1秒待つという仕事自体はキャンセルされていないので、フラグを立てたらまたその作業に戻ってしまいます。そして、RB7を光らせ終わってから、フラグに従ってRA1を光らせる作業に移るのです。
ボタン押したら即反応して欲しい、ということになると少し工夫がいるのですが、ちょっとその件は置いておいて、先にタイマ1とタイマ2についても説明をしてしまいたいと思います。
今回のプログラムは以下の通りです。MPLABにコピペしてビルド、書き込みでちゃんと動いてくれるか確かめてみてください。
#include <xc.h>
#define _XTAL_FREQ 8000000
//コンフィグ設定
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
int Event=0;
//メイン関数ここから
void main(void)
{
//特殊レジスタの設定
OSCCON = 0b01110010 ;//①内部クロック8MHz
ANSELA = 0b00000000 ;//RAの足をすべてデジタルI/Oとして使う
ANSELB = 0b00000000 ;//RBの足をすべてデジタルI/Oとして使う
OPTION_REG = 0b00000111;//内部プルアップ抵抗を使う、②タイマ0のプリスケール256
TRISA = 0b00000000 ;//RAの足はすべて出力に使う
TRISB = 0b00001100 ;//RBの足のうちRB2,RB3を入力に使う
WPUB = 0b00001100 ;//RB2,3に内部プルアップ抵抗をつなぐ
PORTA = 0b00000000 ;//RAの初期値はすべてゼロ
PORTB = 0b00000000 ;//RBの初期値はすべてゼロ
// タイマー0設定
TMR0=99;//③タイマ0初期値初期値99
T0IF=0 ;//タイマ0割込フラグを0にする
T0IE=1;//タイマ0割り込み許可
PEIE=1;//周辺割り込み許可
GIE=1;//全体割り込み許可
while(1)
{
if (Event==0)//平常時
{
RB6=1; RB7=0; __delay_ms(1000);//RB6とRB7を
RB6=0; RB7=1; __delay_ms(1000);//1秒おきに交互に点灯
}
if (Event==1)//イベント時
{
RA1=1;//RA1を点灯
RB6=0; RB7=0;//翼端灯消灯
__delay_ms(5000);//5秒まつ
RA1=0;//RA1を消灯
Event=0;//イベント終了
}
}
}//メイン関数ここまで
//割り込み関数ここから
void __interrupt() xxx(void)
{
if(T0IF)//タイマ0による呼び出し
{
TMR0=99;//タイマ0初期値初期値99に戻す
if(RB3==0 && Event==0)//もしRB3が押されていて、イベントが起きていなかったら
{
Event=1;//イベントフラグ立てる
}
T0IF=0;//タイマ0による割り込みリセット
}
}//割り込み関数ここまで