 |
#include <avr/io.h> #include <avr/sfr_defs.h> #include <stdio.h>
// 晶片型號: ATMega16A // 晶振: 外部11.0592MHz // 熔絲位配置: CKSEL3~0=1110, CKOPT=0 // 只有使用外部晶振才能保證PC端接收到的內容不亂碼
int fputc(int ch, FILE *fp) { if (fp == stdout) { if (ch == '\n') { // 自動添加\r UDR = '\r'; while ((UCSRA & _BV(UDRE)) == 0); } UDR = ch; while ((UCSRA & _BV(UDRE)) == 0); } return ch; }
int fgetc(FILE *fp) { if (fp == stdout) { while ((UCSRA & _BV(RXC)) == 0); return UDR; } return 0; }
void USART_WriteString(const char *s) { while (*s) { UDR = *s++; while ((UCSRA & _BV(UDRE)) == 0); } }
int main(void) { char str[50]; //UBRRL = 71; // 波特率: 9600 UBRRL = 5; // 波特率: 115200 UCSRB = _BV(RXEN) | _BV(TXEN);
USART_WriteString("This is a string.\r\n"); fdevopen((int (*)(char, FILE*))fputc, fgetc); // 進行類型轉換的目的是為了避免警告 printf("Hello World!\nUBRRL=%d\n", UBRRL); while (1) { gets(str); puts(str); } }
|
 |
這個程序有一個問題,就是如果連續發送多行,那麼接收到的內容從第二行開始就會不完整。例如,發送: This is a long string. This is another paragraph.
接收到的卻是: This is a long string. Thph.
這是因為串行通信是收發同時進行的,而在上面的程序中卻是先接收(gets)後發送(puts),這樣當gets獲取完第一行內容後,puts才開始發送,並且發送需要一定的時間。等puts發送完了gets才開始接收,此時單片機端早已發送了很多字符了,中間的內容都沒有收到而溢出(Overrun)了。所以第二行的內容就不完整。
|
 |
上述問題的解決辦法是,創建一個FIFO緩衝區(隊列),並打開串口接收中斷。每接收到一個字符就放入該緩衝區中,而scanf, gets, fgetc等函數則是從緩衝區中讀取字符,如果沒有字符可讀取就一直阻塞。這樣就能做到收發同時進行,且可以正常使用標準輸入函數。 【改進後的程序】 #include <avr/interrupt.h> #include <avr/io.h> #include <avr/sfr_defs.h> #include <stdio.h>
// 晶片型號: ATMega16A // 晶振: 外部11.0592MHz // 熔絲位配置: CKSEL3~0=1110, CKOPT=0 // 只有使用外部晶振才能保證PC端接收到的內容不亂碼
// 隊列結構體 struct fifo { unsigned char buf[128]; unsigned char front; unsigned char rear; } usartbuf;
#define fifo_init(f) ((f)->front = (f)->rear = 0) // 初始化隊列 #define fifo_empty(f) ((f)->front == (f)->rear) // 隊空判定 #define fifo_full(f) ((f)->front == ((f)->rear + 1) % sizeof((f)->buf)) // 隊滿判定
// 入隊 unsigned char fifo_in(struct fifo *f, unsigned char data) { if (fifo_full(f)) return 0; f->buf[f->rear] = data; f->rear = (f->rear + 1) % sizeof(f->buf); return 1; }
// 出隊 unsigned char fifo_out(struct fifo *f, unsigned char *data) { if (fifo_empty(f)) return 0; *data = f->buf[f->front]; f->front = (f->front + 1) % sizeof(f->buf); return 1; }
int fputc(int ch, FILE *fp) { if (fp == stdout) { if (ch == '\n') { // 自動添加\r UDR = '\r'; while ((UCSRA & _BV(UDRE)) == 0); } UDR = ch; while ((UCSRA & _BV(UDRE)) == 0); } return ch; }
int fgetc(FILE *fp) { unsigned char value = 0; if (fp == stdout) while (!fifo_out(&usartbuf, &value)); return value; }
int main(void) { char str[101]; // 假定每一行最長不超過100個字符 //UBRRL = 71; // 波特率: 9600 UBRRL = 5; // 波特率: 115200 UCSRB = _BV(RXEN) | _BV(TXEN) | _BV(RXCIE); // 開接收中斷 sei(); // 開總中斷
fdevopen((int (*)(char, FILE*))fputc, fgetc); while (1) { gets(str); puts(str); } }
// 串口接收中斷 ISR(USARTRXC_vect) { fifo_in(&usartbuf, UDR); }
|
 |
在main函數中最好先將front和rear的值清零: int main(void) { char str[101]; // 假定每一行最長不超過100個字符 //UBRRL = 71; // 波特率: 9600 UBRRL = 5; // 波特率: 115200 UCSRB = _BV(RXEN) | _BV(TXEN) | _BV(RXCIE); // 開接收中斷 sei(); // 開總中斷
fifo_init(&usartbuf); // 將front和rear清零 fdevopen((int (*)(char, FILE*))fputc, fgetc); while (1) { gets(str); puts(str); } }
|
 |
 如果接收到的內容出現亂碼或字符丟失的現象,說明FIFO緩衝區的大小不夠,擴大緩衝區就行了: struct fifo { unsigned char buf[512]; // 緩衝區的大小 unsigned int front, rear; // 這兩個變量要改用int類型 } usartbuf;
|
 |
串口大師這個軟件本身可能有問題,發送同樣的內容,另一個串口調試工具就沒有出現亂碼。

|
 |
如果接收到的內容出現亂碼或字符丟失的現象,說明FIFO緩衝區的大小不夠,擴大緩衝區就行了: struct fifo { unsigned... 
同時還應該注意擴大main函數中str數組的大小。
|
 |
在C程序里直接執行printf("// 只有使用外部晶振才能保證PC端接收到的內容不亂碼");(沒有換行符),串口大師接收到的內容都會亂碼。。。
|