前回までは何回かに分けて割り込み処理のお話をしてきましたが、今回は内容がガラッと変わってシリアル通信のお話を行います。ちなみに、Atmega328Pには一般的にシリアル通信と言われる「USART」のほかにも、「I2C」や「SPI」もありますが、今回紹介するには「USART」です。初めに、この3つの通信方式を筆者の偏見で軽くまとめてみます。
項目 |
USART |
I2C |
SPI |
使用箇所 |
主に機器,基板間 |
主に基板内 |
主に基板内 |
伝送可能距離 |
長い |
短い |
短い |
接続数 |
1対1が基本 |
1対複数が可能 |
1対複数が可能 |
通信線数 |
2本(TX,RX) |
2本(SDA,SCL) |
3本(SCK,MISO ,MOSI) +接続数(SS) |
安定性 |
高い |
UARTより低い |
UARTより低い |
通信速度 |
設定次第 |
設定次第 |
設定次第 |
筆者の感覚的にまとめるとざっとこんな感じです。「USART」と「I2C」「SPI」通信の最大の差は、1対複数のバス通信が規格としてサポートされているかいないかと言えるでしょう。しかし、引き換えに「I2C」と「SPI」は通信可能な距離が短く、距離が延びると安定性が非常に低くなります。筆者の経験上、I2Cは数十センチの距離でもモータなどのノイズでエラーが発生し正しく通信できません。(SPIはArduinoのシールド程度の距離でしか使用していないので不明です)Arduino系の分野では「I2C」はセンサなどとの通信、「SPI」はシールドとの通信に使われることが多いです。また、SPI通信はAVRマイコンのシリアル書き込みにも使われていますね。
そして、今回紹介する「USART」は基本的には1対1の通信ですが、工夫をすることにより、1対複数の通信も実装することは可能です。これにより比較的長い距離でも高い安定性で複数の子に対して通信を行うことも可能というわけです。なので、筆者はこの方法でモータ演奏基板の通信を行っています。(I2CとSPIは好みの問題で自分が実装する箇所には使っていません)
前置きはこの程度にしておいて、実際にAVRマイコンでのシリアル通信の使い方の紹介をしていきたいと思います。とはいえ、基本的にシリアル通信は理論的な個所はほぼなく、条件次第で通信速度設定時に計算を行う程度となっているので、今回はレジスタの設定をメインに紹介を行いたいと思います。
シリアル通信のレジスタの設定はatmega328Pのデータシート(https://avr.jp/user/DS/PDF/mega328P.pdf
)のP138~P141に書かれています。このうちシリアル通信の動作の設定を行うレジスタは「UCSR0A」「UCSR0B」「UCSR0C」の3つ、通信速度の設定を行うレジスタは「UBRR0」(12bit必要なのでデータシートではUBRR0LとUBRR0Hと分かれているが、プログラム上は一括で書ける)となっており、通信データが入るレジスタが「UDR0」となっています。
初めに設定のレジスタを見ていきましょう。
UCSR0A
ビット |
項目名 |
設定内容 |
設定値(書き込み) |
7 |
RXC |
USART受信完了フラグ:USARTのデータの受信が完了すると1が設定されます。受信完了割り込みを使わない場合は、このビットを読み込むことで、データが届いているかの判定を行います。受信完了割り込みを使う場合はこのビットは1にする必要があります。 |
受信完了割り込みを使う場合:1 使わない場合:0 |
6 |
TXC |
USART送信完了フラグ:USARTのデータの送信が完了すると1が設定されます。送信完了割り込みを使う場合はこのビットは1にする必要があります。 |
送信完了割り込みを使う場合:1 使わない場合:0 |
5 |
UDRE |
USART送信データレジスタ空きフラグ:送信のデータレジスタ(UDR0)がデータを受け取り可能な時に1が設定されます。データを送信するときはこのビットを読み込み、1であることを確認してから、データをUDR0に送ります。データレジスタ空割り込みを使う場合はこのビットは1にする必要があります。 |
データレジスタ空割り込みを使う場合:1 使わない場合:0 |
4 |
FE |
フレーミング異常フラグ:フレーミング異常が発生した時に1が設定されますが、特に使いません。 |
0 |
3 |
DOR |
データオーバーラン発生フラグ:オーバーラン状態が発生した時に1が設定されますが特に使いません。 |
0 |
2 |
UPE |
バリティー誤りフラグ:受信したデータがバリティーチェックを通らかった(データが破損している)場合に1が設定されます。Arduinoとの通信(関数を使う場合)では使えません。 |
0 |
1 |
U2X |
倍速許可:非同期動作でシリアル通信の速度を倍にする設定ですが、特に必要性がないので使いません。 |
倍速無効:0 倍速許可:1 |
0 |
MPCM |
複数プロセッサ通信動作:複数のマイコンを接続し到着データの選別を行う機能です。9bit通信が必須となり、Arduinoなどへの互換性確保が難しいので、今回は使いません。 |
無効:0 無効:1 |
UCSR0B
ビット |
項目名 |
設定内容 |
設定値(書き込み) |
7 |
RXCIE |
受信完了割り込み許可:受信完了の割り込みを許可するレジスタです。受信完了割り込みを使うときは、受信完了フラグ(RXC)に1を設定する必要があります。 |
受信完了割り込みを使う場合:1 使わない場合:0 |
6 |
TXCIE |
送信完了割り込み許可:送信完了の割り込みを許可するレジスタです。送信完了割り込みを使うときは、送信完了フラグ(TXC)に1を設定する必要があります。 |
送信完了割り込みを使う場合:1 使わない場合:0 |
5 |
UDRIE |
送信データレジスタ空き割り込み許可:データレジスタ空割り込みを許可するレジスタです。データレジスタ空割り込みを使うときは、送信データレジスタ空きフラグ(UDRE)に1を設定する必要があります。 |
データレジスタ空割り込みを使う場合:1 使わない場合:0 |
4 |
RXEN |
受信許可:シリアル通信の受信部の動作を許可します。シリアルでデータを受信する場合は1を設定しなければなりません。 |
データを受信する場合:1 受信を行わない場合:0 |
3 |
TXEN |
送信許可:シリアル通信の送信部の動作を許可します。シリアルでデータを送信する場合は1を設定しなければなりません。1対複数のシリアル通信を行う場合は、通常時は無効(0)にしないとIOが短絡するので注意が必要です。 |
データを送信する場合:1 送信を行わない場合:0 |
2 |
UCSZ2 |
データビット長選択2:詳細はUCSR0Cのビット1,2部で紹介 |
別途記載 |
1 |
RXB8 |
受信データビット8:9bitのシリアル通信を行う場合は受信データの9bit目をここから読み出します。なお、UDR0より先に読み出す必要があります |
0 |
0 |
TXB8 |
送信データビット8: 9bitのシリアル通信を行う場合は送信データの9bit目をここから書き込みます。なお、UDR0より先に書きこむ必要があります |
0 |
UCSR0C
ビット |
項目名 |
設定内容 |
設定値(書き込み) |
7
|
UMSEL1 |
USART動作選択:シリアル通信の動作を「非同期動作」「同期動作」「主装置SPI」から選択します。通常は「非同期動作」を選択します。 |
非同期動作:0,0 同期動作:0,1 主装置SPI:1,1 |
6 |
UMSEL0 |
||
5 |
UPM1
|
バリティー選択:バリティーチェックを「無効」「偶数バリティー」「奇数バリティー」から選択します。Arduinoなどと通信する場合は「無効」を選択します。 |
無効:0,0 (UPM1,0) 偶数:1,0 奇数:1,1 |
4 |
UPM0 |
||
3 |
USBS |
停止ビット選択:送信時に送られる停止ビットの長さを「1ビット」か「2ビット」から選択します。通常は、「1ビット」を選びます。 |
1ビット:0 2ビット:1 |
2 |
UCSZ1
|
データビット長選択:シリアル通信のデータのビット長の選択を行います。「5ビット」から「9ビット」で選択できますが、通常は「8ビット」を選択します。 |
5ビット:0,0,0 6ビット:0,0,1 7ビット:0,1,0 8ビット:0,1,1 9ビット:1,1,1(複数プロセッサ動作許可を有効時選択) |
1 |
UCSZ0 |
||
0 |
UCPOL |
クロック極性選択:同期動作の時にデータの送受信時のクロックの状態を選択します。非同期動作では0を書き込みます。 |
非同期動作:0 同期動作 送信時上昇端,受信時下降端:0 送信時下降端,受信時上昇端:1 |
※ビット2,1は主装置SPI動作の時は設定項目自体が変わります。設定項目名がビット2:UDORD(データ順選択)、ビット1:UCPHA:クロック位相選択に変わります。詳細はここでは省略します。
以上がシリアル通信(USART)の設定レジスタです。続いて、通信速度の設定レジスタの紹介をします。通信速度の設定レジスタは「UBRR0」です。このレジスタに値を設定することで通信速度が決定されます。このレジスタに入力する値はデータシートP136~P137のボートレート設定例の表から検索するか、データシートP126の数式を変換した以下の式で求めます。
この検索、計算した値を「UBRR0」レジスタに入力することで通信速度が設定できます。
最後にデータの送受信の方法ですが、やり方として2種類あり割り込みを使う方法と使わない方法に分けられます。
前者の割り込みを使う方法の場合は、設定レジスタで割り込みを使う設定(割り込み許可と完了フラグを1)にし、割り込みの関数内で受信時は「UDR0レジスタから読み取り」送信時は「UDR0レジスタに書き込み」を行います。
割り込みを使わない場合は、main関数の無限ループ内で各完了フラグが1に設定されているときに受信時は「UDR0レジスタから読み取り」送信時は「UDR0レジスタに書き込み」を行います。
サンプルプログラム
今回は割り込みを使った場合で、Arduinoからデータを受信するようにした場合のプログラムとなります。なお、ここでの設定した内容は、上の設定レジスタの表の設定値の太線の値となっています。
#include <avr/io.h>
#define F_CPU 16000000 //CPUクロック16Mhz
#define SPEED 38400//シリアル通信の速度
int main(void)
{
//シリアル通信設定
UBRR0 = ((long)F_CPU/((long)SPEED * 16)) - 1;
UCSR0A = 0x80;
UCSR0B = 0x98;
UCSR0C = 0x06;
sei();
/*
Replace with your application code */
while (1)
{
}
}
ISR(USART_RX_vect){//シリアル受信完了時
uint8_t data = 0; //受信データ
data = UDR0;
}
以上がサンプルプログラムとなります。今回はArduinoからのデータ受信を目的にプログラミングを行いました。今回まででモータを鳴らすための回路、プログラム設計に必要な基礎のお話はほぼ紹介したので、次回からは全体的な設計のお話を少ししたいと思います。そのあとまた気分次第でモータを鳴らすことにはつかない基礎的なお話もするかもしれないです。