编写代码时遇到了两个问题
在串口使用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