当前课程知识点:ARM微控制器与嵌入式系统 > 第五章 ARM微控制器的各种外设 > 5.8.3 ARM微控制器外设:ADC寄存器与编程 > 5.8.3 ARM微控制器外设:ADC寄存器与编程
各位同学大家好
我是清华大学工程物理系的曾鸣老师
欢迎大家继续回到我们
ARM微控制器与嵌入式系统的MOOC课程
我们继续进行我们这个单元
关于数据转换器ADC的学习
那么前两个单元里呢
我们了解了ADC的基本原理 概念
以及它的历史渊源
乃至我们的ARM微控制器里头
经常用的SA型的
逐次渐进型的这样一种ADC的
基本工作原理
那么我们这个单元来看一看
我们所用的KL25这样一款
ARM微控制器里头的ADC模块
我们怎么用一个例子把它给用起来
然后以及它接的其他外围功能电路
我们怎么用ADC把这些物理量给采回来
那么如我上次所说
在我们这样一个ARM的微控制器里头
有很多模拟的部分
比如我们有16位的ADC
还有12比特的DAC、比较器等等
那么我们现在举的最简单的这个例子呢
就是把PortC的第0号这样一个引脚
也就是ADC模块0的SE14
单端第14号Channel这样一个复用的引脚
接到了一个非常简单的可调电位器
可调电阻的分压电路上
所以从电路原理上来讲
我们调整板子上那样一个可调电位器的旋钮
就会改变这两个分压电阻节点上的电压值
我们看看我们的程序能不能通过这样引脚
把这样一个在变化的电压值正确的采出来
那么要想做成这样一件事
我们就要在原来那些概念的基础上
真正理解我们这个
ARM微控制器里头的ADC模块
以及他的一些基本的使用
应该说ARM微控制器里头
这个ADC模块的结构是非常复杂的
我们有一个共用的这个ADC的模块单元
特别大家看中间最大那个块写的就是
SA型的逐次渐进型的ADC
那么会有一系列的控制逻辑和寄存器来伺候它
并且这样一个ADC模块对外有十几个通道
几十个引脚的输入端可以被选通和复用
那么这些输入端呢
又可以工作在单端模式
或者是两个引脚的差分模式
来进行电压值的采样
所以在这门课的范畴里
我们不拆开一点点的讲这个ADC的模块的
里头的内部结构
包括待会我给大家讲它的寄存器
我们也只会精选非常简单的一个部分
教会大家把这样一些
板上已有的基本功能给用起来
让大家有信心
那么大家在这样一个程序的基础上
可以大胆的去看一看
芯片手册的其他比特的寄存器的定义
把更多的功能给自己试一试
那么我们要把这样的ADC模块用起来
在前面已经隐约提到了我们这个芯片
会有两个参考电压的引脚
来为我们这个ADC提供进行数模变化
所使用的那样一个参考电压
那么就好像我们有一把尺子
尺子的刻度首先得准
你量东西才可信
所以说参考电压对于一个模数变换器
一个ADC来起的就是这样一个作用
那么在我们这样一块开发板上我说过
它是直接把我们供电的3.3伏
通过一些简单的滤波器
接到了这个参考电压上
所以对于我们日常像实验范围内
一些基本的测量一些简单的物理量
在百分之一乃至千分之一这个精度上
我们讨论讨论它的值还是可信的
如果大家日后要用微控制器设计更高精度
更高稳定性的测量系统
一定要注意参考电压的用专门的电路
来提供和选择加以设计
另外一个概念呢
就是我们ADC它实际上作为一个外设模块
又是一个微控制器自己的外设模块
它的速度和它的精度实际上是一个平衡
是一个Trade off
那么在这个过程当中
它是根据我们的编程寄存器的设置
可以选择的
比如在芯片手册里我提到13比特以下
和16比特这两种工作模式
整个ADC模块的驱动时钟是有一个范围的
一个到比如说12兆
一个到十几兆
那么在这样的不同时钟下
我们ADC就工作在不同的转换速率下
采集一次电压值所用的时间是有长有短的
然后一个单位时间内能够完成的
采样次数也是有差异的
所以在对应的时钟下不同精度模式的这个
ADC的采样率或者叫转化率
在这张表的下面这两行也进行了罗列
从几十K到几百K
而在不同的采样率下
我们对寄存器进行不同的设置
这个ADC虽然它的Resolution
它的分辨率可以从
10比特 12比特 一直到16比特
而它能够达到的精度也是跟这速度相关联的
是有一个平衡
那么我们看一下这张图
如果我们用ADC工作的时钟频率为横轴
那么同样的ADC模块
在采用同样的配置要用四次平均值
或者32次平均值做读出的时候
能够实现的有效位数
大约就是这条曲线所示
然后它们是随着速度略有变化的
当然我们如果在片内寄存器设置这个读出值
在被读出之前做了
自动做了4次或者32次平均
有效位数会略有提升
但总体上来讲随着速度的提升
我们的有效位会略有下降
所以当我们使用这样一个模块的时候
我们实际上根据我们物理需求学会调整
待会我要讲到的寄存器设置
在ADC的工作速度和工作精度当中
达到一个平衡
而如果你设计的是一个低功耗产品
你还在它的工作模式和功耗之间
再来加以选择
那么有了这些基本的概念
我们还是用一个最最简单的编程
就把刚才提到的这样一个PTC0引脚
ADC0模块第14通道复用的这样一个引脚的
一个电位器电阻上的分压电压值
想办法给读出来
那么要几个步骤呢
还是三个步骤
所以呢
前两个步骤现在因为非常熟练
我们可以非常简单的把它讲完
我们最后直接进入到第三个步骤
来看这个ADC的设置
那么第一个步骤是把ADC模块
和我们ADC这个引脚附用的
这个Port的对应的时钟打开
那么仍然是在SIMSCGC4的
这一系列寄存器里头
我们能找到ADC0这个模块的时钟开关
以及我们用到的PortC这个Port的时钟开关
用两句话分别把这两个模块的时钟打开
那么第二个步骤呢
是把我们用到的PTC0这个引脚
配置给我们的ADC来使用
而不是当IO用
那么这也非常简单
就是PORTC_PCR0这个寄存器
那么我们看一看会发现
对于PTC0这个引脚
ADC_SE14
也就是给ADC模块的单端信号第14通道用
来采集电压值这个功能
就是它的第0号功能
所以我们PortC的PCR0这个寄存器
给它赋一个00的值
就让它中间那三个比特选中第0号功能
就配置为当ADC用了
那么真正核心的是最后一个步骤
就像我所说我们这样一个ADC模块
ADC0这个模块
在我们的微控制器里头大大小小
一共有28个寄存器可以配置它
它可以非常灵活的工作在单次的模式
连续的模式
不同的采样精度
以及电压单端或者差分输入
十几个通道的选择
或者几十个引脚之间的复用来选择
所以这28个寄存器组合起来
我们的ADC模块会非常的灵活
但是编起程来也非常的麻烦
那么在这门课的范畴里
我带大家抛开所有冗余的信息
仅仅我们只深入的看这五个寄存器
把我们刚才所说的
这样一个简单功能给用起来
那么我们来一个一个看
第一个寄存器呢是ADC0的CFG1这个寄存器
那么这个寄存器最重要就是
设置我们ADC模块的时钟信号
那么我用红圈标出了ADIV和ADICLK
这两个区域的各两个比特的信息
那么如红圈所示我们在这里头
选择使用Bus Clock
大家记得就是我们20.97兆默认的一半的速度
作为输入时钟
而分频因子做唯一
所以直接这个ADC模块
在这样一组配合下采用的时钟
就是20.97兆的一半就是10.485兆
如果大家回忆前面我讲过那张表
就会发现这个时钟是一个比较适中的值
无论是采用小于13比特
低精度的ADC采样
还是16比特高精度采样
这个时钟都是适宜的
所以大家后面可以比较灵活
第二个需要配置的呢
是在ADC0的CFG同一个寄存器里头
另外一个比特我们称为这个ADLSMP
这个比特可以选择Short sample time
或者Long sample time
也就是设置ADC以更短的时间完成采样
还是以更长的时间完成采样
那么这有一大段英文对它进行解释
简单的说就是选择用更长的时间进行采样
这个引脚的输入阻抗会更大
对外采集电压值的精度可能会更高
但是因为RC程序时间常数会更长
所以它需要的这个信号的
建立时间和稳定时间也会更长一些
而如果选择Short sample time的时候
我们降低了引脚的输入阻抗
那么这个时候我们可以用更高的速率采样
但是相应引脚的阻抗低了以后
我们对于一些驱动能力不足的信号源
或者是对于一些敏感的信号
可能精度不如长采样时间
所以我们这里设为1
选择长时间采样
那么另外一个这个寄存器里头的两个比特呢
是这个比特2和比特3的这个Mode这个比特
这个比特它的00到11的四个组合
决定了我们这个ADC要工作在多少比特
这个精度的模式下
比如我们现在推荐大家在这门课的实验里
把它设成01
于是就是在单端模式下
采用12位精度的ADC来进行读取
那么日后大家熟悉了ADC的使用
你可以自己灵活选择更低精度更快的采样
或者更高精度更慢的采样
来在这儿当中可以自己进行平衡
所以把刚才所有这些比特联系起来
第一个寄存器ADC0_CFG寄存器
我们给它赋的值
就是0X00000014
第二个寄存器是ADC0的CFG2
在我们现在使用的这个引脚PTC0里头
它是ADC0的SE单端第14通道
那么实际上在ADC模块
我们如果仔细看芯片手册
还会发现一些别的引脚
它是ADC0的SE单端某一个序号通道后面
还有一个尾缀a或者b
也就意味着一个通道
又被复用给了两个物理引脚可以用
那个时候在这个寄存器里
我们标红的这个Mux select这个比特
通过0和1来选择一个通道里
我们是用尾缀a还是尾缀b的那个引脚
那是另外一种灵活
所以在我们现在用的这个引脚PTC0
对应的这个单端14通道里头没有a和b
所以这个比特我们设默认值0就可以了
那我们对于ADC要用到的第三个寄存器呢
就是ADC0_SC2这个寄存器
这个寄存器当中最主要的
就是这个我画红框的这个比特6
这个比特决定我们ADC模块的trigger方式
也就是如何来启动一次模数变化
也就是说在什么条件下
开始把一个模拟电压值
尝试转换成一个我们存在寄存器里的数值
那么这个比特为0的时候
是采用软件trigger的方式
这个比特为1的时候采用的是
硬件trigger的方式
所谓软件trigger的方式
就是在我们待会后面讲到的
SC1A这个寄存器被写入值以后
就开始一次模拟电压的采集和数字化
而如果是硬件trigger的方式
则意味着在微控制器的内部会有一组信号
我们要对它进行别的寄存器的设置配置
比如说比较器采到过阈电压
比如说一个定时器定的时间到了
由硬件的方式通知ADC开始一次采集
所以在本门课程的范畴内呢
我们把它设置为0
也就是软件trigger的方式
在待会的函数编程里头
我们会用软件方式用一条语句
来启动ADC的转换
第四个涉及到的配置用的寄存器呢
是ADC的SC3
也就是状态和控制寄存器第三个
在这个寄存器里头
我们主要是控制它的ADCO这样一个比特
那么这个比特非常有意思
当这个比特为0的时候
每一次启动ADC只完成一次电压转化
也就是说启动看一看输入电压值是多少
转换成数字量存起来就结束
而当这个比特是1的时候
就会进入一种continuous conversions
也就是连续转换模式
那么它会用自己尽可能快的速度
在时钟的驱动下
每完成一次转化把值
存到寄存器里等着人读
再紧接着完成下一次转化
会不停的采样这个引脚上的电压值的变化
而来更新寄存器
那么为了编程的简单
在我们这门课现在这个实验范围里头
我们将这个比特至1
也就是一旦启动
我们就不用反复启动ADC了
这个ADC就在不断进行ADC的变化
寄存器的值不断的
根据一次一次的ADC采集来更新
而每次更新值
都是与一个很短时间间隔之前
我们这个引脚上电压值相关联的
那么这条语句就是ADC0
SC3等于0X00000008对这个比特至1
那么涉及到的第五个寄存器
也就是我们要读的倒数第二个寄存器呢
是我们ADC0的SC1A
这个寄存器非常有意思
我们感兴趣的
或者我们需要关注的是两个区域
我标了红色
一个是最底部的这五个比特
也就是ADCH这个区域
这个区域可以赋一系列由01组合的值
那么这些值就决定了
当前这一次启动ADC采样
是与哪一个Channel哪一个引脚相关联
比如说我们现在设成我标了黄色区域
标了红框的这个01110这一组值
就意味着我们这一次ADC转换读取的是
AD的通道14
也就是我们对应的单端引脚的ADC0_SE14
这个Channel上的电压值
这就是我们刚才所说的
PTC0引脚的复用功能
接在电位器上的一个值 对吧
所以我们应该对这个ADC0_SC1A这个寄存器
赋上刚才说的01110这样一个二进制的数
写成16进制
就是0X000000E
那么赋了这个值
那么另外大家需要注意的一个比特
是这个上面这个COCO这个比特
也就是比特7
比特7这是一个flag
是个标志位
每当一次Conversion一次模数转化
完成的时候
这一个比特就会至1
我们就知道我们要读的这个转化的寄存器里头
有了一个新的值
那么刚才我们把这个ADC
设置为了连续转换的模式
所以大家可以想象这个标志位
是不断的被刷新至1
每当它至1一次就意味着寄存器里
有一个新的值
而我们有读这个值
就是离现在最近的那个时间间隔之前
引脚上的电压值的反映
所以我们不停的根据这个标志位
来读我们的数据寄存器的值
就可以不停的获得引脚上电压的变化
那么我们在引脚上电压的变化怎么读呢
读的寄存器就是我们最后一个
涉及到寄存器ADC0_RA
大家注意我们前面是给
ADC0_SC1A这个寄存器
指定了ADC的14通道
也就是说用A这一组来读14通道
所以它对应的数据
就存在了ADC0_RA寄存器里头
那么它的格式在我们前面设置这个
ADC的工作模式时候已经定死了
是12比特的单端模式
所以就如这张表里的红框所示
读出来的数据是一个16比特的数
但是它的高次比特都填了0
后面的12个比特
是我们真实的这一次转化得到的电压值
所以把刚才所说的这样一个读写的流程
如果做成一个ADC0_DATA的封装
读取这个ADC的值的话
我们实际上是每次进去查ADC0_SC1A上面
COCO这么一个标志位的状态是否为1
如果为1就说明了完成一次转化有数据可读
那么用While循环来查询等待它有了以后
我们从ADC0_RA的数据寄存器
把这一次的转换值读出来
然后当做函数的返回值返回
同时注意擦一下ADC0_SC1A这个COCO的标志位
以便下一次读取
那么在这样一个函数的背后
我们应该脑袋里稍微回忆一下
匆匆忙忙刚才讲过这六个寄存器
如果没有想明白同学倒回去看一看
会发现每个寄存器有若干个比特可以设置
但是我们实际上
已经在曾老师的简化模型里头
把这个ADC模块约定在了
一种很有意思的工作模式
我们用一次对ADC0_SC1A的读
来启动ADC模块
制定了ADC模块读的是单端的通道14
这样一个通道
这个通道反映到物理引脚上呢
就是PTC0这个引脚
复用的第0号功能 对不对
读的就是上面的电压值
然后ADC被配置为了一个连续转化的模式
也就一旦启动他就自己一次一次的采样电压值
一次一次获得数据值
把这个数据值一次一次的刷到数据寄存器
并且至标志位说转化完成
所以我们每调用一次
刚才封装的这个C语言函数
实际上都是等
如果当前正在转化
就等一等
然后每次都是等到一次转化结束
读它自动不停刷新的这个采样里头
最近的一次的值
所以实际上这个函数被调用的时候
我们从寄存器读一次离的最近的值
而寄存器的值这个ADC模块
被我们设置在了一个自己不停的
在运行的状态
那么这样一种编程的方式呢
我们ADC用起来比较简单
它用一个指定的速率
指定的精度在不停的转化
你需要知道这个引脚值的时候
就调用这个函数读一下它的值
缺点呢就是它一直在那儿转化
它就相对的一直在那儿耗电
它并不是一种低功耗的设置
而更高阶的如果说要平衡速度和精度
希望速度更快
或者希望精度更高
大家需要更加仔细的研究呢
这个寄存器的设置和你电路板上的外围电路
特别是当大家后面做到光线的物理量测量
温度的物理量测量
如果你觉得你测的值
晃的厉害 抖的厉害的时候
你可以多方面考虑这些因素
但是不管怎么样
我们先用最简单的方式
把这么一个模块给用起来
好 那如果再多说一句
刚才前面讲到那四五个寄存器
它的初始化
包括我们最熟悉的把引脚的时钟打开
把引脚配置为给ADC用 大家都记住了吗
我们尝试把一个ADC0_Init的函数
像这样写出来
所以这些函数并不是范本
并不是大家必须这么做
而是希望大家以这些代码为例
理解如何简单把ADC模块给用起来
能够把电压值给读进来
那么完成了这样一个函数
完成了这样一个读写函数的封装
完成这样一个初始化函数封装以后
我们就可以尝试玩一玩我们这个电位器了
我们都知道把它转一转
它电阻就会在一个范围内变化
电阻我们读的那个端点的电压
就会从一个比较小的值到一个比较大的值
那么给大家一个任务说能不能用电位器
控制大家板子上最早玩过那个点灯
那个灯的闪烁速度呢
你转转旋钮让灯越闪越快
再转转旋钮让灯越闪越慢
如果能那你就玩玩看吧
这就是这节课的一个练习
那么作为它的拔高
包括后面实验我给大家讲的
比方说我们板子上的灯是三色的LED
我曾经给大家提议过
你用不同的颜色的频率占空比的变化
来产生呼吸灯 产生七彩灯
那请问你能用这个旋钮
来改变你这些灯的颜色吗
然后又比方说我们在这个板子上
至少还有两个ADC已经接好的外围电路
一个是用热敏电阻分压
我们可以测量我们周围的环境温度
你能够成功读出映射到了PTC1引脚上的
ADC0的单端15通道这个引脚的电压信号
来测一测我们热敏电阻
产生了多少电压值吗
那么热敏电阻分到电压值
我们能换算回温度吗
又比方说我们在这样一个电路上
有一个光敏的二极管
接了一个电压的跟随和放大电路
那么我们能不能采用
我们的另外一个PTC2引脚
也就是ADC的通道11来测一测
这个输出电压值
乃至还原成测我们板子所处的光线环境
到底有多少流明呢
所以这些任务都可以大家自由地去探索
在这个过程当中去感受ADC
为什么我说它非常重要
帮助大家建立了一个微控制器的
片上计算机系统
与真实物理世界之间的接口与联系
-1.1 课程概览
--1.1 课程概览
-1.2 如何学好嵌入式系统
-2.1 计算机的基本概念、发展历史
-2.2 从晶体管到CPU
-2.3 概念CPU、微控制器MCU和嵌入式系统
-2.4 八卦计算机史
-2.5 不同领域、不同系列的嵌入式系统
-2.6 ARM历史与MKL25Z128 MCU
--2.6 ARM历史与MKL25Z128 MCU【习题】
-3.1 CPU的基本结构和运行机制
-3.2.1 堆栈的概念
--3.2.1 堆栈的概念【习题】
-3.2.2 堆栈的概念-头脑体操
-3.3.1 ARM的体系结构
--3.3.1 ARM的体系结构【习题】
-3.3.2 ARM的体系结构-头脑体操
-3.4.1 中断的概念和机制
-3.4.2 中断子程的概念和编程
-3.5 复位、时钟、存储器和总线
--3.5 复位、时钟、存储器和总线【习题】
-3.6 小结:MCU的总体结构和程序运行机制
--3.6 小结:MCU的总体结构和程序运行机制【习题】
-4.1 第一种外设:IO
-4.2 IO外设的编程实操-点亮LED
-4.3 IO外设的进阶知识
-4.4 嵌入式开发的基本概念与工具链
-4.5 嵌入式开发的进阶知识
-4.6 嵌入式开发中的C语言(上)
--4.6 嵌入式开发中的C语言(上)【习题】
-4.7 嵌入式开发中的C语言(下)
--4.7 嵌入式开发中的C语言(下)【习题】
-E0.1 实验零 开发板的初步认识与工具链的安装
-E0.2 实验零 体验一个例程的编译与下载
-E0.3 实验零 编写第一个程序:点亮核心板LED
-E1 实验一 点灯秘籍
-5.1 ARM微控制器外设学习概述
-5.2.1 ARM微控制器外设:通讯
-5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)
--5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)
--5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)【习题】
-5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下)
--5.2.3 ARM微控制器的外设:异步串行通讯UART的原理(下)
--5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下)【习题】
-5.2.4 ARM微控制器外设:RS-232串口与USB虚拟串口
--5.2.4 ARM微控制器外设:RS-232串口与USB虚拟串口
-5.2.5 ARM微控制器外设:UART的寄存器编程(上)
--5.2.5 ARM微控制器外设:UART的寄存器编程(上)
-5.2.6 ARM微控制器外设:UART的寄存器编程(下)
--5.2.6 ARM微控制器外设:UART的寄存器编程(下)
--5.2.6 ARM微控制器外设:UART的寄存器编程(下)【习题】
-E2 实验二 UART编程实操
-5.3.1 ARM微控制器外设:IO的中断编程(上)
-5.3.2 ARM微控制器外设:IO的中断编程(下)
-5.4.1 ARM微控制器外设:定时器的原理
-5.4.2 ARM微控制器外设:定时器的编程
--5.4.2 ARM微控制器外设:定时器的编程【习题】
-E3 实验三 定时器中断编程实操
-5.5.1 ARM微控制器外设:PWM的原理
-5.5.2 ARM微控制器外设:PWM寄存器与编程
-5.5.3 ARM微控制器外设:PWM编程实例—电子音乐
--5.5.3 ARM微控制器外设:PWM编程实例—电子音乐
-E4 实验四 数码管显示编程实操
-5.6.1 ARM微控制器外设:SPI通讯简介
--5.6.1 ARM微控制器外设:SPI通讯简介【习题】
-5.6.2 ARM微控制器外设:SPI寄存器与编程
-5.6.3 ARM微控制器外设:SPI编程实例—OLED显示屏驱动
--5.6.3 ARM微控制器外设:SPI编程实例—OLED显示屏驱动
-5.7.1 ARM微控制器外设:I2C通讯简介
-5.7.2 ARM微控制器外设:I2C的通讯协议
-5.7.3 ARM微控制器外设:I2C寄存器与编程
--5.7.3 ARM微控制器外设:I2C寄存器与编程【习题】
-5.7.4 ARM微控制器外设:I2C编程实例—加速度传感器
--5.7.4 ARM微控制器外设:I2C编程实例—加速度传感器
-5.8.1 ARM微控制器外设:ADC简介
-5.8.2 ARM微控制器外设:ADC基础
-5.8.3 ARM微控制器外设:ADC寄存器与编程
-E5 实验五 ADC编程实操
-E6 挑战实验
--E6 挑战实验
-6.1 嵌入式系统的接口与设计
-6.2 嵌入式系统的实例