フォト
無料ブログはココログ

« 採用基準 | トップページ | USB DAC (2) »

2013年1月11日 (金)

CharlieplexingでBinary Clock

 Charlieplexingの話(2012/12/28)でマイコンの3つのポートで12個のLEDを駆動する方法について書いたので、Chipiplexing 方式を使ったバイナリクロックを作ってみた。
バイナリクロックは時刻を2進数で表示する時計で、例えば10:39分は

 
 

のように表示する。

 LEDで表示しようとすると12個のLEDが必要である。これをATTiny45でCharlieplexで3つのI/Oを使って実現しようというもの。

 回路図はこんな感じ
Binclock

 最初にAVRでI/Oポートを3つ持つ品種AT90S2323を使おうと考えたが、AT90Sシリーズのデータシートを見るとソース(電流吐出し)とシンク(電流吸込み)のドライブ能力が対象でない。と いうことでATTiny45を使うことにした。

 ATTiny45はRESET#、XTAL1,XTAL2をI/Oポートとして使用できるのだが、 RESET#はドライブ能力がかなり低いのでピークで10数mAの出力が必要な今回のような用途には使えないし、外部X'talを使うのでXTAL1、XTAL2も使えないということでI/O ポートは3つである
(水晶発振器出力を#2PIN (CLKI)に入れて#3PIN(XTAL2)をI/Oに使えば、い~じゃん。というツッコミは無しである!)

 R1、R2、R3にマイコンの出力ポートの内部抵抗が使えないかと思い、ATTiny45の内部抵抗をデータシートで確認すると30数Ωくらい だったので、R4=R6=510Ω、R5=5.6kΩで作ったら、P0=H、P1=LにしたときD1、D8、D9が点灯してしまう。

 SPICEで確認してみたら
Binclk_spice
SPICEさんはD1点灯(緑)のときD3(青)には200uA流れるとおっしゃる。200uAでは明々とは点灯しないような気がするのだけれど...

 D1、D8、D9に関係する部分だけ抜き出してVcc=3.3V、D1,D8,D9のVf=2V、Q1,Q2のVbe=0.7として計算した各ポイントの電圧は↓のとおりである。
Binclock_d1d8d9
 D1のアノード電圧=2.65V、Q2のエミッタ電圧=0.95Vだから、D9のA-K間の電圧=1.7Vである。これはD9のVfより小さいのでD9は点灯しないはずだ。(D8も同様)

 2日くらい考えたところ、
昔のLEDは10m以上流さないと点灯しなかったが、最近のLEDは高輝度と謳っていなくても1mAも流すと点灯する。物によっては0.1mAでも点灯する。

 !よく考えたら、Vf=2VはおおよそIf=20mAの場合のVfである。(D1のA-K間は2V)
ところがD8,D9に少ない電流が流れているとすると当然Vfも小さい値になる。
普通の赤色LEDで電流が流れ始める電圧は1.7V付近だから点灯するくらいの電流が流れているのではないか。

 D8、D9が完全にOFFになるようにQ1のエミッタ電圧を下げ、Q2のエミッタ電圧を上げることにした。

 アルカリ電池×2を使った場合を考えてVcc=3.4V、D8、D9が完全にOFFになる電圧を1.5Vとして計算すると、R4=R5=1.2kにしたときに1.49VになるのでD8,D9は完全に消えるだろう。

 出力ポートの内部抵抗をあてにすると、I/Oピン入出力電流の絶対最大規格40mAに引っ掛かる恐れがあるのだけれど、R1、R2を大きくするとD8、D9を単独で点灯したときの電流が減るので痛し痒しだ。

 D1~D8を点灯した時とD9~D12を点灯したときの電流の差があるのでLEDによって明暗の差が出てしまう。明らかに分かるようならPWMで補正しよう。

 LED表示のソースはこんな感じ

/* bit ON/OFF */
#define ON 1 #define OFF 0
/*
* DDRB PORTD
* xxxx xxxxx : 出力データ
*/ #define DDR(d) ((d)<<4) #define PD(d) ((d)&0x0F)
/* ポートの状態 */ #define HI PD(ON)|DDR(ON) #define LOW PD(ON)|DDR(OFF) #define HIZ PD(OFF)|DDR(OFF)
/* PB0,PB1,PB2の状態
*  xxxx xxxx
*   ||| |||+--- PORTB(0)
*   ||| ||+---- PORTB(1)
*   ||| |+----- PORTB(2)
*   ||+-------- DDRB(0)
*   |+--------- DDRB(1)
*   +---------- DDRB(2)
*/ #define P0H HI #define P0L LOW #define P0Z HIZ #define P1H (HI<<1) #define P1L (LOW<<1) #define P1Z (HIZ<<1) #define P2H (HI<<2) #define P2L (LOW<<2) #define P2Z (HIZ<<2)
/**/ #define MIN1_1 P2Z | P1Z | P0L #define MIN1_2 P2Z | P1Z | P0H #define MIN1_4 P2Z | P1H | P0L #define MIN1_8 P2Z | P1L | P0H #define MIN10_1 P2Z | P1L | P0Z #define MIN10_2 P2Z | P1H | P0Z #define MIN10_4 P2H | P1L | P0Z #define HOUR1_1 P2L | P1Z | P0Z #define HOUR1_2 P2H | P1Z | P0Z #define HOUR1_4 P2L | P1Z | P0H #define HOUR1_8 P2H | P1Z | P0L #define HOUR10_1 P2L | P1H | P0Z
#define ALL_OFF   0x00 #define LED_OFF() PORTB = DDRB = 0x00 #define LED_ON(d) do { register uint8_t pd, dd; \ dd = pd = (d); \ dd &= 0x0F; \ pd >>= 4; \ } while(0) /* output data */ const uint8_t port_data[] = { ALL_OFF,
    MIN1_1,  MIN1_2,  MIN1_4,  MIN1_8, MIN10_1, MIN10_2, MIN10_4, HOUR1_1, HOUR1_2, HOUR1_4, HOUR1_8, HOUR10_1 }; /* display LED
* 指定したLEDを時刻に応じてON/OFFする
* void disp(uint8_t pos) *  arg : uint8_t pos : LED number *        uint8_t data : 0: off, 1: on *  ret : void
*  global: uint8_t min, hour : 時分BCD
*/ void disp(uint8_t pos, uint8_t data) uint8_t  p; /* all off */ LED_OFF(); if (pos < 4) { /* 分 */
if (BCD_L(min) & _BV(pos))
p = port_data[pos+1];
} else
if (pos < 7) { /* 10分 */
if (BCD_H(min) & _BV(pos-4))
p = port_data[pos+1];
} else
if (pos < 11) { /* 時 */
if (BCD_L(hour) & _BV(pos-7))
p = port_data[pos+1];
} else
if (pos < 12) { /* 10時 */
if (BCD_H(hour) & _BV(pos-11))
p = port_data[pos+1];
}
LED_ON(p);
}

ポート(PB(x))をHにするときはDDR(x)←1、PORTB(x)←1
ポート(PB(x))をLにするときはDDR(x)←1、PORTB(x)←0
ポート(PB(x))をHizにするときはDDR(x)←0、PORTB(x)←0
このデータをLED0-LED11毎にテーブルで用意しておいて、表示するLED番号で出力データを検索して、上位4bitをDDRBに、下位4bitをPORTBに出力する。

 そうしているうちに完成
Binaryclock Binalyclock2
↑11:54を表示
後ろは学研のArduino、もっぱらISPに使っている。

動いているところ
Binaryclock20267 ←リンク先にAVI

 リセット時には、全てのLEDが右下から順に点灯し、11:40からカウントが始まる。
時分しか表示できないので撮影用に1秒毎にカウントアップするようにしている。

 まあ動いたから良しとしよう。
 I/Oポートがもう一個あって4個あるなら普通にCharlieplexingで駆動するのが簡単だと思う。

 本当に時計として使うならC3をトリマに変えて発振周波数を合わせる必要がある。
 無調整時の周波数精度10-4とすると、1日で 最大3600×24×10-4=±8.64[sec] くらいずれる。10-5まで追い込むと日差1秒以内に収まりそうだけれど、追い込みに時間がかかるので、TCXOを使うのが現実的だろう。
 月差1秒を実現するには1/(3600×24×30)=3.86-7 なのでオーブンを使わないと難しいのかな。

« 採用基準 | トップページ | USB DAC (2) »

AVR」カテゴリの記事

日記・コラム・つぶやき」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/577514/56404530

この記事へのトラックバック一覧です: CharlieplexingでBinary Clock:

« 採用基準 | トップページ | USB DAC (2) »