完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
在进行stm32开发时,有时会遇到这种情况:需要在设备间进行数据传输,由于stm32串口RDR和TDR寄存器都是8位有效的,我们往往需要定义传输协议(如一帧数据中,包含包含帧头、帧ID、数据帧、校验帧等若干8位数据)。我们希望可以一次收到一帧数据,并进行解码操作。利DMA+串口空闲中断可以有效完成上述任务。
1、简介
DMA用在只需要传输数据,不需要处理数据的地方,有三种传输方式:
在中文参考手册9.2节详细说明了DMA特性 4、DMA控制器结构
[tr]方向指针情况[/tr]
关于DMA还有双缓冲区模式、突发传输等等其他设置,一般用不到,具体查询《stm32中文参考手册》 9、示例代码 文章开头情景的示例代码如下(DMA配置部分): //DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 //chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7 //par:外设地址 //mar:存储器地址 //ndtr:数据传输量 void DMA_Config(DMA_Stream_TypeDef *DMA_Streamx,uint32_t chx,uint32_t par,uint32_t mar,uint32_t dir,u16 ndtr) { DMA_InitTypeDef DMA_InitStructure; if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1 { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能 }else { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能 } DMA_DeInit(DMA_Streamx); while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置 /* 配置 DMA Stream */ DMA_InitStructure.DMA_Channel = chx; //通道选择 DMA_InitStructure.DMA_PeripheralBaseAddr = par; //DMA外设地址 DMA_InitStructure.DMA_Memory0BaseAddr = mar; //DMA 存储器0地址 DMA_InitStructure.DMA_DIR = dir; //direction of transmit. DMA_InitStructure.DMA_BufferSize = ndtr; //数据传输量 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_Normal; // 使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //中等优先级 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输 DMA_Init(DMA_Streamx, &DMA_InitStructure); DMA_Cmd(DMA_Streamx,ENABLE); } //开启一次DMA传输 void DMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr) { DMA_Cmd(DMA_Streamx, DISABLE); //先关闭DMA,才能设置它 while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //等待传输结束 DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //设置传输数据长度 DMA_Cmd(DMA_Streamx, ENABLE); //开启DMA } 二、串口空闲中断 先看一下串口中断表 1、常用的串口接收中断
void My_USART1_Init(void) { GPIO_InitTypeDef GPIO_Initstructure; USART_InitTypeDef USART_Initstructure; NVIC_InitTypeDef NVIC_Initstrcuture; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE ); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA ,ENABLE ); GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); GPIO_Initstructure.GPIO_Pin = GPIO_Pin_9; GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF; GPIO_Initstructure.GPIO_OType = GPIO_OType_PP; GPIO_Initstructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Initstructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOA,&GPIO_Initstructure); GPIO_Initstructure.GPIO_Pin = GPIO_Pin_10;//IO³õʼ»¯RX GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF; GPIO_Initstructure.GPIO_OType = GPIO_OType_PP; GPIO_Initstructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Initstructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOA,&GPIO_Initstructure); USART_Initstructure.USART_BaudRate = 9600; USART_Initstructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Initstructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Initstructure.USART_Parity = USART_Parity_No; USART_Initstructure.USART_StopBits = USART_StopBits_1; USART_Initstructure.USART_WordLength = USART_WordLength_8b; USART_Init(USART1,&USART_Initstructure); USART_Cmd(USART1,ENABLE); USART_ITConfig (USART1,USART_IT_RXNE ,ENABLE);//开启串口接受中断 NVIC_Initstrcuture.NVIC_IRQChannel = USART1_IRQn; NVIC_Initstrcuture.NVIC_IRQChannelCmd = ENABLE ; NVIC_Initstrcuture.NVIC_IRQChannelPreemptionPriority = 1; NVIC_Initstrcuture.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_Initstrcuture); } void USART1_IRQHandler(void) { u8 res=0; if(USART_GetITStatus(USART1,USART_IT_RXNE))//串口非空标志位为1,收到数据 { res = USART_ReceiveData(USART1);//读取最新一个收到的数据 USART_SendData(USART1,res );//发送数据 } } 2、串口空闲中断
void USART1_Init(uint32_t bound)//DMA2_Stream2 { GPIO_InitTypeDef GPIO_Initstructure; USART_InitTypeDef USART_Initstructure; NVIC_InitTypeDef NVIC_Initstrcuture; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE ); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA ,ENABLE ); GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); GPIO_Initstructure.GPIO_Pin = GPIO_Pin_9; GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF; GPIO_Initstructure.GPIO_OType = GPIO_OType_PP; GPIO_Initstructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Initstructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOA,&GPIO_Initstructure); GPIO_Initstructure.GPIO_Pin = GPIO_Pin_10; GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF; GPIO_Initstructure.GPIO_OType = GPIO_OType_PP; GPIO_Initstructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Initstructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOA,&GPIO_Initstructure); USART_Initstructure.USART_BaudRate = bound; USART_Initstructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Initstructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Initstructure.USART_Parity = USART_Parity_No; USART_Initstructure.USART_StopBits = USART_StopBits_1; USART_Initstructure.USART_WordLength = USART_WordLength_8b; USART_Init(USART1,&USART_Initstructure); NVIC_Initstrcuture.NVIC_IRQChannel = USART1_IRQn; NVIC_Initstrcuture.NVIC_IRQChannelPreemptionPriority=1; NVIC_Initstrcuture.NVIC_IRQChannelSubPriority =2; NVIC_Initstrcuture.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_Initstrcuture); // USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启接收中断 USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启空闲中断 USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);//开启DMA接收 USART_Cmd(USART1, ENABLE); //initialize the DMA channel. DMA_Config(DMA2_Stream2,DMA_Channel_4, (uint32_t)&(USART1->DR), //串口DR寄存器 (uint32_t)USART1_Rx_Buffer,//自定义的接收数据buf DMA_DIR_PeripheralToMemory,//外设到存储器方向 USART1_RX_BUFFER_SIZE/2);//长度 } void USART1_IRQHandler(void) { uint8_t rc_tmp; uint16_t rc_len; uint16_t i; if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET) { rc_tmp=USART1->SR; rc_tmp=USART1->DR;//软件序列清除IDLE标志位 DMA_Cmd(DMA2_Stream2, DISABLE);关闭DMA,准备重新配置 DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2); // Clear Transfer Complete flag DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TEIF2); // Clear Transfer error flag rc_len = USART1_RX_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA2_Stream2);//计算接收数据长度 for(i=0;i printf("%d ",USART1_Rx_Buffer); usart1.rx_buf=USART1_Rx_Buffer; } printf("n"); Data_Decode(USART1_Rx_Buffer);//解码收到的数据 } DMA_Enable(DMA2_Stream2,USART1_RX_BUFFER_SIZE);//开启下一次DMA接收 } 上述代码经stm32f407平台测试通过 三、纠正 感谢qq_20246035在评论区提出问题。之前一、9部分的实例代码中,DMA_Config函数定义时,数据传输方向直接写死到配置中了,而二、2部分的示例代码中,调用DMA_Config时把数据传输方向作为参数了,运行应该会报错示例和声明不匹配。 两种改正方法:1.调用时不要写传输方向参数 ; 2.修改DMA_Config。目前已按方法二改正 再次感谢读者帮我找到错误,谢谢! 如果大家发现还有什么问题,欢迎在评论区告诉我 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1617 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1543 浏览 1 评论
977 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
683 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1595 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
645浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
516浏览 3评论
532浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
505浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 10:32 , Processed in 0.994286 second(s), Total 79, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号