老俗话说:不想写好代码的厨子都不是好业务,老俗是谁?老俗是我的又一个外号。本人技校毕业,水平有限,发现问题还请批评指正,还有就是轻点喷。

言归正传,这次写个代码,没办法客户指定了STM32L053的MCU,之前确实没有用过,不过我一直坚信,所有的芯片制造出来,都是给大家使用的,而不是炫耀我们公司生产的多高科技,需要费死洋劲才能使用,这样的厂家肯定也快倒闭了。所以,放平心态,不急不躁,没有啥问题是解决不了的。

说着说着又扯远了,这次的项目使用LPUART连接传感器进行通讯,好在传感器的通讯也是自己写的,通讯协议采用定长数据,10个byte,本想着直接用DMA的定长接收,无奈现场总线上有N多传感器,这要是串了数据,是一个错,个个错,就永远回不了头了。然后就想着用DMA做个不固定长度的接收。多年前用STM32F1系列做过,想着也不算太难,无奈找了找资料,STM32L053已经不支持标准库了。(此时内心略有波动,我不想告诉你们其实是万马奔腾),然后看了看资料,下载cubeMX,网上看了看,大部分是HAL库,然后用HAL库试了试串口中断,效果不是很理想。看了看代码,还是决定转战LL库。

首先要感谢songrsp先生的《基于STM32CubeMX的LL库学习记录(五)USART_DMA_IDLE 串口接收空闲中断接收数据》

文章链接:https://www.pianshen.com/source/36308857364027/

其实DMA接受不定长数据大部分思路就是使用DMA+串口空闲中断,因为我确实水平有限,也想不出什么更好的办法。至于DMA是个啥?手册里是这样说的:

这就好比你买了个房子要装修,DMA就像是那个上料的师傅(不知道DMA有思维的话这么说他会不会痛扁我),你去买了水泥,沙子,油漆啥的,我只告诉师傅从哪里搬到哪里就可以了,至于师傅是扛着,推着,还是背着,就和你没太大关系了,你只是在需要的时候去看下东西的状况就可以了,看看到没到,到了多少。当然,如果你买了很多种类,有些是急需的,你就得告诉师傅先上哪部分料,那么怎么告诉我们的“师傅”呢?

写的有点啰嗦了,开整:

首先是打开cubeMX配置

个人习惯,先把调试勾选上,不然忘了这一步下次程序就下不进去了,需要重新搞BOOT脚。

配置时钟,使用外部时钟

配置时钟树,因为本次是低功耗,所以直接用了外部8M晶振做主时钟,PLL是个用电大户。

配置串口:异步模式,波特率9600,数据位8位,无校验,1位停止位,由于默认GPIO接到了别的功能上,这里复用了PC10和PC11。

配置DMA,点Add添加,LPUART1_RX的接收在DMA的通道3上,这个查手册可以知道,Direction是设置传输方向的,本次是从外设搬运到内存,Prionty是设置LPUART1_RX在DMA中的优先级。Mode 是指单次传输还是循环传输。

使能串口中断,因为要用到串口的空闲中断,DMA的中断是自动生成的。

管理下中断优先级,毕竟是测试,我这也懒得动了

选择下LL库,之后生产代码。

系统生成的代码如下,在dma.文件中,初始化代码MX_DMA_Init干了三件事,使能DMA时钟,设置DMA中断优先级,使能DMA中断。

void MX_DMA_Init(void)
{/* Init with LL driver *//* DMA controller clock enable */LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);/* DMA interrupt init *//* DMA1_Channel2_3_IRQn interrupt configuration */NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);}

大部分的代码在串口初始化中,在usart.c文件中

void MX_LPUART1_UART_Init(void)
{LL_LPUART_InitTypeDef LPUART_InitStruct = {0};//LL_GPIO_InitTypeDef GPIO_InitStruct = {0};///* 使能外设时钟 */LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPUART1);//LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);///**LPUART1 GPIO ConfigurationPC10   ------> LPUART1_TXPC11   ------> LPUART1_RX*///配置管脚模式GPIO_InitStruct.Pin = LL_GPIO_PIN_10;GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;GPIO_InitStruct.Alternate = LL_GPIO_AF_0;LL_GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_InitStruct.Pin = LL_GPIO_PIN_11;GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;GPIO_InitStruct.Alternate = LL_GPIO_AF_0;LL_GPIO_Init(GPIOC, &GPIO_InitStruct);/* LPUART1 DMA Init *//* LPUART1_RX Init */LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMA_REQUEST_5);//使用参数为通道x上的DMA实例配置DMA请求LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);//传输方向设置数据传输方向(从外设或内存读取)。LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_MEDIUM);//设置DMA的“通道优先级”LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL);//设置工作模式,单次//LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_CIRCULAR);//  循环 LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);//设置外设增量模式外设非自增模式LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);//设置内存自增模式LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);//设置外设尺寸LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE);//设置内存尺寸/* LPUART1 interrupt Init */NVIC_SetPriority(RNG_LPUART1_IRQn, 0);//设置中断优先级NVIC_EnableIRQ(RNG_LPUART1_IRQn);//使能中断/* USER CODE BEGIN LPUART1_Init 1 *//* USER CODE END LPUART1_Init 1 */LPUART_InitStruct.BaudRate = 9600;LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
}

至于为什么是LL_DMA_CHANNEL_3和 LL_DMA_REQUEST_5,看这里

这个自生成的代码有个坑,不知道小伙伴们发现了没?在初始化了串口之后,并没有使能串口,对于LPUART而言,这里需要增加2行代码:

    LL_LPUART_Enable(LPUART1);//使能LPUART1LL_LPUART_EnableIT_IDLE(LPUART1);//使能串口空闲中断

同样的,如果想使用接收中断或者其他中断,只需要使能相应中断即可。

接下来就是增加DMA的代码,在生成的代码中,并未详细配置DMA,官方给的步骤是这样:

在添加代码前,需要在内存中给DMA的搬运开辟一块内存空间,创建个数组uint8_t Lpuart_buf[10];

在头文件里定义下DMA接收的数据长度: #define LPRXLEN    10;

接下来添加DMA代码:

    LL_DMA_SetMemoryAddress(DMA1,LL_DMA_CHANNEL_3,(uint32_t)Lpuart_buf);//接收目标地址LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_3,LPRXLEN);//设置DMA接受数据长度LL_DMA_SetPeriphAddress(DMA1,LL_DMA_CHANNEL_3,(uint32_t)&(LPUART1->RDR));//设置外设地址,从哪里搬运数据LL_DMA_EnableIT_TC(DMA1,LL_DMA_CHANNEL_3);//使能DMA完成中断LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_3);//使能通道LL_LPUART_EnableDMAReq_RX(LPUART1);//使能串口DMA接收

到这里,基本的配置就完成了,然后我们打开stm32l0xx_it.c文件,在这里面找到中断函数

void DMA1_Channel2_3_IRQHandler(void)//DMA中断函数

void RNG_LPUART1_IRQHandler(void)//LPUART中断函数

我再DMA完成中断里至做了1件事,就是串口发出0x99,标识已经进入了DMA完成中断,这里留作以后备用,把DMA的搬运数量加长,一旦进入此中断,就标识协议错误了。

void DMA1_Channel2_3_IRQHandler(void)
{/* USER CODE BEGIN DMA1_Channel2_3_IRQn 0 *///判断是否是通道3的中断并且使能了该中断if(LL_DMA_IsActiveFlag_TC3(DMA1)&&LL_DMA_IsEnabledChannel(DMA1,LL_DMA_CHANNEL_3)){LL_LPUART_TransmitData8(LPUART1,0x99);}LL_DMA_ClearFlag_TC3(DMA1);//清除中断标准/* USER CODE END DMA1_Channel2_3_IRQn 0 *//* USER CODE BEGIN DMA1_Channel2_3_IRQn 1 *//* USER CODE END DMA1_Channel2_3_IRQn 1 */
}

在串口空闲中断中,首先要关闭DMA,这样防止在处理的时候有新的数据传进来,还有就是为了重新配置DMA。

void RNG_LPUART1_IRQHandler(void)
{/* USER CODE BEGIN RNG_LPUART1_IRQn 0 */uint8_t i,count;if(LL_LPUART_IsActiveFlag_IDLE(LPUART1))//如果是串口空闲中断{LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_3);//关闭DMA//接收到的数据数量count = LPRXLEN-(LL_DMA_GetDataLength(DMA1,LL_DMA_CHANNEL_3));LL_LPUART_TransmitData8(LPUART1,count);//把数组传输出来for(i=0;i<10;i++){LL_LPUART_TransmitData8(LPUART1,Lpuart_buf[i]);LL_mDelay(10);}//重新配置DMA   LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_3,LPRXLEN);LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_3);}LL_LPUART_ClearFlag_IDLE(LPUART1);//清除中断标志位/* USER CODE END RNG_LPUART1_IRQn 0 *//* USER CODE BEGIN RNG_LPUART1_IRQn 1 *//* USER CODE END RNG_LPUART1_IRQn 1 */
}

验证一下,这里为了方便,我直接用ASCII字符发送了10个字符串,选择了ASCII发送,HEX接收。这里输入的0-9相当于HEX的0x30-0x39

0x99说明进入了 DMA完成中断,0A标识接收了10个字节,30-30就是数组的内容了。

这次我们换几个数字发一下,发送了3个0x36

03说明接收到了3个byte,因为没有对数组进行清除,所以数组后边的数据没变化,所以从数组第四个数到最后一个没有变化。所以测试数据小于数组时,记得用不同的数据,不然会因为发送前几位数据一致,导致误判断。这里stm32没有返回0x99,说明没有进入到DMA完成中断。如果发出的数据超过10个byte呢?我们来看下,这个就比较有趣了,既然说有趣,就是根据配置的不一样,会产生不一样的结果。

现在的配置是DMA单次模式

0x99说明进入了DMA完成中断,0A表示接收到了10个byte,后面就是数组内的数据了,我们把DMA配置为循环模式再试一次:

LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_CIRCULAR);

可以看到,数组前面的数据已经被覆盖掉了。

以上就是我个人调试LPUART的过程,写的不对的地方,还请批评指正。

随记:STM32L053LL库LPUART串口DMA接收数据相关推荐

  1. STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)

    目录 前言 一.工程配置 二.串口DMA部分代码 1.源文件UART_DMA.c 2.头文件UART_DMA.h 3.stm32f1xx_it.c的修改 4.串口收发DMA测试 三.字符串数字提取代码 ...

  2. 第九章 AT32F403A基于V2库串口 dma接收不定长数据

    目录 概述 硬件 DMA 软件 流程 初始化 初始化代码: 中断服务函数: DMA1通道5设置函数:(重新使能通道) DMA1通道4发送函数:(设置dma长度和内存地址) 测试 最后 概述 本文主要是 ...

  3. STM32 HAL库 串口DMA接收不定长数据

    STM32 HAL库 串口DMA接收不定长数据 整体思路:我是用的CUBEMX软件生成的工程,使能了两个串口,串口2用来接收不定长的数据,串口1用来发送串口2接收到的数据:串口2我找了一个UBLOX卫 ...

  4. STM32 USART串口DMA 接收和发送的源码详解!

    硬件平台:STM32F103ZET6: 开发环境:KEIL 4: 先说说应用通讯模式,串口终端的工作方式和迪文屏差不多,终端被动接受MCU发的指令,终端会偶尔主动发送一些数据给MCU(像迪文屏的触摸信 ...

  5. android 串口一直打开_STM32之串口DMA接收不定长数据

    STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...

  6. rtthread 串口dma接收_RT-Thread 设备驱动UART浅析

    OS版本:RT-Thread 4.0.0 芯片:STM32F407 RT-Thread的串口驱动框架与Linux相识,分成 I/O设备框架 + 设备底层驱动: 1. serial设备初始化及使用 将配 ...

  7. STM32F0系列串口DMA收发数据

    关于STM32F0系列串口DMA收发数据详解 这里用的库函数版本,芯片型号为stm32f030c8t6.在用到串口DMA时,要按以下几个步骤进行. 1.确定使用的串口号,这里,我用的是usart2,对 ...

  8. STM32输入捕获模式设置并用DMA接收数据

    参考: STM32的PWM输入模式设置并用DMA接收数据 Input capture mode The input stage samples the corresponding TIx input ...

  9. STM32串口实时接收数据与所提前定义的比较,并作出相应的操作

    STM32串口实时接收数据与所提前定义的比较,并作出相应的操作 //typedef const uint16_t uc16; /*!< Read Only */uc16 BUF[10]={162 ...

  10. c#串口程序接收数据并打印_C#程序可打印各种数据类型的大小

    c#串口程序接收数据并打印 In this C# program – we are going to print size of various data types, to print size o ...

最新文章

  1. C语言中regex_error,为什么这个C 11 std :: regex示例抛出一个regex_error异常?
  2. win10下编译和使用mnn 2021
  3. deap实战_2017中国数学建模大赛_B题_第二题
  4. Azkaban的Web Server源码探究系列20:resolvebuildFlow
  5. JSP PO VO BO DTO POJO DAO解释
  6. Linux下新手基本操作及技巧看图上路 (7)
  7. textarea中插入标签_HTMLCSS学习笔记(二)-- HTML表单标签
  8. mysql多语言运营设计_多语言系统的数据库设计
  9. function 自定义函数、函数调用
  10. Eclipse Debug功能的使用教程
  11. CAD中如何插入图框?CAD插入图框方法教程
  12. Unable to execute diff program: WinDiff 【SourceOffSite】
  13. 临时邮箱有什么用,推荐5个临时邮箱
  14. 在超市使用室内地图的5个好处
  15. 大学计算机专业学习哪些课程?
  16. 开封文化艺术职业学院计算机甲骨文,甲骨文软件学院致19级全体同学的一封信 暨2021年寒假作业安排...
  17. Win10下双系统Ubuntu14.04+GTX1070+CUDAcuDNN+Tensorflow环境搭建
  18. win10如何停止自动更新
  19. 拿下网站服务器,实战拿下某技校网站与服务器
  20. NBUT1457 Sona 莫队算法

热门文章

  1. Excel做数据分析?是真的很强
  2. 计算机音乐按键有声音,计算器上的声音键是哪个键
  3. 计算机化学博士点,2017年新增博士硕士授予单位公布!各高校博士、硕士学位授权点建设的总体情况如何?...
  4. DragonFly BSD 4.2发布
  5. 普歌-云言团队-SSM系列与Spring框架入门
  6. 详解内存SDRAM原理(P-Bank、L-Bank、刷新、预充电等)
  7. Halcon 错误 提示 2021 System clock has been set back 解决方法
  8. 网路连接配置和DNS服务?解决无线网卡连接WIFI问题(硬件和驱动没问题)?
  9. vue 3 的devtools beta 版离线下载
  10. 使用HTML语言和CSS开发商业站点_利用CSS3制作网页动画