実験用安定化電源の改造

[公開:any]

[電子工作/一般]
[電子工作/PIC]
[PIC,電源,電流計,電子負荷,INA226]

origin 2015-09-07



 2007年に「実験用安定化電源の製作」で作ったものは、可変出力(1.5V〜16V)と固定出力(5V)の2系統の電圧に対応した電源で、AVRのAD変換による電圧と電流表示機能を搭載しています。この電源はトランスと3端子レギュレーターを使用したシリーズ電源です。安全回路が3端子レギュレーター自体の短絡保護くらいしかなく、トランスに最大3Aと比較的高容量のものを使用したため、ブレッドボードなどで実験するときに回路の間違いから過電流が流れていくつかの半導体や部品をダメにしました。


 市販されている実験用電源は、実験回路保護のための制御回路が組み込まれています。3端子レギュレータを使った定電流制御(CC:Constant Current)や電圧垂下制御も可能ですが、ここでは、過電流時に自動復帰なしに出力閉鎖することにすれば単純な付加回路で実現可能と考えて、今の実験用安定化電源を改造することにしました。

 これまでの実験用安定化電源では、ローサイドに電流検出用の抵抗を接続して電流測定していました。今回は、ストロベリー・リナックスで取り扱いのあるI2Cディジタル電流・電圧・電力計モジュールを使用してハイサイドで測定します。このモジュールはINA226というデジタル電流出力シャントモニタICを使用したものでI2Cにより、電圧や電流、電力などをリアルタイムに測定可能です。
 可変出力と固定出力の2系統にわけて動作させるようにブレッドボードで実験しました。I2CやLCDにためのマイコンはPIC18F14K22を使用しました。LCDの表示は、上段が可変出力、下段が固定出力で左から電圧(10mV単位)、電流制限値(1mA単位)、電流値(1mA単位)となっています。INA226モジュールの仕様では、電圧が1.25mV単位、電流が0.1mA単位で測定可能ですが、実験用電源には、これくらいで十分です。

INA226モジュール

 これまでの電源では、出力のON/OFFをトグルスイッチで行っていましたが、マイコン制御により、過電流時に出力シャットダウンさせるためにパワーリレーによる出力制御としました。しかし、ある程度は予想が付いていましたが、2系統をONにするとリレーだけで200mA以上の電流が必要となり、制御回路の電流とあわせて250mA近くが流れます。5V固定出力用の3端子レギュレーターは、トランスの14Vタップから全波整流後の18〜19Vを入力としているため、発熱が厳しい状態です。250mAを取り出すとそれだけで3W以上の熱が発生することになります。小さな放熱板では、この3W程度が限界です。この状態から本来の目的の実験回路への電源を供給すれば発熱が処理できなくなる可能性が大きいです。


 リレーによる制御はあきらめて、Pch MOSFETによるハイサイドスイッチを使用することにしました。MOSFETは、2SJ380を使用します。最近のMOSFETには、ON抵抗が数10mΩというものもあります。2SJ380のON抵抗は、0.15Ωとやや大きいのですが、3A以下の電流制御なら1W程度の発熱なので問題はないでしょう。単純にリレーをMOSFETに置き換えて、リレードライブ用のトランジスタをそのままMOSFETのプリドライバとして使用しています。2系統の出力をONにした状態の消費電流は、54mAとなり、ほぼ、制御回路のみの消費電流となりました。リレー動作の約200mAをまるまる減らすことができました。あと、平滑用コンデンサを基板外のブロックコンデンサを使用することにして、基板をすっきりとさせました。画像では、固定出力側にDIPタイプのダイオードブリッジがありますが、これは当初別トランスと使用する予定で取り付けました。結果として不要になりましたが、3端子レギュレータへの入力電圧を少しでも下げるためそのまま残しておくことにします。


 最終的な回路図です。可変出力のFETハイサイドスイッチは、ゲート電圧をソースから供給すると、出力電圧が低い場合にON抵抗が大きくなります。また、ゲート電圧はソース電圧以上が必要となるため、3端子レギュレーターの入力から取り出しています。

experimental power supply

 これまでの電源の基板をあらたに製作した制御基板と交換しました。出力スイッチはトグルスイッチからLED内蔵のプッシュスイッチに変更しました。このLEDは、出力ONで点灯します。また、以前は可変出力用の3端子レギュレーターをケースのリアパネルで放熱し、固定出力の3端子レギュレーターは、TO-220用の小型ヒートシンクを使用していましたが、今回はリアパネルに大きなヒートシンクを取り付けて放熱容量を大幅に強化しました。


 出力電圧の精度を確認してみました。2系統ともテスターでの誤差は0.01Vでした。手持ちのデスクトップマルチメーターで測定すると10mVの桁までは、まったく同じ値となるためINA226での測定はかなり正確です。


 電子負荷を使用して負荷試験を行いました。可変出力を13.8Vに設定して2Aの出力を取り出しています。3端子レギュレーターの入力電圧が実測で18.5Vなので、熱損失は、10W程度となります。10分間動作させると放熱板は熱くはなりますが、触れないほどの熱さにはなりません。当然、サーマルシャットダウンも発生しません。

 また、パネルに取り付けたプッシュスイッチ付きロータリーエンコーダーにて、固定出力、可変出力を切り替えながらそれぞれの電流のリミット値の設定変更が可能です。負荷をかけて電流値がリミットを超過すると出力がシャットダウンすることを確認しました。このエンコーダーのプッシュスイッチを2秒以上押し続けるとそのときの電流リミット値がPICのEEPROMに書き込まれて、次に電源を入れたときからは、その書き込まれた値が自動的に設定されるようにソフトウェアを作成してあります。


 固定出力でも負荷試験を行いました。500mA出力で10分間動作させました。熱損失は7W程度となります。こちらも特段の問題はなく、放熱板も十分触れる熱さです。


 可変出力で電圧を低くして熱損失を大きくした試験です。13W程度の熱が発生していますが、これも10分間では問題ありません。放熱板の熱さからしても連続動作は問題なさそうです。


 制御プログラムソースです。LCDライブラリは省略しています。開発はMPLAB IDE1.85とXC1.21を使用しました。今回、MPLAB IDEをアップデートしようとMicrochipのWebサイトを見てみると最新のものは、Ver3.1となっています。いつのまにかメジャー番号が2つあがっています。使用方法がこれまでと大きく違うと面倒(違うかどうか確認していません)なのでアップデートはやめました。コンパイラは最新がよかろうとVer1.35をインストールしましたが、・・・なんと純正?(・・plib以下に収容されているので純正ではないのか)のI2Cライブラリ等が削られています。ライブラリをスクラッチで作るのは面倒なので、結局、これまでのVer1.21を使用してコンパイルしました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*
  Experimental Power Supply Control
  MPLAB IDE1.85 &  MPLAB XC 1.21
         for PIC18F14K22 
  2015-09-05 www.henteko.org
*/
#include <xc.h>
#include <stdio.h>
#define __18F14K22 1
#include <plib/i2c.h>
#include <plib/EEP.h>

#include "lcd.h"

#define DEBUG    0

#pragma config FOSC = IRC
#pragma config WDTEN = OFF
#pragma config FCMEN = OFF
#pragma config PWRTEN = ON
#pragma config BOREN = ON
#pragma config BORV = 2
#pragma config LVP = OFF
#pragma config MCLRE = OFF
#pragma config HFOFST = ON
#pragma config PLLEN = OFF

#define _XTAL_FREQ 16000000
#define EEPROM        0x0000            // EEPROM address

#define INA226_A_ADDR 0x90
#define INA226_B_ADDR 0x92

unsigned char renc_now, renc_old;    // value of rotary encoder
unsigned char rencflag;        // rorary encoder flag

void delay_ms(unsigned int t)
{
    for(; t > 0; t--)
        __delay_ms(1);
}

void putch(unsigned char ch)
{
    lcd_putch(ch);
    return;
}

void xeeprom_write(unsigned int adr, unsigned int data)
{
    unsigned char i;

     for(i = 0; i < 2; i++) {
        Write_b_eep(EEPROM + adr * 2 + i, (unsigned char)((data >> i * 8) & 0x00ff));
        Busy_eep();
    }
}

unsigned int xeeprom_read(unsigned int adr)
{
    unsigned int d;

    d = Read_b_eep(EEPROM + adr * 2 + 1);
    return (d << 8)+ Read_b_eep(EEPROM + adr * 2);
}


void interrupt isr(void)
{
    if(INTCONbits.TMR0IF == 1) {
        if(PORTBbits.RB7 == 1) {
            if(PORTBbits.RB5 == 0)
                renc_now = 0;
            else
                renc_now = 1;
        } else {
            if(PORTBbits.RB5 == 1)
                renc_now = 2;
            else
                renc_now = 3;
        }
        if((renc_now + 3 + 1) % 3 == renc_old) {
            rencflag = 1;
        }
        if((renc_now + 3 - 1) % 3 == renc_old) {
            rencflag = 2;
        }
        renc_old = renc_now;
        TMR0L = 200;
        INTCONbits.TMR0IF = 0;
    }
}

void i2c_ina226_send(unsigned int addr, unsigned char reg, unsigned int data)
{
    StartI2C();
    WriteI2C(addr);
    WriteI2C(reg);
    WriteI2C(data >> 8);
    WriteI2C(data & 0xff);
    StopI2C();
}

unsigned int i2c_ina226_recv(unsigned int addr, unsigned char reg)
{
    unsigned int ret;
    
    StartI2C();
    WriteI2C(addr);
    WriteI2C(reg);
    RestartI2C();
    WriteI2C(addr | 1);
    ret = ReadI2C() << 8;
    AckI2C();
    ret += ReadI2C();
    NotAckI2C();
    StopI2C();

    return ret;
}

void i2c_ina226_init()
{
    OpenI2C(MASTER, SLEW_ON);        // i2c master mode, clock 400khz
    SSPADD = 9;                    // _XTAL_FREQ / (SSPADD + 1) * 4 = 400khz

    i2c_ina226_send(INA226_A_ADDR, 0x00, 0x4527);
    i2c_ina226_send(INA226_A_ADDR, 0x05, 2048);
    i2c_ina226_send(INA226_B_ADDR, 0x00, 0x4527);
    i2c_ina226_send(INA226_B_ADDR, 0x05, 2048);
}

void main(void)
{
    unsigned char diff;            // current increment step
    unsigned int upper_a, upper_b, lower;
    unsigned int limit_a, limit_b;
    unsigned char sw0_state, sw1_state, sw2_state;
    unsigned int tim;
    unsigned int volt, curr;
    unsigned int up, dw;
    unsigned char mode;

    OSCCON = 0b01110010;    // Internal clock 16MHz
    TRISB = 0b11110000;        // RB4,6 I2C  RB5,7 rotaly encoder
    TRISA = 0b00110100;        // RA2,4,5 sw input
    TRISC = 0b00000000;        // LCD
    ANSEL = 0;
    ANSELH = 0;
    ADCON0 = 0;
    PORTB = 0;

    T0CON = 0b11000100;        // Timer0 rotaly encoder
    TMR0L = 200;
    TMR0IF = 0;
    TMR0IE = 1;

    INTCON2bits.RABPU = 0;    // RA,RB pull up enable
    WPUB = 0b11110000;        // RB pull up
    WPUA = 0b00110100;        // RA pull up

    limit_a = xeeprom_read(0);
    limit_b = xeeprom_read(1);
    if(limit_a == 0xffff)
        limit_a = 1000;
    if(limit_b == 0xffff)
        limit_b = 300;
    diff = 10;        // current incremnt step 10mA
    upper_a = 2500;    // A channel current limit 2.5A
    upper_b = 800;    // B channel current limit 800mA
    lower = 10;    // current low level limit 10mA
    tim = 0;
    rencflag = 0;
    sw0_state = sw1_state = sw2_state = 0;
    mode = 0;
    LATCbits.LATC2 = LATCbits.LATC3 = 0;

    lcd_init();
    i2c_ina226_init();

    delay_ms(100);

    INTCONbits.GIE = 1;        // interrupt enable

    while(1) {

        volt = i2c_ina226_recv(INA226_A_ADDR, 0x02);
        curr = i2c_ina226_recv(INA226_A_ADDR, 0x04);
        if(curr & 0x8000)
            curr = 0;
        lcd_gotopos(0, 0);
        up = volt * 1.25f / 1000;
        dw = ((volt * 1.25f / 1000) - up) * 100;
        printf("%2d.%02dV", up, dw);
        if(mode)
            printf(" %4d", limit_a);
        else
            printf(">%4d", limit_a);
        printf(" %4d", (int)(curr * 0.1f + 0.5));
        if(curr > (limit_a * 10))
            LATCbits.LATC3 = 0;

        volt = i2c_ina226_recv(INA226_B_ADDR, 0x02);
        curr = i2c_ina226_recv(INA226_B_ADDR, 0x04);
        if(curr & 0x8000)
            curr = 0;
        lcd_gotopos(0, 1);
        up = volt * 1.25f / 1000;
        dw = ((volt * 1.25f / 1000) - up) * 100;
        printf("%2d.%02dV", up, dw);
        if(mode)
            printf(">%4d", limit_b);
        else
            printf(" %4d", limit_b);
        printf(" %4d", (int)(curr * 0.1f + 0.5));
        if(curr > (limit_b * 10))
            LATCbits.LATC2 = 0;

        if(rencflag > 0) {
            if(mode == 0) {
                if(rencflag == 1) {
                    limit_a += diff;
                    if(limit_a > upper_a)
                        limit_a = upper_a;
                } else {
                    limit_a -= diff;
                    if(limit_a < lower)
                        limit_a = lower;
                }
            } else {
                if(rencflag == 1) {
                    limit_b += diff;
                    if(limit_b > upper_b)
                        limit_b = upper_b;
                } else {
                    limit_b -= diff;
                    if(limit_b < lower)
                        limit_b = lower;
                }
            }
            rencflag = 0;
        }

        // switch channel current limit
        if(PORTAbits.RA2 == 0) {
            sw0_state = 1;
            delay_ms(10);
            tim++;
        }
        if(sw0_state && PORTAbits.RA2 == 1) {
            sw0_state = 0;
            tim = 0;
            if(mode == 0)
                mode = 1;
            else
                mode = 0;
        }

        // A channel control
        if(PORTAbits.RA4 == 0) {
            sw1_state = 1;
            delay_ms(10);
        }
        if(sw1_state && PORTAbits.RA4 == 1) {
            sw1_state = 0;
            if(PORTCbits.RC2 == 1)
                LATCbits.LATC2 = 0;
            else
                LATCbits.LATC2 = 1;
        }

        // B channel control
        if(PORTAbits.RA5 == 0) {
            sw2_state = 1;
            delay_ms(10);
        }
        if(sw2_state && PORTAbits.RA5 == 1) {
            sw2_state = 0;
            if(PORTCbits.RC3 == 1)
                LATCbits.LATC3 = 0;
            else
                LATCbits.LATC3 = 1;
        }

        // write memory current limit
        if(tim > 100) {
            tim = 0;
            INTCONbits.GIE = 0;        // interrupt disable

            xeeprom_write(0, limit_a);
            xeeprom_write(1, limit_b);
            lcd_gotopos(0, 0);
            printf("Write Memory!!!!");
            lcd_gotopos(0, 1);
            printf(" A:%4d   B:%4d ", limit_a, limit_b);
            delay_ms(1000);

            INTCONbits.GIE = 1;        // interrupt disable
        }
    }
}

▲ページ Top へ...