下面的代碼實現了,通過讀取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[ i] = ADC_READ(); //存入數組對應位置
}
// 冒泡排序(升序)
for(j = 0; j < 7; j++) {
for(i = 0; i < 7 - j; i++) {
if(nADC_BUFF[ i] > nADC_BUFF[i+1]) {
k = nADC_BUFF[ i];
nADC_BUFF[ i] = nADC_BUFF[i+1];
nADC_BUFF[i+1] = k;
}
}
}
// 取中間4個值(索引3~6)求平均
for(i = 3; i <= 6; i++) {
sum += nADC_BUFF[ i];[ i]
}
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
}
|