【STM32】串口不定长接收 保姆级教程

为什么要使用串口不定长接收

正常的接收函数

HAL_UART_Receive(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size,uint32_t Timeout)
huart:句柄
pData:盛放接收数据的变量
Size:接收数据的大小

中断接收函数

HAL_UART_Receive_IT(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size)
huart:句柄
pData:盛放接收数据的变量
Size:接收数据的大小

        这两个接收函数有一个共有特性,就是定长接收,Size参数就是接收数据的长度,当串口发送长度不固定的数据时,会出现吞字符的现象,会使指令信息不完整。所以,为了保证下位机接收到完整的指令,需要串口可以完整接收不定长度的数据,常用方法有两个,一是使用串口空闲中断实现,二是使用串口空闲中断+DMA实现。

什么是串口空闲中断

        串口空闲中断是指当串口接收缓冲区中没有数据时,串口控制器产生的一种中断信号。在串口通信中,当接收到一个完整的数据帧后,通常会有一个停止位,表示数据的传输结束。当接收缓冲区中没有数据时,串口控制器会检测到停止位的连续空闲状态,并产生空闲中断信号。使用串口空闲中断可以消除轮训接收缓冲区的需要,提高系统的性能和效率。

注意:

(1)空闲中断的允许需要手动解除中断屏蔽

(2)空闲中断必须手动清零,不然中断会一直置位,系统会不断进入中断处理函数

方法一:通过串口空闲中断 实现串口不定长接收

思路:

        每次接收一个字符触发中断,这样每次进接收中断就保存下当前接到的字符到一个数组里,然后下一个字符来的时候就继续保存到数组里的下一个元素中,以此类推每次中断保存一个字符,那当发完的时候就保存完了,同时触发空闲中断,在空闲中断中那个保存数组就是接收到的数据。然后就可以对这个数组进行判断发送等操作了。

步骤:

 一定要注意相关函数添加在的位置!!!!

1、

2、

3、

4、

5、

6、

相关部分代码

1、main.c 内 设计全局变量

/* USER CODE BEGIN 0 */
	uint8_t buf[128]={0};
	 uint8_t buf_recv[64];
/* USER CODE END 0 */

2、主函数

//串口空闲中断+串口接收中断
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);	//使能空闲中断
HAL_UART_Receive_IT(&huart1,buf,1);//每次接收一个,然后每次都进中断

3、stm32g0xx_it.c 内修改中断函数中的全局变量

/* USER CODE BEGIN 0 */
uint8_t len=0;
extern uint8_t buf_recv[64];
uint8_t buff[64];
/* USER CODE END 0 */

4、修改中断函数  记得添加头文件#include<string.h>

  /* USER CODE BEGIN USART1_IRQn 0 */

    //使用串口接收中断+空闲中断
	if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))	
	{                               // 1. 获得串口空闲中断标志	
		__HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF);	 // 2. 清除空闲中断标志
		//3.这里写中断执行
		HAL_UART_Transmit(&huart1,buf_recv,sizeof(buf_recv),1000);
		len=0;
		memset(buf_recv,0,sizeof(buf_recv));
	}else
	{			
				buf_recv[len]=USART1->RDR; 
				len++;
				HAL_UART_Receive_IT(&huart1,buff,1);
	}

  /* USER CODE END USART1_IRQn 0 */

方法二:串口空闲中断+DMA

原理: 接收足够的长度,当串口空闲时进中断,直接计算当前接收数组实际接收值,然后停止接收即可。

步骤:

1、main.c

2、

3、中断函数改写

 

 

 效果演示

相关代码 

注意代码位置

1、main.c定义全局变量

/* USER CODE BEGIN 0 */
uint8_t buf_ctl[64];
/* USER CODE END 0 */

2、循环接收数据

while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		       HAL_UART_Receive_DMA(&huart1,buf_ctl,64);	//接收DMA使能
		__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);	//开启串口空闲中断

  }

3、中断函数 添加头文件#include<string.h>

添加全局变量

/* USER CODE BEGIN TD */
extern uint8_t buf_ctl[64];//拿到main.c的buf_ctl
uint8_t len;

/* USER CODE END TD */

修改中断函数

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))	
	{                               // 1. 获得串口空闲中断标志	
		__HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF);													 // 2. 清除空闲中断标志
		HAL_UART_DMAStop(&huart1);	 // 3. 清除->空闲->停止DMA		
		len = 64 - hdma_usart1_rx.Instance->CNDTR;
            // 4. 设定的传输长度-剩余传输数量(DMA_CNDTRx)=实际长度
		buf_ctl[len]='\0';
		HAL_UART_Transmit(&huart1,buf_ctl,sizeof(buf_ctl),500);
		memset(buf_ctl,0,sizeof(buf_ctl));
		HAL_UART_Receive_DMA(&huart1, buf_ctl, 64);
								     // 6. 重新开启DMA
	}
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32 上实现串口不定接收,可以使用中断方式来处理。以下是一个基本的流程: 1. 初始化串口: - 配置串口波特率、数据位、停止位、校验位等参数; - 使能串口中断。 2. 实现串口中断处理函数,用于接收数据: - 在中断处理函数中,如果接收数据,则将数据存储到缓冲区中; - 如果接收到的数据度超过缓冲区度,则需要处理溢出情况。 3. 判断缓冲区中是否有完整的数据包: - 可以根据数据包的头部和尾部进行判断; - 如果有完整的数据包,则可以将数据包取出进行处理。 以下是一个简单的代码示例: ```c #define BUFFER_SIZE 256 // 缓冲区大小 uint8_t buffer[BUFFER_SIZE]; // 缓冲区 uint8_t index = 0; // 当前缓冲区中已经接收到的数据度 // 串口中断处理函数 void USART2_IRQHandler(void) { if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) // 接收数据 { uint8_t data = USART_ReceiveData(USART2); // 读取接收到的数据 if (index < BUFFER_SIZE) // 缓冲区未满 { buffer[index++] = data; // 将数据存储到缓冲区中 } else // 缓冲区已满,需要处理溢出情况 { index = 0; // 重置缓冲区指针,覆盖之前的数据 } } } // 判断缓冲区中是否有完整的数据包 bool isPacketComplete(void) { if (index < 2) // 数据包头部度为2字节 { return false; } uint16_t length = buffer[0] << 8 | buffer[1]; // 数据度为头部中的2字节 if (index < length + 2) // 数据度为数据度加上头部度 { return false; } return true; } int main(void) { // 初始化串口 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USART_InitStructure); USART_Cmd(USART2, ENABLE); // 使能串口中断 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPriority = 0; NVIC_Init(&NVIC_InitStructure); while (1) { if (isPacketComplete()) // 如果有完整的数据包 { // 处理数据包,例如解析数据包中的命令和参数 // ... index = 0; // 清空缓冲区 } } } ``` 在上面的代码中,我们使用一个缓冲区来存储接收到的数据,使用一个指针 `index` 来记录当前缓冲区中已经接收到的数据度。当缓冲区已满时,我们重新从缓冲区开头开始存储数据,覆盖之前的数据。在 `isPacketComplete()` 函数中,我们根据数据包的头部和尾部来判断是否有完整的数据包,如果有,则将数据包取出进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值