目前共有8篇帖子。 內容轉換:不轉換▼
 
點擊 回復
449 7
【程序】在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
{...
同时还应该注意扩大main函数中str数组的大小。
一派護法 十九級
9樓 發表于:2017-3-23 18:24
在C程序里直接执行printf("// 只有使用外部晶振才能保证PC端接收到的内容不乱码");(没有换行符),串口大师接收到的内容都会乱码。。。

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:449 回複數:7
評論數: ?
作者: 巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2017-3-23 18:24
 
©2010-2024 Arslanbar Ver2.0
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。