按照此文的方法实现了串口的收发,但是实际使用中发现:
接收空闲中断的产生是在数据接收停止一个字节时产生的,但是有时由于上位机编写问题或硬件问题(本人用到的USB转串口的硬件有问题)上位机发送数据不连续,中间有时间间隔大于一个字节,从而造成无法完整接收数据。通过对空闲中断接收数据方法的分析,重新修改代码,实现规定数据格式的不定长数据的接收。
主要实现方法:
1、定义通讯协议:
第一个字节为起始符,我用的是0x7B
第二个字节为数据长度(包含起始符)
2、在接收空闲中断的产生时判断是否接收到合法数据(起始符),判断数据接收长度
3、增加了超时处理
如下是代码:
指针
定义串口结构体:
code
#define RECEIVELEN 1024#define USART_DMA_SENDING 1//发送未完成#define USART_DMA_SENDOVER 0//发送完成typedef struct{ uint8_t receive_flag:1;//空闲接收标记 uint8_t dmaSend_flag:1;//发送完成标记 uint16_t rx_len;//接收长度 uint8_t usartDMA_rxBuf[RECEIVELEN];//DMA接收缓存 uint16_t timeOutCount;//超时计数 uint8_t timeOutState;//超时状态 1:容许超时计数 2:超时}USART_REEIVETYPE;变量申明:
blog
USART_RECEIVETYPE UsartType1;串口收发用到的函数:
get
//DMA发送函数void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length){ while(UsartType1.dmaSend_flag == USART_DMA_SENDING); UsartType1.dmaSend_flag = USART_DMA_SENDING; HAL_UART_Transmit_DMA(&huart1, pdata, Length);}//DMA发送完成中断回调函数void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){ __HAL_DMA_DISABLE(huart->hdmatx); if(huart->Instance==USART1) UsartType1.dmaSend_flag = USART_DMA_SENDOVER; if(huart->Instance==USART2) UsartType2.dmaSend_flag = USART_DMA_SENDOVER;}//串口接收空闲中断void Usart1Receive_IDLE(UART_HandleTypeDef *huart){ uint32_t temp; uint8_t *p; uint16_t size; if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET)) { __HAL_UART_CLEAR_IDLEFLAG(&huart1); HAL_UART_DMAStop(&huart1); temp = huart1.hdmarx->Instance->CNDTR; UsartType1.rx_len += RECEIVELEN - temp; //计算数据长度,RECEIVELEN - temp为本次长度 //判断是否为数据开始,判断起始位 if(UsartType1.usartDMA_rxBuf[0]==0x7b) { //判断接收数据长度是否符合 if(UsartType1.rx_len>2)//防止上位机在发了起始位后就有空闲中断产生 { if(UsartType1.usartDMA_rxBuf[1]<=UsartType1.rx_len)//数据接收完整,buff[0]:数据头,buff[1]数据长度(包含头) { //接收标志位=1, UsartType1.receive_flag=1; //下次接收缓存指针从头开始, p=UsartType1.usartDMA_rxBuf; //接收缓存大小=RECEIVELEN, size=RECEIVELEN; //禁止定时器开始对timeOutCount减计数 UsartType1.timeOutState=0; } else { //下次接收缓存指针为UsartType1.usartDMA_rxBuf+已经接收到的长度, p=UsartType1.usartDMA_rxBuf+UsartType1.rx_len; //大小为RECEIVELEN-已经接收到的长度, size=RECEIVELEN-UsartType1.rx_len; //给timeout填值 UsartType1.timeOutCount=1000; UsartType1.timeOutState=1;//容许定时器开始对timeOutCount减计数 } } else { //下次接收缓存指针为UsartType1.usartDMA_rxBuf+已经接收到的长度, p=UsartType1.usartDMA_rxBuf+UsartType1.rx_len; //大小为RECEIVELEN-已经接收到的长度, size=RECEIVELEN-UsartType1.rx_len; UsartType1.timeOutCount=1000; UsartType1.timeOutState=1;//容许定时器开始对timeOutCount减计数 } } else { UsartType1.rx_len=0;//Reset UsartType1 p=UsartType1.usartDMA_rxBuf; size=RECEIVELEN; //禁止定时器开始对timeOutCount减计数 UsartType1.timeOutState=0; } HAL_UART_Receive_DMA(&huart1,p,size);//设置DMA接收缓存和大小,为下次接收作准备 }}
超时计数:
我这里用的是SYSTick的中断,每1ms产生一次中断。
回调函数
void SysTick_Handler(void){ /* USER CODE BEGIN SysTick_IRQn 0 */ if(UsartType1.timeOutCount!=0&UsartType1.timeOutState==1)//USART1超时计数 { UsartType1.timeOutCount--; //判断是否发生超时 if(UsartType1.timeOutCount==0) { UsartType1.timeOutState=2; UsartType1.rx_len=0; //超时发生后,从新设置DMA缓存 HAL_UART_DMAStop(&huart1); HAL_UART_Receive_DMA(&huart1,UsartType1.usartDMA_rxBuf,RECEIVELEN); } } /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); HAL_SYSTICK_IRQHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */}
主程序初始化时,打开串口DMA接收
it
/* USER CODE BEGIN 2 */ HAL_UART_Receive_DMA(&huart1, UsartType1.usartDMA_rxBuf, RECEIVELEN); __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); /* USER CODE END 2 */while(1)中处理数据:
//正常接收到数据 if(UsartType1.receive_flag) { UsartType1.receive_flag=0;//清零标记 Usart1SendData_DMA(UsartType1.usartDMA_rxBuf,UsartType1.rx_len);//串口打印收到的数据。 UsartType1.rx_len=0; } //此处为超时处理 if(UsartType1.timeOutState==2) { UsartType1.timeOutState=0; }
按照此文的方法实现了串口的收发,但是实际使用中发现:
接收空闲中断的产生是在数据接收停止一个字节时产生的,但是有时由于上位机编写问题或硬件问题(本人用到的USB转串口的硬件有问题)上位机发送数据不连续,中间有时间间隔大于一个字节,从而造成无法完整接收数据。通过对空闲中断接收数据方法的分析,重新修改代码,实现规定数据格式的不定长数据的接收。
主要实现方法:
1、定义通讯协议:
第一个字节为起始符,我用的是0x7B
第二个字节为数据长度(包含起始符)
2、在接收空闲中断的产生时判断是否接收到合法数据(起始符),判断数据接收长度
3、增加了超时处理
如下是代码:指针
定义串口结构体:code
#define RECEIVELEN 1024#define USART_DMA_SENDING 1//发送未完成#define USART_DMA_SENDOVER 0//发送完成typedef struct{ uint8_t receive_flag:1;//空闲接收标记 uint8_t dmaSend_flag:1;//发送完成标记 uint16_t rx_len;//接收长度 uint8_t usartDMA_rxBuf[RECEIVELEN];//DMA接收缓存 uint16_t timeOutCount;//超时计数 uint8_t timeOutState;//超时状态 1:容许超时计数 2:超时}USART_REEIVETYPE;变量申明:blog
USART_RECEIVETYPE UsartType1;串口收发用到的函数:get
//DMA发送函数void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length){ while(UsartType1.dmaSend_flag == USART_DMA_SENDING); UsartType1.dmaSend_flag = USART_DMA_SENDING; HAL_UART_Transmit_DMA(&huart1, pdata, Length);}//DMA发送完成中断回调函数void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){ __HAL_DMA_DISABLE(huart->hdmatx); if(huart->Instance==USART1) UsartType1.dmaSend_flag = USART_DMA_SENDOVER; if(huart->Instance==USART2) UsartType2.dmaSend_flag = USART_DMA_SENDOVER;}//串口接收空闲中断void Usart1Receive_IDLE(UART_HandleTypeDef *huart){ uint32_t temp; uint8_t *p; uint16_t size; if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET)) { __HAL_UART_CLEAR_IDLEFLAG(&huart1); HAL_UART_DMAStop(&huart1); temp = huart1.hdmarx->Instance->CNDTR; UsartType1.rx_len += RECEIVELEN - temp; //计算数据长度,RECEIVELEN - temp为本次长度 //判断是否为数据开始,判断起始位 if(UsartType1.usartDMA_rxBuf[0]==0x7b) { //判断接收数据长度是否符合 if(UsartType1.rx_len>2)//防止上位机在发了起始位后就有空闲中断产生 { if(UsartType1.usartDMA_rxBuf[1]<=UsartType1.rx_len)//数据接收完整,buff[0]:数据头,buff[1]数据长度(包含头) { //接收标志位=1, UsartType1.receive_flag=1; //下次接收缓存指针从头开始, p=UsartType1.usartDMA_rxBuf; //接收缓存大小=RECEIVELEN, size=RECEIVELEN; //禁止定时器开始对timeOutCount减计数 UsartType1.timeOutState=0; } else { //下次接收缓存指针为UsartType1.usartDMA_rxBuf+已经接收到的长度, p=UsartType1.usartDMA_rxBuf+UsartType1.rx_len; //大小为RECEIVELEN-已经接收到的长度, size=RECEIVELEN-UsartType1.rx_len; //给timeout填值 UsartType1.timeOutCount=1000; UsartType1.timeOutState=1;//容许定时器开始对timeOutCount减计数 } } else { //下次接收缓存指针为UsartType1.usartDMA_rxBuf+已经接收到的长度, p=UsartType1.usartDMA_rxBuf+UsartType1.rx_len; //大小为RECEIVELEN-已经接收到的长度, size=RECEIVELEN-UsartType1.rx_len; UsartType1.timeOutCount=1000; UsartType1.timeOutState=1;//容许定时器开始对timeOutCount减计数 } } else { UsartType1.rx_len=0;//Reset UsartType1 p=UsartType1.usartDMA_rxBuf; size=RECEIVELEN; //禁止定时器开始对timeOutCount减计数 UsartType1.timeOutState=0; } HAL_UART_Receive_DMA(&huart1,p,size);//设置DMA接收缓存和大小,为下次接收作准备 }}
超时计数:
我这里用的是SYSTick的中断,每1ms产生一次中断。回调函数
void SysTick_Handler(void){ /* USER CODE BEGIN SysTick_IRQn 0 */ if(UsartType1.timeOutCount!=0&UsartType1.timeOutState==1)//USART1超时计数 { UsartType1.timeOutCount--; //判断是否发生超时 if(UsartType1.timeOutCount==0) { UsartType1.timeOutState=2; UsartType1.rx_len=0; //超时发生后,从新设置DMA缓存 HAL_UART_DMAStop(&huart1); HAL_UART_Receive_DMA(&huart1,UsartType1.usartDMA_rxBuf,RECEIVELEN); } } /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); HAL_SYSTICK_IRQHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */}
主程序初始化时,打开串口DMA接收
it
/* USER CODE BEGIN 2 */ HAL_UART_Receive_DMA(&huart1, UsartType1.usartDMA_rxBuf, RECEIVELEN); __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); /* USER CODE END 2 */while(1)中处理数据:
//正常接收到数据 if(UsartType1.receive_flag) { UsartType1.receive_flag=0;//清零标记 Usart1SendData_DMA(UsartType1.usartDMA_rxBuf,UsartType1.rx_len);//串口打印收到的数据。 UsartType1.rx_len=0; } //此处为超时处理 if(UsartType1.timeOutState==2) { UsartType1.timeOutState=0; }
举报