当前位置: 首页 > news >正文

STM32ADC

ADC简介:有打moba游戏的别搞混了,这不是射手adc。在32中,ADC的全称为:Analog-to-Digital Converter,指模拟/数字转换器 也就是模拟-数字电路的转换器。其实通俗的来讲,它就是一个电压表

目录

一.ADC原理

1.ADC框图

2.通道

3.规则组/注入组&转换顺序

4.转换触发

5.周期 

6.转换模式

7.校验 

二.配置

1.ADC基本配置流程(代码)

2.进阶用法:ADC与DMA结合使用

好了,祝你看完就会。


一.ADC原理

1.ADC框图

重点看红框内部的结构就行了,通过接触GPIO来将外接设备的模拟电压进行转换,变为数字逻辑电平,这就是它的主要功能。

2.通道

再经典不过的东西,TIM,DMA都有的东西。功能也和它们差不多,这里对应的自然就是GPIO。不同的通道固定只能监听固定的GPIO口。

这里唯一不同的点就是不同的ADC(比如ADC1和ADC2)同时监听PA0口并不会像其他外设一样出问题,完全可以同时监听(但本质上是间歇性监听,就是不断轮番监听)。另外一般在应用场景中ADC是要同时监听多个通道的

3.规则组/注入组&转换顺序

规则组:如果把ADC看作一个测量电压的机器,那么规则组就是他的流水线。其上面按Rank摆放着一组通道(GPIO口),ADC就依次测量并且把数据放在寄存器中

注入组:很简单,注入组看作中断执行的流水线即可。(假设正在执行规则组此时进来一组注入组则优先执行完注入组再回过头执行规则组。

转换顺序:由三个寄存器控制,看一眼图就行。

4.转换触发

大体来说其实就是硬件自动触发还是软件控制触发:

可以看到要么就是各种定时器或者外部中断触发要么就是软件触发。一般还是软件触发用的比较多也比较方便。

5.周期 

首先ADC的是中原是PCLK,他的时钟源不能超过14Mhz。再应用中,通过选择采集时间来控制对应的周期:

采样时间随箭头增长。

6.转换模式

主要分为三种:
扫描模式:只循环测量规则组的第一个(不论组里配置了几个通道)
循环模式:测量完后继续从头重复测量
间歇模式:不用。

另外,当哪一种模式都不配置的时候,它就变为普通模式,就是你触发它一次它测量一次:

7.校验 

这个看起来可有可无的东西其实再最终结果中产生的影响还是蛮大的:
1.一定要校验,否则电压测量会有约0.05的误差
2.必须再初始化后进行,不然数据特别不正常。(别问问就是作者吃过瘪了)

二.配置

1.ADC基本配置流程(代码)

ADC的流程和其他基本外设配置的流程也极度相似:

ADC基本初始化(数据对齐;搬运模式;转换触发)HAL_ADC_Init()        
                                |
通道配置(通道选择;测量时间;规则通道Rank选择)HAL_ADC_ConfigChannel()
                                |
               校验 HAL_ADCEx_Calibration_Start()
                                |
经典Msp配置(对应GPIO口配置;时钟使能;分频数选择;对应IO口时钟来源)HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
                                |
                  软件触发  HAL_ADC_Start(&adc_init);
                                |
     等待测量结束返回对应值  HAL_ADC_PollForConversion(&adc_init,10);

#include "adc.h"ADC_HandleTypeDef adc_init = {0};
ADC_ChannelConfTypeDef adc_channel_init = {0};
void ADC_INIT(){//ADC基本模式配置adc_init.Instance = ADC1;adc_init.Init.ContinuousConvMode = DISABLE;				//连续转变模式配置adc_init.Init.DiscontinuousConvMode = DISABLE;			//间断转变模式配置adc_init.Init.DataAlign = ADC_DATAALIGN_RIGHT;			//测得数据对齐模式adc_init.Init.ExternalTrigConv = ADC_SOFTWARE_START;	//触发模式adc_init.Init.NbrOfConversion = 1;						//转变数量adc_init.Init.NbrOfDiscConversion = 0;					//间断模式转变量adc_init.Init.ScanConvMode = DISABLE;					//扫描模式//ADC通道配置adc_channel_init.Channel = ADC_CHANNEL_1;					//通道选择adc_channel_init.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;	//测量时间adc_channel_init.Rank = ADC_REGULAR_RANK_1;					//规则通道位置配置HAL_ADC_Init(&adc_init);HAL_ADC_ConfigChannel(&adc_init,&adc_channel_init);HAL_ADCEx_Calibration_Start(&adc_init);						//校验,否则电压测量会有约}void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc){if(hadc->Instance == ADC1){__HAL_RCC_ADC1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();GPIO_InitTypeDef gpio_init;gpio_init.Mode = GPIO_MODE_ANALOG;gpio_init.Pin = GPIO_PIN_1;//ADC时钟配置RCC_PeriphCLKInitTypeDef PeriphClkInit;PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;		//ADC时钟分频PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;		//外设时钟选择HAL_GPIO_Init(GPIOA,&gpio_init);HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);	}
}uint32_t value_get(){HAL_ADC_Start(&adc_init);					//软件启动转换HAL_ADC_PollForConversion(&adc_init,10);	//等待转换结束return (uint16_t)HAL_ADC_GetValue(&adc_init);			//返回}

2.进阶用法:ADC与DMA结合使用

 流程和上面基本一样,只是有以下区别:
1.转换模式调为循环模式                              adc_init.Init.ContinuousConvMode = ENABLE;
2.通道初始化需要同时初始化多个                adc_channel_init.Channel = ADC_CHANNEL_2;
3.ADC初始化转化数量需要更改                           adc_init.Init.NbrOfConversion = 2 
4.Msp引脚初始化需要增加                      gpio_init.Pin = GPIO_PIN_1|GPIO_PIN_2;
5.数据对齐方式需要更改               adc_init.Init.ExternalTrigConv = ADC_SOFTWARE_START;

ADC部分:

#include "adc.h"ADC_HandleTypeDef adc_init = {0};
ADC_ChannelConfTypeDef adc_channel_init = {0};
void ADC_INIT(){//ADC基本模式配置adc_init.Instance = ADC1;adc_init.Init.ContinuousConvMode = ENABLE;				//连续转变模式配置adc_init.Init.DiscontinuousConvMode = DISABLE;			//间断转变模式配置adc_init.Init.ScanConvMode = DISABLE;					//扫描模式配置adc_init.Init.DataAlign = ADC_DATAALIGN_RIGHT;			//测得数据对齐模式adc_init.Init.ExternalTrigConv = ADC_SOFTWARE_START;	//触发模式adc_init.Init.NbrOfConversion = 2;					//转变数量adc_init.Init.NbrOfDiscConversion = 0;					//间断模式转变量//扫描模式//ADC通道配置adc_channel_init.Channel = ADC_CHANNEL_1;					//通道选择adc_channel_init.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;	//测量时间adc_channel_init.Rank = ADC_REGULAR_RANK_1;					//规则通道位置配置HAL_ADC_Init(&adc_init);	HAL_ADC_ConfigChannel(&adc_init,&adc_channel_init);adc_channel_init.Channel = ADC_CHANNEL_2;					//通道选择adc_channel_init.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;	//测量时间adc_channel_init.Rank = ADC_REGULAR_RANK_2;					//规则通道位置配置HAL_ADC_ConfigChannel(&adc_init,&adc_channel_init);HAL_ADCEx_Calibration_Start(&adc_init);					//校验,否则电压测量会有约0.05的误差//注意: }void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc){if(hadc->Instance == ADC1){__HAL_RCC_ADC1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();GPIO_InitTypeDef gpio_init;gpio_init.Mode = GPIO_MODE_ANALOG;gpio_init.Pin = GPIO_PIN_1|GPIO_PIN_2;//ADC时钟配置RCC_PeriphCLKInitTypeDef PeriphClkInit;PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;		//ADC时钟分频PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;		//外设时钟选择HAL_GPIO_Init(GPIOA,&gpio_init);HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);}
}

DMA的配置则并不难,不多赘述,上代码:

#include "dma.h"DMA_HandleTypeDef dma_init = {0};
extern ADC_HandleTypeDef adc_init;
void DMA_INIT(){__HAL_RCC_DMA1_CLK_ENABLE();	dma_init.Instance = DMA1_Channel1;	dma_init.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;		//千万千万千万注意,M和P千万千万不能对应错啊	dma_init.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;	dma_init.Init.MemInc = DMA_MINC_ENABLE;			//内存递增使能dma_init.Init.PeriphInc = DMA_PINC_DISABLE;dma_init.Init.Direction = DMA_PERIPH_TO_MEMORY;	dma_init.Init.Priority = DMA_PRIORITY_MEDIUM;dma_init.Init.Mode = DMA_CIRCULAR;		//循环模式HAL_DMA_Init(&dma_init);__HAL_LINKDMA(&adc_init,DMA_Handle,dma_init);
}

最后主函数:

这里唯一要讲的地方就是红框内的部分,你很有可能会问:为什么不干脆定义成uint32还非要等返回接收的时候进行强转呢?
原因很简单,但不知道就很难。 首先主要是由于区别之一的字节对齐问题,ADC的精度是12位,按照字节对齐则正好占两个字节也就是uint16。但ADC的DMA地址是连续的,也就是如果该数据超过这一字节那么就把下个字节也拿来用了。所以如果一开始你就用uint32,那么一个缓存中就会存入两个通道的数据。像这样:

好了,祝你看完就会。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java中的抽象类和接口区别
  • TypeScript函数类型:提升函数的类型安全性和可读性
  • 2024年厦门市大数据创新应用大赛重磅开赛,邀您来战!
  • 【数据结构初阶】详解:实现循环队列、用栈实现队列、用队列实现栈
  • 通过内网穿透远程访问自己的项目
  • 【力扣】3128. 直角三角形 JAVA
  • matlab y=sin(x) - 2/π*(x)函数绘制
  • 1.1、centos stream 9安装Kubernetes v1.30集群 环境说明
  • CSS mask-image 实现边缘淡出过渡效果
  • Flink-CDC解析(第47天)
  • 【机器学习】机器学习与医疗健康在疾病预测中的融合应用与性能优化新探索
  • CANopen和CAN是什么关系
  • Resilience4j 数据库熔断-健康检查sql
  • C# Web控件与数据感应之 TreeView 类
  • 【数据结构算法经典题目刨析(c语言)】随机链表的复制(图文详解)
  • @angular/forms 源码解析之双向绑定
  • JS数组方法汇总
  • Netty+SpringBoot+FastDFS+Html5实现聊天App(六)
  • oldjun 检测网站的经验
  • opencv python Meanshift 和 Camshift
  • PAT A1050
  • Vue.js-Day01
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • 闭包--闭包之tab栏切换(四)
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 关于List、List?、ListObject的区别
  • 后端_MYSQL
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 每天10道Java面试题,跟我走,offer有!
  • 如何设计一个微型分布式架构?
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 软件开发学习的5大技巧,你知道吗?
  • 数组大概知多少
  • 一个项目push到多个远程Git仓库
  • - 转 Ext2.0 form使用实例
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • ######## golang各章节终篇索引 ########
  • #mysql 8.0 踩坑日记
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (学习日记)2024.02.29:UCOSIII第二节
  • (转)visual stdio 书签功能介绍
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • .aanva
  • .gitattributes 文件
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .NET WPF 抖动动画
  • .Net 基于MiniExcel的导入功能接口示例
  • .NET6 开发一个检查某些状态持续多长时间的类
  • .Net多线程Threading相关详解
  • .Net开发笔记(二十)创建一个需要授权的第三方组件