読者です 読者をやめる 読者になる 読者になる

割り込みを体験する #1

ARM

前回のかなり質素なプログラムから少しステップアップを図るべく、今回は、割り込みを確認するプログラムを作成してみます。

割り込み処理は、組込みソフト開発の特徴を挙げる際、他の分野のソフトウェア開発と異なる点としてよく引き合いに出されると思います。

それほど重要でかつ基本的な仕組みなので、取り上げてみようと思います。

また、割り込みソースにタイマを使用して、その動作原理も併せて確認してみます。

なお、動作環境は前回同様、Bishopエミュレータを利用します。

GOAL

以下の要件で割り込みを確認します。

  • 割り込みを発生させるモジュールにはCPU(S3C2440AC)内臓のタイマを使用。
    • タイマは0〜3の4チャネルありますが、今回はタイマ0を使用します
  • ミリsecオーダのインターバルで割り込みを生成。
  • 割り込み処理内で割り込み発生をカウントし、1秒毎にUARTに文字を送出する事で確認。

タイマの設定

タイマ割り込みの概要

今回の割り込みの発生源はタイマです。
割り込み設定を行った際のタイマの動作概要は以下のようになります。

  • タイマの管理を行うタイマコントローラにはクロック供給がされており、
  • そのクロック周波数をタイマコントローラ内で逓倍して
  • 予めタイマコントローラに設定されたタイマカウントを逓倍された周波数でカウントダウン
  • 予めタイマコントローラに設定された比較値とタイマカウントが一致した際に割り込み発生
  • 自動カウンタリセットが設定されている場合はタイマカウントが設定値に戻る

従って、目的の間隔に応じた供給クロックの逓倍設定、カウント値の設定が必要になります。

タイマ割り込み初期化処理

今回、周期の間隔は、4msに設定してみます。
これまでを踏まえたタイマの処理概要は以下の通りです。

  • タイマコントローラの事前設定
  1. タイマコントローラへのクロック供給(pclk=50Mhz)を1/16分割する。
    • 50(Mhz)/16 = 3.125Mhz駆動
  2. 分割されたクロックパルス回数が125(0x7d)に達したらタイマカウントを減らすよう設定。
    • 3.125(Mhz)/125 = 25Khz、つまり40μsecでタイマカウントをデクリメント
  3. タイマカウントを100に設定
    • 40μsec x 100 = 4msec
  4. タイマカウント比較値を0に設定
    • タイマカウントがこの値と一致した場合に割り込み発生
  • 割り込みコントローラの設定
  1. 割り込み保留レジスタのタイマ0の設定をクリア
    • 割り込みが要因フラグ。で割り込みが発生した際にフラグが立つ(モジュール単位)。
    • クリアしないと割り込みが発生し続ける事になる。
    • 念のため初期化。
  2. 割り込み発生源保留レジスタのタイマ0の設定をクリア
    • 割り込み保留レジスタが割り込みマスクと優先度が評価された後の割り込み状態に対して、発生源はそれらが評価される前の割り込み状態。
    • 同様にクリアしないと割り込みが発生し続ける事になる。
    • 同様に念のため初期化。
  3. 割り込みマスクレジスタをクリア
    • 割り込み発生源からの割り込みマスクを解除する。
    • マスクされていれば発生源から割り込みが通知されても文字通りマスクされ無視される。
  • タイマコントローラの最終設定
  1. タイマを有効にする。且つ、タイマカウントが自動にリセットされるように設定。
タイマ割り込み初期化関数

C言語での初期化関数は以下のようにしました。

void init_timer(void)
{
	register volatile unsigned long tmp;

	// divider setting (1/16) 
	tmp = read_reg32(TCFG1);
	tmp |= 3;
	write_reg32(TCFG1, tmp);

	// prescale setting (for 25Khz)
	write_reg32(TCFG0, 0x7d);

	// counter settings (for 4ms)
	write_reg32(TCNTB0, 100);
	write_reg32(TCMPB0, 0);

	// clearing interrupt pending
	write_reg32(INTPND, INT_TIMER0);
	write_reg32(SRCPND, INT_TIMER0);

	// Unmask timer0 interrupt
	tmp = read_reg32(INTMSK);
	tmp &= ~INT_TIMER0;
	write_reg32(INTMSK, tmp);

	// Enable timer0 w/ auto reload
	write_reg32(TCON, 0xf);
}

write_reg32/read_reg32はレジスタアクセス用のマクロです。別で以下のように定義しました。

#define read_reg32(reg)       (*(volatile unsigned long * const)(reg))
#define write_reg32(reg, val) (*(volatile unsigned long * const)(reg)) = (val)

引数に設定しているINT_TIMER0以外の大文字定数はレジスタのアドレスです。定数宣言は省略します。
INT_TIMER0は割り込みコントローラの各レジスタにおける、タイマ0の番号です。
以下のように定義しています。

#define INT_TIMER0		(1<<10)

これでタイマの設定、及びタイマ割り込みのための設定が完了しました。

タイマ割り込み処理

次に、実際にタイマ割り込みが発生した際の処理に関してです。

今回では以下のような処理の流れになると思います。

  1. 割り込み発生回数をカウントアップ
  2. 250回以上発生したならば、UARTに文字送信して発生回数を0に戻す
    • 4ms x 250 = 1sec
  3. 割り込み保留レジスタのタイマ0の設定をクリア
  4. 割り込み発生源保留レジスタのタイマ0の設定をクリア

実りある処理を行わないためシンプルです。
C言語での割り込み処理は以下のようにしました。

void isr_timer(void)
{
    static unsigned int count = 0;
	register volatile unsigned long tmp;

	tmp = read_reg32(INTOFFSET);
	if ( (1<<tmp) & INT_TIMER0 ) {
		count++;
		if ( 250 < count ) {
			uart_put('.'); //Write char to UART  
			count = 0;
		}

		// Interrupt done.
		write_reg32(INTPND, INT_TIMER0);
		write_reg32(SRCPND, INT_TIMER0);
	}	
}

処理の先頭で値を取得しているINTOFFSETは割り込みが発生したモジュールの割り込みコントローラのオフセットです。
念のため、タイマ0である事を確認しています。


変数countは割り込み発生数をカウントする変数です。
putc関数はUARTへの文字送出関数です。


これでタイマ割り込みの際の処理が完成しました。


長くなってしまったので

今回はここまでとします。
次回はスタートアップコードと上記の関数を呼び出すまでの処理について書きたいと思います。