イベントループやポーリング等を使って、イベントの検出や処理(これをハンドリングと言う)することは出来るが、プログラミングしていてイマイチ感がないだろうか?
もっとスマートな実装方法として、割り込みを使う方法を紹介する。
割り込みを使うと、イベントの検出はハードウエアがやってくれるので、イベントループは不要となる。
割り込みとは、何らかのイベント(スイッチ押したとか、シリアルからデータが届いたとか)が発生すると、これまで行っていた処理を中断して、イベントに対応した処理を呼び出すという一連の流れを言う。 割り込みにはハードウエアの助けが必須なので、ユーザプログラムが求めるイベントの全てに対応できるわけではないが、スイッチ操作やシリアル通信は割り込みのサポート対象になっている。 割り込みの詳細については、「割り込みとは何者ぞ」などを参照のこと。 |
シリアル通信で、割り込みを有効にするには、シリアル通信ユニット(USART)の初期化ルーチンを、以下の様にする。
// USARTの初期化 void usart_init() { // 通信速度の設定 (38400bps) UBRR1 = 25; // 受信ユニット、送信ユニットを起動、受信・送信バッファエンプティ割り込み有効 UCSR1B = _BV(RXEN1) | _BV(TXEN1)| _BV(RXCIE1) | _BV(UDRIE1); // ① // データ長=8bit、ストップビット=1ビット UCSR1C = (0 << USBS1) | 0b00000110; // すべての割り込みを有効 sei(); // ② } |
①_BV(RXCIE1) PCからデータを受け取った時に割り込み発生。
_BV(UDRIE1) 送信時に、UDR1レジスタが空になった時に割り込み発生。(つまり次のデータが書き込める状態)
②sei()
①の様に、割り込みには沢山の種類がある。①の設定は、個別の割り込みについての許可・禁止を設定している。
sei()関数は、①で設定した割り込み全体を有効にする。
反対に、すべての割り込みを禁止する場合は、cli()関数を呼ぶこと。
実際に割り込みが発生すると、事前に登録した関数(これを割り込みハンドラと呼ぶ)が呼び出される。これらの関数は、以下の様に記述する。
// データ送信時に、UDR1が空になることで発生する割り込みによって呼び出される関数 // データ受信時に発生する割り込みで呼び出される関数 |
【注意】
割り込みは、割り込みハンドラ実行中にも発生する。(これを多重割り込みという)何らかの処理を実行中に、他の割り込みが発生すると、不具合が生じる場合がある。この不具合が発生する処理の区間をクリティカルゾーンと言う。
クリティカルゾーン内では、割り込みの発生を禁止する必要があるが、必要最小限の範囲にすることが重要である。