本文档对ADC驱动设计思路,使用方法以及相关术语进行说明。
本文档是微内核项目中ADC驱动模块的详细设计说明书,可以作为开发人员熟悉理解ADC驱动工作流程和ADC驱动维护的参考资料,可作为应用开发者使用ADC的参考资料。
μLinx 操作系统是凝思软件自主研制的具有微内核特征的实时操作系统,μLinx 操作系统支持M400单片机,M400 是智芯微电子的一款以 ARMV8-M 为基础的 MCU 芯片。智芯 MCU_dev V1.0是基于 M400 芯片开发的一块外设功能齐全的开发板。μLinx操作系统中ADC驱动实现了μLinx通过简单的IO操作函数操作ADC外设的功能。
本文档用于软件设计和开发阶段,它的上游(依据的基线)是产品需求说明书,它的下游是测试计划,并为测试报告提供测试依据。
表格 1.1: 术语定义
序号 | 术语 | 描述 |
ADC | 模数转换 |
[1] DEMO_M400_LQFP144.pdf
[2] M400 用户手册 1.1.pdf
在本驱动中,ADC外设驱动分为上下两部分,上半部分驱动是通用驱动程序,功能是为应用层代码提供通用接口,将ADC设备在μLinx系统中注册为字符设备,应用可以通过标准的IO操作来实现对底层ADC外设的控制。下半部分驱动是特定平台的驱动程序,功能是进行M400单片机ADC底层相关的寄存器的设置,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相关的时钟结构图。
备注:M400的典型SYSCLK为200M。XHOSC为外部晶振,M400开发板MCU_dev V1.0使用的XHOSC为30MHz。
ADC模块不涉及输出引脚。
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_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采样的,只看见了触发源是上面提到的三个定时器的计数器。
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采样的,只看见了触发源是上面提到的三个定时器的计数器。需要注意的是差分采样和单端采样存在互斥的关系,在设置通道编号时需要注意,具体的参考芯片手册上的通道设置互斥表。
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驱动拟支持以下功能,驱动相关的参数可以通过IOCTL的方式进行设置或读取。
1.数值采样模式:单通道单次采样,或者采样多次最终获取到一个平均值。
2.简单连续采样模式:单通道多次连续采样,不计算平均值。
3.复杂连续采样模式1:多通道同时采样,而且采样过程中会切换采样通道,采样通道不能进行重排。
4.复杂连续采样模式2:多通道同时采样,采样过程中会切换采样通道,而且通道可以进行重排。
5.可配置DMA数据存储
6.所有通道都能够进行采样,单端采样或者差分采样。
7.支持定时采样。
ADC的总体设计是沿用ulinx系统中的ADC驱动框架,通过在伪文件系统注册生成字符设备的方式供用户层使用,支持IOCTL接口,支持设置各类采样模式。
ADC设备注册流程如图,通过boardctl()接口调用板级初始化程序完成ADC的硬件初始化,设备注册流程,设备注册成功后会在ulinx系统伪文件系统中生成字符设备,设备地址一般是形如/dev/adcX的形式,支持通过标准IO操作读写设备。
上层应用使用open函数开启ADC设备时 底层驱动会完成硬件的初始化,然后把ADC设备绑定到文件IO操作的标准函数中,之后就可以向访问文件一样操作ADC设备。ADC设备open流程如下:
在ulinx中应用程序可以通过IOCTL的方式对ADC进行设置、参数读取。其中ADC相关ioctl支持的命令主要有三个
表格 3.1: ADC通用IOCTL命令
CMD | Description | Return |
ANIOC_TRIGGER | 开启ADC转换 | 没有返回值 |
ANIOC_GET_NCHANNELS | 获取已经设置的ADC通道数 | 返回底层驱动stop函数执行结果 |
ANIOC_RUN_ADCTEST | 运行ADC测试函数 |
在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采样 |
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采样 |
备注:需要注意的是差分采样和单端采样存在互斥的关系,在设置通道编号时需要注意,具体的参考芯片手册上的通道设置互斥表。
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采样 |
备注:需要注意的是差分采样和单端采样存在互斥的关系,在设置通道编号时需要注意,具体的参考芯片手册上的通道设置互斥表。