PIC16F877AでUSART (GT-720Fをつなぐ)

PIC16F877AでUSARTを使い、RS-232C(ADM3202AN)経由でGT-720Fをつないだ。

PIC16F877AにGT-720Fをつなぐ

PIC16F877AにGT-720Fをつなぐ

最初はTera Termで送った文字をただ返すプログラムを書いてそれでUSART通信のテストを行った。

最初に苦労したのが割り込みの原因フラグのPIR1レジスタが最初に参照した時点で0クリアされてしまうこと。今回の場合、送信も受信も割り込みで処理をするので、TXIFRCIFの2つをチェックするけど、最初にTXIFをチェックした時点で0にクリアされてしまって、受信の場合にRCIFをチェックしようとしてもわからなくなってしまっていました。
対策として、割り込みに入ってすぐにグローバル変数にコピーしてそれを使って判定するようにしました。
(もしかして、HI-TECH Cのクセなんでしょうか?)

2009/09/21:修正
HI-TECH Cの割り込みのサンプルで直接T0IFINTFをチェックしているのを見つけたので、どこが違うのかをチェックしたら、割り込み関数の定義方法が違っていました。
誤:interrupt void usart_itr()

正:void interrupt usart_itr()
こうするとRCIFでちゃんと判定できました。

次に苦労したのが、SPBRGレジスタに設定する値。受信したデータがすべて0xFFになってしまってボーレートがおかしいのはわかるんだけど、SPBRGの値を調整することに気が付くまで戸惑ってしまった。
クロックとボーレートから(FOSC/(16 * BAUDRATE))-1と計算する値だけど、最後の-1がクセモノで小数点を削除するだけで良かったりするので、結果としては自分で計算して値を調整する感じだった。

あと、受信はそんなに難しくなかったけど、送信はTXREGに送信データを入れた後1msほど待ちを入れないとうまく送れなかった(割り込み関数内でdelayとか使っていいんだろうか?)。

回路図

PIC16F877AとGT-720Fをつなぐ

PIC16F877AとGT-720Fをつなぐ

ソースコード
ソースコードは以下のmain.cの他にLCD用のソースコード2つ(lcdlib16.hとlcdlib16.c)を使っていますが、USARTの処理はmain.cのみに書いてあります。
2009/09/21:修正しました。
main.c

#include <pic.h>
#include "lcdlib16.h"

__CONFIG(XT & WDTDIS & PWRTEN & BOREN & LVPDIS & UNPROTECT);

#define FOSC		4000000
#define BAUDRATE	9600
#define NINE_MODE	0
#define SYNC_MODE	0	// 0=非同期, 1=同期
#define HIGHT_SPEED	1	// 0=低速, 1=高速
#if (HIGHT_SPEED == 1)
#define SPBRG_VAL 25 //(FOSC/(16 * BAUDRATE))-1
#else
#define SPBRG_VAL (FOSC/(64 * BAUDRATE)-1)
#endif
#define SEND_ENABLE
#define RECIVE_ENABLE
#define USART_INTERRUPT
// 送信バッファ
#define SND_BUFF_SIZE	16
unsigned char SND_BUFF_START = 0;
unsigned char SND_BUFF_INDEX = 0;
unsigned char SND_BUFF[SND_BUFF_SIZE];
// 受信バッファ
#define RCV_BUFF_SIZE	16
unsigned char RCV_BUFF_START = 0;
unsigned char RCV_BUFF_INDEX = 0;
unsigned char RCV_BUFF[RCV_BUFF_SIZE];

#ifdef USART_INTERRUPT
void interrupt usart_itr()
{
#ifdef SEND_ENABLE
	if (TXIF) {	// 送信で割り込み発生
		if (SND_BUFF_START != SND_BUFF_INDEX) {
			TXREG = SND_BUFF[SND_BUFF_START];
			__delay_ms(1);
			SND_BUFF_START++;
			if (SND_BUFF_START == SND_BUFF_SIZE) {
				SND_BUFF_START = 0;
			}
		}
		if (SND_BUFF_START == SND_BUFF_INDEX) {
			TXEN = 0;
		}
		TXIF = 0;
	}
#endif
#ifdef RECIVE_ENABLE
	if (RCIF) {	// 受信で割り込み発生
		RCV_BUFF[RCV_BUFF_INDEX] = RCREG;
		RCV_BUFF_INDEX++;
		if (RCV_BUFF_INDEX == RCV_BUFF_SIZE) {
			RCV_BUFF_INDEX = 0;
		}
		RCIF = 0;
	}
#endif
}
#endif

#ifdef RECIVE_ENABLE
unsigned char getch()
{
	while (RCV_BUFF_START == RCV_BUFF_INDEX) {
		// wait
	}
	unsigned char rcv;
	rcv = RCV_BUFF[RCV_BUFF_START];
	RCV_BUFF_START++;
	if (RCV_BUFF_START == RCV_BUFF_SIZE) {
		RCV_BUFF_START = 0;
	}
	return rcv;
}
#endif

#ifdef SEND_ENABLE
void putch(unsigned char ch)
{
	if ((SND_BUFF_START == 0) && (SND_BUFF_INDEX == SND_BUFF_SIZE - 1)) {
		// バッファフル
		while ((SND_BUFF_START == 0) && (SND_BUFF_INDEX == SND_BUFF_SIZE - 1)) {
			// wait
		}
	} else if (SND_BUFF_START == SND_BUFF_INDEX + 1) {
		// バッファフル
		while (SND_BUFF_START == SND_BUFF_INDEX + 1) {
			// wait
		}
	}
	SND_BUFF[SND_BUFF_INDEX] = ch;
	SND_BUFF_INDEX++;
	if (SND_BUFF_INDEX == SND_BUFF_SIZE) {
		SND_BUFF_INDEX = 0;
	}
	TXEN = 1;
}
#endif

void main()
{
	// ポートの設定 (LCD用)
	ADCON1 = 0x06;	// ポートAをデジタルへ
	TRISA = 0x00;	// 全部出力
	// USARTの設定
	SPBRG = SPBRG_VAL;
	BRGH = HIGHT_SPEED;
	TX9 = NINE_MODE;
	SYNC = SYNC_MODE;
	SPEN = 1;	// Serial Port ENable
#ifdef USART_INTERRUPT
#ifdef SEND_ENABLE
	TXIE = 1;
#endif
#ifdef RECIVE_ENABLE
	RCIE = 1;
#endif
	PEIE = 1;
	GIE = 1;
#endif
#ifdef RECIVE_ENABLE
	CREN = 1;
#endif
	// LCD初期化
	lcd_init();
	lcd_clear();
	lcd_str("Hello World!");
	for (char i = 0; i < 10; i++) {
		__delay_ms(100);
	}
	lcd_clear();

	unsigned char data;
	data = getch();
	while (1) {
		//putch(data);
		if (data == '\r') {
			//putch('\n');
		} else if (data == '\n') {
		} else if (data < 0x20) {
		} else {
			if (data == '$') {
				lcd_clear();
			}
			lcd_data(data);
		}
		data = getch();
	}
}

PICkit2だけだと十分な電力がまかなえない感じで、eneloop単3×4本をつないで動かしています。

Tera Termと通信する時用のメインループ部分。

	unsigned char data;
	data = getch();
	while (1) {
		putch(data);
		if (data == '\r') {
			putch('\n');
			lcd_clear();
		} else if (data == '\n') {
		} else if (data < 0x20) {
		} else {
			lcd_data(data);
		}
		data = getch();
	}
カテゴリー: PIC, プロトタイプ, 回路, 部品 タグ: , , , , , , , パーマリンク

6 Responses to PIC16F877AでUSART (GT-720Fをつなぐ)

  1. davids のコメント:

    いきなり失礼します。
    質問があり投稿させていただきます。

    自分もHI-TECH CコンパイラーでPIC16F648Aでプログラミングしている者です。
    USART通信を利用してシリアル通信をしているのですがうまくできません。

    ハイパーターミナルで受信文字を見てみても文字化けだったり空白だったりです。近頃は何も受信されていないです。オシロで波形を見ると正常に送信されています。

    確実にボーレートの設定だと思うのですが、データシートをみて設定したし、数か月前はうまくできていました。何かアドバイスがあればお願いします。

    設定は高速モード、発振子10Mhz、9600bps、データビット8、ストップビット1
    RCSTA = 0x90;TXSTA = 0x24;SPBRG = 0x40;
    while(!TXIF)
    continue;
    TXREG = byte;

  2. yuji のコメント:

    davidsさん

    コメントありがとうございます。
    未熟者なので、手元に無いものの問題点を指摘できるわけじゃありませんが、データシートでわかる設定に関して指摘できるような点は見つけられませんでした。

    数ヶ月前には動いていたということなので、その時点との違いを洗い出した方がいいように思いますが、いかがでしょう?

    あと、私の場合、TXREGに送信データを入れた後、1msの待ちを入れないと文字化けしたので、それを試されてはいかがでしょう?

    それと、何の根拠もありませんが、USART通信部分を割り込み処理で実装してみてはいかがでしょう?
    私がこの記事を書く時に最初whileループで試していましたがうまく動かせず、割り込み処理で書いたら動きました。

  3. davids のコメント:

    今日友達と原因を探していたら、PICとPCを繋いでいた自作のケーブルとブレッドボードをGNDで繋ぐのを忘れていました><

    過去に出来ていたので外部的な原因を最初に探せばよかったと後悔しています。
    お手を煩わせてすいませんです。

  4. yuji のコメント:

    原因がわかって良かったですね。
    私も原因のほとんどが配線ミスでした。
    自分だけじゃないことがわかって良かったです(^_^;)

  5. 夢声 のコメント:

    見たところgetchやputchで割り込みを禁止していませんね。バッファの読み書きをしたあと関連の変数(RCV_BUFF_STARTとか)を更新するまでの間に割り込みがかかると、バッファの状態と変数の関係に矛盾が生じます。これが文字化けなどの原因じゃないでしょうか。この前後の最小限の期間、最小限の割り込みを禁止することでうまくいくような気がします。

  6. yuji のコメント:

    夢声さん
    ご指摘ありがとうございます。
    現在、なかなか時間が取れないのですが、時間ができた時に試させていただきます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください