DA14580的AD转换

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输出也是有误差的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ydgd118

您的鼓励是我最大的动力!谢赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值