シリアル受信割り込み

UARTでPCなどからシリアルデータを大量に受信したいとき、
1バイトずつcGetChar()していると、処理が間に合わずにデータを取りこぼすことがあります。
そのような場合は、(1)シリアル受信はメイン関数とは独立に、データを受信したらバッファにためていく、(2)メイン関数はシリアル受信を行わず、結果だけバッファを参照する、という構成として、シリアルデータ受信とメイン関数の処理を分離し、バッファ(メモリ)を介して受信データを読む、ということをやると効果的です。
この(1)のためには、「シリアルデータを受信した」場合のみに起こる「割り込み」によって、メイン関数の処理を中断し、「受信したデータをバッファにためる」処理だけを行ってメイン関数に戻る、とします。(※割り込みについては「チュートリアル/PSoC1duino_マトリクスLED編」を参照)

シリアル受信割り込み

ユーザモジュールRX8を使う場合の例を示します。

メイン関数

#define RX_BUFFER_SIZE 256
BYTE rx_buf[RX_BUFFER_SIZE];
BYTE pReceive = 0, pRead = 0;
#pragma interrupt_handler RX8_ISR_C
void RX8_ISR_C(){
  rx_buf[pReceive++] = RX8_cGetChar();
  if (pReceive == RX_BUFFER_SIZE) pReceive = 0;
}
...
main(){
  ...
  RX8_EnableInt(); // enable RX8 interrupt
  M8C_EnableGInt;  // enable global interrupt

これは、RX8で1バイトのデータ受信が起こったときに呼ばれる関数「RX8_ISR_C()」を定義しています。その中身は、受信したデータをrx_buffer[]に入れていくわけですが、その位置をpReceiveという変数で指定しています。1バイト受信するごとにpReceiveは1増え、RX_BUFFER_SIZE(=256)になったら0に戻りますので、この256バイトのrx_buffer[]に、先頭→末尾→先頭に戻る、という順序で、いわゆるラウンドロビン式に受信データがたまっていきます。

なおmain()内では、この2つの関数でシリアル受信割り込みが起こるように設定しておきます。

受信したデータを読み出す関数

int RX8_Receive(){
  BYTE d:
  if (pReceive == pRead) return(-1); // no received data
  else{
    d = rx_buffer[pRead++];
    if (pRead == RX_BUFFER_SIZE) pRead = 0;
    return((int)d);
  } 
}

これは、受信したデータを読み出したいときに使う関数です。
といっても受信したデータはrx_bufferに保存されていますので、
その中身を参照することになります。
いま読みだそうとしている場所(ポインタ)をpReadとしておきます。
pReceiveは、その時点でrx_bufferに受信されているデータの位置ですから、pRead=pReceiveならば、読み出すべきデータがない(前回このRX8_Receive()でrx_bufferを読んだあと、シリアルデータは受信されていない)ことになるので、-1を返します。
pReadとpReceiveが異なるならば、前回このRX8_Receive()でrx_bufferを読んだとき以降に受信したシリアルデータがrx_bufferに書き込まれている、ということですから、前回読んだ位置の次の1バイトを返します。このとき、読み出し位置を示すpReadを1加え、RX_BUFFER_SIZE(rx_bufferの最後)になったら先頭に戻す(ラウンドロビン)を忘れずに。

RX8INT.asm

_RX8_3_ISR:
  ;@PSoC_UserCode_BODY@ (Do not change this line.)
  ;---------------------------------------------------
  ; Insert your custom assembly code below this banner
  ;---------------------------------------------------
  ;   NOTE: interrupt service routines must preserve
  ;   the values of the A and X CPU registers.
  ;---------------------------------------------------
  ; Insert your custom assembly code above this banner
  ;---------------------------------------------------
  ljmp _RX8_ISR_C ; add this line
  ;---------------------------------------------------
  ; Insert a lcall to a C function below this banner
  ; and un-comment the lines between these banners
  ;---------------------------------------------------

さてRX8でシリアルデータが受信されたときに、上で定義したRX8_ISR_C()が呼び出される必要があるわけですが、これは、ちょっと読みにくいですがRX8INT.asm(ファイル名はモジュール名RX8に"INT.asm"をつけたもの)に、上記の"add this line"の行を追加する必要があります。
これは、RX8でシリアルデータが受信された(受信割り込みが起こった)ときに呼び出される部分で、ここで、自分のプログラム内で呼び出したい関数名「_RX8_ISR_C」に分岐(ljmp)する、という動作を記述ししておきます。(この*.asmファイル内では、Cプログラムの関数名の先頭に「_」をつける、という規則)

以上で、

  • シリアルデータを受信したら(受信割り込みが起こったら)RX8_ISR_C()が呼ばれ、受信データをrx_bufferに順に格納していく
  • 必要な時にRX8_Receice()を呼べば、受信データがなければ-1、あれば受信データが返る
    という動作ができます。しかもシリアル受信の動作自体はメイン関数とは独立に割り込みで適宜行われるため、受信データを取りこぼす心配もありません。

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2022-10-02 (日) 11:12:58