.5.2版本移植FreeRTOS正点原子STM32:基于HAL库的FreeRTOS 4.5.2版本移植

目录

  • ADC 实验
  • ADC 简介
  • 单通道ADC 采集实验
  • ADC 寄存器
  • 硬件设计
  • 程序设计(还没拷贝完)
  • 单通道ADC 采集(DMA 读取)实验
  • ADC & DMA 寄存器
  • 硬件设计
  • 多通道ADC 采集(DMA 读取)实验
  • ADC 寄存器
  • 硬件设计
  • 单通道ADC 过采样(16 位分辨率)实验
  • ADC 寄存器
  • 硬件设计
  • 内部温度传感器实验
  • 内部温度传感器简介
  • 硬件设计
  • 光敏传感器实验
  • 光敏传感器简介
  • 硬件设计
  • DAC 实验
  • DAC 简介
  • DAC 输出实验
  • DAC 寄存器
  • 硬件设计
  • DAC 输出三角波实验
  • DAC 寄存器
  • 硬件设计
  • DAC 输出正弦波实验
  • DAC 寄存器
  • 硬件设计
  • ADC 实验

    本章,我们将介绍STM32F103 的ADC(Analog-to-digital converters,模数转换器)功能。
    我们通过四个实验来学习ADC,分别是单通道ADC 采集实验、单通道ADC 采集(DMA 读取)
    实验、多通道ADC 采集(DMA 读取)实验和单通道ADC 过采样(16 位分辨率)实验。

    ADC 简介

    ADC 即模拟数字转换器,英文详称Analog-to-digital converter,可以将外部的模拟信号转换
    为数字信号。
    STM32F103 系列芯片拥有3 个ADC(C8T6 只有2 个),这些ADC 可以独立使用,其中
    ADC1 和ADC2 还可以组成双重模式(提高采样率)。STM32 的ADC 是12 位逐次逼近型的模
    拟数字转换器。它有18 个通道,可测量16 个外部和2 个内部信号源,其中ADC3 根据CPU 引
    脚的不同其通道数也不同,一般有8 个外部通道。ADC 中的各个通道的A/D 转换可以单次、
    连续、扫描或间断模式执行。ADC 的结果可以以左对齐或者右对齐存储在16 位数据寄存器中。
    STM32F103 的ADC 主要特性我们可以总结为以下几条:
    1、12 位分辨率;
    2、转换结束、注入转换结束和发生模拟看门狗事件时产生中断
    3、单次和连续转换模式
    4、自校准
    5、带内嵌数据一致性的数据对齐
    6、采样间隔可以按通道分别编程
    7、规则转换和注入转换均有外部触发选项
    8、间断模式
    9、双重模式(带2 个或以上ADC 的器件)
    10、ADC 转换时间:时钟为72MHz 为1.17us
    11、ADC 供电要求:2.4V 到3.6V
    12、ADC 输入范围:VREF–≤VIN≤VREF+
    13、规则通道转换期间有DMA 请求产生
    下面来介绍ADC 的框图:

    图中,我们按照ADC 的配置流程标记了七处位置,分别如下:
    ①输入电压
    在前面ADC 的主要特性也对输入电压有所提及,ADC 输入范围VREF–≤VIN≤VREF+,最终
    还是由VREF–、VREF+、VDDA和VSSA决定的。下面看一下这几个参数的关系,如图30.1.2 所示:

    从上图可以知道,VDDA和VREF+接VCC3.3,而VSSA和VREF-是接地,所以ADC 的输入范
    围即0~3.3V。
    ②输入通道
    在确定好了ADC 输入电压后,如何把外部输入的电压输送到ADC 转换器中呢,在这里引
    入了ADC 的输入通道,在前面也提及到了ADC1 和ADC2 都有16 个外部通道和2 个内部通
    道;而ADC3 就有8 个外部通道。外部通道对应的是上图中的ADCx_IN0、ADCx_IN1…
    ADCx_IN15。ADC1 的通道16 就是内部通道,连接到芯片内部的温度传感器,通道17 连接到
    Vrefint。而ADC2 的通道16 和17 连接到内部的VSS。ADC3 的通道9、14、15、16 和17 连接
    到的是内部的VSS。具体的ADC 通道表见表30.1.1 所示:

    ③转换顺序
    当ADC 的多个通道以任意顺序进行转换就诞生了成组转换,这里有两种成组转换类型:
    规则组和注入组。规则组就是图中的规则通道,注入组就是图中的注入通道。为了避免大家对
    输入通道,以及规则通道和注入通道的理解混淆,后面规则通道以规则组来代称,注入通道以
    注入组来代称。
    规则组最多允许16 个输入通道进行转换,而注入组最多允许4 个输入通道进行转换。这里
    讲解一下规则组和注入组。
    规则组(规则通道)
    规则组,按字面理解,“规则”就是按照一定的顺序,相当于正常运行的程序,平常用到最
    多也是规则组。
    注入组(注入通道)

    注入组,按字面理解,“注入”就是打破原来的状态,相当于中断。当程序执行的时候,中
    断是可以打断程序的执行。同这个类似,注入组转换可以打断规则组的转换。假如在规则组转
    换过程中,注入组启动,那么注入组被转换完成之后,规则组才得以继续转换。
    为了便于理解,下面看一下规则组和注入组的执行优先级对比图,如图30.1.3 所示:

    了解了规则组和注入组的概念后,下面来看看它们的转换顺序,即转换序列。转换序列可
    以分为规则序列和注入序列。下面分别来介绍它们。
    规则序列
    规则组最多允许16 个输入通道进行转换,那么就需要设置通道转换的顺序,即规则序列。
    规则序列寄存器有3 个,分别为SQR1、SQR2 和SQR3。SQR3 控制规则序列中的第1 个到第
    6 个转换;SQR2 控制规则序列中第7 个到第12 个转换;SQR1 控制规则序列中第13 个到第16
    个转换。规则序列寄存器控制关系汇总如表30.1.2 所示:

    从上表可以知道,当我们想设置ADC 的某个输入通道在规则序列的第1 个转换,只需要
    把相应的输入通道号的值写入SQR3 寄存器中的SQ1[4:0]位即可。例如想让输入通道5 先进行转换,那么就可以把5 这个数值写入SQ1[4:0]位。如果还想让输入通道8 在第2 个转换,那么
    就可以把8 这个数值写入SQ2[4:0]位。最后还要设置你的这个规则序列的输入通道个数,只需
    把输入通道个数写入SQR1 的SQL[3:0]位。注意:写入0 到SQL[3:0]位,表示这个规则序列有
    1 个输入通道的意思,而不是0 个输入通道。
    注入序列
    注入序列,跟规则序列差不多,决定的是注入组的顺序。注入组最大允许4 个通道输入,
    它的注入序列由JSQR 寄存器配置。注入序列寄存器JSQR 控制关系如表30.1.3 所示:


    注入序列有多少个输入通道,只需要把输入通道个数写入到JL [ 1 : 0 ]位,范围是0~3。注
    意:写入0 表示这个注入序列有一个输入通道,而不是0 个输入通道。这个内容很简单。编程
    时容易犯错的是注入序列的转换顺序问题,下面给大家讲解一下。
    如果JL[ 1 : 0 ]位的值小于3,即设置注入序列要转换的通道个数小于4,则注入序列的转
    换顺序是从JSQx[ 4 : 0 ](x=4-JL[1:0])开始。例如:JL [ 1 : 0 ]=10、JSQ4 [ 4 : 0 ]= 00100、JSQ3 [ 4 : 0 ]= 00011、JSQ2 [ 4 : 0 ]= 00111、JSQ1 [ 4 : 0 ]= 00010,意味着这个注入序列的转换顺序
    是:7、3、4,而不是2、7、3。如果JL[ 1 : 0 ]=00,那么转换顺序是从JSQ4 [ 4 : 0 ]开始。
    ④触发源
    在配置好输入通道以及转换顺序后,就可以进行触发转换了。ADC 的触发转换有两种方法:
    分别是通过ADON 位或外部事件触发转换。
    (1)ADON 位触发转换
    当ADC_CR2 寄存器的ADON 位为1 时,再独立给ADON 位写1(其它位不能一起改变,
    这是为了防止误触发),这时会启动转换。这种控制ADC 启动转换的方式非常简单。
    (2)外部触发转换
    另一种方法是通过外部事件触发转换,例如定时器捕获、EXTI 线和软件触发,可以分为规
    则组外部触发和注入组外部触发。
    规则组外部触发使用方法是将EXTTRIG 位置1,并且通过EXTSET[2:0]位选择规则组启动
    转换的触发源。如果EXTSET[2:0]位设置为111,那么可以通过SWSTART 为启动ADC 转换,
    相当于软件触发。
    注入组外部触发使用方法是将JEXTTRIG 位置1,并且通过JEXTSET[2:0]位选择注入组启
    动转换的触发源。如果JEXTSET[2:0]位设置为111,那么可以通过JSWSTART 为启动ADC 转
    换,相当于软件触发。
    ADC1 和ADC2 的触发源是一样的,ADC3 的触发源和ADC1/2 有所不同,这个需要注意。
    ⑤转换时间
    (1)ADC 时钟
    在学习转换时间之前,我们先来了解ADC 时钟。从标号⑤框出来部分可以看到ADC 时钟是
    要经过ADC 预分频器的,那么ADC 的时钟源是什么?ADC 预分频器的分频系数可以设置的
    范围又是多少?以及ADC 时钟频率的最大值又是多少?下面将为大家解答。
    ADC 的输入时钟是由PCLK2 经过分频产生的,分频系数是由RCC_CFGR 寄存器的
    ADCPRE[1:0]位设置的,可选择2/4/8/16 分频。需要注意的是,ADC 的输入时钟频率最大值是
    14MHz,如果超过这个值将会导致ADC 的转换结果准确度下降。
    一般我们设置PCLK2 为72MHz。为了不超过ADC 的最大输入时钟频率14MHz,我们设
    置ADC 的预分频器分频系数为6,就可以得到ADC 的输入时钟频率为72MHz/6,即12MHz。
    例程中,我们也是如此设置的。

    (2)转换时间
    STM32F103 的ADC 总转换时间的计算公式如下:

    ⑥数据寄存器
    ADC 转换完成后的数据输出寄存器。根据转换组的不同,规则组的完成转换的数据输出到
    ADC_DR 寄存器,注入组的完成转换的数据输出到ADC_JDRx 寄存器。假如是使用双重模式,
    规则组的数据也是存放在ADC_DR 寄存器。下面给大家简单介绍一下这两个寄存器。
    (1)ADC 规则数据寄存器(ADC_DR)
    ADC 规则组数据寄存器ADC_DR 是一个32 位的寄存器,独立模式时只使用到该寄存器低
    16 位保存ADC1/2/3 的规则转换数据。在双ADC 模式下,高16 位用于保存ADC2 转换的数
    据,低16 位用于保存ADC1 转换的数据。
    因为ADC 的精度是12 位的,ADC_DR 寄存器无论高16 位还是低16,存放数据的位宽都
    是16 位的,所以允许选择数据对齐方式。由ADC_CR2 寄存器的ALIGN 位设置数据对齐方式,
    可选择:右对齐或者左对齐。
    细心的朋友可能发现,规则组最多有16 个输入通道,而ADC 规则数据寄存器只有一个,
    如果一个规则组用到好几个通道,数据怎么读取?如果使用多通道转换,那么这些通道的数据
    也会存放在DR 里面,按照规则组的顺序,上一个通道转换的数据,会被下一个通道转换的数
    据覆盖掉,所以当通道转换完成后要及时把数据取走。比较常用的方法是使用DMA 模式。当
    规则组的通道转换结束时,就会产生DMA 请求,这样就可以及时把转换的数据搬运到用户指
    定的目的地址存放。注意:只有ADC1 和ADC3 可以产生DAM 请求,而由ADC2 转换的数据
    可以通过双ADC 模式,利用ADC1 的DMA 功能传输。
    (2)ADC 注入数据寄存器x(ADC_JDRx)(x=1~4)
    ADC 注入数据寄存器有4 个,注入组最多有4 个输入通道,刚好每个通道都有自己对应的
    数据寄存器。ADC_JDRx 寄存器是32 位的,低16 位有效,高16 位保留,数据也同样需要选
    择对齐方式。也是由ADC_CR2 寄存器的ALIGN 位设置数据对齐方式,可选择:右对齐或者左
    对齐。
    ⑦中断
    ADC 中断可分为三种:规则组转换结束中断、注入组转换结束中断、设置了模拟看门狗状
    态位中断。它们都有独立的中断使能位,分别由ADC_CR 寄存器的EOCIE、JEOCIE、AWDIE
    位设置,对应的标志位分别是EOC、JEOC、AWD。
    模拟看门狗中断

    模拟看门狗中断发生条件:首先通过ADC_LTR 和ADC_HTR 寄存器设置低阈值和高阈值,
    然后开启了模拟看门狗中断后,当被ADC 转换的模拟电压低于低阈值或者高于高阈值时,就
    会产生中断。例如我们设置高阈值是3.0V,那么模拟电压超过3.0V 的时候,就会产生模拟看
    门狗中断,低阈值的情况类似。
    DMA 请求
    规则组和注入组的转换结束后,除了可以产生中断外,还可以产生DMA 请求,我们利用
    DMA 及时把转换好的数据传输到指定的内存里,防止数据被覆盖。
    注意:只有ADC1 和ADC3 可以产生DAM 请求,DMA 相关的知识请回顾DMA 实验。
    ⑧单次转换模式和连续转换模式
    单次转换模式和连续转换模式在框图中是没有标号,为了更好地学习后续的内容,这里简
    单给大家讲讲。
    (1)单次转换模式
    通过将ADC_CR2 寄存器的CONT 位置0 选择单次转换模式。该模式下,ADC 只执行一
    次转换,由ADC_CR2 寄存器的ADON 位启动(只适用于规则组),也可以通过外部触发启动
    (适用于规则组或注入组)。
    如果规则组的一个输入通道被转换,那么转换的数据被储存在16 位ADC_DR 寄存器中、
    EOC(转换结束)标志位被置1、如果设置了EOCIE 位,则产生中断,然后ADC 停止。
    如果注入组的一个输入通道被转换,那么转换的数据被储存在16 位ADC_DRJx 寄存器中、
    JEOC(转换结束)标志位被置1、如果设置了JEOCIE 位,则产生中断,然后ADC 停止。
    (2)连续转换模式
    通过将ADC_CR2 寄存器的CONT 位置1 选择连续转换模式。该模式下,ADC 完成上一
    个通道的转换后会马上自动地启动下一个通道的转换,由ADC_CR2 寄存器的ADON 位启动,
    也可以通过外部触发启动。
    如果规则组的一个输入通道被转换,那么转换的数据被储存在16 位ADC_DR 寄存器中、
    EOC(转换结束)标志位被置1、如果设置了EOCIE 位,则产生中断。
    如果注入组的一个输入通道被转换,那么转换的数据被储存在16 位ADC_DRJx 寄存器中、
    JEOC(转换结束)标志位被置1、如果设置了JEOCIE 位,则产生中断。
    ⑨扫描模式
    扫描模式在框图中是没有标号,为了更好地学习后续的内容,这里简单给大家讲讲。
    可以通过ADC_CR1 寄存器的SCAN 位配置是否使用扫描模式。如果选择扫描模式,ADC
    会扫描所有被ADC_SQRx 寄存器或ADC_JSQR 选中的所有通道,并对规则组或者注入组的每
    个通道执行单次转换,然后停止转换。但如果还设置了CONT 位,即选择连续转换模式,那么
    转换不会在选择组的最后一个通道上停止,而是再次从选择组的第一个通道继续转换。
    如果设置了DMA 位,在每次EOC 后,DMA 控制器把规则组通道的转换数据传输到SRAM
    中。而注入通道转换的数据总是存储在ADC_JDRx 寄存器中。
    到这里,我们基本上介绍了ADC 的大多数基础的知识点,其它知识后面用到会继续补充,
    如果还有不懂的内容,请参考《STM32F10xxx 参考手册_V10(中文版).pdf》的第11 章。

    单通道ADC 采集实验

    本实验我们来学习单通道ADC 采集实验。本实验使用规则组单通道的单次转换模式,并
    且通过软件触发,即由ADC_CR2 寄存器的SWSTART 位启动。下面先带大家来了解本实验要
    配置的寄存器。

    ADC 寄存器

    这里,我们只介绍本实验用到的寄存器的关键位,其它寄存器后续用到会继续介绍。
    ⚫ ADC 控制寄存器1(ADC_CR1)
    ADC 控制寄存器1 描述如图30.2.1.1 所示:

    SCAN 位用于选择是否使用扫描模式。本实验我们使用单通道采集,所以没必要选择扫描
    模式,该位置0 即可。
    DUALMOD[3:0]位用来设置ADC 的操作模式,我们的例程中ADC 相关的实验都是使用独
    立模式,所以设置为0000 即可。
    ⚫ ADC 控制寄存器2(ADC_CR2)
    ADC 控制寄存器2 描述如图30.2.1.2 所示:

    该寄存器我们针对性的介绍一些位:ADON 位用于打开或关闭AD 转换器,还可以用于触
    发ADC 转换。CONT 位用于设置单次转换模式还是连续转换模式,本实验我们使用单次转换
    模式,所以CONT 位置0 即可。CAL 位用于开启AD 校准。RSTCAL 位用于判断校准寄存器是
    否已初始化。ALIGN 用于设置数据对齐,我们使用右对齐,所以该位置0。EXTSEL[2:0]位用
    于选择规则组启动转换的外部事件触发源,本实验使用的是软件触发(SWSTART),所以这三
    个位置为111。EXTTRIG 位必须置1,EXTSEL[2:0]位才能选择软件触发(SWSTART),别漏了
    这步,否则设置软件触发会不成功。SWSTART 位用于启动一次规则组通道的转换,即软件触发转换。
    ⚫ ADC 采样事件寄存器1(ADC_SMPR1)
    ADC 采样事件寄存器1 描述如图30.2.1.3 所示:


    ⚫ ADC 采样事件寄存器2(ADC_SMPR2)
    ADC 采样事件寄存器2 描述如图30.2.1.4 所示:

    ADC 采样时间设置需要由两个寄存器设置,ADC_SMPR1 和ADC_SMPR1,分别设置通道
    10~17 和通道0~9 的采样时间,每个通道用3 个位设置。可以看出ADC 的每个通道的采样时间
    是支持单独设置的。
    一般每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降
    低ADC 的转换速率,看大家怎么衡量选择了。本实验中,我们设置采样时间是239.5 个周期。
    结合前面介绍过的转换时间公式:

    ⚫ ADC 规则序列寄存器1
    ADC 规则序列寄存器共有3 个,这几个寄存器的功能都差不多,这里我们仅介绍一下ADC
    规则序列寄存器1(ADC_SQR1),描述如图30.2.1.5 所示:

    L[3:0]用于设置规则组序列的长度,取值范围:015,表示规则组的长度是116。本实验
    只用了1 个输入通道,所以L[3:0]位设置为0000 即可。
    SQ13[4:0]SQ16[4:0]位设置规则组序列的第1316 个转换编号,第1~12 个转换编号的设
    置请查看ADC_SQR2 和ADC_SQR3 寄存器。设置过程非常简单,忘记了请参考前面给大家整
    理出来的规则序列寄存器控制关系汇总表。
    本实验我们使用单通道,ADC1 通道14,所以规则组序列里只有一个输入通道,我们将
    ADC_SQR3 寄存器的SQ1[4:0]位的值设置为14 即可。
    ⚫ ADC 规则数据寄存器(ADC_DR)
    ADC 规则数据寄存器描述如图30.2.1.6 所示:

    在规则序列中AD 转换结果都将被存在这个寄存器里面,而注入通道的转换结果被保存在
    ADC_JDRx 里面。该寄存器的数据可以通过ADC_CR2 的ALIGN 位设置左对齐还是右对齐。
    在读取数据的时候要注意。
    ⚫ ADC 状态寄存器(ADC_SR)
    ADC 状态寄存器描述如图30.2.1.7 所示:

    该寄存器保存了ADC 转换时的各种状态。本实验我们通过EOC 位的状态来判断ADC 转
    换是否完成,如果查询到EOC 位被硬件置1,就可以从ADC_DR 寄存器中读取转换结果,否
    则需要等待转换完成。
    至此,本实验要用到的ADC 关键寄存器全部介绍完了,对于未介绍的部分,请大家参考
    《STM32F10xxx 参考手册_V10(中文版).pdf》第11 章相关内容。

    硬件设计

    1. 例程功能
      采集ADC1 通道1(PA1)上面的电压,并在LCD 模块上面显示ADC 规则数据寄存器12
      位的转换值以及将该值换算成电压后的电压值。使用杜邦线将ADC 和RV1 排针连接,使得PA1
      连接到电位器上,然后将ADC 采集到的数据和转换后的电压值在TFTLCD 屏中显示。用户可
      以通过调节电位器的旋钮改变电压值。LED0 闪烁,提示程序运行。

    2. 硬件资源
      1)LED 灯
      LED0 – PB5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)ADC1 :
      通道1 –PA1

    3. 原理图
      ADC 属于STM32F103 内部资源,实际上我们只需要软件设置就可以正常工作,另外还需
      要将待测量的电压源连接到ADC 通道上,以便ADC 测量。本实验,我们通过ADC1 的通道1
      (PA1)来采集外部电压值,开发板有一个电位器,可调节的电压范围是:0~3.3V。我们可以通
      过杜邦线将PA1 与电位器连接,如下图所示:

    使用杜邦线将ADC 和RV1 排针连接好后,并下载程序后,就可以用螺丝刀调节电位器变
    换多种电压值进行测量。
    有的朋友可能还想测量其它地方的电压值,我们只需要1 根杜邦线,一端接到ADC 排针
    上,另外一端就接你要测试的电压点。一定要保证测试点的电压在0~3.3V 的电压范围,否则可
    能烧坏我们的ADC,甚至是整个主控芯片。

    程序设计(还没拷贝完)

    30.2.3.1 ADC 的HAL 库驱动
    ADC 在HAL 库中的驱动代码在stm32f1xx_hal_adc.c 和stm32f1xx_hal_adc_ex.c 文件(及
    其头文件)中。

    1. HAL_ADC_Init 函数
      ADC 的初始化函数,其声明如下:
    HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef *hadc);
    

    ⚫ 函数描述:
    用于初始化ADC。
    ⚫ 函数形参:
    形参1 是ADC_HandleTypeDef 结构体类型指针变量,其定义如下:

    typedef struct
    {
    	ADC_TypeDef *Instance; /* ADC寄存器基地址*/
    	ADC_InitTypeDef Init; /* ADC参数初始化结构体变量*/
    	DMA_HandleTypeDef *DMA_Handle; /* DMA配置结构体*/
    	HAL_LockTypeDef Lock; /* ADC锁定对象*/
    	__IO uint32_t State; /* ADC工作状态*/
    	__IO uint32_t ErrorCode; /* ADC错误代码*/
    }ADC_HandleTypeDef;
    

    该结构体定义和其他外设比较类似,我们着重看第二个成员变量Init 含义,它是结构体
    ADC_InitTypeDef 类型,结构体ADC_InitTypeDef 定义为:

    typedef struct {
    	uint32_t DataAlign; /* 设置数据的对齐方式*/
    	uint32_t ScanConvMode; /* 扫描模式*/
    	FunctionalState ContinuousConvMode; /* 开启连续转换模式否则就是单次转换模式*/
    	uint32_t NbrOfConversion; /* 设置转换通道数目*/
    	FunctionalState DiscontinuousConvMode; /* 是否使用规则通道组间断模式*/
    	uint32_t NbrOfDiscConversion; /* 配置间断模式的规则通道个数*/
    	uint32_t ExternalTrigConv; /* ADC外部触发源选择*/
    } ADC_InitTypeDef;
    
    1. DataAlign:用于设置数据的对齐方式,这里可以选择右对齐或者是左对齐,该参数可选为:
      ADC_DATAALIGN_RIGHT 和ADC_DATAALIGN_LEFT。
    2. ScanConvMode:配置是否使用扫描。如果是单通道转换使用ADC_SCAN_DISABLE,如果
      是多通道转换使用ADC_SCAN_ENABLE。
    3. ContinuousConvMode:可选参数为ENABLE 和DISABLE,配置自动连续转换还是单次转换。
      使用ENABLE 配置为使能自动连续转换;使用DISABLE 配置为单次转换,转换一次后停止
      需要手动控制才重新启动转换。
    4. NbrOfConversion:指定规则组转换通道数目,范围是:1~16。
    5. DiscontinuousConvMode:配置是否使用规则通道组间断模式,比如要转换的通道有1、2、
      5、7、8、9,那么第一次触发会进行通道1 和2,下次触发就是转换通道5 和7,这样不连
      续的转换,依次类推。此参数只有将ScanConvMode 使能,还有ContinuousConvMode 失能
      的情况下才有效,不可同时使能。
    6. NbrOfDiscConversion:配置间断模式的通道个数,禁止规则通道组间断模式后,此参数忽略。
    7. ExternalTrigConv:外部触发方式的选择,如果使用软件触发,那么外部触发会关闭。
      ⚫ 函数返回值:
      HAL_StatusTypeDef 枚举类型的值。
    1. HAL_ADCEx_Calibration_Start 函数
      ADC 的自校准函数,其声明如下:
    HAL_StatusTypeDef HAL_ADCEx_Calibration_Start(ADC_HandleTypeDef *hadc);
    

    ⚫ 函数描述:
    首先调用HAL_ADC_Init 函数配置了相关的功能后,再调用此函数进行ADC 自校准功能。
    ⚫ 函数形参:
    ADC_HandleTypeDef 结构体类型指针变量。
    ⚫ 函数返回值:
    HAL_StatusTypeDef 枚举类型的值。
    3. HAL_ADC_ConfigChannel 函数
    ADC 通道配置函数,其声明如下:

    HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef *hadc,
    				ADC_ChannelConfTypeDef *sConfig);
    

    ⚫ 函数描述:
    调用了HAL_ADC_Init 函数配置了相关的功能后,就可以调用此函数配置ADC 具体通道。
    ⚫ 函数形参:
    形参1 是ADC_HandleTypeDef 结构体类型指针变量。
    形参2 是ADC_ChannelConfTypeDef 结构体类型指针变量,用于配置ADC 采样时间,使
    用的通道号,单端或者差分方式的配置等。该结构体定义如下:

    typedef struct {
    	uint32_t Channel; /* ADC转换通道*/
    	uint32_t Rank; /* ADC转换顺序*/
    	uint32_t SamplingTime; /* ADC采样周期*/
    } ADC_ChannelConfTypeDef;
    
    1. Channel:ADC 转换通道,范围:0~19。
    2. Rank:在常规转换中的常规组的转换顺序,可以选择1~16。
    3. SamplingTime:ADC 的采样周期,最大810.5 个ADC 时钟周期,要求尽量大以减少误差。
      ⚫ 函数返回值:
      HAL_StatusTypeDef 枚举类型的值。
    1. HAL_ADC_Start 函数
      ADC 转换启动函数,其声明如下:
    HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc);
    

    ⚫ 函数描述:
    当配置好ADC 的基础的功能后,就调用此函数启动ADC。
    ⚫ 函数形参:
    ADC_HandleTypeDef 结构体类型指针变量。
    ⚫ 函数返回值:
    HAL_StatusTypeDef 枚举类型的值。
    5. HAL_ADC_PollForConversion 函数
    等待ADC 规则组转换完成函数,其声明如下:

    HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef *hadc,
    										uint32_t Timeout);
    

    ⚫ 函数描述:
    一般先调用HAL_ADC_Start 函数启动转换,再调用该函数等待转换完成,然后再调用
    HAL_ADC_GetValue 函数来获取当前的转换值。
    ⚫ 函数形参:
    形参1 是ADC_HandleTypeDef 结构体类型指针变量。
    形参2 是等待转换的等待时间,单位是毫秒(ms)。
    ⚫ 函数返回值:
    HAL_StatusTypeDef 枚举类型的值。
    6. HAL_ADC_GetValue 函数
    获取常规组ADC 转换值函数,其声明如下:

    uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef *hadc);
    

    ⚫ 函数描述:
    一般先调用HAL_ADC_Start 函数启动转换,再调用HAL_ADC_PollForConversion 函数等
    待转换完成,然后再调用HAL_ADC_GetValue 函数来获取当前的转换值。
    ⚫ 函数形参:
    形参1 是ADC_HandleTypeDef 结构体类型指针变量。
    ⚫ 函数返回值:
    当前的转换值,uint32_t 类型数据。

    单通道ADC 采集配置步骤

    1)开启ADCx 和ADC 通道对应的IO 时钟,并配置该IO 为模拟功能
    首先开启ADCx 的时钟,然后配置GPIO 为模拟模式。本实验我们默认用到ADC1 通道1,
    对应IO 是PA1,它们的时钟开启方法如下:

    __HAL_RCC_ADC1_CLK_ENABLE(); /* 使能ADC1时钟*/
    __HAL_RCC_GPIOA_CLK_ENABLE(); /* 开启GPIOA时钟*/
    

    2)初始化ADCx,配置其工作参数
    通过HAL_ADC_Init 函数来设置ADCx 时钟分频系数、分辨率、模式、扫描方式、对齐方
    式等信息。
    注意:该函数会调用:HAL_ADC_MspInit 回调函数来存放ADC 及GPIO 时钟使能、GPIO
    初始化等代码。
    3)配置ADC 通道并启动AD 转换器
    在HAL 库中,通过HAL_ADC_ConfigChannel 函数来选择要配置ADC 的通道,并设置规
    则序列、采样时间等。
    配置好ADC 通道之后,通过HAL_ADC_Start 函数启动AD 转换器。
    4)读取ADC 值
    这里选择查询方式读取,在读取ADC 值之前需要调用HAL_ADC_PollForConversion 等待
    上一次转换结束。然后就可以通过HAL_ADC_GetValue 来读取ADC 值。

    程序流程图

    程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。ADC 驱动源码包
    括两个文件:adc.c 和adc.h。本章有四个实验,每一个实验的代码都是在上一个实验后面追加。
    adc.h 文件针对ADC 及通道引脚定义了一些宏定义,具体如下:

    /* ADC及引脚定义*/
    #define ADC_ADCX_CHY_GPIO_PORT GPIOA
    #define ADC_ADCX_CHY_GPIO_PIN GPIO_PIN_1
    #define ADC_ADCX_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOC_CLK_ENABLE();\
    }while(0) /* PA口时钟使能*/
    #define ADC_ADCX ADC1
    #define ADC_ADCX_CHY ADC_CHANNEL_1 /* 通道Y, 0 <= Y <= 16 */
    /* ADC1时钟使能*/
    #define ADC_ADCX_CHY_CLK_ENABLE() do{ __HAL_RCC_ADC1_CLK_ENABLE();}while(0)
    

    ADC 的通道与引脚的对应关系在《STM32F103ZET6.pdf》数据手册可以查到,我们这里使
    用ADC1 的通道1,在数据手册中的表格为:

    单通道ADC 采集(DMA 读取)实验

    本实验我们来学习单通道ADC 采集(DMA 读取)实验。本实验使用规则组单通道的连续
    转换模式,并且通过软件触发,即由ADC_CR2 寄存器的SWSTART 位启动。由于使用连续转
    换模式,所以使用DMA 读取转换结果的方式。下面先带大家来了解本实验要配置的寄存器。

    ADC & DMA 寄存器

    本实验我们很多的设置和单通道ADC 采集实验是一样的,所以下面介绍寄存器的时候我
    们不会继续全部都介绍,而是针对性选择与单通道ADC 采集实验不同设置的ADC_CR2 寄存
    器进行介绍,其他的配置基本一样的。另外因为我们用到DMA 读取数据,所以还会介绍如何配置相关DMA 的寄存器。
    ⚫ ADC 配置寄存器(ADC_CR2)
    ADCx 配置寄存器描述如图30.3.1.1 所示:


    ADC_CR2 寄存器中我们主要跟前面设置不同的有两个位,分别如下:
    DMA 位用于配置使用DMA 模式,本实验该位置1。在单通道ADC 采集实验中,默认设
    置为0,即不使用DMA 模式,规则组转换的结果存储在ADC_DR 寄存器,然后通过手动读取
    ADC_DR 寄存器的方式得到转换结果。本实验我们使用ADC 的连续转换模式,并通过DMA
    读取转换结果,这样DMA 就会自动在ADC_DR 寄存器中读取转换结果。
    CONT 位用于设置单次转换模式还是连续转换模式,本实验我们使用连续转换模式,所以
    CONT 位置1 即可。
    这里介绍ADC_CR2 寄存器的这两个位,其它请参考上一个实验的配置。下面介绍DMA
    一些比较重要的寄存器配置。
    ⚫ DMA 通道x 外设地址寄存器(DMA_CPARx)(x = 1…7)
    DMA 通道x 外设地址寄存器描述如图30.3.1.2 所示:

    该寄存器存放的是DMA 通道x 外设地址。本实验,我们需要通过DMA 读取ADC 转换后
    存放在ADC 规则数据寄存器(ADC_DR) 的结果数据。所以我们需要给DMA_CPARx 寄存器
    写入ADC_DR 寄存器的地址。这样配置后,DMA 就会从ADC_DR 寄存器的地址读取ADC 的
    转换后的数据到某个内存空间。这个内存空间地址需要我们通过DMA_CMARx寄存器来设置,
    比如定义一个变量,把这个变量的地址值写入该寄存器。
    注意:DMA_CPARx 寄存器受到写保护,只有DMA_CCRx 寄存器中的EN 为“0”时才可
    以写入,即先要禁止通道开启才可以写入。
    ⚫ DMA 通道x 存储器地址寄存器(DMA_CMARx)(x = 1…7)
    DMA 通道x 存储器地址寄存器描述如图30.3.1.3 所示:


    该寄存器存放的是DMA 通道x 存储器地址。同样的,该寄存器也是受写保护,只有当
    DMA_CCRx 的EN 位为0 时才可以写入。
    ⚫ DMA 通道x 传输数量寄存器(DMA_CNDTRx)(x = 1…7)
    DMA 通道x 传输数量寄存器描述如图30.3.1.4 所示:

    该寄存器控制着DMA 通道x 的每次传输所要传输的数据量。其设置范围为0~65535。并
    且该寄存器的值随着传输的进行而减少,当该寄存器的值为0 的时候就代表此次数据传输已经
    全部发送完成。所以可以通过这个寄存器的值来获取当前DMA 传输的进度。
    其它的DMA 寄存器我们就不一一介绍了,请大家看着寄存器源码对照手册理解,都不难。

    硬件设计

    1. 例程功能
      使用ADC 采集(DMA 读取)通道1(PA1)上面的电压,在LCD 模块上面显示ADC 转
      换值以及换算成电压后的电压值。使用短路帽将ADC 和RV1 排针连接,使得PA1 连接到电位
      器上,然后将ADC 采集到的数据和转换后的电压值在TFTLCD 屏中显示。用户可以通过调节
      电位器的旋钮改变电压值。LED0 闪烁,提示程序运行。
    2. 硬件资源
      1)LED 灯
      LED0 –PB5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)ADC :通道1 –PA1
      5)DMA(DMA1 通道1)
    3. 原理图
      ADC 属于STM32F103 内部资源,实际上我们只需要软件设置就可以正常工作,另外还需
      要将待测量的电压源连接到ADC 通道上,以便ADC 测量。本实验,我们通过ADC1 的通道1
      (PA1)来采集外部电压值,开发板有一个电位器,可调节的电压范围是:0~3.3V。我们可以通
      过杜邦线将PA1 与电位器连接,如下图所示:


    使用杜邦线将ADC 和RV1 排针连接好后,并下载程序后,就可以用螺丝刀调节电位器变
    换多种电压值进行测量。
    有的朋友可能还想测量其它地方的电压值,我们只需要1 根杜邦线,一端接到ADC 排针
    上,另外一端就接你要测试的电压点。一定要保证测试点的电压在0~3.3V 的电压范围,否则可
    能烧坏我们的ADC,甚至是整个主控芯片。

    多通道ADC 采集(DMA 读取)实验

    本实验我们来学习多通道ADC 采集(DMA 读取)实验。本实验使用规则组多通道的连续
    转换模式,并且通过软件触发,即由ADC_CR2 寄存器的SWSTART 位启动。由于使用连续转
    换模式,所以使用DMA 读取转换结果的方式。下面先带大家来了解本实验要配置的寄存器。

    ADC 寄存器

    本实验我们很多的设置和单通道ADC 采集(DMA 读取)实验是一样的,所以下面介绍寄
    存器的时候我们不会继续全部都介绍,而是针对性选择与单通道ADC 采集(DMA 读取)实验
    不同设置的ADC_SQRx 寄存器进行介绍,其他的配置基本一样的。另外我们用到DMA 读取数据,配置上和单通道ADC 采集(DMA 读取)实验是一样的。
    ADC 规则序列寄存器有四个(ADC_SQR1~ ADC_SQR3),具体怎么配置,需要看我们用多少个通道,比如本实验我们使用6 个通道同时采集ADC 数据,具体配置如下:
    ⚫ ADC 规则序列寄存器1(ADC_SQR1)
    ADC 规则序列寄存器1 描述如图30.4.1.1 所示:

    L[3:0]位用于设置规则序列的长度,取值范围:015,表示规则序列长度为116。本实验
    使用到6 个通道,所以设置这几个位的值为5 即可。
    SQ13[4:0]SQ16[4:0]位设置规则组序列的第1316 个转换编号,第1~12 个转换编号的设
    置请查看ADC_SQR2 和ADC_SQR3 寄存器。设置过程非常简单,忘记了请参考前面给大家整
    理出来的规则序列寄存器控制关系汇总表。
    下面我们来看看本实验是怎么设置的:SQ1[4:0]位赋值为0、SQ2[4:0]位赋值为1、SQ3[4:0]
    位赋值为2、SQ4[4:0]位赋值为3、SQ5[4:0]位赋值为4、SQ6[4:0]位赋值为5,即规则序列1 到
    6 分别对应的输入通道是0 到5。SQ1~SQ6 位都是在ADC_SQR3 寄存器中配置。

    硬件设计

    1. 例程功能
      使用ADC1 采集(DMA 读取)通道1\2\3\4\5\6 的电压,在LCD 模块上面显示对应的ADC
      转换值以及换算成电压后的电压值。可以使用杜邦线连接PA0\PA1\PA2\PA3\PA4\PA5 到你想测
      量的电压源(0~3.3V),然后通过TFTLCD 显示的电压值。LED0 闪烁,提示程序运行。

    2. 硬件资源
      1)LED 灯
      LED0 –PE5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)ADC1 :通道1–PA0、通道2–PA1、通道3–PA2、
      通道4–PA3、通道5–PA4、通道6–PA5
      5)DMA(DMA1 通道1)

    3. 原理图
      ADC 和DMA 属于STM32F103 内部资源,实际上我们只需要软件设置就可以正常工作,
      另外还需要将待测量的电压源连接到ADC 通道上,以便ADC 测量。本实验,我们通过ADC1
      的通道1\2\3\4\5\6 来采集外部电压值,并通过DMA 来读取。

    单通道ADC 过采样(16 位分辨率)实验

    本实验我们来学习单通道ADC 过采样(16 位分辨率)实验。STM32F103 自带的ADC 分
    辨率只有12 位,虽然可以满足一般的应用,但是有些场合可能需要更高的分辨率,怎么办呢?
    可以使用外部专用的ADC,或者换一个带更高分辨率ADC 的主控芯片。这样做往往会增加额
    外的成本,那么有没有其它办法呢?答案是有的,可以通过引入过采样技术来实现。
    ADC 过采样技术,是利用ADC 多次采集的方式,来提高ADC 分辨率。下面,简单介绍一
    下怎么提高ADC 测量的分辨率?
    下面直接给大家介绍一个方程,根据要增加分辨率计算过采样频率方程,方程如下:
    fos = 4w ⋅ fs

    其中,w 是希望增加的分辨率位数,fs是初始采样频率要求,fos是过采样频率。
    方程的推导过程比较复杂,这里就不带大家去推导,感兴趣的朋友可以通过下面这个链接
    自行学习:https://max.book118.com/html/2018/0506/165038217.shtm。
    由该方程可以知道,采样速度每提高4 倍,分辨率位数可以提高1 位。结合ADC 的实际
    情况,换个思路来说,分辨率位数每提高1 位,如果采样频率不变的情况下,那么采样速度就
    会降低4 倍。本实验要求得到16 位分辨率,即需要增加4 位分辨率,那么采样速度就会降低
    256 倍,即需要采集256 次才能得出1 次数据,相当于ADC 速度慢了256 倍。
    理论上只要ADC 足够快,我们可以无限提高ADC 精度,但实际上ADC 并不是无限快的,
    而且由于ADC 性能限制,并不是位数无限提高,结果就越好,需要根据自己的实际需求和ADC
    的实际性能来权衡的。
    下面来看一下我们怎么实现单通道ADC 过采样(16 位分辨率)实验的?。

    ADC 寄存器

    本实验我们很多的设置和单通道ADC 采集(DMA 读取)实验是一样的,代码实现也是基
    于该实验实现的,寄存器的介绍请参考前面的ADC 实验。

    硬件设计

    1. 例程功能
      使用ADC1 通道1(PA1),通过软件方式实现16 位分辨率采集外部电压,并在LCD 模块
      上面显示对应的ADC 转换值以及换算成电压后的电压值。可以使用杜邦线连接PA1 到你想测
      量的电压源(0~3.3V),然后通过TFTLCD 显示的电压值。LED0 闪烁,提示程序运行。
    2. 硬件资源
      1)LED 灯
      LED0 –PB5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)ADC1 :通道1 –PA1
    3. 原理图
      ADC 属于STM32F103 内部资源,实际上我们只需要软件设置就可以正常工作,另外还需
      要将待测量的电压源连接到ADC 通道上,以便ADC 测量。本实验,我们通过ADC1 通道1
      (PA1)来采集外部电压值。开发板有一个电位器,可调节的电压范围是:0~3.3V,可以通过断
      路帽将PA1 与电位器连接,从而测量电位器的电压。

    内部温度传感器实验

    本章,我们将介绍STM32F103 的内部温度传感器并使用它来读取温度值,然后在LCD 模
    块上显示出来。

    内部温度传感器简介

    STM32 有一个内部的温度传感器,可以用来测量CPU 及周围的温度(内部温度传感器更
    适合于检测温度的变化,需要测量精确温度的情况下,应使用外置传感器)。对于STM32F103
    来说,该温度传感器在内部和ADC1_IN16 输入通道相连接,此通道把传感器输出的电压转换
    成数字值。温度传感器模拟输入推荐采样时间是17.1us。STM32F103 内部温度传感器支持的温
    度范围为:-40~125 度。精度为±1.5℃左右。
    STM32 内部温度传感器的使用很简单,只要设置一下内部ADC,并激活其内部温度传感
    器通道就差不多了。关于ADC 的设置,我们在上一章已经进行了详细的介绍,这里就不再多
    说。接下来我们介绍一下和温度传感器设置相关的两个地方。
    第一个地方,我们要使用STM32 的内部温度传感器,必须先激活ADC 的内部通道,这里
    通过ADC_CR2 的AWDEN 位(bit23)设置。设置该位为1 则启用内部温度传感器。
    第二个地方,STM32 的内部温度传感器固定的连接在ADC1 的通道16 上,所以,我们在
    设置好ADC1 之后只要读取通道16 的值,就是温度传感器返回来的电压值了。根据这个值,
    我们就可以计算出当前温度。计算公式如下:

    硬件设计

    1. 例程功能
      通过ADC 的通道16 读取STM32F103 内部温度传感器的电压值,并将其转换为温度值,
      显示在TFTLCD 屏上。LED0 闪烁用于提示程序正在运行。
    2. 硬件资源
      1)LED 灯
      LED0 –PB5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)ADC1 通道16
      5)内部温度传感器
    3. 原理图
      ADC 和内部温度传感器都属于STM32F103 内部资源,实际上我们只需要软件设置就可以正常工作,我们需要用到TFTLCD 模块显示结果。

    光敏传感器实验

    本章,我们将学习使用STM32 开发板板载的一个光敏传感器。我们还是要使用到ADC 采
    集,通过ADC 采集电压,获取光敏传感器的电阻变化,从而得出环境光线的变化,并在TFTLCD
    上面显示出来。

    光敏传感器简介

    光敏传感器是最常见的传感器之一,它的种类繁多,主要有:光电管、光电倍增管、光敏
    电阻、光敏三极管、太阳能电池、红外线传感器、紫外线传感器、光纤式光电传感器、色彩传
    感器、CCD 和CMOS 图像传感器等。光传感器是目前产量最多、应用最广的传感器之一,它在
    自动控制和非电量电测技术中占有非常重要的地位。
    光敏传感器是利用光敏元件将光信号转换为电信号的传感器,它的敏感波长在可见光波长
    附近,包括红外线波长和紫外线波长。光传感器不只局限于对光的探测,它还可以作为探测元
    件组成其他传感器,对许多非电量进行检测,只要将这些非电量转换为光信号的变化即可。
    STM32F103 战舰开发板板载了一个光敏二极管(光敏电阻),作为光敏传感器,它对光的
    变化非常敏感。光敏二极管也叫光电二极管。光敏二极管与半导体二极管在结构上是类似的,
    其管芯是一个具有光敏特征的PN 结,具有单向导电性,因此工作时需加上反向电压。无光照
    时,有很小的饱和反向漏电流,即暗电流,此时光敏二极管截止。当受到光照时,饱和反向漏
    电流大大增加,形成光电流,它随入射光强度的变化而变化。当光线照射PN 结时,可以使PN
    结中产生电子一空穴对,使少数载流子的密度增加。这些载流子在反向电压下漂移,使反向电
    流增加。因此可以利用光照强弱来改变电路中的电流。
    利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过ADC 读取电
    压值,判断外部光线的强弱。
    本章,我们利用ADC3 的通道6(PF8)来读取光敏二极管电压的变化,从而得到环境光线
    的变化,并将得到的光线强度,显示在TFTLCD 上面。关于ADC 的介绍,前面已经有详细介
    绍了,这里我们就不再细说了。

    硬件设计

    1. 例程功能
      通过ADC3 的通道6(PF8)读取光敏传感器(LS1)的电压值,并转换为0~100 的光线强
      度值,显示在LCD 模块上面。光线越亮,值越大;光线越暗,值越小。大家可以用手指遮挡LS1
      和用手电筒照射LS1,来查看光强变化。LED0 闪烁用于提示程序正在运行。
    2. 硬件资源
      1)LED 灯
      LED : LED0 –PB5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)ADC3 :通道6 –PF8
      5)光敏传感器
    3. 原理图
      我们主要来看看光敏传感器和开发板的连接,如下图所示:

      图中,LS1 是光敏二极管,外观看起像与贴片LED 类似(战舰位于OLED 插座旁边,LS1),
      R34 为其提供反向电压,当环境光线变化时,LS1 两端的电压也会随之改变,通过ADC3_IN6
      通道读取LIGHT_SENSOR(PF8)上面的电压,即可得到环境光线的强弱。光线越强,电压越
      低,光线越暗,电压越高。

    DAC 实验

    本章,我们将介绍STM32F103 的DAC(Digital -to- analog converters,数模转换器)功能。
    我们通过三个实验来学习DAC,分别是DAC 输出实验、DAC 输出三角波实验和DAC 输出正
    弦波实验。

    DAC 简介

    STM32F103 的DAC 模块(数字/模拟转换模块)是12 位数字输入,电压输出型的DAC。
    DAC 可以配置为8 位或12 位模式,也可以与DMA 控制器配合使用。DAC 工作在12 位模式
    时,数据可以设置成左对齐或右对齐。DAC 模块有2 个输出通道,每个通道都有单独的转换器。
    在双DAC 模式下,2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新2 个通道
    的输出。DAC 可以通过引脚输入参考电压Vref+以获得更精确的转换结果。
    STM32 的DAC 模块主要特点有:
    ①2 个DAC 转换器:每个转换器对应1 个输出通道
    ②8 位或者12 位单调输出
    ③12 位模式下数据左对齐或者右对齐
    ④同步更新功能
    ⑤噪声\三角波形生成
    ⑥双DAC 双通道同时或者分别转换
    ⑦每个通道都有DMA 功能
    DAC 通道框图如图33.1.1 所示:
    图33.1.1 DAC 通道框图

    图中VDDA 和VSSA 为DAC 模块模拟部分的供电,而VREF+则是DAC 模块的参考电压。
    DAC_OUTx 就是DAC 的两个输出通道了(对应PA4 或者PA5 引脚)。ADC 的这些输入/输出
    引脚信息如下表所示:

    从图33.1.1 可以看出,DAC 输出是受DORx(x=1/2,下同)寄存器直接控制的,但是我们
    不能直接往DORx 寄存器写入数据,而是通过DHRx 间接的传给DORx 寄存器,实现对DAC
    输出的控制。
    前面我们提到,STM32F103 的DAC 支持8/12 位模式,8 位模式的时候是固定的右对齐的,
    而12 位模式又可以设置左对齐/右对齐。DAC 单通道模式下的数据寄存器对齐方式,总共有3
    种情况,如下图所示:

    ①8 位数据右对齐:用户将数据写入DAC_DHR8Rx[7:0]位(实际存入DHRx[11:4]位)。
    ②12 位数据左对齐:用户将数据写入DAC_DHR12Lx[15:4]位(实际存入DHRx[11:0]位)。
    ③12 位数据右对齐:用户将数据写入DAC_DHR12Rx[11:0]位(实际存入DHRx[11:0]位)。
    我们本章实验中使用的都是单通道模式下的DAC 通道1,采用12 位右对齐格式,所以采
    用第③种情况。另外DAC 还具有双通道转换功能。
    对于DAC 双通道(可用时),也有三种可能的方式,如下图所示:

    ①8 位数据右对齐:用户将DAC 通道1 的数据写入DAC_DHR8RD[7:0]位(实际存入
    DHR1[11:4]位),将DAC 通道2 的数据写入DAC_DHR8RD[15:8]位(实际存入
    DHR2[11:4]位)。
    ②12 位数据左对齐:用户将DAC 通道1 的数据写入DAC_DHR12LD[15:4]位(实际存入
    DHR1[11:0]位),将DAC 通道2 的数据写入DAC_DHR12LD[31:20]位(实际存入
    DHR2[11:0]位)。
    ③12 位数据右对齐:用户将DAC 通道1 的数据写入DAC_DHR12RD[11:0]位(实际存入
    DHR1[11:0]位),将DAC 通道2 的数据写入DAC_DHR12RD[27:16]位(实际存入
    DHR2[11:0]位)。
    DAC 可以通过软件或者硬件触发转换,通过配置TENx 控制位来决定。
    如果没有选中硬件触发(寄存器DAC_CR 的TENx 位置0),存入寄存器DAC_DHRx 的数
    据会在1 个APB1 时钟周期后自动传至寄存器DAC_DORx。如果选中硬件触发(寄存器DAC_CR
    的TENx 位置1),数据传输在触发发生以后3 个APB1 时钟周期后完成。一旦数据从DAC_DHRx寄存器装入DAC_DORx 寄存器,在经过时间tSETTLING之后,输出即有效,这段时间的长短依电
    源电压和模拟输出负载的不同会有所变化。我们可以从《《STM32F103ZET6.pdf》数据手册查到
    tSETTLING的典型值为3us,最大是4us,所以DAC 的转换速度最快是333K 左右。
    不使用硬件触发(TEN=0),其转换的时间框图如图33.1.4 所示:


    当DAC 的参考电压为Vref+的时候,DAC 的输出电压是线性的从0~Vref+,12 位模式下
    DAC 输出电压与Vref+以及DORx 的计算公式如下:

    DACx 输出电压= Vref *(DORx/4096)
    

    如果使用硬件触发(TENx=1),可通过外部事件(定时计数器、外部中断线)触发DAC 转
    换。由TSELx[2:0]控制位来决定选择8 个触发事件中的一个来触发转换。触发事件如下表所示:

    原表见《STM32F10xxx 参考手册_V10(中文版).pdf》第185 页表71。
    每个DAC 通道都有DMA 功能,两个DMA 通道分别用于处理两个DAC 通道的DMA 请
    求。如果DMAENx 位置1 时,如果发生外部触发(而不是软件触发),就会产生一个DMA 请
    求,然后DAC_DHRx 寄存器的数据被转移到DAC_NORx 寄存器。

    DAC 输出实验

    本实验我们来学习DAC 输出实验。

    DAC 寄存器

    下面,我们介绍要实现DAC 的通道1 输出,需要用到的一些DAC 寄存器。
    ⚫ DAC 控制寄存器(DAC_CR)
    DAC 控制寄存器描述如图33.2.1.1 所示:

    DAC_CR 的低16 位用于控制通道1,高16 位用于控制通道2,下面介绍本实验需要设置
    的一些位:
    EN1 位用于DAC 通道1 的使能,我们需要用到DAC 通道1 的输出,该位必须设置为1。
    BOFF1 位用于DAC 输出缓存控制,这里STM32 的DAC 输出缓存做的有些不好,如果使
    能的话,虽然输出能力强一点,但是输出没法到0,这是个很严重的问题。所以本章的三个实
    验我们都不使用输出缓存,即该位设置为1。
    TEN1 位用于DAC 通道1 的触发使能,我们设置该位为0,不使用触发。写入DHR1 的值
    会在1 个APB1 周期后传送到DOR1,然后输出到PA4 口上。
    TSEL1[2:0]位用于选择DAC 通道1 的触发方式,这里我们没有用到外部触发,所以这几位
    设置为0 即可。
    WAVE1[1:0]位用于控制DAC 通道1 的噪声/波形输出功能,我们这里没用到波形发生器,
    所以默认设置为00,不使能噪声/波形输出。
    MAMP[3:0]位是屏蔽/幅值选择器,用来在噪声生成模式下选择屏蔽位,在三角波生成模式
    下选择波形的幅值。本实验没有用到波形发生器,所以设置为0 即可。

    DMAEN1 位用于DAC 通道1 的DMA 使能,本实验没有用到DMA 功能,所以设置为0。
    ⚫ DAC 通道1 12 位右对齐数据保持寄存器(DAC_DHR12R1)
    DAC 通道1 的12 位右对齐数据保持寄存器描述如图33.2.1.3 所示:

    该寄存器用来设置DAC 输出,通过写入12 位数据到该寄存器,就可以在DAC 输出通道
    1(PA4)得到我们所要的结果。

    硬件设计

    1. 例程功能
      使用KEY1/KEY_UP 两个按键,控制STM32 内部DAC 的通道1 输出电压大小,然后通过
      ADC2 的通道14 采集DAC 输出的电压,在LCD 模块上面显示ADC 采集到的电压值以及DAC
      的设定输出电压值等信息。也可以通过usmart 调用dac_set_voltage 函数,来直接设置DAC 输
      出电压。LED0 闪烁,提示程序运行。
    2. 硬件资源
      1)LED 灯LED0 –PB5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)独立按键:KEY1 – PE3、WK_UP – PA0
      5)ADC3 :通道1 –PA1
      6)DAC1 :通道1 –PA4
    3. 原理图
      我们来看看原理图上ADC3 通道1(PA1)和DAC1 通道1(PA4)引出来的引脚,如下图
      所示:


    我们只需要通过跳线帽连接ADC 和DAC,就可以使得ADC3 通道1(PA1)和DAC 1 通
    道1(PA4)连接起来。对应的硬件连接如图33.2.2.2 所示:

    DAC 输出三角波实验

    本实验我们来学习使用如何让DAC 输出三角波,DAC 初始化部分还是用DAC 输出实验
    的,所以做本实验的前提是先学习DAC 输出实验。

    DAC 寄存器

    本实验用到的寄存器在DAC 输出实验都有介绍。

    硬件设计

    1. 例程功能
      使用DAC 输出三角波,通过KEY0/KEY1 两个按键,控制DAC1 的通道1 输出两种三角
      波,需要通过示波器接PA4 进行观察。也可以通过usmart 调用dac_triangular_wave 函数,来控
      制输出哪种三角波。LED0 闪烁,提示程序运行。
    2. 硬件资源
      1)LED 灯
      LED0 –PB5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)独立按键:KEY0 –PE4、KEY1 –PE3
      5)DAC1 :通道1 – PA4
    3. 原理图
      我们只需要把示波器的探头接到DAC1 通道1(PA4)引脚,就可以在示波器上显示DAC
      输出的波形。PA4 对应P10 的DAC 排针,如图33.3.2.1 所示:

    DAC 输出正弦波实验

    本实验我们来学习使用如何让DAC 输出正弦波。实验将用定时器7 来触发DAC 进行转换
    输出正弦波,以DMA 传输数据的方式。

    DAC 寄存器

    本实验用到的寄存器在前面的实验都有介绍。

    硬件设计

    1. 例程功能
      使用DAC 输出正弦波,通过KEY0/KEY1 两个按键,控制DAC1 的通道1 输出两种正弦
      波,需要通过示波器接PA4 进行观察。TFTLCD 显示DAC 转换值、电压值和ADC 的电压值。
      LED0 闪烁,提示程序运行。
    2. 硬件资源
      1)LED 灯
      LED0 –PB5
      2)串口1(PA9/PA10 连接在板载USB 转串口芯片CH340 上面)
      3)正点原子2.8/3.5/4.3/7/10 寸TFTLCD 模块(仅限MCU 屏,16 位8080 并口驱动)
      4)独立按键:KEY0 –PE4、KEY1 –PE3
      5)ADC1 :通道1 –PA1
      6)DAC1 :通道1 –PA4
      7)DMA(DMA2_Channel13)
      8)定时器7
    3. 原理图
      我们只需要把示波器的探头接到DAC1 通道1(PA4)引脚,就可以在示波器上显示DAC
      输出的波形。PA4 对应P10 的DAC 排针,硬件连接如图33.4.2.1 所示:
    物联沃分享整理
    物联沃-IOTWORD物联网 » .5.2版本移植FreeRTOS正点原子STM32:基于HAL库的FreeRTOS 4.5.2版本移植

    发表评论