目录
- 1、串行通信的基本参数
- 2、轮询方式代码效果
- 3、中断方式代码效果
- 4、中断加上时间戳方式代码及效果
- 5、DMA空闲中断方式接收数据
1、串行通信的基本参数
串行端口的通信方式是将字节拆分成一个接一个的位再传输出去,接收方再将此一个一个的位组合成原来的字符,如此形成一个字节的完整传输,在数据传输时,应在通信端口的初始化时设置几个通信参数。
1)波特率,即传送数据的速度。波特率的意思就是在一秒中可以传输的数据位数,单位是bps。如果采用波特率4800bps进行传输,那么每秒可以传输600个byte。
2)数据位,当接收设备收到起始位后,紧接着就会收到数据位,数据位的个数可以是5、6、7或者8位。在字符数据传输的过程中,数据位从最低有效位开始传输。
3)起始位,在串口线上,没有数据传输时处于逻辑"1"状态,当发送设备要发送一个字符数据时,首先发出一个逻辑“0"信号,这个逻辑低电平就是起始位。
4)停止位,是一个字符数据的结束标志,它可以是1位、1.5位或者2位。
5)奇偶校验位,数据位发送完之后,就可以发送奇偶校验位。奇偶校验用于有限差错校验,通信双方在通信时约定一致的奇偶校验方式。
2、轮询方式代码效果
char ch;
/* Infinite loop */
for(;;)
{
if(HAL_UART_Receive(&huart1,(uint8_t*)&ch,1,100) == HAL_OK)printf("%c",ch);//osDelay(1);
}
轮询方式不适合接收一大段的数据,否则会卡死,如上图。
3、中断方式代码效果
#define MAX_RECV_LEN 128 //定义的一次最多接收字节的位数
uint8_t rx1_buff[MAX_RECV_LEN] = {0}; //串口接收数据缓冲
uint8_t *pBuf; //当前接收字节存放的位置指针
uint8_t line_flag = 0; //一行数据接收结束标志
void StartTaskUsart(void *argument)
{/* USER CODE BEGIN StartTaskUsart */printf("HELLO WORLD\n");pBuf = rx1_buff;HAL_UART_Receive_IT(&huart1,pBuf, 1); /* Infinite loop */for(;;){if(line_flag){printf("%s",rx1_buff);line_flag=0;}osDelay(1);}/* USER CODE END StartTaskUsart */
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{ if (UartHandle->Instance == USART1) { ++ pBuf; // 已接收一个字节数据,当前存储位置指针后移 if(pBuf == rx1_buff + MAX_RECV_LEN) // 如果指针已移出数组边界 pBuf = rx1_buff; // 重新指向数组开头 else if(*(pBuf - 1) == '\n') // 如果之前接收到‘\n’换行符,则表示接收完成 {line_flag = 1; // 行接收标志置1*pBuf = '\0'; // 添加字符串末尾结束符pBuf = rx1_buff; // 重新指向数组开头}__HAL_UNLOCK(UartHandle); // 解锁串口HAL_UART_Receive_IT(UartHandle, pBuf, 1); // 重新开启接收中断}
}
缺点:由于中断接收是以换行符为标准的,这里我们按下两次发送键才将4个数字全部发送。
4、中断加上时间戳方式代码及效果
利用时间戳来辅助判断接收空闲,实现了分段发送。
for(;;){if(recv_tick>0 && (osKernelGetTickCount()-recv_tick)>=20){*pBuf = '\0'; //添加字符串末尾结束符printf("%s",rx1_buff);recv_tick=0;pBuf = rx1_buff;//重新指向数组开头}}
//加了时间戳的完善代码
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{ if (UartHandle->Instance == USART1) { ++ pBuf; // 已接收一个字节数据,当前存储位置指针后移 if(pBuf == rx1_buff + MAX_RECV_LEN) // 如果指针已移出数组边界 pBuf = rx1_buff; // 重新指向数组开头 recv_tick = osKernelGetTickCount();__HAL_UNLOCK(UartHandle); // 解锁串口HAL_UART_Receive_IT(UartHandle, pBuf, 1); // 重新开启接收中断}
}
5、DMA空闲中断方式接收数据
步骤总结:
1、开启串口DMA接收
2、串口收到数据,DMA不断传输数据到接收缓冲
3、一帧数据发送完毕,串口暂时空闲,触发串口空闲中断
4、在串口中断函数中,计算刚才收到的数据长度
5、复制接收缓冲数据,清除标志位,开始下一帧接收
//添加串口数据缓冲、DMA数据缓冲、接收标志
uint8_t data_buff[MAX_BUFF_LEN] = {0}; //串口接收数据缓冲
uint8_t dma_buff[MAX_BUFF_LEN] = {0}; //DMA数据缓冲
uint32_t recv_len = 0; //接收数据长度
#define MAX_BUFF_LEN 512
extern uint8_t data_buff[MAX_BUFF_LEN];
extern uint8_t dma_buff[MAX_BUFF_LEN];
extern uint32_t recv_len;
extern void UART_IDLE_Callback(UART_HandleTypeDef *huart);
void UART_IDLE_Callback(UART_HandleTypeDef *huart)
{if (__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET &&huart->Instance == USART1){__HAL_UART_CLEAR_IDLEFLAG(huart);HAL_UART_DMAStop(huart);recv_len = MAX_BUFF_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);if (recv_len < MAX_BUFF_LEN - 1){memcpy(data_buff, dma_buff, recv_len);data_buff[recv_len] = '\0';}else{recv_len = 0;}__HAL_UNLOCK(huart);HAL_UART_Receive_DMA(huart, dma_buff, MAX_BUFF_LEN);}
}
void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 */UART_IDLE_Callback(&huart1);/* USER CODE END USART1_IRQn 1 */
}
void StartTaskUsart(void *argument)
{/* USER CODE BEGIN StartTaskUsart */__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能idle中断HAL_UART_Receive_DMA(&huart1,dma_buff,MAX_BUFF_LEN);//打开DMA接收,数据存入dma_buff数组中/* Infinite loop */for(;;){if(recv_len>0){printf("%s",(char *)data_buff);recv_len=0;}}osDelay(1);/* USER CODE END StartTaskUsart */
}
这种方法效果极其有效:
使用串口调试助手软件,定时每隔20ms发送总共100个字节的多行字符串对STM32串口接收功能进行测试。
如图所示,发送数据个数超过50000字节后停止发送,接收到的STM32回送数据个数与发送数据个数相同。