配置时无法访问STM32 DMA寄存器

Posted

技术标签:

【中文标题】配置时无法访问STM32 DMA寄存器【英文标题】:I cannot access STM32 DMA register when configuring it 【发布时间】:2021-08-08 12:04:00 【问题描述】:

我目前使用标准外设库来编写一个驱动程序,使内存能够在 STM32 F407 ZGT6 芯片上进行 USART6_TX DMA 传输。但是,我尝试了很长时间,但初始化一直失败:DMA_GetCmdStatus 总是返回 DISABLE。通过使用GDB,我发现DMA_Init尝试将配置写入DMA寄存器后,DMA CR寄存器仍然为0。DMA初始化代码和执行如下:

void DMA_USART6_Init(char* DMA_Start_Pos, uint32_t DMA_Buffer_Size)
DMA_Buffer_Size_GV = DMA_Buffer_Size;
DMA_Start_Pos_GV = DMA_Start_Pos;
/*RCC config*/
RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA2, ENABLE);
/*DMA init*/
//DMA_DeInit(DMA2_Stream7);
DMA_Cmd(DMA2_Stream7, DISABLE);
while ((DMA_GetCmdStatus(DMA2_Stream7) == ENABLE))
DMA_StructInit(&DMA_InitStruct);

DMA_InitStruct.DMA_Channel = DMA_Channel_5;
DMA_InitStruct.DMA_PeripheralBaseAddr = USART6_BASE + 0x04;//(uint32_t)&USART6->DR;
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t) dubuff;//(uint32_t) DMA_Start_Pos;
DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStruct.DMA_BufferSize = (uint16_t)sizeof(dubuff);//DMA_Buffer_Size;
printf("buffer size should be %d \r\n", (uint16_t)sizeof(dubuff));

DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_Init(DMA2_Stream7, &DMA_InitStruct);
printf("data counter after init %d \r\n", DMA_GetCurrDataCounter(DMA2_Stream7));

对于启用代码

void DMA_USART6_Enable_DMA(char* DMA_Start_Pos, uint32_t DMA_Buffer_Size, int MB)
Max_Buffer = MB;

USART6_init_for_DMA();
DMA_USART6_Init(DMA_Start_Pos, DMA_Buffer_Size);
DMA_USART6_NVIC_Init();
DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream7, ENABLE);
while ((DMA_GetCmdStatus(DMA2_Stream7) == DISABLE))//program stucked in this loop

对于 USART6 代码:

void USART6_init_for_DMA(void)
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6);

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate = 57600;//115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

USART_Init(USART6, &USART_InitStructure);
USART_Cmd(USART6, ENABLE);
USART_DMACmd(USART6, USART_DMAReq_Tx, ENABLE);

和 GDB 结果:

the value that should be written into DMA CR register

图片1中的文字:

367   DMAy_Streamx->CR = tmpreg;
(gdb) print tmpreg 
$7 = 167904576

the DMA CR register remains 0

图片中的文字2:

371   tmpreg = DMAy_Streamx->FCR;
(gdb) print *DMAy_Streamx
$10 = CR = 0, NDTR = 0, PAR = 0, M0AR = 0, M1AR = 0, FCR = 0

请告诉我是否可以提供有关我的问题的任何信息或可以进一步尝试的事情... 注意:在同一个程序中,我的 USART6 和 GPIO 工作正常。

【问题讨论】:

“寄存器保持为0”通常表示时钟尚未启用。您是否启用了 DMA 时钟? @Codo 典型问题。使用“魔法”库的人通常不会阅读 uC 文档。 @Brad 使用死 SPL 有什么意义?为什么要使用命令行 gdb? @Codo 我相信“RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA2, ENABLE);”完成了这项工作。 【参考方案1】:

当所需的流被激活时,相关的寄存器不能再被配置。只有在双缓冲模式下,内存地址 0 和 1 才能根据位 CT 功能进行更新。您必须先停用相关频道,然后再应用更改。

【讨论】:

感谢您的建议。但是,我相信“DMA_Cmd(DMA2_Stream7, DISABLE);”并且“while ((DMA_GetCmdStatus(DMA2_Stream7) == ENABLE))” 完成了这项工作。而且,当CR位保持为0时,也意味着EN位为0,表示流没有被激活。 我建议你检查一次大厅创建的源代码。也许你从中意识到了一个想法或一个重要的观点。在配置中,您必须根据一系列特定步骤进行操作,最后通过设置 EN 位来激活通道。这些安排也可以在芯片的用户手册中看到。 模块包含三个BUS。 AHBmem 和 AHBPer 用于在 Bus-matrix 中传输数据,而总线 APB 用于访问配置寄存器。如果您无法访问任何寄存器,则总线 APB 可能尚未激活。模块时钟未配置,必须先通过RCC中对应的寄存器来使能 我查看了参考手册 (RM0090) 和 SPL 描述 (UM1061)。似乎这两个文件都只在 DMA 章节中提到了 AHBmem/per bus。同样在 RCC 寄存器会话中,RCC_APBxENR 中的所有位都与 CAN、SPI、Timer 等有关。如果有其他文件提到如何启动 APB 以访问 DMA 寄存器,请告知我,我将不胜感激。 请看一下AN4031【参考方案2】:

我是 OP。 一开始,我有一种感觉,这可能是一个明显的错误,这是真的。 事实证明,我应该使用 RCC_AHB1PeriphClockCmd 而不是 RCC_AHB1PeriphResetCmd,因为显然后来访问的 RSTR(RCC 复位寄存器)而不是 ENR,因此时钟未启用(关于 Codo 是正确的)。

好吧,希望看到这个问题的人不要重复这个尴尬的错误;(

【讨论】:

仅适用于发现此问题的任何其他人...如果您使用的是 CubeMX,则存在一个已知问题。 community.st.com/s/question/0D50X0000Bmob3uSQA/…

以上是关于配置时无法访问STM32 DMA寄存器的主要内容,如果未能解决你的问题,请参考以下文章

stm32dma串口没有收到包头

STM32 DMA的使用总结

STM32F103(二十一)DMA(超详细的~)

STM32F4 UART1 DMA发送和接收不定长度数据

STM32 CubeMX 学习:06-配置DMA

STM32 CubeMX 学习:06-配置DMA