目录

1.  简介

2.  逐次逼近型ADC(ADC0809)

3.  功能框图(LKS32)

4.  ADC基本结构

4.1  量词含义约定

4.2  触发方式

4.3  ADC 通道选择

4.4  中断配置

4.5  ADC 输出数制

4.6  ADC 量程

4.7  ADC 阈值监测

5.  库函数

5.1  ADC_TypeDef

5.2  ADC_InitTypeDef

5.3  API库函数

6.  软件触发代码编写

6.1  引脚初始化

6.2  ADC初始化

6.3  主函数部分


1.  简介

        ADC全称是(Analog-to-Digital Converter)模拟-数字转换器,一般我们把模拟信号(Analog signal) 用A来进行简写,数字信号(digital signal) 用D来表示。主要用于将连续传输的模拟信号转换为数字信号,便于数字系统(如中央处理器CPU、微控制器MCU等)对传输信息进行快速处理和分析。

         模拟信号是指用连续变化的物理量所表达的信息,如温度、湿度、压力、电压、电流等。ADC模块所采集的模拟信号是连续变化的电压或电流信号,其数值在一定范围内连续变化,如下所示:

        在单片机的使用中,我们可以通过ADC的转换将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。

         ADC采样的实现方式主要有两种:外接采样芯片控制核心内部的采样模块。以前的一些芯片是外挂ADC芯片,不过现在许多MCU和DSP内部集成了ADC模块。

        对于LKS32MC07x 系列芯片,其内部集成了 2 个 12bit SAR-ADC 转换核心,14 个模拟 IO 输入信号、4 个 OPA输出、2 个 DAC 输出、温度传感器输出、内部电源 AVDD 均可作为 ADC 的被采样信号。

        不过我们还是来了解一下外挂芯片的原理,方便理解。

2.  逐次逼近型ADC(ADC0809)

        我们先来了解一下ADC0809的工作原理。

        首先是通道选择开关,通过通道选择开关,选择(IN0~IN7)其中的一路,输入到这个点进行转换,对于0809只有8路选:

        然后是地址锁存和译码,这里你想要选择哪一路,需要将通道号放到(ADDA、ADDB、ADDC)这三个脚上,然后通过ALE给出锁存信号,对应的通道选择开关就会自动拨好了:

        比较器是一个电压比较器,他可以判断两个输入信号电压的大小关系,其中通道选择开关输入的是待测的电压,另一端是DAC(数模转换器)的电压:

        这里待测电压是未知的,但是DAC所出的电压是我们通过数模转换来的,其编码是已知的,我们通过比较器进行比较,当待测电压大于DAC所示电压,我们就调大DAC的值,如果待测电压小于DAC所示电压,我们就调小DAC的值,直到待测电压等于DAC所示电压,这样DAC的输入数据就是待测电压的数据,我们就可以知道待测电压的编码(类似于二分法)。

        得出待测电压的编码通过三态锁存缓冲器进行输出,8位就有8根线,12位就有12根:

3.  功能框图(LKS32)

        每个 ADC 接口包括 16 个数据寄存器(ADC 16 次采样各个通道模拟量对应的数字量),以及若干控制寄存器,14 个常规触发采样寄存器,2 个空闲采样触发寄存器。可以同一时刻进行 2 个通道的采样:

        拆分了解一下,首先是输入部分,其中最左侧代表多路输入选择器,其中:

  • OPA0/1/2/3:4 路片内运算放大器输出
  • ADC Channels:14 路外部模拟 IO 通道
  • Temperature Sensor:芯片内置温度传感器
  • AVDD:模拟电源内部采样

        S/H代表:Sample/Hold,采样/保持。在采集阶段对外部电压采样电容充电;ADC 转换阶段保持电压稳定,保证转换期间输入电压不变。

        SAR‑ADC 内核:12 位逐次逼近 ADC,把模拟电压转换成数字量输出。


ADC 的启动触发来源:

  • Hardware trigger 硬件触发:来自定时器 UTimer/MCPWM 输出 T0/T1/T2/T3。定时器事件自动启动 ADC 转换,常用于电机控制,PWM 中点同步采样,不需要 CPU 软件干预。
  • Software trigger 软件触发:寄存器写位,软件手动启动一次 ADC 转换。


Sampling Control logic 采样控制状态机:

  • Sample State Control:采样状态机,管理采样、保持、转换时序
  • Trigger counter:触发计数器,统计触发事件
  • Channel Select:通道选择,输出信号给左侧模拟多路开关,选哪一路模拟输入

        对于最上面几个操作主要为了稳定最终采集数据:

  • -16'h8000(0):偏移补偿
  • -ADC_DC:直流偏移校准,修正 ADC 零点误差
  • ADC_AMC:增益校准,修正 ADC 增益误差
  • 乘法器、饱和溢出处理:处理采样结果溢出 / 下溢,防止数值越界。

        经过偏移、增益校准之后才输出最终采样数字值。

        最右侧部分,是数据寄存器组:

  • ADC_IDAT0~IDAT1:中间数据缓存
  • ADC_DAT0 ~ ADC_DAT13 共 14 个(加上 IDAT 合计 16 个数据寄存器),对应知识点:每个 ADC 有 16 个数据寄存器保存多次采样结果。
  • ADC_IF:转换完成中断标志位。

        左侧是存放全部 ADC 配置寄存器:

  • ADC_CHN0 ...:通道号配置寄存器,选择要采样的模拟通道
  • ADC_TRIG:触发源配置(选择软件触发 / 哪个定时器硬件触发)
  • ADC_CFG:ADC 功能配置,采样时间、使能、模式等
  • ADC_CHNT:通道计数,配置一轮要采集多少个通道
  • ADC_SWT:软件触发寄存器,写该寄存器软件启动 ADC
  • AMC/DC:增益、直流偏移校准参数寄存器。

        右侧是中断产生逻辑,转换完成后,若中断使能打开,就向 NVIC 申请中断,通知 CPU 读取采样结果。

  • ADC_IE[n]:中断使能位;ADC_IF[n]:中断标志位
  • 与门逻辑:标志位 & 使能位 → 输出IRQ中断信号送给 NVIC。

        完整的工作流程:

  • CPU 通过总线配置adc_reg寄存器:设置采样通道列表、选择触发源(定时器硬件触发 / 软件触发)、开启中断。
  • 触发事件到来:
    • 硬件:UTimer/MCPWM 定时器事件;或者
    • 软件:写ADC_SWT软件触发位。
  • Sampling Control logic状态机工作,输出Channel Select,选通一路模拟输入;控制 S/H 采样保持电路。
  • SAR‑ADC 完成模数转换;数字结果做 DC 偏移、AMC 增益校准、饱和处理。
  • 转换结果存入ADC_DATx数据寄存器。置ADC_IF中断标志。
  • 如果中断使能位打开,产生 IRQ 信号给到 NVIC,进入 ADC 中断服务函数。
  • CPU/DMA 通过 BUS 读取ADC_DATx拿到采样值;软件写对应标志位,清除中断标志。

4.  ADC基本结构

4.1  量词含义约定

        1 次采样:完成对应的一次模拟信号量到数字信号量的采样转换并存储至 ADC_DATx 寄存器;

        1 段采样:可能包含 1 次或若干次采样,若干次采样可以是相同的模拟信号,也可以是不同的模拟信号。采样开始通常由MCPWM、TIMER 或软件进行触发,一个触发信号完成一段采样,采样完成后产生相应的段采样完成中断。

名词定义关键要点
次采样一次模拟→数字转换,结果存入 1 个ADC_DATxADC 最小执行单元;N 次采样结果存ADC_DAT[N]
段采样1 个触发信号启动的一组采样任务一段内可 1 次 / 多次次采样;可同通道重复采样;整段全部次采样做完才产生段完成中断

4.2  触发方式

        ADC 触发分为常规触发(NT: Normal Trigger)空闲触发(IT: Idle Trigger)两种触发。常规触发比空闲触发有更高优先级。

  • 如果 ADC 正在进行常规采样转换,此时发生了空闲触发,则 ADC 会继续完成常规触发的通道采样转换,完成后再进行空闲触发采样转换;
  • 如果 ADC 正在进行空闲采样转换,此时发生了常规触发,则常规触发会打断空闲采样转换,等常规触发采样完成后再进行空闲触发。

        NT 常规触发 Normal Trigger 高优先级,用于业务必需采样,电机电流、传感器信号等实时任务。 对应硬件,14 个常规触发采样寄存器,由 MCPWM/UTimer 硬件触发或者软件触发。

        IT 空闲触发 Idle Trigger 低优先级,后台非紧急采样,芯片温度、AVDD 电源电压等,不要求严格实时。 对应硬件,2 个空闲采样触发寄存器;一般在系统空闲的时候执行。

触发类型优先级寄存器资源行为特性适用场景
NT 常规触发14 个常规采样寄存器可抢占空闲触发;空闲采样运行时,NT 到来立刻打断空闲,先执行 NT,之后恢复空闲;NT 运行时 IT 到来,IT 排队等待电机电流、实时传感器
IT 空闲触发2 个空闲采样寄存器不能抢占常规触发;仅系统空闲时执行后台监测温度、AVDD 电压

        触发方式:

  • 支持单段触发和两段触发
  • 可以设置采样触发事件发生次数从而实现间隔触发
  • 可以软件触发
  • 每段采样触发完成可产生对应中断标志,如果使能中断请求,则会向 NVIC 产生中断服务请求
  • 触发指示信号可以通过 GPIO 送出用于调试
  • 支持空闲触发采样

        其手册配置寄存器如下:

        对于常规触发部分我们细化一下:

        软件触发:软件触发,向 ADC_SWT 写入 0x5AA5 可以产生一次软件触发信号。

        UTIME 触发:硬件触发,ADC 的触发来源为 UTimer 的比较事件,ADC 四段触发采样事件对应 UTimer_T0/UTimer_T1/UTimer_T2/UTimer_T3;其依次对应 Timer2 通道 0/1、Timer3 通道 0/1 的比较事件。

        MCPWM 触发:硬件触发,MCPWM 可以提供 ADC 采样控制。当计数器计数到 MCPWM_TMR0/MCPWM_TMR1/MCPWM_TMR2/MCPWM_TMR3 时,可产生定时事件 MCPWM_T0/MCPWM_T1/MCPWM_T2/MCPWM_T3,触发 ADC 采样。该触发信号可通过 ADC_TRIGGER 输出到 GPIO,便于调试之用。ADC 触发信号在芯片内部为一个 ADC 时钟周期的窄脉冲。每发生一次 ADC 触发,ADC_TRIGGER 信号翻转一次,以便于输出捕捉。

触发源触发方式关键说明
软件触发写寄存器ADC_SWT = 0x5AA5写魔法字,产生 1 次触发,启动一段常规采样
UTIMER 硬件触发定时器比较事件UTimer_T0~T3 依次对应:Timer2_CH0、Timer2_CH1、Timer3_CH0、Timer3_CH1 比较事件
MCPWM 硬件触发PWM 计到 MCPWM_TMR0‑3 产生事件1 个 ADC 时钟窄脉冲;可输出 GPIOADC_TRIGGER调试;每触发一次引脚电平翻转

        需要注意,ADC 无论被配置为几段转换模式,每一段都需要一个触发事件才能进行开始采样转换,否则 ADC 处于等待状态。单段触发模式可以是一次硬件事件即触发,也可以是达到一定次数的硬件事件才触发采样,4 个 MCPWM/UTimer 触发事件均可触发或参与计数;但两段触发和四段触发,4 个 MCPWM/UTimer 触发事件只能按顺序触发 ADC 采样。

工作模式触发规则说明
单段模式一次硬件事件直接触发;支持累计 N 次硬件事件后才触发;4 路 MCPWM/UTimer 均可参与计数
两段模式硬件触发必须严格按顺序触发各段,不允许乱序;每一段需要收到对应触发才开始采样,无触发则等待
四段模式硬件触发必须严格按顺序 T0→T1→T2→T3;来一次触发只执行对应一段,不会自动跑完全部 4 段;无触发 ADC 处于等待

无论几段模式,每一段都必须收到对应触发事件,才会开始采样转换,否则 ADC 等待。

4.3  ADC 通道选择

        每个 ADC 模块有 5 个通道信号来源寄存器,控制采样序列 0~15 的信号选择。

寄存器管控的次采样序号归属类型
ADC_CHN0第 0~3 次采样常规 NT
ADC_CHN1第 4~7 次采样常规 NT
ADC_CHN2第 8~11 次采样常规 NT
ADC_CHN3第 12~13 次采样常规 NT
ADC_ICHN空闲段第 0~1 次采样空闲 IT

        每次采样通道值选择范围都是 0~15,对应通道 0~15, 也就是可以对某一个通道进行多次采样。每一个采样对应一个结果寄存器,转换结束后,可以直接到对应结果寄存器中获取到 ADC 采样结果。

4.4  中断配置

        ADC 一共有七种中断:

序号中断名称含义说明
1第一段采样完成中断第 1 段采样全部次采样执行完毕,段完成中断
2第二段采样完成中断第 2 段采样全部次采样执行完毕,段完成中断
3第三段采样完成中断第 3 段采样全部次采样执行完毕,段完成中断
4第四段采样完成中断第 4 段采样全部次采样执行完毕,段完成中断
5软件触发发生在非空闲状态中断ADC正在工作(非空闲)时,又来了软件触发;此时触发无法执行,产生报错中断
6硬件触发发生在非空闲状态中断ADC正在工作(非空闲)时,又来了硬件触发;触发无法执行,产生报错中断
7ADC0_DAT0 超阈值中断ADC0 的 DAT0 采样结果高于 / 低于设定阈值,触发告警中断(监控模拟信号)

        其寄存器配置如下:

4.5  ADC 输出数制

        ADC 输出数据为 12bit 补码,输入信号 0 对应 12’b0000_0000_0000。 ADC 有两种量程设置 3.6V 和 7.2V。 以 3.6V 量程为例:

输入电压12bit 补码十进制
‑3.6V12'b1000_0000_0000‑2048
0V12'b0000_0000_00000
+3.6V12'b0111_1111_1111+2047

        ADC 转换后的 12bit 补码需要符号扩展存入 16bit 的ADC_DAT寄存器;支持左对齐、右对齐配置。

        左对齐:把 12bit 数据向 16bit 寄存器高位靠齐,低位补 0,这样符号位在 16bit 的最高 bit,做数值运算时,直接对 16 位数右移 4 位,即可还原原始 12bit 补码;

12bit:  1000 0000 1101
左对齐: 1000 0000 1101 0000  (低4位补0)

        右对齐:2bit 向 16bit 寄存器低位靠齐,高位填充符号位(符号扩展),

  • 如果原 12bit 最高位是 1(负数),所以高 4 位全部填 1;
  • 如果原始是正数,符号位 0,则高 4 位全部填 0。
12bit:       1000 0000 1101
右对齐: 1111 1000 0000 1101  (高4位复制符号位1)

4.6  ADC 量程

        ADC 在 2.4V 的默认基准电压配置下,有两种量程设置:3.6V 和 7.2V。

ADC 量程基准电压可承受输入幅度适用场景
3.6V2.4V±3.6V运放 OPA 输出信号(电机电流采样,推荐)
7.2V2.4V±5V外部大幅度差分信号输入

注意:7.2V 量程虽然叫 7.2V 量程,但芯片实际允许最大输入是±5V,不要输入超过 ±5V,损坏芯片。

4.7  ADC 阈值监测

        ADC 阈值监测,又叫做模拟看门狗,硬件实时监控 ADC 采样结果;不需要 CPU 不停轮询读取寄存器,一旦采样值超出设定上下边界,硬件直接置超限中断标志。

        每个 ADC 模块拥有1 组阈值监测电路,同时配置:

  • 上阈值(高门限,12bit 有符号补码)
  • 下阈值(低门限,12bit 有符号补码)

        阈值本身是 12 位有符号补码,和 ADC 原始转换数据位宽一致。

        比较逻辑不受左 / 右对齐影响,无论配置左对齐还是右对齐,硬件自动提取有效的 12bit 采样数值做对比,软件不用因为对齐方式修改阈值参数。

        阈值是 12bit 有符号数:

  • 左对齐模式:ADCx_DATx[15:4](高 12 位,真正的 12bit 采样补码)拿出来和阈值比较,忽略低 4 位填充 0;
  • 右对齐模式:ADCx_DATx[11:0](低 12 位,符号扩展后的有效采样数据)拿出来和阈值比较;

        比较触发时机:ADC 完成一次次采样,经过增益、偏移校正,结果写入ADCx_DATx寄存器的瞬间,硬件自动做阈值比较:

  • 采样结果 > 上阈值 → 置阈值超限中断标志
  • 采样结果 < 下阈值 → 置阈值超限中断标志
  • 结果介于上下阈值之间:不动作。

5.  库函数

        都有注释应该还是很容易理解的。

5.1  ADC_TypeDef

        这里主要是寄存器地址映射结构体,编译器会根据结构体成员顺序、偏移对应芯片实际寄存器地址,ADC0/ADC1是外设基地址强制转换的结构体指针:


typedef struct
{
    __IO uint32_t DAT0;  /**< 偏移地址: 0x00 ADCx 第 0 次常规采样数据*/
    __IO uint32_t DAT1;  /**< 偏移地址: 0x04 ADCx 第 1 次常规采样数据*/
    __IO uint32_t DAT2;  /**< 偏移地址: 0x08 ADCx 第 2 次常规采样数据*/
    __IO uint32_t DAT3;  /**< 偏移地址: 0x0C ADCx 第 3 次常规采样数据*/
                         /**<                                      */
    __IO uint32_t DAT4;  /**< 偏移地址: 0x10 ADCx 第 4 次常规采样数据*/
    __IO uint32_t DAT5;  /**< 偏移地址: 0x14 ADCx 第 5 次常规采样数据*/
    __IO uint32_t DAT6;  /**< 偏移地址: 0x18 ADCx 第 6 次常规采样数据*/
    __IO uint32_t DAT7;  /**< 偏移地址: 0x1C ADCx 第 7 次常规采样数据*/
                         /**<                                      */
    __IO uint32_t DAT8;  /**< 偏移地址: 0x20 ADCx 第 8 次常规采样数据*/
    __IO uint32_t DAT9;  /**< 偏移地址: 0x24 ADCx 第 9 次常规采样数据*/
    __IO uint32_t DAT10; /**< 偏移地址: 0x28 ADCx 第 10 次常规采样数据 */
    __IO uint32_t DAT11; /**< 偏移地址: 0x2C ADCx 第 11 次常规采样数据 */

    __IO uint32_t DAT12; /**< 偏移地址: 0x30 ADCx 第 12 次常规采样数据*/
    __IO uint32_t DAT13; /**< 偏移地址: 0x34 ADCx 第 13 次常规采样数据 */
    __IO uint32_t IDAT0; /**< 偏移地址: 0x38 ADCx 第 0 次空闲采样数据*/
    __IO uint32_t IDAT1; /**< 偏移地址: 0x3C ADCx 第 1 次空闲采样数据*/

    __IO uint32_t ICHN;  /**< 偏移地址: 0x40 ADCx 空闲采样通道配置*/
    __IO uint32_t null;  /**< 偏移地址: 0x44 地址对齐,无意义*/
    __IO uint32_t null1; /**< 偏移地址: 0x48 地址对齐,无意义*/
    __IO uint32_t null2; /**< 偏移地址: 0x4C 地址对齐,无意义*/

    __IO uint32_t CHN0; /**< 偏移地址: 0x50 ADCx 第 0~ 3 次常规采样输入信号选择 Signal 0 Signal 0 Signal 0 Signal 0 Signal 0 Signal 0*/
    __IO uint32_t CHN1; /**< 偏移地址: 0x54 ADCx 第 4~ 7 次常规采样输入信号选择*/
    __IO uint32_t CHN2; /**< 偏移地址: 0x58 ADCx 第 8~11 次常规采样输入信号选择*/
    __IO uint32_t CHN3; /**< 偏移地址: 0x5C ADCx 第 12~15 次常规采样输入信号选择*/

    __IO uint32_t CHNT;  /**< 偏移地址: 0x60 ADCx 各种触发模式下采样次数*/
    __IO uint32_t GAIN;  /**< 偏移地址: 0x64 ADCx 增益选择*/
    __IO uint32_t null3; /**< 偏移地址: 0x68 地址对齐,无意义*/
    __IO uint32_t null4; /**< 偏移地址: 0x6C 地址对齐,无意义*/

    __IO uint32_t null5; /**< 偏移地址: 0x70 地址对齐,无意义*/
    __IO uint32_t CFG;   /**< 偏移地址: 0x74 ADCx 模式配置*/
    __IO uint32_t TRIG;  /**< 偏移地址: 0x78 ADCx 采样触发配置*/
    __IO uint32_t SWT;   /**< 偏移地址: 0x7C ADCx 软件触发*/

    __IO uint32_t DC0;  /**< 偏移地址: 0x80 ADCx 增益为 0 时 DC offset*/
    __IO uint32_t AMC0; /**< 偏移地址: 0x84 ADCx 增益为 0 时增益校正系数*/
    __IO uint32_t DC1;  /**< 偏移地址: 0x88 ADCx 增益为 1 时 DC offset*/
    __IO uint32_t AMC1; /**< 偏移地址: 0x8C ADCx 增益为 1 时增益校正系数*/

    __IO uint32_t IE;    /**< 偏移地址: 0x90 ADCx 中断使能*/
    __IO uint32_t IF;    /**< 偏移地址: 0x94 ADCx 中断标志*/
    __IO uint32_t null6; /**< 偏移地址: 0x98 地址对齐,无意义*/
    __IO uint32_t null7; /**< 偏移地址: 0x9C 地址对齐,无意义*/

    __IO uint32_t null8;  /**< 偏移地址: 0xA0 地址对齐,无意义*/
    __IO uint32_t null9;  /**< 偏移地址: 0xA4 地址对齐,无意义*/
    __IO uint32_t null10; /**< 偏移地址: 0xA8 地址对齐,无意义*/
    __IO uint32_t null11; /**< 偏移地址: 0xAC 地址对齐,无意义*/

    __IO uint32_t null12; /**< 偏移地址: 0xB0 地址对齐,无意义*/
    __IO uint32_t null13; /**< 偏移地址: 0xB4 地址对齐,无意义*/
    __IO uint32_t null14; /**< 偏移地址: 0xB8 地址对齐,无意义*/
    __IO uint32_t null15; /**< 偏移地址: 0xBC 地址对齐,无意义*/

    __IO uint32_t null16; /**< 偏移地址: 0xCC 地址对齐,无意义*/
    __IO uint32_t LTH;    /**< 偏移地址: 0xC4 ADCx 数据低阈值*/
    __IO uint32_t HTH;    /**< 偏移地址: 0xC8 ADCx 数据高阈值*/
    __IO uint32_t GEN;    /**< 偏移地址: 0xCC ADCx 阈值监测使能*/
} ADC_TypeDef;

5.2  ADC_InitTypeDef

        始化结构体,调用ADC_Init()时,把这里成员写入硬件寄存器,集中配置 ADC 参数:

typedef struct
{
    u16 IE;         /**< 中断使能*/
    u16 RE;         /**< DMA请求使能*/
    u16 NSMP;       /**< 两段采样使能*/
    u16 DATA_ALIGN; /**< DAT右对齐使能*/
    u16 CSMP;       /**< 连续采样使能*/
    u16 TCNT;       /**< 触发一次采样所需的事件数*/
    u16 TROVS;      /**< 手动触发过采样使能,开启后一次采样需要多次触发*/
    u16 OVSR;       /**< 过采样率*/
    u16 TRIG;       /**< 触发信号*/
    u16 S1;         /**< 第一段常规采样次数*/
    u16 S2;         /**< 第二段常规采样次数*/
    u16 IS1;        /**< 空闲采样次数*/
    u16 GAIN;       /**< 低增益(7.2V量程)使能*/
    u16 LTH;        /**< ADC 模拟看门狗 0 下阈值*/
    u16 HTH;        /**< ADC 模拟看门狗 0 上阈值*/
    u16 GEN;        /**< ADC 模拟看门狗 0 对应使能位*/
} ADC_InitTypeDef;

5.3  API库函数

函数原型功能简述注意事项
ADC_Init(ADC_TypeDef *ADCx, ADC_InitTypeDef *ADC_InitStruct)根据结构体配置初始化 ADC 外设先结构体初始化,再修改参数,再调用本函数
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct)结构体填充默认参数必须最先调用,避免随机垃圾值
ADC_GetIRQFlag(ADC_TypeDef *ADCx, uint16_t INT_flag)读取中断标志位中断服务函数内判断中断事件
ADC_ClearIRQFlag(ADC_TypeDef *ADCx, uint16_t INT_flag)清除中断标志必须软件清除,否则重复进中断
ADC_CHN_GAIN_CFG(ADC_TypeDef *ADCx, CHNx CHNum, u8 nChannel, ADC_GAINx GAIN3V6_or_7V2)配置某一次采样:通道号、ADC 量程每一次次采样可独立选择量程
s16 ADC_GetConversionValue(ADC_TypeDef *ADCx, DATx DATNum)读取 DATx 采样结果返回s16有符号,存在负数
ADC_SoftTrgEN(ADC_TypeDef *ADCx, FuncState state)开启 / 关闭软件触发功能使能后才可以写 ADC_SWT 软件触发

6.  软件触发代码编写

        我们从上面了解到三种触发方式:软件触发、UTIME 触发和 MCPWM 触发。这里先用软件触发。

6.1  引脚初始化

        我想要做的是读取电位器电压值的变化,这里初始化引脚P0.5,也就是通道9,可以查看数据手册:

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_ANA;
    GPIO_InitStruct.GPIO_Pin =  GPIO_Pin_5;
    GPIO_InitStruct.GPIO_PuPd = DISABLE;
    GPIO_Init(GPIO0, &GPIO_InitStruct);

6.2  ADC初始化

        初始化代码非常简单,把我们上方给出的初始化结构体全部写下来,这里只初始化了一次常规采样,用于软件触发采样:

void ADC0_init(void)
{
    ADC_InitTypeDef ADC_InitStructure;

    ADC_StructInit(&ADC_InitStructure);
    ADC_InitStructure.IE         = 0;           //中断使能
    ADC_InitStructure.RE         = 0;           //DMA请求使能
    ADC_InitStructure.NSMP       = DISABLE;     //两段采样使能
    ADC_InitStructure.DATA_ALIGN = DISABLE;     //DAT右对齐使能
    ADC_InitStructure.CSMP       = DISABLE;     //连续采样使能
    ADC_InitStructure.TCNT       = 0;           //触发一次采样所需的事件数
    ADC_InitStructure.TROVS      = DISABLE;     //手动触发过采样使能,开启后一次采样需要多次触发
    ADC_InitStructure.OVSR       = 0;           //过采样率
    ADC_InitStructure.TRIG       = 0;           //触发信号
    ADC_InitStructure.S1         = 1;           //第一段常规采样次数
    ADC_InitStructure.S2         = 0;           //第二段常规采样次数
    ADC_InitStructure.IS1        = 0;           //空闲采样次数
    ADC_InitStructure.LTH        = 0;           //ADC 模拟看门狗 0 下阈值
    ADC_InitStructure.HTH        = 0;           //ADC 模拟看门狗 0 上阈值
    ADC_InitStructure.GEN        = 0;           //ADC 模拟看门狗 0 对应使能位
    ADC_InitStructure.GAIN       = 0;						//低增益(7.2V量程)使能
    ADC_Init(ADC0, &ADC_InitStructure);         

    ADC_ClearIRQFlag(ADC0, ADC_ALL_IF);         //清除所有中断标志位

    ADC_CHN_GAIN_CFG(ADC0, CHN0, ADC_CHANNEL_9, ADC_GAIN3V6);//配置ADC通道9的增益为3.6V量程
}

        然后初始化通道9电压。

6.3  主函数部分

        首先是采样触发部分,我们实验软件触发采样,更具手册,需要向SWT发送0x5AA5,然后轮询等待采样完成,清楚相关中断标志位:

		ADC0->SWT = 0x5AA5;   // 写0x5AA5,软件触发ADC0执行一次采样转换

		// 轮询等待SF1第一段ADC采样转换完成,等待转换完成标志位置1
		while((ADC0->IF & ADC_SF1_IF) == 0);

		ADC0->IF = ADC_SF1_IF; // 软件写1清除ADC转换完成中断标志位

        之后调用ADC_GetConversionValue()函数读取ADC采集值,注意此时的值是左移四位的结果,要想得到实际值,需要向右移四位:

adc_raw = ADC_GetConversionValue(ADC0, DAT0);

adc_val = (u16)(adc_raw >> 4);

        之后将所得的实际值,根据公式转换成电压,在OLED屏幕上显示出来:

float v_srn = (float)adc_val * 3.6f / 2048.0f;
voltage = v_srn * 3.0f / 2.0f;

OLED_ShowNum(1, 1,adc_val, 6);
OLED_ShowNum(2, 1,adc_raw, 6);

uint16_t v_int  = (uint16_t)voltage;
uint16_t v_dec  = (uint16_t)((voltage - v_int) * 100);
			
OLED_ShowNum(3, 1, v_int, 2);
OLED_ShowString(3,3,".");
OLED_ShowNum(3,4, v_dec, 2);

        也可以根据所得的值进行一些其他判断,例如开关LED:

if(adc_val > 1500)
{
		LED1_ON;
}
else
{
		LED1_OFF;
}

if(adc_val > 1000)
{
		LED2_ON;
}
else
{
		LED2_OFF;
}

if(adc_val > 500)
{
		LED3_ON;
}
else
{
		LED3_OFF;
}
SoftDelay(0x80000);

        此时完整主函数代码:

#include "lks32mc07x_lib.h"
#include "hardware_init.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "OLED.h"

s16 adc_raw;        // ADC原始采样寄存器值(LKS32 ADC左对齐16位)
u16 adc_val;        // 转换后12位ADC有效值 0~4095
float voltage;      // 最终换算得到的实际输入电压

int main(void)
{
    Hardware_init();   // 系统硬件初始化:GPIO、ADC、OLED、按键、LED等外设

	while(1)
	{
		ADC0->SWT = 0x5AA5;   // 写0x5AA5,软件触发ADC0执行一次采样转换

		// 轮询等待SF1第一段ADC采样转换完成,等待转换完成标志位置1
		while((ADC0->IF & ADC_SF1_IF) == 0);

		ADC0->IF = ADC_SF1_IF; // 软件写1清除ADC转换完成中断标志位

		adc_raw = ADC_GetConversionValue(ADC0, DAT0); // 读取DAT0通道原始ADC寄存器数据

		adc_val = (u16)(adc_raw >> 4);  // 左对齐数据右移4位,得到12位有效ADC值(0~4095)

		// 计算采样点电压:参考电压3.6V,12位ADC满量程2048(注意:这里是单极性半量程)
		float v_srn = (float)adc_val * 3.6f / 2048.0f;
		voltage = v_srn * 3.0f / 2.0f;  // 硬件分压补偿,还原电位器实际输入电压

		OLED_ShowNum(1, 1,adc_val, 6);  // OLED第1行第1列,打印12位ADC有效值,占6位
		OLED_ShowNum(2, 1,adc_raw, 6);  // OLED第2行第1列,打印ADC原始寄存器值,占6位

		// 将浮点数电压拆分为整数部分、小数后两位,OLED不支持直接打印float
		uint16_t v_int  = (uint16_t)voltage;                     // 电压整数部分
		uint16_t v_dec  = (uint16_t)((voltage - v_int) * 100);   // 电压小数点后2位
		OLED_ShowNum(3, 1, v_int, 2);      // 打印电压整数
		OLED_ShowString(3,3,".");          // 打印小数点
		OLED_ShowNum(3,4, v_dec, 2);       // 打印电压小数部分

		// ADC阈值判断,ADC数值大于1500点亮LED1
		if(adc_val > 1500)
		{
				LED1_ON;
		}
		else
		{
				LED1_OFF;
		}

		// ADC数值大于1000点亮LED2
		if(adc_val > 1000)
		{
				LED2_ON;
		}
		else
		{
				LED2_OFF;
		}

		// ADC数值大于500点亮LED3
		if(adc_val > 500)
		{
				LED3_ON;
		}
		else
		{
				LED3_OFF;
		}

		SoftDelay(0x80000); // 软件延时,降低采样刷新速度,防止OLED刷屏过快
	}
}

        烧录可以看到电压正常采集,可以扭动电位器旋钮,看一下效果:

完整工程:

凌鸥LKS32MC071系列单片机·软件触发ADC采集电位器电压资源-CSDN下载

6.4  拓展:ISR触发

        上面只是通过简单的轮询进行软件触发,我们也可以通过中断进行标志位的发送,触发ADC采集数据,由于我们初始化ADC的通道0的中断,因此只需要对ADC初始化代码进行第一采集中断使能即可:

void ADC0_init(void)
{
    ADC_InitTypeDef ADC_InitStructure;

    ADC_StructInit(&ADC_InitStructure);
    ADC_InitStructure.IE         = ADC_SF1_IE;
    ADC_InitStructure.RE         = 0;
    ADC_InitStructure.NSMP       = DISABLE;
    ADC_InitStructure.DATA_ALIGN = DISABLE;
    ADC_InitStructure.CSMP       = DISABLE;
    ADC_InitStructure.TCNT       = 0;
    ADC_InitStructure.TROVS      = DISABLE;
    ADC_InitStructure.OVSR       = 0;
    ADC_InitStructure.TRIG       = 0;
    ADC_InitStructure.S1         = 1;
    ADC_InitStructure.S2         = 0;
    ADC_InitStructure.IS1        = 0;
    ADC_InitStructure.LTH        = 0;
    ADC_InitStructure.HTH        = 0;
    ADC_InitStructure.GEN        = 0;
    ADC_Init(ADC0, &ADC_InitStructure);

    ADC_ClearIRQFlag(ADC0, ADC_ALL_IF);

    ADC_CHN_GAIN_CFG(ADC0, CHN0, ADC_CHANNEL_9, ADC_GAIN7V2);
}

        然后在Hardware_init()初始化NVIC:

void Hardware_init(void)
{
    __disable_irq();
    SYS_WR_PROTECT = 0x7a83;
    IWDG_DISABLE();
    GPIO_init();
    GPIO_init_key();
    ADC0_init();
		OLED_Init();
		TIMER2_init();
	
    NVIC_SetPriority(ADC0_IRQn, 1);
    NVIC_EnableIRQ(ADC0_IRQn); 
	
    SoftDelay(100);
    __enable_irq();
}

        然后来到中断服务函数部分,判读ADC0是否发生第一段常规采样完成中断,如果是则读取数据,中断标志位置1:

volatile s16 ADCSample;
volatile u8 adc_data_ready = 0;

void ADC0_IRQHandler(void)
{
    if(ADC_GetIRQFlag(ADC0,ADC_SF1_IF))
    {
        ADC_ClearIRQFlag(ADC0,ADC_SF1_IF);
        ADCSample = ADC_GetConversionValue(ADC0,DAT0) ;
        adc_data_ready = 1;
    }
    else
    {
        ADC_ClearIRQFlag(ADC0,ADC_ALL_IF);
    }
}

        来到主函数,如果触发中断则进行数据显示:

#include "lks32mc07x_lib.h"
#include "hardware_init.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "OLED.h"

extern volatile s16 ADCSample;
extern volatile u8 adc_data_ready;

s16 adc_raw;
u16 adc_val;
float voltage;
float v_srn;

int main(void)
{
    Hardware_init();

		while(1)
		{
			//ADC_SoftTrgEN(ADC0, ENABLE);
			ADC0->SWT = 0x5AA5;   //软件触发

			if(adc_data_ready == 1) // 检查ISR是否已完成
			{
				adc_data_ready = 0; // 清除通知标志
				
				adc_raw = ADCSample;

				adc_val = (u16)(adc_raw >> 4);

				v_srn = (float)adc_val * 7.2f / 2048.0f;
				voltage = v_srn * 3.0f / 2.0f;

				uint16_t v_int1  = (uint16_t)v_srn;
				uint16_t v_dec1  = (uint16_t)((v_srn - v_int1) * 100);

				uint16_t v_int  = (uint16_t)voltage;
				uint16_t v_dec  = (uint16_t)((voltage - v_int) * 100);

				OLED_ShowNum(1, 1,adc_raw, 6);
				OLED_ShowNum(2, 1,adc_val, 6);

				OLED_ShowNum(3, 1, v_int1, 2);
				OLED_ShowString(3,3,".");
				OLED_ShowNum(3,4, v_dec1, 2);				

				OLED_ShowNum(4, 1, v_int, 2);
				OLED_ShowString(4,3,".");
				OLED_ShowNum(4,4, v_dec, 2);

				if(adc_val > 700)
				{
						LED1_ON;
				}
				else
				{
						LED1_OFF;
				}

				if(adc_val > 500)
				{
						LED2_ON;
				}
				else
				{
						LED2_OFF;
				}

				if(adc_val > 300)
				{
						LED3_ON;
				}
				else
				{
						LED3_OFF;
				}
			}
		}
}

        这里为了方便观察实验现象,采用凌鸥官方给的一个图形化工具:

Tools & Software-南京凌鸥创芯电子有限公司

        安装,按照下图操作:

        想要观察什么参数直接拖拽即可,然后点击开始运行:

        旋转电位器可以观察到上图电压的变换。

完整工程:

凌鸥LKS32MC071系列单片机·中断触发软件触发ADC采集电位器电压资源-CSDN下载

        对于UTIME 触发和 MCPWM 触发,由于涉及到二者知识点,等把二者文章基础部分写完,后续在做补充,补充部分会存放到下方博客当中。

凌鸥LKS32_时光の尘的博客-CSDN博客

更多推荐