====== バッテリー充電ユニット(簡易多段定電流充電方式) ======
===== 概要 =====
バッテリ(充電電池含む)の充電には様々な方法があるようです。
<サイクル使用>
* -ΔV制御充電方式
* dT/dt制御充電方式
* ステップ制御充電方式
* 電圧制御充電方式
* Vテーパ制御充電方式
* 定電圧定電流制御充電方式
* タイマー制御充電方式
* 準定電流充電方式
<スタンバイ使用>
* トリクル充電方式
* 間欠充電方式
* パルス充電方式
今回は、対象をバッテリ(鉛蓄電池)に限定した、簡易な“多段定電流充電方式"で充電するユニットを製作してみました。
<多段定電流充電方式の主な特長>
* 従来の1段による定電流充電に比べ、2~6段の定電流で段階的に減少させて強制的に充電するため、充電時間を短縮することが出来ます。
* 多段充電が電極や電解液の保護に作用するため、サイクル寿命を延ばすことが出来ます。
{{:imgpaste:202004:htmikan-20200430-151710.png}}
※NTTファシリティーズ総合研究所で発表された論文よりグラフの引用
※鉛蓄電池について
鉛蓄電池を放電したままで放置しておくと、サルフェーションと呼ばれ、充電できない状態になります。
電池寿命の大半はこのサルフェーションによるものなので、放電後は速やかに充電する事が大切です。
===== 動作原理 =====
充電ステップを11段階(定電流=10段、定電圧=1段)としました。
定電流充電は、1000mA~100mAまでを、100mA単位で行います。
設定された電流値で定電流制御(電圧を上下)を行います。(例、ステップ0では1000mAの定電流制御)
* バッテリ電圧が、設定された電圧(定電圧)に達すると、次のステップに移ります。
定電圧充電は、13.0V~15.0Vまでを、100mV単位で設定可能としました。
* 設定された電圧値で定電圧制御(電圧を上下)を行います。(電流値は無視)
|<250px>|
^ 充電ステップ ^ 定電流 ^ 定電圧 ^
| 0 | 1000mA | - |
| 1 | 900mA | - |
| 2 | 800mA | - |
| 3 | 700mA | - |
| 4 | 600mA | - |
| 5 | 500mA | - |
| 6 | 400mA | - |
| 7 | 300mA | - |
| 8 | 200mA | - |
| 9 | 100mA | - |
| 10 | - | 14.4V |
===== 動作原理(ハードウェア) =====
◎電源部
24V1A以上を供給できる電源をご用意ください。
◎電圧供給部
効率を考慮し、DCDCコンバータ(チョッパ方式の降圧タイプ)を採用しました。
◎電圧測定
* DCDCコンバータの出力電圧(V2)。。。抵抗による分圧方式
* バッテリ電圧(V1)。。。抵抗による分圧方式
* 電流検出抵抗の両端電圧(V3)。。。微弱電圧のためオペアンプで48倍に増幅
===== 動作原理(ソフトウェア) =====
◎DCDCコンバータの出力電圧の制御
PMWのデューティ比を変化させて、出力電圧を制御します。尚、PWMの発振周波数は、約20kHzです。
◎電圧(V1,V2,V3)の測定
各電圧をA/D変換(1000回)し、その平均値を求めます。
◎電流の測定
電流検出抵抗(0.1Ω)の両端電圧(V3)より、オームの法則で電流を求めます。
◎各動作の制御
【動作モード0】
* 10段階の定電流で充電を行います。11段目では定電圧制御に切り替わります。(上記の表を参照)
* 開始条件=開始スイッチ押下
* 停止条件=停止スイッチ押下
【動作モード1】
* 10段階の定電流で充電を行います。
* 開始条件=バッテリーの電圧が、充電可能最低電圧を下回った時
* 停止条件=停止スイッチ押下、10段階の定電流で充電が全て完了した時
【動作モード2】
* 充電電圧を設定します。(SW_UP、SW_DOWN)
* 既定値は、14.4Vです。(設定範囲=13.0V~15.0V)
【動作モード3】
* 充電可能最低電圧を設定します。(SW_UP、SW_DOWN)
* 既定値は、10.5Vです。(設定範囲=9.0~12.0V)
【動作モード(その他)】
* 開始スイッチ(SW_START)を押下しながら、起動することにより、オペアンプのオフセット電圧をキャンセルすることが出来ます。その時には、電源装置をオフの状態、バッテリ未接続の状態にしてください。
|<350px>|
^ 動作モード ^ SW_MODE_1 ^ SW_MODE_0 ^
| モード0 | オン(0) | オン(0) |
| モード1 | オン(0) | オフ(1) |
| モード2 | オフ(1) | オン(0) |
| モード3 | オフ(1) | オフ(1) |
| その他 | 起動時に開始スイッチ\\ (SW_START)を押下 ||
===== 回路図 =====
{{:imgpaste:202004:htmikan-20200430-152350.png}}
===== ソースコード =====
//**********************************************************************
/*
<バッテリー充電ユニット(多段定電流充電方式)>
■動作モード0
・10段階の定電流で充電を行います。11段目では定電圧制御に切り替わります。
・開始条件=開始スイッチ押下
・停止条件=停止スイッチ押下
■動作モード1
・10段階の定電流で充電を行います。
・開始条件=バッテリーの電圧が、充電可能最低電圧を下回ったとき
・停止条件=停止スイッチ押下、10段階の定電流で充電が全て完了した時
■動作モード2
・充電電圧を設定します。
・既定値は、14.4Vです。(設定範囲=13.0V~15.0V)
■動作モード3
・充電可能最低電圧を設定します。
・既定値は、10.5Vです。(設定範囲=9.0~12.0V)
■その他
開始スイッチを押下しながら、起動することにより、オペアンプのオフセット
をキャンセルすることが出来ます。
その時には、電源装置をオフの状態、バッテリ未接続の状態にしてください。
*/
//**********************************************************************
//LCD
sbit LCD_RS at RA0_bit;
sbit LCD_EN at RA1_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_RS_Direction at TRISA0_bit;
sbit LCD_EN_Direction at TRISA1_bit;
sbit LCD_D7_Direction at TRISB7_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB4_bit;
//
sbit SW_MODE_0 at RA5_bit;
sbit SW_MODE_1 at RB1_bit;
sbit SW_UP at RB2_bit;
sbit SW_DOWN at RB3_bit;
sbit SW_START at RB2_bit;
sbit SW_STOP at RB3_bit;
//
#define MODE_0 0
#define MODE_1 1
#define MODE_2 2
#define MODE_3 3
//
#define BYTE unsigned short
#define WORD unsigned int
#define DWORD unsigned long
//**********************************************************************
//■関数宣言
extern void main();
extern void PWM1_Change_DutyEx(unsigned int duty_ratio);
extern WORD ADC_Get_Sample_Average(unsigned short channel);
extern void control();
extern void run_mode_0();
extern void run_mode_1();
extern void run_mode_2();
extern void run_mode_3();
extern short get_mode();
extern void error_disp(char num);
extern void voltage_init();
extern short voltage_set(char c);
extern void voltage_on();
extern void voltage_off();
extern void voltage_get();
//**********************************************************************
char buf[10];
union {
double _double;
short _short[4];
} cal;
//**********************************************************************
//■PWMデューティ設定関数
void PWM1_Change_DutyEx(unsigned int duty_ratio)
{
CCPR1L = duty_ratio >> 2;
CCP1CON.CCP1Y = (duty_ratio & 0b00000001) != 0 ? 1 : 0;
CCP1CON.CCP1X = (duty_ratio & 0b00000010) != 0 ? 1 : 0;
}
//**********************************************************************
//■A/D変換(10000回平均)関数
WORD ADC_Get_Sample_Average(unsigned short channel)
{
DWORD ad;
WORD cnt;
//
ad = 0;
for (cnt = 0; cnt < 1000; cnt++) {
ad += ADC_Get_Sample(channel);
}
return (ad / 1000);
}
//**********************************************************************
//■エラー表示関数
char *error_msg ="error! (?) ";
//
void error_disp(char num)
{
short cnt;
//
for (cnt = 0; cnt < 10; cnt++) {
error_msg[8] = num + '0';
Lcd_Out(2, 1, error_msg);
Delay_ms(100);
error_msg[8] = ' ';
Lcd_Out(2, 1, error_msg);
Delay_ms(100);
}
}
//**********************************************************************
//■電圧制御(初期化)関数
int duty = 1023;
//
void voltage_init()
{
duty = 1023;
PWM1_Init(20000); //20kHz
PR2 = 0xFF;
PWM1_Stop();
TRISB.B0 = 0;
PORTB.B0 = 1;
}
//**********************************************************************
//■電圧制御(設定)関数
short voltage_set(char c)
{
switch (c) {
case '0':
duty = 1023;
PWM1_Change_DutyEx(duty);
break;
case '-':
if (duty < 1023) {
duty++;
PWM1_Change_DutyEx(duty);
} else {
error_disp(1);
return(-1);
}
break;
case '+':
if (duty > 10) {
duty--;
PWM1_Change_DutyEx(duty);
} else {
error_disp(2);
return(-1);
}
break;
}
return(0);
}
//**********************************************************************
//■電圧制御(オン)関数
void voltage_on()
{
PWM1_Change_DutyEx(duty);
PWM1_Start();
}
//**********************************************************************
//■電圧制御(オフ)関数
void voltage_off()
{
PWM1_Stop();
TRISB.B0 = 0;
PORTB.B0 = 1;
}
//**********************************************************************
//■電圧測定関数
double v1, v2, v3, offset = 0.0;
//
void voltage_get()
{
//電圧を測定します。
v1 = ADC_Get_Sample_Average(3);
v2 = ADC_Get_Sample_Average(2);
v3 = ADC_Get_Sample_Average(4);
//電圧値の補正および電流値を求めます。
v1 = v1 * 4.8828125 * 6; //分圧(100kΩ、20kΩ)
v2 = v2 * 4.8828125 * 6; //分圧(100kΩ、20kΩ)
v3 = (v3 * 4.8828125) / 48; //増幅(47kΩ、1kΩ)
v3 = v3 - offset;
v1 = v1 - v3;
}
//**********************************************************************
//■多段定電流充電制御関数
short step = 0;
int minpoint_v = 10500;
int setpoint_v = 14400;
int setpoint_i[] = {1000, 900, 800, 700, 600, 500, 400, 300, 200, 100};
//
short control()
{
double i;
//電圧を測定します。
voltage_get();
//電流を求めます。(オームの法則)
i = v3 / 0.1;
//LCDに表示します。
WordToStr(v1, buf);
Lcd_Out(1, 1, buf);
WordToStr(i, buf);
Lcd_Out(1, 10, buf);
WordToStr(v2, buf);
Lcd_Out(2, 1, buf);
WordToStr(duty, buf);
Lcd_Out(2, 12, buf);
//
if (step == 11) {
Lcd_Chr(1, 9, '-');
return(0);
}
//
if (step == 10) {
//定電圧制御を行います。
Lcd_Chr(1, 9, '*');
if (v1 > setpoint_v) {
if (voltage_set('-') == -1) {
return(-1);
}
} else {
if (voltage_set('+') == -1) {
return(-1);
}
}
return(0);
}
//多段定電流制御を行います。
Lcd_Chr(1, 9, step + '0');
if (i > setpoint_i[step]) {
if (voltage_set('-') == -1) {
return(-1);
}
} else {
if (voltage_set('+') == -1) {
return(-1);
}
}
//設定電圧を超過すると電圧を下げ、電流の設定値を下げます。
if (v1 > setpoint_v) {
step++;
voltage_set('0');
Delay_ms(1000);
}
return(0);
}
//**********************************************************************
//■モード0関数
void run_mode_0()
{
if (SW_START == 1) {
step = 11;
control();
return;
}
//
step = 0;
voltage_on();
voltage_set('0');
//
while (SW_STOP == 1) {
if (control() == -1) {
break;
}
}
voltage_off();
}
//**********************************************************************
//■モード1関数
void run_mode_1()
{
voltage_get();
if (v1 > minpoint_v) {
step = 11;
control();
return;
}
//
step = 0;
voltage_on();
voltage_set('0');
//
while (SW_STOP == 1) {
if (control() == -1) {
break;
}
if (step == 10) {
break;
}
}
voltage_off();
}
//**********************************************************************
//■モード2関数
void run_mode_2()
{
WordToStr(setpoint_v, buf);
Lcd_Out(1, 1, buf);
//
if (SW_UP == 0) {
while (SW_UP == 0) {
Delay_ms(100);
}
//
if (setpoint_v < 15000) {
setpoint_v += 100;
}
//
EEPROM_Write(0, (setpoint_v >> 8) & 0xFF);
EEPROM_Write(1, setpoint_v & 0xFF);
//
return;
}
if (SW_DOWN == 0) {
while (SW_DOWN == 0) {
Delay_ms(100);
}
//
if (setpoint_v > 13000) {
setpoint_v -= 100;
}
//
EEPROM_Write(0, (setpoint_v >> 8) & 0xFF);
EEPROM_Write(1, setpoint_v & 0xFF);
//
return;
}
}
//**********************************************************************
//■モード3関数
void run_mode_3()
{
WordToStr(minpoint_v, buf);
Lcd_Out(1, 1, buf);
//
if (SW_UP == 0) {
while (SW_UP == 0) {
Delay_ms(100);
}
//
if (minpoint_v < 12000) {
minpoint_v += 100;
}
//
EEPROM_Write(2, (minpoint_v >> 8) & 0xFF);
EEPROM_Write(3, minpoint_v & 0xFF);
//
return;
}
if (SW_DOWN == 0) {
while (SW_DOWN == 0) {
Delay_ms(100);
}
//
if (minpoint_v > 9000) {
minpoint_v -= 100;
}
//
EEPROM_Write(2, (minpoint_v >> 8) & 0xFF);
EEPROM_Write(3, minpoint_v & 0xFF);
//
return;
}
}
//**********************************************************************
//■モード取得関数
short flag = -1;
//
short get_mode()
{
if ((SW_MODE_1 == 0) && (SW_MODE_0 == 0)) {
if (flag != 0) {
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1, 6, "mV");
Lcd_Out(2, 6, "mV");
Lcd_Out(1, 15, "mA");
}
flag = 0;
return (MODE_0);
}
if ((SW_MODE_1 == 0) && (SW_MODE_0 == 1)) {
if (flag != 1) {
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1, 6, "mV");
Lcd_Out(2, 6, "mV");
Lcd_Out(1, 15, "mA");
}
flag = 1;
return (MODE_1);
}
if ((SW_MODE_1 == 1) && (SW_MODE_0 == 0)) {
if (flag != 2) {
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1, 6, "mV");
}
flag = 2;
return (MODE_2);
}
if ((SW_MODE_1 == 1) && (SW_MODE_0 == 1)) {
if (flag != 3) {
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1, 6, "mV");
}
flag = 3;
return (MODE_3);
}
}
//**********************************************************************
//■オペアンプオフセット電圧取得関数
void opeamp_offset_get()
{
while (SW_START == 0) {
v3 = ADC_Get_Sample_Average(4);
v3 = (v3 * 4.8828125) / 48; //増幅(47kΩ、1kΩ)
offset = v3;
WordToStr(v3, buf);
Lcd_Out(1, 1, "offset=");
Lcd_Out(1, 8, buf);
Lcd_Out(1, 13, "mV");
}
cal._double = offset;
Eeprom_Write(4, cal._short[0]);
Eeprom_Write(5, cal._short[1]);
Eeprom_Write(6, cal._short[2]);
Eeprom_Write(7, cal._short[3]);
Delay_ms(1000);
}
//**********************************************************************
//■メイン関数
void main()
{
ANSEL = 0b00001100;
TRISA = 0b00111100;
TRISB = 0b00001110;
//
ADC_Init();
//
Lcd_Init();
Lcd_Cmd(_LCD_CURSOR_OFF);
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1, 1, "Battery Charger");
Delay_ms(1000);
Lcd_Cmd(_LCD_CLEAR);
//
voltage_init();
//設定値の読み込み
setpoint_v = EEPROM_Read(0);
setpoint_v <<= 8;
setpoint_v = setpoint_v | EEPROM_Read(1);
if ((setpoint_v < 13000) || (setpoint_v > 15000)) {
setpoint_v = 14400;
}
minpoint_v = EEPROM_Read(2);
minpoint_v <<= 8;
minpoint_v = minpoint_v | EEPROM_Read(3);
if ((minpoint_v < 9000) || (minpoint_v > 12000)) {
minpoint_v = 10500;
}
cal._short[0] = Eeprom_Read(4);
cal._short[1] = Eeprom_Read(5);
cal._short[2] = Eeprom_Read(6);
cal._short[3] = Eeprom_Read(7);
offset = ((cal._double < 0.0) || (cal._double > 100.0)) ? 0.0 : cal._double;
//オペアンプオフセット電圧取得
if (SW_START == 0) {
opeamp_offset_get();
}
//
while (1) {
switch (get_mode()) {
case MODE_0:
run_mode_0();
break;
case MODE_1:
run_mode_1();
break;
case MODE_2:
run_mode_2();
break;
case MODE_3:
run_mode_3();
break;
}
}
}
//**********************************************************************
===== 動作確認 =====
{{:imgpaste:202004:htmikan-20200430-152519.png}}{{:imgpaste:202004:htmikan-20200430-152526.png}}
{{:imgpaste:202004:htmikan-20200430-152531.png}}{{:imgpaste:202004:htmikan-20200430-152535.png}}
多段階定電流充電を行っているところです。(ステップ0、1.....9、定電圧充電)
左上:バッテリの電圧
左下:DCDCコンバータの出力電圧
中央上:動作ステップ番号
右上:充電電流値
右下:PWMの値
{{:imgpaste:202004:htmikan-20200430-152546.png}}{{:imgpaste:202004:htmikan-20200430-152549.png}}
{{:imgpaste:202004:htmikan-20200430-152553.png}}{{:imgpaste:202004:htmikan-20200430-152557.png}}
左側:充電電圧を設定します。
右側:充電開始電圧を設定します。
{{:imgpaste:202004:htmikan-20200430-152602.png}}{{:imgpaste:202004:htmikan-20200430-152606.png}}
このページは稲崎様の閉鎖したHPのコピーで、著作権は稲崎様にあります。[[elechobby:picdic:picdic|詳細]]
This page is a copy of Mr. Inasaki's closed website, and the copyright is held by him.[[elechobby:picdic:picdic|Details]]