STM32F0使用LL库实现DMA方式AD采集

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。在本文中我们将介绍基于LL库的ADC的DMA采集方式。

1、概述

这次我们使用DMA方式实现对AD的采集,在遗忘我们使用HAL库和标准库都做过,这次我们使用LL库来实现。接下来我们简单了解一下STM32F030F4中的ADC和DMA。

首先看一看ADC,STM32F030F4是12位的ADC。它有多达19个多路复用通道,允许它测量来自16个外部和2个内部源的信号。各种通道的A/D转换可采用单通道、连续通道、扫描通道或不连续通道进行。ADC的结果存储在左对齐或右对齐的16位数据寄存器中。ADC结构图如下:

这次我们只使用第1路外部输入。接下来说一说DMA,直接内存访问(DMA)用于在外设和内存以及内存到内存之间提供高速数据传输。DMA可以在没有任何CPU操作的情况下快速移动数据。这使CPU资源可以用于其他操作。STM32F030F4中的DMA控制器有5个通道,每个通道用于管理来自一个或多个外围设备的内存访问请求。它有一个仲裁器来处理DMA请求之间的优先级。DMA结构图如下:

这次我们也使用DMA的第1通道。

2ADC配置

在使用之前我们需要对ADC和DMA的相关寄存器惊醒必要的配置,才能实现我们想要的功能。我们来看看ADC需要配置的寄存器。ADC需要注意的寄存器主要有两个:ADC控制寄存器(ADC_CR)和ADC配置寄存器1(ADC_CFGR1)。首先我们来说说ADC控制寄存器(ADC_CR),器结构如下:

关于ADC控制寄存器(ADC_CR),有几个设置需要说明一下。

ADCAL:ADC校准,设置该位可以软件启动校准,校准完成硬件会复位掉这一位。需要注意的是只有ADC处于失能状态,软件对ADCAL的操作才是有效的。也就是说软件对ADCAL操作时,ADC控制寄存器(ADC_CR)必须是全复位状态,即ADCAL=0,ADSTART=0,ADSTP=0, ADDIS=0和 ADEN=0。

ADSTART: ADC启动转换命令。需要注意只有在ADC已启用,并且没有禁用ADC的挂起请求。也就是说ADEN=1和ADDIS=0时,软件对ADSTART的操作才有效。

ADEN: ADC使能命令。只有在ADC控制寄存器(ADC_CR)处于全复位状态,即ADCAL=0,ADSTART=0,ADSTP=0,ADDIS=0 和 ADEN=0下,软件对ADEN的操作才有效。这就有一个问题,如果你使用了ADCAL必须等校准完成,才能使能,否则无效。

接下来我们看一看ADC配置寄存器1(ADC_CFGR1),其结构如下:

关于ADC配置寄存器1(ADC_CFGR1),我们需要关注:CONT(转换模式)、EXTEN[1:0](外部触发使能)、DMACFG(DMA访问配置)、DMAEN(DMA访问使能)。需要说明的是,这几个配置都必须在启动转换前完成配置,即配置时ADSTART=0。

3DMA配置

配置了ADC还需要配置DMA才能实现我们的想法。关于DMA的配置我们主要说一下4个寄存器:DMA通道配置寄存器(DMA_CCRx)、DMA通道数据数量寄存器(DMA_CNDTRx)、DMA通道外设地址寄存器(DMA_CPARx)、DMA通道内存地址寄存器(DMA_CMARx)。

首先,我们来看看DMA通道配置寄存器(DMA_CCRx),其结构如下:

对于DMA通道配置寄存器(DMA_CCRx),我们需要关注如下位:MSIZE[1:0](内存大小)、PSIZE[1:0] (外设大小)、MINC(内存的增加模式)、PINC(外设增加模式)、CIRC(循环模式)、DIR(数据传输方向)、EN(通道使能)。除通道使能外,其它均可通过初始化函数进行配置。

接下来,我们来看看DMA通道数据数量寄存器(DMA_CNDTRx),其结构如下:

其实DMA通道数据数量寄存器(DMA_CNDTRx)用于配置传送数据的个数,如果是往内存中写,就是内存缓冲区的大小,单位与配置寄存器中MSIZE和PSIZE有关。接下来,我们来看一看DMA通道外设地址寄存器(DMA_CPARx),其结构如下:

对于DMA通道外设地址寄存器(DMA_CPARx),就是存储外设的地址,如果我们的外设是ADC,那就是ADC的地址。最后,我们来看一看DMA通道内存地址寄存器(DMA_CMARx),其结构如下:

对于DMA通道内存地址寄存器(DMA_CMARx),其存储的就是对应的变量在内存中的地址,就是我们开辟的数据缓存区的首地址。

4、软件实现

我们已经说明了ADC和DMA的配置,在这一小节,我们将根据我们前面的分析实现代码。首先来实现ADC的配置代码。

/* ADC 初始化配置 */
static void ADC_Init_Configuration(void)
{LL_ADC_InitTypeDef ADC_InitStruct = {0};LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};LL_GPIO_InitTypeDef GPIO_InitStruct = {0};/* ADC相关外设时钟使能 */LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_ADC1);LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);/**ADC GPIO 配置:PA0   ------> ADC_IN0  */GPIO_InitStruct.Pin = LL_GPIO_PIN_0;GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;LL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* ADC DMA初始化 */LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_WORD);LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_WORD);LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_1,ADBufferSize);LL_DMA_SetPeriphAddress(DMA1,LL_DMA_CHANNEL_1,LL_ADC_DMA_GetRegAddr(ADC1,LL_ADC_DMA_REG_REGULAR_DATA));LL_DMA_SetMemoryAddress(DMA1,LL_DMA_CHANNEL_1,(uint32_t)ADC_ConvertedValue);LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_1);/* 配置ADC通道 */LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_0);/* 配置ADC的全局特性:时钟、分辨率、数据对齐和转换次数 */ADC_InitStruct.Clock = LL_ADC_CLOCK_ASYNC;ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;LL_ADC_Init(ADC1, &ADC_InitStruct);ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS;ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_PRESERVED;LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD);LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_239CYCLES_5);LL_ADC_DisableIT_EOC(ADC1);LL_ADC_DisableIT_EOS(ADC1);LL_ADC_StartCalibration(ADC1);while( LL_ADC_IsCalibrationOnGoing(ADC1));LL_ADC_Enable(ADC1);LL_ADC_REG_SetDMATransfer(ADC1,LL_ADC_REG_DMA_TRANSFER_UNLIMITED);LL_ADC_REG_StartConversion(ADC1);
}

其实在ADC的初始化配置中也对DMA作了配置,但DMA还需要对始终和中断进行配置。

/**  DMA 控制器初始化配置  */
static void DMA_Init_Configuration(void)
{/* DMA 控制器时钟使能 */LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);/* DMA1_Channel1_IRQn中断配置 */NVIC_SetPriority(DMA1_Channel1_IRQn, 0);NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

配置后,ADC的寄存器如下:

配置后,DMA的寄存器如下:

其实,到这里ADC采集世纪上已经实现了,DMA已经将数据从ADC读出来存到了指定的内存区域,后续的处理就很简单了。

5、总结

我们已经实现了基于LL库使用DMA方式获取ADC的数据。下面我们就下载到目标设备并检测一下结果。测试结果如下:

       上图中,上部是计算完成的物理量值,下部则是DMA写到内存缓存区的ADC的原始码值。

欢迎关注:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32与宇电设备实现AI-BUS通讯

宇电的设备使用基于RS-485的自定义协议,协议本身比较简单,只有2条指令: 读:地址代号52H(82) 要读的参数代号00校验码 写:地址代号43H(67)要写的参数代号写入数低字节写…

FreeRTOS如何结束和重新启动调度程序

大多数主机或桌面系统(比如Linux,Mac或Windows)都有一个正常的用例,你可以在早上启动操作系统,然后在晚上关闭它,然后你就离开机器。嵌入式系统是不同的:他们没有参加,他们应该“永远…

先进过程控制之一:浅说APC

先进过程控制(APC)技术作为在生产装置级的信息化应用,在优化装置的控制水平和提高生产过程的管理水平的同时,还为企业创造了可观的经济效益。 1、什么是APC 先进过程控制,简称APC,并不是什么新概念。它仅…

STM32与多台MS5803压力传感器I2C通讯

MS5803压力传感器支持SPI和I2C总线通讯,拥有24位AD转换。能够同时获得压力值和温度值,其中压力测量范围为10-1100mbar,温度的测量范围是-40-85摄氏度。各引脚功能及参数如下: 传感器内部结构图如下: 通讯协议的选择通过…

STM32F0使用LL库实现SHT70通讯

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。本篇我们将基于LL库采用模拟I2C接口的方式实现温湿度采集。 1、SHT70简述 SHT70是一款集温湿度…

STM32F0使用LL库实现PWM输出

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。本文我们将说明如何通过LL库实现PWM信号的输出。 1、概述 我们知道STM32的TIM计时器可以输出P…

STM32F0使用LL库实现Modbus通讯

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。本篇将说明基于LL实现USART通讯。 1、概述 我们想要实现基于RS485的Modbus通讯实际就是基于US…

STM32基于SPI和AD7192的数据采集

在开发臭氧发生器的时,我们需要一个高分辨率的AD采集,于是选择了AD7192,选择这款ADC的原因比较简单。首先它是24位的符合我们的精度要求;其次它自带时钟,便于节省空间;第三它有4路单端或2路差分输入&#x…

Modbus协议栈实现Modbus RTU多主站支持

前面我们已经详细讲解过Modbus协议栈的开发过程,并且利用协议栈封装了Modbus RTU主站和从站,Modbus TCP服务器与客户端,Modbus ASCII主站与从站应用。但在使用过程中,我们发现一些使用不便和受限的地方,所以我们就想要…

STM32基于AD5663的UV灯电压控制

在开发臭氧发生器的时,我们使用UV灯来实现臭氧的产生。而UV灯的强度决定了臭氧产生的浓度,UV灯的光强则与其控制电压密切相关。所以我们要控制产生的臭氧的浓度就需要调节其控制电压。我们选择了AD5663这一模拟量输出模块来实现这一点。 1、AD5663简介 …

实现Modbus ASCII多主站应用

前面我们已经分析了Modbus RTU的更新设计和具体实现(如果不清楚可查看前一篇文章)。其实Modbus ASCII与Modbus RTU都是基于串行链路实现的,所以有很多的共同点,基于此,这篇文章我们只讨论与Modbus RTU所不同的部分。 …

STM32一种基于NTC的控温电路及软件实现

NTC(Negative Temperature Coefficient)是一种随温度上升时,电阻值呈指数关系减小的热敏电阻。应用广泛,最近我们就采用了NTC来控制加热并测温,并达到了预期的效果。 1、硬件设计 我们使用三极管作为加热元件&#x…

STM32利用光敏二极管实现光度测量

最近我们在开发臭氧发生器时,需要监测生成的臭氧的浓度,于是想到使用光度计来测量。因为不同浓度的臭氧对管的吸收作用是不相同的,于是检测光照强度的变化就可以得到相应的浓度数据。 1、硬件设计 此次光照度检测我们选用了S1336-5BQ光电点二…

STM32的ADC通道间干扰的问题

最近我们在开发一个项目时,用到了MCU自带的ADC,在调试过程中发现通道之间村在相互干扰的问题。以前其实也用过好几次,但要求都不高所以没有太关注,此次因为物理量的量程较大,所以看到了变化。 首先来说明一下此次的软…

实现Modbus TCP多网段客户端应用

对于Modbus TCP来说与Modbus RTU和Modbus ASCII有比较大的区别,因为它是运行于以太网链路之上,是运行于TCP/IP协议之上的一种应用层协议。在协议栈的前两个版本中,Modbus TCP作为客户端时也存在一些局限性。我们将对这些不足作一定更新。 1、…

改进初学者的PID-介绍

最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助。作者Brett Beauregard的原文网址:http://brettb…

改进初学者的PID-采样时间

最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助。作者Brett Beauregard的原文网址:http://brettb…

改进初学者的PID-微分冲击

最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助。作者Brett Beauregard的原文网址:http://brettb…

LwIP应用开发笔记之一:LwIP无操作系统基本移植

现在,TCP/IP协议的应用无处不在。随着物联网的火爆,嵌入式领域使用TCP/IP协议进行通讯也越来越广泛。在我们的相关产品中,也都有应用,所以我们结合应用实际对相关应用作相应的总结。 1、技术准备 我们采用的开发平台是STM32F407…

改进初学者的PID-修改整定参数

最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助。作者Brett Beauregard的原文网址:http://brettb…