小白的单片机之旅——串口到printf
上节已经说了串口相关的功能,在软件实现串口打印这个动作上,软件完成的是将需要发送的字符在合适的时机送个硬件对应的数据寄存器,而后面的将字节数据在帧格式的约束下,按照对应的比特率将之变成bit流传输出去的工作是有硬件完成的,当然了这里的硬件本质上也只是将对应的协议将“软件”硬化的结果,如果不借助对应的UART外设单纯利用软件使用IO口进行模拟uart也是可以的,只是效率比较低而已。
在最后的演示验证阶段,我们是使用标准库的USART_SendData接口发送我们需要的字符的ascii码实现串口打印的,这种实现效率低、可读性差、也难以移植,离我们常用的printf到底还有多少距离呢?
我们先看一下,基于之前的实现,能不能更友好的打印一个字符串呢?所谓字符串,本身就是一个连续的字符数组,特点就是其内容一般是可用于显示的ASCII码,且在最后一个0作为结束符。简单实现如下图所示。
我们简单说明一下,上面的代码实际就是在之前的字节发送的基础上,将传入的字符串(以0结束的字节数组)一次性全部自动的发出去了而已。验证效果如下图所示。
至此,我们已经可以通过USART_SendStr发送已经组装好的字符串,其实还只是一个简单的字节发送的封装。
如果我们直接用printf实现,那会怎么样呢?我们先直接包含<stdio.h>并且直接调用printf,编译,出现如下错误。
因此为了使用标准库的相关接口,需要进行一些接口的实现,不关注的可以直接打桩(假装实现)。具体实现如下图所示。
至此,我们就可以使用标准库的printf等接口实现跟我们的串口进行交互啦。
51单片机串口通信 环形缓冲区队列(FIFO)
最近在做毕业设计刚好涉及到51单片机,简单的研究一下发现51单片机串口只有一个字节的缓存,如果遇到单片机串口中断没有及时处理SBUF的值或者串口中断长时间未退出很容易照成数据丢失,于是就自己写了个缓冲区,代价就是消耗一部分内存空间,时间-空间本来就是一对矛盾体,想减少串口通信中数据丢失问题只能牺牲部分空间,来减少数据通信过程中的丢失问题。
核心代码如下所示:
/**********************************************************/
#define BUFFER_MAX 16 //缓冲区大小
typedef struct _circle_buffer{
unsigned char head_pos; //缓冲区头部位置
unsigned char tail_pos; //缓冲区尾部位置
unsigned char circle_buffer[BUFFER_MAX]; //缓冲区数组
}circle_buffer;
circle_buffer buffer;
void bufferPop(unsigned char* _buf)
{
if(buffer.head_pos==buffer.tail_pos) //如果头尾接触表示缓冲区为空
*_buf=0xFF;
else
{
*_buf=buffer.circle_buffer[buffer.head_pos]; //如果缓冲区非空则取头节点值并偏移头节点
if(++buffer.head_pos>=BUFFER_MAX)
buffer.head_pos=0;
}
}
void bufferPush(const unsigned char _buf)
{
buffer.circle_buffer[buffer.tail_pos]=_buf; //从尾部追加
if(++buffer.tail_pos>=BUFFER_MAX) //尾节点偏移
buffer.tail_pos=0; //大于数组最大长度 制零 形成环形队列
if(buffer.tail_pos==buffer.head_pos) //如果尾部节点追到头部节点 则修改头节点偏移位置丢弃早期数据
if(++buffer.head_pos>=BUFFER_MAX)
buffer.head_pos=0;
}
考虑到看到此博文的人可能有很多小白并不知道如何使用,在此简单的说一下,假设你已经能进行简单的串口发送接收了,然后串口中断部分可以这样写
void serial1(void) interrupt 4
{
if(RI)
{
bufferPush(SBUF);
RI=0;
}
if(TI)
{
TI=0;
}
}
在主程序中我们只需要调用函数就行了如:
void main()
{
unsigned char dat ;
//读取缓冲区一个字符,如果dat=0xff表示缓冲区为空,所以接收的字符不能有0xff。
bufferPop(&dat);
}
bufferPop函数中没调用一次,便从缓冲区取出一个字符,头部指针就会进行偏移,具体看源码并不是很复杂 只是一个数组类型的环形FIFO缓冲区。
有一点要注意的是,如果缓冲区满的话,后面的数据会覆盖最前面的数据。
你可以把缓冲区设置大些,就可以尽可能的减少数据覆盖问题,但是带来的额外问题就是51或者其他系列的单片机RAM是非常小的,并不像PC中缓冲区动不动就1024KB。所以缓冲区设置多大,根据自己需求调整就行了。
相关问答
C语言 单片机 编程,如何将 串口 接收到的十六进制字符转存到 数组 中?接收数组也可以定义成不定长度的,没有想要寻找你要的字符串可以用strstr函数,如果想清零可以用memset函数全部赋值为0接收数组也可以定义成不定长度的,没有...
求教, 单片机串口 发送文件問題-ZOL问答楼主先介绍一下什么是《单片机中的文件》吧。串口发送,那是十分简单的,一个字节一个字节的发送就行了。串口工具直接把数据输入点击发送就行了啊。单片机那里...
想问一下用汇编语言写 单片机 程序时,怎么定义一个 数组 变量?严格的说,汇编语言不支持显式数组变量,就是说你想定义一个任意数组,然后用下标访问是不行的。但汇编语言支持隐式数组变量,其实就是对变量表间接寻址,使用...
单片机 怎么给 数组 分配地址?单片机给数组分配地址是随机的。单片机给数组分配地址是随机的。
单片机 怎么定义全局 数组 ?在单片机中定义全局数组需要在程序的开头或者全局变量区域声明数组变量,并指定数组的数据类型和大小。全局数组可以在程序的任何位置被调用和修改,它的作用域...
单片机 C语言里怎么定义BYTE型的 数组 ? - 131****2698 的回答 ...BYTE这个是数组名而已0x00,0x00.......0x00这些是十六进制数1、C语言中的字符类型即char类型,一般情况占1个字节,因此一般使用char类型来描述字节...
单片机 C语言里怎么定义BYTE型的 数组 ? - 156****9155 的回答 ...你这个不是BYTE型数组啊。typedefunsignedcharBYTE;BYTEbuff[16]={0};表示将buff[0]....buff[15]都赋值为0x001、C语言中的字...
用F429进行 串口 通信时怎么把接收到的字符串中的数字作为整型数据提取出来呢?像这种固定格式的数据,就把所有串口接收字符按ASCII码找到标识符#,再把前后的数字识别出来组成数据就好了像这种固定格式的数据,就把所有串口接收字符按ASCI...
单片机 程序 数组 越界了会怎么样?在单片机程序中,如果数组越界了,会导致不可预测的结果。这可能会导致程序崩溃、出现错误或产生不正确的输出。当访问数组时,如果使用的索引超出了数组的边...
51 单片机 如何清空buf 数组 ?在51单片机中,清空buf数组需要使用一个循环语句将每一个元素赋值为0。可以使用for循环或while循环实现,循环次数应该根据数组的长度来确定。例如,对于一个长度...