完成したブレッドボード
みなさん、こんばんは。 トドお父さんです。
前回『ELM-ChaNさんの回路内導通チェッカを作りました』では、ATTINY13AのAVRプログラムを
ATIMEL STUDIO上で展開して、ATTINY85との相違点を修正して動作にこぎつけました。
今回は、いつもの流れでNew ATTNY 0-シリーズのATTINY402/202に移植したので、そのレポです。
特にATTINY202は、秋月でATTINY402が安く売られるようになって余っているので、新たな用途が
でてくると助かるという大人の事情もあります (爆)。
ATTINY402/202のプログラミングについては、前にIRリモコンをTINY202で作ってGithubに
アップしていますので、その記事も参考にしながら作っていきました。
また、今回は新たな取り組みとしてAIのChatGPT先生にプログラム作成を手伝ってもらいました。
これがなんか新たな世界が切り開けたようで、プログラムが3倍(10倍?)楽になりますね。
回路図
回路図はこちらになります。今回はCPUがATTINY402/202に変わったのでIOが少し変わります。
PA0 UPDIは予約ピン、Beep音を出すTCA0カウンタのWO0出力ピンもPA3(ピン7)固定です。
それ以外は、自由に配置できますね。
ATTINY402/202は秋月で取り扱いがあり、最初はTINY202しか取り扱いがなかったのですが、
最近はメモリ2kから4kに増量したTINY402も扱うようになりました。
どちらも8ピンのSOPパッケージしかないのですが、DIP変換基板と細ピンを使ってブレッドボード
や2.54mmピッチのユニバーサル基板に実装することができます。
値段はどちらも70円で変わらないので、最近はTINY402ばっかり使っています。
前はレガシーAVRのTINY85をよくつかっていましたが、今は240円と高値の花。
DIP 8ピンがあるのが嬉しいが、それ以外の理由ではもう使う必要はないかもしれませんね。
そこで、今回のようにプログラムの移植が必要となるということですね。うん、納得(かな?)
もうすでに、導通チェッカはTINY85で作ってしまいましたので、今回はブレッドボードで
移植したプログラムの動作を確認しますね。
部品リストも前に作った下記の通りです。CPUがATTINY202(とDIP変換基板、ピン)に
変わるだけです。
部品リスト
今回のプログラムの説明をする前に、今回作成したプログラムは下記のgoogleドライブに入れました。
解凍してから、ATMEL STUDIOで開いて下さいね。
さて、ここからは備忘録を兼ねてプログラムの説明です。
ChaNさんのプログラムは、さすがAVRソフトの大御所だけあってマニアックな表記が多くあり、
正直解析に苦労しましたが、ChatGPT先生(?)の助けもあって移植を行うことができました。
限定版ですが、無償で提供してくれるOPEN AIさんには感謝です!
1.クロックの設定
ChaNさんは、TINY13A(85)でWDT用の128kHzクロックを選択していましたが、New ATTINY
シリーズには128kHzがありません。仕方がないので、RTC用の内蔵32kHzと、内蔵16MHzを最大
分周比1/48で分周した333kHzの2種で条件付きコンパイルを行えるようにしました。
消費電力的には、もちろん32kHzの方がいいのですが、ADCの仕様にクロックは50kHz 以上と
あったからです。AD変換器のクロックはメインクロックを最低でも1/2分周するので、実際の
メインクロックは100kHz以上である必要があります。
でも仕事で使うわけではないので、アマチュア精神で使えたら使うようにしましょう(大汗💦)
CLKCTRLレジスタでは、20MHzを選ぶ選択しかできないので、16MHzにする場合は
こちらの記事を参考にFUSE2を書き換えて下さいね。
20MHzのまま使うのであれば、417kHz になるので、F_CPU を416666 に書きかえが必要です。
可能であればms_delayのタイミングも、現物合わせ!で n の値を修正してください。
2.GPIOの初期設定

レガシーのTINY85とTINY402等のNew ATTINYシリーズとの違いはレジスタの設定が構造体で
パラメータは構造体のメンバーで読みだせることです。
例えば PORTA.OUTと入力すると、OUTのところで予測変換して使えるパラメータがでます。
STマイクロのSTM32CubeIDEのような使いやすさになりました。
/* set pin 7 of PA3, pin 5 of PA2 as output */
PORTA.DIR |= LED_PIN | TONE_PIN | Bias_PIN ; // set PIN to Output
まず、ポートの入力、出力を決めて、
/* set PA2 and PA3 LED OFF */
PORTA.OUTSET = LED_PIN; // Visible LED -> "H" (OFF)
PORTA.OUTCLR = TONE_PIN | Bias_PIN; // Bias_PIN & TONE PIN -> "L" (OFF)
それから、出力ピンに出力する ”1”、”0” を指定します。
元プログラム レガシーAVRでの、
DDRB = 0b01011; /* BP0=LED, PB1=BZ, PB2=SW, PB3=Bias, BP4=ADC */
PORTB = 0b00101; // LED OFF, Disable bias circuit
とかの表記に比べると可読性がかなりいいんじゃないかと思います。
PINnについては、分かりやすく事前に #define LED_PIN PIN2_bm とかの名前を
つけていますが、もちろん、PORTA.OUTSET = PIN2_bm; でもOKです。
PORTA.OUT |= PIN2_bm; は、 PORTA.OUTSET = PIN2_bm; と同じです。
PORTA.OUT &= ~PIN2_bm; は、 PORTA.OUTCLR = PIN2_bm; と同じです。
でも、右側のほうが操作の意図がわかりやすいですよね?(AI談w オイラも同感です)
閑話休題:プログラムの説明に戻ります。
PORTAにPINnCTRLとあります。
これはレガシーAVRにはないレジスタで、各ピンの割り込み許可と割込みモードを設定します。
また、各ピンにプルアップあり/なしを設定します。SW1ピンにプルアップを設定し、同時に
他のBitには”0”を書いているので、割り込みDisableになります。
後に説明するADCのアナログ入力のときは、0x4 INPUT DISABLE をここで選択します。
3.ADC(ADコンバータ)の初期設定
ここで、ADコンバータの初期化を行います。

PINnCTRLにPORT_ISC_INPUT_DISABLE_gc はディジタル入力を無効にする処理です。
アナログ入力として使うので、ディジタル系からノイズ等の影響をさけるためのようです。
前に言ったように、ADクロックは50kHz以上 (1.5MHz以下) の仕様になっています。
32kHzクロックを使ったときは16kHzになってしまいますが、消費電力低減を優先して、
0~1024の0~5付近でのAD値が実使用上 問題がなければ、これで使ってみたいと思います。
ADC0.CTRLBの設定:サンプルを複数回足し合わせて、ノイズを低減する機能 → つかわない
あとは、REF電圧を内部1.1Vにして、マルチプレクサでAIN7(PA7)を選択した後に、
10bitモードで、ADC_ENABLEでADCを有効にするところまでです。
このように、CTRLAのFreerun bitを1にしなければ、シングル変換モードになります。
あとはプログラムの中でAD変換が必要なところで、下記StartコマンドでADCを開始するだけです。
ADC0.COMMAND = ADC_STCONV_bm;
シングル変換モードなので、毎回Startコマンドを発行する必要がありますが、省電力になります。
元プログラムのように時間待ちにする手もありますが、今回は変換完了フラグを見るようにします。
4.TCA0(beep())の動作説明
指定した周波数でBeep音を出す処理です。
TCA0(16bit)を使って、高速PWMモード(シングルスロープ)を使います。
FREQモードを使う手もあるのですが、周波数に加えデューティを変えれば音量も変えられる
ので、オリジナルのChaNさんの意図通り、PWMで音を作成します。
TCA0を使ったPWM波形の作成方法については、MicroChip社のTM3217資料に詳しいので
興味があるかたは参考にしてください。
Beep音を出すための周期はカウンタのPerレジスタに入れた数値で変わります。
下のtone[] テーブルにあるように、32kHzクロックで4KHzを出すためには、8を設定します。
クロックをカウントするPerのカウント値が8になるとリセットされて、また0から始まります。
CMP0には、Perの半分の値 4が入っていて、4になると出力が”1”にトグルします。
”1”はカウントが8まで続き、カウンタがリセットされて0になると”0”になります。
これでCMP0の出力 WO0はデューティ50%の4kHzの出力が出るわけです。
その下の2640であれば、Perに12、CMP0には 6 が入り、2.67KHzになります。
なんちゃって雑な説明になりましたが、理解不足のためご容赦ください。
5.Sleepと割込みの動作説明
電源ONしてのmain()ルーチンで起動後に初期設定を行った後、Sleepしてスイッチが押されるまで
CPUは消費電力を落とした状態で待機する処理です。
スイッチが押されるとピン変化割り込みがはいり、CPUがWakeup起動するとともにLEDを点灯させ
起動のBeep音を短く(100mS)ピッと鳴らし、まずADCで電池電圧を測定します。
・電池電圧が2.4V以上… 通常動作に入ります。
・電池電圧が2.0V以上、2.4V以下 … LEDを点滅させてバッテリ電圧低下を警告します。
・電池電圧が2.0V以下… LEDが一旦光りますが、起動しません。Sleepに戻ります。
元ファーム通り、アナログコンパレータを禁止します。(デフォで禁止なら不要かも?)
SW1での割り込み許可をします。レベルでなく、BOTHEDGEがいいようです。
sei(); で話あり込み許可をします。
set_sleep_mode(SLEEP_MODE_PWR_DOWN); でSleepモードをパワーダウンにします。
for(;;) ループに入って、
まず、消費電力低減のためLEDとBiasをOFFにします。ADCも動作を停止します。
ここまで来たら、Sleep_mode(); を実行してCPUをSleepモードにします。
Sleepには、idleモード、standbyモード、Power downモードがあり、一番消費電力の少ない
Power downモードを選びます。
Power downモードではCPUおよびメインクロックは停止していますが、ピン変化割り込みやWDT、
BOD、RTC内のPITは生きています。(Sleep後0.1uAになったので、この辺は問題ないでしょう)
スイッチが押されるとピン変化割り込みが入り、CPUはWakeupして処理を開始します。
ISR() 割り込みハンドラは下記になりますが、今回は割り込みフラグをクリアするだけです。
主な処理は、Sleep_mode();以降の記述になります。
if (PORTA.IN & SW1_PIN )
continue;
SW1が”1”なら、ノイズ等で割り込みが入ったと判断して、for(;;)の先頭に戻ります。
LEDとBiasを”ON"にして、sdt=0、ONカウンタを ”0”にリセットします。
スイッチが離されたのを確認して、電池電圧の確認処理に入ります。
2Vの時は 5.6k/(27k+5.6k)x2V=0.34V になります。
Vref=1.1Vなので、0.34/1.1x1024=320 以下で、ONしない処理に入ります。
2.4Vの時は384になるので、ADの値が384以下、320以上でLEDをブリンクさせます。
(lvd=1という電圧警告フラグを立てる)
それ以上の値であれば、100mS間ピッという音を立てて通常動作にはいります。
do{ 以降は、AD値 adが0~4までの値であれば、tone[ad]の値でピーという音を出します。
ad=0なら、0~5Ω、ad=1なら5~10Ωといった具合です。
for(;;)ループを回るたびに、sdt++ で値が増し、10000になると停止処理を行い、
停止beep音を出した後に、またsleepにはいります。
またlvd=1という電圧警告フラグが立ていた場合は、ループ 20回毎にLED点滅がトグルして
電圧低下を知らせます。
ここまでの説明でソフトウェアの動作が分かりましたでしょうか?
最後に32kHzクロックでの消費電流を測りましたので、お知らせしますね。
起動動作時は0.6mA程度(LEDが0.3mA)、ピー音がなるとさらに0.1mAほど増します。
ちなみに333kHzクロックでは、0.9mAほどになりました(うちLEDが0.3mA)。
LEDの電流制限抵抗は4.7kΩの場合です。 10kΩくらいにしてもいいかもしれません。

Sleep時は0.1uA以下になりました。電子オルゴールでは1uA程度流れていたので、なにか違うの
かもしれません。電子オルゴールもSleepを見直してみましょうかね?

これで、おもちゃ病院の誰かから『作って!』といわれても、ドヤ顔で対応できますね!
プログラムの勉強にもなりました。
プログラム中にChatGPTに相談した内容は下のリンクにあります。
興味があれば、どうぞ。
それではみなさん、おやすみなさい。
関連記事