2015年1月27日火曜日

MSP430/Energia+RTC+秋月巨大LED=デジタル時計

暗いとアラが目立たない

■またデジタル時計だ■

お前は他にやることがないのか、と言われるのは承知で作ります。

前回はダイナミック点灯させたところで力尽きたので、今回はちゃんと時計として動くようにします。TIの低消費電力MCU MSP430G2シリーズの2553、開発環境は同LauchPad + Energiaを使います。2553/LauchPadは日本だとちょっと手に入れにくいですが、RS onlineなどでLaunch Padとして$9.99で売ってます。せっかくなので、チップ単体のMSP430G2553も何個かまとめて買っておくと良いと思います(1個300円ぐらい)。LPC1114FN28と比べるとメモリなどごく限られるのですが、うそみたいに電池が長持ちしますし、かなりフットプリントが小さいので。

表示器は秋月の「超大型4桁7セグLED表示キット」です。低消費電力チップと組み合わせるのもどうかと思いますが。本来、秋月の時計キット用なのですが、巨大なLEDが今どきこの値段(1500円)で手に入るのは珍しいと思います。まぁaitendoで1280円で売ってたけどね…。注意しなければいけないのは、使っているLEDは16セグメントなのですが、秋月のキットでは7セグメントとして使ってます。aitendoのは16セグメント全部使っているけどね…でも16セグメント使いたくてもピンが足りないのよだからこれでいいのよ文句ないのよ(何その未練)

あとはRTCですが、今回は2553内蔵の発振回路 / カウンターを使います。キットには32768hzの水晶発振子が同梱されてます。小さくてLaunchPad基板へのハンダ付け大変ですけども。

温度計も2553内蔵の温度センサを使います。analogReadで華氏☓10の数値が直接返ってくるので便利です。まぁチップが多少なりともあたたまるので正確ではないけども(電源を入れた直後は他の室温計と一致していた)。

■先に落とし穴を書いておく■

内蔵温度センサも同じA/Dコンバータ使ってます。なので、Reference電圧を正しく設定しないと値がズレます。これで30分つぶしましたよ…。

■ハード編:巨大LED■

追記:
キットに付いてくるコロン用のLEDはクリアタイプで、方向によって明るさがかなり異なります(というか正面が眩しい)。「LED光拡散キャップ(5mm) 赤 (50個入)」を被せるか、広角タイプのLEDを用いると良くなると思います。

ほぼキットの説明書通り組み立てるだけなのですが、キットに付属しているコネクタはL型で表示器と平行に下側に伸びるように取り付けます。私は下面をフラットにしたかったので、手持ちの20ピンヘッダを取り付けました。あと、ハンダ付けの注意ですが…

  1. 金メッキ基板にはフラックスを(昔やった
  2. くれぐれもLEDの向きを間違えない(昔やった
  3. くれぐれも水平垂直平行を(昔やった
  4. ハンダ付けは必要十分に(昔(ry
基板と部品はまず最初に必ず仮組みをします。はんだ付けをしないで並べてみて、ぶつからないかちゃんとピンが全部ホールに入るかどうか、などを確認します。

1)のフラックスというのはピンや基板などをハンダで濡れ(?)やすくする処理剤です。小瓶で数百円、まぁ10年ぐらい使えます。キャップをあけると小さな刷毛がついてますので、それでハンダ付けする部分(ランドと言います)に少量塗ってやります。

2)はピンが多い部品を外すのはかなり困難です。確認確認また確認しましょう。

3)はピンを通す穴は案外大きいです。なので、雑に取り付けると微妙に部品が傾いてしまいます。傾く方向は左右だけでなく、基板と部品が平行になっているかも注意しなくてはいけません。なので、最初に1本をハンダ付けしたら、平行になっているかどうかをしっかり確認し、ずれていたらもう一度コテをあてて修正します。これで平行になっていることを確認したら、対角線上にあるピンをハンダ付けして再度確認します。これでOKならば、一応、隣のLEDも支障なくピンに入ることを確認してから、最初のLEDの残りのピンをハンダ付けします。

4)はリズムを身体で覚えるしかないっす。ただ、温度調整機能のついたハンダゴテを使っている分には最近の部品は滅多に壊れないです。それよりも、溶かしこむ時間が短すぎて接触不良になるケースの方が遥かに多いと思います。コテをあててピンを温める、コテというよりピンにハンダを当てて溶かす、ピンに自然にハンダが流れ込んだところでハンダとコテを同時に離す…ここまでまぁ3〜5秒くらい、10秒くらいかかってしまってもOKです。

今回の配線はソースに書いてありますが、こんな感じで。

信号名LEDボードのピン2553のピン番号同ピン名
Anode1桁目43P1_1
Anode2桁目22P1_0
Anode3桁目15P1_3
Anode4桁目56P1_4
Seg A177P1_5
Seg B188P2_0
Seg C129P2_1
Seg D1410P2_2
Seg E1311P2_3
Seg F1612P2_4
Seg G1513P2_5
dp<未使用>
コロン(+)204P1_2
コロン(-)18(GND)
水晶-19XIN
水晶-18XOUT

水晶は、Launch Padにこんなのが付属しています。


袋およびパッケージから取り出すとこんなに小さい。


2553のXIN-XOUTは右側のピンヘッダにもつながっているのですが、水晶発振子はピンから離れると動作が不安定になるので、なるべく近くに取り付ける必要があります。下の写真では中央の2553の右ちょっと上の方にある縦に2つ穴の空いたところがソレです。



貧乏症のワタシはこういう開発キットのようなものに手をいれるのはイヤなのですが、ハンダ付けします。ただ、こんなに小さいものなので、まずテープで止めて位置をルーペで見ながら位置を微調整してからハンダ付け。

これはハンダ付け前

ああしんど。

その他LaunchPadとLEDボード間は、メス−オスのジャンプワイヤで一本ずつ配線していきます。

■ソフト編■

MSP430/Energia用のRTCライブラリを使います。ライブラリ、ありがとうございます。なお、同ライブラリのライセンスはGPLです。
リンク先フランス語ですが、英語に切り替えられます。リンクからダウンロードすると、他にも面白そうなライブラリが並ぶのですが、今回はsRTCだけを使います。sRTCフォルダをEnergiaのディレクトリ下にあるLibraryに入れます。

使い方は簡単で、ヘッダをインクルードし、setup()で.begin()を呼び出し、秒単位の割り込みルーチンで秒をインクリメントします。あとは時分秒を取ってくるだけ。2秒間時計〜1秒間温度表示、を繰り返します。



あとはもういつものダイナミック点灯方式のよくあるデジタル時計ルーチンです。

以下ソースを公開しますが、ライセンスは一応GPLで。難しいことはしていませんが、ご質問いただければ喜んでお答えします。この後は、これに赤外線リモコン受光器か時刻合わせスイッチを取り付けてから、久々にプリント基板にしてみようと思います。

// Digital Clock : by Akizuki's Large LED and Energia/MSP430G2553
// 秋月巨大液晶利用のデジタル時計/温度計
//
// 2015-01-27 : リリース
// 2015-01-31 : コロンの明るさをPWMで調整
#include <sRTC.h>
#include <legacymsp430.h>
RealTimeClock myRTC;
//秋月巨大LEDピン
//Anode(+) Common
//1桁 - 4 : P1_1
//2桁 - 2 : P1_0
//3桁 - 1 : P1_3
//4桁 - 5 : P1_4
//
//a 17 : P1_5
//b 18 : P2_0
//c 12 : P2_1
//d 14 : P2_2
//e 13 : P2_3
//f 16 : P2_4
//g 15 : P2_5
//
//dp <NC>
//col 20 : P1_2(4, TXD)
//
//Tempe : TEMPSENSOR
//SDA : SDA 15 = P1_6
//SCL : SCL 14 = P1_7
//XIN / XOUT
// LEDのポート
int segA = P1_5;
int segB = P2_0;
int segC = P2_1;
int segD = P2_2;
int segE = P2_3;
int segF = P2_4;
int segG = P2_5;
int segCol = P1_2;
int digits[] = {P1_1, P1_0, P1_3, P1_4};
#define ALL_OFF 10
// LED PWM
int brightness;
// sprintf用バッファ
char spfBuf[10];
void setup()
{
// rtc init
myRTC.begin();
// 温度計ポートの基準電圧は1.5V
analogReference(INTERNAL1V5);
// led
pinMode(segA, OUTPUT);
digitalWrite(segA, HIGH);
pinMode(segB, OUTPUT);
digitalWrite(segB, HIGH);
pinMode(segC, OUTPUT);
digitalWrite(segC, HIGH);
pinMode(segD, OUTPUT);
digitalWrite(segD, HIGH);
pinMode(segE, OUTPUT);
digitalWrite(segE, HIGH);
pinMode(segF, OUTPUT);
digitalWrite(segF, HIGH);
pinMode(segG, OUTPUT);
digitalWrite(segG, HIGH);
analogWrite(segCol, 0);
for (int i = 0; i < 4; i++) {
pinMode(digits[i], OUTPUT);
digitalWrite(digits[i], LOW);
}
setDigit(0);
for (int i = 0; i < 8; i++) {
spfBuf[i] = i + '0';
}
spfBuf[8] = 0;
myRTC.RTC_hr = 05;
myRTC.RTC_min = 17;
myRTC.RTC_sec = 50;
}
void setDigit(int d) {
for (int i = 0; i < 4; i++) {
if (d == i)
digitalWrite(digits[i], HIGH);
else
digitalWrite(digits[i], LOW);
}
}
int digit = 0;
int loopCount = 0;
int prevSec = 99;
void loop()
{
// まず全セグメント、全桁をoff
// out(ALL_OFF, HIGH); // これがなくても残像でなかった
setDigit(-1);
// 1秒ごとに時刻&温度文字列バッファを更新
loopCount++;
if (prevSec != myRTC.RTC_sec) {
loopCount = 0;
prevSec = myRTC.RTC_sec;
createTimeAndTempString();
}
// 表示処理
// 2:1秒ごとに時計と温度を切り替える
boolean isClock = (myRTC.RTC_sec % 3) != 0;
int offset = isClock ? 0 : 4;
// 対象となる桁の値を0-9か空白に変換しセグメントビットに出力
int num = spfBuf[digit+offset];
num = (num != ' ') ? num - '0' : ALL_OFF;
out(num, isClock ? HIGH : LOW);
// 桁ビットを出力(点灯=high)
setDigit(digit);
// 少し維持
delayMicroseconds(4000);
//次の桁へ
digit++;
if (digit >= 4) digit = 0;
}
// 時計と温度文字列を作る
void createTimeAndTempString(){
char b1[10], b2[10];
// 時計
int hhmm = 10000 + myRTC.RTC_hr*100 + myRTC.RTC_min;
sprintf((char *)b1, "%d", hhmm);
// 温度計
float f = analogRead(TEMPSENSOR);
int t = (5.0 / 9.0) * (f/10.0 - 32.0) * 10;
int tttt = t < 0 ? 19999 : t + 10000;
sprintf((char *)b2, "%d", tttt);
for (int i = 1; i < 4 && b2[i] == '0'; i++) {
b2[i] = ' ';
}
// 一つの文字列に
sprintf(spfBuf, "%s%s", b1+1, b2+1);
}
int patA[] = {
HIGH, LOW, HIGH, HIGH, LOW, HIGH, HIGH, HIGH, HIGH, HIGH, LOW};
int patB[] = {
HIGH, HIGH, HIGH, HIGH, HIGH, LOW, LOW, HIGH, HIGH, HIGH, LOW};
int patC[] = {
HIGH, HIGH, LOW, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, LOW};
int patD[] = {
HIGH, LOW, HIGH, HIGH, LOW, HIGH, HIGH, LOW, HIGH, HIGH, LOW};
int patE[] = {
HIGH, LOW, HIGH, LOW, LOW, LOW, HIGH, LOW, HIGH, LOW, LOW};
int patF[] = {
HIGH, LOW, LOW, LOW, HIGH, HIGH, HIGH, LOW, HIGH, HIGH, LOW};
int patG[] = {
LOW, LOW, HIGH, HIGH, HIGH, HIGH, HIGH, LOW, HIGH, HIGH, LOW};
int sevenPort[] = {
segA, segB, segC, segD, segE, segF, segG};
int *sevenSeg[] = {
patA, patB, patC, patD, patE, patF, patG};
void out(int num, int DP) {
for (int i = 0; i < 7; i++) {
digitalWrite(sevenPort[i], sevenSeg[i][num] == HIGH ? LOW : HIGH);
}
analogWrite(segCol, DP == LOW ? 0 : 24);
}
interrupt(TIMER1_A0_VECTOR) Tic_Tac(void) {
myRTC++; // Update secondes
};
view raw gistfile1.cpp hosted with ❤ by GitHub

■使ってみて■

いやー、デカい。存在感ありすぎます。3.3vでのダイナミック駆動なのをいいことに電流制限抵抗もドライバも入れてないので少し表示が暗いのですが、ちょっと昭和の薫りがしていい感じw 昼間でも見えますし夜電灯を消すと睡眠の邪魔にならない程度の明るさで動いてくれています。何より、乱視+近視でも目を凝らさず見えるのが良いです。

ピンがあと2本空いているので照度センサと時刻合わせスイッチ(または赤外線受光器)は取り付けられるか…。プリント基板が遠のくなぁ。

■更新■

  • 2015-01-31
    コロンの出力をPWMに変更。ほどよい明るさになったけど点滅が見えてウザい

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。