【STM32F103】ADC 模拟数字转换器

news2024/11/26 3:58:30

ADC

ADC(Analog-to-Digital Converter),模拟-数字转换器,也叫模数转换器,可以将连续变化的模拟信号转换为离散的数字信号。

我们可以外接上将采集信号转为模拟信号的模块,如光敏电阻传感器,热敏电阻传感器,反射式红外传感器等。

这些传感器模块通过采样,量化,编码,通过AO引脚将模拟信号传出,我们可以使用STM32来接收,并且通过ADC来获取对应的数字信号。

STM32中的ADC

特征

STM32F103的ADC具有12位的分辨率,也就是转换结果的范围是 0~2^12-1 。

ADC的供电要求是2.4V~3.6V。

ADC可以配合DMA转运,也可以配合看门狗进行监控。

输入时钟不得超过14MHz,由APB2(72MHz)分频得到ADC的输入时钟,由于分频系数的限制,不超过14MHz的能得到的最高的频率为12MHz(6分频)

最短的转换时间是1.17us。(1s/12MHz * (1.5(最少的采样周期) + 12.5 (固定量化编码的时间)) ))

通道

每个ADC都有18个通道,ADC1和ADC2的最后两个通道是内部通道,ADC3的最后两个通道是内部通道,其余为外部通道,我们可以连接到外设,另外从STM32F103C8T6的引脚定义表我们可以知道,在C8T6这个型号的芯片中,我们一共可以外接10个模拟信号传感器模块。也就是说尽管我们有18个通道,但最多只能使用12个(10个外部通道+2个内部通道),不过也是够用的。

其中ADC1的第17个通道连接的是内部的温度传感器,可以读取到CPU周围的温度。

通道分为外部通道和内部通道,外部通道又分为规则通道和注入通道,一般使用规则通道。注入通道最多4个,规则通道最多16个。

注入通道是一种不安分的通道,注入通告只有在规则通道存在时才会出现,并且在规则转换通道转换的时候会强行插入要转换,等注入通道转换玩之后才会回到规则通道,和中断很像。我们尽量不使用注入通道。

转换结果

ADC转换后的数据存放在ADC_DR寄存器(规则通道)或JDRx(注入通道)中。

ADC_DR寄存器是32位的寄存器,低16位在单ADC的时候使用(独立工作模式),高16位在双模式下保存ADC2的转换结果。之前说过,STM32中的ADC的精度为12位,但是这里却用16位来存储结果,因此我们可以选择将结果左对齐或是右对齐,左对齐的话就是将结果整体左移4位,也就是数值乘16,一般就右对齐。

由于通道可以有很多个,但是存放数据的寄存器就只有一个,所以一旦数据转换完之后我们就要将数据取走,以免被后续的转换结果覆盖,通常使用多通道转换的时候我们会配合上DMA来替我们自动转运多个结果。

转换的结果是12位的数字,而因为输入的电压范围为0~3.3V,所以我们可以将转换后的结果再乘上3.3再除(2^12-1)得到电压值,然后再根据模块的说明书来进行进一步转换成有效的数据值。

固件库函数

 注:红色为固定的函数名,绿色为推荐的参数

 设置ADCCLK的预分频器

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

设置ADCCLK的预分频器,之前说过,ADC最多只能14MHz,而APB2位72MHz,因此我们选择6分频得到12MHz。

初始化ADC 

ADC_Init(ADC1,&itd1);

初始化ADC,第一个参数指定使用哪个ADC资源,我们一般就使用ADC1或ADC2。

第二个参数传入ADC_InitStruct类型变量的地址。

我们通过给ADC_InitStruct类型变量的成员赋值来配置ADC。

ADC_Mode选择工作模式,仅用单个ADC的话,那我们选择ADC_Mode_Independent
ADC_ScanConvMode

是否开始扫描模式,ENABLE或是DISABLE,不开启的话,仅转换

待转换队列的第一个通道,开启之后会自动按照顺序来转换每个配置过

的通道,一般扫描模式配合DMA转运一起,因为ADC转换速度极快,而

所有通道的转换结果都是放到同一个寄存器里的,因此手动转移结果太慢

的话就会被覆盖,太快的话就会拿到错误数据。

ADC_ContinuousConvMode

是否开启连续模式,ENABLE或是DISABLE,不开启的话每次转换

完一次待转换队列的通道之后就会停下,等待下次转换指令。

开启的话就会不断地一次次自动转换所有配置过的通道。

ADC_ExternalTrigConv

是否使用外部触发,不使用则软件触发,一般使用

软件触发ADC_ExternalTrigConv_None

触发函数下面会介绍。

ADC_DataAlign

数据对齐方式,之前说过,ADC转换的结果是12位的,而存放结果的

寄存器是16位的,因此有4位的空白位,我们可以选择左对齐或是右对齐,

一般选择右对齐ADC_DataAlign_Right

ADC_NbrofChannel通道数目,我们配置了多少个通道就填多少。

配置规则组的输入通道 

ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_239Cycles5);

配置规则组的输入通道,第一个参数指定使用哪个ADC资源。

第二个参数指定通道,这个我们根据引脚定义表来决定。

第三个参数根据固件库函数给的注释,直译是“常规组序列发生器中的秩。此参数必须介于1到16之间。”,我们可以当成要给ADC转换的通道需要排好队,从1排到16,按照顺序来转换。

第四个参数可以选择转换时间,实际上是选择用几个周期去采样时间,使用的周期越多采样时间越长,得到的数据也就更稳定,没有特殊要求的话可以随意选择一个选项。

这里以内部的温度传感器为例来计算一下这个选项应该如何选择

参考手册里说推荐采样时间为17.1us,而采样时间的计算公式为:

(1s/12MHz)*x。x为我们选择的采样花费的周期数,经过计算我们可以知道,x至少要为205.2,因此如果我们要采样内部的温度传感器的话,我们应该选择ADC_SampleTime_239Cycles5。

上电

ADC_Cmd(ADC1,ENABLE);

参数一选择ADC资源,参数二选择是否上电。

校准

“ADC 有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在
校准期间,在每个电容器上都会计算出一个误差修正码 ( 数字值 ) ,这个码用于消除在随后的转换
中每个电容器上产生的误差。”
以上出自参考手册,我试过了,如果不校准的话差别其实不大,大概和校准后差个二三十,反正加个校准的代码也不麻烦,我们就加上。
具体函数的功能我们不用去记,要用的时候把下面4行代码加上就行,在ADC初始化之后。
    ADC_ResetCalibration(ADC1);                             //复位校准
    while(SET==ADC_GetResetCalibrationStatus(ADC1));        //等到复位校准完成
    ADC_StartCalibration(ADC1);                             //开始校准
    while(SET==ADC_GetCalibrationStatus(ADC1));             //等待校准完毕

取值

ADC_GetConversionValue( ADC1 )
参数一指定要读取的ADC资源。
如果去看了源码我们可以知道,其实这个函数就是读取的对应ADC的DR寄存器。
读取转换完的数据的前提是转换完了,如果不是连续模式的话,我们是需要先触发转换并且等待转换完毕之后再读取的。

软件触发转换

ADC_SoftwareStartConvCmd(ADC1,ENABLE);

参数一指定触发转换的ADC资源。
触发转换之后,我们在读取结果之前还需要等待转换结束。

获取ADC标志位

ADC_GetFlagStatus( ADC1 , ADC_FLAG_EOC )

 参数一指定ADC资源。

参数二指定要获取的标志位,我们要知道转换是否结束,获取的标志位为ADC_FLAG_EOC。具体的做法可以参考下面的代码。

使用内部温度传感器

ADC_TempSensorVrefintCmd(ENABLE);

如果要使用内部的温度传感器的话,使用上面的函数把功能打开,并且这个传感器只能为ADC1使用,通道为第17个通道,也就是 ADC_Channel_16。并且上面说了,推荐的采样时间为17.1us,需要根据这个来配置采样周期。

假设得到的值为x,那么转换为摄氏度的公式为: (1.43-(x*(3.3/4096)))/0.0043+25

接线

 将传感器的AO口接到GPIOA的0号口,根据引脚定义表,对应的是通道0,这点在代码里体现,如果要改GPIO口的话,只需要根据引脚定义表来更改代码中指定的通道即可。

思路

首先我们需要打开外设时钟,使用ADC1就把对应的时钟打开。

另外,如果我们是外接模拟信号的话,就把对应的GPIO口配置为模拟输入模式,同时对应的外设时钟也要打开。

第二步是配置ADCCLK的分频器,因为不能超过14MHz,所以至少是6分频。

第三步是初始化ADC,参考上面的函数。

第四步配置输入通道(一般使用规则组的通道)。

第五步上电,上电完之后校准(非必须,但是加上不碍事)

第六步在需要的时候触发转换(如果是连续模式的话就不用手动触发了),然后获取值。

完整代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"

//获取ADC转换的值
uint16_t getValue(void){
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);                  //软件触发转换
    while(RESET==ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));     //等到转换完毕
    return ADC_GetConversionValue(ADC1);                    //获取到转换的数据
}


int main(void){
    
    OLED_Init();
    OLED_ShowString(1,1,"Hello World!");
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);    //使能GPIO口外设时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);     //使能ADC1外设时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);                       //设置ADCCLK分配器
    
    GPIO_InitTypeDef itd;
    itd.GPIO_Mode=GPIO_Mode_AIN;                            //选择GPIO口的模式
    itd.GPIO_Pin=GPIO_Pin_0;                                //选择GPIO口
    itd.GPIO_Speed=GPIO_Speed_50MHz;                        //默认选择50MHz
    GPIO_Init(GPIOA,&itd);
    
    //配置输入通道
    ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_239Cycles5);
    
    ADC_InitTypeDef itd1;
    itd1.ADC_ContinuousConvMode=DISABLE;                    //不开启连续转换模式
    itd1.ADC_DataAlign=ADC_DataAlign_Right;                 //数据右对齐
    itd1.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;    //不使用外部触发(软件触发)
    itd1.ADC_Mode=ADC_Mode_Independent;                     //独立工作模式(ADC1与ADC2不配合)
    itd1.ADC_NbrOfChannel=1;                                //仅1个通道
    itd1.ADC_ScanConvMode=DISABLE;                          //不开启扫描转换模式
    ADC_Init(ADC1,&itd1);
    
    ADC_Cmd(ADC1,ENABLE);
    
    ADC_ResetCalibration(ADC1);                             //复位校准
    while(SET==ADC_GetResetCalibrationStatus(ADC1));        //等到复位校准完成
    ADC_StartCalibration(ADC1);                             //开始校准
    while(SET==ADC_GetCalibrationStatus(ADC1));             //等待校准完毕
    
    while(1){
        OLED_ShowNum(2,1,getValue(),5);
    }
    
}

参考

STM32F10xxx参考手册(中文)

[7-1] ADC模数转换器_哔哩哔哩_bilibili

《ARM Cortex-M3 嵌入式原理及应用(基于STM32F103微控制器)》

《STM32库开发实战指南基于STM32F103(第二版)》

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

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

相关文章

光学遥感显著目标检测初探笔记总结

目录 观看地址介绍什么是显著性目标检测根据不同的输入会有不同的变体(显著性目标检测家族)目前这个领域的挑战 技术方案论文1(2019)论文2(2021)论文3(2022) 未来展望 观看地址 b站链接 介绍 什么是显著性目标检测 一张图片里最吸引注意力的部分就是显著性物体,…

【大数据-Hadoop】从入门到源码编译-概念篇

【大数据-Hadoop】从入门到源码编译-概念篇 Hadoop与大数据生态(一)Hadoop是什么?(二)Hadoop组成1. HDFS1.1 NameNode(nn)1.2 DataNode(dn)1.3 Secondary NameNode&#…

公众号怎么提高2个限制

一般可以申请多少个公众号?许多用户在申请公众号时可能会遇到“公众号显示主体已达上限”的问题。这是因为在2018年11月16日对公众号申请数量进行了调整,具体调整如下:1、个人主体申请公众号数量上限从2个调整为1个。2、企业主体申请公众号数…

Docker - Android源码编译与烧写

创建源代码 并挂载到win目录 docker run -v /mnt/f/android8.0:/data/android8.0 -it --name android8.0 49a981f2b85f /bin/bash 使用 docker update 命令动态调整内存限制: 重新运行一个容器 docker run -m 512m my_container 修改运行中容器 显示运行中容器 d…

深入理解 Goroutines 和 Go Scheduler

本文将重点帮助您了解 Golang 中的 goroutines。Go 调度程序如何工作以在 Go 中实现最佳并发性能。我会尽力用简单的语言解释,这样你就可以理解了。 我们将介绍什么是操作系统中的线程和进程,什么是并发,为什么实现并发很难,以及 goroutines 如何帮助我们实现并发。然后,…

十八)Stable Diffusion使用教程:艺术二维码案例

今天说说怎么样使用SD生成艺术二维码。 我们直接上图。 方式有三种,分别如下: 1)方式一:直接 contronet 的tile模型进行控制 使用QRBTF Classic生成你的二维码。 首先输入网址,选择喜欢的二维码样式(推荐第一种就行): 然后选择相应参数,这里推荐最大的容错率,定…

IT圈茶余饭后的“鄙视链” C,C++,Java,Python

目录 C语言的自尊心 C语言的历史与地位 C语言的支持者心态 鄙视链的表现 自尊心的盲点 C的复杂之美 多范式编程的复杂性 高度的控制权 模板元编程的奇妙 面向对象的强大 Java的跨平台与舒适感 跨平台性的优势 舒适的开发体验 对其他语言的轻蔑 面向企业级应用的自…

不再兼容“安卓“,鸿蒙开发与android对比

首先,鸿蒙系统采用了分布式技术,其设计理念是“能用分布式解决的问题就不用单机解决”。这意味着鸿蒙旨在构建一个统一的分布式操作系统,可以支持不同设备之间的交互和通信。 而安卓系统基于Linux内核和Java编程语言构建,属于单机…

SqlServer中,数字-null的问题

一、业务描述 叫货单,已知叫货金额,填写本次付款金额,计算待付款金额 二、问题 在计算待付款金额时,偶尔会出现待付款金额为空的情况,百思不得其解 三、解决 仔细检查,发现了猫腻。 简单的说&#xff…

Axure元件的介绍使用以及登录界面

一、Axure元件介绍 简介: Axure元件是一种功能强大的设计工具,专门用于用户体验设计和交互设计。它可以帮助设计师创建可交互的原型,并实现各种界面元素的设计和布局。 Axure元件的基本特点包括: 多样性:Axure元件包括…

Unity检测AssetBundle是否循环依赖

原理:bundle的依赖关系构建一个二维的矩阵图,如果对角线相互依赖(用1标记)则表示循环依赖。 using PlasticGui; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public cl…

数字社会观察:TikTok如何影响青少年文化?

TikTok,这个全球短视频巨头,正在成为塑造青少年文化的引领者。在这个数字社会中,TikTok的崛起不仅改变了信息传递的方式,更深刻地影响着青少年的价值观、审美观和社交方式。本文将深入探讨TikTok如何在数字社会中塑造和影响青少年…

都是星光赶路人

不知不觉已经快工作五年了,工作以后就感觉时间一年比一年快,仿佛昨天才刚毕业,就像陈鸿宇歌中的那样,多少遗憾自负存念想,唯有时间不可挡。五年,思考了很多,也想明白了许多。正好借着年末&#…

可视化 Java 项目

有一定规模的 IT 公司,只要几年,必然存在大量的代码,比如腾讯,2019 年一年增加 12.9 亿行代码,现在只会更多。不管是对于公司,还是对于个人,怎么低成本的了解这些代码的对应业务,所提…

【设计模式--行为型--策略模式】

设计模式--行为型--策略模式 策略模式定义结构案例优缺点使用场景 策略模式 定义 该模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算…

【摸鱼向】利用Arduino实现自动化切屏

曾几何时,每次背着老妈打游戏的时候都要紧张兮兮地听着爸妈是不是会破门而入,这严重影响了游戏体验,因此,最近想到了用Arduino加上红外传感器来实现自动监测的功能,当有人靠近门口的时候,电脑可以自动执行预…

Vmd+lstm代码详解 完整代码数据可直接运行

项目视频讲解:Vmd+lstm时间序列预测分类回归预测代码详解 完整代码可直接运行_哔哩哔哩_bilibili 项目演示效果: 代码详解: # -*- coding: utf-8 -*- # 导入库pip install openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple import pandas as pd import numpy as np fr…

解决:ModuleNotFoundError: No module named ‘ldm‘

import sys sys.path.append(程序所在路径) 就好了

《TDA4》专栏导航

文章目录 1. 前言2. 章节1. 前言 《TDA4》专栏主要介绍TI TDA4芯片的工程应用笔记,“授人以鱼不如授人以渔”,本专栏着眼于如何从零上手一款复杂的多核异构的芯片平台,其中包含了博主如何查找资料,如何寻求资源,如何实验测试,如何搭建环境等点点滴滴的过程,希望对TDA4感…

计算机网络网络层(期末、考研)

计算机网络总复习链接🔗 目录 路由算法静态路由与动态路由距离-向量算法链路状态路由算法层次路由 IPv4(这个必考)IPv4分组IPv4地址与NAT子网划分与子网掩码、CIDRARP、DHCP与ICMP地址解析协议ARP动态主机配置协议DHCP IPv6IPv6特点 路由协议…