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回送数据个数与发送数据个数相同。
在这里插入图片描述