完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
STM32F1应用DMA——串口收发不定长数据
使用STM32自带DMA传输数据,可以减轻CPU负担,只需设置一些参数即可发送想要发送的数据,以下是STM32F1系列芯片测试过的部分代码,可实现DMA串口收发数据。下图来自STM32官网的手册,RM0008.pdf 发送数据逻辑图: 接收数据逻辑图 下面是使用STM32 HAL库进行配置,大致实现思路都是一样的,先开启串口初始化(开启DMA传输),相应的DMA初始化,然后设置好传输地址,传输字节个数,然后启动使能 一、初始化部分 uint8_t u8txbuff[1024]; //发送缓冲区 uint8_t u8rxbuff[1024]; //接收缓冲区 void uart2_init(uint32_t m_u32bound) { //01-GPIO配置 GPIO_InitTypeDef GPIO_InitStruct = {0}; //02-开启时钟 __HAL_RCC_USART2_CLK_ENABLE(); //启动串口2时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); //启动GPIOA时钟 //03-配置引脚 GPIO_InitStruct.Pin = GPIO_PIN_2; //USART1_TX PA.2 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; //推完复用模式 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速配置 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //PA2初始化 GPIO_InitStruct.Pin = GPIO_PIN_3; //USART1_RX PA.3 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; //输入模式:浮空输入 GPIO_InitStruct.Pull = GPIO_NOPULL; //没有上下拉 HAL_GPIO_Init(GPIOA,&GPIO_InitStruct); //PA3初始化 //04-设置串口中断 HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); //05-USART 配置 I_huart2.Instance = USART2; I_huart2.Init.BaudRate = m_u32bound; //波特率 I_huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; //没有硬件控制流 I_huart2.Init.Mode = UART_MODE_TX_RX; //收发模式 I_huart2.Init.StopBits = UART_STOPBITS_1; //停止位1位 I_huart2.Init.Parity = UART_PARITY_NONE; //无奇偶校验位 I_huart2.Init.OverSampling = UART_OVERSAMPLING_16; // I_huart2.Init.WordLength = UART_WORDLENGTH_8B; //数据位:8个 if(HAL_UART_Init(&I_huart2)!=HAL_OK) //初始化串口 while(1); //04-设置中断优先级 HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); //05-启动DMA1时钟 __HAL_RCC_DMA1_CLK_ENABLE(); //07-DMA1 USART2_RX hdma_usart2_tx.Instance = DMA1_Channel7; //通道7 hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; //外设到内存 hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE; //外设地址不增长 hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE; //内存地址增长模式 hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //字节对齐 hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //字节对齐 hdma_usart2_tx.Init.Mode = DMA_NORMAL; //正常模式 hdma_usart2_tx.Init.Priority = DMA_PRIORITY_HIGH; //传输等级:高 if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK) while(1); //06-DMA1 USART2_RX hdma_usart2_rx.Instance = DMA1_Channel6; //通道6 hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; //内存到外设 hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; //外设地址不增长 hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE; //内存地址增长 hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//字节对齐 hdma_usart2_rx.Init.MemDataAlignment = DMA_PDATAALIGN_BYTE; //字节对齐 hdma_usart2_rx.Init.Mode = DMA_CIRCULAR; //循环模式 hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW; //传输等级:低 if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK) while(1); //07-开启发送完成中断 __HAL_UART_ENABLE_IT(&I_huart2, UART_IT_TC); //发送完成中断 //08-开启DMA接收 HAL_DMA_Start(&hdma_usart2_rx,(uint32_t)&USART2-》DR,(uint32_t)u8rxbuff,BUFFMAX); SET_BIT(I_huart2.Instance-》CR3, USART_CR3_DMAR);//USART2请求 DMA启动 //09-开启DMA发送 HAL_DMA_Start(&hdma_usart2_tx, (uint32_t)u8txbuff,(uint32_t)&USART2-》DR, 0); SET_BIT(I_huart2.Instance-》CR3, USART_CR3_DMAT);//USART2请求 DMA启动 } 二、串口中断部分(发送数据部分) 这个需要开启串口发送完成中断,当DMA往串口传送数据完成后,串口会产生发送完成中断。 //串口2发送中断 void USART2_IRQHandler(void) { if((USART2-》SR & UART_FLAG_TC)!=RESET) { //当DMA传输数据完成以后,就会进入一次这个中断 USART2-》SR = ~(UART_FLAG_TC); u8txmode = SEND_FINISH//此处设置:发送完成标志 } } 三、发送数据函数 每当调用这个函数就会,要求DMA发送一段数据给USART的数据寄存器当中,接下来等待进入串口发送中断后,就可以判定DMA传送数据到串口已经完成发送。 //入口参数 //txlen : 发送数据长度 void USART2_TxdSend(uint16_t u16txlen) { __HAL_DMA_DISABLE(&hdma_usart2_tx); //暂停DMA __HAL_UART_CLEAR_FLAG(&I_huart2, UART_FLAG_TC); //清除发送中断 __HAL_DMA_GET_COUNTER(&hdma_usart2_tx) = u16txlen; //设置发送数据长度 __HAL_DMA_ENABLE(&hdma_usart2_tx); //启动DMA } 三、开启定时器 开启定时器的目的是希望,周期性的去查询DMA传送了多少的数据到内存,也就是说接收了多少个字节。当个DMA传送字节个数没有变化之后(DMA_CNDTR寄存器没有变化),又或者时超过一定时间后,就可以判定一帧数据已经接收完成了。 void Timer4_Init(void) { TIM_MasterConfigTypeDef sMasterConfig = {0}; //01-定时器4时钟 __HAL_RCC_TIM4_CLK_ENABLE(); //02-定时器4 I_htim4.Instance = TIM4; //TIM4¶¨Ê±Æ÷ I_htim4.Init.Prescaler = 35; //Ô¤·ÖƵϵÊý I_htim4.Init.CounterMode = TIM_COUNTERMODE_UP; //ÏòÉϼÆÊý I_htim4.Init.Period = 10000; //ÖØ×°ÔØÖµ 0 -》》 10000 I_htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; //²»·ÖƵ I_htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; //ʹÄÜÖØÐÂ×°ÔØ if (HAL_TIM_Base_Init(&I_htim4) != HAL_OK) while(1); //03-设置中断 HAL_NVIC_SetPriority(TIM4_IRQn, 3, 0); HAL_NVIC_EnableIRQ(TIM4_IRQn); //05-¶¨Ê±Æ÷´¥·¢Ô´ÉèÖà sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; //更新中断 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&I_htim4, &sMasterConfig) != HAL_OK) while(1); //06-清除中断标志位 __HAL_TIM_CLEAR_FLAG(&I_htim4,TIM_FLAG_UPDATE); //07-打开定时器中断 HAL_TIM_Base_Start_IT(&I_htim4); } 四、定时器中断(接收数据部分) DMA的DMA_CNDTR寄存器每当传输一个数据时,这个寄存器会自动减1,即接收一个数据。当串口处于接收状态时,使用定时器中断1ms周期查询DMA的DMA_CNDTR寄存器变化。 假定BUFFMAX为缓冲区长度: ①当BUFFMAX等于DMA_CNDTR寄存器值,没有输入接收(空闲状态) ②当BUFFMAX不等于DMA_CNDTR寄存器值,有新的数据接收(接收状态) ③当DMA_CNDTR寄存器超过20ms没有变化,判定接收数据完成。 #define RECE_FINISH 0 //接收数据完成标志 #define RECE_ING 1 //正在就收数据 #define RECE_ILDE 2 //空闲状态 uint16_t u16rxp; //现在接收数据长度 uint16_t u16timer; //接收计时器 uint8_t u8rxmode; //接收状态 //定时器4(每1毫秒进入一次中断) void TIM4_IRQHandler(void) { //获取定时器更新 if(__HAL_TIM_GET_FLAG(&I_htim4,TIM_FLAG_UPDATE) != RESET) { //清除定时器中断 __HAL_TIM_CLEAR_FLAG(&I_htim4,TIM_FLAG_UPDATE); if(u8rxmode != RECE_FINISH) { if(u16rxp != (BUFFMAX - DMA1_Channel6-》CNDTR)) {//查询接收数据有变化 u16timer =0; u16rxp = (BUFFMAX - DMA1_Channel6-》CNDTR); //当前已经获取的数据长度 } if(u16timer 《 20 ) u16timer ++; //接收数据计数 if(u16timer ==20) {//超过一定时间,DMA没传过任何数据,则认为完成 u16timer =0; u8rxmode = RECE_FINISH; __HAL_DMA_DISABLE(&hdma_usart2_rx); //暂停DMA2 DMA1_Channel6-》CNDTR = BUFFMAX; //写入缓冲区长度 __HAL_DMA_ENABLE(&hdma_usart2_rx); //启动DMA2 } } } } 五、测试 #include 《string.h》 extern uint16_t u16rxp; extern uint8_t u8txbuff; extern uint8_t u8rxbuff; extern uint8_t u8rxmode; int main(void) { uart2_init(115200); //波特率设置 115200 Timer4_Init(); //开启定时器 while(1) { if(u8rxmode==RECE_FINISH) //判定接收完成 { memcpy(u8txbuff,u8rxbuff,u16rxp); //将接收的数据放入发送缓冲区 USART2_TxdSend(u16rxp); //发送数据 u8rxmode=RECE_ILDE;//进入空闲状态 } } } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1614 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1541 浏览 1 评论
970 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
682 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1592 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
644浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
531浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
504浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 01:03 , Processed in 0.772176 second(s), Total 77, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号