完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>
STM32实现USART+DMA接收未知长度的数据和发送
STM32学习笔记三 竹天笑 前言:开始学USART+DMA的时候看到帖子《STM32 UART DMA实现未知数据长度接收》,觉得方法妙极了。此下出自此帖子——(整体的思路是这样的,一开始设置好DMA接收,可以把缓冲区长度设置为帧最大长度,我们可以把RX连接到定时器的管脚输入端,并且一开始设置输入并且使能引脚下降沿中断,当帧的第一个字节发送时,因为起始位为低电平,空闲时UART为高电平,满足条件,进入中断,禁止中断,并且在中断中开启定时器,该定时器工作在复位模式,上升沿复位,并且设置好定时器输出比较值为超时时间,比如20ms,这样,在传输后面字节时,肯定会有高低电平出现,即便是传输的是0x00,0xFF,虽然UART数据区不变,但是都为1,或都为0,但是因为起始位为低电平,停止位是高电平,所以肯定会有上升沿,定时器会一直复位,输出定时器的计数器一直到达不了输出比较值,当一帧传输结束后,定时在最后一个字节复位后,由于没有数据继续到达,无法复位,则计数器就能计到输出比较值,这时发出中断,在定时器中断中可以计算出接收数据的长度,并且通知外部数据已经接收完毕。) 今天我在工作中调通了另一种USART+DMA接收未知数据长度的接收,使用的是USRAT空闲总线中断接收,这种方法也在网站上比较多见,以前没试过,今天才知道如此的爽,另外我使用DMA发送USART数据替代了以前的查询法发送,发现更加爽了。其速度快了很多,尤其是在大量数据传输与发送的时候其优势更加明显。 我举个例子:1、后台数据->USART1-> USART2->其它设备,其它设备数据->USART2-> USART1->后台,这两个数据过程也可能同时进行。 2、由于硬件的限制,USART1和USART2的传输波特率不一样,比如USART1使用GPRS 通信,USART2使用短距离无线通信;或者USART1使用以太网通信,USART2使用485总线通信。 由于在寝室只有笔记本电脑,只有一个串口转USB,没办法实现两个串口之间的数据转发了,只好实现串口各自的数据转发。 现在我把我实现的过程简单描述一下: 1、 初始化设置:USART1_RX+DMA1_ Channel5,USART2_RX+DMA1_ Channel6,USART1_TX+DMA1_ Channel4,USART2_TX+DMA1_ Channel7(具体设置请看程序包)。 2、 当数据发送给USART1接收完毕时候会引起USART1的串口总线中断,计算DMA1_ Channel5内存数组剩余容量,得到接收的字符长度。将接收的字符复制给DMA1_ Channel4内存数组,启动DMA1_ Channel4通道传输数据,(传输完成需要关闭。)下一次数据接收可以在启动DMA1_ Channel4时候就开始,不需要等待DMA1_ Channel4数据传输完成。但是上一次DMA1_ Channel4完成之前,不可以将数据复制给DMA1_ Channel4内存数组,会冲掉以前数据。 3、 USART2类同USART1。 呵呵,下面贴程序:IO口定义: void GPIO_Configura tion(void) { GPIO_InitTypeDef GPIO_InitStructure; /* 第1步:打开GPIO和USART部件的时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); /* 第2步:将USART Tx的GPIO配置为推挽复用模式 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* 第3步:将USART Rx的GPIO配置为浮空输入模式 由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的 但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); /* 第1步:打开GPIO和USART2部件的时钟 */ //RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); /* 第2步:将USART2 Tx的GPIO配置为推挽复用模式 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* 第3步:将USART2 Rx的GPIO配置为浮空输入模式 由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的 但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); /*第3步已经做了,因此这步可以不做 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; */ GPIO_Init(GPIOA, &GPIO_InitStructure); } 串口初始化: void USART_Configuration(void) { USART_InitTypeDef USART_InitStructure; /* 第4步:配置USART参数 - BaudRate = 115200 baud - Word Length = 8 Bits - One Stop Bit - No parity - Hardware flow control disabled (RTS and CTS signals) - Receive and transmit enabled */ USART_InitStructure.USART_BaudRate = 19200; 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(USART1, &USART_InitStructure); //空闲中断 USART_ITConfig(USART1, USART_IT_IDLE , ENABLE); /* 第5步:使能 USART, 配置完毕 */ USART_Cmd(USART1, ENABLE); /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去 如下语句解决第1个字节无法正确发送出去的问题 */ USART_ClearFlag(USART1, USART_FLAG_TC); /* 清发送外城标志,Transmission Complete flag */ USART_InitStructure.USART_BaudRate = 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; USART_Init(USART2, &USART_InitStructure); USART_ITConfig(USART2, USART_IT_IDLE , ENABLE);//开启空闲,帧错,噪声,校验错中断 USART_Cmd(USART2, ENABLE); /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去 如下语句解决第1个字节无法正确发送出去的问题 */ USART_ClearFlag(USART2, USART_FLAG_TC); /* 清发送外城标志,Transmission Complete flag */ } DMA配置: void DMA_Configuration(void) { DMA_InitTypeDef DMA_InitStructure; /* DMA clock enable */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1 /* DMA1 Channel4 (triggered by USART1 Tx event) Config */ DMA_DeInit(DMA1_Channel4); DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40013804; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_SEND_DATA; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 512; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); DMA_ITConfig(DMA1_Channel4, DMA_IT_TE, ENABLE); /* Enable USART1 DMA TX request */ USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); DMA_Cmd(DMA1_Channel4, DISABLE); /* DMA1 Channel5 (triggered by USART2 Tx event) Config */ DMA_DeInit(DMA1_Channel7); DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40004404; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART2_SEND_DATA; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 512; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel7, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel7, DMA_IT_TC, ENABLE); DMA_ITConfig(DMA1_Channel7, DMA_IT_TE, ENABLE); /* Enable USART1 DMA TX request */ USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE); DMA_Cmd(DMA1_Channel7, DISABLE); /* DMA1 Channel5 (triggered by USART1 Rx event) Config */ DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40013804; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_RECEIVE_DATA; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 512; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel5, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); DMA_ITConfig(DMA1_Channel5, DMA_IT_TE, ENABLE); /* Enable USART1 DMA RX request */ USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); DMA_Cmd(DMA1_Channel5, ENABLE); /* DMA1 Channel6 (triggered by USART1 Rx event) Config */ DMA_DeInit(DMA1_Channel6); DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40004404; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART2_RECEIVE_DATA; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 512; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel6, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE); DMA_ITConfig(DMA1_Channel6, DMA_IT_TE, ENABLE); /* Enable USART2 DMA RX request */ USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE); DMA_Cmd(DMA1_Channel6, ENABLE); } 中断优先级配置: void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Configure one bit for preemption priority */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* Enable the USART1 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* Enable the USART2 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //Enable DMA Channel4 Interrupt NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //Enable DMA Channel7 Interrupt NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /*Enable DMA Channel5 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /*Enable DMA Channel6 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } 数组定义,含义如题名: u8 USART1_SEND_DATA; u8 USART2_SEND_DATA; u8 USART1_RECEIVE_DATA; u8 USART2_RECEIVE_DATA; u8 USART1_TX_Finish=1;// USART1发送完成标志量 u8 USART2_TX_Finish=1; // USART2发送完成标志量 USART1中断服务函数 void USART1_IRQHandler(void) { u16 DATA_LEN; u16 i; if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//如果为空闲总线中断 { DMA_Cmd(DMA1_Channel5, DISABLE);//关闭DMA,防止处理其间有数据 //USART_RX_STA = USART1->SR;//先读SR,然后读DR才能清除 //USART_RX_STA = USART1->DR; DATA_LEN=512-DMA_GetCurrDataCounter(DMA1_Channel5); if(DATA_LEN > 0) { while(USART1_TX_Finish==0)//等待数据传输完成才下一次 { ; } //将数据送DMA存储地址 for(i=0;i USART1_SEND_DATA=USART1_RECEIVE_DATA; } //USART用DMA传输替代查询方式发送,克服被高优先级中断而产生丢帧现象。 DMA_Cmd(DMA1_Channel4, DISABLE); //改变datasize前先要禁止通道工作 DMA1_Channel4->CNDTR=DATA_LEN; //DMA1,传输数据量 USART1_TX_Finish=0;//DMA传输开始标志量 DMA_Cmd(DMA1_Channel4, ENABLE); } //DMA_Cmd(DMA1_Channel5, DISABLE);//关闭DMA,防止处理其间有数据 DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_TE5 | DMA1_FLAG_HT5);//清标志 DMA1_Channel5->CNDTR = 512;//重装填 DMA_Cmd(DMA1_Channel5, ENABLE);//处理完,重开DMA //读SR后读DR清除Idle i = USART1->SR; i = USART1->DR; } if(USART_GetITStatus(USART1, USART_IT_PE | USART_IT_FE | USART_IT_NE) != RESET)//出错 { USART_ClearITPendingBit(USART1, USART_IT_PE | USART_IT_FE | USART_IT_NE); } USART_ClearITPendingBit(USART1, USART_IT_TC); USART_ClearITPendingBit(USART1, USART_IT_IDLE); } USART2中断服务函数 void USART2_IRQHandler(void) { u16 DATA_LEN; u16 i; if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) //如果为空闲总线中断 { DMA_Cmd(DMA1_Channel6, DISABLE);//关闭DMA,防止处理其间有数据 //USART_RX_STA = USART1->SR;//先读SR,然后读DR才能清除 //USART_RX_STA = USART1->DR; DATA_LEN=512-DMA_GetCurrDataCounter(DMA1_Channel6); if(DATA_LEN > 0) { while(USART2_TX_Finish==0)//等待数据完成才下一次 { ; } //将数据送DMA存储地址 for(i=0;i USART2_SEND_DATA=USART2_RECEIVE_DATA; } //USART用DMA传输替代查询方式发送,克服被高优先级中断而产生丢帧现象。 DMA_Cmd(DMA1_Channel7, DISABLE); //改变datasize前先要禁止通道工作 DMA1_Channel7->CNDTR=DATA_LEN; //DMA1,传输数据量 USART2_TX_Finish=0;//DMA传输开始标志量 DMA_Cmd(DMA1_Channel7, ENABLE); } //DMA_Cmd(DMA1_Channel5, DISABLE);//关闭DMA,防止处理其间有数据 DMA_ClearFlag(DMA1_FLAG_GL6 | DMA1_FLAG_TC6 | DMA1_FLAG_TE6 | DMA1_FLAG_HT6);//清标志 DMA1_Channel6->CNDTR = 512;//重装填 DMA_Cmd(DMA1_Channel6, ENABLE);//处理完,重开DMA //读SR后读DR清除Idle i = USART2->SR; i = USART2->DR; } if(USART_GetITStatus(USART2, USART_IT_PE | USART_IT_FE | USART_IT_NE) != RESET)//出错 { USART_ClearITPendingBit(USART2, USART_IT_PE | USART_IT_FE | USART_IT_NE); } USART_ClearITPendingBit(USART2, USART_IT_TC); USART_ClearITPendingBit(USART2, USART_IT_IDLE); } DMA1_Channel5中断服务函数 void DMA1_Channel5_IRQHandler(void) { DMA_ClearITPendingBit(DMA1_IT_TC5); DMA_ClearITPendingBit(DMA1_IT_TE5); DMA_Cmd(DMA1_Channel5, DISABLE);//关闭DMA,防止处理其间有数据 DMA1_Channel5->CNDTR = 580;//重装填 DMA_Cmd(DMA1_Channel5, ENABLE);//处理完,重开DMA } DMA1_Channel6中断服务函数 void DMA1_Channel6_IRQHandler(void) { DMA_ClearITPendingBit(DMA1_IT_TC6); DMA_ClearITPendingBit(DMA1_IT_TE6); DMA_Cmd(DMA1_Channel6, DISABLE);//关闭DMA,防止处理其间有数据 DMA1_Channel6->CNDTR = 580;//重装填 DMA_Cmd(DMA1_Channel6, ENABLE);//处理完,重开DMA } DMA1_Channel4中断服务函数 //USART1使用DMA发数据中断服务程序 void DMA1_Channel4_IRQHandler(void) { DMA_ClearITPendingBit(DMA1_IT_TC4); DMA_ClearITPendingBit(DMA1_IT_TE4); DMA_Cmd(DMA1_Channel4, DISABLE);//关闭DMA USART1_TX_Finish=1;//置DMA传输完成 } DMA1_Channel7中断服务函数 //USART2使用DMA发数据中断服务程序 void DMA1_Channel7_IRQHandler(void) { DMA_ClearITPendingBit(DMA1_IT_TC7); DMA_ClearITPendingBit(DMA1_IT_TE7); DMA_Cmd(DMA1_Channel7, DISABLE);//关闭DMA USART2_TX_Finish=1;//置DMA传输完成 }呵呵,全部完,但是程序在开始启动时会出现自己发几个不知道什么字符,之后一切正常。如有什么问题,请大神指教。个人认为问题不大,因为在工作的时候通过STM32访问后台或者后台访问STM32大量的间隔密的数据时没有出现问题。而如果没有使用DMA,单帧数据发收可以,多帧数据经过USART1转USART2,就收不到从USART2反馈的第二帧数据了。不一定是速度上的问题,可能是我处理顺序的问题,但是不管是巧合,还是瞎撞的,总归解决办法的就是好办法。
|
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
608 浏览2 评论
12634 浏览0 评论
3981 浏览7 评论
2214 浏览9 评论
2017 浏览2 评论
340浏览2评论
608浏览2评论
389浏览1评论
436浏览1评论
AD7656在没有输入信号时,测量输入端都会有几mV至几十mV的电压,怎么消除呢?
353浏览1评论
小黑屋|手机版|Archiver|德赢Vwin官网(湘ICP备2023018690号)
GMT+8, 2024-9-1 03:06, Processed in 0.683905 second(s), Total 42, Slave 33 queries .
Powered by德赢Vwin官网 网
© 2015bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号