串口通讯
串口通讯代码
补充一下串口知识:
1、接收采用中断是为了能及时迅速接收。及时响应。
2、中断标志位写法
3.标志位使用方法:先判断标志位是否为1,出去之前清空标志位
——————————————————————main.py——————————————————————————————————
while(1)//反复检查标志位
{
if(rxd_flag=1)//标志位,说明一个数据包接收完毕
{
process_data();
rxd_flag=0;//标志位=0,是在处理数据之后,表明rxd_flag=0时,数据处理完毕
}
}
——————————————————————————————————————————————————————————————————
_______________________usart.py__________________________________
uint8_t rx_buffer[100];//不固定保持,数据缓冲去设置大些
uint8_t rxd_flag;
void USART2_IRQHandler(void)
{
uint8_t Recv_data;
static uint8_t recv_state = 0;
static uint8_t rx_buffer_index = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断
{
uint8_t Recv_data = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量
/*使用状态机的思路,依次处理数据包的不同部分*/
switch(recv_state)
{
case 0:
{
//# !rxd_flag 用于判断是否缓存区数据全部处理完,处理完之后才能处理新的数据。#
if(Recv_data=='$' && !rxd_flag) //判断包头,是包头就进入下一个状态
{
rx_buffer_index = 0;
recv_state=1;
}
else
{
recv_state=0; //不是包头,就保持当前状态,一直等待包头
}
}
break;
case 1:
{
if(Recv_data=='\r')//如果是\r,进入下一个阶段,不是就保存数据到rx_buffer中
{
recv_state=2;
}
else
{
rx_buffer[rx_buffer_index++]=Recv_data;
}
}
break;
case 2:
{
if(Recv_data=='\n')
{
rxd_flag= 1;////标志位=1,是在接收完一包数据状态,表明rxd_flag=1时,一包数据接收完毕
recv_state=0;
}
}
break;
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除标志位
}
}
_________________________________________________________
1 发送数据
/**
* 函 数:串口发送一个字节
* 参 数:Byte 要发送的一个字节
* 返 回 值:无
*/
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte); //将字节数据写入数据寄存器,写入后USART自动生成时序波形
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完成
/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}
/**
* 函 数:使用printf需要重定向的底层函数
* 参 数:保持原始格式即可,无需变动
* 返 回 值:保持原始格式即可,无需变动
*/
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch); //将printf的底层重定向到自己的发送字节函数
return ch;
}
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1; //设置结果初值为1
while (Y --) //执行Y次
{
Result *= X; //将X累乘到结果
}
return Result;
}
/**
* 函 数:串口发送数字
* 参 数:Number 要发送的数字,范围:0~4294967295
* 参 数:Length 要发送数字的长度,范围:0~10
* 返 回 值:无
*/
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++) //根据数字长度遍历数字的每一位
{
Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0'); //依次调用Serial_SendByte发送每位数字
}
}
2 接收数据
2.1 如果说你的数据包协议比较简单,我们可以在数据接收的时候,在串口中断里面实时解析。

2.1.1接收固定包长 :
数据格式:[ 0XFF 0X01 0X02 0X03 0X04 0XFE ]
在接收中断接收一个字节后,用状态机来接收该字节(会多次反复进入状态机,每来一个数据,进入一次状态机)
uint8_t rx_buffer[4];
uint8_t rx_buffer_RxFlag;
void USART2_IRQHandler(void)
{
uint8_t Recv_data;
static uint8_t recv_state = 0;
static uint8_t rx_buffer_index = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断
{
uint8_t Recv_data = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量
/*使用状态机的思路,依次处理数据包的不同部分*/
switch(recv_state)
{
case 0:
{
if(Recv_data==0xFF) //判断包头,是包头就进入下一个状态
{
rx_buffer_index=0;
recv_state=1;
}
else
{
recv_state=0; //不是包头,就保持当前状态,一直等待包头
}
}
break;
case 1:
{
rx_buffer[rx_buffer_index++]=Recv_data;
if(rx_buffer_index>=4)
{
recv_state=2;
}
}
break;
case 2:
{
if(Recv_data==0xFE)
{
recv_state=0;
rx_buffer_RxFlag=1;
}
}
break;
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除标志位
}
}
2.1.2接收可变包长 :

uint8_t rx_buffer[100];//不固定保持,数据缓冲去设置大些
uint8_t rx_buffer_RxFlag;
void USART2_IRQHandler(void)
{
uint8_t Recv_data;
static uint8_t recv_state = 0;
static uint8_t rx_buffer_index = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断
{
uint8_t Recv_data = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量
/*使用状态机的思路,依次处理数据包的不同部分*/
switch(recv_state)
{
case 0:
{
if(Recv_data=='$') //判断包头,是包头就进入下一个状态
{
rx_buffer_index = 0;
recv_state=1;
}
else
{
recv_state=0; //不是包头,就保持当前状态,一直等待包头
}
}
break;
case 1:
{
if(Recv_data=='\r')//如果是\r,进入下一个阶段,不是就保存数据到rx_buffer中
{
recv_state=2;
}
else
{
rx_buffer[rx_buffer_index++]=Recv_data;
}
}
break;
case 2:
{
if(Recv_data=='\n')
{
rx_buffer_RxFlag = 1;
recv_state=0;
}
}
break;
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除标志位
}
}
补充:发送方发送数据过快,接收来不及处理
一般而言,单片机发送方,发送一个数据包到下一个发送新的数据包,有一定的间隔,这段时间足够,单片机接收方,解析并处理了该数据。
但是如果单片机发送方发送数据过快,接收方来不及处理怎么办?
思路1:双缓冲区,
:思路2:接收方接收的数据过快,新的数据会覆盖缓冲区,怎么办?老的数据如果没有处理完,新的数据就不保存到缓冲区。
但是这种方法其实不好,部分新的数据会丢失。
mian.py
这种标志位的用法,进去之前判断标志位==1,出去之前标志位清零。
gpio_init():
usart_init();
led_init();
while(1)
{
if(rxd_flag=1)//标志位,说明一个数据包接收完毕
{
process_data();
rxd_flag=0;//标志位,说明一个数据包处理完毕,可以再次接受新的数据
}
}
usart.py
uint8_t rx_buffer[100];//不固定保持,数据缓冲去设置大些
uint8_t rx_buffer_RxFlag;
void USART2_IRQHandler(void)
{
uint8_t Recv_data;
static uint8_t recv_state = 0;
static uint8_t rx_buffer_index = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断
{
uint8_t Recv_data = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量
/*使用状态机的思路,依次处理数据包的不同部分*/
switch(recv_state)
{
case 0:
{
//# !rxd_flag 用于判断是否缓存区数据全部处理完,处理完之后才能处理新的数据。#
if(Recv_data=='$' && !rxd_flag) //判断包头,是包头就进入下一个状态
{
rx_buffer_index = 0;
recv_state=1;
}
else
{
recv_state=0; //不是包头,就保持当前状态,一直等待包头
}
}
break;
case 1:
{
if(Recv_data=='\r')//如果是\r,进入下一个阶段,不是就保存数据到rx_buffer中
{
recv_state=2;
}
else
{
rx_buffer[rx_buffer_index++]=Recv_data;
}
}
break;
case 2:
{
if(Recv_data=='\n')
{
rx_buffer_RxFlag = 1;
recv_state=0;
}
}
break;
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除标志位
}
}
接收一个字节数据,马上处理该数据(暂时保留)
/**
***********************************************************************
包格式:帧头0 帧头1 数据长度高8位 数据长度低8位 功能字 和校验高8位 和校验低8位
0x42 0x4d 0x00 0x1C ... ... ... ... SumH SumL 总计32字节
***********************************************************************
*/
#define FRAME_HEAD_0 0x42
#define FRAME_HEAD_1 0x4D
#define PACKET_DATA_LEN 32 //包长度
void USART0_IRQHandler(void)
{
if (usart_interrupt_flag_get(g_uartHwInfo.uartNo, USART_INT_FLAG_RBNE) != RESET)
{
usart_interrupt_flag_clear(g_uartHwInfo.uartNo, USART_INT_FLAG_RBNE);
uint8_t uData = (uint8_t)usart_data_receive(g_uartHwInfo.uartNo);
//接收完数据,马上处理
ProcUartData(uData);
}
}
//实现来了一个数据,是包头和包尾剔除,保存载荷数据,当接收一个完整的数据包,标志位置1
//标志位置1,用于通知处理该数据包。
static void ProcUartData(uint8_t data)//这个函数会自动反复执行,因为中断中调用该函数
{
static uint8_t s_index = 0;//静态变量,只要第一次进入赋值为0,以后进入不在初始化。
g_rcvDataBuf[s_index++] = data;
switch (s_index)//S_index 是数组的索引。
{
case 1:
if (g_rcvDataBuf[0] != FRAME_HEAD_0)//如果不是包头1,索引设置为0
{
s_index = 0;
}
break;
case 2:
if (g_rcvDataBuf[1] != FRAME_HEAD_1)//包头1成立,但包头2不成立,索引设置为0
{
s_index = 0;
}
break;
case PACKET_DATA_LEN://当数组长度存够32个字节(一个数据包为32个字节)
g_pktRcvd = true;//标志位1,标志着一个完整数据包接收完毕
s_index = 0;
break;
default:
break;
}
}
uint8_t Serial_RxData[100]; //定义串口接收的数据变量
uint8_t Serial_RxFlag; //定义串口接收的标志位变量
/**
* 函 数:USART1中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
*/
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断,防止其他中断进入
{
while(USART_GetITStatus(USART1, USART_IT_RXNE) == RESET);//等待当前字节接收结束
//如果USART_GetITStatus(USART1, USART_IT_RXNE) == RESET,就反复执行空语句,一直等待、
//直到USART_GetITStatus(USART1, USART_IT_RXNE) == SET,跳出循环
Serial_RxData[res1] = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量
res1++;
Serial_RxFlag = 1; //置接收标志位变量为1
USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除USART1的RXNE标志位
//读取数据寄存器会自动清除此标志位
//如果已经读取了数据寄存器,也可以不执行此代码
}
}
2.1.3 不带针头和帧尾的数据包:串口中断+定时中断超时接收数据包的编程方法

这种数据包没有涵盖帧头和帧尾,无法通过帧头和帧尾去判断数据包的完整性,只能通过时间间隔来判别我们数据包的完整性,我们标准的modus数据协议就是这种,数据包和数据包之间有一定的间隔,
补充:串口中断需要中断优先级比定时器优先级设置高一下,防止漏数据。

数据包间隔一般设置为4~10个数据间隔。
9600波特率 9600个bit/1 s
1个bit 1/9600s
1个字节(1个数据帧10位) 1/960010
10个字节(1个数据帧10位) 1/960010*10 S = 10ms
-------------------------time.py----------------------------
uint8_t rxd_cnt;
uint8_t rxd_flag =0;
uint8_t rxd_buf[100];
uint8_t rxd_index = 0;
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
rxd_cnt++;//每过1ms 计数变量+1
if(rxd_cnt>=10)//当没有接收数据超过10ms。
{
rxd_cnt = 0;
rxd_flag = 1;//标识数据包接收完成
}
TIM_ClearITPendingBit(TIM2 , TIM_IT_Update);
}
}
--------------------------------------------------------------
--------------------------usart.py-----------------------------
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART1 , USART_IT_RXNE == SET ))
{
rxd_buf[rxd_index++] = USART_ReceiveData(USART1);
rxd_cnt = 0;//只要接收到数据就rxd_cnt = 0,清零,
//每接收一次数据,都会清零,因此接收数据时无法累加,只要在没有接收数据时,才计数累加。
USART_ClearITPendingBit(USART1 , USART_IT_RXNE);
}
}
---------------------------------------------------------------------


3234

被折叠的 条评论
为什么被折叠?



