串口+DMA+空闲中断 实现不定长数据的接收与发送

今天学了个新的知识点  :  空闲中断          

是接收不定长数据的大杀器

感觉比我上一篇文章的环形队列方便多了,有了这个工具,就不用每次接收一位数据都进入一次中断,而且不用判断数据是否接收完毕。

降低了cup算力的消耗。

本次主要介绍空闲中断。

1 空闲中断

空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。空闲中断的优点在于省去了帧头帧尾的检测,进入中断程序即意味着已经接收到一组完整数据,仅需及时对数据处理或将数据转移出缓冲区即可。

串口空闲中断在串口无数据接收的情况下,是不会产生的,产生的条件是当清除空闲标志位后,必须有接收到第一个数据后,才开始触发,一旦接收的数据断流,没有接收到数据,即产生空闲中断

当检测到总线空闲,即判断接收一个字节后的下一个字节时间内,是否继续接收数据,如果超出这个字节时间,并且判断CR1的第四位是否为1(是否使能空闲中断),如果是则会产生空闲中断。

2 题目要求

电脑给单片机发送不定长数据,单片机用串口通过DMA接收,保存到一个数组里,然后再发给电脑。

3 cubemx 配置

1 设置时钟频率 72mhz  使用外部晶振

2 使能串口1  设置串口中断以及优先级,接收和发送都使用DMA,接收DMA使用循环模式 ,发送DMA使用普通模式,收发DMA都是存储器增量模式。

4 程序框图

5 程序代码

main.c

uint16_t    buf[20] = {0};  	        // 收发数组
uint8_t 		R_long = 0;  			// 接收长度
uint8_t  		buf_long = 0;  		    // 收发数组的长度


int main(void)
{
    HAL_Init();
    SystemClock_Config();

    MX_GPIO_Init();
    MX_DMA_Init();
	
    MX_USART1_UART_Init();
    __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);//使能空闲中断

    buf_long = sizeof buf/sizeof buf[0]; //得到 buf 数组的长度
    HAL_UART_Receive_DMA(&huart1,(uint8_t *)buf,buf_long); //使能串口DMA接收
	
  while (1)
  {
		
  }
}

使能串口DMA接收和空闲中断后,此时只要接收数据停止时,就会触发空闲中断,进入串口1中断函数中

void USART1_IRQHandler(void)
{
	

	if (__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET)         //判断是否时空闲中断
	{
	__HAL_UART_CLEAR_IDLEFLAG(&huart1);					            //清除空闲中断标志
	HAL_UART_DMAStop(&huart1);		                	            //停止DMA
	R_long = buf_long - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);	    //接收了多长的数据
	HAL_UART_Transmit_DMA(&huart1,(uint8_t *)buf,R_long);			//使能DMA串口发送
	}

  HAL_UART_IRQHandler(&huart1);

}

串口发送完成之后会进入发送完成回调函数中

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) 
{
  if (huart->Instance == USART1)
	{
    R_long = 0;                 // 清除发送长度
    memset(buf,0,R_long);        //清空收发数组
    HAL_UART_Receive_DMA(&huart1,(uint8_t *)buf,buf_long);    //使能接收
  }
}

6 实验现象

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用DMA空闲中断接收不定数据的步骤如下: 1. 初始化串口DMA:配置串口接收模式,启用DMA传输;配置DMA为循环模式,传输大小为1字节,传输方向为从串口接收数据到内存。 2. 开启DMA传输:调用DMA启动函数启动DMA传输。 3. 开启串口接收中断:调用串口中断使能函数,开启空闲中断。 4. 在空闲中断中处理数据:当DMA传输完成并且串口没有接收到新数据时,说明接收完成,可以在空闲中断中处理接收到的数据。 下面是一个简单的例子: ```c #include "stm32f4xx_hal.h" #define RX_BUF_SIZE 256 UART_HandleTypeDef huart1; DMA_HandleTypeDef hdma_usart1_rx; uint8_t rx_buf[RX_BUF_SIZE]; uint8_t rx_len = 0; uint8_t rx_flag = 0; void UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } hdma_usart1_rx.Instance = DMA2_Stream2; hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4; hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_rx.Init.Mode = DMA_CIRCULAR; hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx); HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); HAL_UART_Receive_DMA(&huart1, rx_buf, RX_BUF_SIZE); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { rx_len += RX_BUF_SIZE - hdma_usart1_rx.Instance->NDTR; HAL_UART_Receive_DMA(huart, rx_buf, RX_BUF_SIZE); } } void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { rx_len += RX_BUF_SIZE / 2 - hdma_usart1_rx.Instance->NDTR; } } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { __HAL_UART_CLEAR_PEFLAG(&huart1); HAL_UART_Receive_DMA(huart, rx_buf, RX_BUF_SIZE); } } void DMA2_Stream2_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart1_rx); } void HAL_UART_IdleCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { if (hdma_usart1_rx.Instance->NDTR == RX_BUF_SIZE) { rx_len = RX_BUF_SIZE; rx_flag = 1; } else { rx_len = RX_BUF_SIZE - hdma_usart1_rx.Instance->NDTR; } } } int main(void) { HAL_Init(); UART_Init(); while (1) { if (rx_flag) { // 处理接收到的数据 rx_flag = 0; } } } ``` 在上面的例子中,我们使用了循环DMA传输模式,当接收到一定数量的数据后,将触发空闲中断,并在空闲中断中处理接收到的数据。同时,在DMA传输完成和空闲中断中,我们使用了两个不同的回调函数,分别处理DMA传输完成和空闲中断的事件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值