你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

DMA+UASRT,DMA无法使能

[复制链接]
gege770 提问时间:2017-4-27 10:37 /
各位大侠,小弟遇到一个十分让人困惑的问题。在如下的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);        
}                                               

收藏 3 评论12 发布时间:2017-4-27 10:37

举报

12个回答
gege770 回答时间:2017-6-17 14:22:31
不是堆的问题,答案已经被找到了。由于将#if 0改为#if 1之后,由于代码量变大,MDK5自动的将发送缓冲区分派到CCM上了,而CCM与DMA并未直接连接,所以会导致transfer error从而导致DMA使能失败。解决方法是用attribute指定缓冲区的位置。如果带有RTOS的环境下,如果RTOS管理的是外扩内存的话,也可以用RTOS自带的类似于malloc的函数来动态的分配缓冲区。
在这里要提醒各位使用MDK5的童鞋,这款编译器有一定的坑,特别是对于内存的分配上。有时候一不小心就会给你把缓冲区分配到一些非法地址上去,大家一定要注意。如果大家遇到什么诡异现象,无法从程序本身上去解释的话,那么建议大家从内存的角度去考虑,检查一下MDK有没有将缓冲区分配到了非法地址上。

评分

参与人数 1ST金币 +5 收起 理由
zero99 + 5 结贴奖励

查看全部评分

xmshao 回答时间:2017-4-27 15:19:29
你现在安排的堆多大?会不会是HEAP设置小了点?
arenas 回答时间:2017-6-24 09:02:07
gege770 发表于 2017-6-17 14:22
不是堆的问题,答案已经被找到了。由于将#if 0改为#if 1之后,由于代码量变大,MDK5自动的将发送缓冲区分派 ...

学习了。感谢分享。

MDK5其实总是会有一些BUG,编译和调试都如此,之前也遇到调试跳转到非指定的地址,然后优化等级越高(比如level3)有时候也会导致一些问题。
无薪税绵 回答时间:2017-6-24 11:47:18
这么快就找到解决方法了,不错。
好吃好好吃 回答时间:2017-6-24 20:41:25
这个不错,经验之谈,
风中的IT 回答时间:2017-6-24 21:04:14
学习了,这种编译器导致的问题还是很难查的
xmshao 回答时间:2017-6-25 13:34:19
gege770 发表于 2017-6-17 14:22
不是堆的问题,答案已经被找到了。由于将#if 0改为#if 1之后,由于代码量变大,MDK5自动的将发送缓冲区分派 ...

oh 谢谢反馈!赞!
shuolang126 回答时间:2017-6-26 08:56:55
恩,学习了,之前较少使用M4系列内核MCU,也没有遇到过这个问题,可以参考下面这个文档,学习CCM的用法。
http://www.stmcu.com.cn/Designre ... b7562f11b9bbd82d0df
--------------------------------------------------------
STM32家族中基于ARM Cortex M4内核的STM32F3与STM32F4系列芯片内有一块特殊的SRAM存储区---内核耦合存储器[CCM RAM],为CPU所专享访问。主要是为了实现最高系统时钟频率下执行代码,同时避免出现等待状态。因此,与从闪存里执行代码相比,就大大减少了关键任务的执行时间。

CCM RAM一般用于实时性高或计算密集型程序,比如:
* 数字电源的控制环(开关电源,照明)
* 3相矢量电机控制
* 实时DSP 任务

当程序代码位于CCM RAM且数据保存在常规SRAM时,M4内核便处于最优的哈佛结构配置,让指令零等待执行从而实现速度最大化。如果中断服务程序位于CCM RAM中,可以确保最短的时延,提高响应速度。

当CCM RAM不用来存放执行代码时,也可当做常规SRAM使用。不过它不能被DMA访问。另外,也不推荐在CCM中同时存放指令代码和数据,不然内核访问CCM里的数据和指令时可能会出现冲突,影响CPU的处理性能。
-------------------------------------------------------------
从资料看,如何使用CCM内存,需要工程做相应配置。

评分

参与人数 1ST金币 +5 收起 理由
zero99 + 5

查看全部评分

lulugl 回答时间:2017-6-26 09:21:07
除了用keil还可以用什么工具没有坑的,楼主
asmhai 回答时间:2017-6-26 23:11:36
签到签到。
sz_Dean 回答时间:2018-11-16 11:28:58
不错,学习了
pig141 回答时间:2020-6-1 09:52:22
gege770 发表于 2017-6-17 14:22
不是堆的问题,答案已经被找到了。由于将#if 0改为#if 1之后,由于代码量变大,MDK5自动的将发送缓冲区分派 ...

学习学习

所属标签

相似问题

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版