4インチの7セグLEDを3つ使用してカウンター表示器を作成した。
といっても、昔の用にシフトレジスターを論理回路でゴチャゴチャする必要が無いので、作るのは非常に簡単だ。
今回作成したのは大型の7セグLEDを3つ並べて使用する物なのだけれど、将来的に4桁必要になるかもしれないという事から、1桁毎にTPIC6C596を取り付けて、このLEDドライバーをチェーン接続するという方式を取った。
この7セグLEDドライバー1つで制御できるのは一桁だけなのだが、これをディジーチェーンすると、いくらでも桁数を増やす事ができる。
カウンターの制御は当初Arduino nanoを使う予定だったのだが、既にヘッダピンを下向きに半田付けしてしまっていて、組み込みにくかった事と、最近Arduinoが互換品でもそれなりの値段になっていて(ヘタするとESP8266やEPS32のボードの方が安いぐらい)、使うのが勿体なかったので、
他に何か良いのが無いかと探してみた処、AtmelのATtiny88を見つけた。
このマイコンボード、実はnanoと勘違いして購入してしまった代物で、届いてからArduino nanoとは全く別物だと気づいて愕然としていたのだけれど、調べてみるとArduino IDEが使えるし、Vinや5Vout、EEPROM等、今回のカウンター作成で使いたい機能は一通り揃っていたので、試しに使ってみる事にした。
microUSB-Bのポートは付いているものの、USB制御チップが乗っていないので、シリアルで書き込みしないと駄目かなぁと思ったら、V-USBという仕組みを採用しているらしく、PCにドライバーを入れれば容易に認識した。
ArduinoIDEで使う際は、コンパイル後、ファームウェアを転送し始めたらPCとATtiny88をUSBケーブルで繋ぐ、もしくはATtiny88のリセットボタンを押してリブートするといった作法が必要になるけれど、慣れればCOMポートの設定も必要ないし便利といえば便利かもしれない。
カウンターのプログラムは多分に漏れず他人の書いたコードのコピペで、それを元にして実装したい機能に合わせてライブラリを追加したり、足りない部分や辻褄合わせの為のコードを追記していく。 Arduinoの良い処は既に安定して動作するライブラリを他人様が無料で提供してくれているところだろう。そして、ハードウェアレベルのロジックを考えなくても大抵の事ができてしまう。
何故そうなるのかといった理屈は知っておいた方が良いとは思うが、昔みたいにオシロを持ち出して波形なんて眺めなくても、電卓とスペックシートとプログラミング環境(とインターネットの集合知)があれば、ショボい電子工作なら素人でも手が出せるようになっているので、間口は無茶苦茶広くなっている。
凝った事をしたくなったら、基板の作成は安価に出せるようになってるし、機構や外装には3Dプリンターが使えるので、これもハードルが低い。
コードは以下の通り。
#include EEPROM.h
#include ButtonEvents.h
//GPIO declarations
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
byte segmentClock = 4;
byte segmentLatch = 5;
byte segmentData = 3;
const int up_pin = 6; //カウントアップボタン
const int down_pin = 7; //カウントダウンボタン
const int reset_pin = 1; //リセットボタン
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ButtonEvents up_button; // create an instance of the ButtonEvents class to attach to our button
ButtonEvents down_button;
ButtonEvents reset_button;
int number = 0;
int segnumber= 0;
int button_pt = 0;
char sp = ' ';
void setup()
{
Serial.begin(9600);
Serial.println(EEPROM.length());
Serial.println("Large Digit Counter ver.1.0");
pinMode(segmentClock, OUTPUT);
pinMode(segmentData, OUTPUT);
pinMode(segmentLatch, OUTPUT);
pinMode(up_pin, INPUT_PULLUP);
pinMode(down_pin, INPUT_PULLUP);
pinMode(reset_pin, INPUT_PULLUP);
digitalWrite(segmentClock, LOW);
digitalWrite(segmentData, LOW);
digitalWrite(segmentLatch, LOW);
EEPROM.get(0x00,number);
// attach our ButtonEvents instance to the button pin
up_button.attach(up_pin);
down_button.attach(down_pin);
reset_button.attach(reset_pin);
//ButtonParameter Setup
//ダブルタップ制限時間(ms)
up_button.doubleTapTime(200);
down_button.doubleTapTime(200);
reset_button.doubleTapTime(200);
//長押し判定時間(ms)
up_button.holdTime(5000);
down_button.holdTime(5000);
reset_button.holdTime(5000);
}
void loop()
{
//アップボタン(シングルタップ +1, ダブルタップ +10)
up_button.update();
if(up_button.tapped() == true)
{
EEPROM.put(0xf0,number); //カウンター数字バックアップ
number++;
number %= 1000; //Reset x after 999
EEPROM.put(0x00,number);
}
if(up_button.doubleTapped() == true)
{
EEPROM.put(0xf0,number); //カウンター数字バックアップ
number = number + 10;
number %= 1000; //Reset x after 999
EEPROM.put(0x00,number);
}
//ダウンボタン(シングルタップ -1, ダブルタップ -10)
down_button.update();
if(down_button.tapped() == true)
{
EEPROM.put(0xf0,number); //カウンター数字バックアップ
number--;
if(number < 0)
{
number = number + 1000;
}
number %= 1000; //Reset x after 999
EEPROM.put(0x00,number);
}
if(down_button.doubleTapped() == true)
{
EEPROM.put(0xf0,number); //カウンター数字バックアップ
number = number - 10;
if(number < 0)
{
number = number + 1000;
}
number %= 1000; //Reset x after 999
}
// リセットボタン(長押し リセット, ダブルタップ リストア)
reset_button.update();
if(reset_button.held() == true) //リセットボタン長押しでリセット
{
EEPROM.put(0xf0,number); //カウンター数字バックアップ
number = 0;
EEPROM.put(0x00,number);
}
if(reset_button.doubleTapped() == true) //リセットボタンダブルタップでリストア
{
EEPROM.get(0xf0,number); //カウンター数字リストア
EEPROM.put(0x00,number);
}
// Serial.println(number);
showNumber(number);
}
//Takes a number and displays 3 numbers. Displays absolute value (no negatives)
void showNumber(float value)
{
segnumber = number ;
int segnumber = abs(value); //Remove negative signs and any decimals
Serial.print("number: ");
Serial.println(segnumber);
for (byte x = 0 ; x < 3 ; x++) //桁数決定</span>
{
if ((x > 0) && (segnumber == 0)) //下2桁目以上が0の場合スペース
{
postNumber(sp, false);
}
else
{
int remainder = segnumber % 10; //各桁の数字を抽出
postNumber(remainder, false); //1桁ずつ送信
segnumber /= 10;
}
}
//Latch the current segment data
digitalWrite(segmentLatch, LOW);
digitalWrite(segmentLatch, HIGH); //Register moves storage register on the rising edge of RCK
}
//Given a number, or '-', shifts it out to the display
void postNumber(byte segnumber, boolean decimal)
{
// - A
// / / F/B
// - G
// / / E/C
// -. D/DP
#define a 1<<0
#define b 1<<6
#define c 1<<5
#define d 1<<4
#define e 1<<3
#define f 1<<1
#define g 1<<2
#define dp 1<<7
byte segments;
switch (segnumber)
{
case 1: segments = b | c; break;
case 2: segments = a | b | d | e | g; break;
case 3: segments = a | b | c | d | g; break;
case 4: segments = f | g | b | c; break;
case 5: segments = a | f | g | c | d; break;
case 6: segments = a | f | g | e | c | d; break;
case 7: segments = a | b | c; break;
case 8: segments = a | b | c | d | e | f | g; break;
case 9: segments = a | b | c | d | f | g; break;
case 0: segments = a | b | c | d | e | f; break;
case ' ': segments = 0; break;
case 'c': segments = g | e | d; break;
case '-': segments = g; break;
}
if (decimal) segments |= dp;
//Clock these bits out to the drivers
for (byte x = 0 ; x < 8 ; x++)
{
digitalWrite(segmentClock, LOW);
digitalWrite(segmentData, segments & 1 << (7 - x));
digitalWrite(segmentClock, HIGH); //Data transfers to the register on the rising edge of SRCK
}
}
参考というか、ほぼコピペさせて頂いたサンプルプログラムはsparkfunのLarge Digit Driver Hookup Guideにある「Example:Two Large Digits」
使用させて頂いたライブラリは
ButtonEvents
とそれに付随する
Bounce2
というわけで、これらをとりあえずブレッドボードで接続して動作させた処、問題無く動作したので、実装を進めていく。
ちなみに、ATtiny88やTPIC6C596はamazonで購入すると、安かったり高かったりとなかなか値段が安定しないので、aliexpressで売られている価格と比較すれば良いと思う。ATtiny88はAliexpressに近い値段で売られている事が多いが、TPIC6C596は値差が大きい気がする。