USBシリアル通信サンプル

USB-シリアル変換モジュールを使って、PCとAVRで通信ができるようにする。
AVRの通信というと、UART(USART)やら、I2Cやら、SPIやらという単語が出てくるけど、それぞれの位置づけがよくわかっていない。

今回使うUART(USART)は、送信と受信の2つのポートでデータのやり取りをする方式で、基本的には1対1の通信なのかな?
I2Cってのは、同じ2本だけど、複数繋いで、それぞれを識別してデータを送れる、バス通信の方式なのかな?
SPIは4本使って、複数繋いで、I2Cより速くデータを送れるバス通信の方式?

AVRはこれらの方式をサポートしていて、それぞれ違うレジスタとポートを使って、機能を実現するっぽい。
UART(USART)はRXD(PD0)とTXD(PD1)、I2CはSCL(PC5)とSDA(PC4)、SPIはSCK(PB5)とMISO(PB4)とMOSI(PB3)とSS(PB2)なので、使う機能によって繋げる場所を変えないといけないということか。

秋月のUSB-シリアル変換モジュールをPCのUSBに繋ぐと、まず、ドライバのインストールをする必要がある。
FT232RのUSB UARTドライバと、USBシリアルポートドライバの2つ。

ドライバは、以下のページからダウンロードできる。
FTDI社のドライバダウンロードページ

VCPとD2XXというのがあるけど、今はどちらも同じファイルが置いてあるらしい。
昔は用途で分かれていたとかなんとか。
どっちのページに行ってもいいので、OS(今回はXP用)に対応したドライバをダウンロード。

ファイルを解凍しておいて、モジュールにPCからUSBを接続すると、ドライバのインストールウィザードが出るので、解凍したフォルダを指定してインストール。
2回とも同じフォルダね。

USB UARTドライバUSBシリアルポートドライバ

インストールが終わると、デバイスマネージャのポートのところに、今インストールしたシリアルポートが出来ているはず。
今回の例だと、COM8として認識されています。(AVRライタがCOM7)

デバイスマネージャ

とりあえずの動作確認を行うために、まず、TeraTermをダウンロードしてインストール。
まぁ、標準のハイパーターミナルとかでもいいんだけど、使い慣れてないから……。

で、モジュールのTXDとRXDのピンを、ジャンパで繋ぐ。
送信ポートと受信ポートをつないで、ループバックで返すようにするってことね。

繋いだら、TeraTermを立ち上げて、接続先をシリアルの、インストールしたポートを指定。(今回はCOM8)
繋がったら、適当に文字を入力してみて、それが画面に表示されたら、動作確認OK。
入力した文字が、USBを経由して、モジュールのTXD(送信側)からRXD(受信側)を通って、画面に表示されているってことね。
デフォルトの9600bpsでつながるけど、今回はこの速度のままで動作は確認できた。

シリアルポートテスト

TXDとRXDのピンを繋いでるジャンパを抜いたら、文字を入力しても表示されなくなるから、表示される=ちゃんと繋がってるってことで。

回路図

今回は、RXD(PD0)とTXD(PD1)のピンを使うため、今までPDに繋がっていたLCDを、PBに移動しました。
それに伴って、LCDライブラリも多少手を加えてあります。

USBシリアル通信サンプル

FT232RLには、PCからUSBが繋がっており、電源はそっちから取っています。
AVRの方から取ってもいいとは思うんだけど。
試してみたら、FT232RLに繋がってるUSBの方から、経路はわからんけど電流が漏れてしまっているらしい……。
FT232RLにUSBを繋ぐと、基板の電源の方に付けている、導通確認用のLEDが、SWもOFFのままなのに、うっすらと光ってました。
その電流の影響があるのか、基板のSWをONにした最初のタイミングで、LCDの初期化に失敗して、文字が出力されないようでした。
リセットボタンを押すと、正常に表示されるんだけどね。
なので、電源は基板と同じ方から取るようにしました。

で、FT232RLのTXD(送信側)をAVRのRXD(受信側)に、FT232RLのRXD(受信側)をAVRのTXD(送信側)に繋げて終了。

USBシリアル通信サンプル

もうブレッドボードがいっぱいいっぱい……。
というわけで、後で追加でブレッドボードを購入します。

USBシリアル通信サンプル

ちょっとすっきり!

ソースコード

まず、メインで参照する各種ライブラリ。
fileユーティリティヘッダfileユーティリティソースfileLCDライブラリヘッダfileLCDライブラリソース

#include <avr/io.h>
#include "LcdLib.h"

#define USART_BPS	25	// ボーレート設定値

void Usart_init( void )
{
	UBRR0 = USART_BPS;		// ボーレート分周値レジスタ(1MHz駆動の2400bpsに設定)
	UCSR0B = 0b00011000;	// 受信許可bitと送信許可bitを立てる
	UCSR0C = 0b00000110;	// 非同期/パリティ禁止/停止1bit/データbit長8bit
}

void Usart_rcvchar( char *data )
{
	while( !( UCSR0A & 0b10000000 )){
		;	// 受信完了フラグが立つまで待つ
	}
	*data = UDR0;	// 受信データを出力引数に設定
}

void Usart_sndchar( char data )
{
	while( !( UCSR0A & 0b00100000 )){
		;	// 送信データレジスタが空くまで待つ
	}
	UDR0 = data;	// 送信データをレジスタに設定
}

int main( void )
{
	char data = ' ';	// 受信/送信データ格納用

	Lcd_init();						// LCD初期化
	Lcd_setstr( "Receive Data:" );	// 初期表示

	Usart_init();	// シリアル通信初期化

	while( 1 ){
		// 一文字受信
		Usart_rcvchar( &data );

		// LCDを初期位置に戻して、テキストと受信データを出力
		Lcd_setpos( 0, 0 );
		Lcd_setstr( "Receive Data:" );
		Lcd_setchar( data );

		// 受信データをエコーバック
		Usart_sndchar( data );
	}

	return 0;
}

基本動作

まずは初期化。
UBRRレジスタに、ボーレートを設定している。
このボーレートを設定するレジスタは、実はUBRRHとUBRRLに分かれていて、Lの8bit+Hの4bitのあわせて12bit表現だけど、UBRRに値を入れれば、そのあたりはコンパイラが(というかinclude/avr内のマクロが)それをやってくれる。

UCSRBとUCSRCに設定してあるのは、ソース内コメント通り。
受信や送信完了での割り込みはしていません。
パリティは調べたらなんとなくわかるけど、停止bitとかとあわせて、どのように解釈してスタートbit、ストップbitを認識して通信するのかということは、実はあんまりわかってない。
そのうち余力があれば掘り下げたいけど……。

受信関数、送信関数は簡単ですね。
それぞれ、なんか来たよフラグと、送る入れ物が空いてるよフラグが立つまで待って、データレジスタの読み書きをしているだけです。

メインは、初期化後、受け取ってLCDに表示してPCに戻すというループを繰り返しているだけです。

あぁ、ちなみに、動かすときは、まずAVRの電源を入れて、PCにCOMポートを認識させた上で、今回は2400bpsに設定しているので、TeraTerm等でそのCOMポートに2400bpsで繋いで(設定→シリアルポート)から、文字を入力してください。
最初、電源を入れずにTeraTermを立ち上げて、よく見ないでCOMポートに繋いだら、それはAVRライタのCOMポートで、なんも反応しなかったから焦ったよ……。

TeraTerm設定

というわけで、実際に動かしてみたのはこんな感じ。
TeraTerm上でキーを打つと、LCDに表示されると共に、エコーバックしてTeraTerm上にも表示されています。

ボーレートを上げるには、ベースクロックを上げたり、倍速モードbitを立ててやんないとならないらしい。
誤差とかも色々考慮しないとならないので、大変そうだ。
クロックを上げると、今のLCDライブラリだと、動作不安定だしな……。
この辺も今後の課題。

おまけ

UART部分をライブラリ化して、LCDに受信したデータの16進コードも表示する機能をつけてみた。

fileUSARTライブラリヘッダfileUSARTライブラリソース

#include <stdio.h>
#include "LcdLib.h"
#include "UsartLib.h"

int main( void )
{
	char data = ' ';		// 受信/送信データ格納用
 	char str[5] = "    ";	// 16進コード格納用

	Lcd_init();						// LCD初期化
	Lcd_setstr( "Receive Data:" );	// 初期表示

	Usart_init();	// シリアル通信初期化

	while( 1 ){
		// 一文字受信
		Usart_rcvchar( &data );

		// LCDを初期位置に戻して、テキストと受信データを出力
		Lcd_setpos( 0, 0 );
		Lcd_setstr( "Receive Data:" );
		Lcd_setchar( data );

		// 2行目に16進コードを出力
 		Lcd_setpos( 1, 0 );
		sprintf( str, "0x%02X", data );
		Lcd_setstr( str );

		// 受信データをエコーバック
		Usart_sndchar( data );
	}

	return 0;
}

sprintfを使ってるけど、これでプログラムサイズが一気に3kb以上増えるので注意。
ATmega168Pだったら、Flashは16kbあるので、まだまだ余裕(上記プログラムで6kbちょい)だけどね。
ごめん、そんなに増えないわ、増えるのは1kbくらいかも。
hexファイルサイズが結構増えるけど、実際に書き込まれるサイズはもっと小さいです。


添付ファイル: fileUsartLib.c 1497件 [詳細] fileUsartLib.h 1491件 [詳細] filettsetting.png 1038件 [詳細] fileUsbTestp2.png 1282件 [詳細] fileUsbTestp.png 1148件 [詳細] fileLcdLib.c 1481件 [詳細] fileLcdLib.h 1469件 [詳細] fileutil.c 1124件 [詳細] fileutil.h 1182件 [詳細] fileUsbTest.png 2410件 [詳細] fileserealtest.png 1143件 [詳細] filedevicemng.png 1503件 [詳細] fileserealport.png 1121件 [詳細] fileusb_uart.png 1337件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2010-04-18 (日) 11:56:19 (3290d)