DA14580在两处体现了ADC功能。一是电源管理方面,10bit的ADC能够测量电池电压;二是模拟接口方面,有4通道的10bit AD转换。

10bit ADC可单端可差分,一次转换耗时65ns,最大转换率3.3M/s,100K转换率时功耗才5uA。并且有1.2V和3.6V两种满量程配置。


ADC固定使用P0-0至P0-3四个引脚。
下面看ADC的寄存器。
ADC控制寄存器1


寄存器bit12 GP_ADC_MUTE设置为1的时候,用于进行ADC的校准。分别将bit10 GP_ADC_SIGN置1和0,打开ADC采样可以分别得到正负偏移值,此值分别写入下面两个寄存器:GP_ADC_OFFP_REG 和 GP_ADC_OFFN_REG。
ADC正负偏移值寄存器
官方SDK提供的校准程序如下:
/**
****************************************************************************************
* @brief ADC calibration sequence.
* @return void
****************************************************************************************
*/
void adc_calibrate(void)
{
uint32_t adc_res1, adc_res2;
int32_t adc_off_p, adc_off_n;
int32_t new_adc_off_p, new_adc_off_n;;
SetWord16(GP_ADC_CTRL_REG, GP_ADC_LDO_EN|GP_ADC_SE|GP_ADC_EN);
adc_usDelay(20);
SetWord16(GP_ADC_OFFP_REG, 0x200);
SetWord16(GP_ADC_OFFN_REG, 0x200);
SetBits16(GP_ADC_CTRL_REG, GP_ADC_MUTE, 1);//校准时,此位置1
SetBits16(GP_ADC_CTRL_REG, GP_ADC_SIGN, 0);//采集正向偏移值
adc_res1 = adc_get_sample();
adc_off_p = adc_res1 - 0x200;
SetBits16(GP_ADC_CTRL_REG, GP_ADC_SIGN, 1);//采集负向偏移值
adc_res2 = adc_get_sample();
adc_off_n = adc_res2 - 0x200;
new_adc_off_p = 0x200 - 2 * adc_off_p;
new_adc_off_n = 0x200 - 2 * adc_off_n;
SetWord16(GP_ADC_OFFP_REG, new_adc_off_p);
SetWord16(GP_ADC_OFFN_REG, new_adc_off_n);
}
ADC控制寄存器2

该寄存器的bit3,置1可以实现信号的3倍衰减,也就是可以采样最大幅度为1.2*3=3.6V的信号。
ADC中断复位寄存器,写任何值,均可以清除ADC中断。

ADC结果值寄存器 每次AD转换结果后,10bit的结果值存在该寄存器
ADC结果值寄存器 每次AD转换结果后,10bit的结果值存在该寄存器
ADC延时寄存器

/**
****************************************************************************************
* @brief Gets ADC sample from VBAT1V or VBAT3V power supplies using the 20 usec delay.
* @param[in] sample_vbat1v :true = sample VBAT1V, false = sample VBAT3V
* @return ADC VBAT1V or VBAT3V sample
****************************************************************************************
*/
uint32_t adc_get_vbat_sample(bool sample_vbat1v)
{
uint32_t adc_sample, adc_sample2;
adc_init(GP_ADC_SE, GP_ADC_SIGN, GP_ADC_ATTN3X);//
adc_usDelay(20);
if (sample_vbat1v)
adc_enable_channel(ADC_CHANNEL_VBAT1V);
else
adc_enable_channel(ADC_CHANNEL_VBAT3V);
adc_sample = adc_get_sample();
adc_usDelay(1);
adc_init(GP_ADC_SE, 0, GP_ADC_ATTN3X );
if (sample_vbat1v)
adc_enable_channel(ADC_CHANNEL_VBAT1V);
else
adc_enable_channel(ADC_CHANNEL_VBAT3V);
adc_sample2 = adc_get_sample();
//We have to divide the following result by 2 if
//the 10 bit accuracy is enough
adc_sample = (adc_sample2 + adc_sample);
adc_disable();
return adc_sample;
}
/**
****************************************************************************************
* @brief ADC module initialization.
* @param[in] mode :0 = Differential mode, GP_ADC_SE(0x800) = Single ended mode
* @param[in] sign :0 = Default, GP_ADC_SIGN(0x0400) = Conversion with opposite sign at
input and output to cancel out the internal offset of the ADC and
low-frequency
* @param[in] attn :0 = attenuation x1, GP_ADC_ATTN3X(0x0002) = attenuation x3
* @return void
****************************************************************************************
*/
void adc_init(uint16_t mode, uint16_t sign, uint16_t attn)
{
SetWord16(GP_ADC_CTRL_REG, GP_ADC_LDO_EN | mode | sign);
SetWord16(GP_ADC_CTRL_REG, GP_ADC_LDO_EN | mode | GP_ADC_EN | sign);
SetWord16(GP_ADC_CTRL2_REG, GP_ADC_DELAY_EN | GP_ADC_I20U | attn ); // Enable 3x attenuation
}
/**
/**
****************************************************************************************
* @brief Enable selected channel.
* @param[in] input_selection Input channel
* @return void
****************************************************************************************
*/
void adc_enable_channel(uint16_t input_selection)
{
SetBits16(GP_ADC_CTRL_REG,GP_ADC_SEL,input_selection & 0xF);
}
最后还有一个把AD转换值换算成电量百分比的函数:
/**
****************************************************************************************
* @brief Calculates battery level percentage for CR2032 batteries
* @param[in] adc_sample adc sample
* @return Battery level. 0 - 100%
****************************************************************************************
*/
uint8_t batt_cal_cr2032(uint16_t adc_sample)
{
uint8_t batt_lvl;
if (adc_sample > 1705)
batt_lvl = 100;
else if (adc_sample <= 1705 && adc_sample > 1584)
batt_lvl = 28 + (uint8_t)(( ( ((adc_sample - 1584) << 16) / (1705 - 1584) ) * 72 ) >> 16) ;
else if (adc_sample <= 1584 && adc_sample > 1360)
batt_lvl = 4 + (uint8_t)(( ( ((adc_sample - 1360) << 16) / (1584 - 1360) ) * 24 ) >> 16) ;
else if (adc_sample <= 1360 && adc_sample > 1136)
batt_lvl = (uint8_t)(( ( ((adc_sample - 1136) << 16) / (1360 - 1136) ) * 4 ) >> 16) ;
else
batt_lvl = 0;
return batt_lvl;
}
注意,以上函数中if (adc_sample > 1705),电量等于100%。可能有些人不理解,因为10bit 的AD采样转换值最大只是1024,怎么可能出来1705这个数?
其实仔细看前面的采样函数adc_get_vbat_sample(bool sample_vbat1v)就知道了, 该函数末尾一句是adc_sample = (adc_sample2 + adc_sample);就是把正向采集的结果和反向采集的结果相加,所以等于满量程是10242=2048了。1705/20483.6V=2.997V,也就是说CR2032电池电压是3V时,认为时100%电量。CR2032的标称电压就是3V。
下面附一张我们实测的图:

000003A4是十六进制,转换为十进制是932。可以看出正向和反向转换结果均为932。
932+932=1864,1864/2048*3.6V=3.2765625V;误差率:1-3.2765625/3.3=1-99.3%=0.7%
这是在3.3V供电下测出的结果,还是很准确的,毕竟供电的LDO输出也是有误差的。

1208

被折叠的 条评论
为什么被折叠?



