ADC使用指南
1. ADC 介绍
目前ADC 有8个通道可以使用,单元测量范围 0 ~ 1.0 V, 可以单独通道测试,也可以多通道循环测试;可以测试一组数据后结束,也可以一直测试输出数据。
2. ADC 配置
ADC的配置包括PINMUX的配置和ADC时钟相关的配置,时钟等参数的配置在adc_config.h中 定义,用户可以根据需要进行修改。
PINMUX的设置目前A0/Z0的方式稍有不同, 在Z0上,如果PIN当作ADC使用,可以在设置PIN时直接选择ADC功能(比如HAL_PIN_Set(PAD_PB_04, ADC_PIN, PIN_PULLUP, 0)这种方式);
而在A0上,每个可配置成GPADC的管脚,都对应设置了不同的FUNCTION: GPADC_CH0/GPADC_CH1 …
在使用时,要按照各自PIN来设置,比如 PAD_PB8 对应的GPADC 为通道0,那设置应该为: HAL_PIN_Set(PAD_PB08, GPADC_CH0, PIN_NOPULL, 0)
而PAD_PB13 对应的GPADC为通道3, 则应该设置为: HAL_PIN_Set(PAD_PB13, GPADC_CH3, PIN_NOPULL, 0)
也可以直接设置PIN为模拟功能,不再具体关心每个PIN对应的具体模拟功能,如HAL_PIN_Set_Analog(PAD_PB08, 0) / HAL_PIN_Set_Analog(PAD_PB13, 0)
在A0上各个可用做GPADC的PAD 及功能对应表:
PAD 号 |
通道号 |
描述 |
---|---|---|
PAD_PB08 |
GPADC_CH0 |
挂载到LCPU |
PAD_PB10 |
GPADC_CH1 |
挂载到LCPU |
PAD_PB12 |
GPADC_CH2 |
挂载到LCPU |
PAD_PB13 |
GPADC_CH3 |
挂载到LCPU |
PAD_PB16 |
GPADC_CH4 |
挂载到LCPU |
PAD_PB17 |
GPADC_CH5 |
挂载到LCPU |
PAD_PB18 |
GPADC_CH6 |
挂载到LCPU |
PAD_PB19 |
GPADC_CH7 |
挂载到LCPU |
3.ADC 使用
在我们系统中,默认将ADC注册成了电池电压设备, 可以使用设备方式进行操作读写, 默认设备名为 “bat1”:
uint32_t chnl = 1;
uint32_t value;
// find device
rt_device_t dev = rt_device_find(argv[2]);
if (dev)
{
// open device
rt_device_open(dev, RT_DEVICE_FLAG_RDONLY);
// enable adc
rt_device_control(dev, RT_ADC_CMD_ENABLE, (void *)chnl);
// read adc value
rt_device_read(dev, chnl, &value, 1);
}
......
4.电压值的计算
关于通过ADC获取电压,可以通过接口(HAL_ADC_GetValue, 如果用rt_device 则用read接口)获取ADC值.
寄存器数值每增加1,电压增加1mv左右(每颗芯片不会完全相同,需要每个芯片都做出厂校准).
ADC值和电压之间是线性关系,而如何校验获取精准的偏移和斜率,可以参考函数(sifli_adc_calibration)。
其原理和方法为:通过两点确定一条直线,后面通过新的一个坐标值(寄存器值)在直线上算出另外一个坐标—-即电压值
具体操作为:
ATE几台将已知两个准确的电压(比如0.3, 0.8, 不能为0或者最大电压值), 接入ADC后,读取ADC值,将两个电压值和寄存器值保存,后续应用通过两点确定一条直线的方式,从而可以获取电压的偏移(0V时对应的电压值,不过在0附近时并不精确)和斜率(寄存器值每增加1对应的电压增加值),可以将这组数值保存以便后续电压测试使用。
寄存器值转换电压的参考代码如下:
// default value, they should be over write by calibrate
// it should be register value offset vs 0 v value.
static uint32_t adc_vol_offset = 200;
// mv per bit, if accuracy not enough, change to 0.1 mv or 0.01 mv later
static uint32_t adc_vol_ratio = 3930; //6;
/**
* @brief Get voltage by register value.
* @param[in] value register value.
* @retval voltage in mv.
int sifli_adc_get_mv(uint32_t value)
{
uint32_t offset, ratio;
// get offset
offset = adc_vol_offset;
// get ratio
ratio = adc_vol_ratio;
return (value - offset) * ratio / ADC_RATIO_ACCURATE;
}
/**
* @brief Get voltage offset and ratio.
* @param[in] value1 register value 1.
* @param[in] value2 register value 2.
* @param[in] vol1 voltage 1 in mv.
* @param[in] vol2 voltage 2 in mv.
* @param[out] offset, reg value offset vs 0v value.
* @param[out] ratio, voltage (mv)per bit, ratio with 100 based or 0.01 mv, 1 base for 1 mv
* @retval offset.
*/
int sifli_adc_calibration(uint32_t value1, uint32_t value2,
uint32_t vol1, uint32_t vol2, uint32_t *offset, uint32_t *ratio)
{
uint32_t gap1, gap2;
if (offset == NULL || ratio == NULL)
return 0;
gap1 = value1 > value2 ? value1 - value2 : value2 - value1; // register value gap
gap2 = vol1 > vol2 ? vol1 - vol2 : vol2 - vol1; // voltage gap -- mv
if (gap1 != 0)
{
*ratio = gap2 * ADC_RATIO_ACCURATE / gap1; // gap2 * 10 for 0.1mv, gap2 * 100 for 0.01mv
adc_vol_ratio = *ratio;
}
*offset = value1 - vol1 * ADC_RATIO_ACCURATE / adc_vol_ratio;
adc_vol_offset = *offset;
return adc_vol_offset;
}