Arduino 9) 4連 Digital Tube LED Displayを試す
2019.02.28


注文してから1ヶ月以上かかりました。長かった・・orz



左からVCC、SCLK、RCLK、DIO、GND
この5ピンを接続し、SCLK、RCLK、DIOの3ピンで制御します。



基本配線は上記でいいと思います。



どうせなら変化があったほうが面白いので
前回の可変抵抗を読み取って表示することを踏まえて配線しています。



LED文字には各パーツごとに光るようになっています。
それらを組み合わせて人間が数字として読み取れるように
場所を指定し光らせます。
ビットが0なら光る。1なら消えるを意味します。
つまり
0なら 0b11000000
1なら 0b11111001
2なら 0b10100100
3なら 0b10110000
4なら 0b10011001
5なら 0b10010010
6なら 0b10000010
7なら 0b11111000
8なら 0b10000000
9なら 0b10010000
といった2進数コードを使用します。

ではコーディングですが、いちから作るのは面倒なので、
どこぞから拾ってきたものをカスタマイズして仕上げました。

//接続するピンを設定する
#define SCLK 3
#define RCLK 4
#define DIO 5


byte digitBuffer[4]; //キャラ数(文字数を設定)この配列に入れた数字がそのまま表示される。

void setup(){
pinMode(RCLK, OUTPUT);
pinMode(SCLK, OUTPUT);
pinMode(DIO, OUTPUT);
}


void loop()
{
digitBuffer[0] = 1; //1文字目に1を表示
digitBuffer[1] = 2; //2文字目に2を表示
digitBuffer[2] = 3; //3文字目に3を表示
digitBuffer[3] = 4; //4文字目に4を表示

showDisplay();
}


//表示するために関数を定義
void showDisplay(){
const byte digit[10] = {
0b11000000,//0
0b11111001,//1
0b10100100,//2
0b10110000,//3
0b10011001,//4
0b10010010,//5
0b10000010,//6
0b11111000,//7
0b10000000,//8
0b10010000 //9
};
const byte chr[4] = {
0b00001000,//1
0b00000100,//2
0b00000010,//3
0b00000001,//4

};


for(byte i = 0; i <= 3; i++)
{
digitalWrite(RCLK, LOW);
shiftOut(DIO,SCLK,MSBFIRST, digit[digitBuffer[i]]);
shiftOut(DIO,SCLK,MSBFIRST, chr[i]);
digitalWrite(RCLK, HIGH);
delayMicroseconds(1000);
}
}




無事光りました。
では可変抵抗の値を表示するプログラムに改造してみたいと思います。

int Denatu = 0;
int OutNum = 0;
int StackNum = 0;


//接続するピンを設定する
#define SCLK 3
#define RCLK 4
#define DIO 5


byte digitBuffer[4]; //キャラ数(文字数を設定)この配列に入れた数字がそのまま表示される。

void setup(){
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
pinMode(13, OUTPUT); //ボードのLEDを使用する
pinMode(RCLK, OUTPUT);
pinMode(SCLK, OUTPUT);
pinMode(DIO, OUTPUT);
}


void loop()
{
//digitalWrite(13, HIGH); //LEDを光らせる
Denatu = analogRead(0); //A0ピンの電圧を測る0(0V)~1023(5V)
if (Denatu != StackNum) { //前回と値が違うときだけ処理を行う


Serial.println(Denatu); //シリアルモニタに値を出力
OutNum = Denatu;


//4文字目に数字を表示
digitBuffer[3] = OutNum % 10; //10で割ってあまりを求める。
OutNum = OutNum / 10; //値を10で割って結果を元の数字に返す。これで4ケタが3ケタにシフトする。

//3文字目に数字を表示
digitBuffer[2] = OutNum % 10; //10で割ってあまりを求める。
OutNum = OutNum / 10; //値を10で割って結果を元の数字に返す。これで3ケタが2ケタにシフトする。

//2文字目に数字を表示
digitBuffer[1] = OutNum % 10; //10で割ってあまりを求める。
OutNum = OutNum / 10; //値を10で割って結果を元の数字に返す。これで2ケタが1ケタにシフトする。


//1文字目に数字を表示
digitBuffer[0] = OutNum % 10; //10で割ってあまりを求める。


//結果を4LEDに表示
showDisplay();
//今回の電圧を記録する。
StackNum = Denatu;
}

delay(Denatu); //読み取った値分処理を止める1023=1.023秒
digitalWrite(13, LOW); //LEDを消す
delay(Denatu); //読み取った値分処理を止める1023=1.023秒


}

//表示するために関数を定義
void showDisplay(){
const byte digit[10] = {
0b11000000,//0
0b11111001,//1
0b10100100,//2
0b10110000,//3
0b10011001,//4
0b10010010,//5
0b10000010,//6
0b11111000,//7
0b10000000,//8
0b10010000 //9
};
const byte chr[4] = {
0b00001000,//1
0b00000100,//2
0b00000010,//3
0b00000001,//4

};


for(byte i = 0; i <= 3; i++)
{
digitalWrite(RCLK, LOW);
shiftOut(DIO,SCLK,MSBFIRST, digit[digitBuffer[i]]);
shiftOut(DIO,SCLK,MSBFIRST, chr[i]);
digitalWrite(RCLK, HIGH);
delayMicroseconds(1000);
}
}


赤字の部分はこの仕組のキモで、
可変抵抗の4ケタまでの数字を分解して4つのLEDに表示する。
プログラマーを長くやっていると定番のアルゴリズムですが、
ここでも役立つとは思いませんでした。
では実行。



表示がオカシイ。
値が1024でも一桁目の4しか正常に表示しない。
他の桁はチカチカしてしまう。
いろいろソースを変えて試してみたけど
Delayで処理を止めるとその間だけ表示も止まっているようだ。

今の所解決方法が見つからない。
よく使われる部品でこんなトラブルとはがっかり(;´Д`)
Arduino 8) EEPROMを使って電源を切っても変数が消えないようにする
2019.02.15
プログラムを作るに当たって、電源を切ったらデータが消えてしまうというのは
考えられません。あってはならないことです。
Windowsはデータベースに保存したり、iniファイルを使用したりと様々な方法で
環境の保存を行っております。
Arduinoは頻繁に電源を入り切りするコンピュータです。
Windowsのようにアプリの終了やシャットダウンで環境保存することはできません。
そこでプログラム動作中に変数を退避する必要が出てくるわけですが、
それを実現するのがEEPROMに記憶するという方法です。

EEPROMのエリアは基本0~255の256個(256Byte)となってますが、
Unoの場合1KB(1024Byte)Megaの場合4KBまで使用可能です。
一般的には10万回の書き込みに耐えられるとされていますが、
実際にテストを行った方が居て、300万回は耐えたという情報もあります。

1秒毎にカウントして記憶するプログラムを組んでみました。
EEPROM.hを覆っている<>はhtmlの都合上全角になっております。

#include <EEPROM.h>
int Access_count;

void setup() {
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
pinMode(13, OUTPUT); //ボードのLEDを使用する
Access_count = EEPROM.read(0); //0番地にEEPROMから値を読み込む
}


void loop() {
digitalWrite(13, HIGH); //LEDを光らせる
delay(1000); //1秒
Access_count = Access_count + 1; //アクセスカウンターを追加
Serial.println(Access_count); //シリアルモニタに値を出力
EEPROM.write(0, Access_count); //0番地からEEPROMに値を書き込む。
}


シンプルにまとめてみました。
正常にEEPROMに書き込まれたかは電源入れた都度
シリアルモニタで確認できるようにしています。
このプログラムは1秒毎にカウントしていくものですが、
1Byteを超える数字256以上まで進んだ場合は正常に保存できません。
255より大きい数字は自分で分割して保存エリアを分ける必要があります。
それではちょっと勝手が悪いので、別の方法を模索していると、
ちょうどいい命令を見つけました。

EEPROM.put (番地, データ);  //保存
EEPROM.get (番地, データ); //読込

INT型であれば、-32768から32767まで扱うことができます。
これは2byte使うので0番地で保存した場合同時に1番地も使用しているとみた方がいいです。
他の変数も保存する場合はぶつからないように2番地から使うと行った配慮が必要になるかと思われます。
ためしに以下のようなプログラムを作って検証してみました。

#include <EEPROM.h>
int TestNum = 12345;

void setup() {
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
EEPROM.put(0,0);
EEPROM.put(1,0);
EEPROM.put(2,0);
EEPROM.put(3,0);
EEPROM.put(0,TestNum);
Serial.println(EEPROM.read(0));
Serial.println(EEPROM.read(1));
Serial.println(EEPROM.read(2));
Serial.println(EEPROM.read(3));
Serial.println(EEPROM.get(0,TestNum));
}


結果は以下の通り



0番地に57(2進数00111001)
1番地に48(2進数00110000)
連結すると0011000000111001 = 12345
計算は合ってます。
想像通り自動的に2byte使ってますね。
これは大変便利な機能ですが大きいプログラムを組むときは
アドレスマップを作らないと危険ですね。

では次の疑問に行きたいと思います。
intをlong(-2147483684から2147483647)の変数に変えた場合はどうなるのか。
上の経験を踏まえれば32bitなので4byte使われると思いますが。

ソースを以下のように変更して実行してみます。

#include <EEPROM.h>
long TestNum = 1234567890;

void setup() {
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
EEPROM.put(0,0);
EEPROM.put(1,0);
EEPROM.put(2,0);
EEPROM.put(3,0);
EEPROM.put(4,0);
EEPROM.put(5,0);
EEPROM.put(0,TestNum);
Serial.println(EEPROM.read(0));
Serial.println(EEPROM.read(1));
Serial.println(EEPROM.read(2));
Serial.println(EEPROM.read(3));
Serial.println(EEPROM.read(4));
Serial.println(EEPROM.read(5));
Serial.println(EEPROM.get(0,TestNum));
}


結果は想像したとおりになりました。



では事前に0番地から5番地に255を入れ込んでおき、
long TestNum = 123;
とした場合はどうなるのでしょうか。

#include <EEPROM.h>
long TestNum = 123;

void setup() {
Serial.begin( 9600 ); //シリアルモニタを使うときの儀式
EEPROM.put(0,255);
EEPROM.put(1,255);
EEPROM.put(2,255);
EEPROM.put(3,255);
EEPROM.put(4,255);
EEPROM.put(5,255);
EEPROM.put(0,TestNum);
Serial.println(EEPROM.read(0));
Serial.println(EEPROM.read(1));
Serial.println(EEPROM.read(2));
Serial.println(EEPROM.read(3));
Serial.println(EEPROM.read(4));
Serial.println(EEPROM.read(5));
Serial.println(EEPROM.get(0,TestNum));
}


値は1Byte、変数は4Byteのもの。
命令が変数のサイズで反応しているなら5番地と6番地は255のまま。
値のサイズで反応しているなら0番地以外全て255のままとなります。
結果は・・



つまり値ではなく、変数の種類で書き込み場所が決まるようです。
これはとても重要なことですよ。ぜひ頭に焼き付けて置くべき事柄です。
実験してよかった(*´ω`*)

あと問題があるとすればEEPROMの書き込み寿命ですかね。
今、F-RAMっていうのが登場しているようですが、
F-RAMはCypressのFM25W256を使うと、ほぼ制限を気にすることなく書き込みができるという記事があります。
https://www.switch-science.com/catalog/1406/
結構いい値段がしますね。庶民にはまだまだ手が出ないかなぁ。
ジャンプスターターのその後
2019.02.11


以前ジャンプスターターを検証した後、車内に放置していたのですが、
イザというときに自然放電していたら意味がないということで、
残量状況を確認してみました。
検証したのが9月の末ですので、まるまる4ヶ月は放置していたのですが、
驚きの100%。しかも極寒の車内から持ってきたのに100%
中身、パナソニックのバッテリーかな?
いや中国製品だし、一度満充電したらある程度減るまで100%に表示するとかいう
仕組みがあっても不思議ではないな(;´Д`)

- CafeNote -