平台环境:stm32f407+freertos
硬件接口:USART1+DMA
出现的问题:因为是在RTOS系统下,因此串口1的发送思路:调用HAL_UART_Transmit_DMA函数前需要获得对应的信号量,DMA发送完成中断触发后在中断中释放对应的信号量。然而,在stm32f4xx_it.c文件中找到串口1链接的DMA2_Stream7的中断,该中断只做了一件事,调用HAL_DMA_IRQHandler中断处理函数。如果直接在DMA2_Steam7_IRQHandler释放信号量,那么如果DMA触发半传输完成中断的时候也会释放一次信号量(即1次传输释放2次)从设计的角度来说不能接收。
/**
* @brief This function handles DMA2 stream7 global interrupt.
*/
void DMA2_Stream7_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream7_IRQn 0 */
/* USER CODE END DMA2_Stream7_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart1_tx);
/* USER CODE BEGIN DMA2_Stream7_IRQn 1 */
/* USER CODE END DMA2_Stream7_IRQn 1 */
}
因此,在HAL_DMA_IRQHandler在HAL_DMA_IRQHandler中断处理函数中找到下述代码,代码中判断如果是DMA发送完成中断,则调用回调函数。这里,我准备注册一个自定义的回调函数来释放所需的信号量。
if ((tmpisr & (DMA_FLAG_TCIF0_4 << hdma->StreamIndex)) != RESET)
{ ...;
if(hdma->XferCpltCallback != NULL)
{
/* Transfer complete callback */
hdma->XferCpltCallback(hdma);
}
}
DMA库函数通过调用下述函数进行注册:
HAL_StatusTypeDef HAL_DMA_RegisterCallback(DMA_HandleTypeDef *hdma, HAL_DMA_CallbackIDTypeDef CallbackID, void (* pCallback)(DMA_HandleTypeDef *_hdma))
于是,写了如下注册函数用以实现回调:
void Uart_DMA_Tx_CPLT_Callback(DMA_HandleTypeDef *hdma)
{
static portBASE_TYPE xHigherPriorityTaskWoken;
MY_PRINTF(HAL_GetTick(),"Uart1_DMA_Tx_CPLT_Callback",0,0);
xSemaphoreGiveFromISR(sem_uart1_tx,&xHigherPriorityTaskWoken);
}
然而,调试结果始终没有打印出相关信息,直到研究了HAL_UART_Transmit_DMA(&huart1,buf,size)这个函数才发现问题所在。截取该函数中下面这句话,意味着每次调用HAL_UART_Transmit_DMA函数都会重新注册一次DMA发送中断完成的回调函数。
/* Set the UART DMA transfer complete callback */
huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;
在下述函数中加入对应代码,实现了DMA中断传输完成释放信号量:
static void UART_DMATransmitCplt(DMA_HandleTypeDef *hdma)
{
static portBASE_TYPE xHigherPriorityTaskWoken;
UART_HandleTypeDef* huart = ( UART_HandleTypeDef* )((DMA_HandleTypeDef* )hdma)->Parent;
/* DMA Normal mode*/
if((hdma->Instance->CR & DMA_SxCR_CIRC) == 0U)
{
huart->TxXferCount = 0U;
/* Disable the DMA transfer for transmit request by setting the DMAT bit
in the UART CR3 register */
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);
/* Enable the UART Transmit Complete Interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);
if ( hdma == &hdma_usart1_tx )
{
xSemaphoreGiveFromISR(sem_uart1_tx,&xHigherPriorityTaskWoken);
}
}
/* DMA Circular mode */
else
{
HAL_UART_TxCpltCallback(huart);
}
}