設置 | 登錄 | 註冊

作者共發了8篇帖子。

【程序】在AVR單片機中使用printf函數

1樓 巨大八爪鱼 2017-3-22 10:11
#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);
    }
}
2樓 巨大八爪鱼 2017-3-23 09:30

這個程序有一個問題,就是如果連續發送多行,那麼接收到的內容從第二行開始就會不完整。例如,發送:
This is a long string.
This is another paragraph.

接收到的卻是:
This is a long string.
Thph.


這是因為串行通信是收發同時進行的,而在上面的程序中卻是先接收(gets)後發送(puts),這樣當gets獲取完第一行內容後,puts才開始發送,並且發送需要一定的時間。等puts發送完了gets才開始接收,此時單片機端早已發送了很多字符了,中間的內容都沒有收到而溢出(Overrun)了。所以第二行的內容就不完整。

3樓 巨大八爪鱼 2017-3-23 09:55
上述問題的解決辦法是,創建一個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);
}
4樓 巨大八爪鱼 2017-3-23 09:58
在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);
    }
}
6樓 巨大八爪鱼 2017-3-23 18:02

如果接收到的內容出現亂碼或字符丟失的現象,說明FIFO緩衝區的大小不夠,擴大緩衝區就行了:
struct fifo
{
    unsigned char buf[512]; // 緩衝區的大小
    unsigned int front, rear; // 這兩個變量要改用int類型
} usartbuf;
7樓 巨大八爪鱼 2017-3-23 18:06

串口大師這個軟體本身可能有問題,發送同樣的內容,另一個串口調試工具就沒有出現亂碼。

8樓 巨大八爪鱼 2017-3-23 18:22

回復6樓 @巨大八爪鱼 的內容:

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

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。