在本次项目中,限于空间要求我们选用了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的原始码值。

欢迎关注:

STM32F0使用LL库实现DMA方式AD采集相关推荐

  1. 利用电表SOC芯片RN7211低成本实现三路高精度24位AD采集(带PGA功能)

    RN7211为一款ARM Cortex-M0内核的电表SOC芯片,集成有3通道用于测量计量的高精度差分输入Σ-ΔADC,更新速率7.2KHz,具有128KByte FLASH.16KByte SRAM ...

  2. 基于STM32F103C8T6四路AD采集数据显示在oled屏上非DMA传输方式(附百度网盘下载链接)

    本文采用四路AD采集光照强度.烟雾浓度.一氧化碳.空气质量等四个物理量,并采用中位值平均滤波(防脉冲干扰平均滤波法)算法对偶然出现的脉冲性干扰,消除由其引起的采样值偏差. ADC简介 STM32F10 ...

  3. STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

    STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号 一. 问题描述 二. 方法一--使用HAL_UART_Receive_DMA 三. 方法二--使用HAL_UARTEx_Rece ...

  4. 基于uFUN开发板的心率计(一)DMA方式获取传感器数据

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

  5. STM32之ADC实例(基于DMA方式)

    出处:https://blog.csdn.net/zouleideboke/article/details/75112224?locationNum=9&fps=1 ADC简介: ADC(An ...

  6. 嵌入式作业STM32采用串口DMA方式发送数据

    目录 前言 要求 一.DMA的基本介绍 DMA的基本定义 DMA的主要特征 STM32F411x系列芯片DMA控制器 二.通过CubeMX配置项目 1.创建项目 2.选择芯片STM32F103C8T6 ...

  7. USART向串口助手发送数据(DMA方式)

    目录 一.前言 二.什么是DMA? 1.DMA简介 2.DMA主要特性 3.DMA框图 三.STM32CubeMX工程创建 1.题目要求 2.CubeMX工程配置 四.Keil工程的创建 五.效果展示 ...

  8. STM32CubeIDE STM32H743实现软件触发ADC实现多通道DMA方式、过采样方式采集数据

    在STM32CubeIDE 1.4.0环境下测试通过. 1. ADC设置 设置CPU频率:400MHz ADC输入时钟自动设置为:150MHz 2. DMA设置 3. NVIC设置 4. 代码 //d ...

  9. gazebo 直接获取传感器数据_基于uFUN开发板的心率计(一)DMA方式获取传感器数据...

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

最新文章

  1. Android 自定义Dialog 的使用
  2. 从复现人类智能到挑战AI大工程,智能计算正经历什么考验?
  3. 研究javascript中的this
  4. 2018-08-19-Python全栈开发day43-正反选练习
  5. 神策数据杨宁:券商财富管理数字化转型客户全生命周期解读
  6. C. Barcode dp
  7. Spring Boot 细节挖掘(全局异常处理)
  8. 应用ajax实现检测注册用户名是否已经存在
  9. Android 10正式版发布,支持5G和折叠屏设备
  10. 你最关心的马蜂窝事件舆论全景图在这里,用文本挖掘一挖到底
  11. 变分法,欧拉-拉格朗日方程推导
  12. 深海迷航代码_《深海迷航(Subnautica)》入坑指南
  13. 网络上的罗刚·心灵之约
  14. 13.2.3 VAM服务器的配置步骤及示例(2)
  15. 视觉SLAM十四讲——第四讲李群与李代数
  16. ye graph editor安装
  17. Android 9 导航栏添加截图按钮
  18. Orange pi GPIO输出控制,裸机点灯大法(二)!
  19. RxJava操作符学习APP
  20. Text-to-Table: A New Way of Information Extraction

热门文章

  1. 《程序员面试宝典》笔记一
  2. 论文学习16-Going out on a limb: without Dependency Trees(联合实体关系抽取2017)
  3. Python之旅Day8 socket网络编程
  4. js实际工作中的技能点
  5. [Mac]Python 安装MySQLdb模块
  6. 什么是社会性网络?什么是六度分隔理论?
  7. linux标准I/O——流的打开和关闭
  8. 计算机网络——链路层之PPP协议和HDLC协议
  9. VMware下主机与虚拟机剪切板独立,无法直接复制粘贴
  10. Leetcode--826. 安排工作以达到最大收益