本文导读
在传统的基于寄存器的开发模式中,使用一个外设往往要阅读英文手册,理解寄存器每一位的含义,一步一步操作、调试,十分麻烦。AWorks提供的外设通用接口不仅可以跨平台复用,而且非常简洁,一个外设往往只有2~3个接口。本文将介绍通用的I²C、UART和ADC接口。本文为《面向AWorks框架和接口的编程(上)》第三部分软件篇——第7章通用外设接口——第4~6小节:I²C总线、UART总线和A/D转换器。7.4 I²C总线
7.4.1 I²C总线简介
I²C器件的两线制总线,不仅硬件电路非常简洁,而且还具有极强的复用性和可移植性。I²C总线不仅适用于电路板内器件之间的通信,而且通过中继器还可以实现电路板与电路板之间长距离的信号传输,因此使用I²C器件非常容易构建系统级电子产品开发平台。其特点如下:
-
总线仅需2根信号线,减少了电路板的空间和芯片管脚的数量,降低了互连成本;
-
同一条I²C总线上可以挂接多个器件,器件之间按不同的编址区分,因此不需要任何附加的I/O或地址译码器;
-
非常容易实现I²C总线的自检功能,以便及时发现总线的异常情况;
-
总线电气兼容性好,I²C总线规定器件之间以开漏I/O互连,因此只要选取适当的上拉电阻就能轻易实现3V/5V逻辑电平的兼容;
-
支持多种通信方式,一主多从是最常见的通信方式。此外还支持双主机通信、多主机通信与广播模式;
-
通信速率高,其标准传输速率为100Kbps(每秒100K位),在快速模式下为400Kbps,按照后来修订的版本,位速率可高达3.4Mbps。
7.4.2 I²C接口
绝大部分情况下,MCU都作为I²C主机与I²C从机器件通信,因此这里仅介绍AWorks中将MCU作为I²C主机的相关接口,接口原型详见表7.14。
表7.14 I²C标准接口函数
1. 定义I²C从机器件实例
对于用户来讲,使用I²C总线的目的往往是用于操作一个从机器件,比如,LM75、E2PROM等。MCU作为I²C主机与从机器件通信,需要知道从机器件的相关信息,比如,I²C从机地址等。在AWorks中,定义了统一的从机器件类型:aw_i2c_device_t,用于包含从机器件相关的信息,以便主机正确的与之通信。该类型的具体定义用户无需关心,在使用I²C操作一个从机器件前,必须先使用该类型定义一个与从机器件对应的器件实例,例如:
其中,dev为用户自定义的从机实例,其地址可以作为接口函数中p_dev的实参传递。
2. 初始化从机器件实例
当完成从机器件实例的定义后,还需要完成其初始化,指定从机器件相关的信息,初始化函数的原型为:
其中,p_dev为指向I²C从机实例的指针,busid为I²C总线编号,addr为从机器件地址,flags为从机器件相关的一些标志。
在AWorks中,一个系统往往具有多条I²C总线,各总线之间通过busid区分,一个系统实际支持的I²C总线条数与具体硬件平台相关。例如,在i.MX28x系统中,最高可以支持2条I²C总线,对应的总线编号即为:0 ~ 1。busid参数即用于指定使用那条I²C总线与从机器件进行通信。
addr为从机器件的I²C地址,由于读/写方向位由系统自动控制,因此,地址中不需要包含读写方向位。地址可以为7位地址和10位地址。
flags为从机器件相关的属性标志,可分为3大类:从机地址的位数、是否忽略无应答和器件内子地址(通常又称之为“寄存器地址”)的字节数。具体可用属性标志详见表7.15,可使用“|”操作连接多个属性标志。
表7.15 从机设备属性
例如,要使用I²C0(I²C总线的busid为0)操作温度传感器芯片LM75B,则应该首先定义并初始化一个与LM75B对应的从机器件。这就还需要知道两点重要的信息:器件从机地址和实例属性。
要获取这些信息,就必须查看芯片相关的数据手册,对于LM75B,其引脚分布图详见图7.12。LM75B的器件地址为7位地址:1001A2、A1、A0,其中,A2、A1、A0分别为引脚A2、A1、A0的状态。在扩展板中,LM75B等效的应用电路详见图7.11。
其中,R9和R10是I²C总线的上拉电阻,同时,只要短接(J13_1、J13_2)与(J11_1、J11_2),LM75的SCL和SDA引脚分别与I²C0总线的SCL和SDA相连。
图7.11 LM75B应用电路
图7.12 LM75B管脚图
由图7.11可知,A2、A1、A0均与地连接,因此,LM75的地址中相应位的值均为0,由此可得LM75B的7位地址为:1001000,即0x48。
从机属性分为从机地址属性、应答属性和器件内子地址属性。LM75B的从机地址为7位,其对应的属性标志为AW_I²C_ADDR_7BIT。
如果从机实例不能应答,则设置AW_I²C_IGNORE_NAK标志,一般来说,标准的I²C从机器件均可产生应答信号,除非特殊说明,否则都不需要使用该标志。LM75B内部共计有4个寄存器,它们的定义详见表7.16。
表7.16 LM75B内部寄存器列表
由表中地址栏内容可知,所有寄存器的地址均为8位,因此,器件内子地址为一个字节,对应的属性标志为:AW_I²C_SUBADDR_1BYTE。因为器件子地址只有一个字节,所以没有高字节与低字节之分,也就无需使用
AW_I²C_SUBADDR_MSB_FIRST
或AW_I²C_SUBADDR_LSB_FIRST标志。
通过以上分析,得到了LM75B相关的从机信息,为此,可以定义并初始化一个与LM75B对应的从机器件实例,范例程序详见程序清单7.26。
程序清单7.26 从机器件实例初始化函数范例程序
3. 读操作
从I²C从机器件指定的子地址中读出数据的函数原型为:
其中,p_dev为指向I²C从机实例的指针,subaddr为器件子地址,以指定读取数据的位置,p_buf指向存放读取数据的缓冲区,nbytes指定读取数据的字节数。返回值为标准的错误号,返回AW_OK时表示读取成功,否则,表示读取失败。
由表7.16可知,在LM75B中,地址0存放了2字节的温度值,如需读取温度,则可以直接从地址0中读取2字节数据,范例程序详见程序清单7.27。
程序清单7.27 读取数据范例程序
读取的两字节数据表示的温度值是多少呢?这两个字节具体表示的温度值含义可从芯片的数据手册获取。温度使用16位二进制补码表示,最高位为符号位,最高位为1时,表示温度为负数。读取温度时,读取的首个字节是高8位数据,紧接着的字节是低8位数据。各个位表示的温度权重详见表7.17。
表7.17 温度值数据各个位的含义
表中,2的n次方表示温度的权重。实际中,LM75B的温度分辨率有限,只能达到小数点后3位,低5位的值通常为0,是无效的,因此,LM75B实际温度的分辨率为2-3,即0.125℃。
据此,可以将字节0和字节1合并为一个有符号的16位整数,例如:
由于低5位无效,因此,式中将字节1与0xE0(1110 0000)作了 “与”运算,将低5位可靠的清0。同时,式中将整数部分左移了8位,小数部分也使用整数表示,相当于把原温度值扩大了256(28)倍。因此,temp的值为实际温度值的256倍,由此得到了LM75B采集到的温度值。
基于此,可以编写一个温度采集的范例程序,详见程序清单7.28。
程序清单7.28 温度采集范例程序
程序中,每秒采集一次温度值,并使用aw_kprintf()打印输出。打印输出当前的温度值时,由于aw_kprintf()暂时不支持直接打印浮点数,例如:
因此,分别打印了整数部分和小数部分,整数部分可以将temp整除256得到。计算小数部分时,先将temp扩大了1000倍,再除以256,得到的值即为实际温度的1000倍,最后对1000取余,即可得到实际温度小数点后3位的值。例如,实际温度为11.375度,则temp的值为11.375的256倍,即2912,整数部分即为该值整除256:
小数部分的值计算过程如下:
最终打印输出的结果即为:
这样的计算过程虽然看起来复杂了一些,但是其却从根本上避免了浮点运算,保证了程序运行的效率。在没有硬件浮点运算单元的MCU中,浮点运算是通过软件模拟的,效率非常低下。在AWorks中,如非必要,都应该尽可能避免浮点运算。即使是在有浮点运算单元的MCU中,也应该在一些基础的运算中避免使用浮点运算,因为在少量简单的浮点运算中,使用硬件浮点运算单元计算时,效率并不能得到明显的提升,反而增加了系统的负担,例如,当外部中断产生时,需要保护现场,如果使用了硬件浮点运算单元,则保护现场的数据量将大大增加。一般来讲,只有在需要大量浮点运算的场合(比如,在一些算法计算中,很难使用整数运算来避免浮点运算),才使用浮点运算。
需要特别说明的是,这里通过I²C总线直接读取了LM75B温度传感器中的温度值,仅用于介绍I²C总线接口的使用方法。实际中,AWorks已经定义了通用的温度接口,在应用中读取温度时,均建议直接使用通用的温度接口。
4. 写操作
向I²C从机实例指定的子地址中写入数据的函数原型为:
其中,p_dev为指向I²C从机实例的指针,subaddr为器件子地址,以指定写入数据的位置,p_buf指向存放待写入数据的缓冲区,nbytes指定写入数据的字节数。返回值为标准的错误号,返回AW_OK时表示写入成功,否则,表示写入失败。
由表7.16可知,在LM75B中,地址2和地址3中各存放了2字节的温度值,分别表示温度上限值(Thyst)和下限值(TOS),Thyst必须小于TOS,两个温度值均是可读可写的,默认情况下,Thyst的值为75℃,TOS的值为80℃。它们存储温度值的格式和地址0中温度值的格式类似,唯一不同的是,其表示温度的分辨率只有0.5度,因此,小数部分只有一位有效,低7位全为0。例如,同样将2字节数据看作一个有符号的16位整数temp,则temp的值为温度值的256倍。由此可得,若要表示80.5度,则对应的16位数据的值为:80.5 * 256 = 20608,即0x5080。
LM75B每次采集到新的温度时,都将与这两个温度值作比较,比较的结果将决定OS引脚的输出,以作为一种温度报警的机制。具体比较的方法与LM75B所处的模式相关,LM75B可以工作在比较模式或中断模式(可通过配置寄存器配置):若LM75B工作在比较模式,则当采样温度大于TOS时,OS引脚输出激活电平(激活电平可以通过配置寄存器配置为高电平或低电平),当采样温度降低到THYST以下时,OS引脚恢复到正常电平;若LM75B工作在中断模式,首先,采样温度与TOS比较,当采样温度大于TOS时,OS引脚输出激活电平,直到主机读取一次LM75B后,OS引脚将自动恢复为正常电平。接着,采样温度将切换为与THYST比较,当采样温度低于THYST时,OS引脚输出激活电平,直到主机读取一次LM75B后,OS引脚将自动恢复为正常电平。接着,又将采样温度切换为与TOS比较,当采样温度大于TOS时,OS引脚输出激活电平,以此类推。示意图详见图7.13。
图7.13 LM75B的OS引脚输出示意图
注:图中以激活电平为低电平,正常电平为高电平为例。在中断模式下,OS引脚可以被两种操作复位为正常电平:被主机读取一次数据;主机通过写配置寄存器,使LM75B进入关机模式。更多详细的内容可以查阅LM75B的数据手册,这里仅为简单介绍使用AWorks的I²C接口操作I²C从机器件的方法。
例如,要修改TOS的值为80.5℃,则需要修改TOS寄存器的值为0x5080,写入时,高字节先写入,低字节后写入,即先写入0x50,后写入0x80。范例程序详见程序清单7.29。
程序清单7.29 写入数据范例程序
由于TOS寄存器是可读可写的,为了验证是否写入成功,可以通过I²C读取接口,再读取出TOS寄存器的值,如果读出的值与写入的值相同,则表明写入成功。范例程序详见程序清单7.30。
程序清单7.30 验证写入数据是否成功的范例程序
7.5 UART总线
7.5.1 UART简介
UART
(Universal Asynchronous Receiver/Transmitter)是一种通用异步收发传输器,其使用串行的方式在双机之间进行数据交换,实现全双工通信。数据引脚仅包含用于接收数据的RXD和用于发送数据的TXD。
UART是一种串行通信协议,数据在数据线上按位一位一位的发送,UART协议主要有以下几个概念需要了解:
-
波特率
波特率是衡量数据传输速率的指标,表示每秒传送数据的位数,值越大,数据通信的速率越高,数据传输得越快。常见的波特率有4800、9600、14400、19200、38400、115200等等,如果波特率为115200,则表示每秒钟可以传输115200位(注意:是bit,不是byte)数据。
-
空闲位
数据线上没有数据传输时,数据线处于空闲状态。空闲状态的电平逻辑为“1”。
-
起始位
起始位表示一帧数据传输的开始,起始位的电平逻辑是“0”。
-
数据位
紧接起始位后,即为实际通信传输的数据,数据的位数可以是5、6、7、8等,数据传输时,从最低位开始依次传输。
-
奇偶校验位
奇偶校验位用于接收方对数据进行校验,及时发现由于通信故障等问题造成的错误数据。奇偶校验位是可选的,可以不使用奇偶校验位。奇偶校验有奇校验和偶校验两种形式,该位的逻辑电平与校验方法和所有数据位中逻辑“1”的个数相关。
奇校验:通过设置该位的值(“1”或“0”),使该位和数据位中逻辑“1”的总个数为奇数。例如,数据位为8位,值为:10011001,1的个数为4个(偶数),则奇校验时,为了使1的个数为奇数,就要设置奇偶校验位的值为1,使1的总个数为5个(奇数)。
偶校验:通过设置该位的值(“1”或“0”),使该位和数据位中逻辑“1”的总个数为偶数。例如,数据位为8位,值为:10011001,1的个数为4个(偶数),则偶校验时,为了使1的个数为偶数,就要设置奇偶校验位的值为0,使1的个数保持不变,为4(偶数)。
通信双方使用的校验方法应该一致,接收方通过判断“1”的个数是否为奇数(奇校验)或偶数(偶校验)来判定数据在通信过程中是否出错。
-
停止位
停止位表示一帧数据的结束,其电平逻辑为“1”,其宽度可以是1位、1.5位、2位。即其持续的时间为位数乘以传输一位的时间(由波特率决定),例如,波特率为115200,则传输一位的时间为1/115200秒,约为8.68us。若停止位的宽度为1.5位,则表示停止位持续的时间为:1.5 × 8.68us ≈ 13us。
常见的帧格式为:1位起始位,8位数据位,无校验,1位停止位。由于起始位的宽度恒为1位,不会变化,而数据位,校验位和停止位都是可变的,因此,往往在描述串口通信协议时,都只是描述其波特率、数据位,校验位和停止位,不再单独说明起始位。
注意,通信双方必须使用完全相同的协议,包括波特率、起始位、数据位、停止位等。如果协议不一致,则通信数据会错乱,不能正常通信。在通信中,若出现乱码的情况,应该首先检查通信双方所使用的协议是否一致。
7.5.2 串行接口
在AWorks中,定义了通用的串行接口,可以使用串行接口操作UART,实现数据的收发。接口原型详见表7.18。
表7.18 串行接口(aw_serial.h)
1. UART控制
在使用UART进行数据传输前,需要正确配置串行通信协议,比如:波特率、数据位、停止位、校验位等。其函数原型为:
其中,com为串口设备的ID,request表示本次请求控制的命令,p_arg为与request对应的参数,其具体类型与request的值相关。返回值为标准的错误号,返回AW_OK时表示本次控制成功,否则,表示控制失败。
在AWorks中,一个系统往往可以有多路串口输出,例如,在i.MX28x中,有1路调试串口,5路应用串口,为了区分各个串口,为各个串口设备分配了唯一的编号,如在i.MX28x中,各个串口设备分配的编号详见表7.19。
表7.19 各串口设备对应的编号
COM0 ~ COM5是在aw_serial.h文件中定义的宏,即:
request表示本次请求控制的命令,p_arg为与之对应的参数。常见命令与对应p_arg参数的实际类型详见表7.20。
表7.20 UART常用控制命令
-
设置波特率
设置波特率使用SIO_BAUD_SET命令,该命令(包括后文以SIO_口味前缀的各个命令宏定义)在aw_sio_common.h文件中定义,aw_serial.h文件已经自动包含该文件,用于无需再额外包含。设置波特率为115200的范例程序详见程序清单7.31。
程序清单7.31 设置波特率范例程序
-
获取波特率
获取波特率使用SIO_BAUD_GET命令,获取波特率的范例程序详见程序清单7.32。
程序清单7.32 获取波特率范例程序
-
设置硬件参数
设置硬件参数包括通信协议相关的参数,比如:数据位、校验位、停止位等。设置硬件参数对应的命令为SIO_HW_OPTS_SET,其对应的p_arg为32位整数,是由多个参数宏通过或(“|”)运算符连接组成。相关的参数宏详见表7.21。
表7.21 UART硬件参数(aw_sio_common.h)
例如,几种常见的配置范例详见程序清单7.33。
程序清单7.33 设置硬件参数的范例程序
-
获取硬件参数
获取当前硬件参数的命令为SIO_HW_OPTS_GET。例如,通过获取硬件参数,判断当前使用何种校验方式的范例程序详见程序清单7.34。
程序清单7.34 获取硬件参数的范例程序
此外,在发送或接收数据时,还会使用到几个命令,这些命令在讲解发送数据和接收数据时再详细介绍。
2. 发送数据
在AWorks中,为每个串口设备都分配了一个发送数据缓冲区(默认大小为128字节),用于缓存用户发送的数据。当用户发送数据时,首先会将数据加载到缓冲区中,加载到缓冲区后,串口设备将按照设定的波特率自动发送缓冲区中的数据。用户将数据写入缓冲区后,即可不用再处理串口的发送,转而处理其它事务。发送数据的函数原型为:
其中,com为串口设备的编号,p_buffer指向待发送数据的缓冲区,nbytes为发送数据的字节数。返回值为成功写入缓冲区的数据个数。比如,发送一个字符串“Hello World!“,范例程序详见程序清单7.35。
程序清单7.35 发送数据范例程序
3. 接收数据
在AWorks中,同样为每个串口设备都分配了一个接收数据缓冲区(默认大小为128字节),用于缓存串口设备接收到的数据,用户可以通过命令查询当前接收到的数据字节数,其对应的命令为:AW_FIONREAD,获取COM1的接收缓冲区中已接收数据个数的范例程序详见程序清单7.36。
程序清单7.36 获取接收缓冲区中已接收数据个数的范例程序
用户可通过接收数据接口读取缓冲区中的数据,其函数原型为:
其中,com为串口设备的编号,p_buffer指向存储读取数据的缓冲区,maxbytes为读取数据的最大字节数,其值往往与p_buffer指向的缓冲区大小一致。返回值为成功读取的数据个数。例如,接收数据的范例程序详见程序清单7.37。
程序清单7.37 接收数据范例程序
若当前接收缓冲区中具有足够的数据,即已接收数据不小于maxbytes,则成功读取maxbytes字节的数据,函数立即返回。若没有足够的数据,即已接收数据小于maxbytes小,则默认情况下,会一直阻塞等待,直到接收的数据量达到maxbytes。若用户不希望一直阻塞等待,则可以设定一个超时时间,当等待时间达到该值时,无论是否接收到maxbytes字节的数据,函数都会返回。
设定超时时间的命令为:AW_TIOCRDTIMEOUT(在aw_ioctl.h文件中定义)。设置超时时间为100ms的范例程序详见程序清单7.38。
程序清单7.38 设置读等待的超时时间为100ms
例如,通过串口控制LED0的亮灭,当接收到"on"时,则点亮LED0,当接收到"off"时,则熄灭LED0。同时,当接收到可以识别的"on"或"off"命令时,回复"OK!",若是非法命令,无法识别,则回复"Failed! Unknown Command!",范例程序详见程序清单7.39。
程序清单7.39 使用串口控制LED0的范例程序
7.6 A/D转换器
7.6.1 模数信号转换
1. 基本原理
我们经常接触的噪声和图像信号都是模拟信号,要将模拟信号转换为数字信号,必须经过采样、保持、量化与编码几个过程,详见图7.14。
图7.14 模数信号转换示意图
将以一定的时间间隔提取信号的大小的操作称为采样,其值为样本值,提取信号大小的时间间隔越短越能正确地重现信号。由于缩短时间间隔会导致数据量增加,所以缩短时间间隔要适可而止。注意,取样频率大于或等于模拟信号中最高频率的2倍,就能够无失真地恢复原信号。
将采样所得信号转换为数字信号往往需要一定的时间,为了给后续的量化编码电路提供一个稳定值,采样电路的输出还必须保持一段时间,而采样与保持过程都是同时完成的。虽然通过采样将在时间轴上连续的信号转换成了不连续的(离散的)信号,但采样后的信号幅度仍然是连续的值(模拟量)。
此时可以在振幅方向上以某一定的间隔进行划分,决定个样本值属于哪一区间,将记在其区间的值分配给其样本值。图7.14将区间分割为0~0.5、0.5~1.5、1.5~2.5,再用0、1、2……代表各区间,对小数点后面的值按照四舍五入处理,比如,201.6属于201.5~202.5,则赋值202;123.4属于122.5~123.5,则赋值123,这样的操作称为量化。
量化前的信号幅度与量化后的信号幅度出现了不同,这一差值在重现信号时将会以噪声的形式表现出来,所以将此差值称为量化噪声。为了降低这种噪声,只要将量化时阶梯间的间隔减小就可以了。但减小量化间隔会引起阶梯数目的增加,导致数据量增大。所以量化的阶梯数也必须适当,可以根据所需的信噪比(S/N)确定。
将量化后的信号转换为二进制数,即用0和1的码组合来表示的处理过程称为编码,“1”表示有脉冲,“0”表示无脉冲。当量化级数取为64级时,表示这些数值的二进制的位数必须是6位;当量化级数取为256级时,则必须用8位二进制数表示。
2. 基准电压
基准电压就是模数转换器可以转换的最大电压,以8位A/D模数转换器为例,这种转换器可以将0V到其基准电压范围内的输入电压转换为对应的数值表示。其输入电压范围分别对应4096个数值(步长),其计算方法为:参考电压/256=5/256=19.5mV。
看起来这里给出的10位A/D的步长电压值,但上述公式还定义了该模数转化器的转换精度,无论如何所有A/D的转换精度都低于其基准电压的精度,而提高输出精度的唯一方法只有增加定标校准电路。
现在很多MCU都内置A/D,即可以使用电源电压作为其基准电压,也可以使用外部基准电压。如果将电源电压作为基准电压使用的话,假设该电压为5V,则对3V输入电压的测量结果为:(输入电压/基准电压)×255=(3/5)×255=99H。显然,如果电源电压升高1%,则输出值为(3/5.05)×255=97H。实际上典型电源电压的误差一般在2~3%,其变化对A/D的输出影响是很大的。
3. 转换精度
A/D的输出精度是由基准输入和输出字长共同决定的,输出精度定义了A/D可以进行转换的最小电压变化。转换精度就是A/D最小步长值,该值可以通过计算基准电压和最大转换值的比例得到。对于上面给出的使用5V基准电压的8位A/D来说,其分辨率为19.5mV,也就是说,所有低于19.5mV的输入电压的输出值都为0,在19.5mV~39mV之间的输入电压的输出值为1,而在39mV~58.6mV之间的输入电压的输出值为3,以此类推。
提高分辨率的一种方法是降低基准电压,如果将基准电压从5V降到2.5V,则分辨率上升到2.5/256=9.7mV,但最高测量电压降到了2.5V。而不降低基准电压又能提高分辨率的唯一方法是增加A/D的数字位数,对于使用5V基准电压的12位A/D来说,其输出范围可达4096,其分辨率为1.22mV。
在实际的应用场合是有噪音的,显然该12位A/D会将系统中1.22mV的噪音作为其输入电压进行转换。如果输入信号带有10mV的噪音电压,则只能通过对噪音样本进行多次采样并对采样结果进行平均处理,否则该转换器无法对10mV的真实输入电压进行响应。
4. 累积精度
如果在放大器前端使用误差5%的电阻,则该误差将会导致12位A/D无法正常工作。也就是说,A/D的测量精度一定小于其转换误差、基准电压误差与所有模拟放大器误差的累计之和。虽然转换精度会受到器件误差的制约,但通过对每个系统单独进行定标,也能够得到较为满意的输出精度。如果使用精确的定标电压作为标准输入,且借助存储在MCU程序中的定标电压常数对所有输入进行纠正,则可以有效地提高转换精度,但无论如何无法对温漂或器件老化而带来的影响进行校正。
5. 基准源选型
引起电压基准输出电压背离标称值的主要因素是:初始精度、温度系数与噪声,以及长期漂移等,因此在选择一个电压基准时,需根据系统要求的分辨率精度、供电电压、工作温度范围等情况综合考虑,不能简单地以单个参数为选择条件。
比如,要求12 位A/D分辨到1LSB,即相当于1/212=244ppm。如果工作温度范围在10℃,那么一个初始精度为0.01%(相当于100ppm),温度系数为10ppm/℃(温度范围内偏移100ppm)的基准已能满足系统的精度要求,因为基准引起的总误差为200ppm,但如果工作温度范围扩大到15℃以上,该基准就不适用了。
6. 常用基准源
(1)初始精度的确定
初始精度的选择取决于系统的精度要求,对于数据采集系统来说,如果采用n位的ADC,那么其满刻度分辨率为1/2n,若要求达到1LSB的精度,则电压基准的初始精度为:
初始精度≤1/2n=1/2n×102%
如果考虑到其它误差的影响,则实际的初始精度要选得比上式更高一些,比如,按1/2LSB的分辨率精度来计算,即上式所得结果再除以2,即:
初始精度≤1/2n+1=1/2n+1×102%
(2)温度系数的确定
温度系数是选择电压基准另一个重要的参数,除了与系统要求的精度有关外,温度系数还与系统的工作温度范围有直接的关系。对于数据采集系统来说,假设所用ADC的位数是n,要求达到1LSB的精度,工作温度范围是ΔT,那么基准的温度系数TC可由下式确定:
同样地,考虑到其它误差的影响,实际的TC值还要选得比上式更小一些。温度范围ΔT通常以25℃为基准来计算,以工业温度范围-40℃~+85℃为例,ΔT可取60℃(85℃-25℃),因为制造商通常在25℃附近将基准因温度变化引起的误差调到最小。
图7.15 系统精度与基准温度系数TC的关系
如图7.15所示是一个十分有用的速查工具,它以25℃为变化基准,温度在1℃~00℃变化时,8~20位ADC在1LSB分辨精度的要求下,将所需基准的TC值绘制成图,由该图表可迅速查得所需的TC值。
TL431和REF3325/3330均为典型的电压基准源产品,详见表7.22。TL431的输出电压仅用两个电阻就可以在2.5~36V范围内实现连续可调,负载电流1~100mA。在可调压电源、开关电源、运放电路常用它代替稳压二极管。REF3325输出2.5V,REF3330输出3.0V。
表7.22 电压基准源选型参数表
REF33xx是一种低功耗、低压差、高精密度的电压基准产品,采用小型的SC70-3和SOT23-3封装。体积小和功耗低(最大电流为5μA)的特点使得REF33xx系列产品成为众多便携式和电池供电应用的最佳选择。在负载正常的情况下,REF33xx系列产品可在高于指定输出电压180mV的电源电压下工作,但REF3312除外,因为它的最小电源电压为1.8V。
图7.16 12bits系统基准选择
从初始精度和温漂特性来看,REF3325/3330均优于TL431,但是TL431的输出电压范围很宽,且工作电流范围很大,甚至可以代替一些LDO。
由于基准的初始精度和温漂特性是影响系统整体精度的关键参数,因此它们都不能用于高精密的采集系统和高分辨率的场合。而对于12bits的AD来说,由于精度要求在0.1%左右的采集系统,到底选哪个型号呢?测量系统的初始精度,均可通过对系统校准消除初始精度引入的误差;对于温漂的选择,必须参考1LSB分辨精度来进行选择,详见图7.16。
如果不是工作在严苛环境下,通常工作温度为-10℃~50℃,温度变化在60℃,如果考虑0.1%系统精度,温度特性低于50ppm,则选择REF3325/3330。
7.6.2 A/D转换接口
AWorks提供了A/D转换接口,可以直接通过接口获取相应引脚输入的模拟电压大小。相关接口详见表7.23。
表7.23 ADC通用接口函数
1. 获取ADC通道的采样率
获取当前ADC通道的采样率,采样率的单位为Samples/s,表示每秒进行多少次采样。其函数原型为:
其中,ch为ADC的通道号,p_rate为输出参数,用以得到指定通道的采样率。返回值为标准的错误号,返回AW_OK时表示获取成功,否则,表示获取失败,失败的原因可能是通道号不存在。
通常情况下,一个A/D转换器往往支持多个通道,即可以支持多路模拟信号输入,在部分微控制器中,还存在多个A/D转换器。在AWorks中,为了区分各个模拟信号输入的通道,为每个通道定义了一个唯一的通道号。例如,在i.MX28x中,有LRADC和HSADC两个A/D转换器,它们分别支持16个通道和8个通道。各通道对应的编号在{chip}_adc_def.h文件中使用宏的形式进行了定义。例如,在i.MX28x中,各个通道号在imx28x_adc_def.h文件中定义如下:
由此可见,通过宏的形式,将通道号 0 ~ 23 使用了更具有意义的宏来表示。用户也可通过查看此文件获知当前硬件平台支持的ADC通道数目。
通道号的类型为aw_adc_channel_t,其本质上是一个无符号整数类型,具体的位宽与平台相关。如一个平台中仅支持24个通道,则其类型可能为uint8_t,即使用8位来表示通道号。获取通道0的采样率范例程序详见程序清单7.40。
程序清单7.40 获取采样率的范例程序
2. 设置ADC通道的采样率
由于采样频率必须大于或等于模拟信号中最高频率的2倍,才能够无失真地恢复原信号,因此,实际中,可能需要根据模拟信号的频率调整A/D转换器的采样率。设置某一通道采样率的函数原型为:
其中,ch为ADC的通道号,rate为设置的采样率。返回值为标准的错误号,返回AW_OK时表示设置成功,否则,表示设置失败。
一般情况下,若对采样率没有特殊的要求,使用默认的采样率即可。此外,A/D转换器可能只支持部分采样率,并不能支持任意的采样率,当使用该函数设置采样率时,系统会自动设定一个最为接近的采样率,因此,实际采样率可能与设置的采样率存在差异,实际采样率可由aw_adc_rate_get()函数获取。
注意,通常情况下,一个A/D转换器的所有通道共用一个采样率,因此,设置其中一个通道的采样率时,可能会影响其它通道的采样率。
设置通道0的采样率为1000的范例程序详见程序清单7.41。
程序清单7.41 设置采样率范例程序
设置采样率为1000 Samples/s。表示每秒采集1000个数据点,即每隔1毫秒采集一个数据点。
3. 获取ADC通道的基准电压
一般来讲,在对精度要求不是特别严格的场合,可以直接使用MCU的电源电压作为ADC的基准电压。但若对精度要求较高,往往需要使用具有更高精度的外部基准源电压作为ADC的基准电压。当前ADC实际使用的基准电压可通过该接口获得,其函数原型为:
其中,ch为ADC的通道号。基准电压通过返回值返回:若返回值大于0,则获取成功,其值即为基准电压(单位:mV);若返回值小于0,则获取失败。
获取通道0的基准电压范例程序详见程序清单7.42。
程序清单7.42 获取基准电压范例程序
例如,基准电压为2.5V,则vref的值为2500。
4. 获取ADC通道的转换位数
获取ADC通道的转换位数,其函数原型为:
其中,ch为ADC的通道号。转换位数通过返回值返回:若返回值大于0,则获取成功,其值即为转换位数;若返回值小于0,则获取失败。
获取通道0的转换位数范例程序详见程序清单7.43。
程序清单7.43 获取转换位数范例程序
如在i.MX28x中,LRADC和HSADC均为12位AD转换器,因此,bits的值为12。
5. 读取ADC通道的采样值
读取指定通道的采样值,其函数原型为:
其中,ch为ADC的通道号,p_val为存储采样值的缓冲区,samples指定本次采样的次数,urgent指定本次读取操作的优先级。返回值为标准的错误号,返回AW_OK时表示读取成功,否则,表示读取失败。
p_val指向用于存储采样值的缓冲区,缓冲区实际类型与ADC的位数相关:若ADC的位数为 1 ~ 8,则其类型为uint8_t;若ADC的位数为9 ~ 16,则其类型为uint16_t;若ADC的位数为 17 ~ 32,则其类型为uint32_t。例如,在i.MX28x中,ADC的位数为12,则应使用uint16_t类型的缓冲区来存储ADC的采样值。如定义一个大小为100的缓冲区,以存储100个采样值:
samples表示本次读取的采样值个数。实际应用中,每次读取操作往往会读取多个采样值,以便通过取平均值等方法对采样值进行处理,得到更加准确的结果。多个采样值将依次存放在p_val指向的缓冲区中,需确保p_val指向的缓冲区的大小与samples保持一致。
实际上,读取采样值的操作包含的完整过程为:首先需要启动ADC转换,然后等待转换完成(转换的时间与采样率相关),转换完成后,再将转换结果存储在用户提供的缓冲区中。显然,整个过程需要消耗一定的时间。虽然一个A/D转换器往往有多个通道,但某一时刻只能对某一个通道的输入进行转换,并不能同时转换多个通道。若当前A/D转换器正在转换中,则后续其它通道的转换请求就只能排队等待,urgent指定了转换请求的紧急性,其决定了排队的方式,若urgent为TRUE,表示紧急转换请求,排队时将插队到头部,以便当前A/D转换结束后,立即启动需要紧急转换的通道;若urgent为FALSE,则排队时将依次排至尾部。一般情况下,没有特殊需求,urgent均设置为FALSE。
读取100个采样值的范例程序详见程序清单7.44。
程序清单7.44 读取采样值的范例程序
此时,多个采样值存储在adc_val中,实际中,每次读取多个采样值只是为了通过处理得到一个更加精确的采样值,最简单的处理方法就是取平均值,范例程序详见程序清单7.45。
程序清单7.45 数据处理范例程序(取平均值)
注意,程序中,为了避免sum溢出,将sum的类型定义为了32位无符号数。最终的结果存储在code变量中。
至此,获得了一个较为精确的ADC采样值,但在实际使用A/D转换器时,其目的往往并非简单的获取一个ADC采样值,而是获取相应通道的电压值。可以通过基准电压和转换位数将code转换为电压值,公式如下:
式中,code为读取的编码值,Vref为基准电压,bits为ADC的位数。实际中,2的bits次方可以简化为移位运算,即:
获取通道0输入电压的完整范例程序详见程序清单7.46。
程序清单7.46 电压采集综合范例程序
在i.MX28x中,通道0对应的外部输入引脚时LRADC0引脚,运行程序后,可以通过向该引脚接入模拟电压来测试ADC采集的结果是否正确。
程序中,电压值的计算未使用到浮点运算,仅使用了整数运算,效率较高。但运算结果vol的值也只能精确的mV,若需要提高计算结果的精度,可以使用浮点数来存储计算的结果,将vol的类型定义为float,即:
同时,在计算电压值时,要确保表达式使用浮点运算,即:
-
转换器
+关注
关注
27文章
8639浏览量
146897 -
致远电子
+关注
关注
13文章
405浏览量
31278
原文标题:AWorks软件篇 — 通用外设接口(I²C、UART、ADC)
文章出处:【微信号:ZLG_zhiyuan,微信公众号:ZLG致远电子】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论