DMA+UASRT,DMA使能失败

2019-07-14 14:25发布

各位大侠,小弟遇到一个十分让人困惑的问题。在如下的main函数中将BspInit();前面的#if 0改为#if 1之后,也就是将BspInit();到main结尾部分的代码编译进来之后,串口无法正常发数,原因在于DMA使能失败;在#if 0的情况下,也就是不将BspInit();到main结尾部分的代码编译进来,则串口能够正常工作。按道理来说,后面的代码还未运行,怎么会影响到前面代码的执行呢?不应该啊。于是考虑是不是栈溢出导致的问题,然后仔细想了一下,发现说不通,栈的空间是在函数被调用的时候才会被占用,因为串口测试代码放在了main函数的最开始,而且运行到for循环的时候,栈的空间才被占用不到60个字节,在启动代码里面分配的栈的大小是1K字节。小弟我想了各种办法,做了各种尝试,试了各种初始化以及使能顺序,依然没有能够解决这个问题。想请各位前辈大侠高手们帮忙给支支招,小弟先谢谢论坛里的各位前辈大侠高手们了。
        
main函数如下
int main(void)
{
        int ret;
        struct PlcJDIRT *jdirt;
        char *memory;
        char *r_memory;               
        /*串口测试*/
        uint32_t i=0;
        uint8_t b[16] = {1,3, 0,0,0,2,0xFF,0xFF};
        
        delay_init(168);

        ret = OpenUart(PLC_STM32_UART_COM1);
        for(i=0; i<5; i++)
        {        
                        ret = WriteToUart(PLC_STM32_UART_COM1, b, 8);        
                        delay_ms(1000);               
        }
        /*串口测试*/
        /*-------------init all board device---------------*/        
               
        //NVIC config 0-15 prio        
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);        
        
#if 0        
        BspInit();

    //InitDrac200MConfig();

        ret = InitPlcSys();
    assert(ret == PLC_RTSUCCESS);

    ret = InitPSocketSystem();
    assert(ret == PLC_RTSUCCESS);

    ret = InitPlcCommonFW();
    assert(ret == PLC_RTSUCCESS);
        
        InitialDRAC200MIOBoard();
        InitialModbusSlave();
        InitialModbusMaster();

        memory = pv_EXT_PortMalloc(SIZE_OF_MEMORY);
        assert(memory != NULL);
        r_memory = pv_EXT_PortMalloc(SIZE_OF_R_MEMORY);
        assert(r_memory != NULL);

  jdirt = InitPlcJDIRT(memory, SIZE_OF_MEMORY,
         r_memory, SIZE_OF_R_MEMORY);
  assert(jdirt != NULL);
        
        xTaskCreate(led_task,"TASK_1",256,NULL,20,NULL);        
        
        xTaskCreate(StartUDPServer,"UDPServer",1000, jdirt, 5, NULL);        
        
        vTaskStartScheduler();
#endif
}

OpenUart(PLC_STM32_UART_COM1);调用顺序如下
/*Initialize UART5*/
PLC_STM32_UART_COM1_Config(PLC_UART_485, PLC_BAUDRATE_115200, PLC_PARITY_NO, PLC_DATABIT_8, PLC_STOPBIT_1);
/*Enable UART5*/                                       
/*Configure DMA1_Stream0 for UART5_RX*/
DMAConfig(DMA1_Stream0,
                  BUF_SIZE_MAX,
                  DMA_DIR_PeripheralToMemory,
                  (uint32_t)g_buf_rx[COM1],
                   DMA_Mode_Circular,
                  (uint32_t)&UART5->DR,
                  DMA_Priority_Medium);        
NVICConfig(DMA1_Stream0_IRQn, 12, 0);
/*Configure DMA1_Stream7 for UART5_TX*/
DMAConfig(DMA1_Stream7,
                  0,
                  DMA_DIR_MemoryToPeripheral,
                  (uint32_t)g_buf_tx[COM1],
                  DMA_Mode_Normal,
                  (uint32_t)&UART5->DR,
                  DMA_Priority_High);
NVICConfig(UART5_IRQn, 12, 0);                                
/*Start to receive data through COM1*/
DMAEnable(DMA1_Stream0, UART5, USART_DMAReq_Rx, BUF_SIZE_MAX);
/*Enable UART5*/
USART_Cmd(UART5, ENABLE);                                       

WriteToUart(PLC_STM32_UART_COM2, b, 8);中会调用WriteData(DMA1_Stream7, UART5, COM1, buf, length);
        
WriteData(DMA1_Stream7, UART5, COM1, buf, length);中会调用DMAEnable(DMAy_Streamx, USARTx, USART_DMAReq_Tx, (uint16_t)length);

DMAEnable(DMAy_Streamx, USARTx, USART_DMAReq_Tx, (uint16_t)length);程序如下
/*
*********************************************************************************************************
*        @brief        Enable DMA transfer
*   @param  DMAy_Streamx: where y can be 1 or 2 to select the DMA and x
*                can be 0 to 7 to select the DMA Stream.
*        @param        USARTx: where x can be 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or UART peripheral.
*        @param        USART_DMAReq: Specifies the DMA request.
*                This parameter can be any combination of the following values:
*                USART_DMAReq_Tx: USART DMA transmit request
*                USART_DMAReq_Rx: USART DMA receive request
*        @param  counter: Specifies the number of data items need to be transfered.
*        @retval None
*********************************************************************************************************
*/
static void DMAEnable(DMA_Stream_TypeDef * DMAy_Streamx, USART_TypeDef * USARTx, uint16_t USART_DMAReq, uint16_t counter)
{
        USART_DMACmd(USARTx, USART_DMAReq, DISABLE);
        DMA_Cmd(DMAy_Streamx, DISABLE);
        while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE){}
        DMA_SetCurrDataCounter(DMAy_Streamx, counter);
        USART_DMACmd(USARTx, USART_DMAReq, ENABLE);        
        DMA_Cmd(DMAy_Streamx, ENABLE);        
}

GPIO初始化代码如下
/*
*********************************************************************************************************
*        @brief        Configure the PLC_STM32_UART_COM1 according to the specified
*         parameters, referenced by SetSTM32UartConfig.
*        @param  mode: Select RS485 or RS232.
*   @param  baud: Set baud rate.
*        @param  parity: Set parity mode.
*        @param        data_bit: Set word length.
*        @param        stop_bit: Set number of stop bits.
*        @retval None
*********************************************************************************************************
*/
static void PLC_STM32_UART_COM1_Config(char mode, char baud, char parity, char data_bit, char stop_bit)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOG, ENABLE);//Enable clock of GPIOB, GPIOC, GPIOD, GPIOG
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);//Enable clock of UART5

        /*Alternate function PC12->UART5_TX, PD2->UART5_RX*/
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);//AF, GPIOC12->UART5
        GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);//AF, GPIOD2->UART5
        
        /*Configure Pin_12 of GPIOC as AF UART5_TX*/
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//GPIOC12
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//Alternate function
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//Speed 50MHz
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//Push & pull
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//No pull
        GPIO_Init(GPIOC, &GPIO_InitStructure);//Initialize GPIOC
        /*Configure Pin_2 of GPIOD as AF UART5_RX*/
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//GPIOD2
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;// Alternate function
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//Speed 50MHz
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//Push & pull
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//No pull
        GPIO_Init(GPIOD, &GPIO_InitStructure);//Initialize GPIOD                 
        /*Configure Pin_5 of GPIOB to select RS232 or RS485 in main board*/  
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//GPIOB5
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//Out put
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//Speed 50MHz
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//Push & pull
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//No pull
        GPIO_Init(GPIOB, &GPIO_InitStructure);//Initialize GPIOB
        /*Configure Pin_9 of GPIOG to select RS232 or RS485 in core board*/  
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//GPIOG9
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//Out put
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//Speed 50MHz
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//Push & pull
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//No pull
        GPIO_Init(GPIOG, &GPIO_InitStructure);//Initialize GPIOG
        /*Configure Pin_7 of GPIOD to select TX or RX of RS485*/
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//GPIOD7
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//Out put
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//Speed 50MHz
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//Push & pull
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//No pull        
        GPIO_Init(GPIOD, &GPIO_InitStructure);//Initialize GPIOD         
        
        /*Initialize UART5*/
        USART_Config(UART5, baud, parity);
        
        /*Select RS485 or RS232*/
        UART5_485OR232 = mode;//Select RS485 or RS232 in core board         
        UART5_MB485OR232 = mode;//Select RS485 or RS232 in main board
        
        /*Set default mode of RS485*/
        UART5_485 = RS485_RX_CONTROL;//Default Rx        
}        

串口初始化代码如下
/*
*********************************************************************************************************
*        @brief        Configure the USART according to the specified parameters.
*        @param  USARTx: where x can be 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or UART peripheral.
*   @param  baud: Set baud rate.
*        @param  parity: Set parity mode.
*        @retval None
*********************************************************************************************************
*/
static void USART_Config(USART_TypeDef * USARTx, char baud, char parity)
{
        USART_InitTypeDef USART_InitStructure;
        
        switch (baud){
                case PLC_BAUDRATE_300:
                        USART_InitStructure.USART_BaudRate = 300;//Set baud rate to 300
                        break;
                case PLC_BAUDRATE_9600:
                        USART_InitStructure.USART_BaudRate = 9600;//Set baud rate to 9600
                        break;                        
                case PLC_BAUDRATE_38400:
                        USART_InitStructure.USART_BaudRate = 38400;//Set baud rate to 38400
                        break;                        
                case PLC_BAUDRATE_115200:
                        USART_InitStructure.USART_BaudRate = 115200;//Set baud rate to 115200
                        break;        
        }
        switch (parity){
                case PLC_PARITY_NO:
                        USART_InitStructure.USART_Parity = USART_Parity_No;//No parity
                        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//Set word length to 8
                        break;
                case PLC_PARITY_EVEN:
                        USART_InitStructure.USART_Parity = USART_Parity_Even;//Even parity
                        USART_InitStructure.USART_WordLength = USART_WordLength_9b;//Set word length to 9
                        break;                        
                case PLC_PARITY_ODD:
                        USART_InitStructure.USART_Parity = USART_Parity_Odd;//Odd parity
                        USART_InitStructure.USART_WordLength = USART_WordLength_9b;//Set word length to 9
                        break;
        }
        USART_InitStructure.USART_StopBits = USART_StopBits_1;//1 stop bit
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//No hardware control
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //Tx & Rx are both enabled
        USART_Init(USARTx, &USART_InitStructure);//Initialize USARTx
        
        /*Clear TC bit in USART_SR*/
        while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == SET){
                USART_ClearFlag(USARTx, USART_FLAG_TC);               
        }
        
        /*Enable transmission complete interrupt*/
        USART_ITConfig(USARTx, USART_IT_TC, ENABLE);//for test
}        

NVIC初始化代码如下
/*
*********************************************************************************************************
*        @brief        Configure DMA or USART interrupt.
*        @retval None
*********************************************************************************************************
*/
static void NVICConfig(uint8_t  NVIC_IRQChannel,
                                           uint8_t  NVIC_IRQChannelPreemptionPriority,
                                           uint8_t  NVIC_IRQChannelSubPriority)
{        
        NVIC_InitTypeDef NVIC_InitStructure;
        
        /*Enable DMA TC interrupt*/
        NVIC_InitStructure.NVIC_IRQChannel = NVIC_IRQChannel;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_IRQChannelPreemptionPriority;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_IRQChannelSubPriority;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
}

DMA初始化代码如下
/*
*********************************************************************************************************
*        @brief        Configure the DMA according to the specified parameters,
*                referenced by ReadFromUart & WriteToUart.
*   @param  DMAy_Streamx: where y can be 1 or 2 to select the DMA and x
*                can be 0 to 7 to select the DMA Stream.
*        @param        DMA_DIR: Specifies if the data will be transferred from memory to peripheral,
*                from memory to memory or from peripheral to memory.
*        @param        DMA_Memory0BaseAddr: Specifies the memory 0 base address for DMAy Streamx.
*        @param        DMA_PeripheralBaseAddr: Specifies the peripheral base address for DMAy Streamx.
*        @param        DMA_Priority: Specifies whether the Peripheral address register
*                should be incremented or not.
*        @retval None
*********************************************************************************************************
*/
static void DMAConfig( DMA_Stream_TypeDef *  DMAy_Streamx,
                                           uint32_t  DMA_BufferSize,        
                                           uint32_t  DMA_DIR,
                                           uint32_t  DMA_Memory0BaseAddr,
                                           uint32_t  Mode,        
                                           uint32_t  DMA_PeripheralBaseAddr,
                                           uint32_t  DMA_Priority )
{
        DMA_InitTypeDef  DMA_InitStructure;
        /*Enable the clock of DMA*/
        if((uint32_t)DMAy_Streamx<(uint32_t)DMA2)//Determin the current stream belongs to DMA1 or DMA2
                RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);//Enable the clock of DMA1        
        else
                RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);//Enable the clock of DMA2
        DMA_DeInit(DMAy_Streamx);
        
        /*Waiting for the EN bit in DMA_SxCR become reset*/
        DMA_Cmd(DMAy_Streamx, DISABLE);        
        while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE){}                                   
        
        /*Configure DMA stream*/               
        DMA_InitStructure.DMA_BufferSize = DMA_BufferSize;//need to be configured
        DMA_InitStructure.DMA_Channel = DMA_Channel_4;
        DMA_InitStructure.DMA_DIR = DMA_DIR;//need to be configured
        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//changed
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;        
        DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;//need to be configured
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
        DMA_InitStructure.DMA_Mode = Mode;//need to be configured        
        DMA_InitStructure.DMA_PeripheralBaseAddr = DMA_PeripheralBaseAddr;//need to be configured
        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
        DMA_InitStructure.DMA_Priority = DMA_Priority;//need to be configured
        DMA_Init(DMAy_Streamx, &DMA_InitStructure);        
        
        /*Enable DMA TCIF if the stream is used for receiving*/
        if((DMAy_Streamx == DMA1_Stream0) || (DMAy_Streamx == DMA1_Stream1) || (DMAy_Streamx == DMA1_Stream2))//for test        
                DMA_ITConfig(DMAy_Streamx, DMA_IT_TC, ENABLE);         
}                                               
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
9条回答
dlksmad
2019-07-14 23:52
2016李勇 发表于 2019-1-23 20:17
不是堆的问题,答案已经被找到了。由于将#if 0改为#if 1之后,由于代码量变大,MDK5自动的将发送缓冲区分派到CCM上了,而CCM与DMA并未直接连接,所以会导致transfer error从而导致DMA使能失败。解决方法是用attribute指定缓冲区的位置。如果带有RTOS的环境下,如果RTOS管理的是外扩内存的话,也可以用RTOS自带的类似于mallo ...

学习了。感谢分享。

MDK5其实总是会有一些BUG,编译和调试都如此,之前也遇到调试跳转到非指定的地址,然后优化等级越高(比如level3)有时候也会导致一些问题。

一周热门 更多>