割り込みオブ割り込み、外部割込み(
IRQ:
Interrupt
Re
Quest)の設定を行います。
割り込みを使う理由と概念はこちら:
割り込み処理とは外部割込みに設定した端子に入力が加わると、メイン処理はそっちのけで特定の関数を実行します。
高速で出力されるパルス数のカウントやパルスの周期測定などに用います。
私は超音波の発信から受信までの時間測定に使用しました。
また、音の様に一瞬だけ出力信号も検出できることから、100均で売ってるタイマーのブザーから信号を取り出す時にも使用しています。
端子の選定
外部入力割り込み可能な端子はIRQ表記があります
PA2:IRQ0
PA3:IRQ1
PA4:IRQ2
PA5:IRQ3
このうち
PA3、
PA4はシリアル通信で使っています。
プログラムの書き込みにも
PA3、
PA4を使用しRS232C用の電圧変換ICも接続されているので熟知するまでは他の目的では使用するのはやめた方が良いでしょう。
従って、使用可能なのは
PA2と
PA5の2つです。
初期化設定
ソースファイル「
intIRQ.c」を作り割り込み処理に必要なプログラムを書いていきます。
int _intrupt_f;
static unsigned long _intrupt_count[4];
void init_IRQ(void)
{
int i=0;
PFC.PACRL2.BIT.PA5MD=7;
//PA5を外部割込みに設定 PFC.PACRL1.BIT.PA2MD=3;
//PA2を外部割込みに設定 INTC.IRQCR.BIT.IRQ0S=2;
//立上り:2 立下り:1 INTC.IRQCR.BIT.IRQ3S=2;
//立上り:2 立下り:1 // SCIの割り込みレベル(優先順位)を設定 INTC.IPRA.BIT._IRQ0 = 0x9;
// ※(0x0:最低~0xF:最高) INTC.IPRA.BIT._IRQ3 = 0x8;
// ※(0x0:最低~0xF:最高) _intrupt_f=0;
for(i=0;i<4;i++){
_intrupt_count[i]=0;
// カウント値を初期化 }
INTC.IRQSR.BIT.IRQ0F=0;
// IRQ割込フラグをクリア INTC.IRQSR.BIT.IRQ3F=0;
}
IRQCR:IRQコントロールレジスタ
割り込みの条件(立ち上がり検出、立下り検出等)を設定します。
信号がOFFからONに変わることを
立ち上がり、ONからOFFに変わることを
立下りと呼びます。
IRQSR:IRQステータスレジスタ
IRQ端子の割り込み許可と状態取得をします。
IRQ0F、
IRQ3F:それぞれ0にすると割り込みが機能するようになります。
ついでに割り込みが発生したか分かるように
_intrupt_f、割り込みが発生した回数が分かるように
_intrupt_count_0、
_intrupt_count_3を作成しておきます。
割り込み動作の有効化
PWMの時と同様に初期状態では割り込みを受け付けないように設定されているので、割り込みを有効化しておきます。
resetprg.cの36行目付近
#define SR_Init 0x000000
F0
を
#define SR_Init 0x000000
00
へ変更します。
外部入力割込み関数
IRQ0による割り込みIRQ0は
ベクタ番号64にて実行されます。
IRQ3による割り込みIRQ3は
ベクタ番号67にて実行されます。
これらはそれぞれ
vecttbl.cに記述されており、最終的には
intprg.cの
INT_IRQ0、
INT_IRQ3が実行されます。
intprg.cで
INT_IRQ0、
INT_IRQ3を検索すると関数の実体が見つかりますが、
// 64 Interrupt IRQ0void
INT_IRQ0(void){
/* sleep(); */}
// 67 Interrupt IRQ0void
INT_IRQ3(void){
/* sleep(); */}
となっており、相変わらず関数の中身は
コメントしかないので結局
何も実行されません。
とりあえず割り込みが発生した回数をカウントする関数を作成します。
//IRQ0割り込みvoid IRQ_INT_IRQ0(void)
{
_intrupt_f|=1;
_intrupt_count[0]++;
INTC.IRQSR.BIT.IRQ0F=0;
}
//IRQ3割り込みvoid IRQ_INT_IRQ3(void)
{
_intrupt_f|=8;
_intrupt_count[3]++;
INTC.IRQSR.BIT.IRQ3F=0;
}
この割り込み関数はとてもシンプルです。
立ち上がりを検出した瞬間にINT_IRQ0が実行される、ただそれだけです。
次の立ち上がりを検出するためにIRQ0Fをもう一度0にします。
これでまた次の立ち上がりを検出できます。
忘れないうちにintprg.cの
INT_IRQ0、
INT_IRQ3を編集しておきます。
void
INT_IRQ0(void){
IRQ_INT_IRQ0();}
void
INT_IRQ3(void){
IRQ_INT_IRQ3();}
情報取得関数
最後に_intrupt_fと_intrupt_countを別のファイルからアクセスできるように関数を作成します。
//割り込み検出を取得するint get_intrupt_f(void){
return _intrupt_f;
}
//割り込み検出を初期化するint clr_intrupt_f(void){
_intrupt_f=0;
}
//割り込み回数を取得するunsigned long get_irq_cnt(int port){
if(port<0||port>3)
return 0;
return _intrupt_count[port];
}
ヘッダファイル
ヘッダファイル「
intIRQ.h」は関数のプロトタイプ宣言を書いておきます。
#if !defined _INTIRQ_H_
//読み込まれていない(_INTIRQ_H_が定義されていない)とき読み込む#define _INTIRQ_H_ 0
void init_IRQ(void);
void IRQ_INT_IRQ0(void);
void IRQ_INT_IRQ3(void);
int get_intrupt_f(void);
int clr_intrupt_f(void);
unsigned long get_irq_cnt(int port);
#endif
作成したソースファイル「
intIRQ.c」とヘッダファイル「
intIRQ.h」は
プロジェクトに追加しておきます。
メインプログラム
シリアル通信を使って割り込みの結果を確認してみます。
今回作成した「
intIRQ.c」「
intIRQ.h」の他に汎用IO設定(
io_setup.c、
io_setup.h)タイマー処理一式(
intCMT.c、
intCMT.h)とシリアル通信一式(
intSCI.c、
intSCI.h)を使用します。
メインプログラム#include "iodefine.h"
#include <stdio.h>
#include "io_setup.h"
#include "intCMT.h"
#include "intSCI.h"
#include "intIRQ.h"
void main(void){
//変数定義 char trans_buf[100];
unsigned long timer;
//ループ制御用タイマー int i;
//各機能の初期化
hardware_setup();
//汎用IOの初期化 init_CMT0(1);
//タイマーの初期化 init_SCI1();
//シリアル通信の初期化 init_IRQ();
timer=getsystime();
//現在時刻の取得(intCMT) write_sci1("sh7125 connect\n");
while(1){
//無限ループ開始 if(timer<getsystime()){
//タイマー設定時刻を経過 timer=getsystime()+50;
//50ms後に設定 if(get_intrupt_f()){
//外部割込みが発生していた clr_intrupt_f();
//フラグを解除 sprintf(trans_buf,"%d,%d\n",get_irq_cnt(0),get_irq_cnt(3));
//割り込み回数を文字列に格納 write_sci1(trans_buf);
//通信で出力 }
//intrupt_f }
//50ms }
//end while}
書き込んで実行してみます。
シリアル通信受信はいつものTMZあたりで。
起動時は「sh7125 connect」と数字が幾つかが表示され、あとはPA2,PA5当たりを触ると
指をアンテナにして割り込みが発生しカウント数がポロポロ表示されるはずです。
これで高速で入力される信号をカウントすることができますが、
次回はもう少し機能を拡張します。