PWMによる電流可変回路の実験

 「PWMによる定電流回路の制御実験」に続いてバッテリー放電器の基礎実験をやっています。前回は定電流回路をPWMでオン・オフさせて電流可変しましたが、今回は直接、PWMで電流可変を試してみます。

 今回も負荷としてMOS FET 2SK2232をAVRでPWM制御するところは同じですが、負荷に接続した電流検出抵抗0.1Ωに発生した電圧をAVRのAD変換で取り込んで電流値に換算し、あらかじめ設定した放流電流値(0~1000mA)になるようにPWMのDuty比をフィードバック制御するようにしました。また、電池の電圧が放電休止電圧(0.9V)に達すると放電を終了するようにしてあります。

 実験に使用した回路です。とりあえず実験ということで電池1本分の回路です。

  • 回路図一部修正:電池電圧測定用AD変換へローパスフィルタ追加(2008-03-03)
  • LCDのRSとR/Wのピン配置が間違っています。(2008-03-06)

 放電電流300mAに設定したときの動作状態です。実際に単3のニッケル水素電池1本を放電させて見ました。アップ・ダウンスイッチで放電電流を100mA単位で可変することができます。

 この放電回路では、電池の電圧や内部抵抗にかかわらず、常に一定の電流で放電するように制御されています。ためしに、電池にかえて実験用可変電源を接続し、電圧を1.5V~12Vまで変化させて見ましたが、常に電流値は設定した一定の値となります。
 この状態なら、放電電流と放電時間から正確に放電容量を求めることが出来ると思います。また、正確な負荷を設定できる「電子負荷」としても応用が利きそうです。

 実機は電池4本を同時に処理する予定ですので、PWM出力4ポート、電流検出4ポート、電圧検出4ポートとLCDやスイッチのポートが必要となり、28ピンのATmega168ではIOポートが不足します。特にAD変換が8ポート必要となるので、どうやりくりしても足りません。

・・ということで、最近、秋月電子で取り扱いを始めた40ピンDIPのAVR ATmega644Pを購入しました。

余談ですが・・・ATMEL AVRは、同じ機能でフラッシュメモリサイズ違いを数種類ラインナップしたものが多いようです。当然、フラッシュの容量が大きいほうが値段も高くなります。製品として何十台、何百台と作れば、そのコストは大きな差になりますが、われわれシロートが趣味で数台程度を製作する場合は、たいしたコストにはならないので(僅か数百円程度)、フラッシュ容量が一番大きなものを購入したほうが良いと思います。

 参考までに実験で使用したソースです。AVRStudio4とWinAVR(GCC)で作ってあります。LCD関連のソースは省略してあります。実験では、放電電流値を放電中も可変(100mA単位でアップダウン)できるようにしてあります。


#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <math.h>

#include "lcd.h"

#define cbi(addr, bit) addr &= ~(1<<bit)
#define sbi(addr, bit) addr |= (1<<bit)
#define ADC_ENABLE (_BV(ADEN)|_BV(ADATE)|_BV(ADIF)|0b110)
#define ADC_START (ADC_ENABLE|_BV(ADSC))

void delay_ms(unsigned int t) { while(t--) _delay_ms(1); }

FILE *fp; // for fdevopen()

unsigned int get_adc(char ch)
{
ADMUX = ch;
 ADCSRA = ADC_START;
loop_until_bit_is_set(ADCSRA, ADIF);

return ADCW;
}

int main()
{
unsigned int duty;
unsigned int load;
unsigned int volt;
unsigned int cur;
unsigned char sw0_state, sw1_state;

DDRC = 0b00000000; //
DDRB = 0b11111111; // LCD
DDRD = _BV(PD6); // PD6 PWM output
DIDR0 = _BV(ADC0D); // ADC0 Digital Input Disable
PORTD = 0b00000011; // PD0,1 pull up

lcd_init();
lcd_cls();
fp = fdevopen(lcd_putch, NULL); // LCD出力ファイルディスクプリタ

cur = 0;
duty = 0;

ADCSRA = ADC_ENABLE;

OCR0A = 0;
TCCR0A = _BV(WGM01)|_BV(WGM00)|_BV(COM0A1)|_BV(COM0B1);
TCCR0B = 1; // Timer0 CLK/0

while(1) {
if(bit_is_clear(PIND, PD0)) {
sw0_state = 1;
delay_ms(10);
}
if(sw0_state && bit_is_set(PIND, PD0)) {
sw0_state = 0;
if(cur < 1000) { // Current Up 100mA
cur += 100;
}
}
if(bit_is_clear(PIND, PD1)) {
sw1_state = 1;
delay_ms(10);
}
if(sw1_state && bit_is_set(PIND, PD1)) {
sw1_state = 0;
if(cur > 0) { // Current Down 100mA
cur -= 100;
}
}

volt = get_adc(1);
if(volt < 184) { // 0.9 / (5/1024) = 184....
cur = 0; // Stop Discharge
}

load = (int)(get_adc(0) * 4.88); // 5/1024 * level = Load
if(load < cur) {
duty++;
}
if(load > cur) {
duty--;
}
if(duty > 120) { // limiter
duty = 120;
}
if(duty < 0) {
duty = 0;
}

OCR0A = duty;

lcd_goto(0, 0);
fprintf(fp, "Set:%03dmA %03dmA", cur, load);
lcd_goto(1, 0);
fprintf(fp, "PWM:%03d %.1fV", duty, volt * 0.00488);
}
}