前記事で触れたN-VANで使っている自作サブバッテリーモニターです。SBC-004とSMF31MS-850の間に入れています。機能は以下のとおり。
・サブバッテリー電圧、充電電流表示
・サブバッテリー電源供給回路(ACC OFF後24時間で自動OFF)
・インバーターON/OFF用リレーコントロール(30分で自動OFF)
・起動するとサブバッテリーの電圧と充電電流を表示します。
・サブバッテリー電源供給回路はサブバッテリーからPower MOSFET経由で出力しています。ACC ONで自動ON、サブバッテリーの過放電を防ぐためACC OFF後24時間で自動OFFします。残り時間は液晶右下に表示。ENGELのポータブル冷蔵庫、ルームランプ、車載USB電源などに使っています。電源供給中は液晶のバックライトがONになり、LEDが点滅します。
・中央のプッシュスイッチで12V→100Vインバーター制御用のリレーをON/OFFします。サブバッテリーの過放電を防ぐためON後30分で自動OFFします。残り分数は液晶右上に表示。インバーターにコントロール端子が無い場合はリレー出力をインバーターの電源スイッチと並列にはんだ付けします。リレーON時は液晶のバックライトがONになり、OFFタイマー付電源供給回路もその後24時間ONになり、液晶下のLEDが点灯します。
回路図は以下のとおり。使っている液晶パネルは秋月のAQM0802A-FLW-GBWです。
サブバッテリーモニター回路図
制御プログラムは以下のとおり(少し大きい)。消費電力を抑えるためにClockは1MHzで動作。初期化後は125msおきのタイマー割り込みで電源OFFタイマーを減算。フォアグラウンド側で電圧・電流測定、タイマー値を見て電源・リレーON/OFF、ACC電圧とプッシュスイッチON/OFFの確認などを行っています。
#include
#pragma config FCMEN=OFF
#pragma config IESO=OFF
#pragma config CPD=OFF
#pragma config CP=OFF
#pragma config BOREN=ON
#pragma config MCLRE=OFF
#pragma config PWRTE=OFF
#pragma config LVP=OFF //
#pragma config WDTE=OFF
#pragma config FOSC=INTOSCIO
#define _XTAL_FREQ 1000000
#define TMR1H_VAL 0x85 // 65536-31250=34286=0x85ee
#define TMR1L_VAL 0xee // 31250 * 4us = 125ms period
#define RELAY_TIME 30 // relay on time 30min.
#define EXT_POW_TIME 24 // ext. power on time 24H.
#define RELAY RA7
#define ACC RA6
#define PUSH_SW RA5
#define EXT_POW RA1
#define LED_R RB5
#define SCL RB4
#define LED_BL RB2
#define SDA RB1
#define PANEL_POW RB0
#define TRIS_SDA TRISB1
#define LCD_ADDR 0x7c
#define ON 1
#define OFF 0
unsigned char t_rm=0, t_rs=0; // relay-on counter t_rm:minute, t_rs:second
unsigned char t_ph=0; // external power-on counter
unsigned int t_ps=0;
unsigned char timer_flag=0, disp_refresh=0;
void pic_init(void)
{
OSCCON = 0x40; // INTOSC 1MHz
ANSEL = 0x09; // RA3:VSB, RA0:CSNS
PORTA = 0x00;
PORTB = 0x12; // SCL=1, SDA=1
TRISA = 0x79; // RA7:RELAY, RA6:ACC, RA5:PUSH-SW, RA4:
// RA3:VSB, RA2:, RA1:Ext_Pow, RA0:CSNS
TRISB = 0x00; // RB5:LED_R, RB4:SCL, RB1:SDA, RB0:PANAL_POW
OPTION_REG = 0xff; // PORT B weak pull up disable
// adc setup
ADCON0 = 0b00000001; // clock=fosc/2, a/d enable
ADCON1 = 0b10000000; // right justified, Vdd/Vss ref
// timer1 setup
T1CON = 0b00000100; // T1CKPS:1/1 T1OSCEN:F *T1SYNC:F TMR1CS:INT TMR1ON:F
// timer1 clock freq. is 1MHz/4/1=250KHz
PEIE = ON; // all peripheral interrupts enable
GIE = ON; // global interrupt enable
TMR1H = TMR1H_VAL; // set timer1 interrupt period
TMR1L = TMR1L_VAL;
TMR1IE = ON; // timer1 interrupt enable
TMR1ON = ON; // timer1 start
return;
}
// read adc 'sample' times, return value: 0x0000-0xffff
unsigned int adc_read( unsigned char ch, unsigned char sample )
{
unsigned int value; // adc value
unsigned char i;
ADCON0 = (unsigned char)( (ch<<3) | 0b00000001); // right justified, VDD ref, ADON
// measure 'sample' times for noise reduction
for( i = 0, value = 0; i < sample; i++ ){
GO_nDONE = 1; // ADC start
while( GO_nDONE )
continue;
value += ADRESH*256+ADRESL;
}
return value;
}
void i2c_start(){
SDA = 1;
SCL = 1;
SDA = 0;
SCL = 0;
return;
}
unsigned char i2c_write(unsigned char d){
unsigned char i, ack;
for (i=0; i<8; i++){
SDA = (d & 0x80)? 1: 0;
SCL = 1; // send 1bit
SCL = 0;
d <<= 1; // next bit
}
TRIS_SDA = 1; // SDA input mode
SCL = 1;
ack = SDA;
SCL = 0;
TRIS_SDA = 0; // SDA output mode
return(ack);
}
void i2c_stop(){
SDA = 0;
SCL = 1;
SDA = 1;
return;
}
void lcd_cmd(unsigned char cmd){
i2c_start();
i2c_write(LCD_ADDR);
i2c_write(0x00);
i2c_write(cmd);
i2c_stop();
__delay_us(30);
return;
}
void lcd_putc(unsigned char data){
i2c_start();
i2c_write(LCD_ADDR);
i2c_write(0x40);
i2c_write(data);
i2c_stop();
__delay_us(30);
return;
}
void lcd_puts(unsigned char pos, unsigned char* s){
lcd_cmd( pos );
while(*s){
lcd_putc(*s++);
}
return;
}
void lcd_init(){
TMR1IE = OFF; // timer1 interrupt disable
TMR1ON = OFF; // timer1 stop
PANEL_POW = 1; // LCD power on
__delay_ms(40);
lcd_cmd(0x38);
lcd_cmd(0x39);
lcd_cmd(0x14);
lcd_cmd(0x70);
lcd_cmd(0x52);
lcd_cmd(0x6c);
__delay_ms(200);
lcd_cmd(0x38);
lcd_cmd(0x0c);
lcd_puts(0x80, (unsigned char*)"SUB BATT");
lcd_puts(0xc0, (unsigned char*)" MONITOR");
__delay_ms(1000);
lcd_cmd(0x01);
__delay_ms(2);
lcd_puts(0x84, (unsigned char*)"V");
lcd_puts(0xc4, (unsigned char*)"A");
TMR1ON = ON; // timer1 start
TMR1IE = ON; // timer1 interrupt enable
return;
}
void bin2str4(unsigned int val, unsigned char *b){
union{
unsigned long int bd32;
struct{
unsigned bd8l : 8;
unsigned bd8h : 8;
unsigned bcd0 : 4;
unsigned bcd1 : 4;
unsigned bcd2 : 4;
unsigned bcd3 : 4;
}bcd;
}work;
char i;
work.bd32 = (unsigned long int)val;
for(i = 0; i < 16; i++){
if(work.bcd.bcd0 >= 5)
work.bcd.bcd0 += 3;
if(work.bcd.bcd1 >= 5)
work.bcd.bcd1 += 3;
if(work.bcd.bcd2 >= 5)
work.bcd.bcd2 += 3;
if(work.bcd.bcd3 >= 5)
work.bcd.bcd3 += 3;
work.bd32 <<= 1;
}
b[0] = (work.bcd.bcd2)? work.bcd.bcd2 + '0': ' ';
b[1] = work.bcd.bcd1 + '0';
b[2] = '.';
b[3] = work.bcd.bcd0 + '0';
b[4] = 0x00;
return;
}
void bin2str2(unsigned char val, unsigned char *b){
union{
unsigned short int bd16;
struct{
unsigned bd8 : 8;
unsigned bcd0 : 4;
unsigned bcd1 : 4;
}bcd;
}work;
char i;
work.bd16 = (unsigned short int)val;
for(i = 0; i < 8; i++){
if(work.bcd.bcd0 >= 5)
work.bcd.bcd0 += 3;
if(work.bcd.bcd1 >= 5)
work.bcd.bcd1 += 3;
work.bd16 <<= 1;
}
b[0] = (work.bcd.bcd1)? work.bcd.bcd1 + '0': ' ';
b[1] = work.bcd.bcd0 + '0';
b[2] = 0x00;
return;
}
void __interrupt() timer1_overflow(void)
{
static unsigned char freerun=0;
TMR1ON = OFF; // stop timer1
TMR1IF = OFF; // clear timer1 interrupt flag
TMR1H = TMR1H_VAL; // reset timer1
TMR1L = TMR1L_VAL;
TMR1ON = ON; // restart timer1
timer_flag = 1;
freerun++;
if(!(freerun & 0x07)){ // once per 8 interrupts
disp_refresh = 1;
if(t_rm){
t_rs++; // count up relay timer
if(t_rs >= 60){
t_rs = 0;
t_rm--;
}
}
if(t_ph){
t_ps++; // count up ext.pow timer
if(t_ps >= 3600){
t_ps = 0;
t_ph--;
}
}
}
return;
}
void main(void) {
unsigned char state;
unsigned char s_flag=OFF, s_ctr=16;
unsigned int batt, csns; // sub battery voltage and charge current
unsigned char disp[5]; // LCD display buffer
pic_init();
lcd_init();
while(1){
while(!timer_flag)
;
timer_flag = 0;
if(disp_refresh){
disp_refresh = 0;
batt = (adc_read(3, 72)+0x80)>>8; //voltage:175mV/V
bin2str4(batt, disp);
lcd_puts(0x80, disp);
csns = adc_read(0, 63)>>6; //csns:50mV/A
bin2str4(csns, disp);
lcd_puts(0xc0, disp);
bin2str2(t_rm, disp); // relay on timer
lcd_puts(0x86, disp);
bin2str2(t_ph, disp); // ext.power on timer
lcd_puts(0xc6, disp);
if(t_rm){
RELAY = ON; // relay power on
LED_R = ON;
}else{
RELAY = OFF; // relay power off
LED_R = OFF;
}
if(t_ph){
EXT_POW = ON; // ext.power on
LED_BL = ON; // backlight on
if(!t_rm){
LED_R = ON; // LED on
__delay_ms(10);
LED_R = OFF; // LED off
}
}else{
EXT_POW = OFF; // ext.power off
LED_BL = OFF; // backlight off
}
}
if(!PUSH_SW){ // switch pushed
if(s_flag&&s_ctr){ // pushed continuously
s_ctr--;
}
s_flag=ON;
}else{
if(s_flag){ // switch pushed->released
if(s_ctr){
// pushed less than 2 sec.
if(!t_rm){ // relay is off?
t_rm = RELAY_TIME; // turn on relay 30min.
t_ph = EXT_POW_TIME; // turn on ext.pow 24h
}else{
t_rm = 0; // turn off relay
}
t_rs = t_ps = 0;
}else{
// pushed more than 2 sec.
t_rm = t_rs = t_ph = t_ps = 0; // clear relay & ext_pow. timer
lcd_init(); // initialize LCD
__delay_ms(100); // prevent sw. chattering
}
}
s_flag=OFF;
s_ctr=16; // 16*125ms=2sec
}
if(ACC){
t_ph = EXT_POW_TIME; // hold ext.power 24h after ACC-OFF
t_ps = 0;
}
}
return;
}