下面的代码实现了,通过读取STC8G1K08A FLASH中记录的出厂校准过的带隙电压值,直接计算VCC,过程完全在单片机内部进行,不需要外部连线!可以用于单节锂电池的应用场合直接测量VCC。复用的话直接在定时器中断中调用VCC_CAL函数,给vcc赋值。
代码调用后会在for循环中采样8次,输出数组,并通过冒泡排序取中间的四个值,然后四舍五入求平均,不需要测量频率很高,100ms一次就行了。
测量原理:stc单片机内部有一个电压接近1.19v的带隙基准电压(后文简称BG),出厂校准值直接存储在flash中,具体位置可以在手册中“存储器”章节寻找。通过读取这个电压,再使用ADC测量15通道的回报值,就可以直接计算VCC,这一过程完全在单片机内部实现,不需要外部连线。
具体原理为,adc测量电压后会回报一个数值,叫做码数,adc是靠比较测量电压的,参考源是vcc。如果是十位adc,码数范围为0~1023,12位为4096。stc单片机adc的15通道固定测量内部bg电压值,读取这个电压,可以得到1.19v bg电压对应的码数,通过除法计算,可以直接得到毫伏每码这个值。而adc测量vcc,由于参考的就是vcc,所以回报的是满量程值,那么就可以省去测量vcc的过程,直接用满量程码数1024乘以毫伏每码,直接得到vcc电压。
而bg电压是一个在1.19v左右的值,会因为制造过程产生差异,烧录程序时我们可以看到软件会回报带隙电压校准值,实际上这个值就是直接存储在flash中的,所以可以手动读取。这个值为一个分为高八位和低八位的十六位二进制数,高字节在前,分为4个四位二进制组,读取之后可以直接组合为毫伏整数值。可以直接用于计算。
//计算VCC用的变量(全局)
unsigned int bgv = 0;//多个函数调用,声明为全局变量
unsigned int vcc = 0;//值超过256,不能用char,int可以到65536
//char为字符型,8位,int整数型,16位
// 从idata读取带隙电压值
unsigned int BGV_READ(void) {
unsigned int temp = *((unsigned int idata *)0xEF);
return temp;//给BGV_READ赋值
}
// ADC读取函数
unsigned int ADC_READ(void) {
unsigned int res;
ADC_CONTR |= 0x40; // 启动ADC转换
_nop_(); _nop_(); _nop_(); _nop_(); // 短暂延时
while (!(ADC_CONTR & 0x20)); // 等待转换完成
ADC_CONTR &= ~0x20; // 清除完成标志
res = (ADC_RES << 8) | ADC_RESL; // 合并高8位和低8位
return res;
}
//ADC软件滤波(连续读取8次)
unsigned int ADC_FILTER(void) {
unsigned char i, j;
unsigned int k;
unsigned long sum = 0;
unsigned int vccvlaue = 0;
unsigned int nADC_BUFF[8]; //改成数组
// 采集8个样本
for(i = 0; i < 8; i++) {
nADC_BUFF = ADC_READ(); //存入数组对应位置
}
// 冒泡排序(升序)
for(j = 0; j < 7; j++) {
for(i = 0; i < 7 - j; i++) {
if(nADC_BUFF > nADC_BUFF[i+1]) {
k = nADC_BUFF;
nADC_BUFF = nADC_BUFF[i+1];
nADC_BUFF[i+1] = k;
}
}
}
// 取中间4个值(索引3~6)求平均
for(i = 3; i <= 6; i++) {
sum += nADC_BUFF;
}
sum = (sum + 2) / 4; // 四舍五入并求平均
// 计算VCC(使用带隙电压bgv),1024对应10位ADC
vccvlaue = (unsigned int)(1024UL * bgv / sum);
return vccvlaue;
}
void VCC_CAL(void) {
// 初始化ADC
//P_SW2 = 0x80; //允许访问扩展寄存器。但是IO配置中已经打开
ADCTIM = 0x3F; //设置ADC采样时间
ADCCFG = 0x2F; //ADC时钟 = 系统时钟/2/16
ADC_CONTR = 0x8F; //使能ADC,选择通道15(带隙电压)
//读取带隙电压
if (bgv == 0) {
bgv = BGV_READ();
}
// 计算VCC
vcc = ADC_FILTER();//在中断中调用这个vcccal函数,而不是调用adcfilter或者adcread
}
|