有幸在工程中用到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("over\r\r\n");
- 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("over\r\r\n");
- 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("over\r\r\n");
- 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("over\r\r\n");
- 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总线,除非数据传输完成
|