PIC16F877AでUSARTを使い、RS-232C(ADM3202AN)経由でGT-720Fをつないだ。
最初はTera Termで送った文字をただ返すプログラムを書いてそれでUSART通信のテストを行った。
最初に苦労したのが割り込みの原因フラグのPIR1
レジスタが最初に参照した時点で0クリアされてしまうこと。今回の場合、送信も受信も割り込みで処理をするので、TXIF
とRCIF
の2つをチェックするけど、最初にTXIF
をチェックした時点で0にクリアされてしまって、受信の場合にRCIF
をチェックしようとしてもわからなくなってしまっていました。
対策として、割り込みに入ってすぐにグローバル変数にコピーしてそれを使って判定するようにしました。
(もしかして、HI-TECH Cのクセなんでしょうか?)
2009/09/21:修正
HI-TECH Cの割り込みのサンプルで直接T0IF
とINTF
をチェックしているのを見つけたので、どこが違うのかをチェックしたら、割り込み関数の定義方法が違っていました。
誤:interrupt void usart_itr()
↓
正:void interrupt usart_itr()
こうするとRCIF
でちゃんと判定できました。
次に苦労したのが、SPBRG
レジスタに設定する値。受信したデータがすべて0xFFになってしまってボーレートがおかしいのはわかるんだけど、SPBRGの値を調整することに気が付くまで戸惑ってしまった。
クロックとボーレートから(FOSC/(16 * BAUDRATE))-1
と計算する値だけど、最後の-1がクセモノで小数点を削除するだけで良かったりするので、結果としては自分で計算して値を調整する感じだった。
あと、受信はそんなに難しくなかったけど、送信はTXREG
に送信データを入れた後1msほど待ちを入れないとうまく送れなかった(割り込み関数内でdelayとか使っていいんだろうか?)。
回路図
ソースコード
ソースコードは以下の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(); }
いきなり失礼します。
質問があり投稿させていただきます。
自分もHI-TECH CコンパイラーでPIC16F648Aでプログラミングしている者です。
USART通信を利用してシリアル通信をしているのですがうまくできません。
ハイパーターミナルで受信文字を見てみても文字化けだったり空白だったりです。近頃は何も受信されていないです。オシロで波形を見ると正常に送信されています。
確実にボーレートの設定だと思うのですが、データシートをみて設定したし、数か月前はうまくできていました。何かアドバイスがあればお願いします。
設定は高速モード、発振子10Mhz、9600bps、データビット8、ストップビット1
RCSTA = 0x90;TXSTA = 0x24;SPBRG = 0x40;
while(!TXIF)
continue;
TXREG = byte;
davidsさん
コメントありがとうございます。
未熟者なので、手元に無いものの問題点を指摘できるわけじゃありませんが、データシートでわかる設定に関して指摘できるような点は見つけられませんでした。
数ヶ月前には動いていたということなので、その時点との違いを洗い出した方がいいように思いますが、いかがでしょう?
あと、私の場合、TXREGに送信データを入れた後、1msの待ちを入れないと文字化けしたので、それを試されてはいかがでしょう?
それと、何の根拠もありませんが、USART通信部分を割り込み処理で実装してみてはいかがでしょう?
私がこの記事を書く時に最初whileループで試していましたがうまく動かせず、割り込み処理で書いたら動きました。
今日友達と原因を探していたら、PICとPCを繋いでいた自作のケーブルとブレッドボードをGNDで繋ぐのを忘れていました><
過去に出来ていたので外部的な原因を最初に探せばよかったと後悔しています。
お手を煩わせてすいませんです。
原因がわかって良かったですね。
私も原因のほとんどが配線ミスでした。
自分だけじゃないことがわかって良かったです(^_^;)
見たところgetchやputchで割り込みを禁止していませんね。バッファの読み書きをしたあと関連の変数(RCV_BUFF_STARTとか)を更新するまでの間に割り込みがかかると、バッファの状態と変数の関係に矛盾が生じます。これが文字化けなどの原因じゃないでしょうか。この前後の最小限の期間、最小限の割り込みを禁止することでうまくいくような気がします。
夢声さん
ご指摘ありがとうございます。
現在、なかなか時間が取れないのですが、時間ができた時に試させていただきます。