目次
前
次
割込み処理
PICは、ワンチップマイコンなので内蔵している
ハードウエアモジュールが多数あります。
チップにもよりますが、28ピンであれば、以下の
ハードウエアモジュールが揃っています。
- timer0
- timer1
- timer2
- analog comparator
- A/D converter
これらのモジュールを使う場合、割込みを使うと
ファームウエアが単純になります。
PICは、割込みのエントリアドレスが0x004と固定で
このエントリーアドレスからのコードで割込み種別
を判定します。
この形式を採用しているマイコンは、ARMがあります。
割込みのエントリアドレスが、ひとつしかないと
いうので、Cで割込み処理コードを担当する関数は
ひとつでよくなります。
CCS社のCでは、次のようにプラグマ記述で個別の
割込み処理を定義します。
#int_rtcc
void timer_handler(void)
{
/* set TMR0 6 (256-250) */
set_timer0(6);
/* interval process */
state++ ;
if ( state == STA_LAST ) state = 0 ;
/* set flag */
tflag = ON ;
}
#int_rda
void echo_handler(void)
{
UBYTE chx ;
/* get one character */
chx = rcreg ;
sbuf[s_idx] = chx ;
/* update buffer index */
s_idx++ ;
if ( s_idx == SMAX ) s_idx = 0 ;
/* judge */
if ( chx == '\r' ) {
sflag = ON ;
s_idx = 0 ;
}
}
エントリー用の関数はなく、ハードウエアモジュール
専用の関数を定義して対応します。
MikroCでは、エントリー関数を用意して、その中に
ハードウエアモジュールの動作を記述します。
例は、以下となります。
void interrupt(void)
{
/* generate 1ms */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = 6 ;
/* increment */
timcnt++ ;
/* judge 10ms */
timv = (UBYTE)(timcnt & MASK0F) ;
if ( timv == LAST ) {
/* set flag */
tflag = ON ;
}
}
}
割込みを要求したハードウエアモジュールの判断は
レジスタのフラグがセット、リセットで判断します。
PICでは、ハードウエアモジュールの初期化と割込みの
有無を指定後、全体割込みを許可します。全体割込み
を許可しないと、いずれのハードウエアモジュールから
も割込みは、発生しません。
これは、データシートの回路図を見るとわかります。
(PIC16F84Aのデータシート上の割込み関係回路図)
割込みをイネーブルと割込みフラグの2ビットを
論理積でまとめて管理しています。GIEビットを
セットすると、CPUコアに対して割込み信号が伝達
される仕組み。
CCSのCは、論理積部分の内容をプラグマで記述し
MikroCは、全体をひとつの関数にまとめています。
SDCCは、MikroCと同じ方式を採用しています。
関数名は、好きにつけてよいのですが、定義の
後に「interrupt 0」をつけます。
次のように記述します。
static void int_tim0() interrupt 0
{
/* clear interrupt flag */
T0IF = 0 ;
/* ????? */
}
全体割込みをイネーブルするまでの初期化は
次のように記述します。
void init_usr(void)
{
/* I/O initial state */
/* I/O direction */
/* disable compare module */
/* pull-up */
WPU = 0x14 ;
/* initialize A/D converter */
{
/* select A/D bits */
/* enable A/D converter */
/* right justify */
/* select reference voltage */
/* select A/D frequency */
}
/* initialize serial interface */
{
/* BAUD rate */
/* initialize TxD module */
/* initialize RxD module */
/* enable receive interrupt */
}
/* initialize Timer 0 */
{
/* set prescaler */
/* initialie counter */
/* enable timer0 overflow interrupt */
}
/* enable general interrupt */
}
割込みに関連するレジスタとビットがどうなって
いるのかは、チップごとに異なるのでデータシート
を丹念に読んで理解します。
タイマー割込み応用
タイマー割込みを利用すると、時間を正確に
管理できます。応用している2例を紹介します。
タイマー0は、オーバーフロー割込みができるので
これを利用して、システムタイマーを実現します。
1ms周期で発生する割込みを定義し、カウンタを
インクリメントしていくと、32ビットカウンタ
で、約50日の時間を1msの分解能で実現できます。
MikroCで、PIC16F629を使ったコードを公開します。
カウンタ定義
typedef unsigned char UBYTE ;
typedef unsigned int UWORD ;
typedef unsigned long ULONG ;
ULONG timcnt ;
初期化
/* initialize Timer 0 */
{
/*
4MHz/4 = 1MHz -> 1MHz/4 = 250kHz prescaler = 1:4
all inputs are pull-up .
*/
OPTION_REG = 0x01 ;
/* 256 - 250 = 6 */
TMR0 = 6 ;
/* enable timer0 overflow interrupt */
INTCON.T0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
割込み処理
void interrupt(void)
{
/* generate 1ms */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = 6 ;
/* increment */
timcnt++ ;
}
}
1msディレイ処理関数
void delay_ms(UWORD x)
{
ULONG target ;
/* calculate */
target = timcnt + x ;
/* */
while ( timcnt < target ) ;
}
1msディレイ処理関数は、最大で65535msまでの
待ちを生成できます。ただし、待っている最中
他のことができないので、10ms程度が利用範囲
内だと思います。
スイッチを利用する場合、チャタリング除去が
必要になります。10ms程度で通知してもらって
スイッチの状態を読み込んで、判定すると簡単
にチャタリングを除去できます。
チャタリング除去では、シフトレジスタを使い
シフトレジスタの2ビットあるいは3ビットの
値で、スイッチの状態を判断します。10ms経過
はフラグで通知します。
シフトレジスタ定義
typedef unsigned char UBYTE ;
typedef unsigned int UWORD ;
typedef unsigned long ULONG ;
UBYTE sflag ;
ULONG timcnt ;
UBYTE timv ;
UBYTE trg_sft ;
初期化
void init_usr(void)
{
/* I/O state */
GPIO = 0x00 ;
/* I/O directions */
TRISIO = 0x1c ; /* bit2,3,4 as input , others as output */
/* disable compare module */
CMCON0 = 0x07 ;
/* pull-up */
WPU = 0x14 ;
/* initialize Timer 0 */
{
trg_sft = 0 ;
/*
4MHz/4 = 1MHz -> 1MHz/4 = 250kHz prescaler = 1:4
all inputs are pull-up .
*/
OPTION_REG = 0x01 ;
/* 256 - 250 = 6 */
TMR0 = 6 ;
/* enable timer0 overflow interrupt */
INTCON.T0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
/* clear flag */
sflag = OFF ;
}
割込み処理
void interrupt(void) {
/* generate 1ms */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = 6 ;
/* increment */
timcnt++ ;
/* get lower 8 bits */
timv = timcnt & MASKFF ;
/* judge */
if ( (timv & 15) == 10 ) {
/* set flag each 10ms */
sflag = ON ;
}
}
}
チャタリング除去処理
/* switch handling */
if ( sflag == ON ) {
/* clear flag */
sflag = OFF ;
/* get pin state */
trg_sft <<= 1 ;
trg_sft &= MASK03 ;
if ( GPIO.B4 == OFF ) { trg_sft |= ON ; }
/* debouncing */
if ( trg_sft == 0x01 ) {
/* ????? */
}
}
目次
前
次