コメントがあればこちらから
sorry. japanese only.

[YES!AVR!へ戻る]

AVR(ATMega328P)でarduino開発環境が使える物を作ってみた

Cirunoとそれっぽい名前を付けてみた

●概要 秋月のH8Sジャンクも取り扱いがなくなったので,この機会に新しいプラットフォーム をと,ゆるゆる考えていたが,規模・性能や供給面でATmega328Pベースで行く事にした。 作るおもちゃの規模的な住み分けとして,小物はATtiny2313で,中規模はATMega328Pで, 大規模なものは,在庫のH8Sジャンクにしようと思っている。
参考資料:【Touhou】Cirno & Beer/チルノとビール【東方】
いろいろ試行することも多いし,取り合えずということで,お手軽にarduinoIDEを 使って開発できるようにしただけのインチキ*rduinoをデッチあげることにした。 それらしいネーミングをするのが流行ってるように見えたので,それに倣って上の 動画を見ながら決めた。 最小限機能のステキさから,名前は東方projectの氷の妖精チルノからチルーノと なんとなくサポート役っぽいイメージとコネクタ類のせいで,妙に基板サイズが ふとましくなってしまったことから冬の忘れ物(雪女)のレティと呼ぶことに。 チルーノの傍には,その友達の大ちゃんをということで,シールドにもそれらしい ネーミングをしてみた。 MPUボードはCiruno(チルーノ),書込みサポートボードはLetty(レティ),I/O拡張 ボードは,Dyield(大ールド)と呼ぶことにする。 さーて,コレを使ってなにができるかな。なにができるかな。 ●開発環境とライブラリ 参考までに開発時のarduino環境とブートローダーは以下の通り arduinoIDE(0021or0022) [ URL ] USBaspLoader(2010-07-27版) [ URL ] 追加したライブラリ Timer1,Timer3: [ URL ] → $(arduino)/libraries/TimerOne/ → $(arduino)/libraries/Timer3/ Timer2: [ URL ] → $(arduino)/libraries/MsTimer2/ FatFs: [ URL ] → $(arduino)/libraries/FatFS/
まずは,MPUボードと書き込みサポートボードから... 一応簡単に概略仕様として,この期に及んでRS232Cのようなレガシーシリアルを メインにはさすがに躊躇れるので,USBaspLoaderによってUSB経由でブートロード することにした。 ただし,やはりレガシーはあればあったで使いやすいのでサポートボードにレベル コンバータを乗せて使えるようにはすることにした。 MPUボードは,最低限(ATmegaとXtalとブートロード選択ジャンパのみ)のシンプル さ生かしながら,ブレッドボードでも扱いがしやすいように40ピンDIP程度のサイズ とした。 書き込みや通信接続用のUSB/レガシーシリアルのコネクタとかも邪魔になるんで, それらは分離可能として,書込みサポートボードに配して分離が行える様にした。 ●製作物1(チルーノとレティ) 最初の一枚目は,CirunoとLettyを一体化して設計製造をした。切り離しさえすれば 最小機能を切り出せるコンセプトどおりにはしてある。 USBaspLoaderを使う関係から,クリスタルを実装。H8Sジャンクから引っこ抜いた 20MHzの発振子が幾つか転がっていたので,コレを使うことにした。 端子列は,それぞれを片一方だけ使うようなことも想定して,制御/通信/書込み用の 8ピンと入出力用の20ピンとで分けている。サイズ的には40pinDIPソケットにちょうど 刺さるように作成した。 一発目の製作なのに動作確認用の最低限のLEDすらも省いてしまった上に,分離した 後のLettyボードとの接続で片持ちの接続で少々不安定な格好となる問題作であった。 arduinoIDEでの動作を確認するため後付けでLEDをCirunoボードに,固定用のウレタン フォームをLettyボードにそれぞれぞんざいながら追加したw
CirunoボードとLettyザマザーボード(部品面からとハンダ面から)
http://www.youtube.com/watch?v=dJbCCWoWW6M
スケッチ例:LED(PC5=digital#19に接続)ピカピカスケッチ(あたいったらザ・シンプレストね!)
/* LED BLINKING at PC5 - Sample sketch *//* for Ciruno Board */ #include <avr/io.h> #define PC5 (19) #define nLED1 (PC5) #define val_ms_wait (500UL) #define led_off (HIGH) #define led_on (LOW) static uint8_t led1; void setup() { pinMode(nLED1, OUTPUT); led1= led_off; } void loop() { if(led_on==led1) led1=led_off; else led1=led_on; digitalWrite(nLED1, led1); delay(val_ms_wait); }
まあそんなこんなで,スケッチを起こしてアップロード&動作を確認できたので, Cirunoボードを5枚ほど作っておいた。 チルーノボードは,ボードサイズが50x25mmで,75x100の基板から6枚切り出せるの だが,今回は一緒にレガシーシリアルを省いて25x25mmの基板に実装したシュリンク 版のLettyボード(画像にあるちっこい2個の基板がソレ)を混ぜて作った関係で5枚と なった。 感光基板が@\600で,ATmega328Pが秋月でレール買いで@\230,クリスタルを@\50で ピンヘッダは同等品で調達すると秋月の長めのヤツ1x40Pの@\50にICソケット@\10か。 材料コスト的には1チルーノあたり大体\500くらいになる勘定だ。 機能は比べるべくもないがH8Sジャンク(その1)と同程度のコストには収まった。
量産Cirunoとシュリンク版Lettyボード(部品面からとハンダ面から)
量産Cirunoとシュリンク版Lettyボード接続の様子
scematic graph of the Ciruno チルーノボードの回路図
board layout of the Ciruno チルーノボードのレイアウト(ハンダ面から/画像は50x50mmの範囲)
scematic graph of the Letty レティボードの回路図
scematic graph of the petit-Letty シュリンク版レティボードの回路図
●製作物2(7セグLED表示器大ールド:プロトタイプ) 光物シリーズ第0弾!!4桁の7セグLED表示拡張ボード(Attiny2313兼用)プロトタイプ ネーミングは,↑同様に東方Projectの東方紅魔郷の大妖精(通称大ちゃん)にかけて。 最初はシールドっぽくSyieldという名称を考えていたが,MPUボードをチルーノにした ので,それにあわせて呼称を大ールド(Dyield board)とした。 プロトタイプとしたのは,ユニバーサルボードで作ったためで特に意味はない。
7セグLED表示器大ールド(7segLEDとチルーノを搭載した状態(左)とバラした状態(右))
arduinoのタイマ割り込みを調べるのに作ったもので,Timer1ライブラリを使用して 一定周期の割り込みを行い,計時や周波数発生を行う実験に使った大ールドだ。 材料は,手持ちにあったもので,チルーノを含めて費用は大体\1000くらいかな? それにしてもライブラリ経由だと,タイマーのインターバル等の設定が自由にでき なくて歯がゆいw その上,OCRA,OCRBを使って,位相のあるタイミングを作り出す ことができるのかできないのかすらもよくわからんw まあ,いざとなれば制御レジスタを直接叩いても構わんのだが,割り込みベクタを 乗っ取る方法がわからないと価値が半減なので,このあたりは調査中といった按配。
scematic graph of 4digits 7segment LED dyield 7セグLED大ールド(プロトタイプ)の回路図
後述のスケッチを見ればわかるとおり,カレンダー無しで単純に時刻を刻むだけの 簡単なものを実装してみた。SW1を押すごとにhh.mm表示→mm.ss表示→ss.nn(1/100秒) 表示を切り替えて表示する簡易時計である。SW1を押したままSW2を押すことで時分 の設定ができる。(秒は分の設定で0秒にリセットされる)
http://www.youtube.com/watch?v=LAg5mAB6QN0
まったく狂いのない時計。正確に地球上のある地点の時刻を表示する機能を持つ。 どこの地点の時刻かは,そのときの表示時刻によって決まる。 以下のJJYモドキ時報を発する機能を追加した。 毎秒開始5ms間は,約1600Hzの秒時報(コツ,コツという風に聞こえる)。 毎分49秒から,約1000Hzキャリアのモールスコードで時分予告を(hhmm△hhmm△と 二回)打鍵。 毎分59秒には,約600Hzの分繰り上がり予告音(約0.6秒間ミーと鳴く)。 毎9分40秒あたりから,約1000Hzキャリアのモールスでコールサインを(JJY△JJY△ と二回)打鍵。 モールスコードは,トン/ツーがそれぞれ45/135msと若干速目になっている。毎分60 文字程度というか制御の都合で秒1文字固定なのである。 なお,残念ながら音声アナウンスと10分前半のピー音は無い。後者は煩いからねw 出力はPD0(digital0)で圧電スピーカーなどをつなぐと音が出るようにした。 慣れると音で知らせる時計も意外に便利だわ。 いきなり始まる時分の打鍵聞き漏らしても,二度打鍵するようにしたので,何とか 判別できるようになったと思う。 これで時計を見なくとも時刻がわかる,腹時計のようなものに一歩近づいたわけだw
スケッチ例:表示したり時報をするだけの簡単なお仕事です?(あたいの腹時計ったら正確ね!)
/* four digits 7segmented LED display Dyield demonstration sketch *//* for Ciruno Board *//* w/4Digs7segLED dyield board. */ #include <TimerOne.h> #include <avr/io.h> #include <avr/pgmspace.h> #define bsht(x,a,b,c,d,e,f,g,h) ~(((((x)>>7)&1)<<(a))|((((x)>>6)&1)<<(b))|((((x)>>5)&1)<<(c))|((((x)>>4)&1)<<(d))|((((x)>>3)&1)<<(e))|((((x)>>2)&1)<<(f))|((((x)>>1)&1)<<(g))|((((x)>>0)&1)<<(h))) #define isMCU_2313 (0) #if (isMCU_2313) # define fontform(x) (bsht((x),1,7,5,2,0,3,6,4)) # define cursor_seg (2) # define bp_seg (4) # define SW1 (0==PIND&(1<<2)) # define SW2 (0==PIND&(1<<3)) # define seg_SET(x) {PORTB=(x);} # define com0_SET {asm("CBI PORTD,6");} # define com1_SET {asm("CBI PORTD,5");} # define com2_SET {asm("CBI PORTD,4");} # define com3_SET {asm("CBI PORTD,1");} # define blankOUT {PORTB=0xff;PORTD|=0x72;} # define BUZ(x) {if(x) asm("SBI PORTD,0"); else asm("CBI PORTD,0"); } #else # define fontform(x) (bsht((x),4,6,0,3,5,2,7,1)) # define cursor_seg (3) # define bp_seg (1) # define SW1 (digitalRead(18)==LOW) # define SW2 (digitalRead(19)==LOW) # define seg_SET(x) {PORTB=(PORTB&0xc0)|((x)&0x3f);PORTD=(PORTD&0x3f)|((x)&0xc0);} # define com0_SET {digitalWrite(17, LOW);} # define com1_SET {digitalWrite(16, LOW);} # define com2_SET {digitalWrite(15, LOW);} # define com3_SET {digitalWrite(14, LOW);} # define blankOUT {PORTB|=0x3f;PORTD|=0xc0;PORTC|=0x0f;} # define BUZ(x) {if(x) digitalWrite(0, HIGH); else digitalWrite(0, LOW); } #endif const prog_uint8_t LED7_fonts[] PROGMEM ={ fontform(0xfc), // 0 fontform(0x60), // 1 fontform(0xda), // 2 fontform(0xf2), // 3 fontform(0x66), // 4 fontform(0xb6), // 5 fontform(0xbe), // 6 fontform(0xe4), // 7 fontform(0xfe), // 8 fontform(0xf6), // 9 fontform(0xee), // A fontform(0x3e), // b fontform(0x9c), // C fontform(0x7a), // d fontform(0x9e), // E fontform(0x8e), // F fontform(0x00), // (SPC) fontform(0x02), // - fontform(0x62), // + fontform(0x12), // := }; const prog_uint8_t Mcode[] PROGMEM ={ //len Mcode-bitmaps(LSB first) 22, 0x07,0x77,0x77, // 0 - - - - - 20, 0x01,0xDD,0xDD, // 1 . - - - - 18, 0x00,0x77,0x75, // 2 . . - - - 16, 0x00,0x1D,0xD5, // 3 . . . - - 14, 0x00,0x07,0x55, // 4 . . . . - 12, 0x00,0x01,0x55, // 5 . . . . . 14, 0x00,0x05,0x57, // 6 - . . . . 16, 0x00,0x15,0x77, // 7 - - . . . 18, 0x00,0x57,0x77, // 8 - - - . . 20, 0x01,0x77,0x77, // 9 - - - - . 7, 0x00,0x00,0x00, // (SPC) 16, 0x00,0x1D,0xDD, // J . - - - 16, 0x00,0x1D,0xD7, // Y - . - - }; static volatile uint16_t fcnt; static volatile uint8_t RTC_buf[4]; // ss,mm,hh,aux static volatile uint8_t RTC_nxt[4]; // ss,mm,hh,aux static volatile uint8_t disp_buf[4]; static volatile uint8_t Gmode; static volatile uint8_t LED_col; static volatile uint8_t LED_dp; static volatile uint8_t LED_blink; void RTC_count(){ RTC_buf[0]++; if(RTC_buf[0]>59){RTC_buf[0]=0; RTC_buf[1]++; if(RTC_buf[1]>59){RTC_buf[1]=0; RTC_buf[2]++; if(RTC_buf[2]>23){RTC_buf[2]=0; RTC_buf[3]++; }; }; }; RTC_nxt[0]=RTC_buf[0]; RTC_nxt[1]=RTC_buf[1]+1; if(RTC_nxt[1]>59){RTC_nxt[1]=0; RTC_nxt[2]=RTC_buf[2]+1; if(RTC_nxt[2]>23){RTC_nxt[2]=0; RTC_nxt[3]=RTC_buf[3]+1; }; }; } static volatile uint8_t SW_stat; uint8_t SW_cond(uint8_t i,uint8_t j){ if((i & 1)==(j & 1)){ if(i<0x0e)i+=2; return(i); }else{ return(j); }; } void SW_read(){ uint8_t k; k= SW_cond((SW_stat>>4)&0x0f ,SW2)<<4; k|=SW_cond((SW_stat )&0x0f ,SW1) ; SW_stat=k; } #define slowfactor (1) // adj 2011.02.23-21:15 // fclk=20MHz -> 20013178.7872Hz // (2^32)/(fclk/64/101)=1387215.04234 // inc_val=1387215 // tune_val=4234/100000 //#define tick_inc_val (1388133UL) #define tick_inc_val (1387219UL/slowfactor) #define tick_tune_timing (100000UL) // #define tick_tune_adjval (43007UL) #define tick_tune_adjval (33664UL/slowfactor) static volatile struct { union { uint32_t sec; uint16_t w[2]; uint8_t b[4]; } i; union { uint32_t undersec; uint16_t w[2]; uint8_t b[4]; } f; } tm; static volatile uint32_t adj_count; static volatile uint32_t tm_work; static volatile uint8_t TIM1_stat; static volatile uint8_t TIM1_limit; #define TIM1_us_period (323) // expects 3094Hz=fclk/64/101 at fclk=20MHz void isr_TIM1(){ if(TIM1_limit){ if(TIM1_limit>TIM1_stat){ TIM1_stat++; BUZ(0); }else{ TIM1_stat=0; BUZ(1); }; }else{ TIM1_stat=0; BUZ(0); }; // TIM1_limit=0:BUZ is off // TIM1_limit=1:BUZ is 1547Hz out // TIM1_limit=2:BUZ is 1031Hz out // TIM1_limit=4:BUZ is 619Hz out if(tick_tune_timing){ adj_count++; if(adj_count>=tick_tune_timing){adj_count=0; tm_work= tick_tune_adjval + tm.f.undersec; if(tm_work < tm.f.undersec){ tm.i.sec++; RTC_count(); }; tm.f.undersec= tm_work; }; }; tm_work= tick_inc_val+tm.f.undersec; if(tm_work < tm.f.undersec){ tm.i.sec++; RTC_count(); }; tm.f.undersec= tm_work; JJY_sound(); } void init_TIM1(){ tm.f.undersec=0UL; tm.i.sec=0UL; adj_count=0UL; TIM1_stat=0; TIM1_limit=0; Timer1.initialize(TIM1_us_period); Timer1.attachInterrupt(isr_TIM1); } #define m_offset (2) // 10ms- #define m_unit (9) // 45ms #define m_call_offset (40) #define m_date_offset ((m_call_offset)+9) #define m_time_offset ((m_date_offset)+5) static union { uint32_t l; uint16_t w[2]; uint8_t b[4]; } mcode; void msound(uint8_t u, uint8_t n){ uint8_t seq, code; n<<=2; seq= (u - m_offset)/m_unit; if(pgm_read_byte_near(Mcode + (n)) <= seq){ TIM1_limit=0; return; }; code= pgm_read_byte_near(Mcode + n + 3 - (seq / 8)); if(1== ((code>>(seq & 0x07)) & 1)){ TIM1_limit=2; }else{ TIM1_limit=0; }; } void JJY_sound(){ uint8_t u; u= tm.f.w[1]/327; // about 5ms/unit switch(u){ case(0): TIM1_limit=1; break;; case(1): case(200): TIM1_limit=0; break;; default: switch(RTC_buf[0]){ case(m_call_offset ): // J case(m_call_offset+1): // J case(m_call_offset+4): // J case(m_call_offset+5): // J if(0==(RTC_nxt[1]%10)){ msound(u, 11); }; break;; case(m_call_offset+2): // Y case(m_call_offset+6): // Y if(0==(RTC_nxt[1]%10)){ msound(u, 12); }; break;; case(m_date_offset ): // M10 case(m_time_offset ): // h10 msound(u, RTC_nxt[2]/10); break;; case(m_date_offset+1): // M1 case(m_time_offset+1): // h1 msound(u, RTC_nxt[2]%10); break;; case(m_date_offset+2): // D10 case(m_time_offset+2): // m10 msound(u, RTC_nxt[1]/10); break;; case(m_date_offset+3): // D1 case(m_time_offset+3): // m1 msound(u, RTC_nxt[1]%10); break;; case(59): if(9<=u && u<140){ TIM1_limit=4; }else{ TIM1_limit=0; }; break;; default: TIM1_limit=0; break;; }; break;; }; } void LED7_edit(uint8_t pos,uint8_t num) { // format string up to two digits. uint8_t i; i= num/10; disp_buf[pos]= pgm_read_byte_near(LED7_fonts + i); i= num-(i*10); disp_buf[pos+1]=pgm_read_byte_near(LED7_fonts + i); } void LED7_edit4(uint16_t num) { // format string up to four digits. disp_buf[3]= pgm_read_byte_near(LED7_fonts + (num % 10));num/=10; disp_buf[2]= pgm_read_byte_near(LED7_fonts + (num % 10));num/=10; disp_buf[1]= pgm_read_byte_near(LED7_fonts + (num % 10));num/=10; disp_buf[0]= pgm_read_byte_near(LED7_fonts + (num % 10));num/=10; } void LED7_edit4hex(uint16_t num) { // format string up to four digits. disp_buf[3]= pgm_read_byte_near(LED7_fonts + (num & 0x0f));num>>=4; disp_buf[2]= pgm_read_byte_near(LED7_fonts + (num & 0x0f));num>>=4; disp_buf[1]= pgm_read_byte_near(LED7_fonts + (num & 0x0f));num>>=4; disp_buf[0]= pgm_read_byte_near(LED7_fonts + (num & 0x0f));num>>=4; } void disp_datetime() { // form date&time into disp_buf LED_dp= 0; switch(Gmode){ case(0): LED7_edit(0,RTC_buf[2]); if(0!=(tm.f.b[3]&0x80)) LED_dp= 1<<6; LED7_edit(2,RTC_buf[1]); break;; case(1): LED7_edit(0,RTC_buf[1]); LED_dp= 1<<6; LED7_edit(2,RTC_buf[0]); break;; case(2): LED7_edit(0,RTC_buf[0]); LED_dp= 1<<6; LED7_edit(2,(((tm.f.w[1]>>5)*50UL) / 1024)); break;; case(3): LED7_edit4hex( mcode.w[1] ); // LED7_edit4hex( tm.f.w[1] ); // sub-seconds. break;; case(4): LED7_edit4hex( mcode.w[0] ); // LED7_edit4( (uint16_t)analogRead(14) ); // band gap voltage source // LED7_edit4( (uint16_t)(1100UL * 1024UL / analogRead(14)) ); // Vcc voltage source if(0==(tm.f.b[3]&0xe0)) LED_dp= 1<<4; break;; case(5): LED7_edit4( (uint16_t)analogRead(8) ); // temperature senser // LED7_edit4( (uint16_t)(1100UL * 1024UL / analogRead(14)) ); // Vcc voltage source if(0==(tm.f.b[3]&0xe0)) LED_dp= 1<<5; break;; case(6): LED7_edit4hex( (uint16_t)(1100UL * 1024UL / analogRead(14)) ); // Vcc voltage source if(0==(tm.f.b[3]&0xe0)) LED_dp= 1<<6; break;; case(7): LED7_edit4( analogRead(15) ); // GND if(0==(tm.f.b[3]&0xe0)) LED_dp= 1<<7; break;; default: break;; }; } void LED7_disp() { uint8_t m,n; blankOUT; if(0!=(LED_col&0x10))m=0; // cursor blinking control else m=(1<<cursor_seg); switch(LED_col&3){ case(0): com0_SET; n=disp_buf[0]; if(LED_dp&(1<<7))n&=~(1<<bp_seg); if(LED_blink&(1<<7))n^=m; break;; case(1): com1_SET; n=disp_buf[1]; if(LED_dp&(1<<6))n&=~(1<<bp_seg); if(LED_blink&(1<<6))n^=m; break;; case(2): com2_SET; n=disp_buf[2]; if(LED_dp&(1<<5))n&=~(1<<bp_seg); if(LED_blink&(1<<5))n^=m; break;; case(3): com3_SET; n=disp_buf[3]; if(LED_dp&(1<<4))n&=~(1<<bp_seg); if(LED_blink&(1<<4))n^=m; break;; }; LED_col++; seg_SET(n); } void setup() { #if (isMCU_2313) PORTA=0x03; DDRA=0x00; PORTB=0xFF; DDRB=0xFF; PORTD=0xFF; DDRD=0xF3; #else PORTB=0xFF; DDRB=0xFF; PORTC=0xFF; DDRC=0x0F; PORTD=0xFF; DDRD|=0xEB; #endif analogReference(DEFAULT); // analogReference(INTERNAL); // analogReference(EXTERNAL); SW_stat=0; Gmode=0; fcnt=0; LED_col= 0; LED_dp= 0; LED_blink= 0; RTC_buf[0]=RTC_buf[1]=RTC_buf[2]=RTC_buf[3]=0; init_TIM1(); interrupts(); } void loop() { volatile uint16_t i; SW_read(); switch(Gmode){ case(0): // display hh.mm if( ((SW_stat )&1) ){ // is push SW1? if(((SW_stat>>1)&7)==6){ // is sensed the edge of push SW1 Gmode= 1; }else{ if(((SW_stat>>1)&7)==7){ // pushing SW1 be contonued from Gmode is 1 if( ((SW_stat>>4)&1) && ((SW_stat>>5)&7)==6 ){ // is sensed the edge of push SW2 noInterrupts(); // BEGIN critical section RTC_buf[2]++; // increments hh if(RTC_buf[2]>23)RTC_buf[2]=0; interrupts(); // END of critical section }; }; }; }; break;; case(1): // display mm.ss if( ((SW_stat )&1) ){ // is push SW1? if(((SW_stat>>1)&7)==6){ // is sensed the edge of push SW1 Gmode= 2; }else{ if(((SW_stat>>1)&7)==7){ // pushing SW1 be contonued from Gmode is 1 if( ((SW_stat>>4)&1) && ((SW_stat>>5)&7)==6 ){ // is sensed the edge of push SW2 noInterrupts(); // BEGIN critical section RTC_buf[1]++; // increments mm if(RTC_buf[1]>59)RTC_buf[1]=0; RTC_buf[0]=0; // reset ss tm.f.undersec=0UL; interrupts(); // END of critical section }; }; }; }; break;; case(2): // display ss.nn if( ((SW_stat )&1) ){ // is push SW1? if(((SW_stat>>1)&7)==6){ // is sensed the edge of push SW1 Gmode= 3; i=analogRead(15); }; }; break;; case(3): // display xxxx if( ((SW_stat )&1) ){ // is push SW1? if(((SW_stat>>1)&7)==6){ // is sensed the edge of push SW1 Gmode= 4; }; }; break;; case(4): // display xxxx if( ((SW_stat )&1) ){ // is push SW1? if(((SW_stat>>1)&7)==6){ // is sensed the edge of push SW1 Gmode= 0; }; }; break;; case(5): // display xxxx if( ((SW_stat )&1) ){ // is push SW1? if(((SW_stat>>1)&7)==6){ // is sensed the edge of push SW1 Gmode= 6; }; }; break;; case(6): // display xxxx if( ((SW_stat )&1) ){ // is push SW1? if(((SW_stat>>1)&7)==6){ // is sensed the edge of push SW1 Gmode= 0; }; }; break;; default: Gmode=0; break;; }; disp_datetime(); LED7_disp(); delay(1UL); }
●製作物3(時計&VFD表示器大ールド) クヒヒッw時計ものばかりでサーセンwww arduino環境の使い方も朧げながら見えてきたので,それらしくまともな物を 作ってみようかということで,あのTCXO内蔵のRTCを使って,VFD表示器に時刻 を表示する,ちょっとばっかしノスタルジックな匂ひのする時計をば,こさえて みたんさ。
8桁のVFD管表示器の時計(これは奥行きを詰めるためVFD管搭載部を分離&スタックした物。右下がチルーノとRTC大ールド)
VFD管LD8035E(4本セット@\350)とソースドライバTD62783(@\136)は,大阪の 共立電子から,RTCのDS3231(@\764)はDigikeyからそれぞれ調達した。 他の部品は大体,秋葉の秋月,鈴商,日米,千石あたりで調達。 ケースは\100ショップのディスプレイケースw 費用は送料を除くと,チルーノを含めて全部で\4000弱。 VFDが4本セットモノだったので,8桁として表示器を構成してコロン表示にも 1桁使うという贅沢仕様になっている。
scematic graph of RTC Dyield board RTC大ールドの回路図
VFD管表示器にはグリッド電圧生成とドライバ,ラッチ付きシフトレジスタを 搭載して,制御用MCU部は別の基板に作成している。 表示形式はダイナミック点灯方式で,セグメントと表示桁の選択情報をシリアル でクロックとともに送出し,ラッチ出力するようになっている。 表示器へのインターフェイスは,シリアル(信号線4本)接続で10Pのalteraの ByteBlasterMVのISPケーブルに準拠した配列のコネクタとなっている。 また点灯制御や明度調整がしやすいようにCLR#信号一発で全表示をOFFにできる。 ぶっちゃけチルーノに限らず大抵のマイコンでも使えるようになっているわけだ。 チルーノとの接続では,UARTによる同期シリアルを使用すること前提で作ったが, arduinoのライブラリにUARTの同期シリアルを使う機能がないことが判明して, SPIを使ってやるようにすればよかったと激しく後悔している所だw とりあえず,今の所はarduinoのshiftOut機能を使ったスケッチとしている。
scematic graph of 8digits VFD display board 8桁VFD表示器の回路図
作成に当たって一番面倒だったのは,VFD管をきれいに揃う様に並べて実装する ところである。 「試験管立てみたいな治具があればこんな苦労しなくてもいいのに」とつくづく 思いながらハンダ付けを何回もやり直して調整した。 また余談ではあるが,RTC大ールドのパターンを起こすときにチルーノボードの レイアウトを鏡像で配置してしまったため,通常のチルーノボードではなく, ハンダ面にピンヘッダを出した逆さチルーノ専用になってしまっているw
board layout of 8digits VFD display board 8桁VFD表示器のボードレイアウト(ハンダ面からサイズ100x75mm)
http://www.youtube.com/watch?v=fW5Bkkr9CZ0
スケッチ例:時を刻む!かっちょ良く表示する!!(あたいったら圧倒的にモガね!)
/* Display date time on VFD display from RTC - Sample sketch *//* for Ciruno Board w/RTC-dyield & VFDdisplay */ #include <Wire.h> #define DS323X (0x68) #define RTC_INT_SQW (2) #include <avr/io.h> #include <avr/pgmspace.h> #define setDCNT(x) {RTC_int=((x&0x7f)<<1)|(RTC_int&1);} #define isDCNTz (0==(RTC_int>>1)) #define bsht(x,a,b,c,d,e,f,g,h) (((((x)>>7)&1)<<(a))|((((x)>>6)&1)<<(b))|((((x)>>5)&1)<<(c))|((((x)>>4)&1)<<(d))|((((x)>>3)&1)<<(e))|((((x)>>2)&1)<<(f))|((((x)>>1)&1)<<(g))|((((x)>>0)&1)<<(h))) #define VFD_sepa (1) #if (VFD_sepa) # define fontform(x) (bsht((x),3,4,1,7,5,2,0,6)) # define cursor_seg (7) # define bp_mask_0 (0x10) # define bp_mask_1 (0x20) # define bp_mask (0x30) #else # define fontform(x) (bsht((x),7,5,3,4,6,0,1,2)) # define cursor_seg (4) # define bp_mask_0 (0x40) # define bp_mask_1 (0x80) # define bp_mask (0xc0) #endif #define TxD (1) #define CLK (4) #define nSTB (0) #define nCLR (3) #define SW1 (14) #define SW2 (15) const prog_uint8_t VFD_fonts[] PROGMEM ={ fontform(0xfc), // 0 fontform(0x60), // 1 fontform(0xda), // 2 fontform(0xf2), // 3 fontform(0x67), // 4 fontform(0xb6), // 5 fontform(0xbe), // 6 fontform(0xe4), // 7 fontform(0xfe), // 8 fontform(0xf6), // 9 fontform(0xee), // A fontform(0x3e), // b fontform(0x9c), // C fontform(0x7a), // d fontform(0x9e), // E fontform(0x8e), // F fontform(0x00), // (SPC) fontform(0x02), // - fontform(0x63), // + fontform(0x12), // := }; static uint8_t G_stat; static uint8_t B_ctrl; static uint8_t BP_cnt; static uint8_t column; static uint8_t count; static uint8_t offset; static uint8_t SW_stat; // SW2=high-nibble/SW1=low-nibble ,,nibble_LSB is prev_SWstat,other bits are meaning of count in hold SW static uint8_t RTC_int; // LSB:previous RTCsINT/SQWstate MSB7bits:down count every changed INT/SQW state when if not zero static uint16_t F_cnt; static uint8_t RTC_buf[18]; void setup() { G_stat=0; F_cnt=0; BP_cnt=10; B_ctrl=0; column=0; count=0; offset=0; pinMode(nSTB, OUTPUT); pinMode(nCLR, OUTPUT); digitalWrite(nCLR, LOW); digitalWrite(nSTB, HIGH); digitalWrite(nSTB, LOW); digitalWrite(nSTB, HIGH); digitalWrite(nCLR, HIGH); pinMode(TxD, OUTPUT); pinMode(CLK, OUTPUT); delay(200UL); // pinMode(RTC_INT_SQW, INPUT); digitalWrite(RTC_INT_SQW, HIGH); RTC_int= digitalRead(RTC_INT_SQW); delay(100UL); Wire.begin(); // initialize for I2C master Wire.beginTransmission(DS323X); Wire.send(0x0e); // register address set Wire.send(0x00); // 0x0e: ctrl=SQWE(rate=1Hz) Wire.send(0x08); // 0x0f: stat=32kHzE Wire.endTransmission() ; delay(200UL); // pinMode(SW1, INPUT); pinMode(SW2, INPUT); digitalWrite(SW1, HIGH); digitalWrite(SW2, HIGH); SW_stat= ((digitalRead(SW2)==HIGH)<<7) | ((digitalRead(SW1)==HIGH)<<3); delay(100UL); // Wire.beginTransmission(DS323X); Wire.send(0x00); // register address set Wire.endTransmission() ; delay(200UL); } void RTC_read(){ uint8_t i; if((RTC_int & 1) != (HIGH==digitalRead(RTC_INT_SQW))){ i= RTC_int>>1; if(i>0)i--; RTC_int = (i<<1) | (HIGH==digitalRead(RTC_INT_SQW)); Wire.requestFrom(DS323X, sizeof(RTC_buf)); for(i=0;i<sizeof(RTC_buf);i++){ RTC_buf[i]= Wire.receive(); }; Wire.beginTransmission(DS323X); Wire.send(0x00); // register address set Wire.endTransmission() ; }; } uint8_t SW_cond(uint8_t i,uint8_t j){ if((i & 1)==(j & 1)){ if(i<0x0e)i+=2; return(i); }else{ return(j); }; } void SW_read(){ uint8_t k; k= SW_cond((SW_stat>>4)&0x0f ,(digitalRead(SW2)==LOW))<<4; k|=SW_cond((SW_stat )&0x0f ,(digitalRead(SW1)==LOW)) ; SW_stat=k; } void VFD_out(uint8_t col,uint8_t seg,uint8_t dp) { uint8_t bp; col&=7; digitalWrite(nCLR, LOW); digitalWrite(nSTB, LOW); digitalWrite(nSTB, HIGH); digitalWrite(nCLR, HIGH); if( 0!=(B_ctrl&(1<<col)) && 0!=(F_cnt&0x0040) ){ seg^=(1<<cursor_seg); }; if(BP_cnt>0){ if(col & 1){ bp= bp_mask_1; }else{ bp= bp_mask_0; }; }else{ bp=0; }; #if (VFD_sepa) shiftOut(TxD, CLK, LSBFIRST, seg); shiftOut(TxD, CLK, LSBFIRST, (seg&0x0e)|((seg&1)<<7)|((dp&1)<<0)|(bp) ); shiftOut(TxD, CLK, LSBFIRST, 1<<(7-col) ); #else shiftOut(TxD, CLK, LSBFIRST, seg); shiftOut(TxD, CLK, LSBFIRST, (seg&0xf)|((dp&1)<<4)|(bp)); shiftOut(TxD, CLK, LSBFIRST, 1<<(col) ); #endif digitalWrite(nSTB, LOW); digitalWrite(nSTB, HIGH); } uint8_t VFD_init() { uint8_t i; if(( (column==0) && ((SW_stat&0x80) == 0) )||( (column==7) && ((SW_stat&0x08) == 0) ) ){ i= 1; }else{ i= 0; }; VFD_out(column, pgm_read_byte_near(VFD_fonts + offset), i); column++; if(column>=64){ count++; if(count>=16){ count=0; offset++; if(offset>=sizeof(VFD_fonts)){ offset=0; return(1); }; }; }; return(0); } void VFD_time() { uint8_t i,j; j=0; switch(column & 7){ case(0): i= RTC_buf[2]>>4; break;; case(1): i= RTC_buf[2]&0x0f; break;; case(2): i= 0x13; break;; case(5): if(RTC_int){ i= 0x13; }else{ i= 0x10; }; break;; case(3): i= RTC_buf[1]>>4; break;; case(4): i= RTC_buf[1]&0x0f; break;; case(6): i= RTC_buf[0]>>4; break;; case(7): i= RTC_buf[0]&0x0f; break;; }; VFD_out(column, pgm_read_byte_near(VFD_fonts + i), j); column++; } void VFD_date() { uint8_t i,j; j=0; switch(column & 7){ #if 1 case(0): i= SW_stat>>4; break;; case(1): i= SW_stat&0x0f; break;; case(2): case(5): i= 0x10; break;; case(3): i= B_ctrl>>4; break;; case(4): i= B_ctrl&0x0f; break;; case(6): i= RTC_buf[4]>>4; break;; case(7): i= RTC_buf[4]&0x0f; break;; #else case(0): i= RTC_buf[6]>>4; break;; case(1): i= RTC_buf[6]&0x0f; break;; case(2): i= 0x11; break;; case(5): if(RTC_int){ i= 0x11; }else{ i= 0x10; }; break;; case(3): i= RTC_buf[5]>>4; break;; case(4): i= RTC_buf[5]&0x0f; break;; case(6): i= RTC_buf[4]>>4; break;; case(7): i= RTC_buf[4]&0x0f; break;; #endif }; VFD_out(column, pgm_read_byte_near(VFD_fonts + i), j); column++; } void loop() { uint8_t i,j; F_cnt++; if((BP_cnt>0) && (0==(F_cnt&0x07))) BP_cnt-=1; switch(G_stat){ case(0): G_stat= VFD_init(); break;; case(1): VFD_time(); if((SW_stat&0xf0)==0xd0){ G_stat=2; setDCNT(5); }; break;; case(2): VFD_date(); if(isDCNTz){ G_stat=1; }; break;; default: G_stat=0; break;; }; if((SW_stat&0x0f)==0x0d){ BP_cnt=(G_stat!=2)?5:50; if(B_ctrl){ B_ctrl<<=1; }else{ B_ctrl=1; }; }; RTC_read(); SW_read(); delay(1UL); }
あもりにも長すぎるでしょう!! 機能を追加してたり,いじってる部分もあったりで,固まってソース整理が終わる までテストで作ったRTC時刻表示のみのスケッチを掲載しておく。 ●その他の製作物 また光物(電光掲示板とか)で何かを色々作り中(coming soon!) ●資料 チルーノのarduino_function対応表
●蛇足 実はチルーノを作る以前にもtiny2313を使用して同じようなものを作ってはいたのだ。 ただのMCUとしてだけではなく,PCをメインコントローラにしてUSB経由で操作するよう な使い方もできるような素敵なヤツを....[ 参考URL ]
image of the past thing こんなのも作ってはみたが...
だがいかんせん,規模が小さすぎてお手軽に試行錯誤するのが難しかった(ポートの 上げ下げだけならともかく,ステートフルな制御を使おうとすると途端にリソースの パズルになってしまう)ので,ある程度余裕のあるリソース(十数Kワードの命令と数kB 程度のRAM)を持ったMCUで,入手も容易なモノをということでMega328Pにしたのである。 H8系はルネサスがあんな状態なんで,ジャンクはともかく新規ものの使用は,暫くの 間様子見しておこうと思っている。 ●独り言 もうね,休日引篭もって...ナニやってんだろ...オレ?的な感じ... <乾杯!! [ご注意] ・このページの記載事項については,一切無保証です。
    inserted by FC2 system