2017-04-08 376 views
2

我试图在DMA模式下实现UART,每次按下按钮时传输一个简单的字符串。STM32:在DMA模式下实现UART

所以我用CubeMX生成代码,并且我在正常(非循环)模式下配置了UART2 TX DMA,也没有FIFO并且没有突发。

每当我在调试模式下运行代码,我看到我第一次尝试发送字符串,它工作正常并发送字符串,但在DMA IRQ处理程序内,它调用TxHalfCpltCallback而不是TxCpltCallback,还有UART gState将保持繁忙模式,所以我不能用它来传输更多的字符串。

我的问题是为什么它调用TxHalfCpltCallback而不是TxCpltCallback?我应该如何处理它(因为HAL参考说它等待发送缓冲区的后半部分!什么?)

而且,还会发送下一半的数据来关闭UART的g状态吗?

我想问某人给我们一个在项目中配置UART的例子。

回答

4

如果您使用的是DMA,那么您将有两个中断:一个发送缓冲区的一半,另一个发送后半(整个)。

如果一切正常,他们都应该被解雇。其原因在于,当发送大量数据时,您可以开始将新数据加载到TxHalfCpltCallback缓冲区的前半部分,而DMA的后半部分正在传输缓冲区。同样,当前半部分正在传输时,您可以将新数据加载到TxCpltCallback缓冲区的后半部分。

好处是,在将下一块数据复制到缓冲区之前,您不必等待整个传输完成,但是您仍然可以在传输仍在进行时开始加载它。

下面是一个例子:

在这个例子中2000个字节将使用DMA被转移,传输完成一半发送完成中断实现最佳性能。

发送缓冲区的前半部分由CPU在中断回调中由CPU加载新数据,而后半部分由DMA在后台传输。

然后,在发送完成发送缓冲区的后半部分由CPU通过新数据加载,而前半部分(先前更新)由背景中的DMA发送。

#include "stm32f4xx.h" 

uint8_t dma_buffer[2000]; 
volatile uint8_t toggle = 0; 

UART_HandleTypeDef huart2; 
DMA_HandleTypeDef hdma_usart2_tx; 

void uart_gpio_init() 
{ 
    GPIO_InitTypeDef GPIO_InitStruct; 

    __GPIOA_CLK_ENABLE(); 

    /**USART2 GPIO Configuration 
    PA2  ------> USART2_TX 
    PA3  ------> USART2_RX 
    */ 
    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3; 
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 
    GPIO_InitStruct.Pull = GPIO_PULLUP; 
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; 
    GPIO_InitStruct.Alternate = GPIO_AF7_USART2; 
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); 
} 

void uart_dma_init() 
{ 
    /* DMA controller clock enable */ 
    __DMA1_CLK_ENABLE(); 

    /* Peripheral DMA init*/ 
    hdma_usart2_tx.Instance = DMA1_Stream6; 
    hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4; 
    hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; 
    hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE; 
    hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE; 
    hdma_usart2_tx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE; 
    hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; 
    hdma_usart2_tx.Init.Mode = DMA_NORMAL; 
    hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW; 
    hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; 
    HAL_DMA_Init(&hdma_usart2_tx); 

    __HAL_LINKDMA(&huart2,hdmatx,hdma_usart2_tx); 

    /* DMA interrupt init */ 
    HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0); 
    HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); 
} 

void uart_init() 
{ 
    __USART2_CLK_ENABLE(); 

    huart2.Instance = USART2; 
    huart2.Init.BaudRate = 115200; 
    huart2.Init.WordLength = UART_WORDLENGTH_8B; 
    huart2.Init.StopBits = UART_STOPBITS_1; 
    huart2.Init.Parity = UART_PARITY_NONE; 
    huart2.Init.Mode = UART_MODE_TX_RX; 
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; 
    huart2.Init.OverSampling = UART_OVERSAMPLING_16; 
    HAL_UART_Init(&huart2); 

    /* Peripheral interrupt init*/ 
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); 
    HAL_NVIC_EnableIRQ(USART2_IRQn); 
} 

/* This function handles DMA1 stream6 global interrupt. */ 
void DMA1_Stream6_IRQHandler(void) 
{ 
    HAL_DMA_IRQHandler(&hdma_usart2_tx); 
} 

void USART2_IRQHandler(void) 
{ 
    HAL_UART_IRQHandler(&huart2); 
} 

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) 
{ 
    uint16_t i; 
    toggle = !toggle; 

    for(i = 1000; i < 1998; i++) 
    { 
    if(toggle) 
     dma_buffer[i] = '&'; 
    else 
     dma_buffer[i] = 'z'; 
    } 

    dma_buffer[1998] = '\r'; 
    dma_buffer[1999] = '\n'; 
} 

void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart) 
{ 
    uint16_t i; 

    for(i = 0; i < 1000; i++) 
    { 
    if(toggle) 
     dma_buffer[i] = 'y'; 
    else 
     dma_buffer[i] = '|'; 
    } 
} 

int main(void) 
{ 
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ 
    HAL_Init(); 

    uart_gpio_init(); 
    uart_dma_init(); 
    uart_init(); 

    uint16_t i; 

    for(i = 0; i < 1998; i++) 
    { 
    dma_buffer[i] = 'x'; 
    } 

    dma_buffer[1998] = '\r'; 
    dma_buffer[1999] = '\n'; 

    while(1) 
    { 
    HAL_UART_Transmit_DMA(&huart2, dma_buffer, 2000); 
    } 
} 

这个例子是为STM32F4探索板(STM32F407VG)编写的。适当的DMA实例,UART-DMA通道,GPIO和备用功能设置应根据所使用的STM32微控制器进行更改。

0

如果在CubeMX库使用功能

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 

,它将使所有的DMA中断。通过清除DMA_SxCR寄存器中的HTIE位,可以禁用半传输中断。

-1

当你使用裸寄存器方法而不是主宰HAL怪物时,编码DMA传输(当然接收)要容易得多。

例STM32F446(假设在寄存器复位值)

DMA1_Stream6 -> NDTR = nTransfers; 
DMA1_Stream6 -> PAR = (uint32_t)&(USART2 -> DR); 
DMA1_Stream6 -> M0AR = (uint32_t)&dataBuff; 
DMA1_Stream6 -> CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE; // you can enable half transfer enable as well 

USART2 -> BRR = FCLK/LOWSPEED; 
USART2 -> CR3 |= USART_CR3_DMAT; 
USART2 -> CR1 = (USART_CR1_TE | USART_CR1_RE | USART_CR1_UE); 
DMA1_Stream6 -> CR |= DMA_SxCR_EN; 

相当容易 - 不是吗?

void DMA1_Stream6_IRQHandler(void) { // now it does nothing only clears the flag 
    if(DMA1 -> HISR & (DMA_HISR_TCIF6)) { 
     DMA1 -> HIFCR |= DMA_HISR_TCIF6; 
     while(!(USART2 -> SR & USART_SR_TC)); 
    } 
} 
0

您的问题,看起来类似https://community.st.com/thread/40080-dma-uart-with-hal-remain-busy-bug

您应该启用HAL_UART_IRQHandler()

即 内部 “main.c” 文件(或任何你初始化硬件)地址:

HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);           
HAL_NVIC_EnableIRQ(USART2_IRQn); 

里面“stm32f4xx_it.c”:

void USART2_IRQHandler(void) 
{ 
    HAL_UART_IRQHandler(&huart2); 
}