完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
在使用串口时,一般采用查询发送,中断接收。但当要接收一串很长的数据时,每收到一个字节进入一次串口中断,有可能会导致中断占用时间过长。如果有一种方式,能够让串口收完一串数据,才进一次中断,那将是对写底层驱动的人来说,是极其好的一件事。经过查资料看手册,发现可以采用串口空闲中断和DMA接收来实现这个功能。具体更详细的说明后续补充,现只贴出代码,以供参考。
调试的过程中发现几个问题: 1、要串口初始化放在DMA初始化之前,否则会出现DMA发送和接收使用不了的问题; 2、DMA接收配置中DMA模式要配置为循环模式,如果配置成为正常模式会导致只能接收到一次数据,问题未知; 如果有某位大神知道这两个问题的原因,望不吝赐教。 画布多少,直接上代码: //usart.h 头文件中要定义如下: //该结构主要用来存放所有的串口相关的配置参数 typedef struct { uint32_t baud_rate; uint16_t gpio_tx; uint16_t gpio_rx; uint8_t gpio_tx_pin_source; uint8_t gpio_rx_pin_source; GPIO_TypeDef * usart_tx_port; GPIO_TypeDef * usart_rx_port; USART_TypeDef * usart_base; uint32_t gpio_tx_rcc; uint32_t gpio_rx_rcc; uint32_t apb_periph_rcc ; uint8_t gpio_af; uint32_t dma_rx_rcc; DMA_Stream_TypeDef* dma_rx_stream; uint32_t dma_rx_channel; uint32_t dma_tx_rcc; DMA_Stream_TypeDef* dma_tx_stream; uint32_t dma_tx_channel; uint8_t tx_buff[48]; uint8_t rx_buff[48]; uint8_t len; }UsartParameter_Typedef; /******************************USART6***********************************/ #define USART6_GPIO_TX GPIO_Pin_9 #define USART6_TX_PIN_SOURCE GPIO_PinSource9 #define USART6_GPIO_RX GPIO_Pin_14 #define USART6_RX_PIN_SOURCE GPIO_PinSource14 #define USART6_TX_PORT GPIOG #define USART6_RX_PORT GPIOG #define USART6_GPIO_TX_RCC RCC_AHB1Periph_GPIOG #define USART6_GPIO_RX_RCC RCC_AHB1Periph_GPIOG #define USART6_APBPeriph_RCC RCC_APB2Periph_USART6 #define USART6_GPIO_AF GPIO_AF_USART6 #define USART6_RX_DMA_RCC RCC_AHB1Periph_DMA2 #define USART6_RX_DMA_STREAM DMA2_Stream1 #define USART6_RX_DMA_CHANNEL DMA_Channel_5 #define USART6_TX_DMA_RCC RCC_AHB1Periph_DMA2 #define USART6_TX_DMA_STREAM DMA2_Stream6 #define USART6_TX_DMA_CHANNEL DMA_Channel_5 #define USART6_DMA_IT_TCIF DMA_IT_TCIF6 #define USE_USART6_TX_DMA //使能发送DMA #define USE_USART6_RX_DMA //使能接收DMA //usart.c //指定初始化结构 UsartParameter_Typedef Usart6_parameter= { .baud_rate = USART6_BaudRate, .gpio_tx = USART6_GPIO_TX, .gpio_rx = USART6_GPIO_RX, .gpio_tx_pin_source = USART6_TX_PIN_SOURCE, .gpio_rx_pin_source = USART6_RX_PIN_SOURCE, .usart_tx_port = USART6_TX_PORT, .usart_rx_port = USART6_RX_PORT, .usart_base = USART6, .gpio_tx_rcc = USART6_GPIO_TX_RCC, .gpio_rx_rcc = USART6_GPIO_RX_RCC, .apb_periph_rcc = USART6_APBPeriph_RCC , .gpio_af = USART6_GPIO_AF, .dma_rx_rcc = USART6_RX_DMA_RCC, .dma_rx_stream =USART6_RX_DMA_STREAM , .dma_rx_channel = USART6_RX_DMA_CHANNEL, .dma_tx_rcc = USART6_TX_DMA_RCC, .dma_tx_stream = USART6_TX_DMA_STREAM, .dma_tx_channel = USART6_TX_DMA_CHANNEL, }; //串口初始化模板函数 static void InitUsart(const UsartParameter_Typedef* usart) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(usart->apb_periph_rcc, ENABLE); RCC_AHB1PeriphClockCmd( usart->gpio_tx_rcc, ENABLE); RCC_AHB1PeriphClockCmd( usart->gpio_rx_rcc, ENABLE); USART_InitStructure.USART_BaudRate = usart->baud_rate; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(usart->usart_base, &USART_InitStructure); // USART_ITConfig(usart->usart_base, USART_IT_RXNE, ENABLE); USART_ITConfig(usart->usart_base,USART_IT_IDLE,ENABLE); USART_Cmd(usart->usart_base, ENABLE); /*初始化串口后再,配置Io口,防止串口初始化中会发一个无效的0x00或者0xfe*/ GPIO_InitStructure.GPIO_Pin = usart->gpio_tx ; 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_50MHz; GPIO_Init(usart->usart_tx_port, &GPIO_InitStructure); GPIO_PinAFConfig(usart->usart_tx_port, usart->gpio_tx_pin_source, usart->gpio_af); GPIO_InitStructure.GPIO_Pin = usart->gpio_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_50MHz; GPIO_Init(usart->usart_tx_port, &GPIO_InitStructure); GPIO_PinAFConfig(usart->usart_rx_port, usart->gpio_rx_pin_source, usart->gpio_af); USART_GetFlagStatus(usart->usart_base,USART_FLAG_TC);//清除TXE,防止第一个数据没有发送出来 } void UsartDmaTxConfig(const UsartParameter_Typedef* usart) { DMA_InitTypeDef DMA_InitStructure; /*开启DMA时钟*/ RCC_AHB1PeriphClockCmd(usart->dma_tx_rcc, ENABLE); //————————————————————————————————发送————————————————————// /* 复位初始化DMA数据流 */ DMA_DeInit(usart->dma_tx_stream); /* 确保DMA数据流复位完成 */ while (DMA_GetCmdStatus(usart->dma_tx_stream) != DISABLE) { } /*usart1 tx对应dma2,通道4,数据流7*/ DMA_InitStructure.DMA_Channel = usart->dma_tx_channel; /*设置DMA源:串口数据寄存器地址*/ DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&usart->usart_base->DR); /*内存地址(要传输的变量的指针)*/ DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)usart->tx_buff; /*方向:从内存到外设*/ DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; /*传输大小DMA_BufferSize=SENDBUFF_SIZE*/ DMA_InitStructure.DMA_BufferSize = MAX_DATA_LEN; /*外设地址不增*/ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /*内存地址自增*/ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /*外设数据单位*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; /*内存数据单位 8bit*/ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; /*DMA模式:普通模式*/ DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; /*优先级:中*/ DMA_InitStructure.DMA_Priority = DMA_Priority_High; /*禁用FIFO*/ DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; /*存储器突发传输 16个节拍*/ DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; /*外设突发传输 1个节拍*/ DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; /*配置DMA2的数据流6*/ DMA_Init(usart->dma_tx_stream, &DMA_InitStructure); /*配置发送完成中断*/ DMA_ITConfig(usart->dma_tx_stream, DMA_IT_TC,ENABLE); //此处必须开启中断,否则DMA_GetITStatus(DMA2_Stream6,DMA_IT_TCIF6)一直为0 /*使能DMA*/ DMA_Cmd(usart->dma_tx_stream, ENABLE); USART_DMACmd(usart->usart_base,USART_DMAReq_Tx,ENABLE); //使能DMA发送 } //DMA TX RX配置模板函数 static void UsartDmaRxConfig(const UsartParameter_Typedef* usart) { DMA_InitTypeDef DMA_InitStructure; /*-----------------------接收------------------------------------*/ RCC_AHB1PeriphClockCmd(usart->dma_rx_rcc, ENABLE); // RCC_AHB1PeriphResetCmd(usart->dma_rx_rcc, ENABLE); /* 复位初始化DMA数据流 */ DMA_DeInit(usart->dma_rx_stream); /* 确保DMA数据流复位完成 */ while (DMA_GetCmdStatus(usart->dma_rx_stream) != DISABLE) { } /*usart6 rx对应dma2,通道5,数据流1*/ DMA_InitStructure.DMA_Channel = usart->dma_rx_channel; /*设置DMA源:串口数据寄存器地址*/ DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t)(&usart->usart_base->DR)/*((uint32_t)usart->usart_base+0x04)usart->usart_base->DR*/; /*内存地址(要传输的变量的指针)*/ DMA_InitStructure.DMA_Memory0BaseAddr = (u32)usart->rx_buff; /*方向:从内存到外设*/ DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; /*传输大小DMA_BufferSize=SENDBUFF_SIZE*/ DMA_InitStructure.DMA_BufferSize = MAX_DATA_LEN; /*外设地址不增*/ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /*内存地址自增*/ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /*外设数据单位*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; /*内存数据单位 8bit*/ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; /*DMA模式:普通模式*/ DMA_InitStructure.DMA_Mode = /*DMA_Mode_Normal*/DMA_Mode_Circular; /*优先级:中*/ DMA_InitStructure.DMA_Priority = DMA_Priority_High; /*禁用FIFO*/ DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; /*存储器突发传输 16个节拍*/ DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; /*外设突发传输 1个节拍*/ DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; /*配置DMA2的数据流6*/ DMA_Init(usart->dma_rx_stream, &DMA_InitStructure); /*使能DMA*/ DMA_Cmd(usart->dma_rx_stream, ENABLE); USART_DMACmd(usart->usart_base,USART_DMAReq_Rx,ENABLE); //使能DMA接收 } //串口初始化函数 void init_usart(void) { InitUsart(&Usart6_parameter); #ifdef USE_USART6_TX_DMA UsartDmaTxConfig(&Usart6_parameter); #endif #ifdef USE_USART6_RX_DMA UsartDmaRxConfig(&Usart6_parameter); #endif } |
|
|
|
//发送函数
static void Usart6Send(const uint8_t *buf,const uint8_t len) { #ifdef USE_USART6_TX_DMA UsartDMASend(buf,len,&Usart6_parameter); #else UsartSend(buf,len,&Usart6_parameter); #endif } //中断接收函数 void USART6_IRQHandler(void) { #ifdef USE_USART6_RX_DMA if(USART_GetITStatus(USART6, USART_IT_IDLE) != RESET) { Usart6_parameter.usart_base->SR; Usart6_parameter.usart_base->DR; //清USART_IT_IDLE标志 //关闭DMA DMA_Cmd(Usart6_parameter.dma_rx_stream,DISABLE); //清除标志位 DMA_ClearFlag(Usart6_parameter.dma_rx_stream,DMA_FLAG_TCIF6); //获得接收帧帧长 由于SnDTR寄存器用于指示要传输的剩余数据选项,每传输一次,递减1 Usart6_parameter.len = MAX_DATA_LEN - DMA_GetCurrDataCounter(Usart6_parameter.dma_rx_stream); // Usart6.onrx(NULL,Usart6_parameter.rx_buff,Usart6_parameter.len); DMA_Cmd(Usart6_parameter.dma_rx_stream,ENABLE); Usart6Send(Usart6_parameter.rx_buff,Usart6_parameter.len ); memset(Usart6_parameter.rx_buff,0,sizeof(Usart6_parameter.rx_buff)); //清空接收缓冲区 Usart6_parameter.len = 0; // Usart6DmaTx(TmpBuff,len); } #else uint8_t temp = 0; if(USART_GetFlagStatus(Usart6_parameter.usart_base,USART_FLAG_RXNE)==SET) { USART_ClearITPendingBit(Usart6_parameter.usart_base, USART_IT_RXNE); temp = USART_ReceiveData(Usart6_parameter.usart_base); Usart6Send(&temp,1); // Usart6ReceivedProsess(); } #endif } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
2306 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1884 浏览 1 评论
1388 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
907 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
2813 浏览 2 评论
2140浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
1099浏览 4评论
stm32f4下spi+dma读取数据不对是什么原因导致的?
445浏览 3评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
758浏览 3评论
740浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-5-12 13:00 , Processed in 0.818235 second(s), Total 49, Slave 43 queries .
Powered by 电子发烧友网
© 2015 www.ringvoyeur.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191