ソーラーカー搭載用電流積算計とセットで使うことをを想定した、カウンタ回路を作ってみました。積算計(V/Fコンバータ回路)の出力パルスをカウントして、積算電流を把握することが目的です。
ABCDカウンタ
積算電流計(V/Fコンバータ回路)による積算電流の測定では、電子カウンタが使用されます。この電子カウンタには、ライン精機「ABCカウンタ G37-102」などが使われます。
このABCカウンタを意識して、カウンタ回路を作ってみました。
(積算電流値計測システムの概要については、電流積算計をつくる(1)を参照。)
今回製作するABCDカウンタは、入力が3系統(A、B、C)、「D」にカウンタ演算値(D=A-B+C)を表示します。入力「A」にソーラー積算電流値、入力「B」にモータ積算電流値、入力「C」にモータ回生積算電流値とすると、「D」がバッテリの積算電流値に対応します。
ABCDカウンタの設計
回路構成と方針
ABCDカウンタが行う処理は、主に次の3つです。
- パルス入力ごとにカウントアップ
- 「D」値を演算
- 値を表示
これらの機能を実現するために、PICマイコンを用いました。値の表示は、16文字×2行キャラクタLCDモジュールを使いました。
仕様
- 入力波形:5V矩形波
- 入力最大周波数:10kHz
- 電源電圧:5V
- 入力系統数:3(A、B、C)
- 演算値出力:1(D=A-B+C)
- 表示ディスプレイ:キャラクタLCD「SC1602BS*B」
- 表示桁数:各系統 6桁
- 表示更新:200ms
キャラクタLCDモジュールの表示例は、こんな感じになります。
プログラム
パルス検出はPICマイコンのPortBピン変化割込みを利用します。入力パルス3系統をPortBの入力として、ピン変化割込み処理内で、カウントアップを行います。
ピン変化で割り込みが発生するので、パルス1個あたり、パルスの立ち上がりと立ち下がりで2回割込み処理に入ります。割込み関数内での処理は、立ち上がりと立ち下がりを区別せず、どちらでもカウントアップします。そして、表示する際にカウント値を(1/2)倍して、パルスの数を表示することにしました。
メインループ内では、D値の演算とLCDへの表示更新を行います。
全体回路
キャラクタLCDモジュールは4bitモードで使用しました。空きピン処理は「液晶モジュールを4ビットモードで使ったときの空きピン処理」(トランジスタ技術2005年9月号)を参考に、オープンとしました。
また、入力信号のLPFはfc=16kHzとしました。
動作試験
製作したABCDカウンタの動作試験として、5000パルス入力テストを実施しました。
5000個のパルスを発生する回路を作り、その出力をABCDカウンタに入力します。その結果、カウンタの表示値が"5000"となることを確認します。
試験用5000パルス出力回路は、PIC12F683を用いて作りました。フローチャートはこんな感じです。
5000パルス出力回路の出力周波数は10kHzとしました。試験の結果、A,B,Cとも表示値が"5000"となり、動作良好でした。また、Dの値がちゃんと「D=A-B+C」となっていることも確認しました。
運用上の注意
このABCDカウンタは、電源を落とすと、カウント値は消えてしまいます。そのため、電源を落とす前に、カウント値をメモをとる。など運用上の対応が必要です。
ソースコード
/************************************************ ABCD_Counter 電気系ものづくりブログ https://www.sonohen.page/ CPU:PIC16F88 CLK:8MHz RS:RA3 EN:RA4 D7:RB3 D6:RB2 D5:RB1 D4:RB0 A:RB4 B:RB5 C:RB6 D=A-B+C ************************************************/ #include <xc.h> #include <pic16f88.h> #include <stdio.h> #include "hd44780.h" // CONFIG1 #pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled) #pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital I/O, MCLR internally tied to VDD) #pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled) #pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming) #pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off) #pragma config CCPMX = RB0 // CCP1 Pin Selection bit (CCP1 function on RB0) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) // CONFIG2 #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled) #pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode disabled) #define _XTAL_FREQ 8000000 //CLK:8MHz /*グローバル変数*****************/ long A_CNT; long B_CNT; long C_CNT; long D_CNT; char PB_mem; /*割込み処理*****************/ void __interrupt() INT(void){ char PB, PB_b; PB = PORTB & 0b01110000; //マスク PB_b = PB ^ PB_mem; //XOR if(PB_b & 0x10){ //bit4 A_CNT++; } if(PB_b & 0x20){ //bit5 B_CNT++; } if(PB_b & 0x40){ //bit6 C_CNT++; } PB_mem = PB; INTCONbits.RBIF = 0; //flag clear } void main() { char txtA[12]; char txtB[12]; char txtC[12]; char txtD[12]; char DispL1[17]; char DispL2[17]; char i; //各種設定 OPTION_REG = 0b11000000; //Pull-up:D INTCON = 0b00001000; //PBIE:E PIE1 = 0x00; PIR1 = 0x00; PIE2 = 0x00; PIR2 = 0x00; PCON = 0x03; TRISA = 0b00000000; TRISB = 0b01110000; CCP1CON = 0x00; //CCP1 disable SSPCON = 0x00; RCSTA = 0x00; ANSEL = 0x00; CMCON = 0b00000111; OSCCON = 0b01110000; PORTA = 0x00; //clear PORTB = 0x00; lcd_init(); // Initialize LCD sprintf(DispL1, "ABCD Counter "); sprintf(DispL2, " sonohen.page "); lcd_locate(0, 0); for(i=0; i<16; i++){ lcd_putc(DispL1[i]); } lcd_locate(1, 0); for(i=0; i<16; i++){ lcd_putc(DispL2[i]); } __delay_ms(1500); sprintf(DispL1, "A B "); sprintf(DispL2, "C D "); lcd_locate(0, 0); //1行目 for(i=0; i<16; i++){ lcd_putc(DispL1[i]); } lcd_locate(1, 0); //2行目 for(i=0; i<16; i++){ lcd_putc(DispL2[i]); } A_CNT = 0; B_CNT = 0; C_CNT = 0; D_CNT = 0; PB_mem = 0; INTCONbits.GIE = 1; //割り込み許可 //Mainloop while(1){ //6桁overflow if(A_CNT >= 2000000){ A_CNT -= 2000000; } if(B_CNT >= 2000000){ B_CNT -= 2000000; } if(C_CNT >= 2000000){ C_CNT -= 2000000; } D_CNT = A_CNT - B_CNT + C_CNT; sprintf(txtA, "%6ld" ,(A_CNT>>1)); sprintf(txtB, "%6ld" ,(B_CNT>>1)); sprintf(txtC, "%6ld" ,(C_CNT>>1)); sprintf(txtD, "%6ld" ,(D_CNT>>1)); lcd_locate(0, 1); //A for(i=0; i<6; i++){ lcd_putc(txtA[i]); } lcd_locate(0, 9); //B for(i=0; i<6; i++){ lcd_putc(txtB[i]); } lcd_locate(1, 1); //C for(i=0; i<6; i++){ lcd_putc(txtC[i]); } lcd_locate(1, 9); //D for(i=0; i<6; i++){ lcd_putc(txtD[i]); } __delay_ms(200); } }
LCDの表示はEZ-LCDを使いました。変更点を抜粋すると、こんな具合です。
hd44780.h
#define _LCD_ROWS 2 /* Number of Rows (1,2 or 4) */ #define _LCD_COLS 16 /* Number of Columns (8..40) */
hd44780.c
#include <xc.h> #define _XTAL_FREQ 8000000 //CLK:8MHz
/* Bus controls */ #if 1 //#include <avr/io.h> /* Hardware specific include file */ #define IF_BUS 4 /* Bus width (4 or 8) */ #define DELAY_US(n) __delay_us(n) /* Delay d microseconds */ #define IF_INIT() /* Initialize control port */ #define IF_DLY60() /* Delay >=60ns (can be blanked for most uC) */ #define IF_DLY450() __delay_us(1) /* Delay >=450ns@3V, >=250ns@5V */ #define E1_HIGH() PORTA|=0x10 /* Set E/E1 high */ #define E1_LOW() PORTA&=0xEF /* Set E/E1 low */ //#define E2_HIGH() PORTB|=0x01 /* Set E2 high (dual controller only) */ //#define E2_LOW() PORTB&=0xFE /* Set E2 low (dual controller only) */ #define RS_HIGH() PORTA|=0x08 /* Set RS high */ #define RS_LOW() PORTA&=0xF7 /* Set RS low */ #define OUT_DATA(d) PORTB = (PORTB & 0xF0) | ((d>>4) & 0x0F) /* Output a byte d on the bus (higher 4 bits of d in 4-bit mode) */
ディレイ関数の置き換え
DELAY_US(LCD_DT2); → __delay_us(86);
DELAY_US(LCD_DT1); → __delay_us(3060);
0 件のコメント:
コメントを投稿