前回の続きで、ATtiny202-SSNRで“Lチカ”した時のプログラムのソースコードを以下に示す。アセンブリ言語で組んだ。
;******************************************************* ;タイマAを使用したLチカプログラム (ATTiny202-SSNRを使用) ;******************************************************* .include "tn202def.inc" ;---- 汎用レジスタ ---- .def R_TEMP1 = R16 ;汎用的な変数 .def R_TEMP2 = R17 ;汎用的な変数 .def STACK = R18 ;ステータス・レジスタ退避用 .def U_FLAG = R19 ;ユーザーフラグ ;ユーザーフラグのビット .EQU B_TCA_OVF = 0 ;タイマAオーバーフロー .CSEG ;コードセグメント RJMP MAIN ;リセット .ORG TCA0_OVF_vect RJMP TCA_OVF ;タイマAオーバーフロー ;********************************* ;タイマAオーバーフロー割り込み処理 ;********************************* TCA_OVF: ;---- 全割り込み禁止 ---- CLI ;---- ステータスレジスタの内容を退避 ---- IN STACK, CPU_SREG ;---- タイマAオーバーフロー割り込み要求フラグをクリア ---- CLR R_TEMP1 SBR R_TEMP1, (1<<TCA_SINGLE_OVF_bp) STS TCA0_SINGLE_INTFLAGS, R_TEMP1 ;---- タイマAオーバーフローユーザ・フラグをセット ---- SBR U_FLAG, (1<<B_TCA_OVF) ;---- ステータスレジスタの内容を復帰 ---- OUT CPU_SREG, STACK ;---- 全割り込み許可 ---- SEI RETI ;*************** ;LED切り替え処理 ;*************** LED_CHANGE: ;---- タイマAオーバーフローユーザ・フラグをクリア ---- CBR U_FLAG, (1<<B_TCA_OVF) ;---- PORTAのビット6を反転 ---- LDI R_TEMP1, 0B01000000 STS PORTA_OUTTGL, R_TEMP1 RJMP MAIN01 ;************** ;メインルーチン ;************** MAIN: ;---- 全割り込み禁止 ---- CLI ;---- PORT設定 ---- LDI R_TEMP1, 0B11001111 ;PA4とPA5は存在しないので0 STS PORTA_DIR, R_TEMP1 LDI R_TEMP1, 0B00000000 STS PORTA_OUT, R_TEMP1 ;---- タイマA設定 ---- LDS R_TEMP1, TCA0_SINGLE_INTCTRL SBR R_TEMP1, (1<<TCA_SINGLE_OVF_bp) STS TCA0_SINGLE_INTCTRL, R_TEMP1 ;タイマAオーバーフロー割り込み許可 LDI R_TEMP1, 0B00000111 STS TCA0_SINGLE_CTRLA, R_TEMP1 ;プリスケーラ8 ;---- ユーザーフラグをクリア ---- CLR U_FLAG ;---- 全割り込み許可 ---- SEI ;*********** ;タイマA待機 ;*********** MAIN01: ;---- タイマAオーバーフローユーザ・フラグを監視 ---- SBRC U_FLAG, B_TCA_OVF ;ビットがセットされていなければ1行スキップ RJMP LED_CHANGE ;ビットがセットされていたらLED切り替え処理にジャンプ RJMP MAIN01
今回使用した機能とレジスタについて解説する。
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
– | – | – | – | CLKSEL2 | CLKSEL1 | CLKSEL0 | ENABLE |
R | R | R | R | R/W | R/W | R/W | R/W |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
ビット3~1のCLKSEL2~0でプリスケーラを設定する。
000 | 1 |
001 | 2 |
010 | 4 |
011 | 8 |
100 | 16 |
101 | 64 |
110 | 256 |
111 | 1024 |
データシートによれば、ATtiny202はデフォルトで20MHzを6分周しているので、設定を触らなければ3.333333MHzで動作している事になる。
マイコンの動作クロック周波数をMCLK(MHz)、プリスケーラをPSCLとすると、16ビットタイマで作り出せる最大の時間は(65536*PSCL)/(MCLK*10^6)となる。
今回はプリスケーラを8とすると、最大時間が約157msとなり、肉眼でわりと早くLEDが点滅しているのが分かるということでこれを採用した。
また、タイマAのCTRLAレジスタのビット0に1を書き込むとカウントを始めるということで、ビット3~1のプリスケーラ8は011で、ビット0は1なので未使用のビット7~4と合わせて0B00000111となる。
この値をCTRLAレジスタに書き込む事で、タイマAはプリスケーラ8でカウントを始める。
マイコンが起動すると、157msごとにタイマAがオーバーフローし割り込みが発生する。そしてPORTA_OUTTGLのビット6に1を書き込む命令が実行され、結果として157msごとにLEDの点灯、消灯が切り替わる。LEDが点滅し、Lチカプログラムが完成した。
タイマAの割り込みルーチンでは前回の記事で説明した通り、自動で全割り込み禁止にはならず、割り込み要求フラグは自動で解除されないので、プログラムでこれを実行している。
次回はSPI通信でDDS(Direct Digital Synthesizer)を制御するプログラムについて説明する。
]]>前回の記事で、ATtiny202はATmega328PやATtiny13Aとはレジスタの名前や機能の設定値からして違うという話をした。
私はAVRの開発は基本的にアセンブリ言語でしているので、どういう風にプログラムの記述が変わるのかアセンブリ言語で説明する。
以下、ATmega328PやATtiny13Aの事を「以前のマイコン」と表記する事がある。
まず、ATtiny202と以前のマイコンでは、レジスタのアドレスの振り方が違う。
データシートから抜粋した両者のメモリ配置図を見て欲しい。
以前のマイコンではメモリアドレス0x0000~0x001Fに汎用作業レジスタのR0~R31が割り当てられ、I/Oレジスタのアドレスはその続きの0x0020からになっている。
対して、ATtiny202では汎用作業レジスタは別のメモリ空間に割り当てられており、I/Oレジスタはアドレス0x0000から始まっている。
そのおかげで、ATtiny202にはI/Oアドレスという少しややこしい概念がない。
そして、以前のマイコンでは各機能設定用のレジスタの一部がI/Oレジスタ領域にあるが、ATtiny202では各機能設定レジスタは基本的に拡張I/Oレジスタ領域にしかない。I/Oレジスタ領域にあるのは仮想のポート設定レジスタとCPU用レジスタ(ステータスレジスタ等)くらいだ。
ATtiny202のデータシートを読むと、各機能の制御用にCTRLAやCTRLBといったレジスタがある。
しかし、様々な機能で同じようにCTRLAやCTRLBという名前になっており、ソースコードにそのまま記述しても設定したい機能のレジスタを特定できない。
そこで、ATtiny202の様々なレジスタ等の定義値を記している”tn202def.inc”というファイルを開くと、例えばSPIのCTRLAレジスタは”SPI0_CTRLA”と定義されている。
この定義値はデータシートに書いてないので、データシートと合わせて”tn202def.inc”の中身を見ながらプログラムを記述する必要がある。
対して、以前のマイコンでは基本的に各機能のレジスタ名はデータシートに書いてある名前がそのまま使える。ATmega328Pのデータシートでは、SPIの設定をするレジスタは”SPCR”となっていて、ソースコードに”SPCR”と書けばプログラムはそのままアセンブルできる。
ATtiny202のタイマも、データシートを読むとかなり多機能なようだ。タイマAには標準動作モードと分割動作モードがあり、標準動作モードでは普通の16bitタイマなのだが、分割動作モードでは8bitタイマ2つとして使う事が出来るようだ。分割動作モードは今回は使用しておらず、データシートもちゃんと読んでいないのでこの記事では割愛する。
以前のマイコンでは、タイマの動作開始は制御レジスタにプリスケーラ設定値を書き込んだ時点だったが、ATtiny202ではCTRLAレジスタにENABLEというビットがあり、このビットに1を書き込む事でタイマは動作を開始する。
確かに、慣れれば「プリスケーラを設定した時点で動作開始」より「ENABLEビットに1を書き込んだ時点で動作開始」の方が直感的で分かりやすいかもしれない。
ATtiny202ではタイマに限らず、殆どの機能のCTRLAレジスタにENABLEビットがあり、このビットを1にすることでその機能が有効になる。
以前のマイコンでは、割り込みがかかると自動的にステータスレジスタのIフラグが0になり全割り込み禁止になる。また、割り込み要求フラグも自動的に解除される。
しかし、ATtiny202では、割り込みがかかった後の全割り込み禁止処理と割り込み要求フラグの解除が必要な場合は、プログラムに記述しなければならない。特別な理由がない限り、通常は記述しておいた方が良いと思われる。
割り込みがかかってもステータスレジスタの退避が自動で行われないのは以前のマイコンと同じなので、通常はこちらもプログラムに記述しておく必要がある。
また、ATtiny202のタイマAオーバーフロー割り込みベクタは0x0008なのだが、これも”tn202def.inc”に定義値が記載されており、”TCA0_OVF_vect”となっている。
この記事を書くために改めて確認したら、実は以前のマイコンのincファイルにも記載されていたが、知らずに今までは即値をそのまま使っていた。
以前のマイコンでは、ポート関連のレジスタは基本的にI/Oレジスタ領域0x0020~0x005F(I/Oアドレスでは0x0000~0x003F)の範囲内にあるが、ATtiny202では冒頭でも述べたように拡張I/Oレジスタ領域にある。例えば、ポートAの入出力方向を設定する”PORTA_DIR”レジスタは0x0400にある。
しかし、ポート設定へは頻繁にアクセスするし、ピンの値によって処理を分岐させる際等に使用するSBICやSBIS命令が使えないのはあまりにも不便という配慮なのか、仮想ポートというレジスタが用意されている。
例えば、”PORTA_DIR”に対応する仮想ポートレジスタは”VPORTA_DIR”で、アドレス0x0000だ。アドレス0x0000~0x001Fの間に入っているので、SBICやSBIS命令も使える。仮想ポートは入出力方向、出力値、入力値、割り込み要求フラグの4レジスタが用意されており、ポートAの場合それぞれ”VPORTA_DIR”, “VPORTA_OUT”, “VPORTA_IN”, “VPORTA_INTFLAGS”で、アドレスは0x0000~0x0003となっている。
これらへのアクセスは、本来のレジスタである”PORTA_DIR”, “PORTA_OUT”, “PORTA_IN”, “PORTA_INTFLAGS”(アドレスはそれぞれ0x0400、0x0404、0x0408、0x0409)へのアクセスと同じ結果になる。通常は仮想ポートの方へのアクセスで良さそうである。
当然、この4レジスタ以外のポート設定を変更したい場合は本来のアドレス0x0400以降のレジスタにアクセスするしかないのだが。
また、以前のマイコンより便利なレジスタがいろいろと増えていて、例えば今回のプログラムでも使用している”PORTA_OUTTGL”などがある。
このレジスタはその名の通り、PORTAの出力値を反転させるというものだ。トグルスイッチのようにON-OFFを切り替えるイメージがしやすい。
使い方は簡単で、ポートAの切り替えたいビットに1を書き込むだけだ。例えばポートAのビット7の出力値を切り替えたかったら”PORTA_OUTTGL”に0B1000000(0x80)を書き込めば良い。そうするとポートA7ピンの出力値が0なら1、1なら0になる。いちいち現在の出力値を確認する手間が省けて楽だ。
実は、同じ事はポートAの入力値を返す”PORTA_IN”レジスタの該当するビットに1を書き込むことでも出来、それは以前のマイコンでも実装されていた(“PINx”レジスタの該当するビットに1を書き込む)のだが、OUTTGLという名前の専用レジスタが用意されたのは、より直感的にプログラム出来て良いと思う。
ポート設定だけでもここには書き切れない程の多数の機能があるので、詳しくはデータシートを参照されたい。
次回は、ATtiny202で“Lチカ”してみた時のプログラムを説明する。
]]>そりゃそうだろう。同じ8ピンのATtiny13Aと比べて、完全上位互換だ。
ATtiny202 | ATtiny13A | |
プログラムメモリ | 2kB | 1kB |
EEPROM | 64B | 64B |
RAM | 128B | 64B |
GPIO | 6pin | 6pin |
ADC | 6ch | 4ch |
USART | 1ch | – |
I2C | 1ch | – |
SPI | 1ch | – |
タイマ | 16bit×2 | 8bit×1 |
価格※ | 60円 | 130円 |
※2022年11月現在
小さいながら、通信機能が充実しているのが嬉しい。外部機器(例えばDDSとかLCDとか)を制御出来るし、プログラムメモリもSRAMも同じ8ピンのATtiny13Aと比べると倍の容量だ。そして価格は半分以下。AVRで8ピンのマイコンが欲しいとなったら趣味レベルでは殆どATtiny202でいいと思う。
私はAVRの開発をする時はいつもCQ出版社 AVRマイコン・プログラミング入門 廣田修一・著を片手にしている。この本は2010年1月1日発行で、題材としてATmega88を使用している。ATmega328PやATtiny13Aをプログラムする時は、レジスタのアドレスや割り込みベクタの違いなど細かい相違点に気を付ければそう戸惑うことはなかった。
しかし、このATtiny202-SSNRはレジスタの名前からして全く違う。ポート設定やタイマなど、御馴染みの機能を使うにしても今まで触ったことがあるATmega328PやATtiny13Aとは違う設定用レジスタやビットがある。マイコン界での“Hello World”に当たる“Lチカ”プログラムを組むのも少し苦労したので、また別記事で取り上げようと思う。
]]>ここで、故障は以下の三点が考えられる。
サーモスイッチはエンジンをかけて水温を上げて接点が導通するかどうかテスターで調べた。結果は異常なし。水温が上がれば接点は繋がるし、水温が下がれば接点は切れる。
配線の断線は、ちゃんと調べようと思ったらテスターで導通を調べないといけないが、とりあえずヒューズを見た。ヒューズは溶断していた。しかし、これで配線がどこかでショートしていると考えるのは早計だ。モータ自体の故障でも過電流が流れる可能性はある。
試しにファンをドライバーでつついて回そうとしてみたが、回らなかった。どうやらモータが固着しているようだ。ヒューズはファンが固着したことにより過電流が流れて切れたと思われる。
サービスマニュアルの手順では、ラジエターを外してからファンを外すことになっているが、ラジエターを外すにはクーラントを抜いて配管を外して…とかなり大掛かりな作業になってしまう。試しにラジエターの下の部分だけ外して手で押し広げて隙間からレンチを突っ込んだらファンのボルトが外れたので、ラジエターを車体に付けたままファンを外せた。
コネクタは初めて見た形だ。配線コムで調べたところ、住友電装製HM防水シリーズ3極Mコネクタだった。
ファンを車体から外す前は確かに固着していたのだが、外してみたら固着していなかったので試しに回してみた。
不具合が再現しなくて困ったが、次の日にもう一度調べたら固着していた。しかし、少し動かすと固着が解かれる。これはもうモータ自体の故障で決まりだ。
パーツカタログでモータの品番を調べたらV,W(自分のはV)は19030-MAT-003だが、廃番になっていた。試しにX,Yの19030-MAT-E00とIの19030-MAT-021も調べたが、どちらも廃番だった。つまり、CBR1100XXの純正のモーターAssyは全て廃番だ。
仕方ないので、Webikeで似たような形のモーターAssyが無いか探してみた。
似たような形のファンモータがこれだけ引っかかったが、全てCMS経由なので、注文してから来るまでに3週間とかかかってしまう。これらの部品の品番で純正部品検索をして、値段が極端に高くないものという条件にしたら、ゴールドウイング用の19032-MCA-003が22880円だったのでそれを選んだ。尚、自分が注文したのが最後の1個だったらしく、後にもう一度検索したら廃番になってしまっていた(ゴールドウイング乗りの方、ごめんなさい!)。
このモータはそのままではCBR1100XXにはつかないので、先ほど配線コムで調べたコネクタも注文した。
モータが届いたら、早速配線をぶった切ってコネクタや配線を取り付ける。貴重な新品の純正部品に元に戻せない加工をするのはかなり抵抗があったが、そのままでは使えなくて押入れの肥やしになるだけでありそれも勿体無いので仕方ない。
モータと並列に繋がっている謎の黒い部品は不要なので外した。外した後で気付いたが、この部品はどうやらモータのノイズ除去用コンデンサらしい。250V 0.47と書いてあるので、おそらく耐圧250Vで容量が0.47μFなのだろう。コンデンサなら別にあっても困らないし、付けたままにしておけばよかった。
恐らく、このモータを取り付けるゴールドウイングはECUがついていて、誤作動防止のためにノイズ除去をした方がいいのだろう。しかし、キャブレターのCBR1100XXにはECUがついておらず、それっぽいものと言えばエンジンの点火タイミングを制御するイグニッションコントロールモジュールがあるくらいだ。だから多少のノイズは大丈夫なのだろう。
また、φ6mmの丸型端子と写真には無いが250型の平型端子と小さいタイラップが必要だ。
その後、冷却水が温まったらファンは無事に元気よく回り、修理が完了した。
自宅からそれほど遠くない通勤経路で起こった故障だったので大した被害は出なかったが、ロングツーリングの出掛けた先で起こったらと思うとぞっとする。
作業時間自体は多分合計3、4時間程だと思うのだが、モータを注文して部品待ちだったり、仕事の絡みで明るい時間に作業時間を取れなくて結局直るまでに11日かかってしまった。
今回は何とか別の車種のモータを流用出来て事無きを得たが、次にまた同じ不具合が出たらその頃はもうモータが無いかもしれない。その場合はオーバーホール屋さんに出してモータをオーバーホールして貰うしかない。
]]>設計した回路を下図に示す。
HK829とNJM386の周辺は殆どデータシートの回路例の通りなので、何も難しい事は無い。HK829のメッセージ再生用スイッチの代わりにマイコンを接続した部分は、今回アレンジした点。HK829のスピーカー出力の部分はデータシートに386では無い別のアンプICに繋ぐ例が載っていたので、それを参照した。
導入編でも述べたが、HK829のM0~M3のピンはIC内部でプルアップされており、開放電圧を測ったらほぼ電源電圧だった。マイコンのGPIOも待機状態でHigh(=電源電圧が出力されている)であり、HK829のデータシートを読んだ限りではM0~M3にHighが入力される事は想定されていないようなのでそこが少し心配だが、HK829のHighもマイコンのHighもほぼ同電位なら殆ど電流は流れないから大丈夫だろうと勝手に納得することにした。間にトランジスタかMOSFETを挟んで開放状態を作り出す事も可能だが、面倒臭かったので。もしICが突然壊れたらこれが原因という事で対策の候補の一つになるだろう。
まずはブレッドボードで組んで実験した。ICに録音する際は、TVの音声を一旦PCに録音し編集してWalkmanに移し、Walkmanの出力をブレッドボードに接続した。PCから直接接続して録音してもノイズだらけで使い物にならなかったので、面倒だがこの手順を踏んだ。そしてテスト再生は見事に成功した。
こうして設計した回路をユニバーサル基板に組んだ。
この際、回路図上でHK829のMIC+、MIC-、MICG、/RECに接続されている部品はメッセージを再生する上では必要無いので省略した。
ケースはタカチのSW-85S。よくある72×47mmの基板の回路を収納するのにちょうどいい。
当初はモバイルバッテリを電源にしようと思っていたが、ケースに入りそうなモバイルバッテリを見つけられなかったので単4電池3本にすることにした。始めは電源電圧は5Vの想定で回路を設計していたが、データシートを見る限り4.5Vでも全てのICの定格電圧範囲に入っていたのでよしとする事にした。基板との接続はXHコネクタにして、道具を使わなくても着脱できるようにした。
ハンドグリップに付けるスイッチは秋月電子通商で売っていたSS-10GL13というレバー付きのマイクロスイッチにした。接点の定格はAC250V10.1Aで、機械等に使う工業用と思われる。ロジックのHigh、Lowを切り替えるという数mAオーダーの電流しか流れない用途には明らかにオーバースペックだが…。大きさやレバーがあることなどが今回の用途にちょうど良かったので選定した。
ハンドグリップはダイソーで25kgのものにした。ハンドグリップにスイッチを固定するのは、ホームセンターで配線を束ねるための金具を購入してスイッチのねじ穴に合うように穴を開けて加工した。
ケースにホームセンターで売っていた4mmの雄ねじが切ってあるヒートンを取り付け、ハンドグリップの輪の部分とカラビナで繋げた。
スピーカーは秋月電子通商で売っているDXYD50N-22Z-8A-F。5cm程度でフレームにねじ穴がついていてケースに取り付けるのが楽なのが選定理由。
ケース加工の寸法を下図に示す。
図は省略したが、この他に電源スイッチとヒートン取り付け穴を開けた反対の面の中心辺りにハンドグリップに取り付けるスイッチの配線用の穴を開ける必要がある。
そして、基板に組んだ後で改めてブレッドボードでHK829と最低限の部品で回路を組み、試しにスピーカー端子に直接スピーカーを繋いでみたら、なんと結構な音量で音が鳴ってしまった。386のアンプ回路は要らなかったことになる。しかし今更基板を組み直すのも面倒なので、そのままにした。
完成写真を以下に示す。
必要な部品が揃った所でこれから開発していく(正確には部品の選定も開発の一部だが)わけだが、自分のようなマイコン初心者の場合、多分一番大変なのはマイコンプログラミングなので、まずはそこから取り掛かる。
ATtiny13Aのピン配列を下図に示す。
ATTiny13Aの場合、GPIOのPB1が外部割り込み0のINT0を兼ねるので、PB0、PB2、PB3、PB4をメッセージ再生用のピンとして使用する。PB5はRESETを兼ねていて通常はRESETであり、PB5として使うには多分ヒューズビットの書き換えが必要で面倒臭いのでそのままにしておく。
メッセージは、以下のように割り当てた。
マイコンの大まかな動作の流れはこうだ。
上記では細かい設定やタイマの動き等は省略しているので、それも含めたフローチャートを以下に示す。
16ms以上Lowという部分の時間をタイマ0で作っている。今回はプリスケーラが1024で、255カウントしたらオーバーフロータイマ0の割り込みが発生するようにした。クロック周波数はAVRのデフォルトの1MHzで動作し、1024クロック毎にカウンタが1上がり255でオーバーフローして割り込みが発生する。ということは、(1024×255)/(1×10^6)≒0.26秒の時間を作り出す。
マイコンで電子的に0.26秒間、HK829に接続したスイッチを押したようにするというわけだ。
以上を踏まえて、プログラムしたのが以下のソースコードだ。
;----------------------------------------------------------------------------------- ;スイッチを押す度にHK829のM0, M1, M2, M3を順番に再生するプログラム (ATtiny13Aを使用) ;----------------------------------------------------------------------------------- .include "tn13adef.inc" ;汎用レジスタ .def R_TEMP1 = R16 ;汎用的な変数 .def R_TEMP2 = R17 ;汎用的な変数 .def COUNTER = R18 ;メッセージのカウンタ .def STACK = R19 ;ステータス・レジスタ退避用 .def U_FLAG = R20 ;ユーザーフラグ ;ユーザーフラグのビット .EQU B_IOVF0 = 0 ;タイマ0オーバーフロー .EQU B_INT0 = 1 ;外部割り込み0 .CSEG ;コードセグメント RJMP MAIN ;リセット .ORG 0x0001 ;外部割り込み0 RJMP EXT_INT0 .ORG 0x0003 ;タイマ0オーバーフロー RJMP IOVF0 ;----------------------------- ;タイマ0オーバーフロー時の処理 ;----------------------------- IOVF0: ;ステータス・レジスタの内容を退避 IN STACK, SREG ;タイマ0カウンタ停止 LDI R_TEMP1, 0x00 OUT TCCR0B, R_TEMP1 ;タイマ0オーバーフローユーザーフラグをセット SBR U_FLAG, (1<<B_IOVF0) ;ステータス・レジスタの内容を復帰 OUT SREG, STACK ;割り込み前の処理に戻る RETI ;---------------------------------------------------------------- ;スイッチを押してから0.26秒後にLow出力していたピンをHighに戻す処理 ;---------------------------------------------------------------- IOVF01: ;タイマ0オーバーフローのユーザーフラグをクリア CBR U_FLAG, (1<<B_IOVF0) ;PB0, PB2~PB4を全てHighにする LDI R_TEMP1, 0B00011111 OUT PORTB, R_TEMP1 ;外部割り込み0許可 IN R_TEMP1, GIMSK SBR R_TEMP1, (1<<INT0) OUT GIMSK, R_TEMP1 ;待機ルーチンにジャンプ RJMP MAIN01 ;--------------- ;タイマ0起動処理 ;--------------- TMR0: ;タイマ0カウント値セット LDI R_TEMP1, 0x00 OUT TCNT0, R_TEMP1 ;タイマ0カウント開始 LDI R_TEMP1, 0x05 ;プリスケーラ1024 OUT TCCR0B, R_TEMP1 ;待機ルーチンにジャンプ RJMP MAIN01 ;----------------- ;外部割り込み0処理 ;----------------- EXT_INT0: ;ステータス・レジスタの内容を退避 IN STACK, SREG ;外部割り込み0禁止 IN R_TEMP1, GIMSK CBR R_TEMP1, (1<<INT0) OUT GIMSK, R_TEMP1 ;外部割り込み0ユーザーフラグをセット SBR U_FLAG, (1<<B_INT0) ;ステータス・レジスタの内容を復帰 OUT SREG, STACK ;割り込み前の処理に戻る RETI ;------------------------------------------------------- ;カウンタの数によって指定したピンをLow出力する処理を分岐 ;------------------------------------------------------- EXT_INT01: ;外部割り込み0のユーザーフラグをクリア CBR U_FLAG, (1<<B_INT0) CPI COUNTER, 0x00 ;カウンタが0の場合 BREQ MASSAGE0 ;MASSAGE0に分岐「にぎにぎ」 CPI COUNTER, 0x01 ;カウンタが1の場合 BREQ MASSAGE1 ;MASSAGE1に分岐「コメコメ」 CPI COUNTER, 0x02 ;カウンタが2の場合 BREQ MASSAGE2 ;MASSAGE2に分岐「ハートを」 CPI COUNTER, 0x03 ;カウンタが3の場合 BREQ MASSAGE1 ;MASSAGE1に分岐「コメコメ」 RJMP MASSAGE3 ;カウンタが4の場合、MASSAGE3に分岐「シェアリンエナジー!」「コメー!」 ;----------------------------------- ;カウンタが0の場合の処理「にぎにぎ」 ;----------------------------------- MASSAGE0: ;PB0をLowにする LDI R_TEMP1, 0B00011110 OUT PORTB, R_TEMP1 ;カウンタを1増やす INC COUNTER ;タイマ0起動処理にジャンプ RJMP TMR0 ;----------------------------------- ;カウンタが1の場合の処理「コメコメ」 ;----------------------------------- MASSAGE1: ;PB2をLowにする LDI R_TEMP1, 0B00011011 OUT PORTB, R_TEMP1 ;カウンタを1増やす INC COUNTER ;タイマ0起動処理にジャンプ RJMP TMR0 ;----------------------------------- ;カウンタが2の場合の処理「ハートを」 ;----------------------------------- MASSAGE2: ;PB3をLowにする LDI R_TEMP1, 0B00010111 OUT PORTB, R_TEMP1 ;カウンタを1増やす INC COUNTER ;タイマ0起動処理にジャンプ RJMP TMR0 ;----------------------------------------------------------- ;カウンタが4の場合の処理「シェアリンエナジー!」「コメー!」 ;----------------------------------------------------------- MASSAGE3: ;PB4をLowにする LDI R_TEMP1, 0B00001111 OUT PORTB, R_TEMP1 ;カウンタを0にする LDI COUNTER, 0x00 ;タイマ0起動処理にジャンプ RJMP TMR0 ;--------------- ;メインルーチン ;--------------- MAIN: CLI ;全割り込み禁止 ;PORT設定 ;メッセージ出力はPB0, PB2, PB3, PB4を使用。初期値は全て出力でHighにする ;PB1はINT0として使用するので、初期値は入力でHighにする ;PB5は使用しないので初期値は出力でLowにする ;ビット6, ビット7は予約済み(未使用ビット)なので0にする LDI R_TEMP1, 0B00111101 LDI R_TEMP2, 0B00011111 OUT DDRB, R_TEMP1 OUT PORTB, R_TEMP2 ;外部割り込み0関連レジスタ設定 IN R_TEMP1, MCUCR ;MCU制御レジスタ読み込み SBR R_TEMP1, (1<<ISC01) ;ISC01ビットをセット (INT0がHigh→Lowで外部割り込み0が発生) OUT MCUCR, R_TEMP1 ;セットしたビットを制御レジスタに書き込み IN R_TEMP1, GIMSK SBR R_TEMP1, (1<<INT0) OUT GIMSK, R_TEMP1 ;外部割り込み0許可 ;タイマ0割り込み許可 IN R_TEMP1, TIMSK0 ;タイマ/カウンタ0割り込みマスク・レジスタ読み込み SBR R_TEMP1, (1<<TOIE0) ;TOIE0ビットをセット (タイマ0割り込み許可) OUT TIMSK0, R_TEMP1 ;セットしたビットをマスク・レジスタに書き込み ;カウンタに0をセット LDI COUNTER, 0x00 ;ユーザーフラグに0をセット LDI U_FLAG, 0x00 SEI ;全割り込み許可 ;-------------------------------- ;外部割り込み0待機及びタイマ0待機 ;-------------------------------- MAIN01: ;外部割り込み0の監視 SBRC U_FLAG, B_INT0 RJMP EXT_INT01 ;タイマ0オーバーフロー割り込みの監視 SBRC U_FLAG, B_IOVF0 RJMP IOVF01 RJMP MAIN01
今回も自分は初心者なのでいろいろ躓いた。始めは全く動かなくて、原因を調べた。
そこで判明したのは、ATtiny13Aは機能を絞っているからか、マイコンの機能を設定するI/Oレジスタはアドレス0x0020~0x005Fの範囲にしかない。汎用レジスタとこれらのレジスタ間のデータのやり取りは全てIN、OUT命令で出来るということだが、この事を見過ごしてATmega328Pの時の癖で外部割り込みの設定にLDS、STS命令を使っていた。なので、外部割り込みが全く設定出来ていなかった。
次に、スイッチを押すとPB0がLowになるが、そのままそこで止まってしまうという症状が出た。
当初はユーザーフラグ(マイコンには元々用意されていない自分で勝手に作ったフラグ)は使用せず、割り込みが発生したら直接カウンタを判定して指定したラベルにジャンプするように記述していたのだが、それだと次の割り込みが発生しない。
何故なら、AVRは割り込みが発生したら自動的に全割り込み禁止にし、RETI命令で割り込み処理から割り込み前の処理に戻る時に再び全割り込みが許可される。
また、割り込みが発生するとプログラムアドレス(現在実行しているプログラムが何行目かみたいなもの)を一旦マイコン内部のメモリに保存して、RETI命令が実行される時にこのメモリの値を参照して元のプログラムに戻り、同時にメモリに保存されたアドレスの値を消去しているが、これも実行されないのでおかしな事になる。
割り込み処理から他の処理にジャンプするときにこれらRETI命令がやっている事を手動でプログラムすればこの問題は解決すると思ったが、今後のメンテナンス性等が悪くなりそうだったので、ユーザーフラグを用意してちゃんとRETI命令を記述するようにした。
これで、遂にマイコンが意図した通りに動くようになったので、いよいよ回路設計だ。
回路設計・組み立て編に続く。
]]>シリーズ19作目で17代目のデリシャスパーティープリキュア。今回は早い段階で追加プリキュアの登場を匂わせ、おもちゃ等いろんな方面から情報を小出しにして全く隠す気がなかった。自分はスイート♪プリキュアのキュアミューズの情報がシリーズ後期までかなり厳重に隠されていたのが印象的なので、今回の追加プリキュアの情報を全く隠す気がない公式の動きにまた別の強い印象を覚えることになった。
ここ数週間はキュアフィナーレの登場で界隈が盛り上がっているところではあるが、今回はキュアプレシャスについての話だ。
ゆいがキュアプレシャスに変身する時の始めの方で、ゆいとコメコメの「にぎにぎ」「コメコメ」「ハートを」「コメコメ」「シェアリンエナジー!」「コメー!」というやり取りがある。
これは仕草としてはゆいがおむすびを握っているものなのだが、この変身シーンを何度か見ている内に、握る→握るといえばハンドグリップ→ハンドグリップを握ってゆいの声で「にぎにぎ」としゃべったら面白そう!と何故か突然閃いてしまった。閃いてしまったので作るしかない。どうやって作るか考えてみた。
今回は秋月電子通商で売っている音声録音再生IC「HK829」を使用する。最大4メッセージまで録音でき、このようなおもちゃを作るのに持って来いだ。このICにあらかじめTVの音声を録音しておいて、スイッチを押す度に順番にメッセージを再生するような回路を作れば良い。
データシートの図によれば、通常はメッセージ毎にそれぞれスイッチを取り付け、再生したいメッセージに対応したスイッチを押して使用する。4メッセージを再生するならスイッチは4つ必要になる。
しかし、今回は「ハンドグリップを握るたびに指定された順番でそれぞれのメッセージを流す」という仕様で、ハンドグリップを握った事を検知して装置が反応するということで「スイッチは一つ」だ。これはこのICだけでは実現できない。こういった機能を出来るだけ単純な回路で実装しようと思ったら、マイコンの登場だ。
HK829のデータシートを更に読み込むと、メッセージ再生用のピンが16ms以上Low(GND電位)になるとメッセージが再生されるとある。HK829のM0~M3ピンの周辺回路を下図に示す。
通常はこれらのピンの電位はHighで、スイッチを押した時だけLowになる。このスイッチと同じ役割をマイコンにやらせることも出来る。HK829メッセージ再生用の4つのピンにそれぞれマイコンのGPIOピンを繋ぎ、一つしかないスイッチもマイコンに繋ぎ、スイッチを押す度に指定した順番でマイコンの対応するメッセージのピンを16ms以上Lowにすれば良い。下図がマイコンとHK829を接続した状態だ。
以上を踏まえて、必要な部品を選定する。
まずは音声録音再生IC「HK829」。これが無いと始まらない。
また、マイコンが必要だ。上記のような単純な動作は多分どんなマイコンでも出来るが、自分はAVRの開発環境が整っているのでAVR ATtiny13Aを使用する。スイッチ入力を検知するための外部割り込み機能があり、それに使用するピンを除いてもGPIOが5つある。後はタイマも内蔵されている。つまり、必要なマイコンの機能は外部割り込みとタイマだ。後はスイッチが押された回数を記憶する機能。これらは多分殆ど全てのマイコンが持っていると思うので、自分の知識や環境に合わせてPICでもArduinoでも好きなマイコンを使えばいいと思う。
後、自分は元々ATmega328Pならいくつか持っていたので別にそれでもいいのだが、外部割り込みとタイマしか使わない割にはオーバースペックであり、DIP28ピンもあり基板上で場所を取る。ATtiny13AはDIP8ピンで小さく、必要な機能が揃っているという事で選定し、新たに購入して用意した。
また、スイッチ入力はチャタリングが付き物だが、対策としてシュミットトリガインバータとコンデンサを組み合わせた回路が有効だ。しかし、ロジックICで定番の74HCシリーズではシュミットトリガインバータが内部に6個も実装されているが実際に使うのは1個であり、DIP14ピンで場所も取る。これも秋月電子通商に1回路入りのTC7S14FというSOT23パッケージに似た米粒より小さいとても小型の物があったのでそれを使用した。折角小さいのだが、逆に小さすぎて使いづらいので変換基板でDIP6にしてしまったが…。それでも74HCシリーズのDIP14ピンに比べれば大分小さい。
それと、後に必要無い事が判明してしまうのだが、HK829で直接スピーカーを鳴らすのは難しいかと思い、定番のアンプICである386を使用した。
マイコンプログラム編に続く。
]]>この記事の話は2021年3月13日現在の土休日ダイヤにのみ言及している。平日はホームライナー静岡36号が運転されないので注意。
豊橋7時48分発普通浜松行き(土休日の列車番号は5920M)は普通列車だが、特急型車両の373系で運転される。
この列車は浜松に8時26分に到着し、引き続き東海道本線の静岡方面へ土休日は最短で浜松8時29分発ホームライナー静岡36号(列車番号5386M)が接続する。ホームライナーなので、当然車両も373系だ。
1年位前のダイヤではこのホームライナーよりの後に浜松始発の普通熱海行きがあり、ホームライナーに乗ったとしても結局静岡でこの熱海行きに乗り換えることになる。当時のダイヤでは熱海へはそれに乗って行くのが最速だったので、ホームライナー静岡の存在は知っていたがわざわざ追加料金を払い乗り換え回数を増やしてまで乗ることは無かった。
豊橋から浜松までの普通列車が373系で、間3分で浜松始発のホームライナーの存在…これは、ホームライナー浜松3号が20時10分に浜松に到着し、そのまま普通豊橋行きに切り替わって20時12分に浜松を発車する事を彷彿させる…。時刻表で見た瞬間、そう思った。
しかし、実際に乗ってみると、豊橋発の5920Mは浜松駅の3番線に到着し、ホームライナー静岡36号は1番線からの発車であった。つまり、373系から別の編成の373系へ乗り換えが必要であった。
急いでざっと見た感じ、浜松駅の上りホームに乗車整理券売機は見当たらなく、3分の乗り換え時間で改札の外に出て乗車整理券を買う事は不可能なので、乗車整理券は乗車してから車掌から購入した(周りの大半の乗客もそうであった)。
青春18きっぷで東海道本線の静岡県区間下りを快適に移動するのは、沼津18時31分発ホームライナー浜松3号にのり、そのまま373系で豊橋まで行けてしまう。
同区間の上りの移動は浜松で乗り換えが必要なのが難点だが、豊橋7時48分発普通浜松行きと浜松8時29分発ホームライナー静岡36号を推したい。下りと比べて距離は若干短くなるが、豊橋から静岡まで373系で快適に移動出来る。
]]>マイコン初心者の筆者が、作業を始めてから3日くらいかかり、何とか一応意図通り動くところまで漕ぎ着けた軌跡の話。
筆者は30代半ばにして、初めてマイコンを触った。なんとなくのイメージだが、(趣味で)人がマイコンを初めて触るという経験をするのは10代が多い気がする。
10年くらい前にマイコンを始めようと思って買った参考書(そのまま10年放置状態だったがw)がアセンブリ言語での説明で、今回の開発はこの本を大いに参考にしたのでこのブログも説明は基本的にアセンブリ言語で行う。筆者が参考にした本を下記に示す。以下「参考書」という。
筆者は、学生時代に電気、電子回路のことは一応学んだが、殆ど忘れていて基本的な事しか覚えていないというレベル。例えば、トランジスタ、ダイオード、コイル、コンデンサ等の素子がどういう動きをするか位は大まかに分かる。電気に関する簡単な計算は出来る。しかし、複素数、微分、積分、行列を使うか、それ以上のレベルの計算のやり方は忘れた。
プログラミングのレベルとしては、高級言語プログラミングの基本的な事は分かる。例えばExcel VBAでちょっとしたアプリケーションは作れる。しかし、C言語は学生の頃に少し習ったが、殆ど完全に忘れている。何故わざわざアセンブリ言語で開発しているのかと言えば、昔たまたま買ったマイコンの参考書がアセンブリ言語で書かれていて、C言語を勉強するのが面倒臭いというのが大きいw
なので、想定する読者も基本的な電気の知識はあり、高級言語プログラミングの基本的な事は知っている(変数、条件分岐、ループ等その辺の概念は分かる)と言う前提で話を進める。
ロータリーエンコーダの出力をマイコンで受け取るにはどうすれば良いかいろいろ検索したのだが、上位にヒットするのは大抵ArduinoかPICの情報で、言語はSketchと呼ばれるC言語に似たもの(PICはC言語)だ。AVRのアセンブリ言語で開発しようとしてる筆者にはプログラミングとしてはあまり参考にならない。
この記事は、AVRをアセンブリ言語で開発し、ロータリーエンコーダの出力を受け取るにはどうすればいいか示すのを目的にしている(そんな需要あるのか?w)。今回はロータリーエンコーダがCWかCCWのどちらの方向に回ったかだけを判定し、速さなどは問わないものとする。電子ボリュームやセレクタとして使うことを想定している。以下に、ブレッドボードで組んだ回路の写真と全体回路図を示す。
写真1 回路をブレッドボードに組んだ写真
図1 全体回路図
ATmega328P(以下、マイコンと言う)のPORTBの全ビットにLEDを接続してある。PORTBの各ビットが変化すると、それに対応したLEDの点灯状態も変化する。始め、LEDに直列に接続している抵抗は1kΩにしていたのだが、眩し過ぎるので途中で10kΩに変更した。これでも十分に明るい。点灯しているか否か分かれば良いだけならもっと暗くても大丈夫だ。
LEDのカソード側がマイコンのピンに繋がっているので、ピンがLになると点灯する。参考書にも、マイコンのHの出力電流よりもLの入力電流の方が大きいので、ピンに接続するトランジスタやLEDはLになった時に動作するようにした方が良いと書いてあった。
ロータリーエンコーダは千石電商で売っている100円程度の安い物で、インクリメンタル方式でクリックありの物を使用した。インクリメンタル方式のロータリーエンコーダを回すとどのように出力するか分からない人は、各自で検索して欲しい。
ロータリーエンコーダのA相をトランジスタで組んだNOTゲートを通してマイコンの外部割りこみ0のピンINT0に接続する。
ロータリーエンコーダのB相は10kΩの抵抗でVccからプルアップしてマイコンのPINC0に接続する。
ロータリーエンコーダを回すと、A相の出力がH→L、L→Hと変化し、NOTゲートを通してマイコンのINT0はL→H、H→Lと変化する。
何故わざわざNOTゲートを通すのかは、後述の筆者による勘違いのためなのだが、上手く動作した後にこのNOTゲートを外したらまた上手く動かなくなったので、最終的にこのままにしてある。多分NOTゲートを外しても抵抗とコンデンサの値を適切に設定すれば上手く動くと思うがメンドクサイ。
この時、マイコンのINT0がH→Lと変化したタイミングで外部割りこみを発生させ、ロータリーエンコーダのB相が接続されているPINC0の状態により、回転方向がCWかCCWか判定する仕組みだ。
回路設計において、最終的にうまくいった要因はマイコンのピン4 INT0とGND及びピン23 PC0とGND間に接続したコンデンサC0とC1による。これを接続しないとロータリーエンコーダのチャタリングノイズを拾ってしまい、右に1クリック回した時に「右に1クリック、左に1クリック回した」動作をし、左に1クリック回すとその逆になったりする。
酷いと左に1クリック回したはずなのに右に1クリック回した動作をしたり、左に速く数クリック回すと右に数クリック回した動作をしたりと全く安定しなかった。今回の開発は3日くらいかかったが、そのうち2日以上はこの原因を取り除くのに費やしてしまった。
マイコンでロータリーエンコーダを使用する際は、ノイズ対策のコンデンサが必須。これ重要。
ノイズ対策としてコンデンサを接続すると良いという情報は下記の記事を参考にした。Arduinoの記事だが、ロータリーエンコーダからマイコンまでの回路設計と言う意味では非常に参考になった。
第二十一項 ロータリーエンコーダとノイズ対策・割り込み – kusamura
この記事ではノイズ対策として0.01μFのコンデンサを入れているが、手元に無かったので試しに0.047μFのコンデンサを入れてみた。しかし、あまり変化が無かったので、思い切って0.47μFのコンデンサにしてみたところ、容量が大きすぎて誤動作すると言うこともなく、寧ろ非常にスムーズに動作するようになった。
レジスタとは何か、割り込みとは何かというレベルから説明し始めると、それこそ一冊の本が出来上がるくらいの分量になってしまうので、そこら辺を正確に知りたい人はネットや本で調べて欲しい。
筆者はアセンブリ言語が良く分からない内は高級言語の知識に絡めて以下のように理解した(アセンブリ言語の正確な概念から鑑みれば甚だ不正確な表現が含まれるのは承知いただきたい)。
高級言語の変数のように使用する。ただし、入れられるデータ型はVisual BASICで言うところのバイト型。一つのレジスタには0~255の値までしか代入出来ない。AVRのR0~R15は制約が多いので、初心者の内は特に理由がない場合はR16から使った方が良い。
きっと、マイコンに慣れてきて汎用レジスタの中にも特殊な役割があるものを知ったり、数が足りなくなったりしたら自然とR0~R15を使うことも出てくるだろう。
マイコンの入出力や各機能を使用するのに用いる。ある意味高級言語の関数に近い。例えば入力のPINBというレジスタは、PBx(xは0~7の整数)のピンの値が変化すると同様に変化し、それを読み込むことでピンの値を読み取る事が出来る。
IN R16, PINB
例えば上記のアセンブリ言語のソースは、高級言語風に表現すれば
R16 = PINB()
R16という変数が存在して、PINB()はマイコンのピンB群の状態を返す関数で、その値を変数に代入する…みたいな意味だ。勿論、実際のアセンブリ言語のソースでこのように表記してもエラーになるので注意。
このレジスタに値を代入すると、定められたマイコンの機能が起動したり、機能の動きを変える事が出来る…とイメージすると良いと思う。
高級言語はCPUの挙動やメモリ構造をあまり意識せずに使用出来るが、マイコンをアセンブリ言語で開発すると言うのはマイコンと言うCPUの動きそのものを命令することなので、大変だがマイコンのメモリ構造は理解していないと使いこなすことは出来ない。ここでは深くは触れないので、本やネットで頑張って調べよう。
標準IOレジスタも拡張IOレジスタも、マイコンの機能を起動したりその動きを変えたりと役割は殆ど同じ。違いは、マイコンの中でどのメモリアドレスに配置されているかという部分だ。
マイコンはメモリのあるアドレスに値を代入するという操作一つを取っても、アドレス群ごとに命令が異なる。高級言語のように何でも「変数 = 値」と代入は出来ないのだ。
何故なのかとか考えてはいけない。そういうものなのだ。
プログラムのフローチャートを下図に示す。
図2 フローチャート1/2
図3 フローチャート2/2
要約すると、ロータリーエンコーダが回されたらA相からの出力変化により外部割りこみ0を発生させ、B相からの出力をPINC0で読み取りCWかCCWか判定し、PORTBに接続した8つのLEDを2進数表示で点灯させる仕組みだ。
右に回すと数字が増えていき、255クリック目で全点灯状態になる。
左に回すと数字が減っていき、全点灯状態から255クリック目で全消灯状態になる。
チャタリング防止の為に回路にコンデンサを入れているが、ソフトウェアでもA相の出力変化からPINC0の値を読み取るのに約2msの遅延時間を入れて対策している。
プログラム全文を下記に示す。
;ロータリーエンコーダを回してLED点灯を変化させるプログラム .include "m328pdef.inc" ;汎用レジスタ .def STACK = R16 .def R_TEMP1 = R17 .def R_TEMP2 = R18 .def R_FLAG = R19 ;定数定義 .EQU B_INT0 = 0 ;外部割り込み0 .EQU B_TMR0 = 2 ;タイマ0 .EQU B_RTY_ENC = 0 ;ロータリーエンコーダがCWかCCWか判定するビット .CSEG RJMP MAIN .ORG 0x0002 RJMP EXT_INT0 ;外部割り込み0 .ORG 0x0020 RJMP TMR0 ;タイマ0 ;外部割り込み0 EXT_INT0: ;ステータス・レジスタの内容を退避 IN STACK, SREG ;外部割りこみ0発生フラグセット SBR R_FLAG, (1<<B_INT0) ;ステータス・レジスタの内容を復帰 OUT SREG, STACK RETI ;タイマ0 TMR0: ;ステータス・レジスタの内容を退避 IN STACK, SREG ;カウンタ停止 LDI R_TEMP1, 0x00 OUT TCCR0B, R_TEMP1 ;タイマ0割り込み発生フラグセット SBR R_FLAG, (1<<B_TMR0) ;ステータス・レジスタの内容を復帰 OUT SREG, STACK RETI ;メインルーチン MAIN: CLI ;全割り込み禁止 ;PORT設定 LDI R_TEMP1, 0B11111111 LDI R_TEMP2, 0B11111111 ;全ポートを1にしてLEDを消灯させる OUT DDRB, R_TEMP1 OUT PORTB, R_TEMP2 LDI R_TEMP1, 0B11111110 ;PORTCのピン0を入力にする LDI R_TEMP2, 0B00000000 ;PORTCのピン0をプルアップなしにする OUT DDRC, R_TEMP1 OUT PORTC, R_TEMP2 LDI R_TEMP1, 0B11111011 ;PORTDのピン2はINT0なので入力にする LDI R_TEMP2, 0B00000000 ;PORTDのピン2をプルアップなしにする OUT DDRD, R_TEMP1 OUT PORTD, R_TEMP2 ;外部割り込み関連 レジスタ設定 LDS R_TEMP1, EICRA CBR R_TEMP1, (1<<ISC00) SBR R_TEMP1, (1<<ISC01) ;INT0がH→Lで割り込み発生 STS EICRA, R_TEMP1 SBI EIMSK, INT0 ;外部割り込み0許可 ;タイマ0関連 レジスタ設定 LDS R_TEMP1, TIMSK0 SBR R_TEMP1, (1<<TOIE0) STS TIMSK0, R_TEMP1 ;タイマ0割り込み許可 ;全割り込み許可 SEI MAIN01: ;外部割り込み発生フラグ OFF ;タイマ0桁溢れフラグOFF CLR R_FLAG MAIN02: ;外部割り込み判定 SBRC R_FLAG, B_INT0 RJMP MAIN03 ;外部割り込み0が発生 RJMP MAIN02 MAIN03: ;タイマ0セット(外部割り込みが発生してから2ms待つ) LDI R_TEMP1, 0x02 ;プリスケーラ8 OUT TCCR0B, R_TEMP1 LDI R_TEMP1, 0x06 ;割り込み時間2ms OUT TCNT0, R_TEMP1 MAIN04: ;タイマ0桁溢れ判定 SBRC R_FLAG, B_TMR0 RJMP MAIN05 ;タイマ0桁溢れ RJMP MAIN04 MAIN05: ;ロータリーエンコーダの回転方向を判定 SBIC PINC, B_RTY_ENC ;PINCのビット0が1ならCW、0ならCCW(PINCのビット0が0なら1行スキップ) RJMP MAIN10 ;ロータリーエンコーダの回転方向がCWの場合 RJMP MAIN20 ;ロータリーエンコーダの回転方向がCCWの場合 MAIN10: ;ロータリーエンコーダの回転方向がCWの場合 IN R_TEMP1, PORTB CPI R_TEMP1, 0x00 ;PORTBが0かどうか判定 BREQ MAIN01 ;PORTBが0なら何もせずにメインルーチンのループに戻る DEC R_TEMP1 OUT PORTB, R_TEMP1 ;PORTBが0でないならデクリメントする RJMP MAIN01 MAIN20: ;ロータリーエンコーダの回転方向がCCWの場合 IN R_TEMP1, PORTB CPI R_TEMP1, 0xFF ;PORTBがFFかどうか判定 BREQ MAIN01 ;PORTBがFFなら何もせずにメインルーチンのループに戻る INC R_TEMP1 OUT PORTB, R_TEMP1 ;PORTBがFFでないならインクリメントする RJMP MAIN01
思い返せば初歩的な部分だと思うが、つまずいた部分としては…
LDI R_TEMP1, 0B11111011 ;PORTDのピン2はINT0なので入力にする LDI R_TEMP2, 0B00000000 ;PORTDのピン2をプルアップなしにする OUT DDRD, R_TEMP1 OUT PORTD, R_TEMP2
このPORTDのピン2を入力に設定する部分。ネットを検索すると、入力に設定しなくてもピンの値が変化すれば割り込みが発生すると書いてあるが、出力に設定しているピンの値を変化させるには、普通はソフトウェア的な処置が必要だろう。
今回のように、ロータリーエンコーダやスイッチで値を入力する場合は、やはり外部割りこみを兼ねるピンは入力に設定するべきだ。
もう一つ、外部割りこみ制御レジスタの部分。
;外部割り込み関連 レジスタ設定 LDS R_TEMP1, EICRA CBR R_TEMP1, (1<<ISC00) SBR R_TEMP1, (1<<ISC01) ;INT0がH→Lで割り込み発生 STS EICRA, R_TEMP1 SBI EIMSK, INT0 ;外部割り込み0許可
ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
EICRA | – | – | – | – | ISC11 | ISC10 | ISC01 | ISC00 |
表1 EICRAレジスタの構造
ISC01 | ISC00 | 割りこみ発生条件 |
0 | 0 | INT0がLow |
0 | 1 | INT0がLow→High、High→Low |
1 | 0 | INT0がHigh→Low |
1 | 1 | INT0がLow→High |
表2 外部割りこみ0発生条件
始め、このレジスタのISC01、ISC00の部分を「01」と設定すると、「INT0がLow→High→Lowとなった時に外部割り込み0が発生する」とずっと勘違いしていた。つまり、形が「凸」のような波形を受信した時に割り込みが発生すると思っていた。
実際にはISC01、ISC00を「01」と設定しているとINT0がLow→Highになった時も、High→Lowになった時も、つまり波形の立ち上がりと立ち下がりの両方で外部割り込み0が発生する。
ロータリーエンコーダを1クリック回した時に外部割りこみ0の動作を2回してしまうのは何故か分からない原因の一つだった。
回路が上手く動かなくてハマっている2日余りのうち1日余りはこのレジスタ設定の勘違いに気付いていないからだった。
ロータリーエンコーダA相の出力とマイコンのINT0の間にNOTゲートを入れたのもこの勘違いによるもの。
ロータリーエンコーダはチャタリング対策として、マイコンの入力端子とGNDの間にコンデンサを接続しよう。
ロータリーエンコーダやスイッチで外部割りこみを発生させるときは、そのピンは入力に設定しよう。
AVRの外部割りこみに「波形が立ち上がって立ち下がった時」という発生条件は無い。
以上、マイコン初心者が実験でこの知見を得るのに3日かかった…というお話でした!w
これで、ロータリーエンコーダを値セレクタとして使う準備は整ったので、次の段階に進める。
]]>使えない。
エアブラシを使うときに必須のツールクリーナー。典型的な有機溶剤で、消防法の危険物の分類としては第4類第1石油類である。
ツールクリーナーは1l辺り1500~2000円位して高いなと思って調べたら、アセトンで代用している人も結構いるらしい。アセトンはヒロバ・ゼロで税込で1lが770円、2lが1210円、4lが1320円、18lが5073円で売っていて4lか18lなら結構安い(2020年9月15日現在)。そして、アセトンも第1石油類(水溶性)である。
ホワイトガソリンも第1石油類なので、引火点、沸点等の特性が近い筈…これはキャンプのガソリン機器に使えるのでは?と思った。
しかし、ネット上でガソリンの代わりにアセトンを使ってみたと言う情報は自分が検索した範囲では見つからなかったので、自分で確かめてみよう!と思った。
何故そんな事をするかと言えば、ホワイトガソリンが高い、これに尽きる。
コールマン純正のホワイトガソリンは4lで3000円以上する。自動車用のレギュラーガソリンは時期にも依るが高くても1lで150円程度と考えたら、驚く程高価だ。後者は価格の半分以上税金で、前者は消費税程度しかかかっていないのにも関らずだ。
そこでネットで調べてみると、一斗缶で6400円~8100円程度のものが見つかる(2020年9月15日現在)。
しかし、アセトンの一斗缶はこれより更に安い上に、エアブラシ洗浄用としても使える。ホワイトガソリンは自分は燃料としてしか使い道が無い。
始めは一瞬火が着くが、すぐに消えてしまう。挙動的に空気過多の様子。バーナーヘッドの段を減らしてやってみてもうまくいかない。
挙句の果てには、何日か経った後に通常のホワイトガソリンを入れて使おうとしたらポンピングのゴムが劣化し、チェックバルブも故障した模様でポンピングしてもタンクの圧がすぐに抜けてしまう。アセトンに侵されてポンピングやチェックバルブのゴムが駄目になったものと思われる。後でポンピングのゴムとチェックバルブ買って交換しないと…orz
ホワイトガソリン機器にアセトンを入れたらダメ、ゼッタイ。
燃え方がどう見ても空気過多(燃料が薄すぎる)という状態だったので、ガソリン燃焼の空燃比とアセトン燃焼の空燃比が違いすぎるのだろう。
ホワイトガソリンは、缶の表記に依ればベンゼン、トルエン、キシレン、n-ヘキサン、エチルベンゼンが含まれているらしい。
$$\require{mhchem}$$
$$\ce {2C6H6 + 15O2->12CO2 + 6H2O}…ベンゼン \\
\ce {C6H5CH3 + 9O2->7CO2 + 4H2O}…トルエン \\
\ce {2C6H4(CH3)2 + 21O2 ->16CO2 + 10H2O}…キシレン \\
\ce {2C6H14 + 19O2 ->12CO2 + 14H2O}…n-ヘキサン \\
\ce {2C6H5C2H5 + 21O2 ->16CO2 + 10H2O}…エチルベンゼン $$
$$
\ce {CH3COCH3 + 4O2 ->3CO2 + 3H2O}…アセトン$$
上5つがホワイトガソリンの成分、下1つがアセトンの燃焼式だ。この式から分かるように、それぞれの物質1に対して必要な酸素はベンゼン7.5、トルエン9、キシレン10.5、n-ヘキサン9.5、エチルベンゼン10.5と、それぞれの成分の含有率が不明な為正確ではないがホワイトガソリン1に対しておよそ10の酸素が必要であろう。対して、アセトン1に対して必要な酸素は4と、2.5倍もの開きがある。これでは、燃焼不良を起こしても納得と言うものだ。
繰り返すが、ガソリン機器にアセトンはダメ、ゼッタイ。
まともに燃えない上にゴムの部品が駄目になり故障する。
]]>