PIC12F1822のA/Dコンバータを利用して、乾電池チェッカーを試作しました。
乾電池は、消耗すると起電力が低下するとともに内部抵抗が大きくなります。起電力は、A/Dコンバータで簡単に測定できます。内部抵抗は、負荷抵抗に電流を流し、その電圧降下から計算することができます。回路図と計算式です。
PIC12F1822のFVR(固定参照電圧)を1.024×2=2.048Vとします。これにより、A/Dコンバータの変換値は、
2.048Vの時、1023になります。したがって、A/D変換値に1を加え2倍すれば、実際の電圧の1000倍の値になります。この値を使って、電圧を表示します。(浮動小数点計算はしません)
電池の内部抵抗rは、上の式で計算しますが、E,Vは実際の値ではなく、A/D変換値をそのまま使います。
というのは、A/D変換値(起電力Ead,負荷抵抗をつけた時の値Vad)から実際の電圧を計算すると
E=(Ead+1)/500,V=(Vad+1)/500となり、これを代入すると
r=R*(Ead-Vad)/(Vad+1) となり、A/D変換値だけで計算ができます。これも浮動小数点計算をしないために、
r=R*(Ead-Vad)*10/(Vad+1)としてrの10倍の値を計算しています。
SWを押す前は、起電力Eが表示されます。(1MΩに1uA程度の電流が流れますが、ほとんど影響ありません)
SWを押すと電池から負荷抵抗100Ωに電流(約15mA)が流れて、内部抵抗による電圧降下で電圧が下がります(測定値V)。
実際に少し消耗した電池を測定してみます。(中国製のマンガン電池)
完全に消耗した電池
消耗した電池は、起電力の低下だけでなく、内部抵抗も増えますので負荷抵抗を通したときに大きく電圧が低下します。電圧と内部抵抗で消耗の度合いが判断できます。
プログラムです。
/*
* File: main.c
* Author: JH7UBC Keiji Hata
* PIC12F1822 Battery Checker
* Created on 2019/02/16
*/
* File: main.c
* Author: JH7UBC Keiji Hata
* PIC12F1822 Battery Checker
* Created on 2019/02/16
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
// CONFIG2
#pragma config WRT = OFF
#pragma config PLLEN = ON
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
#pragma config WRT = OFF
#pragma config PLLEN = ON
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
/* RA1 SCL
* RA2 SDA
* RA3 SW
* 乾電池の内部抵抗を計算する
* E=電池の起電力
* r=電池の内部抵抗
* R=負荷抵抗
* V=負荷抵抗の両端の電圧(測定値)
* V=E*R/R+r
* VR+Vr=ER
* Vr=ER-VR=R(E-V)
* r=R(E-V)/V
* Ead=EのAD変換値
* Vad=VにAD変換値
* FVR=2.048VとするとE=(Ead+1)/500 V=(Vad+1)/500
* r=R*((Ead+1)/500 - (Vad+1)/500)/(Vad+1)=R*(Ead+1-Vad-1)/(Vad+1)=R*(Ead-Vad)/(Vad+1)
*/
* RA2 SDA
* RA3 SW
* 乾電池の内部抵抗を計算する
* E=電池の起電力
* r=電池の内部抵抗
* R=負荷抵抗
* V=負荷抵抗の両端の電圧(測定値)
* V=E*R/R+r
* VR+Vr=ER
* Vr=ER-VR=R(E-V)
* r=R(E-V)/V
* Ead=EのAD変換値
* Vad=VにAD変換値
* FVR=2.048VとするとE=(Ead+1)/500 V=(Vad+1)/500
* r=R*((Ead+1)/500 - (Vad+1)/500)/(Vad+1)=R*(Ead+1-Vad-1)/(Vad+1)=R*(Ead-Vad)/(Vad+1)
*/
#define _XTAL_FREQ 32000000
#define LCD_addr 0x7C //3E+0
#define SW RA0
unsigned int val=0; //電圧測定値
unsigned int old_val=1; //電圧の前の値
#define R 100 //負荷抵抗(Ω)
#define LCD_addr 0x7C //3E+0
#define SW RA0
unsigned int val=0; //電圧測定値
unsigned int old_val=1; //電圧の前の値
#define R 100 //負荷抵抗(Ω)
/* I2C 初期化*/
void I2C_init(){
SSP1CON1 = 0x28; //SSPEN = 1,I2C Master Mode
SSP1STATbits.SMP = 1; //標準速度モード(100KHz)
SSP1ADD = 0x4F; //Fosc/(4*Clock)-1 Clock=100kHz,Fosc=32MHz
}
/* スタートコンディション */
void I2C_start(){
SEN = 1;
while(SEN);
}
void I2C_start(){
SEN = 1;
while(SEN);
}
/* ストップコンディション */
void I2C_stop(){
SSP1IF = 0;
PEN = 1;
while(PEN);
SSP1IF = 0;
}
void I2C_stop(){
SSP1IF = 0;
PEN = 1;
while(PEN);
SSP1IF = 0;
}
/* I2Cに1byte送信 */
void I2C_write(unsigned char dat){
SSP1IF = 0;
SSP1BUF = dat;
while(!SSP1IF);
}
void I2C_write(unsigned char dat){
SSP1IF = 0;
SSP1BUF = dat;
while(!SSP1IF);
}
/* write command */
void LCD_cmd(unsigned char cmd){
I2C_start(); //start condition
I2C_write(LCD_addr); //send slave address
I2C_write(0x00); //send control byte
I2C_write(cmd); //send command
I2C_stop(); //stop condition
}
void LCD_cmd(unsigned char cmd){
I2C_start(); //start condition
I2C_write(LCD_addr); //send slave address
I2C_write(0x00); //send control byte
I2C_write(cmd); //send command
I2C_stop(); //stop condition
}
/* write charactor */
void LCD_char(unsigned char dat){
I2C_start(); //start condition
I2C_write(LCD_addr); //send slave address
I2C_write(0x40); //send control byte
I2C_write(dat); //send data
I2C_stop(); //stop condition
}
void LCD_char(unsigned char dat){
I2C_start(); //start condition
I2C_write(LCD_addr); //send slave address
I2C_write(0x40); //send control byte
I2C_write(dat); //send data
I2C_stop(); //stop condition
}
/* LCD initialize */
void LCD_init(){
__delay_ms(40); //40ms wait
LCD_cmd(0x38); //8bit,2line
LCD_cmd(0x39); //IS=1 : extention mode set
LCD_cmd(0x14); //Internal OSC Frequency
LCD_cmd(0x70); //Contrast set
LCD_cmd(0x56); //Power/ICON/Contrast Control
LCD_cmd(0x6C); //Follower control
__delay_ms(200);//200ms wait
LCD_cmd(0x38); //IS=0 : extention mode cancel
LCD_cmd(0x0C); //Display ON
LCD_cmd(0x01); //Clear Display
__delay_ms(2); //wait more than 1.08ms
}
void LCD_init(){
__delay_ms(40); //40ms wait
LCD_cmd(0x38); //8bit,2line
LCD_cmd(0x39); //IS=1 : extention mode set
LCD_cmd(0x14); //Internal OSC Frequency
LCD_cmd(0x70); //Contrast set
LCD_cmd(0x56); //Power/ICON/Contrast Control
LCD_cmd(0x6C); //Follower control
__delay_ms(200);//200ms wait
LCD_cmd(0x38); //IS=0 : extention mode cancel
LCD_cmd(0x0C); //Display ON
LCD_cmd(0x01); //Clear Display
__delay_ms(2); //wait more than 1.08ms
}
/* Clear Display */
void LCD_clear(){
LCD_cmd(0x01);
__delay_ms(1);
__delay_us(80);
}
void LCD_clear(){
LCD_cmd(0x01);
__delay_ms(1);
__delay_us(80);
}
/* Return Home */
void LCD_home(){
LCD_cmd(0x02);
__delay_ms(1);
__delay_us(80);
}
void LCD_home(){
LCD_cmd(0x02);
__delay_ms(1);
__delay_us(80);
}
/* Cursor x,y */
void LCD_cursor(unsigned char x,unsigned char y){
if(y==0){
LCD_cmd(0x80+x);
}
if(y==1){
LCD_cmd(0xC0+x);
}
}
void LCD_cursor(unsigned char x,unsigned char y){
if(y==0){
LCD_cmd(0x80+x);
}
if(y==1){
LCD_cmd(0xC0+x);
}
}
/* write 1 charactor to LCD */
void putch(unsigned char ch){
LCD_char(ch);
}
void putch(unsigned char ch){
LCD_char(ch);
}
/* 電圧表示*/
void Volt_disp(unsigned int voltage){
voltage = (voltage + 1)*2;
unsigned int VH=voltage/1000;
unsigned int VL=((voltage%1000)+5)/10;
LCD_cursor(2,0);
printf("%d",VH);
printf(".");
printf("%02d",VL);
}
void Volt_disp(unsigned int voltage){
voltage = (voltage + 1)*2;
unsigned int VH=voltage/1000;
unsigned int VL=((voltage%1000)+5)/10;
LCD_cursor(2,0);
printf("%d",VH);
printf(".");
printf("%02d",VL);
}
/* 内部抵抗表示*/
void R_disp(unsigned int E,unsigned int V){
unsigned long R_int =(E - V)*R*10L;
R_int = R_int/(V+1);
unsigned int RiH = R_int/10;
unsigned int RiL = R_int%10;
void R_disp(unsigned int E,unsigned int V){
unsigned long R_int =(E - V)*R*10L;
R_int = R_int/(V+1);
unsigned int RiH = R_int/10;
unsigned int RiL = R_int%10;
LCD_cursor(2,1);
printf("%2d",RiH);
printf(".");
printf("%1d",RiL);
}
printf("%2d",RiH);
printf(".");
printf("%1d",RiL);
}
void main() {
OSCCON = 0b01110000 ; // 内部クロック8MHz
ANSELA = 0b00000001 ; // RA0をアナログPORTに設定
TRISA = 0b00001111 ; // RA0,RA1,RA2,RA3は入力、RA4RA5は出力
PORTA = 0b00000000 ; // PORTの初期化
LATA = 0b00000000;
OSCCON = 0b01110000 ; // 内部クロック8MHz
ANSELA = 0b00000001 ; // RA0をアナログPORTに設定
TRISA = 0b00001111 ; // RA0,RA1,RA2,RA3は入力、RA4RA5は出力
PORTA = 0b00000000 ; // PORTの初期化
LATA = 0b00000000;
I2C_init(); //I2C初期化
LCD_init(); //LCD初期化
/* A/Dコンバータ関係の設定 */
ADCON0 = 0b00000001; //AN0をselect,ADCイネーブル
ADCON1 = 0b10100011; //ADFM=1(右詰め),クロックFosc/32,VREF=FVR
FVRCON = 0b10000010; //FVR ON ,FVR×2 VREF=2.048V
/* タイトル表示 */
printf("Battery");
LCD_cursor(1,1);
printf("Checker");
__delay_ms(1000); //2秒待つ
__delay_ms(1000);
LCD_clear();
printf("E V");
LCD_cursor(0,1);
printf("R");
LCD_cursor(7,1);
putch(0x1E); //Ω
while(1){
__delay_us(20);
GO = 1; //ADCスタート
while(GO); //ADC完了まで待つ
val = ADRESH;
val = (val<<8) | ADRESL;
if(val < old_val-1){
R_disp(old_val,val);
}
Volt_disp(val);
__delay_ms(100);
old_val = val;
}
}
LCD_init(); //LCD初期化
/* A/Dコンバータ関係の設定 */
ADCON0 = 0b00000001; //AN0をselect,ADCイネーブル
ADCON1 = 0b10100011; //ADFM=1(右詰め),クロックFosc/32,VREF=FVR
FVRCON = 0b10000010; //FVR ON ,FVR×2 VREF=2.048V
/* タイトル表示 */
printf("Battery");
LCD_cursor(1,1);
printf("Checker");
__delay_ms(1000); //2秒待つ
__delay_ms(1000);
LCD_clear();
printf("E V");
LCD_cursor(0,1);
printf("R");
LCD_cursor(7,1);
putch(0x1E); //Ω
while(1){
__delay_us(20);
GO = 1; //ADCスタート
while(GO); //ADC完了まで待つ
val = ADRESH;
val = (val<<8) | ADRESL;
if(val < old_val-1){
R_disp(old_val,val);
}
Volt_disp(val);
__delay_ms(100);
old_val = val;
}
}