AVRマイコンのシリアル通信

 シリアル通信は、以下の手順で動作する。

 (1) UDRレジスタにデータを書き込む。(UDRにデータを書き込む際は、TXC1ビットを確認してから行うこと)
 (2) 書き込まれたデータ(8bit幅)は、シフトレジスタにコピーされる。この時、スタートビットとストップビットが前後に付加される。
 (3) 通信速度(ボーレート ~ bit/sec)に従って、シフトレジスタの値が、1ビットずつ、信号線に押し出される。

 (4) 受信側では、信号線を通じて受け取ったデータを、順番にシフトレジスタに挿入する。
 (5) ストップビットまでデータを受け取ると、スタートビット・ストップビットを取り払ったデータをUDRにコピーする。
 (6) UDRレジスタの値を読み取る。(RXC1ビットを確認してから読み取ること)
   ※1  ストップビットのビット長は、1 or 2 ビット。スタートビットのビット長は1ビット。
       ※2  ストップビットの直前に、パリティビットが付加される場合もある。パリティビットについては、後述。



UDRレジスタにデータを書き込む場合は、UCSR1AレジスタのRXC1ビットとTXC1ビットに注意すること。
送信が完了していない状態でUDRにデータを書き込むと、データが上書きされてしまう。
例えば、以下のように、0b10110001 → 0b11111101 → 0b00011101の順で連続してデータを送信する場合を考えてみると、

           

  •  一番目のデータ:UDR、シフトレジスタとも空なので、すぐにデータが書き込める。
  • 2番目のデータ: 最初のデータはUDRに書き込み後、すぐにシフトレジスタにコピーされる(=UDRは空)となるので、UDRに書き込んでも問題ない。
  • 3番目のデータ: シフトレジスタ、UDRとも使用中なので、UDRに書き込みを行うと、2番目のデータが上書きされて消える。

 となり、問題が起こる。このような事態を避けるため、UCSR1Aレジスタのビットをチェックすることが重要となる。
 


 

シリアル通信に関連するレジスタ

1.通信速度の設定(UBRR1)
 ○非同期ノーマルモード(通常の通信)では、以下の計算式で求めた値をUBRR1レジスタに設定する。(UCSR1A:U2X1=0)
             
   BAUD:通信速度(300、1200、2400、4800、9600、19200、38400、57600 ....)
 
 ○非同期倍速モードでは、以下の計算式で求めた値をUBRR1レジスタに設定する。(UCSR1A:U2X1=1)
             
   BAUD:通信速度(300、1200、2400、4800、9600、19200、38400、57600 ....)

 ※foscはマイコンのクロック周波数。 ”CLKPR = 0x80; CLKPR = 0; ”の設定をしているなら、16MHzなので、16000000になる。
 ※UBRRnのnは、1に読み替える。

2.設定レジスタ
 シリアル通信に係るレジスタは、UBRR1以外に、以下の3つがある。

(1) UCSR1A

  bit7 bit6 bit5 bit4 but3 bit2 bit1 bit0
ビットの名前 RXC1 TXC1 UDRE1 FE1 DOR1 UPE1 U2X1 MPCM1
初期値 0 0 1 0 0 0 0 0

  ○RXC1:受信完了 ~ このビットが1になった時に、受信データがUDRレジスタにセットされる。
  ○TXC1:送信完了 ~ 送信側のシフトレジスタのデータについて送信が完了。
  ○UDRE1:送信バッファが空 ~ UDR1レジスタに送信データを書き込み可能。
  ○FE1:フレーミングエラー ~ 受信したデータにフレーミングエラー(壊れたデータを受け取った)場合に、ビットが1になる。
  ○DOR1:データオーバラン ~ 受信したデータを処理する前に、次のデータを受け取ってしまった場合に発生。ビットが1。
  ○UPE:パリティエラー ~ 受信データとパリティが不一致の場合に発生。ビットが1。
  ○U2X1:非同期ノーマルモード(= 0)、非同期倍速モード(= 1)の切り替え。通常はノーマルモード。
  ○MPCM1:マルチプロセッサ通信モード。(とりあえず、使わないかな)

(2) UCSR1B

  bit7 bit6 bit5 bit4 but3 bit2 bit1 bit0
ビットの名前 RXCIE1 TXCIE1 UDRIE1 RXEN1 TXEN1 UCSZ12 RXB81 TXB81
初期値 0 0 0 0 0 0 0 0

  ○RXCIE1:このビットを1に設定すると、データ受信時に割り込みが発生。
  ○TXCIE1:このビットを1に設定すると、データ送信完了時に割り込みが発生。
  ○UDRIE1:このビットを1に設定すると、UDR1レジスタが空になった時に割り込みが発生。
  ○RXEN1:受信モジュールを開始
  ○TXEN1:送信モジュールを開始
  ○UCSZ12:UCSR1C:UCSZ11、UCSR1C:UCSZ10の3つのビットを使ってデータ長を設定する。

UCSR1B:UCSZ12 UCSR1C:UCSZ11 UCSR1C:UCSZ10 データ長
0 0 0 5ビット
0 0 1 6ビット
0 1 0 7ビット
0 1 1 8ビット
1 0 0 予約
1 0 1 予約

  ○RXB81:9番目のビット(受信データの)を受け取る場合に1
  ○TXB81:9番目のビット(送信データの)を送信する場合に1

(3) UCSR1C:UCSZ11

  bit7 bit6 bit5 bit4 but3 bit2 bit1 bit0
ビットの名前 UMSEL11 UMSEL10 UPM11 UPM10 USBS1 UCSZ11 UCSZ00 UCPOL1
初期値 0 0 0 0 0 1 1 0

  ○UMSEL1とUMSEL0の設定で、シリアル通信のモードを設定する。

UMSEL11 UMSEL10 モード
0 0 非同期モード
0 1 同期モード
1 0 予約
1 1 マスタSPIモード

  ○UPM11とUPM10の設定で、パリティモードを設定する。

UPM11 UPM10 パリティモード
0 0 無効
0 1 予約
1 0 偶数パリティ
1 1 奇数パリティ

  ○USBS1でストップビット長を設定する。 0でストップビット長が1ビット、1で2ビットになる。
  ○UCSZ11、UCSZ10: 前述
  ○UCPOL1:クロックの極性(同期モードでのみ有効) クロックの立ち上がり、立下りエッジのいずれかを選択する。
 



プログラミングの実際

プログラムの概略をフローチャートで書くと、以下の様になる。


       受信側のフロー                               送信側のフロー

※このフローチャートでは、それぞれに初期化処理があるが、送受信機能を1つのプログラムに実装するなら、どちらかは
 省略できる。(逆に言うと、送信のデータ長を8bitで、受信のそれを7bitなんて設定は出来ないということ)

○実装例

/*
  * 0.1秒ごとにPCに文字'.'を送信する。
  * 使ってないけど、受信の処理(関数)も実装してある。
  */
#define F_CPU 16000000UL  // 16MHz動作 _delay_ms()などに関係

#include < avr/io.h >
#include < util/delay.h >
#include < avr/interrupt.h >

// 一文字受信(PC → AVR)
unsigned char getch(void)
{
    while(!(UCSR1A & _BV(RXC1))) ; // 下の行は、このようにも書ける。
    while((UCSR1A & 0b10000000) == 0b00000000) ; // UCSR1AレジスタのRXC1ビットが1になると、データの受信が完了し、
                                                  // UDR1レジスタにデータが保管される
                                                  //  0の場合はデータ無し
    return UDR1;   // データ受信の場合は、UDR1レジスタを読み取る
}

// 一文字送信(AVR → PC)
void putch(unsigned char c)
{
//    while(!(UCSR1A & _BV(UDRE1))) ;  // 下の行は、このようにも書ける。
    while((UCSR1A & 0b00100000) == 0b00000000) ;  // UCSR1AレジスタのUDRE1ビットが0の間は
                                                    // UDR1が使用中で、このビットが1になったら使用可と
                                                    // なるので、UDR1レジスタに送信データを書き込む
    UDR1 = c;  // データ送信の場合は、送信データをUDR1レジスタに書き込む
}

// シリアル通信ユニット(USART)の初期化
void usart_init()
{
    // 通信速度の設定
    //     通信速度はCPUのクロック周波数によって値が変わる
    //     計算式は、 F_CPU / (16 * 通信速度) - 1
    //         得られた値を、UBRR1に設定する。
    UBRR1 = 25;  // 25: 38400bps @ 16MHz

    // 送信ユニット、受信ユニットを有効にする
    UCSR1B = (1 << RXEN1)|(1 << TXEN1);

    // 通信モードの設定
    //      データ長 8 bit
    //      ストップビット長  1bit
    UCSR1C = (0 << USBS1) | 0b00000110;
}

int main()
{
    CLKPR = 0x80; CLKPR = 0;    // 16MHz動作

    usart_init();

    while(1) {
        putch('.');
        _delay_ms(100);
    }
}


※ _BV()とは、ビット演算を簡単にするための補助的関数で、引数の値で示されるビットを1にして返す関数。

    char    bit;

    bit = _BV(5);     // この時、bitの値は、 0b00100000となる。

 



PCとの接続

 PCとの接続は、USB・シリアル変換ケーブルを用いて行う。ケーブルのUSBコネクタをPC前面のUSB端子に挿入する。また、反対側のプラスチックのモールド端子(下図参照)を、AVRマイコンの各ピンに接続する。

                
              図 AVRマイコンのピン配置             図 ケーブルのピン配置

 実際の接続は、以下の様に行う。
         



PCとの通信

 

 WindowsメニューからTERA TERMを立ち上げ、「ファイル」→「新しい接続」を選択。以下のメニューが表示される。

            
「シリアル」 → 「COM?」: USB Serial Port(COM?)を選択する。

           

また、上図の様に「設定」→「シリアルポート」→「ボーレート」の順でメニューを選択し、通信速度を38400bpsに設定する。

これで、AVRマイコンと通信できる。

最終更新:2016年02月18日 10:02