Arduino

2023年8月10日 (木)

「御詠歌プレーヤー」の製作 (MP3-TF-16Pモジュールの使用例)

お盆の精霊流し、昔はご近所の橋の上からすべり板で
川に浮かべた船に乗せていました。
ちょっと前までは、橋の上でご供物を集めていたのですが、
橋の上だと雨になるとたいへんだということで、現在は
地域の会館で行っています。
  ・御幸橋の上でしていた精霊流し
  ・2016年の様子
その時のBGMが「四国八十八箇所霊場の御詠歌」。
最初はカセットテープで。
それからMP3プレーヤに。 (アンプは別で)
そのMP3プレーヤー、文鎮の佐藤テック君が持っている
のを借りていたのですが、MP3プレーヤのモジュールを
買ったので、連続リピート再生するのを作ってみました。
MP3-TF-16P  という型番。
DFPlayer mini がオリジナルなのでしょうか。
秋月でも扱っています(M-12544)

説明書を見ますとADKEY1端子を51kΩの抵抗
オンしたら連続再生(Loop All、All cycle)になる
と書かれています。
ただし、51kをGNDにつなぎっぱなしでの電源オン
には反応無し。
オープンで電源をオンしてから51kをGNDにつなぐ
操作をしなくちゃなりません。
電源オンで遅延ワンショットかと思ったのですが、
お手軽にプッシュスイッチを使うことにしました。

こんな回路。
Go11
電池を3本使います。
モジュールに直結。
Low Bat警報の出る2.7Vまで下がってもまだ
動いていました。

左右のオーディオ出力に0.5Vほどの直流が
乗っていたので、C2とC3でカットしてから
モノラルに合成しています。

ダイソーで買った樹脂ケースに入れ込みました。
G012
G013
Go14
MP3モジュールの拡大。
Go17
Go18

SDカードに入れた曲(御詠歌)を順に再生する
だけの機能です。
四国霊場1番札所、霊山寺(りょうぜんじ)から
88番札所、大窪寺(おおくぼじ)まで、それぞれの
札所の御詠歌がえんえんと流れます。
ただそれだけの機能です。

再生の順序はファイル名に関係なくて、SDカード
に書き込んだ順番になります。
今回のように「何番札所」という曲順が重要なときは、
PCでのコピー操作、一括じゃなく、めんどうでも
ファイルを一つずつコピーしなくちゃなりません。
  ※一つのフォルダにどれだけのファイルを
   入れられるとか、フォルダによる
   再生順序はどうなるかまでは不明っす。

※昔、別のモジュールで自動再生を試したことがあります。
  ・2016年06月16日 御詠歌を自動再生
この時は、モジュール単独での自動リピート機能がどうも
うまく行かずで断念しました。
   リピート再生が途中で中断
   曲が変わらない
   外部からの制御無しでの運転はなんか
   おかしい

その後、このモジュール、Arduino UNOを使った
BGMランダム再生装置で使っています。
  ファイルを拾って乱数化して再生開始。
  全曲再生完了後、異なった順番にして再リピート。
  この乱数化するとき、直前の曲番をチェックして
  重ならないよう。 (直前の20曲が出ないよう)
F11_20230810172701
制御はArduino。
F12_20230810172801

制御スケッチ。
  ・ダウンロード - mp3_mk138.txt
    「.ino」じゃなく.txtファイルに

再生するファイルの数はArduino側で設定。
   (モジュール側のTXDは使っていません)

| | コメント (2)

2023年7月 7日 (金)

Arduino UNO R3のソケット・・思えば違和感がぁ

Arduinoの初体験 が2012年12月。

この時からの違和感・・・
  基板での信号接続が「ソケット」で「メス」。

手組みした基板も含めて、基板側にハンダするのは
2列のピンヘッダだったりXH(JST)コネクタだった
りと・・・「オス」のピン。
  ところがArduino UNOだとソケット!。

ブレッドボードに使う「ジャンパ線を刺せる」という
のが大きな理由だと「納得」したいんですが、ほんとは
「これキライや」のトップになるかと。

| | コメント (0)

2023年5月25日 (木)

初めて買ったArduino UNO・・・今は

Arduino初体験 が2012年12月。 (10年以上前だわ)

その基板の現在がこれ。
 ・どこかから拾ったプラケースをベースに。
 ・16MHzセラミック発振子を水晶発振子に交換。
 ・ブートローダ書き込み済みのATmega328Pと
  通信(スケッチアップロード)するための
  ケーブルを付加。
 ・乗っているチップは何度も交換。

Aa21_20230525110601

ちょっとした実験や試運転で便利なのが
アナログ入力ポートにつなぐ4つのボリューム。
スイッチも2つ付けてます。
こんな回路です。
Aa1_20230525110701

ボリュームのスライダーに0.1uFのコンデンサを
入れておくと安心。

発端はこんな3つのボリューム。
Aa22_20230525110801

+5VとGNDを取れるようにピンヘッダをハンダして
います。

ケチって片面のユニバーサル基板なんで、配線は
ちょっと面倒。

AnalogReadで読んだA/D値(0~1023)を使って
Delay値を設定したり、動作パラメータを変えたりと
リアルタイムでできますんで、なかなか便利です。

使ったボリュームは秋月電子通商のつまみ付半固定抵抗
  (足の1・2・3の順に注意:刻印あり)
  似てるけど3386K-EY5-103TRじゃない!


※応用のためのスケルトン・スケッチ
4つのボリュームと2つのスイッチを入力するための
応用基本スケッチを示しておきます。
 ・ボリューム値の入力は1ms割り込みで処理。
 ・64回平均。
 ・4つあるので256msごとにデータが確定。
 ・勝手にスキャンするのでAnalogReadのように
  100us待たされるということがない。
 ・割込禁止にしなくてもいつでも読めるよう
  VR値は0~255の8bitで。
    ad_avr[ch]を読めばok。

起動すると256msごとに値をシリアル出力します。
  A/D 8bit:VR1,2,3,4 SW1,2
  105 178 123 249 0 0
  105 178 123 255 0 0
   :
  0 64 128 255 0 0
  0 64 128 255 0 0
  0 64 128 255 1 0 SWはオンで1
  0 64 128 255 1 1
  0 64 128 255 0 1
  0 64 128 255 0 0

ダウンロード -  ad_vr4.txt
   .inoではなくUTF8Nのテキストです。


※タイマー割り込みとADC変換完了割り込み

ソースを見てもらえれば、その手順がわかるかと。
さまざまなライブラリ、確かに便利です。
でも、Arduino UNOのATmega328Pマイコン
あたりなら、レジスタの直接操作はそんなに
難しくはありません。
マイコンに備わっているさまざまな機能を引き
出すには、データーシートをにらみながらの
プログラミングをしなければなりません。

| | コメント (0)

2023年5月16日 (火)

液晶表示コントローラ HD44780で迎撃

オリジナルデバイス:なんぎな日記(2023年5月10日)
に、HD44780が乗った液晶表示モジュールが出ていたので、
こちらからも迎撃してみます。

2種類のモジュールを発掘できました。

50_20230516102701
上側、東芝のが1987年4月製のようです。
50a

HD44780、このサフィックスで内蔵しているキャラジェネ
のデータが変わります。
手持ちのHD44780データシート(1997年8月)、これで
示されていたのは「A00 A01 A02」の3種類。

・A00
51_20230516102701
・A01
52_20230516102701  
・A02
53_20230516102701

「A01」と「A02」はずいぶん賑やかに見えます。

CG RAMエリアの8文字、コード0x00~0x07と0x08~0x0F
は同じものがは表示されます。
C言語では、0x00はnullターミネートされる文字列で
使われます。
ですので、0x08~0x0FをCG RAMの文字コードに割り当て
たいところなんですが、「BS」や「HT」「LF」「CR」の
制御コードと被ってしまいますんで、CG RAMを使う時は
ちょっと気遣いが必要です。


※関連
液晶表示モジュールを4ビットモードで使ったときの空きピン処理

※Arduinoに絡めて
Arduinoでの液晶制御、一般には「LiquidCrystal」ライブラリを
使います。
8ビットモードだと、
  LiquidCrystal(rs, E, d0~d7)   :10bit
  LiquidCrystal(rs, rw, E, d0~d7) :11bit
4ビットモードだと
  LiquidCrystal(rs, E, d0~d3)   :6bit
  LiquidCrystal(rs, rw, E, d0~d3) :7bit
と、いちばん接続線数が少ない接続は
「4ビットモード」で「rs」と「E」を使った場合の
6本になります。

もう1ビット増やして「rw」を指定したら、
  「busyチェックしてくれるので早くなるかも
は、期待できません。
LiquidCrystalライブラリはbusyチェックは完全に無視。
delayMicroseconds()」を使った時間待ちでタイミングを
作っています。

さて。。。この時間待ち。
LiquidCrystalライブラリのソース「LiquidCrystal.cpp」を
読みますと、初期化(begin)のところで、
  delayMicroseconds(50000);
と「50ms待ち」をしているところがあります。

しかし、delayMicrosecondsにはこんな制限が・・・
Arduino-UNOでのdelayMicrosecondsの設定は16383までだ!

液晶初期化のdelayMicroseconds(50000)、
 ・ほんとにこれでエエの?
 ・動いてるからエエんとちゃう。
っということしか言えません。

| | コメント (0)

2023年5月 1日 (月)

Arduino UNOで3相モーターを回す

ラジオペンチblog:DVDのスピンドルモーターを低速回転させる-続編
に倣って、ジャンク箱から発掘した小さな3相モーターを
回してみました。

ラジオペンチさんの記事を見て、
 「光学ドライブかHDDか何からだったかは忘れた
  けど、小さな3相モーターがあったはず」
っと、ジャンク箱を漁りました。
発見できたので、真似っこしてみました。

仕事では、RX220マイコンと L6234 という
3相モータードライブICを使って「回した」こと
があります。
RX220には3相駆動用のPWM出力が付いてい
ますが、Arduino UNOのATmega328Pは
別個のタイマーユニットを操作しなくてはなりません。

3つあるタイマー、そのうちタイマー0がdelay()などの
タイマー処理で使われています。
それを・・・Arduinoから「タイマー0」を取り上げる(ユーザーが使う)
の手法で「我がモノ」にします。

初期化(setup)から、もう、Arduinoの環境からは外れて
しまいます・・・
でも、ATmega328Pはちゃんと「言うことを聞いてくれます」
ので。

試した回路、はこんな様子。
M11_20230501175601
下の銀色のがモーター。
右のユニバーサル基板が駆動回路。
モーターとは3本の電線がつながります。
2ph1
タイマー0のOC0AとOC0B出力。
そしてタイマー2のOC2B出力で、3つのPWM出力を
制御します。
  ※N-ch、P-ch MOS FETの記号は
   簡略化して記述。
  ・パワーMOSFETの回路記号:MOSFETの矢印
  ※ゲートのプルダウン抵抗はゲートの浮き防止。
   浮くと貫通電流が流れるかも。
  ※3相モータの回路記号↓ (BSch3V用)
     bsch3v_lib_230502.zip
   6本線を3本線にしたのはSHAPES.LIBの中に。

オシロ波形、緑・青・赤の3つがこの3相制御出力。
M12_20230501175601

・スケッチ
  ・ダウンロード - 3ph_mot1a.txt

  ※.inoではなく.txtにしています。
  ※ちょいと虫を発見したんで
    mot1→mot1aに入れ換え

タイマー0とタイマー2は似たような8bitタイマー。
  ※タイマー1は16bit
それを「同期」させて3相の制御パルスを得ます。
二つを「8bit位相基準PWM」に初期化して、同期したPWM制御
ができるようにします。

スケッチをピックアップすると・・・

//  タイマー0,2  PWM出力 OC0A,OC0B,OC2B
TIMSK0 = 0b00000000; // 割込禁止
TIMSK2 = 0b00000000;
// ||+--- TOIE
// |+---- OCIEA
// +----- OCIEB
GTCCR = 0b10000011; // ★1
// | |+--- PSRSYNC タイマ0,1前置分周器リセット
// | +---- PSRACY タイマ2前置分周器リセット
// +---------- TSM 同期処理
TCNT0 = 0; // タイマーカウント値クリア
TCNT2 = 0;
OCR0A = 128; // U相 中央値
OCR0B = 128; // V相 (-1せず)
OCR2B = 128; // W相 (H=128:L=127)
TCCR0A = 0b10100001;
// |||| ++--- 8bit位相基準PWM
// ||++------- OCR0B 非反転PWM
// ++--------- OCR0A 非反転PWM
TCCR0B = 0b00000010;
// || |+++--- CS 1/8 2MHz →3.92kHz
// || +------ WGM02
// ++--------- FOC0
TCCR2A = 0b00100001;
// |||| ++--- 8bit位相基準PWM
// ||++------- OCR2B 非反転PWM
// ++--------- OCR2A Portで
TCCR2B = 0b00000010;
// || |+++--- CS 1/8 2MHz
// || +------ WGM22
// ++--------- FOC2
GTCCR = 0b00000000; // ★2
// +---------- TSM=0で計数開始
  :
★1でタイマー0とタイマー2の前置分周回路を停止。
★2で計数再開。

★1でクロックの供給が止まり、その間に二つのタイマーを
「8bit位相基準PWM」に初期化しています。
   ※タイマー1は16bitなので
    ちょっと、この二つとは
    違います。これをタイマー
    割り込みに使います。
PWMの周波数は、
  16MHz/8 = 2MHz
  1/255  1/2 → 3.92kHz
になります。

★2の計数再開でタイマー0とタイマー2の「頭」が
そろいます。

もう一つ、高速化のための手法。
三角関数データをROM内のテーブルに配置します。
「gawk」を使って配列を作り、それをPROGMEMで
ROMに配置します。
角度0~359度から「±127」の「8bitの数値=sin値」が
得られるようにしておきます。
  ※PWMの設定値が8bitなので
これで浮動小数点演算→三角関数の演算を使わなくて
良いようになります。

下表のように角度0~359度からテーブル読み出しで
8bitの「sin値」が出てきます。

/*****  SIN 0~359      *****/
// 振幅±127, 中央値は0
const int8_t sin_tbl[] PROGMEM ={
0, 2, 4, 6, 8, 11, 13, 15, 17, 19, // 0
22, 24, 26, 28, 30, 32, 35, 37, 39, 41, // 10
    :
  125, 125, 125, 126, 126, 126, 126, 126, 126, 126, // 80
127, 126, 126, 126, 126, 126, 126, 126, 125, 125, // 90
125, 124, 124, 123, 123, 122, 122, 121, 120, 120, // 100
    :
-22, -19, -17, -15, -13, -11, -8, -6, -4, -2, // 350
};

ツールは「gawk」。
  文字を処理するなら、コレ!。

駆動周期でPWM値を更新します。

    pwm_3ph[0] = degpwm(rot_deg,       ad_vr[0]); // U相
pwm_3ph[1] = degpwm(rot_deg + 120, ad_vr[0]); // V相
pwm_3ph[2] = degpwm(rot_deg + 240, ad_vr[0]); // W相
cli(); // 割り込み禁止で
OCR0A = pwm_3ph[0]; // U相 PWM duty設定
OCR0B = pwm_3ph[1]; // V相 (-1せず)
OCR2B = pwm_3ph[2]; // W相
sei(); // 割り込み有効に戻す
rot_deg++; // 0~359度
if(rot_deg >= 360) rot_deg = 0;

120度位相差の3つの信号からPWM設定値を得て、
それをタイマー0、2の「OCR」レジスタに書き込んで
sin波に合致したデューティ比のパルスを得ます。
ad_vr[0]は、「PWM振幅調整用」の8bitデータです。

正弦波データテーブルを読んでいる処理がこれ。

/****  角度からPWMデータに変換   *****/
// 角度 0~359 → 8bit PWMデータ(0~255)
// VR1値(8bit)でPWMの振幅を設定
// 中央値は128
byte degpwm(short deg, short vr1)
{
short a, g;
byte d;
while(deg < 0){ // マイナスならdeg+360
deg = deg + 360; // プラスに
}
// deg = deg % 360; // 0~359の範囲に(割り算やめて)
while(deg >= 360){ // 360を越えている場合はdeg-360
deg = deg - 360;
}
// SIN値に変換
a = (short)(int8_t)pgm_read_byte(&sin_tbl[deg]); // 0~359度→sin
g = (a * vr1) / 256; // VR1 : PWM振幅調整 ±127最大
d = (byte)(g + 128); // 8bitの中央値
return d;
}

g = (a * vr1) / 256; のところ
ぎりぎりで符号付16bit乗算が可能。
  aは±127でvr1は0~255。
aとgをlongにして乗算するとずいぶん遅くなります
んで、shortの範囲にしておきたかった。


今回の実験(お休み中の腕試し)はここまで。

※面白がって追試
3相PWM出力の処理時間が11~12usだったので、
「これなら100us割り込み内で処理できるぞ」
っと、タイマー割り込み内でPWM出力を制御する
ように書き直してみました。
お試し下さい。
  ・ダウンロード - 3ph_mot2.txt
    ※.inoではなく.txtにしています。

VR2を絞って最高速度にすると
360度の経過が1秒間に27~28回。
  1度の変化が0.1ms。
  0~359度で 36ms。
  1秒 ÷ 36ms = 27.78


| | コメント (5)

2023年3月23日 (木)

Arduino サーミスタを使った温度測定で 【ゼロ除算問題】

2023年3月21日:A/Dコンバータでサーミスタの抵抗値を読む サーミスタをつなぐ場所は?
この続き。 ゼロ除算問題が見えてきました。

あれこれ検索。

★電源側にサーミスタ

VasteeLab:Arduinoで火災報知機をつくってみた
 int val = analogRead(PinTemp);     // get analog value
 resistance=(float)(1023-val)*10000/val; // get resistance
   val値が0ならゼロ除算。

★サーミスタをGND側に持ってきた例

今日から始める電子工作 【初めてのArduino】6.サーミスタ|ハンズオンで学ぶ初心者向け入門コース
 サーミスタはGND側。
 しかし、Vref値を1023としているため、抵抗値算出の時、
 A/D値がフルスケールの1023ならゼロ除算エラー発生。
 正しくは「/ (1024 - readValue)」。
  //アナログ値を読む
   float readValue = analogRead(analogPin);
  //Rtを計算する
   float Rt = Rd * readValue / (1023 - readValue);

  ※1023が出るのはサーミスタが外れた時。
   0が短絡なんで、回路の異常報知案件。

みのや電子工作所:なんちゃって自作体温計の製作
  電圧計算に1023を使っているので、A/Dの
  フルスケール値を読み出したときはV0が5.0Vと
  なり、抵抗値算出の/(5.0-V0)でゼロ除算エラー。
  val*5.0/1024.0にしておくのが正しい計算で、
  これで、ゼロ除算エラーを回避できる。
   val = analogRead(AN0); //アナログ値読込み
   V0 = val*5.0/1023.0; //アナログ値から電圧換算
   THR = 10000.0*V0/(5.0-V0); //電圧値からサーミスタ抵抗値換算

adafruit.com:Using a Thermistor
  な、なんなんだ、この式は!
  A/D値が0でも1023でもアウト。
   float reading;
   reading = analogRead(THERMISTORPIN);
   reading = (1023 / reading) - 1; // (1023/ADC - 1)
   reading = SERIESRESISTOR / reading; // 10K / (1023/ADC - 1)


◆正しく計算 (コメントしたところも)

アイデアノート:Arduinoとサーミスタで温度測定

jh4vaj:Arduinoでサーミスタを使って温度計を作るのに、電圧を求める必要はない

プチモンテ:サーミスタ(NTC)の使い方 [Arduino]

arduino.stackexchange.com:How to include Vref in thermistor temperature calculation?
  あれこれ議論。


◆基準電圧で誤差が出るかも

ラジオペンチ:Arduiono を使ってサーミスタで温度を測る
   (電圧算出に1/1023を使っているのも気になる←修正済)

◆イイこと書いてある

Qiita @c_in_g:analogReadの値の変換を誤解していた話(1023、1024問題)
 引用させて頂きます。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  長々と書いたが、ぶっちゃけ、255/1023と256/1024の
  違いなんて微々たる差(1%にも満たない)で考慮する必要ない
  だろと思う。  こういうことは言ってはいけない
   :
  初心者はこんなクソ細かいことを気にするより色々作ってみた
  ほうがいい。  こういうことも言ってはいけない
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

★エラいことにつながるかも
『1023 vs 1024 問題』が【ゼロ除算問題】にまで膨らんじゃ
いました。
1023 vs 1024は「ちょっとした誤差やん」で見逃せた
んですが、ゼロ除算は笑って済ませられないかもしれ
ません。

『ミサイル巡洋艦・ヨークタウン ゼロ除算』を検索すると、
ゼロ除算エラーでシステムがダウン。航行不能に」なんて
記事が見つかります。

コンピュータのトラブルで漂流したアメリカのイージス巡洋艦 の考察

◆ゼロ除算エラーの話
PLCが突然停止、原因は割り算の演算エラー
【中級編】GX Works3 除算演算エラー回避方法 0で割らない

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
※追記

サーミスタでの温度計算とゼロ除算問題、式との相性が良い(!)
のかあれこれ見つかります。
「1023 vs 1024」と絡むのも面白いかと。

理系男子の電子工作:【PIC】ADCの使い方 サーミスタで温度測定
 サーミスタはGND側。
 エクセルでA/D値→温度テーブルを作っておくという
 手法です。
 このように↓言い切られています。
  『A = 0, 1023 の時にゼロ除算のエラーが
   発生しますが、測定値がそのような値を
   とることは考えられないので気にする
   必要はありません。』
 A/D値(A)からサーミスタ抵抗(R)の計算で
  「R = (r × A) ÷ (1023 - A)
 としているのを、1023→1024にするだけで
 フルスケールでのゼロ除算が回避できます。
 1024だと半値もちゃんと出るし。
   (log(0)エラーの問題は置いておいて)
 前もってデータテーブルを作るときのエラーですんで、
 実行時のエラーとは違う話になります。
 しかし、「いつも気にしておく」のが設計という
 ものです。

Digi-Key:正確なサーミスタベースの温度検出回路を迅速に作成
 デジキーの技術解説。
 サーミスタは電源側。
    Vadco A/D入力電圧
    Dout  A/Dデータ
    2^N  A/D分解能
    Rth  サーミスタ抵抗
    R25  GND側抵抗
  Vadco = Vref × (R25 ÷ (R25 + Rth))
  Dout = 2^N × (Vadco / Vref)
  Rth  = R25 × ((2^N ÷ Dout) - 1)
 
   (2^N ÷ Dout)でゼロ除算エラー発生(の可能性)。

 

| | コメント (2)

2023年3月21日 (火)

A/Dコンバータでサーミスタの抵抗値を読む サーミスタをつなぐ場所は?

私の場合、サーミスタ読み取りのための接続は、
基準抵抗をVref側に持ってきます。
こんな具合。

A01_20230321164901
こうすると、サーミスタの片方をGNDにできるので
シールド線が使えます。
この場合、温度が上昇するとサーミスタの抵抗値が
下がり、それに連れてA/D値も下がってしまいます。

温度が上がるとA/D値が下がってしまうので、直感的に
これを嫌う方が居られるのでしょうか、こんな具合に
基準抵抗をGND側につないでいる回路例を見かけます。

A02_20230321164901
こうすると、
 温度が上がる
   ↓
 サーミスタの抵抗値が下がる
   ↓
 A/D値が上がる
と、温度上昇でA/D値が上昇と、まぁ見た目の感覚に
合うような気がします。
しかしちょいと問題が・・・

RaあるいはRbの値を固定して、その時のA/D値から
反対側の抵抗値を計算する方法を見てみましょう。

VrefとVin、そしてRaとRbの関係式です。
A2_20230321165001
VrefとVinは何ボルトという実値でなくてもかまいません。

Vinは「ゼロ~フルスケール」。
8bitのADCなら「0~255」、10bitなら「0~1023」と
いう範囲の値になります。
そして、Vrefは「フルスケール値 + 1」

まず、サーミスタをGND側につないだ時
Raが基準抵抗。 Rbがサーミスタ。
VinからRb値を計算します。
A4_20230321165201

分母の「Vref - Vin」に注目。
サーミスタがGNDに短絡して0ΩになってVin=0になると
分子がゼロでRb=0が出てきます。
そして、分母はVrefそのもので計算可能。

サーミスタ入力がオープンになってVinがフルスケールに
なっても、分母の「Vref - Vin」は「1」になって計算可能
です。
  ※Vref=フルスケール値 + 1 が重要!

問題がサーミスタを電源側につないだ時の計算。
基準抵抗がGND側。
Raがサーミスタ。 Rbが基準抵抗。 
A3_20230321165401
サーミスタ入力がオープンになるとVinは
RbでGNDに落ちて、Vinはゼロ
この時、「Vref ÷ Vin」の分母がゼロになってしまって
ゼロ除算エラーが発生して、正しく計算できません。

このようなつなぎ方の時、A/D値「Vin = 0」をチェック
して、ゼロ除算エラーを回避する処置が必要です。
例えば、「0なら1に」するような。

Arduino UNOだとint値をゼロで割っても答えを「0xFFFF」
(intだと-1)にしているようですし、floatだと「inf」
無限大を出して、「ゼロで割ったから停止!」とはして
いません。

ネットに上がっているいろんなサンプル、ゼロ除算エラー
を無視しているのが多いようです。
マイコンを使っての計算でこれは「ちょっとなぁ」です。

~~~~~~~~~~~~~~~~~~~~~~~~~~~
※追記
サーミスタによる温度計測で、ゼロ除算エラーが
発生する可能性のある回路やスケッチの例。
  ※ネットを検索

Arduino 入門 Lesson 18 【サーミスタ編】:おもろ家
 サーミスタは電源側。
 「Rth = ((Vin/Vout) - 1) × R1」として
 サーミスタ抵抗値Rthを計算。
 VoutがA/D入力値で、「0」だとアウト。

https://asukiaaa.blogspot.com/2021試行錯誤な日々:Arduino(ESP32)でサーミスタを使い温度を取得
 サーミスタは電源側。
 「(double)(analogMax - analog) / analog * resistorPullDown;」
 でサーミスタ抵抗値を算出。
 analogがA/D値。 「0」だとアウト。
 12bit ADCだが、「#define ANALOG_MAX 4095」として
 「フルスケール+1」を使っていない。
 考え方として、これもダメ。

NOBのArduino日記!サーミスタの使い方! その2( 実測編!)(103JT-050)
 サーミスタは電源側。
 「R1 = ((Vcc × R2) / Vout) - R2 」で
 サーミスタ抵抗値R1を算出。
 VoutがA/D入力値で、やはり「0」だとアウト。

初めてのロボット組立:Arduinoにサーミスタを接続して温度を測定する
 サーミスタは電源側。
 「10000.0 * ((1024.0 / tempReading - 1))」でサーミスタの
 抵抗値を算出。
 tempReadingがA/D入力値で「0」だとアウト。

   ※LCDのコントラスト調整の半固定抵抗記号に
      んっ? ボリュームの記号が!
    で紹介した表記が使われている。
      電子回路エンジニアの皆さん、
      ほんとにこれ、どうにかして!

まったりYO$HI日記 気の向くまま(・∀・)【Arduino】サーミスタで温度測定
 サーミスタは電源側。
 「R = R1 × ((V / Vt) - 1)」でサーミスタの
 抵抗値Rを計算。 R1はGND側の抵抗100kΩ。
 VtがA-D入力値。 これが0ならアウト。

基礎からの IoT 入門Arduino IoT とは? 温度を測る ~ サーミスタの利用
 サーミスタは電源側。
 「R = ((Vin / Vout) - 1) × R1」で計算。
 VoutがA/D値。 0ならアウト。

~~~~~~~~~~~~~~~~~~~~~~~~~~~
Arduino UNOのような単純な8bitマイコンだと
機械語として割り算命令は持っていませんので
ゼロ除算によるトラップ(例外処理)は考慮しなくて
かまいません。(勝手には止まらない)
しかし、高機能なマイコンだとトラップに引っかかり
制御がそこで止まってしまうかもしれません。
  (そんな仕掛けをしていたら)

int値のゼロ除算なら結果はおそらく「-1」。
  (符号無しなら最大値に)
ゼロ除算を無視するのなら、この「-1」がその後の
計算にどんな影響を与えるのかを考えておかなくて
はなりません。

単純な表示でも、想定する桁数を越えちゃうと、表示
が「グチャ」っとなるかもしれません。
何かの制御に使っていたら・・・ちょっと怖い。

浮動小数点なら「NaN:Not a Number」や
inf:infinity」てなところでしょうか。

今回はサーミスタの抵抗値計算での話ですんで、
「マイナスの抵抗値」が出現てなことに
なっちゃうかも、です。

やはり、どこかで数値のエラーチェックが必要でしょう。
0での割り算をしちゃう大もとの、A/D値のゼロを
排除というのが簡単かと。

  これ、あれこれ書いてきましたが、
  サーミスタをGND側につないだ時は
  大丈夫なんですよ。
  10bitのADCなら0~1023の範囲で
  ちゃんと答えが得られます。

うだうだ言ってますが、結局はサーミスタの接続が
外れなければ大丈夫。
しかし・・・
  ・接触不良がおきたら?
  ・コネクタやプラグを使って抜き差しできる
   構造なら、抜いた時は?
  ・サーミスタへの配線が切れたら?
と、Vinが0Vになる可能性があるのなら、
ゼロ除算エラーが生じる可能性もあるわけです。
このつなぎ方をするのなら、ソフト的な対策は必要か
と思うのです。


★続き
 ↓
Arduino サーミスタを使った温度測定で 【ゼロ除算問題】

  サーミスタを使って温度計測の手順から、
  【1023 vs 1024 問題】だけじゃなく【ゼロ除算問題】が
  見えてきました。

| | コメント (0)

2023年3月 9日 (木)

トラ技の目次で「Arduino」を検索すると

Arduino初体験 が2012年12月。
  「1023 vs 1024」問題 を知るのはずっとあと。

トラ技の目次検索でArduinoが最初に出て
くるのは2012年2月号。
  Ethernetシールド付きArduinoにアップロードの
  ためのライブラリを搭載
  ネットワーク温度&照度計

次が2012年3月号
  頒布カメラのレジスタを設定するUSB書き
  込み器の製作
  ビギナ向けマイコン・ボードArduinoを改造!

そして、2013年3月号の特集
  1万円で作るMy実験室,誰でもマイコン基板
  Arduinoでズルしてサクッと!

これ↑には記事を載せてもらっています。

目次での検索ですんで、これら以前にも記事内で
Arduinoを使っている製作記事、解説記事がある
かもしれません。


| | コメント (0)

2023年3月 1日 (水)

8ビットマイコンの割り込み処理・・・1バイトに収まるなら1バイトに

2022年9月1日:8ビットマイコンの割り込み処理・・・言い足りないぞ
ここ↑では、トランジスタ技術2018年5月号での記述を
「ちょっと違うけどなぁ」っと話題にしました。

この内容↓です。
 「割り込み処理関数とメイン関数の両方から
  アクセスされる変数はchar型にしておく」

Pp1_20220901103201

確かに、1バイトにしておくと安全側にはなるのですが、
間違いなく割り込みを動かそうとすると、アトミック処理
(いったんn割り込み禁止にしてごそごそ)が必須です。

で、またまた何気なく古いトラ技を見ていたらの話
になります。
今度は2019年4月号
Tr1904  

この連載:宇宙ロケットMOMO 開発深堀り体験<2>

ブロック図を記したp.129の図1を見ると、ジャイロや
サーボの制御に8bitマイコンATmega328が乗った
Arduino Pro Miniが使われています。

Tr1904a

p.135に制御ソフトが載っていたんで、ちょっと
追いかけてみました。
T11a_20230301162201

まず、目に入ったのが割り込みで処理されるであろう
変数です。
Volatileが前置されたint値(2バイト値)です。

T11b  

これを使うのが0.1ms周期のタイマー割り込みの中

T11c

pwm_cntがアップカウンタで、200になったら
出力ピンをHにしてゼロクリア。
そして、pwm_cntがpwm_h_periodeになったら
出力ピンをLにという制御。
200がPWM周期でpwm_h_periodでPWMの
パルス幅を決めています。
pwm_cntはこの割り込みの中だけで使われて
いるようなので、割り込みとの競合は問題なし。

比較するデータpwm_h_periodはどうかと追いかけ
ますと、gimbal_agl_to_pwmという関数で値を
出していました。
これはメイン側の処理です。

T11d

gimbal_agl_to_pwmがint値を算出して、それを
pwm_h_periodeに書く時に割り込みが入ると
どうなるか・・・

2バイト値ですので、2回に分けて値が書き込まれます。
もしその中間で割り込みが入り、なおかつ、
0x00FF → 0x01000xFFFF(-1) → 0x0001(+1)の
ような2バイトにまたがる数値の変化が発生したら、
割り込み処理での数値判定をミスするかもしれません。

しかし・・・このプログラムでは
  ・pwm_cntは200が最大値なんで、int値(2バイト)の
   下位側しか変化しない。
  ・関数gimbal_abl_to_pwmが返す値は
   +8~+20の範囲で1バイトの範囲内。
   負にもならない。

ということで、2バイトデータと割り込み処理との競合は
大丈夫でした。
でも、もし上位バイト変化するようなint値なら
割り込み禁止にしてデータを更新して割り込み処理に
知らせるというアトミック処理が必須です。

このプログラムでは、変数が1バイトに収まるならデータの
宣言は1バイトでということにしておくと、処理も速くなるし
でエエんじゃないでしょか。

ただ、昔人間からすると、
  char符号有り符号無しかどっちやねん?
     コンパイラはどう処理するんや?
  Arduino環境なら符号無しは  byte やろ。
  今ふうにちゃんと書くなら uint8_tint8_t で宣言か。
こんなところが気になります。

| | コメント (0)

2023年1月13日 (金)

Arduino UNOで0.00~40.00mA定電流負荷回路

過去、あれこれと定電流回路を紹介してますが、
今回のは電流は少なめ。
2023年1月 2日:PWMでD/A変換:アナログマルチプレクサの応用で
2023年1月 7日:PWMでD/A変換:アナログマルチプレクサの応用、解決方法

この続きで、「4~20mA電流ループ・インターフェース」回路用の
実験用定電流回路です。

計測系での4~20mA電流ループ・インターフェース、こんな
つなぎ方があります。

・装置側が電源を持っていて、計器側は
 その電流をちょっともらって回路を動かし、
 結果を4~20mAの電流にして返す構成。
 2線でつなげます。
C21_20230113101901
ちょっとの電流で動かせる圧力センサーとか
温度センサーなど。

・計器側の電流消費が大きい時は、装置側から
 電源を供給。
 3線接続。
C22_20230113101901

・計器側は自分で電源を持っていて、
 測定値を4~20mAで出力。
 制御装置からの供給だと4線で接続。
C23_20230113102001

こんな回路のテストには4~20mAの電流発生器
あるいは定電流負荷回路が必要になります。
  ※中華製のが安く出ているようですが・・・

計器の真似を(装置側から電源をもらって動作)
しようとしてもよかったんですが、汎用的に
使える吸い込み型の定電流回路=定電流負荷
にして、
  「PWM+LPFによるDACの能力」
を確かめてみました。
  ※電流ループだと、電流の向きさえ合わ
   せれば定電流回路はどこにでもつなげる。

吸い込み型の定電流回路、基本はこんなの。
C13_20230113102101

ボリュームやポテンショ、あるいはスイッチで
Vp電圧を操作して、「Vp÷Rs」で電流を決める
という方法です。

設定した電流を読む方法が、
 ・ループの途中に電流計を入れる。
 ・Rs両端の電圧から電流値を計算。
と、いうのが普通。

今回は、
  PWM+LPFで出したVp電圧と、検出抵抗Rs値を信じて、
  PWMのデューティを変えて電流値を可変出力。
という方法を使いました。

ブロック図で示すとこんな具合。

C14_20230113102301

校正モードで設定するのは、デューティ100%
(MPXのVref選択入力がいつもH)のフルスケール電流値。
これを「0.01mA」単位の読みで(別の神様電流計で計測)
設定します。
  ※今回はフルスケールで12bitあたりを狙っています。

校正時、デューティ100%で仮に40.00mAが出てきたと
すると、PWMでのデューティ設定範囲は「0~3999」
にします。
つまり、デューティ0/4000~3999/4000が範囲。

デューティ比がそのまま「mA」値になり、
「00.00~39.99mA」と0.01mA単位で設定できるはず・・・
そんな目論見です。

そして、0.00mA→0.01mA→0.02mA→という最小桁を
安定して可変する出力というのもやってみたかった
制御で、そのために
 ・オフセット電圧の小さなオペアンプを選ぶ。
 ・バイアス電流も小さいの。
 ・±電源で動作させる。 ぜいたくに。

使ったケースは
2022年8月24日:パルスジェネレータを作ってみた:箱に入れた
での、ダイソー・100円(税別)樹脂ケースです。

C12_20230113102501

FLUKEを電流計モードにして操作してらほんとにぴったし。
エエ感じにできました。
手動操作だから PWM+LPF でも時間遅れは気になりません。
  ※セトリング時間、なに?それ?のモード

C11_20230113102501

「0.01mA」桁の操作がそのままテスターの読みに
出ます。
電流の読みを見ながらポテンショを左右に微調という
操作とは違う感覚です。

Rsを小さくすれば電流値を大きくできます。
今は20Ωで40mAですが、2Ωなら400mA。0.2Ωなら4Aに。

  ※電流を大きくしようとすると、FETのゲート
   駆動電圧を高くする必要があるかもです。
   オペアンプの+電源が+5Vでは間に合わないかも。

※関連
2021年4月4日:オペアンプとMOS-FETを使った定電流負荷回路
2020年8月27日:オペアンプとMOS FETを使った定電流回路・・・電子負荷回路・・・
2020年3月12日:定電流回路の電流検出抵抗を試す
2020年1月24日:メモ:4-20mA電流ループ用ICとデジタルアイソレータ


※現時点の回路
Cc31

・手持ち部品の関係でロータリーエンコーダはBOURNS
 のを使用。
・2回路入りオペアンプを使ったのも手持ち部品の関係。

※スケッチ zip圧縮
  ダウンロード - cl40ma1.zip

※温度変化の様子
2022年7月6日:デジタルテスター「FLUKE 87IV」の赤外線通信ユニット完成
でFLUKEテスターからのデータ吸い上げができるようになった
ので、定電流回路の温度変化を調べてみました。
使ったのはこの→2017年11月6日:液晶表示器「焼き鈍し」
での保温箱。
この中に定電流回路を置いて通電開始。
「電球」で段ボール箱内を温めます。

結果・・・
Cap074
定電流回路の設定を20.00mA。 (供給電圧は24Vで)
その時のテスターでの読みが20.003mA。
箱内を50℃まで上げると19.983mAに。
20.003mA→0.02mA下がりました。
30℃の温度上昇で電流がざっと0.1%下降。
1℃に直すと「33PPM/℃」。
基準電圧の変動なのか電流検出抵抗RsとかLPFの
分圧抵抗の変動なのか。
もうちょい調査が必要です。

| | コメント (2)

より以前の記事一覧