2012-03-24
2012-03-20
■[AVR] 7セグ時計をATMEGA168Pに対応
前回まででは、ATMEGA168を使用していました。
ですが、今は「ATMEGA168P」のほうが主流な様で、秋月電子でも千石通商でもATMEGA168ではなくATMEGA168Pを取り扱っているようです。
予備として買ってあるのもATMEGA168Pなので、これに対応させておきたいです。
ソースコード
そんなわけで、以下のようにプログラムを修正してみました。
/*********************************** デジタル時計 V3.0 (2012/03/18 11:55) ATMEGA168P対応 割り込み周波数を1にして、 タイマ割り込み自体が1秒ごとに発生するように設定してみた。 ちょっとみたところうまくいっているようではあるが、 未だ厳密に調べてはいない。 参考: AVR タイマー実験 http://www9.plala.or.jp/fsson/NewHP_elc/AVR/Avr_Tmr0_OVF.html ユアネーム・7セグ・12セグフォント大全集 http://www.yourname.jp/soft/digitalfonts-20090306.shtml WinAVR付属のドキュメント C:/WinAVR-20100110/doc/avr-libc/avr-libc-user-manual/group__avr__interrupts.html ************************************/ #include <avr/io.h> #include <avr/interrupt.h> #define BTN_TH 20 #define F_CPU 1000000 /* CPUクロック周波数[Hz] */ #define PITCH 1 /* 割り込み周波数[Hz] */ #define PRESC 64 /* プリスケーラ値 (分周比) */ #define INTRCOUNT (0x10000 - ((F_CPU / PRESC) / PITCH)) unsigned int led_seg[4] = {0x00, 0x00, 0x00, 0x00}; unsigned char hour = 0, min = 0, sec = 0, cnt = 0, on_dp = 1; ISR (TIMER1_OVF_vect) { TCNT1 = INTRCOUNT; /* タイマ1の初期値設定 */ on_dp = !on_dp; sec++; if (sec > 59) { sec = 0; min++; if (min > 59) { min = 0; hour++; if (hour > 23) hour = 0; } } /* cnt++; if (cnt > 99) { cnt = 0; on_dp = !on_dp; sec++; if (sec > 59) { sec = 0; min++; if (min > 59) { min = 0; hour++; if (hour > 23) hour = 0; } } }*/ } void timer1_init(void) { TCCR1A = 0; /* TCCR1A タイマモード */ TCCR1B = 3; /* プリスケーラ (64分周) */ TIFR1 |= (1 << TOV1); /* オーバーフローフラグをクリア */ TIMSK1 |= (1 << TOIE1); /* オーバーフロー割り込み許可 */ } const unsigned int to_num_pattern[10] = { 0xfc, 0x60, 0xda, 0xf2, 0x66, 0xb6, 0xbe, 0xe0, 0xfe, 0xf6}; void scan(void) { static unsigned char line = 0; PORTD = led_seg[line]; DDRB = 1 << line; line = (line + 1) % 4; } void display(unsigned long num) { led_seg[3] = to_num_pattern[(num / 1000) % 10]; led_seg[2] = to_num_pattern[(num / 100) % 10]; if (on_dp) led_seg[2] |= 0x01; led_seg[1] = to_num_pattern[(num / 10) % 10]; led_seg[0] = to_num_pattern[num % 10]; } int main(){ unsigned char btn_cnt[4] = {0}; /* ポートのデータ方向(入力/出力)設定 */ DDRB = 0x00; DDRC = 0x00; DDRD = 0xff; /* ポートの初期値設定 */ PORTB = 0x00; PORTC = 0xff; PORTD = 0x00; timer1_init(); sec = 0; min = 0; hour = 0; cnt = 0; TCNT1 = INTRCOUNT; /* タイマ1の初期値設定 */ sei(); while (1) { scan(); /* M-- についてのチェック */ if ((PINC & 0x01) == 0) { if (btn_cnt[0] > BTN_TH) { btn_cnt[0] = 0; cnt = 0; sec = 0; min--; if (min == 255) min = 59; } else { btn_cnt[0]++; } } else { btn_cnt[0] = 0; } /* M++ についてのチェック */ if ((PINC & 0x02) == 0) { if (btn_cnt[1] > BTN_TH) { btn_cnt[1] = 0; cnt = 0; sec = 0; min++; if (min > 59) min = 0; } else { btn_cnt[1]++; } } else { btn_cnt[1] = 0; } /* H-- についてのチェック */ if ((PINC & 0x04) == 0) { if (btn_cnt[2] > BTN_TH) { btn_cnt[2] = 0; cnt = 0; sec = 0; hour--; if (hour == 255) hour = 23; } else { btn_cnt[2]++; } } else { btn_cnt[2] = 0; } /* H++ についてのチェック */ if ((PINC & 0x08) == 0) { if (btn_cnt[3] > BTN_TH) { btn_cnt[3] = 0; cnt = 0; sec = 0; hour++; if (hour > 23) hour = 0; } else { btn_cnt[3]++; } } else { btn_cnt[3] = 0; } display((unsigned long)(hour * 100 + min)); } }
33行目あたりが、
ISR (SIG_OVERFLOW1)
から、下のように変更しています。
ISR (TIMER1_OVF_vect)
これは、ATMEGA168Pでの書き方で、「SIG_OVERFLOW1」のままだと警告が出ます。
また、オーバーフロー割り込み自体を1秒ごとに発生させて、割り込み周期は1に設定しています(24〜28行目のdefine)。
もともと割り込み周期を100にしていたのは、ATMEGA168のタイマには8ビットのタイマがあるためでした。
今回は16ビットのタイマ(タイマ1)を使用しているので、タイマ/カウンタレジスタ(TCNT1)に1秒分の値を設定できるため、
このように変更しています。
(実は、以前からタイマ1を使用しているため、割り込み周期100の設定はあまり意味はありませんでした。)
所感
ATMEGA168を使用した時ほどの動作確認はしていないのですが、
少し時間の進みが早いような気がします。
といっても、2・3時間動かした限りでは、1分もずれたりはしていませんでした。
2012-03-18
■[AVR] AVRマイコンデジタル時計をユニバーサル基板へ実装
先日のブレッドボードデジタル時計をユニバーサル基板上に実装しました。
材料
前回から新たに増えた素材は以下のものです。
できあがり
比較のものを載せていないですが、大きさは
ユニバーサル基板が「Cタイプ(72x48mm)」で、電池の大きさを含めると、
72x55x14mm ほどです。
使い方
ボタンの使い方は下のようになっています。
- 右の赤色のボタン
| 上 | 1分増加 |
| 下 | 1分減らす |
- 左の青色のボタン
| 上 | 1時間増加 |
| 下 | 1時間減らす |
ソースコード
実は、7セグ表示器に間違ってカソードコモンの「OSL40562-LRA」をはんだ付けしてしまいました。
回路としてはどちらでも構わないので、プログラムを以下に示すものに変えました。
また、AVR Studioのプロジェクトファイルはこちらです。
/*********************************** デジタル時計 V1.1をカソードコモン対応 参考: AVR タイマー実験 http://www9.plala.or.jp/fsson/NewHP_elc/AVR/Avr_Tmr0_OVF.html ユアネーム・7セグ・12セグフォント大全集 http://www.yourname.jp/soft/digitalfonts-20090306.shtml ************************************/ #include <avr/io.h> #include <avr/interrupt.h> #define BTN_TH 20 #define F_CPU 1000000 /* CPUクロック周波数[Hz] */ #define PITCH 100 /* 割り込み周波数[Hz] */ #define PRESC 64 /* プリスケーラ値 (分周比) */ #define INTRCOUNT (0x10000 - ((F_CPU / PRESC) / PITCH)) /* アノードコモンの場合 unsigned int led_seg[4] = {0xff, 0xff, 0xff, 0xff}; */ unsigned int led_seg[4] = {0x00, 0x00, 0x00, 0x00}; unsigned char hour = 0, min = 0, sec = 0, cnt = 0, on_dp = 1; ISR (SIG_OVERFLOW1) { TCNT1 = INTRCOUNT; /* タイマ1の初期値設定 */ cnt++; if (cnt > 99) { cnt = 0; on_dp = !on_dp; sec++; if (sec > 59) { sec = 0; min++; if (min > 59) { min = 0; hour++; if (hour > 23) hour = 0; } } } } void timer1_init(void) { TCCR1A = 0; /* TCCR1A タイマモード */ TCCR1B = 3; /* プリスケーラ (64分周) */ TIFR1 |= (1 << TOV1); /* オーバーフローフラグをクリア */ TIMSK1 |= (1 << TOIE1); /* オーバーフロー割り込み許可 */ } /* アノードコモンの場合 const unsigned int to_num_pattern[10] = { 0xff - 0xfc, 0xff - 0x60, 0xff - 0xda, 0xff - 0xf2, 0xff - 0x66, 0xff - 0xb6, 0xff - 0xbe, 0xff - 0xe0, 0xff - 0xfe, 0xff - 0xf6}; */ const unsigned int to_num_pattern[10] = { 0xfc, 0x60, 0xda, 0xf2, 0x66, 0xb6, 0xbe, 0xe0, 0xfe, 0xf6}; void scan(void) { static unsigned char line = 0; PORTD = led_seg[line]; DDRB = 1 << line; line = (line + 1) % 4; } void display(unsigned long num) { led_seg[3] = to_num_pattern[(num / 1000) % 10]; led_seg[2] = to_num_pattern[(num / 100) % 10]; if (on_dp) { /* アノードコモンの場合 led_seg[2] &= 0xfe; */ led_seg[2] |= 0x01; } led_seg[1] = to_num_pattern[(num / 10) % 10]; led_seg[0] = to_num_pattern[num % 10]; } int main(){ unsigned char btn_cnt[4] = {0}; /* ポートのデータ方向(入力/出力)設定 */ DDRB = 0x00; DDRC = 0x00; DDRD = 0xff; /* ポートの初期値設定 */ /* アノードコモンの場合 PORTB = 0xff; */ PORTB = 0x00; PORTC = 0xff; PORTD = 0x00; timer1_init(); sec = 0; min = 0; hour = 0; cnt = 0; TCNT1 = INTRCOUNT; /* タイマ1の初期値設定 */ sei(); while (1) { scan(); /* M-- についてのチェック */ if ((PINC & 0x01) == 0) { if (btn_cnt[0] > BTN_TH) { btn_cnt[0] = 0; cnt = 0; sec = 0; min--; if (min == 255) min = 59; } else { btn_cnt[0]++; } } else { btn_cnt[0] = 0; } /* M++ についてのチェック */ if ((PINC & 0x02) == 0) { if (btn_cnt[1] > BTN_TH) { btn_cnt[1] = 0; cnt = 0; sec = 0; min++; if (min > 59) min = 0; } else { btn_cnt[1]++; } } else { btn_cnt[1] = 0; } /* H-- についてのチェック */ if ((PINC & 0x04) == 0) { if (btn_cnt[2] > BTN_TH) { btn_cnt[2] = 0; cnt = 0; sec = 0; hour--; if (hour == 255) hour = 23; } else { btn_cnt[2]++; } } else { btn_cnt[2] = 0; } /* H++ についてのチェック */ if ((PINC & 0x08) == 0) { if (btn_cnt[3] > BTN_TH) { btn_cnt[3] = 0; cnt = 0; sec = 0; hour++; if (hour > 23) hour = 0; } else { btn_cnt[3]++; } } else { btn_cnt[3] = 0; } display((unsigned long)(hour * 100 + min)); } }
所感
お昼12時から今(22:18)まで動かしてみた時点で、
2分ほど遅れています。
だいたい半日で2分遅れるので、1日で4分ほど遅れる様子です。
時計としては致命的かもしれません。
ですが、「所さんのDAITAI時計」とかをみると、
5分くらいは余裕を持って生活したほうが健康には良さそうです。
(いいわけですか・・・。)
今後やること
プラスチックのケースに収めたいです。
LEGOブロックでケースを作ろうかなと思っています。
追記
2012年3月31日
AVR Studioのプロジェクトファイルを追加しました。
2012-03-03
■[AVR]AVRマイコンと7セグメント表示器でデジタル時計
AVRマイコンでデジタル時計を作ってみました。使ってる技術要素としては
くらいで、入門で初めて作るものとしては最適かと。
開発環境
開発環境はこんな感じです。
| マイコン | ATMEGA168-20PU(リンク先はATMEGA168P-20PUですが) |
| ライタ | AVRISP mk? |
| 評価ボード | ATMEGAマイコンボード・キット(液晶付) |
ライタのAVRISPがISPで出力するので、それをマイコンへつなぐために、
今回は転がってたマイコンボードキットを使いました。
ソフトウェアの環境は以下のようになってます。
| コンパイラなどのセット | WINAVR-20100110 |
| 統合開発環境 | AVRStudio 4(Version 4.18) |
WINAVRは http://winavr.sourceforge.net/ からダウンロードしてきたものを、
AVRStudioはAVRISPに付属のCDのを使いました。
(これらは、特にどうでもいいですが。)
材料
材料はこんなところです。
| AVRマイコン | ATMEGA168-20PU | 1個 |
| 7セグメント表示器 | OSL40562-IRA | 1個 |
| 抵抗 | 10Ω | 4個 |
| タクトスイッチ | 青色と赤色 | それぞれ2個ずつ |
| 電池 | CR2032 | 1個 |
| ボタン電池基板取付用ホルダー | CR2032用(小型タイプ) | 1個 |
| ブレッドボード | EIC-108J | 1枚 |
| ジャンパワイヤ | - | 適量 |
7セグメント表示器は、今回はアノードコモンのものを使いました。
回路図
回路図の作成には、水魚堂ホームページで公開されているBSch3Vを使いました。
BSch3Vのプロジェクトファイルとかはこちらです。
なお、7セグメントLEDの各セグメントへ流れる際にそのセグメントのLEDをいくつ光らせるかで、
マイコンの出力ポートからの電流が2つや6つに分散します。
ですが、今回はトランジスタを用いた電流の増幅をしていないため、
- 1を表示するときは比較的明るく(セグメントで光らせるLEDが2つ)
- 8を表示するときは比較的暗い(セグメントで光らせるLEDが7つ)
なことになっています。
ソースコード
/*********************************** デジタル時計 Ver. 1.1 (2012/03/03 09:03) 参考: AVR タイマー実験 http://www9.plala.or.jp/fsson/NewHP_elc/AVR/Avr_Tmr0_OVF.html ユアネーム・7セグ・12セグフォント大全集 http://www.yourname.jp/soft/digitalfonts-20090306.shtml ************************************/ #include <avr/io.h> #include <avr/interrupt.h> #define BTN_TH 20 #define F_CPU 1000000 /* CPUクロック周波数[Hz] */ #define PITCH 100 /* 割り込み周波数[Hz] */ #define PRESC 64 /* プリスケーラ値 (分周比) */ #define INTRCOUNT (0x10000 - ((F_CPU / PRESC) / PITCH)) unsigned int led_seg[4] = {0xff, 0xff, 0xff, 0xff}; unsigned char hour = 0, min = 0, sec = 0, cnt = 0, on_dp = 1; ISR (SIG_OVERFLOW1) { TCNT1 = INTRCOUNT; /* タイマ1の初期値設定 */ cnt++; if (cnt > 99) { cnt = 0; on_dp = !on_dp; sec++; if (sec > 59) { sec = 0; min++; if (min > 59) { min = 0; hour++; if (hour > 23) hour = 0; } } } } void timer1_init(void) { TCCR1A = 0; /* TCCR1A タイマモード */ TCCR1B = 3; /* プリスケーラ (64分周) */ TIFR1 |= (1 << TOV1); /* オーバーフローフラグをクリア */ TIMSK1 |= (1 << TOIE1); /* オーバーフロー割り込み許可 */ } const unsigned int to_num_pattern[10] = { 0xff - 0xfc, 0xff - 0x60, 0xff - 0xda, 0xff - 0xf2, 0xff - 0x66, 0xff - 0xb6, 0xff - 0xbe, 0xff - 0xe0, 0xff - 0xfe, 0xff - 0xf6}; void scan(void) { static unsigned char line = 0; PORTD = led_seg[line]; DDRB = 1 << line; line = (line + 1) % 4; } void display(unsigned long num) { led_seg[3] = to_num_pattern[(num / 1000) % 10]; led_seg[2] = to_num_pattern[(num / 100) % 10]; if (on_dp) led_seg[2] &= 0xfe; led_seg[1] = to_num_pattern[(num / 10) % 10]; led_seg[0] = to_num_pattern[num % 10]; } int main(){ unsigned char btn_cnt[4] = {0}; /* ポートのデータ方向(入力/出力)設定 */ DDRB = 0x00; DDRC = 0x00; DDRD = 0xff; /* ポートの初期値設定 */ PORTB = 0xff; PORTC = 0xff; PORTD = 0x00; timer1_init(); sec = 0; min = 0; hour = 0; cnt = 0; TCNT1 = INTRCOUNT; /* タイマ1の初期値設定 */ sei(); while (1) { scan(); /* M-- についてのチェック */ if ((PINC & 0x01) == 0) { if (btn_cnt[0] > BTN_TH) { btn_cnt[0] = 0; cnt = 0; sec = 0; min--; if (min == 255) min = 59; } else { btn_cnt[0]++; } } else { btn_cnt[0] = 0; } /* M++ についてのチェック */ if ((PINC & 0x02) == 0) { if (btn_cnt[1] > BTN_TH) { btn_cnt[1] = 0; cnt = 0; sec = 0; min++; if (min > 59) min = 0; } else { btn_cnt[1]++; } } else { btn_cnt[1] = 0; } /* H-- についてのチェック */ if ((PINC & 0x04) == 0) { if (btn_cnt[2] > BTN_TH) { btn_cnt[2] = 0; cnt = 0; sec = 0; hour--; if (hour == 255) hour = 23; } else { btn_cnt[2]++; } } else { btn_cnt[2] = 0; } /* H++ についてのチェック */ if ((PINC & 0x08) == 0) { if (btn_cnt[3] > BTN_TH) { btn_cnt[3] = 0; cnt = 0; sec = 0; hour++; if (hour > 23) hour = 0; } else { btn_cnt[3]++; } } else { btn_cnt[3] = 0; } display((unsigned long)(hour * 100 + min)); } }
7セグメントLEDがアノードコモンタイプなので、to_num_pattern配列では
0xffから目的のパターンの差分をとっています。
完成写真
こんな感じで出来上がりました。
左から2つ目のセグメントのDPを時表示と分表示の区切りに使っています。
そして、このDPで1秒おきに明滅を繰り返すことで秒を表現しています。
(あと、「時計らしさ」も表現しています。)
また、回路図作成の際に問題点としていた
各セグメントで光らせるLEDの数に応じた明るさの変化については
実際に見てみるとそれほど気になるものではありませんでした。
また、時刻のずれについては、
電池の出力電圧が2.9Vほどへ落ちた時点でも
2時間ほど使用し続けた際に1分のずれも起こりませんでした。
(まだまだ、実際に使ってみないと分からないですが。)
今後の方針
あとは、ブレッドボード上に実装したものを、
ユニバーサル基盤上に実装します。
参考文献
- 作者: 松原拓也
- 出版社/メーカー: 電波新聞社
- 発売日: 2007/02/01
- メディア: 単行本
- クリック: 61回
- この商品を含むブログ (4件) を見る
追記
2012年3月31日
BSch3V用ファイルを追加しました。
2011-12-11
■[emacs]yasnippetの「yas/snippet-dirs」に関して
(yas/initialize)
の実行時に、
[yas] Check your `yas/snippet-dirs': ~/.emacs.d/snippets is not a directory
なんてエラーが出る場合の対処法。
対処法
snippetsのディレクトリの場所が~/.emacs.d/直下ではない場合に出るので、
(setq yas/snippet-dirs "~/.emacs.d/plugins/yasnippet/snippets")
を(yas/initialize)の前に評価するようにするだけ。
■[emacs]yasnippetのインストール
ついでに、インストールについてもメモしておこう。
~/.emacs.d/plugins/
以下にインストールするやり方で書いていますので、
適宜読み替えてください。
取得
https://github.com/capitaomorte/yasnippet
こちらのページから持ってくる。
git cloneの場合
$ cd ~/.emacs.d/plugins/
$ git clone https://github.com/capitaomorte/yasnippet.git
ZIPダウンロードの場合
それを「~/.emacs.d/plugins/」へ配置。
その後、
$ cd ~/.emacs.d/plugins/
$ unzip capitaomorte-yasnippet-ec9a75a.zip
$ rm capitaomorte-yasnippet-ec9a75a.zip
$ mv capitaomorte-yasnippet-ec9a75a yasnippet
「ec9a75a」のあたりは変化するかと思うので注意を。
.emacsに設定
;; yasnippet (add-to-list 'load-path "~/.emacs.d/plugins/yasnippet") (require 'yasnippet) (setq yas/snippet-dirs "~/.emacs.d/plugins/yasnippet/snippets") (yas/initialize)
のように設定を追加して完了。





