使用8-bit G1 系列 ADC时的注意事项以及如何补偿Zero Offset
ABOV现代单片机的 8 位 G1 系列是通用 MCU,配备12bit分辨率的 ADC(模数转换器),因此可用于控制二次电池充电器。一般而言,充电器应用需要低电压频段的高精度 A-D 转换器。 因此,想解释一下使用ADC时要考虑的几点以及零偏移补偿的方法。
首先了解ADC的使用方法及注意事项.
[ADC Block Diagram]
8 位 G1 系列中安装的 12 位 ADC 具有相同的结构,但输入通道数、触发和外部电压Refence支持请参考每个 MCU 的框图.

[图 1] ADC 框图 (Example:
A96G174)
[A-D Conversion Speed]
8-bit G1系列搭载的A-D转换器时SAR(逐次比较)型,没有Sample & Hold,所以可以快速使用A-D转换速度。
A-D 转换的时钟允许高达 8MHz,A-D 转换过程有 4 个步骤(4 Clock edges)来转换每个位,另外需要 12 个时钟。
例如,如果系统工作时钟 (fx) 为 16MHz,A-D 转换时钟除以 2 以使用 8MHz 的最大时钟,则公式如下。

A-D 转换时间至少为 (1/8MHz) x 60 clocks = 7.5us 或更多,最好选择尽可能快的时间.
[参考电压选择]
A-D 转换的参考电压可以通过使用 MCU 的 VDD(内部参考)或直接将参考电压施加到 AVREF 引脚(外部参考)来使用。使用 AVREF 引脚时,噪声的影响小于 VDD,因此可以获得更准确的结果,但很难将 AVREF 引脚用于其他用途。 但是,在非常小的 A-D 输入信号的情况下,施加的 AVREF 电压越低,分辨率就越高。
可以施加到 AVREF 的电压应设置为与 VDD 电压相同或低于 VDD 电压,并且应至少为 1.8V。 如果 AVREF 电压高于 VDD 电压,则从 AVREF 引脚通过引脚保护二极管向 VDD 供电,导致状态不稳定。使用 AVRE时,必须使用电容器施加正确的直流电压。
[图 2] AVREF 引脚供应电压高于VDD

[表 1] AVREF Pin 有无
[A-D 输入信号选择]
理想的 A-D 转换输入应该是没有噪声的完美直流信号。 也就是说,输入带限幅的信号很重要。 通常,使用 R-C 滤波器来抑制输入信号中引起的噪声。如果电阻值影响输入电平,则存在仅使用简单电容器的情况。 对于 A-D 信号输入,根据分配的引脚设置输入。通常最后一个输入连接到内部恒压电源(1.2V),无需 A-D 信号输入引脚分配。
[A-D 转换时序]
A-D转换的启动可以直接在用户程序中设置,也可以通过同步信号(Trigger)自动启动。 在电源状态最稳定的时候进行 A-D 转换是排除电源噪声带来的不利影响的基础。特别是在使用 VDD 参考电压时,必须避免在通过输入/输出端消耗大电流的开关部分进行 A-D 转换。在相关区间, 由于Overshoot、Undershoot或VSS晃动等多种因素, A-D转换值的直接偏差出现。 A-D转换开始的同步信号(Trigger)可以通过External Interrupt或Timer设置。
[Linearity Characteristics]
DNL(Differential Non-Linearity)特性是数值在特定位置异常反弹的现象,INL(Integral Non-Linearity)特性是误差累积导致与理想倾斜发生背离的因素, 在输入接近标准电压的信号时, 以最大值(0xFFF)之间的差异进行说明。
[零输入偏置补偿]
在精密 A-D 转换中,零偏置特性会导致测量微小电压信号时出现致命错误。例如,在基于2.8V电源电压测量充电结束电流时,5mV ADC转换结果值在理论上是0x007电平的小值,因此ADC Zero Input Offset管理是一个重要项。如果零偏置 值为 -6,则 5mV A-D 转换的结果为 0x001,不能视为有效值。特别是负偏置特性是几个mV的小信号在AD转换器中全部被读取为0x000,所以测量本身有时是不可能的,应该修正以提高精度。设置可以区分最小输入电压的偏置,并设置可以使用的负偏置的限制。

[图 3] ADC 零偏置特性 (Example: A96G174)
[图2] 测量每个 MCU 的 A-D 转换器输入偏置值,并将其记录在特定的内存位置。用户在启动程序时预先读取写入各个MCU的偏置值,经过A-D转换后,从读取的值中减去偏置量,处理成准确的A-D转换值。零偏置补偿仅适用于精确测量 100mV 或更低的低输入电压。 零偏置补偿的参考电压是内部参考电压 (VDD)。

* NOTE) 将来会支持此设备。如有需要,请与我司联系.
[表 2] ADC 零偏置储存位置
以上使用 ADC 时的注意事项和 ADC 零偏置补偿方法已经进行了说明,但这次,让我们看看如何应用零偏置与 S/W 和 ADC 转换算法。
读取A-D 偏置值
为了精密的A-D转换,存储唯一的ADC零偏置值. 用户读取偏置值存储区【表 2】中的 ADC 零偏置值,通过 A-D 转换后的测量值减去 ADC 零偏置值进行补偿,得到正确的 A-D 转换值。. 在生产MCU的测试过程中,ADC Zero Offset值记录在偏置值存储区[表2]中。 数据类型为有符号字符,测量参考电压为内部参考电压(VDD)。
A-D 转换算法
如果噪声严重或输入信号的波动范围较大,则应用Mean-Average滤波算法得到平均值。A-D转换的次数越高,需要的RAM(保存 A-D转换值的Buffer)就越多,但可以获得更准确的值。本应用中提供的 AD 转换输出是经过 6 次读取的初始 AD 转换值中除最大值和最小值之外的四个 AD 转换值的平均值并减去唯一的 ADC 零偏置量后校正的最终 AD 转换值生产时储存。
示例代码 – 使用 ADC 零偏置
8-bit G1系列的A-D转换过程基本类似,例子是A-D转换为A96G174应用ADC 零偏置的代码。每个 MCU 的 ADC 零偏置地址都不同,请参阅 [表 2]。.
▶ 读取ADC零偏置值
#define CODEROM ((char volatile code *) 0) #define OFFSET_ADDRESS (0x2068) // refer to offset address of each mcu char adc_offset = 0; // signed char void Get_ADC_Offset(void) { unsigned char IE_temp; IE_temp = IE; // Backup IE IE = 0; // Disable Global Interrupt FESR |= 0x20; // Remap Enable adc_offset = CODEROM[OFFSET_ADDRESS]; // Read ADC Zero Offset Value FESR &= ~(0x20); // Remap Disable IE = IE_temp; // Restore IE |
▶ 12-bit A-D 转换器设置
void main( void ) { ⁞ /* Initialize ADC */ ADCCRL = 0x00 // Initial | (1<<7) // 1 : Enable ADC Module | (0<<6) // 0 : A/D Conversation No effect | (0<<5) // 0 : Internal Reference(VDD) | (0<<0) // 0 : A/D Converter Input Select(AN0) ; ADCCRH = 0x00 // Initial | (0<<6) // 0 : External Input Source Select(for use ANx) | (0<<3) // 0 : ADST(ADC Conversion Start from User S/W) | (1<<2) // 0 : LSB align | (3<<0) // 3 : A/D Converter Clock Selection (ADC clock should use below 8MHz) ; ⁞ while(1) { ⁞ ADC_Mean_Average(); ⁞ } } |
▶ Mean & Average 算法
uint16_t adc_data = 0; uint16_t temp_data[6]; void ADC_Mean_Average(void) { uint8_t i, save_array_number; uint16_t compare_data; for(i=0; i<6; i++) { temp_data[i] = Get_ADC12(); // ADC Data 6 time Read and save } compare_data = 0x1000; // Clear minimum data for(i=0; i<6; i++) { if(temp_data[i] < compare_data) // Compare Data { compare_data = temp_data[i]; save_array_number = i; } } temp_data[save_array_number] = 0; // Replace ADC Data compare_data = 0x0; // Clear maximum data for(i=0; i<6; i++) { if(temp_data[i] > compare_data) // Compare Data { compare_data = temp_data[i]; save_array_number = i; } } temp_data[save_array_number] = 0; // Replace ADC Data compare_data = 0x0; // Sum four adc data for(i=0; i<6; i++) { compare_data += temp_data[i]; } adc_data = compare_data >> 2; // Average four adc data adc_data -= (short)adc_offset; // adc offset adjustment } |