電子カウンタ(ABCDカウンタ)をつくる

2023/08/13

PIC 計測 自作

t f B! P L

ソーラーカー搭載用電流積算計とセットで使うことをを想定した、カウンタ回路を作ってみました。積算計(V/Fコンバータ回路)の出力パルスをカウントして、積算電流を把握することが目的です。

ABCDカウンタ

積算電流計(V/Fコンバータ回路)による積算電流の測定では、電子カウンタが使用されます。この電子カウンタには、ライン精機「ABCカウンタ G37-102」などが使われます。

このABCカウンタを意識して、カウンタ回路を作ってみました。

(積算電流値計測システムの概要については、電流積算計をつくる(1)を参照。)

ABCDカウンタ外観

今回製作する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モジュールの表示例は、こんな感じになります。

ABCDカウンタの表示例

プログラム

パルス検出はPICマイコンのPortBピン変化割込みを利用します。入力パルス3系統をPortBの入力として、ピン変化割込み処理内で、カウントアップを行います。

ピン変化で割り込みが発生するので、パルス1個あたり、パルスの立ち上がりと立ち下がりで2回割込み処理に入ります。割込み関数内での処理は、立ち上がりと立ち下がりを区別せず、どちらでもカウントアップします。そして、表示する際にカウント値を(1/2)倍して、パルスの数を表示することにしました。

メインループ内では、D値の演算とLCDへの表示更新を行います。

ABCDカウンタのプログラムフローチャート

全体回路

ABCDカウンタ回路図

キャラクタLCDモジュールは4bitモードで使用しました。空きピン処理は「液晶モジュールを4ビットモードで使ったときの空きピン処理」(トランジスタ技術2005年9月号)を参考に、オープンとしました。

また、入力信号のLPFはfc=16kHzとしました。

動作試験

製作したABCDカウンタの動作試験として、5000パルス入力テストを実施しました。

5000個のパルスを発生する回路を作り、その出力をABCDカウンタに入力します。その結果、カウンタの表示値が"5000"となることを確認します。

試験用5000パルス出力回路は、PIC12F683を用いて作りました。フローチャートはこんな感じです。

5000パルス出力回路のフローチャート

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);

当ブログはアフィリエイト広告を利用しています。

このブログを検索

最新記事

ブログ運営者:そのへんの誰か


このブログのURL

QooQ