Skip to content

Latest commit

 

History

History
488 lines (331 loc) · 26.9 KB

04-ADC驱动.rst

File metadata and controls

488 lines (331 loc) · 26.9 KB

ADC驱动模块

硬件特性

简介

16 位 ADC 模块的工作时钟最高为 56MHz 支持多达 8 个单端输入和 4 个差分输入的 ADC 信号的采集,一路采样保持电路,这些通道的 ADC 转换可在单次采样,多次采样取平均值, 简单连续采样(不切换通道),复杂连续采样(切换通道)模式下进行, ADC 的结果存储 在一个 16 位数据寄存器中,两个单端输入可以组成一个差分输入。差分输入和单端输入是 互斥的。

时钟

ADC时钟来源于PCLK,PCLK由SYSCLK分频得到,而SYSCLK来自HCLK的分频,HCLK来自SYSCLK或SYSCLK的特殊分频,SYSCLK可选PLL、HOSC、LOSC三者之一。下图为M400单片机中与ADC相关的时钟结构图。

图示 1: ADC时钟来源

备注:M400的典型SYSCLK为200M。XHOSC为外部晶振,M400开发板MCU_dev V1.0使用的XHOSC为30MHz。

输入输出引脚

外部输出引脚EXOUT

ADC模块不涉及输出引脚。

外部输入引脚EXIN

ADC最多支持8路单端或 4路差分输入信号的采集,每个单端采集对应一个外部输入引脚(也即每组差分输入对应两个输入引脚例如在原理图上面第0组差分输入的两个输入引脚分别为ADC_IN<0>和ADC_IP<0>),除此之外还有两个参考电压的输入引脚分别为正参考电压VREFP和负参考电压VREFN

表格 2.1: ADC 输入引脚功能IO复用表

Pin编号(144封装) 第几功能 输入源
27 OPTION 0 ADC_IN< 3>
28 OPTION 0 ADC_IP< 3>
29 OPTION 0 ADC_IN< 2>
30 OPTION 0 ADC_IP< 2>
31 OPTION 0 ADC_IN< 1>
32 OPTION 0 ADC_IP< 1>
33 OPTION 0 ADC_IN< 0>
34 OPTION 0 ADC_IP< 0>
35 OPTION 0 VREFP
36 OPTION 0 VREFN (VSS)

ADC模块功能

参考ADC的硬件特性,ADC驱动支持以下功能

1.单数值采样模式:单通道单次采样,或者采样多次最终获取到一个平均值。

2.简单连续采样模式:单通道多次连续采样,不计算平均值。

3.复杂连续采样模式1:多通道同时采样,而且采样过程中会切换采样通道,采样通道不能进行重排。

4.复杂连续采样模式2:多通道同时采样,采样过程中会切换采样通道,而且通道可以进行重排。

5.可配置DMA数据存储

6.所有通道都能够进行采样,单端采样或者差分采样。

7.支持定时采样。

单数值采样模式

数值采样模式:单通道单次采样,或者采样多次最终获取到一个平均值。

简单连续采样模式

单通道多次连续采样,不计算平均值。

复杂连续采样模式1

多通道同时采样,而且采样过程中会切换采样通道,采样通道不能进行重排。

复杂连续采样模式2

多通道同时采样,采样过程中会切换采样通道,而且通道可以进行重排。

硬件中断

ADC驱动函数中比较重要的是ADC中断的产生与处理,由于ADC完成转换之后的数据需要及时的传输出去,为了提高数据传输的效率使用DMA来进行数据传输。所以最终的中断发生在DMA传输完数据之后,因此中断处理函数的重点是DMA中断函数的处理,为了方便和ADC进行联动,ADC驱动上面设计了一个会从DMA 数据缓冲区里面读取数据的回调函数,当ADC数据转换完成,并且数据由DMA传输完成之后会产生一个DMA数据传输完成的中断,然后DMA中断函数调用该回调函数。回调函数间接调用驱动上半部分的adc_receive,把来自DMA的数据和完成数据转化的ADC通道打包成一条adc_msg_s消息,然后交由缓冲队列来管理数据。加入缓冲队列的目的主要是为了防止驱动层数据转换太快,应用层可能来不及处理每条数据,所以用缓冲队列起到数据暂存的效果。

关键数据结构

编程接口

ADC的总体设计是沿用ulinx系统中的ADC驱动框架,通过在伪文件系统注册生成字符设备的方式供用户层使用,支持IOCTL接口,支持设置各类采样模式。

应用层编程接口

open接口

上层应用使用open函数开启ADC设备时底层驱动会完成硬件的初始化,然后把ADC设备绑定到文件IO操作的标准函数中,之后就可以向访问文件一样操作ADC设备。ADC设备open流程如下:

IOCTL接口

在ulinx中应用程序可以通过IOCTL的方式对ADC进行设置、参数读取。其中ADC相关ioctl支持的命令主要有三个

表格 3.1: ADC通用IOCTL命令

CMD Description Return
ANIOC_TRIGGER 开启ADC转换 没有返回值
ANIOC_GET_NCHANNELS 获取已经设置的ADC通道数 返回底层驱动stop函数执行结果
ANIOC_RUN_ADCTEST 运行ADC测试函数  

接口4

接口5

驱动层接口

驱动功能

单数值采样模式

DC_CON1_REG1的Bit5:4设置为0时,ADC只进行单个数值采样,采样之后会自动结束采样。这里单数值模式有两种情况,一种是ADC只采样一次,或者采样多次取平均值,平均值计算一共有2次采样平均,4次采样平均,8次采样平均三种情况,具体的由ADC_CON1_REG1的Bit7:6决定(要注意的是单值和单次,单值模式下不论ADC连续采样几次最终读取到的数据只有一个)。单值采样模式下面的多次采样过程不受trig_en控制,trig_en可以理解为ADC采用触发信号的使能开关。在ADC其它采样模式时具体介绍。

简单连续采样模式

ADC_CON1_REG1的Bit5:4设置为1时,进行简单连续采样(不切换采样通道),该采样模式下面不支持自动求平均值,采样时会产生多次中断和多个有效数据。产生中断和有效数据之后如果trig_en==0(ADC_CON1_REG1的bit0为0), 则由DMA或者CPU搬移数据。然后ADC立马进行下一次采样,要停止采样只有手动编程设置ADC_CON4_REG寄存器的bit0为0。如果trig_en==1(ADC_CON1_REG1的bit0为1),则需要等待下一次采样的触发信号(adc_trig)来临之后,才进行下一次采样。触发信号可以配置为timerb_tc1,timerc_tc1,timerd_tc1具体使用哪一种触发信号由ADC_CON1_REG1的Bit3:1决定。timerb_tc1,timerc_tc1,timerd_tc1分别是由定时器B,C,D的计数器1产生的,具体定时器怎么配置需要结合芯片手册上面定时器章节的说明。但是在手册上面没有看见具体的的说明,定时器是如何触发ADC采样的,只看见了触发源是上面提到的三个定时器的计数器

复杂连续采样模式1

ADC_CON1_REG1的Bit5:4设置为2时,进行复杂连续采样(切换采样通道,但不重载通道),其中切换采样通道是指,多个通道同时有信号输入时,adc处理完一个通道的数据,按照事先指定的采样通道编号顺序,处理下一个通道的数据,例如8个采样通道如果都有信号输入,但是实际采样的顺序是设置为 3 4 7 1 5 采样时就先采集3通道的信号,然后4通道,再7通道,实际的通道采样顺序由ADC_CON3_REG寄存器的Bit11:0 决定,重载通道在后续采样模式中进行介绍。该采样模式下面不支持自动求平均值,采样时会产生多次中断和多个有效数据。产生中断和有效数据之后如果trig_en==0(ADC_CON1_REG1的bit0为0), 则由DMA或者CPU搬移数据。采样的过程成是按照ADC_CON3_REG寄存器的Bit11:0 指定的通道编号,从小到大依次扫描对应的通道采样,完成一轮通道扫描之后就自动进行下一轮通道扫描进行下一次采样,要停止采样只有手动编程设置ADC_CON4_REG寄存器的bit0为0。如果trig_en==1(ADC_CON1_REG1的bit0为1),则需要等待下一轮通道扫描的触发信号(adc_trig)来临之后,才进行下一论采样。触发信号可以配置为timerb_tc1,timerc_tc1,timerd_tc1具体使用哪一种触发信号由ADC_CON1_REG1的Bit3:1决定。timerb_tc1,timerc_tc1,timerd_tc1分别是由定时器B,C,D的计数器1产生的,具体定时器怎么配置需要结合芯片手册上面定时器章节的说明。但是在手册上面没有看见具体的的说明,定时器是如何触发ADC采样的,只看见了触发源是上面提到的三个定时器的计数器。需要注意的是差分采样和单端采样存在互斥的关系,在设置通道编号时需要注意,具体的参考芯片手册上的通道设置互斥表。

复杂连续采样模式2

ADC_CON1_REG1的Bit5:4设置为3时,进行复杂连续采样(切换采样通道,并且重载通道),其中切换采样通道是指,多个通道同时有信号输入时,adc处理完一个通道的数据,按照事先指定的采样通道编号顺序,处理下一个通道的数据,例如8个采样通道如果都有信号输入,但是实际采样的顺序是设置为 3 4 7 1 5 采样时就先采集3通道的信号,然后4通道,再7通道,实际的通道采样顺序由ADC_CON3_REG寄存器的Bit11:0 决定,重载通道是指在采样过程中可以重新设置寄存器ADC_CON3_REG寄存器的Bit11:0的值,来改变通道采样的顺序,比如上面的顺序最开始为3 4 7 1 5,可以根据实际需要改为2 6 8 1 6 4。该采样模式下面不支持自动求平均值,采样时会产生多次中断和多个有效数据。产生中断和有效数据之后如果trig_en==0(ADC_CON1_REG1的bit0为0), 则由DMA或者CPU搬移数据。采样的过程成是按照ADC_CON3_REG寄存器的Bit11:0 指定的通道编号,从小到大依次扫描对应的通道采样,完成一轮通道扫描之后就自动进行下一轮通道扫描进行下一次采样,(重载通道模式下需要先再次设置好下一次采样的通道编号,才可进行下一轮采样)。要停止采样只有手动编程设置ADC_CON4_REG寄存器的bit0为0。如果trig_en==1(ADC_CON1_REG1的bit0为1),则需要等待下一轮通道扫描的触发信号(adc_trig)来临之后,才进行下一论采样。(重载通道模式下要先等待adc_chn_int 中断产生,然后重新设置通道采样顺序。才可进行下一轮采样) 触发信号可以配置为timerb_tc1,timerc_tc1,timerd_tc1具体使用哪一种触发信号由ADC_CON1_REG1的Bit3:1决定。timerb_tc1,timerc_tc1,timerd_tc1分别是由定时器B,C,D的计数器1产生的,具体定时器怎么配置需要结合芯片手册上面定时器章节的说明。但是在手册上面没有看见具体的的说明,定时器是如何触发ADC采样的,只看见了触发源是上面提到的三个定时器的计数器。需要注意的是差分采样和单端采样存在互斥的关系,在设置通道编号时需要注意,具体的参考芯片手册上的通道设置互斥表。

驱动中断

ADC驱动设计

驱动框架

设备注册流程

ADC设备注册流程如图,通过boardctl()接口调用板级初始化程序完成ADC的硬件初始化,设备注册流程,设备注册成功后会在ulinx系统伪文件系统中生成字符设备,设备地址一般是形如/dev/adcX的形式,支持通过标准IO操作读写设备。

图示 2: ADC驱动注册流程

设备使用流程

上层应用使用open函数开启ADC设备时 底层驱动会完成硬件的初始化,然后把ADC设备绑定到文件IO操作的标准函数中,之后就可以向访问文件一样操作ADC设备。ADC设备open流程如下:

详细设计

软件框架设计

操作函数集

在ADC驱动中,构建了一个ADC的操作函数集,对接上层ADC驱动框架的驱动函数集。

static const struct adc_ops_s g_adcops =

{

   .ao_bind = adc_bind,

   .ao_reset = adc_reset,

   .ao_setup = adc_setup,

   .ao_shutdown = adc_shutdown,

   .ao_rxint = adc_rxint,

   .ao_ioctl = adc_ioctl,

};

以上操作函数集是几乎所有平台的ADC驱动均应支持的操作函数。

ADC驱动中还实现了一个私有结构体,用于M400的ADC的各种初始化设置。

static struct taishan400_dev_s g_adcpriv1 =

{

   .irq = TAISHAN400_IRQ_ADC,

   .isr = adc123_interrupt,

   .intf = 1,

   .base = TAISHAN400_ANACTRL_BASE,

   .adcpara.avgTime = ADC_CON1_AVGTIMES_1,

   .adcpara.chCombined = ADC_CON3_CHNCOMBINED_0,

   .adcpara.smpWidth = ADC_CON1_SMP_WIDTH_(8),

   #ifdef ADC_HAVE_TIMER

   .adcpara.waitFetch = ADC_CON1_NOTWAITFETCH,

   .adcpara.trigSrc = ADC_CON1_TRIG_SEL_TIMERD1,

   .adcpara.trigEn = ADC_CON1_TRIG_EN,

   #else

   .adcpara.trigEn = ADC_CON1_TRIG_DIS,

   #endif

   #ifdef ADC1_HAVE_TIMER

   .timirq = ADC1_TIMER_IRQ,

   .tbase = ADC1_TIMER_BASE,

   .freq = ADC_SAMPLE_FREQUENCY,

   .cfreq = ADC_COUNT_FREQUENCY,

   .timpara.matchCtl = TC_MCR_CLEARTC,

   .timpara.outMode = 0,

   .timpara.outPulseW= 0,

   #ifdef ADC_TIRGGER_CAPTURE

   .timpara.prescale = ADC_TIRGGER_COUNTER_DIV;

   #endif

   #endif

   #ifdef ADC1_HAVE_DMA

   .dmachan = {DMAMAP_DMA0S0,DMAMAP_DMA1S0},

   .hasdma = true,

   .s_base = TAISHAN400_ANACTRL_BASE_S,

   .blocks = 1,

   .trcnt = 1,

   .dmabuffer = {0},

   #endif

   #ifdef CONFIG_PM

   .pm_callback =

   {

   .prepare = adc_pm_prepare,

   }

   #endif

};

中断处理

ADC驱动函数中比较重要的是ADC中断的产生与处理,由于ADC完成转换之后的数据需要及时的传输出去,为了提高数据传输的效率使用DMA来进行数据传输。所以最终的中断发生在DMA传输完数据之后,因此中断处理函数的重点是DMA中断函数的处理,为了方便和ADC进行联动,ADC驱动上面设计了一个会从DMA 数据缓冲区里面读取数据的回调函数,当ADC数据转换完成,并且数据由DMA传输完成之后会产生一个DMA数据传输完成的中断,然后DMA中断函数调用该回调函数。回调函数间接调用驱动上半部分的adc_receive,把来自DMA的数据和完成数据转化的ADC通道打包成一条adc_msg_s消息,然后交由缓冲队列来管理数据。加入缓冲队列的目的主要是为了防止驱动层数据转换太快,应用层可能来不及处理每条数据,所以用缓冲队列起到数据暂存的效果。

功能实现

单数值采样模式

ADC_CON1_REG1的Bit5:4设置为0时,ADC只进行单个数值采样,采样之后会自动结束采样。这里单数值模式有两种情况,一种是ADC只采样一次,或者采样多次取平均值,平均值计算一共有2次采样平均,4次采样平均,8次采样平均三种情况,具体的由ADC_CON1_REG1的Bit7:6决定(要注意的是单值和单次,单值模式下不论ADC连续采样几次最终读取到的数据只有一个)。单值采样模式下面的多次采样过程不受trig_en控制,trig_en可以理解为ADC采用触发信号的使能开关。单数值采样模式主要涉及的寄存器汇总如下表

表格 3.2: 单数值采样模式设置

寄存器 功能
ADC_CON1_REG[15:8] 根据实际情况设置 设置采样周期数
ADC_CON1_REG[7:6] 根据实际情况设置 单值采样设置的取平均数系数
ADC_CON1_REG[5:4] 0 设置ADC采样模式
ADC_CON1_REG[0] 0 设置是否需要采样触发信号

简单连续采样模式

ADC_CON1_REG1的Bit5:4设置为1时,进行简单连续采样(不切换采样通道),该采样模式下面不支持自动求平均值,采样时会产生多次中断和多个有效数据。产生中断和有效数据之后如果trig_en==0(ADC_CON1_REG1的bit0为0), 则由DMA或者CPU搬移数据。然后ADC立马进行下一次采样,要停止采样只有手动编程设置ADC_CON4_REG寄存器的bit0为0。如果trig_en==1(ADC_CON1_REG1的bit0为1),则需要等待下一次采样的触发信号(adc_trig)来临之后,才进行下一次采样。触发信号可以配置为timerb_tc1,timerc_tc1,timerd_tc1具体使用哪一种触发信号由ADC_CON1_REG1的Bit3:1决定。timerb_tc1,timerc_tc1,timerd_tc1分别是由定时器B,C,D的计数器1产生的。

表格 3.3: 简单连续采样模式设置

寄存器 功能
ADC_CON1_REG[15:8] 根据实际情况设置 设置采样周期数
ADC_CON1_REG[7:6] 必须设置为0 单值采样设置的取平均数系数
ADC_CON1_REG[5:4] 1 设置ADC采样模式
ADC_CON1_REG[3:1] 根据实际情况设置 设置ADC采样出发信号
ADC_CON1_REG[0] 1 设置是否需要采样触发信号
ADC_CON4_REG[0]   是否开启ADC采样

复杂连续采样模式1

ADC_CON1_REG1的Bit5:4设置为2时,进行复杂连续采样(切换采样通道,但不重载通道),其中切换采样通道是指,多个通道同时有信号输入时,adc处理完一个通道的数据,按照事先指定的采样通道编号顺序,处理下一个通道的数据,例如8个采样通道如果都有信号输入,但是实际采样的顺序是设置为 3 4 7 1 5 采样时就先采集3通道的信号,然后4通道,再7通道,实际的通道采样顺序由ADC_CON3_REG寄存器的Bit11:0 决定,重载通道在后续采样模式中进行介绍。该采样模式下面不支持自动求平均值,采样时会产生多次中断和多个有效数据。产生中断和有效数据之后如果trig_en==0(ADC_CON1_REG1的bit0为0), 则由DMA或者CPU搬移数据。采样的过程成是按照ADC_CON3_REG寄存器的Bit11:0 指定的通道编号,从小到大依次扫描对应的通道采样,完成一轮通道扫描之后就自动进行下一轮通道扫描进行下一次采样,要停止采样只有手动编程设置ADC_CON4_REG寄存器的bit0为0。如果trig_en==1(ADC_CON1_REG1的bit0为1),则需要等待下一轮通道扫描的触发信号(adc_trig)来临之后,才进行下一论采样。触发信号可以配置为timerb_tc1,timerc_tc1,timerd_tc1具体使用哪一种触发信号由ADC_CON1_REG1的Bit3:1决定。timerb_tc1,timerc_tc1,timerd_tc1分别是由定时器B,C,D的计数器1产生的

3.4: 复杂连续采样模式1设置
寄存器 功能
ADC_CON1_REG[15:8] 根据实际情况设置 设置采样周期数
ADC_CON1_REG[7:6] 必须设置为0 单值采样设置的取平均数系数
ADC_CON1_REG[5:4] 2 设置ADC采样模式
ADC_CON1_REG[3:1] 根据实际情况设置 设置ADC采样出发信号
ADC_CON1_REG[0] 1 设置是否需要采样触发信号
ADC_CON3_REG[11:0] 根据实际情况设置 设置连续采样模式时的采样通道切换顺序。
ADC_CON4_REG[0]   是否开启ADC采样

备注:需要注意的是差分采样和单端采样存在互斥的关系,在设置通道编号时需要注意,具体的参考芯片手册上的通道设置互斥表。

复杂连续采样模式2

ADC_CON1_REG1的Bit5:4设置为3时,进行复杂连续采样(切换采样通道,并且重载通道),其中切换采样通道是指,多个通道同时有信号输入时,adc处理完一个通道的数据,按照事先指定的采样通道编号顺序,处理下一个通道的数据,例如8个采样通道如果都有信号输入,但是实际采样的顺序是设置为 3 4 7 1 5 采样时就先采集3通道的信号,然后4通道,再7通道,实际的通道采样顺序由ADC_CON3_REG寄存器的Bit11:0 决定,重载通道是指在采样过程中可以重新设置寄存器ADC_CON3_REG寄存器的Bit11:0的值,来改变通道采样的顺序,比如上面的顺序最开始为3 4 7 1 5,可以根据实际需要改为2 6 8 1 6 4。该采样模式下面不支持自动求平均值,采样时会产生多次中断和多个有效数据。产生中断和有效数据之后如果trig_en==0(ADC_CON1_REG1的bit0为0), 则由DMA或者CPU搬移数据。采样的过程成是按照ADC_CON3_REG寄存器的Bit11:0 指定的通道编号,从小到大依次扫描对应的通道采样,完成一轮通道扫描之后就自动进行下一轮通道扫描进行下一次采样,(重载通道模式下需要先再次设置好下一次采样的通道编号,才可进行下一轮采样)。要停止采样只有手动编程设置ADC_CON4_REG寄存器的bit0为0。如果trig_en==1(ADC_CON1_REG1的bit0为1),则需要等待下一轮通道扫描的触发信号(adc_trig)来临之后,才进行下一论采样。(重载通道模式下要先等待adc_chn_int 中断产生,然后重新设置通道采样顺序。才可进行下一轮采样) 触发信号可以配置为timerb_tc1,timerc_tc1,timerd_tc1具体使用哪一种触发信号由ADC_CON1_REG1的Bit3:1决定。timerb_tc1,timerc_tc1,timerd_tc1分别是由定时器B,C,D的计数器1产生的,具体定时器怎么配置需要结合芯片手册上面定时器章节的说明阅读keil工程的范例代码。

3.5: 复杂连续采样模式1设置
寄存器 功能
ADC_CON1_REG[15:8] 根据实际情况设置 设置采样周期数
ADC_CON1_REG[7:6] 必须设置为0 单值采样设置的取平均数系数
ADC_CON1_REG[5:4] 3 设置ADC采样模式
ADC_CON1_REG[3:1] 根据实际情况设置 设置ADC采样触发信号
ADC_CON1_REG[0] 1 设置是否需要采样触发信号
ADC_CON3_REG[11:0] 根据实际情况设置 设置连续采样模式时的采样通道切换顺序。
ADC_CON4_REG[0]   是否开启ADC采样

备注:需要注意的是差分采样和单端采样存在互斥的关系,在设置通道编号时需要注意,具体的参考芯片手册上的通道设置互斥表。

GPIO设置(根据具体情况)

编程示例

编程示例需要涵盖各个相应的功能