トドお父さん通信 -16ページ目

トドお父さん通信

北部九州在住 高BMI中高年のオタク趣味の活動記録

みなさんこんにちは。 トドお父さんです。

 

l ブレッドボードでの確認です

 

PZEM-004Tを使った電力計 前回でBluePill対応での作成完了しましたが、宿題が残っていました。

>あとは内蔵のRTCを動かして、SD Card基板を追加し、FatFsでSDカードに記録する処理を入れる

>ことができれば完璧なんですが、なにしろケースが小さいのでムリゲーそうです。

 

今回、なんとかSTM32F103 BuePillでの対応が終わりましたので報告しますね。

今回はPart1ということで、ブレッドボードで作成したプログラムの説明をします。

前回説明したプログラムから、今回 RTCとSDカード対応で変更した部分の説明をしますね。

 

まずはRTC対応から始めましょう。

STM32F103にはRTC機能が入っていて、BluePill基板には32.768kHzの水晶も実装されています。

SDカードでのログ書き込み機能を実装するなら、RTCでタイムスタンプも入れたいですよね。

まぁ、BAT端子に3V電源が必要ですから、CR2032ボタン電池などを付ける必要がありますが。

 

てなわけで、バッテリバックアップがついたRTC機能の検討です。

STM32duno対応のRTCライブラリとサンプルプログラムはここから持って来ました。

 

 

 

 

 

 

Simple RTCサンプルの先頭部です。

頭にあるのは、PCからコンパイル時に年月と時間情報をゲットするマクロです。

// RTC Initial data from PC  
static const char* mydate = __DATE__;
static const char* mytime = __TIME__;

 

RTCにこの時間を設定するための関数を作ります。 initDateTime()で下記設定を行います。

void initDateTime(void) {
  // コンパイル時のPC日時を解析して設定 (2000年基準)
  year = conv2d(mydate + 9); // 年
    // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
  switch (mydate[0]) {
    case 'J': month = (mydate[1] == 'a') ? 1 : ((mydate[2] == 'n') ? 6 : 7); break;
    case 'F': month = 2; break;
    case 'A': month = mydate[2] == 'r' ? 4 : 8; break;
    case 'M': month = mydate[2] == 'r' ? 3 : 5; break;
    case 'S': month = 9; break;
    case 'O': month = 10; break;
    case 'N': month = 11; break;
    case 'D': month = 12; break;
  }
  day = conv2d(mydate + 4); // 日
  hours = conv2d(mytime);   // 時
  minutes = conv2d(mytime + 3); // 分
  seconds = conv2d(mytime + 6); // 秒
}

 

これで、無事にRTCの設定が読みだされました。

 

バッテリバックアップされてないので、電源を切るとまたコンパイル時の日時が

 

RTCにロードされますが、バックアップしていると切ってもRTCで保持されます。
 

 
これで、RTCはOKです。あとひとつ、BluePillでの32.768KHzの発振がおかしくて日時が
ずれる現象が発生しました。
 オシロで見ると発振が不安定
 
BLuePillの回路図の発振回路は下記のようになっています。
 
C9とC12が32.768KHz用の帰還CはC9とC12 20PFです。
外して容量を見ると回路図通り20pFになっていました。なぜ発振がおかしくなるのでしょうか?
パチ品の水晶のC0と負荷容量CLのマッチングが取れてないのでしょう。 
8PFに変更すると正しい周波数で発振するようになりました。ここはツッコミなしでお願いします💦
 
 
さて、RTCの実装試験が終わったので、次はSDカード読み書きの実装試験ですね。
Arduinoでは、SD.h かSDFat.hを使ってSDカード読み書きを実装することが一般的のようです。
STM32coreでは、この辺のライブラリはSTM32の仕様にあわせてラッパーで処理しているようです。
つまり、どれがちゃんと動くかやってみないとわからないようです。
こちらのサイトの情報で、まずSDFat.hを使った試験をやってみました。
情報が詳細なので、勉強にはなりますがなぜか動かないようです。
 
次にSTM32向けSD.hを検討します。
まずSDfat.hを使おうと、こちらの記事を参考にします。

 

 

でも、SPI Started... から先に進みません。

STM32coreでの対応に何か問題がありそうです。

 

仕方がないので、SD.hの利用を考えます。
ArduinoのSD.hと違いSTM32での使用を考えて、SPIのピンはこちらのピン配置で設定しています。
 

SPI1に関して、
CSはPA4
SCLKはPA5
MISO(SDI)はPA6
MOSI(SDO)はPA7ですね
 
STM32F103でのSD.hのサンプルは、こちらのライブラリのCardInfoを利用します。

SDカードの情報を読み取って、serialで流すだけのプログラムです。

これがOKなら、読み書きもOKになると思います。

 

SDカードの情報が読み取れるようになりましたね。
こちらのライブラリでSDカードの読み書きを実装することにします。

実装する仕様は下記です。

SDカード基板はAdafruitのMicroSD card breakout boardを使います。

 

 

5V-3.3V 電圧レベル変換ICも入っていてちょっと大きいので、内蔵するにはこちらの基板を使います。

 

今回SPIを使うのでSDIOは使わないのですが、大きさがほどほどです。

 

新仕様として、下記を追加します。

① 起動時にENTキーを押していたら、パラメータ設定モードに入る

 

② 起動時にSELキーを押していたら、RTC設定モードに入る

 

③ PZEM004T測定データ表示中にSELキーを押すと、SDカードにログデータ記録を開始する
 (画面右上に小さくRECと表示)

 

④  PZEM004T測定データ表示中にENTキーを押すと、ログデータ記録を停止する

   ファイル名は、setting.txtにファイル名を入れていればその名前をBaseとして後ろに
  月、日と、01から始まる番号を追加する。ファイル名がなければLO030201.csvになる

   一旦記録を停止して、新たに記録を開始した場合、番号を+1したLO030202.csvを作り

  そのファイルにログを記録する。(8.3形式ファイル命名規則から、ファイル名は8文字とする)

 

⓹ 記録中でない時に、ENTキーを3秒以上長押しすると積算電力の値がが0クリアされる

 

⓺ sleep時間経過後に、焼き付き防止でsleep画面になった時は画面にランダムにドットが表示される

  SELキーまたはENTキーを押すとSleepが解除され通常表示モードに戻る

 (キーによる記録開始/停止は通常表示中でしか有効にならない)

 

といったところです。
大分使い勝手が改善したのではないでしょうか?
 
今回作成したプログラムはこちらのGoogleドライブに置いています。

 

コンパイルすると、Flashが73kB、RAMが7KB 強なので、実行速度も相まってArduino Nano等

では実装が困難だと思われます。(STM32F103C8 (128kB)でないとコンパイルエラーになります)

 

 

プログラムの説明ですが、今回は初めての試みとしてプログラム作成にChatGPTとXのGrokを

使いましたので、そのログを添付して手抜きの説明とさせていただきます。

 

ChatGPT

 

Grok

https://x.com/i/grok/share/mSsfThX3ekpRZBV12Ppy9OQS4

 

便利な世の中になったもんですね。

 

このソフトのコンソールでのログとCSVファイル名、それとCSVファイルの中身です。

 読み取りデータのログです

 

 

ファイル名

 

ログのCSVファイルの中身です。 Date Timeがちゃんと記録されています。

ここまで、プログラムのデバッグ用として、PZEM-004T電力計はつながず、ダミーデータです。
 
次回はハードウェア編として、実際にPZEM-004Tを接続して、SD Card基板とバックアップ用の
CR2032電池フォルダをケースに組み込む話をしますね。

それでは、皆さんおやすみなさい


関連記事

先週末は近隣の自治体のおもちゃ病院の開院日でした。
 
担当したのはアンパンマンピアノというキーボードのおもちゃ。
起動しないということで、開けてみると電池ボックスの端子が錆びまくっていたので
手持ちの部品で4個の端子交換をしました。
 
これで、無事電源が入って、ピアノの演奏をするようになりました。
 
マイクも動作しないということだったので、端子をテスターで当たると抵抗無限大。
これはマイクのところで断線しているということで、マイクの先をカパッと割って
切れている電線を接続しました。
 
これで音がでるようになったので、ミッション・コンプリート! と返却ボックスに
入れて、お客さんに連絡する手はずを進めていました。
 
『あれっ、人形が動かない!?』 っと、先輩ドクターからつっこみが。
『これって、動くんですか?』って言うと、このおもちゃは人形が動くのがウリなんだよ。
ちょっと前は良く修理に来ていたのでわかる。とのご回答。
 
新米ドクターだからしりませんよね? 確かに人形の下に配線は行っていた気がする。
もう、閉院まじかだから持ち帰って修理するしかないですね。
 
てなわけで、アンパンマンピアノ、持ち帰り残業?をすることになりました。
 
家に帰っていつもの和室でこたつに入って修理します。
まず、本体をばらして、基板を見てみました。
確かに基板から3つの人形へと電線が伸びていますね。
 
 
ネットでググると、アンパンマンキーボードには前期のモータとプーリで人形を駆動するタイプと
人形に内蔵されたソレノイドで動かすタイプがあるようです。
これはモータがないので、ソレノイドのタイプのようです。
 
つぎに基板を見てみます。
基板から、3つの人形を動かす配線が出ています。 それぞれ +側と―側があります。
 
この状態で試験用のDCベンチ電源から電源を入れてスイッチをONします。
音はなりますが、基板の人形駆動用信号(+側と―側)に電圧がでず、0Vのままです。
 
基板上にはJ2Yとマーキングされたトランジスタが10個くらいと、T1Yとマーキングされた
トランジスタが2個あります。
J2YはS8050 NPNのSMD(表面実装タイプ)、T1YはS8550 PNPのSMDトランジスタのようで、
ソレノイドを正/逆に駆動するHブリッジ回路を構成しているようです。
 
オシロで確認しましたが、トランジスタのベース側には0.7Vの駆動信号は入っているようです。
出力になにも信号がでてきません。テスターで調べると+側と―側間には10~30Ωくらいの
抵抗があるので、ソレノイドは断線していないようです。
 
う~ん、としばらく考えました。
トランジスタが破壊されているとしても、全部動かないのはおかしいですね。電源を調べてみましょう。
電池から、基板の右側に赤線でV+電源が接続されています。
 
V*電源は、黄色の過電流保護のポリスイッチを経由して、CPUへは逆接防止用のダイオードでつないで
います。Hブリッジの電源へは、基板端 上側のパターンでつながれています。
 
 
もう一度、パターンをよく見ると黒いシミのようなものが見えます。
もしかすると、パターンが切れているのでしょうか?
カッターで緑レジストを剥いで、銅箔を出してみます。 ビンゴ、途中でパターンが切れてます!
 
 
丁寧にレジストを削って銅ハクを露出させたら、フラックスを塗って予備はんだを付けます。
そのあと、ジャンパー線で接続すればOKです。余分な線は半田付け後に切断します。
 
ドキドキしながら、電源を入れてみます。
 
 
ベンチ電源の電流値が0.15~0.3Aくらい流れて、なにやらゴソゴソしています。
ベンチ電源から、電池に切り替えて動作確認を行います。
 
 
おっ、起動時に音楽と一緒に、人形が動くようになりました!
これでミッション・コンプリートです!
この子たち、何年間動かないで過ごしていたんでしょうね?
 
これで依頼主さんも喜んでくれると思います。
なんとか直ったので、ほっとして晩酌も進みます!
 
それでは、おやすみなさい。
 

 完成したブレッドボード

みなさん、こんばんは。 トドお父さんです。

前回『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に相談した内容は下のリンクにあります。

興味があれば、どうぞ。

 

 

それではみなさん、おやすみなさい。

 

関連記事