STM32
直播中

王尚岱

9年用户 1616经验值
擅长:电源/新能源
私信 关注
[问答]

STM32F103的DMA无法实现共存怎么解决?

有幸在工程中用到STM32F103的DMA功能,而且是两个串口(USART2和USART3)都需要用到DMA功能来实现数据传输。以前用STM32F103都是只用过一个DMA通道,这次用到了DMA1的两个通道,结果发现了一个惊天的问题:

两个通道的DMA无法实现共存!!!

先贴代码:

  • //DMA1的各通道配置
  • //这里的传输形式是固定的,这点要根据不同的情况来修改
  • //从存储器->外设模式/8位数据宽度/存储器增量模式
  • //DMA_CHx:DMA通道CHx
  • //cpar:外设地址
  • //cmar:存储器地址
  • void UART_DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar, u32 Direction)
  • {
  •         DMA_InitTypeDef DMA_InitStructure;
  •         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1|RCC_APB2Periph_AFIO, ENABLE);        //使能DMA传输
  •         DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
  •         DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址
  •         DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
  •         DMA_InitStructure.DMA_DIR = Direction;//数据传输方向,DMA_DIR_PeripheralDST:从内存读取发送到外设;DMA_DIR_PeripheralSRC:从外设读取发送到内存
  •         DMA_InitStructure.DMA_BufferSize = 32;  //DMA通道的DMA缓存的大小
  •         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
  •         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
  •         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
  •         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
  •         DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA_Mode_Normal;//;// ;  //工作在循环缓存模式
  •         DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//DMA_Priority_Medium; //DMA通道 x拥有中优先级
  •         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
  •         DMA_Init(DMA_CHx,  DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
  • }

这是一个总的配置函数,接下来是USART2和USART3的配置:

  • void My_usart3_init(u32 bound)
  • {
  •         //GPIO端口设置
  •         GPIO_InitTypeDef  GPIO_InitStructure;
  •         USART_InitTypeDef USART_InitStructure;
  •         NVIC_InitTypeDef  NVIC_InitStructure;

  •         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);
  •         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
  •          //USART3_TX   PB.10
  •         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  •         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  •         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  •         GPIO_Init(GPIOB,  GPIO_InitStructure);

  •         //USART3_RX          PB.11
  •         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
  •         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  •         GPIO_Init(GPIOB,  GPIO_InitStructure);

  •         //USART 初始化设置
  •         USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
  •         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;

  •         //配置串口3的DMA中断
  •         NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;
  •         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  •         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  •         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  •         NVIC_Init( NVIC_InitStructure);

  •         UART_DMA_Config(DMA1_Channel3,(u32) USART3->DR,(u32)USART3_RX_BUF,DMA_DIR_PeripheralSRC);//DMA1通道3,外设为串口3,存储器为USART3_RX_BUF,外设到内存
  •         USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);          //使能串口2的DMA接收
  •         USART_Init(USART3,  USART_InitStructure);
  •         USART_Cmd(USART3, ENABLE);                    //使能串口
  •         DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);        //使能DMA1_Channel3的数据传输完成中断        DMA1_IT_TC3
  •         DMA_Cmd(DMA1_Channel3, ENABLE);           //开启DMA传输
  • }


  • void My_usart2_init(u32 bound)
  • {
  •         //GPIO端口设置
  •         GPIO_InitTypeDef  GPIO_InitStructure;
  •         USART_InitTypeDef USART_InitStructure;
  •         NVIC_InitTypeDef  NVIC_InitStructure;

  •         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
  •         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  •          //USART2_TX   PA.2
  •         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  •         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  •         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  •         GPIO_Init(GPIOA,  GPIO_InitStructure);

  •         //USART2_RX          PA.3
  •         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  •         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  •         GPIO_Init(GPIOA,  GPIO_InitStructure);

  •         NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  •         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
  •         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;                //

  •         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
  •         NVIC_Init( NVIC_InitStructure);        //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器USART1

  •         //USART 初始化设置
  •         USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
  •         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;

  •         UART_DMA_Config(DMA1_Channel6,(u32) USART2->DR,(u32)USART2_RX_BUF,DMA_DIR_PeripheralSRC);//DMA1通道7,外设为串口2,存储器为USART2_RX_BUF,外设到内存
  •         USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);          //使能串口2的DMA接收
  •         USART_Init(USART2,  USART_InitStructure);
  •         USART_Cmd(USART2, ENABLE);                    //使能串口
  •         DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);        //使能DMA1_Channel6的数据传输完成中断        DMA1_IT_TC6
  •         DMA_Cmd(DMA1_Channel6, ENABLE);           //开启DMA传输
  • }

OK了,这样USART2和USART3就和DMA通道连在一起了。USART2是DMA1_Channel6,USART3是DMA1_Channel3。
然后就是两个DMA中断函数,在这里进行数据处理:

  • void DMA1_Channel3_IRQHandler()
  • {

  •         if(DMA_GetITStatus(DMA1_IT_TC3) != RESET)
  •         {
  • //                printf("overrrn");
  •                 DMA_ClearITPendingBit(DMA1_IT_TC3);
  • ………………………………

  •                 }
  •         //执行完DMA1_Channel3的中断后, 把channel3失能,把channel6使能,这样就可以进入usart2的DMA中断了
  •         //和usart2中遥相呼应
  • //        DMA_Cmd(DMA1_Channel6, ENABLE);
  • //        DMA_Cmd(DMA1_Channel3, DISABLE);
  •         }
  • }


  • void DMA1_Channel6_IRQHandler()
  • {
  •         u16 sum=0;
  •         if(DMA_GetITStatus(DMA1_IT_TC6) != RESET)
  •         {
  • //                printf("overrrn");
  •                 DMA_ClearITPendingBit(DMA1_IT_TC6);

在上面的中断函数中,进行相应的数据处理。

接下来就是重点了!高能!

经过认真测试,USART2的DMA1_Channel6和USART3的DMA1_Channel3无法同时在程序中起作用!!!

不过仔细一想,本来嘛,STM32的DMA1的总线只有一条,如果Channel6占据了,当然Channel3就无法工作了,这也是合理的。怎么办呢?分时复用一下就OK了吧。OK,设置分时复用:

  • void DMA1_Channel3_IRQHandler()
  • {

  • if(DMA_GetITStatus(DMA1_IT_TC3) != RESET)
  • {
  • //        printf("overrrn");
  • DMA_ClearITPendingBit(DMA1_IT_TC3);
  • ………………………………

  • }
  • //执行完DMA1_Channel3的中断后, 把channel3失能,把channel6使能,这样就可以进入usart2的DMA中断了
  • //和usart2中遥相呼应
  •         DMA_Cmd(DMA1_Channel6, ENABLE);
  •         DMA_Cmd(DMA1_Channel3, DISABLE);
  • }
  • }


  • void DMA1_Channel6_IRQHandler()
  • {
  • u16 sum=0;
  • if(DMA_GetITStatus(DMA1_IT_TC6) != RESET)
  • {
  • //        printf("overrrn");
  • DMA_ClearITPendingBit(DMA1_IT_TC6);
  •         //执行完DMA1_Channel6的中断后, 把channel6失能,把channel3使能,这样就可以进入usart3的DMA中断了
  •         //和usart3中遥相呼应
  •         DMA_Cmd(DMA1_Channel6, DISABLE);
  •         DMA_Cmd(DMA1_Channel3, ENABLE);
  • }
  • }

这样总可以了吧!虽然不断地开关浪费时间,可是我只要能实现两个通道都可以用就OK!

然而too young too simple!竟然是不可以的,两个通道都不工作!!!

是因为变化的太快了?
反正是都没有受到数据。
这是不是STM32F103的固有缺陷?就像硬件IIC一样?

猜测刚进系统的时候,两个DMA传输是抢占式的,谁在前使能,谁就占用了DMA总线,除非数据传输完成

回帖(1)

安德森大

2024-5-11 17:20:03
STM32F103的DMA无法实现共存的问题可能是由于DMA通道冲突或者配置不当导致的。为了解决这个问题,你可以尝试以下方法:

1. 确保DMA通道没有冲突:检查你的代码,确保USART2和USART3使用的DMA通道没有冲突。例如,USART2使用DMA1_Channel1,而USART3使用DMA1_Channel2。

2. 检查DMA配置:确保你正确配置了DMA的传输方向、数据宽度、存储器增量模式等参数。你提供的代码片段中,这些参数需要根据实际情况进行修改。

3. 配置DMA优先级:如果两个DMA通道的优先级相同,可能会导致冲突。你可以尝试调整DMA通道的优先级,以确保它们可以共存。例如,你可以使用`DMA_PriorityLevel_Config(DMA_CHx, DMA_Priority_High);`来设置DMA通道的优先级。

4. 确保DMA中断没有冲突:检查你的代码,确保USART2和USART3的DMA中断没有冲突。如果它们共享同一个中断,可能会导致问题。你可以尝试为每个USART配置不同的中断。

5. 检查DMA传输完成标志:在DMA传输完成后,确保你检查了DMA传输完成标志,如`DMA_GetFlagStatus(DMA1_FLAG_TC1)`。这可以帮助你确定DMA传输是否成功完成。

6. 使用DMA中断回调函数:在DMA传输完成后,你可以使用DMA中断回调函数来处理数据。确保你正确实现了这些回调函数,以便在DMA传输完成后进行适当的处理。

7. 更新固件库:确保你使用的STM32固件库是最新的,因为新版本的固件库可能修复了旧版本中的一些已知问题。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分