第十三届蓝桥杯嵌入式串口竞赛
目录
区分UART、TTL、RS-232、RS-422、RS-485(客观题会考)
库函数讲解:(下面的好像是从b站小蜜蜂的视频里面截取下来的图片)
串口简介
串行通讯接口,简称串口。即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平。常用两根数据线,即TXD和RXD,配合GND至少需要三根线即可进行通信。以下除特殊说明,串口均指代单片机UART串口
串口信息以数据线上的高低电平表示,高电平为1,低电平为0
图1 串口时序图
图1为数据线上典型时序图,数据线空闲时为高电平,以一位低电平最为起始(除特殊说明,串口大部分都是一位起始位),而后以先传输低位,再传输高位的方式发送数据位,最后以高电平为数据停止位(常用1位停止位,也有2位和1.5位等)。数据位长度此处为8位,但不一定是8位,也会有7位、9位等,有时也会包含有奇偶校验位(校验位实际上就是数据的最后一位,但通常不算在数据位里)。上图所示即我们常用的8位数据位,无校验,一位停止位的串口格式
最为异步通信接口,串口不具有时钟线,所以需要在通信双方设置数据位长度,即电平持续时间,其倒数为电平转换的频率,这一参数即为波特率(但注意,波特的概念并不是位的概念,此处按下不表)。例9600波特率即为电平改变频率为9600Hz,电平持续时间1/9600秒,则一秒最多传输9600/(1+8+1)=960个字节(不严谨,但差不多)
因串口发送数据线与接收数据线分离,其为全双工接口,串口最少使用三根线即可完成通讯,但除这三根线外还可以有硬件流控RTS、CTS等,我没用到过,具体我也不是很清楚
区分UART、TTL、RS-232、RS-422、RS-485(客观题会考)
UART即使用TTL电平的串口,TTL是电平形式,通常低于0.8V为低电平,高于2.4V为高电平,不是协议格式,但我们有时也常说TTL串口,以区分RS-232串口
RS-232串口数据格式与上文介绍的UART串口基本一致,但是电平不一样,通常以-3V至-15V为逻辑1,3V-15V为逻辑0,与UART不可混接,有烧板子的风险
RS-422常用4线制和2线制,4线制发送和接收均有两根线,两根线上为差分信号,增加传输距离,全双工。2线制的两根线上为差分信号,同时只能做为发或者收,半双工
RS-485为422的改进版本,最少可用两根线通信,因为使用差分信号,不使用地线也可正常通信,但不建议,半双工
CUBMAX初始化
串口需要配置的地方不多,Mode中间设置成异步通信,在配置一下波特率,停止位,效验位。一般来说只需要配置波特率就ok。
库函数讲解:(下面的好像是从b站小蜜蜂的视频里面截取下来的图片)
*pData:发送的数据的地址
Size:发送的数据的大小
Timeout:发送数据最大时间
上图中是发送完成一半中断回调函数,不是一般。
代码讲解:
编写代码之前先讲几个我们需要用到的c函数;
sscanf:解析链接 可以将串口接收到的数据变成你想要的格式、样式
strcmp:解析链接 可以比较两个字符串是否一样(两个字符串自左向右逐个字符相比(按 ASCII 值大小相比较),直到出现不同的字符或遇 \0 为止,所以最后需要流出一个空来告诉函数停止比较。)
memset:解析链接 可以用于将一个数组清除,比如串口接收缓存,我们接收结束就可以将他全部置0;
sprintf:解析链接 可以格式化输出
中断接收函数:
HAL_UART_Receive_IT(&huart1,Rx_Data,5);
开启中断接收函数,参数: *pData:接收的数据的存放地址 Size:接收的数据的大小
每当串口接收到Size个数据就会显示接收完成,进入中断。
unsigned char Rx_Data[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
sprintf((char *)LCD_dispaly,"%5s",Rx_Data);
LCD_DisplayStringLine(Line4,LCD_dispaly);
HAL_UART_Receive_IT(&huart1,Rx_Data,5);
}
阻塞发送函数:
HAL_UART_Receive_IT(&huart1,Rx_Data,10);
unsigned char Tx_Data[20];
unsigned char Rx_Data[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
sprintf((char *)LCD_dispaly,"%5s",Rx_Data);
LCD_DisplayStringLine(Line4,LCD_dispaly);
sprintf((char *)Tx_Data,"%10s",Rx_Data);
HAL_UART_Transmit(huart,Tx_Data,10,20);
HAL_UART_Receive_IT(&huart1,Rx_Data,10);
}
将串口接收的数据发送到PC机
一些复杂一点的串口数据处理:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
sprintf((char *)LCD_dispaly,"%s",Rx_Data);
LCD_DisplayStringLine(Line4,LCD_dispaly);
sscanf((char *)Rx_Data,"%3s%3s%4s",Tx_Data1,Tx_Data2,Tx_Data3);
//使用sscanf将串口接收到的数据Rx_Data里面的数据分配给Tx_Data1,Tx_Data2,Tx_Data3
//在第十二届主观题串口接收到车牌,停车时间这样多个内容的情况下就可以使用sscanf将内容分配给其他数组
sprintf((char *)Tx_Data,"%s\r\n",Tx_Data1);
HAL_UART_Transmit(huart,Tx_Data,5,20);
//将Tx_Data1的数据加上\r\n换行后发送到pc机,对应的Tx_Data1的数据也要加2.
sprintf((char *)Tx_Data,"%s\r\n",Tx_Data2);
HAL_UART_Transmit(huart,Tx_Data,5,20);
sprintf((char *)Tx_Data,"%s\r\n",Tx_Data3);
HAL_UART_Transmit(huart,Tx_Data,6,20);
HAL_UART_Receive_IT(&huart1,Rx_Data,10);
}
如果我们需要连续发送数据,但是怕上一次发送的数据在Rx_Data里面残留着,会影响下一次的数据。我们就可以调用函数memset将Rx_Data清空置0;
memset(Rx_Data,0,sizeof(Rx_Data));
如果我们需要比较两个字符串的内容是否一样就可以调用strcmp函数;具体代码看上面的链接,十二届主观题比较车牌号是否一样。
需要注意的是两个字符串自左向右逐个字符相比(按 ASCII 值大小相比较),直到出现不同的字符或遇 \0 为止,所以最后需要流出一个空来告诉函数停止比较。
比如串口第一次接收到一个字符串为“ASCLL”,我们将它放到数组Rx_Data1里面,串口第一次接收到一个字符串为“world”,我们将它放到数组Rx_Data2里面,我们需要比较两个数组是不是一样的,我们需要定义数组Rx_Data1和Rx_Data2的大小是多少呢,不是5,是6。最后需要流出一个空来告诉函数停止比较。
如果我们需要对串口接收到的数字进行运算
串口接收到的数字是以ASCLL码的方式显示的,我们接收到’0‘,进行’0‘*3的时候,实际进行的操作是0x30*3。所以我们需要对接收到的数据进行处理。
u8 Data=Rx_Data[0]-'0';
对接收到的数字减去符号’0‘,得到的就是数字0~9;
串口接收不定长数据
1.定时器
2.空闲中断+DMA
问题和注意事项:
串口接收到的数据是以askll码表示出来的