首页 > 其他分享 >串口空闲中断+DMA收发不定长数据

串口空闲中断+DMA收发不定长数据

时间:2024-12-10 15:57:43浏览次数:8  
标签:DMA HAL USART UART 收发 Init Handler 串口

编写代码时遇到了两个问题

在串口使用DMA传输数据并且需要每传输一帧数据后产生空闲中断时出现问题

问题原因:误认为hal库串口的空闲中断和接收中断使用的是同一个接收回调函数HAL_UART_Receive_IT(该函数会开启接收中断:标志位UART_IT_RXNE),经过查找发现接收中断回调函数只是在置位接收中断RXNE标志位后才会中断,对于空闲中断IDLE则不会发生中断,所以进行调试的时候程序一直无法响应。

解决办法:需要单独的使能空闲中断,并且需要自己声明,定义并实现空闲中断回调函数。

串口使用DMA传输数据时发现数据只能传输一次后续无法再进行传输

问题原因:查阅手册发现DMA的数据流数据项数寄存器NDTR只有在禁止数据流时才能进行此寄存器的写操作;并且在一次数据传输完成后,在这个寄存器保持为0;如果在这个寄存器为0的情况下即使使能了数据流也无法完成任何事务。我在传输一帧数据之后没有进行数据流的禁止就直接使能并设置了数据项数所以没有办法启动数据传输。

解决办法:在使能DMA接收任务之前失能DMA。关闭再开启DMA不仅仅是为了给用于发送数据的DMA设置发送多少数据,而是改变DMA 数据流 x 数据项数寄存器 (DMA_SxNDTR)寄存器的值,更是为了使得当接收到的数据小于我们设定的数据大小时用于接收数据的DMA所配置的存储器地址重新开始递增(指针指向存储器的起始地址),否则将会把原来的值也发送出去。

uart.c

/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t g_usart_rx_buf[USART_REC_LEN];

/*  接收状态
 *  bit15,      接收完成标志
 *  bit14,      接收到0x0d
 *  bit13~0,    接收到的有效字节数目
*/
uint16_t g_usart_rx_sta = 0;
uint8_t  num = 0;

uint8_t g_rx_buffer[RXBUFFERSIZE];  /* HAL库使用的串口接收缓冲 */

UART_HandleTypeDef g_uart1_handle;  /* UART句柄 */

/**
 * @brief       串口X初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @note        注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
 *              这里的USART的时钟源在sys_stm32_clock_init()函数中已经设置过了.
 * @retval      无
 */
void usart_init(uint32_t baudrate)
{
    /*UART 初始化设置*/
    g_uart1_handle.Instance = USART_UX;                                       /* USART_UX */
    g_uart1_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    g_uart1_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    g_uart1_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    g_uart1_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&g_uart1_handle);                                           /* HAL_UART_Init()会使能UART1 */

	UART1_DMA_Init();
	
	//使能空闲中断
	__HAL_USART_ENABLE_IT(&g_uart1_handle,USART_IT_IDLE);
	
	HAL_UART_Receive_DMA(&g_uart1_handle,g_usart_rx_buf,USART_REC_LEN);
    /* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */
    //HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE); 
}

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (huart->Instance == USART_UX)                            /* 如果是串口1,进行串口1 MSP初始化 */
    {
        USART_TX_GPIO_CLK_ENABLE();                             /* 使能串口TX脚时钟 */
        USART_RX_GPIO_CLK_ENABLE();                             /* 使能串口RX脚时钟 */
        USART_UX_CLK_ENABLE();                                  /* 使能串口时钟 */

        gpio_init_struct.Pin = USART_TX_GPIO_PIN;               /* 串口发送引脚号 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* IO速度设置为高速 */
        HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);
                
        gpio_init_struct.Pin = USART_RX_GPIO_PIN;               /* 串口RX脚 模式设置 */
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;    
        HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct);   /* 串口RX脚 必须设置成输入模式 */
        
#if USART_EN_RX
        HAL_NVIC_EnableIRQ(USART_UX_IRQn);                      /* 使能USART1中断通道 */
        HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3);              /* 组2,最低优先级:抢占优先级3,子优先级3 */
#endif
    }
}

void USART_UX_IRQHandler(void)
{
#if SYS_SUPPORT_OS                          /* 使用OS */
    OSIntEnter();    
#endif
	//在串口1中断服务函数中进行判断是否发生了空闲中断
	if(__HAL_USART_GET_FLAG(&g_uart1_handle,USART_FLAG_IDLE) != RESET)
	{
		//首先需要清除空闲中断的标志位IDLE
		__HAL_USART_CLEAR_IDLEFLAG(&g_uart1_handle);
		//空闲中断的回调处理函数
		IDLE_Callback(&g_uart1_handle);
		
	}
	
    HAL_UART_IRQHandler(&g_uart1_handle);   /* 调用HAL库中断处理公用函数 */

#if SYS_SUPPORT_OS                          /* 使用OS */
    OSIntExit();
#endif

}

void IDLE_Callback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		num++;
		//将使用DMA接收到的串口数据再通过DMA控制发送到上位机串口调试助手进行显示
		HAL_UART_Transmit_DMA(huart,g_usart_rx_buf,USART_REC_LEN);
		//等待发送完成
		while(__HAL_UART_GET_FLAG(huart,UART_FLAG_TC) != SET);
		//清除发送完成标志位
		__HAL_UART_CLEAR_FLAG(huart,UART_FLAG_TC);
		
		/* HAL_UART_DMAStop(&huart1);不仅停止了发送数据的DMA,更停止了接收数据的DMA。
		 * 当然,这里最重要的是失能接收数据的DMA,使得接收数据的数量小于为我们设定时
		 * 可以让指向存储器的指针回到存储器位置的起始地址。关闭再开启DMA不仅仅是为了给用于发送数据的DMA设置发送多少数据,
		 * 而改变DMA 数据流 x 数据项数寄存器 (DMA_SxNDTR)寄存器的值,
		 * 更是为了使得当接收到的数据小于我们设定的数据大小时用于接收数据的DMA所配置的存储器地址重新开始递增(指针指向存储器的起始地址),
		 * 否则将会把原来的值也发送出去。
		 */		
		HAL_UART_DMAStop(huart);
		//重新开启串口DMA进行接收
		HAL_UART_Receive_DMA(&g_uart1_handle,g_usart_rx_buf,USART_REC_LEN);
		
	}
}

dma.c

DMA_HandleTypeDef TX1_DMA_Handler;
DMA_HandleTypeDef RX1_DMA_Handler;



void UART1_DMA_Init(void)
{
	__HAL_RCC_DMA1_CLK_ENABLE();
	
	TX1_DMA_Handler.Instance = DMA1_Channel4;
	TX1_DMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH;
	TX1_DMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE;
	TX1_DMA_Handler.Init.MemInc=DMA_MINC_ENABLE;
	TX1_DMA_Handler.Init.Mode=DMA_NORMAL;
	TX1_DMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;
	TX1_DMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE;
	TX1_DMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM;
	
	__HAL_LINKDMA(&g_uart1_handle,hdmatx,TX1_DMA_Handler);
	HAL_DMA_Init(&TX1_DMA_Handler);

	RX1_DMA_Handler.Instance = DMA1_Channel5;
	RX1_DMA_Handler.Init.Direction=DMA_PERIPH_TO_MEMORY;
	RX1_DMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE;
	RX1_DMA_Handler.Init.MemInc=DMA_MINC_ENABLE;
	RX1_DMA_Handler.Init.Mode=DMA_NORMAL;
	RX1_DMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;
	RX1_DMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE;
	RX1_DMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM;
	
	__HAL_LINKDMA(&g_uart1_handle,hdmarx,RX1_DMA_Handler);
	HAL_DMA_Init(&RX1_DMA_Handler);

	

}

标签:DMA,HAL,USART,UART,收发,Init,Handler,串口
From: https://blog.csdn.net/ls20010901/article/details/144376072

相关文章

  • c#通过串口读取到的分段json提取方法
    privateList<byte>receivedBuffer=newList<byte>();privatevoidbtnConnect_Click(objectsender,EventArgse){this.btnConnect.Enabled=false;this.btnDisconnect.Enabled=true;_deviceAdapter=newComDeviceAdapter(this......
  • T113-S3 Tina 串口切换
    前面介绍了如何在Tina中添加新的板子及切换存储类型,本节介绍如何修改板子串口配置。1、修改调试串口Tina调试串口配置在device/config/chips/t113/configs/evbemmc/sys_config.fex文件中,可以修改uart_para变量来指定调试串口。;--------------------------------......
  • STM32 串口和I2C结合案例 hal库代码书写
    hal库的创建打开cubemx->选择芯片STM32F103ZET6,双击打开SystemCore系统核心->SYS下->DeBug选择SerialWire串口SystemCore系统核心->RCC下->highSpeedClock(高速时钟)以及LSE全部选择外部晶振SystemCore系统核心->GPIO下->PA0/PA1/PA8三者全部选择GPIO_Output,具体配......
  • C# 串口通信利器 SerialPortStream库
    学习备忘-源自 https://mp.weixin.qq.com/s/hDVD9Ij22KSHm7TUmETcmQ前言物联网(IoT)和嵌入式系统的开发中,串口通信是一项不可或缺的技术。C#作为一种广泛使用的编程语言,同样支持多种方式进行串口通信。其中,SerialPortStream 库因其简单易用的API和强大的功能成为了众多开发者......
  • C# SerialPort 串口通讯
    学习备忘-源自https://mp.weixin.qq.com/s/xO7yCpNj3zG8lfqyz4ARVw前言串口通信在.NET平台下创建C#串口通信程序,.NET2.0提供了串口通信的功能,其命名空间是System.IO.Ports。这个新的框架不但可以访问计算机上的串口,还可以和串口设备进行通信。创建C#串口通信程序之命名空间S......
  • 207-FMC207-基于FMC 两路QSFP+光纤收发子卡
    FMC207-基于FMC两路QSFP+光纤收发子卡  一、板卡概述   本卡是一个FPGA夹层卡(FMC)模块,可提供高达2个QSFP / QSFP +模块接口,直接插入千兆位级收发器(MGT)的赛灵思FPGA。支持利用Spartan-6、Virtex-6、Kintex-7 、Virtex-7FPGA系列FPGA。兼容xilinx 开发板使用。   ......
  • C#WinForm实操串口通讯使用GtkSharp库实现跨平台
     Linux下运行.NET项目:1、环境安装2、cd项目路径3、dotnet项目dll,即可运行 部分代码分享:usingGtk;usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Refl......
  • k8s-rdma-shared-dev-plugin
    文章目录前言一、创建k8s集群二、启用primarynetwork三、启用secondarynetworkk8s-rdma-shared-dev-pluginMultusCNISecondaryCNIMulti-NetworkCRD四、启用pod五、在pod中启动RoCE流量总结前言写给自己的入门篇。后续会在原理方面持续更新一、创建k8s集群k8s集......
  • Extension Can't Connect to NeatDownloadManager Application,You Can :
    ExtensionCan'tConnecttoNeatDownloadManagerApplication,YouCan:1-ChecklfNeatDownloadManagerisRunning.2-orHolddownDelete-KeyandclickontheDownloadlink.3-orDisableNeatDownloadManagerExtensiontemporarily.  NeatDownloadManager应用程......
  • XCVU13P板卡设计原理图:509-基于XCVU13P的4路QSFP28光纤PCIeX16收发卡
     一、板卡概述     基于XCVU13P的4路QSFP28光纤PCIeX16收发卡。该板卡要求符合PCIe3.0标准,包含一片XCVU13P-2FLGA2014I、4组64-bit/8GBDDR4;4路QSFP284X光纤,每路光纤支持4X25Gbps,双向;支持32路IO。板卡工作温度范围0到60℃,板卡设计加工包含散热装置,支持服务器风冷散热......