Arduinoで確認できたので単独動作させるためブレッドボードで組み立て
LCDの右のボタンが画面表示切替用,その右にあるのがSW付のロータリーエンコーダで電圧の微調整用(ロータリーエンコーダは常に使う訳でないので基板上では取り外し可能にする予定)
機能仕様
画面表示切替で3つの表示を切り替える
①入力電圧と負荷電流表示
②負荷電流のみ表示
③タイマーと負荷電流の表示(タイマーのリセットはタイマー表示時に画面表示切替ボタンを3秒押す)
→ → → ①へ戻る
ロータリーエンコーダ(SW)を押すと微調整モードに切り替わる(もう1度押すと戻る)
調整画面は「入力電圧」と「負荷電流のシャント抵抗ドロップ電圧」の2つあり画面表示切替で切り替えてロータリーエンコーダを回す
←→
調整
ブレッドボードの状態で電圧の微調整を行ってみると表示電圧が不安定であることが判明
電源の問題だろうと三端子レギュレータのパスコン等の調整を行ったが変わらない
取り付けた「S-812C33AY-B-G」の仕様を観てみると(出力電圧精度:3.3 V±2.0%)でありADCの基準電圧としては安定度が良くないようだ
そこで(出力電圧精度:3.3 V±0.5%)の「LP2950L-3.3V」(100mA)に変更
更に分圧抵抗に0.1μFのパスコンを追加することで安定した
回路図
プログラム
ロータリーエンコーダの処理はこちらを利用できたので参考にしてライブラリを使用
昔の人間なもので全て整数で処理してる
#include <EEPROM.h>
#include "MsTimer2.h"
#include "U8glib.h"
#include "rotary.h"
/*
#define REFINTERNAL 1 //内蔵基準電圧
*/
#define PIN_VOLTREG 0 //電流計測用電圧DACピン
#define PIN_VOLTM25 3 //電圧計測用DACピン
#define PIN_MAINTSW 4 //調整モードスイッチピン番号
#define PIN_SHUNTSW 7 //シャント抵抗切替スイッチピン番号
#define PIN_MODESW 8 //モード切替スイッチピン番号
#ifndef REFINTERNAL
#define REF_DEFINE DEFAULT //電源電圧定義
#define REF_VOLT 33 //3.3V * 10
#else
#define REF_DEFINE INTERNAL //内蔵基準電圧(1.1V)
#define REF_VOLT 11 //1.1V * 10
#endif
#define R1_PART 9825 //分圧抵抗1(100KΩ)
#define R2_PART 1965 //分圧抵抗2(20KΩ)
#define MAX_VOLTREG ((long)REF_VOLT * 100 - 1)
#define MAX_VOLTM25 ((long)REF_VOLT * (R1_PART + R2_PART) / R2_PART * 100 - 1)
//電圧はmV換算
#define DSP_LINE1 28 //上行表示
#define DSP_LINE2 60 //下行表示
#define DSP_LINEC 44 //中央表示
#define OP_NORMAL 0 //通常オペレーション
#define OP_MAINTENANCE 1 //メンテナンスオペレーション
#define NRD_VOLTCRNT 0 //電圧電流表示
#define NRD_CRNTONLY 1 //電流のみ表示
#define NRD_TIMECRNT 2 //タイマー電流表示
#define NRD_VERSION 3 //ソフトウェアバージョン表示
#define MNT_VOLTADJ 0 //電圧調整
#define MNT_CRNTADJ 1 //電流換算電圧調整
#define TM_SEC 20 //秒換算値(50ms)
#define TM_MINUTE (TM_SEC * 60) //分換算値
U8GLIB_MINI12864 u8g(10, 9);
Rotary r = Rotary(2, 3);
static boolean running = false; //動作状態フラグ
static boolean forced; //強制表示フラグ
static unsigned long initTimeCount = 0; //初期時間
static unsigned long pushTimeCount = 0; //ボタン押下時間
static unsigned long timeCount = 0; //時間計測
static int opState = OP_NORMAL; //初期オペレーションモード(通常モード)
static int normalMode = NRD_VOLTCRNT; //初期通常モード
static int maintMode = MNT_VOLTADJ; //初期メンテナンスモード
static boolean btnOnA = false; //ボタンA押下フラグ
static boolean btnWaitA = false; //ボタンA押下中フラグ
static boolean btnOnB = false; //ボタンB押下フラグ
static int adjVolt; //電圧調整値(EEPROM:0,1)
static int adjCrntVolt; //電流換算電圧調整値(EEPROM:2,3)
//調整値表示
//最小値の1は0.1%
static void displayAdjValue(int dl, int vv) {
char str[8];
int i = vv / 10;
int f = vv % 10;
if(f < 0) f *= (-1);
if(vv < 0 && i == 0) {
sprintf(str, "-0.%d\0", f);
} else {
sprintf(str, "%2d.%d\0", i, f);
}
u8g.setPrintPos(16, dl);
u8g.print(str);
u8g.setPrintPos(86, dl);
u8g.print(" %");
}
//電圧を小数点付出力文字列に調整する
//入力電圧は1000倍された整数で下3桁が小数点以下となる
//小数点以下の表示は2桁とする
// 1000未満 0.XX
// 10000未満 N.XX
// 上記以外 NN.XX
//
static void displayVoltString(int dl, int ee) {
char str[8];
if(ee < 0) {
//最大電圧超
u8g.setPrintPos(4, dl);
u8g.print("@OVER@");
} else {
int i = ee / 1000;
int f = (ee % 1000)/10; //小数点以下2桁にする
if(ee < 1000) {
sprintf(str, " 0.%02d\0", f);
} else if(ee < 10000) {
sprintf(str, " %d.%02d\0", i, f);
} else {
sprintf(str, "%2d.%02d\0", i, f);
}
u8g.setPrintPos(4, dl);
u8g.print(str);
u8g.setPrintPos(86, dl);
u8g.print(" V");
}
}
//電流表示(swで単位をmA/A切換え)
static void displayCrntString(int dl, int ii, int sw) {
char str[8];
if(sw == HIGH) {
sprintf(str, "%4d\0", ii);
u8g.setPrintPos(4, dl);
u8g.print(str);
u8g.setPrintPos(78, dl);
u8g.print("mA");
} else {
int i = ii / 1000;
int f = (ii % 1000)/10; //小数点以下2桁にする
if(ii < 1000) {
sprintf(str, " 0.%02d\0", f);
} else {
sprintf(str, "%2d.%02d\0", i, f);
}
u8g.setPrintPos(4, dl);
u8g.print(str);
u8g.setPrintPos(86, dl);
u8g.print(" A");
}
}
//時間表示(HH:MM:SS)
static void displayTimeString(int dl, unsigned long tm) {
char str[10];
int hh = tm / ((unsigned long)60 * TM_MINUTE);
int mm = (tm / TM_MINUTE) % 60;
int ss = (tm % TM_MINUTE) / TM_SEC;
sprintf(str, "%02d:%02d:%02d\0", hh, mm, ss);
u8g.setPrintPos(2, dl);
u8g.print(str);
}
//電圧測定ポートから読込み
static int readVoltPort() {
int e, val = (-1);
if((e = analogRead(PIN_VOLTM25)) < 1023) {
//ここでMAX_VOLTM25を補正する
int maxvolt = (long)MAX_VOLTM25 * adjVolt / 1000 + MAX_VOLTM25;
val = map(e, 0, 1023, 0, maxvolt);
}
return(val);
}
//電流測定ポートから読込み
static int readCrntPort() {
//ここでMAX_VOLTREGを補正する
int maxvolt = (long)MAX_VOLTREG * adjCrntVolt / 1000 + MAX_VOLTREG;
return(map(analogRead(PIN_VOLTREG), 0, 1023, 0, maxvolt));
}
//電流読込:電流値算出
//抵抗値の電圧降下による電圧から電流値を算出する
// D7:0.5Ω=H,5Ω=L 切替
//
static int changeCrnt(int ee, int sw) {
int val;
if(sw == HIGH) {
//R = 5.00
val = ee / 5; //I = E / R
} else {
//R = 0.5
//val = ee * 2; //0.5 -> 逆数で*2
//R = 0.52
val = (long)ee * 192 / 100; //1.92 = 0.52 の逆数
}
return(val);
}
void timeUp() {
timeCount++;
//メンテナンスSW処理(優先)
int maintsw = digitalRead(PIN_MAINTSW);
if(btnOnB) {
//メンテナンスボタン押下後
if(maintsw == HIGH) {
//メンテナンスボタンが離された
btnOnB = false;
if(opState == OP_NORMAL) {
//メンテナンスモードへ移行
opState = OP_MAINTENANCE;
maintMode = MNT_VOLTADJ;
} else {
//通常モードへ移行
opState = OP_NORMAL;
//電圧調整値書込み(EEPROMへの書き込みには3.3ミリ秒かかる)
EEPROM.write(0, adjVolt>>8);
EEPROM.write(1, adjVolt&0xff);
EEPROM.write(2, adjCrntVolt>>8);
EEPROM.write(3, adjCrntVolt&0xff);
}
}
} else if(maintsw == LOW) {
//メンテナンスボタン押下
btnOnB = true;
}
//モードSW処理
int modesw = digitalRead(PIN_MODESW);
if(btnWaitA) {
if(modesw == HIGH) {
btnWaitA = btnOnA = false; //ボタン押下終了
}
} else {
if(btnOnA) {
//ボタン押下後
if(timeCount - pushTimeCount >= (TM_SEC * 3)) {
btnWaitA = true; //ボタンが離されるまで待つ
//3秒以上の長押で操作モード変更
//opState = (opState == OP_NORMAL)? OP_MAINTENANCE: OP_NORMAL;
//メンテナンスモードにしたら通常モードを1にする(止め)
//normalMode = NRD_VOLTCRNT;
if(normalMode == NRD_TIMECRNT) {
//時間表示モードならリセット
initTimeCount = timeCount;
forced = true; //強制再表示
}
} else if(modesw == HIGH) {
//ボタンが離された
btnOnA = false; //ボタン押下終了
//3秒未満のクリックは表示モード変更
if(opState == OP_NORMAL) {
//通常オペレーション
switch(normalMode) {
case NRD_VOLTCRNT: normalMode = NRD_CRNTONLY; break;
case NRD_CRNTONLY: normalMode = NRD_TIMECRNT; break;
case NRD_TIMECRNT: normalMode = NRD_VOLTCRNT; break;
}
} else {
//メンテナンスオペレーション
maintMode = (maintMode == MNT_VOLTADJ)? MNT_CRNTADJ: MNT_VOLTADJ;
}
}//else ボタン押下中
} else if(modesw == LOW) {
//ボタン押下
btnOnA = true;
pushTimeCount = timeCount;
}
}
}
ISR(PCINT2_vect) {
if(opState == OP_MAINTENANCE) {
unsigned char result = r.process();
if(result) {
//Serial.println(result == DIR_CW ? "Right" : "Left");
int adj = (result == DIR_CW)? 1: (-1);
if(maintMode == MNT_VOLTADJ) {
adjVolt += adj;
} else {
adjCrntVolt += adj;
}
}
}
}
void setup() {
analogReference(REF_DEFINE);
pinMode(PIN_SHUNTSW, INPUT_PULLUP); //シャント抵抗切替スイッチの入力設定
pinMode(PIN_MODESW, INPUT_PULLUP); //モード切替スイッチの入力設定
pinMode(PIN_MAINTSW, INPUT_PULLUP); //調整モードスイッチの入力設定
u8g.setContrast(190);
u8g.setFont(u8g_font_helvR24);
MsTimer2::set(1000/TM_SEC, timeUp); //インターバルタイマー設定
MsTimer2::start();
//調整用ロータリーエンコーダ割込み設定
PCICR |= (1<<PCIE2);
PCMSK2 |= (1<<PCINT18) | (1<<PCINT19);
//電圧調整値
adjVolt = (EEPROM.read(0)<<8) + EEPROM.read(1);
adjCrntVolt = (EEPROM.read(2)<<8) + EEPROM.read(3);
}
void loop() {
int volt, crnt, crntvolt, shuntsw, modesw;
int tm, st, md, mm;
noInterrupts();
forced = false;
tm = timeCount / TM_SEC;
st = opState;
md = normalMode;
mm = maintMode;
interrupts();
volt = readVoltPort();
crntvolt = readCrntPort();
shuntsw = digitalRead(PIN_SHUNTSW);
crnt = changeCrnt(crntvolt, shuntsw);
u8g.firstPage();
do {
if(st == OP_NORMAL) {
//通常オペレーション
switch(md) {
case NRD_VOLTCRNT:
displayVoltString(DSP_LINE1, volt);
displayCrntString(DSP_LINE2, crnt, shuntsw);
break;
case NRD_CRNTONLY:
displayCrntString(DSP_LINEC, crnt, shuntsw);
break;
case NRD_TIMECRNT:
displayTimeString(DSP_LINE1, timeCount - initTimeCount);
displayCrntString(DSP_LINE2, crnt, shuntsw);
break;
}
} else {
//メンテナンスオペレーション
if(mm == MNT_VOLTADJ) {
displayVoltString(DSP_LINE1, volt);
displayAdjValue(DSP_LINE2, adjVolt);
} else {
displayAdjValue(DSP_LINE1, adjCrntVolt);
displayVoltString(DSP_LINE2, crntvolt);
}
}
} while(u8g.nextPage());
while(!forced && tm == (timeCount / TM_SEC)) delay(100);
}