Arduino

2022年9月11日 (日)

ロータリーエンコーダーのチャタリング波形

2022年8月24日:パルスジェネレータを作ってみた:箱に入れた
このスケッチ、
   ・ダウンロード - p_gen16c1.txt
では、ロータリーエンコーダの計数をINT0割り込みではなく
タイマー割り込みを使った周期的処理でチャタリングを
除去してA相の↓エッジを検出するようにしました。

そして、CRによるフィルタが不要になったので、エンコーダ接点に
入れたコンデンサを取り外してしまいました。

※タイマー処理によるチャタリング除去の考え方は
ここを参照。
  ・2020年9月15日:今度はチャタリング除去、その考え方
  ・2020年9月16日:ロータリーエンコーダーの2相パルスをタイマー割り込みで

コンデンサ無しだと、どんなチャタリングが出るのか、
オシロで観察しました。
デジタルオシロの無限残光モードが役立ちます。
こんなパルスが入ってきます。

Aa000

Aa001

0.5ms(2kHz)ごとにそのH/Lを記録して、HあるいはLが
4回以上連続してたらその信号は安定。
途中で変化があればチャタリングと判断します。
ですので、高速2相パルスは検出できません。
「クリック有り」ロータリーエンコーダ向けの
処理です。

ch3の波形が、A相信号のチャタリングを除去後、
A相↓エッジを検出して、計数処理をしているタイミングです。
その後に、新カウント値による液晶表示が始まっています。
周期や周波数データの液晶表示に10ms近くかかるので
loop()内でのA/B相処理は間に合いません。
割り込みでしか追いつかないのです。

けっこうなチャタリングが見えています。
全部のon/offでこれが発生するのではなく、たまにひどいのが出る
という感覚です。
そして、その場所(回転位置)は一定ではありません。
どこで出るかわからんなぁ~です。


| | コメント (0)

2022年9月 6日 (火)

Arduino A/D値の1/1023問題 ・・・正解は?!

タイトルを検索していて、こんなページに到達。
  ・岐阜高専 電気情報工学科 自動計測

こんな設問が・・・
~~~~~~~~~~~~~~~~~~~~~~~~~
 int val = analogRead(analogInputPin);
 float val_volt = // ここの変換を自分で考える 1023を5Vに変換
 Serial.println(val_volt); // PCに結果を送信
~~~~~~~~~~~~~~~~~~~~~~~~~

さて、そのお答えは?・・・

※関連
2020年1月8日:ミスが広まる 1/1023 vs 1/1024
2020年5月17日:Arduino なんとかして誤用を正したい:A/Dの1/1023とmap関数


| | コメント (0)

2022年8月24日 (水)

パルスジェネレータを作ってみた:箱に入れた

2022年8月5日:Arduino-UNOでパルスジェネレータを作ってみた
2022年8月7日:パルスジェネレータを作ってみた:16MHzクロックを追加
この続きで、完成形に持ってきまして、2台作りました。
  ※前のとは回路が少し違います。

一つは乾電池2本仕様。
バックライト無しの液晶で。
P11_20220824162601
タカチのプラケース「SW-125」。
こんな中味。
P13

二つ目がバックライト付きの液晶なんで、ちょい
電流が増えるので電池を3本に。
P12_20220824162601
ケースは「ダイソ-」で入手。 税込110円。
P14
安く買ったケース、ついつい手抜きで加工精度が
もうひとつ。
液晶の窓部分の寸法が微妙にずれてます。
  ※気にしない、気にしない。
   買ったのはダイソー。
   日本製。東大阪市高井田の「(株)シャルム」
   SAI-20-P8  No.141  181 x 90 x 28mm
   材質:ポリプロピレン

Arduino UNO単体で試すならこんな接続で。
Pa11

箱に入れた回路には、電池電圧低下検出回路も
入れてあります。
電池3本仕様のを図示。
Pa12

さて、これをまとめて製作記事としてトラ技に投稿したの
ですが、記事を出してから、ロータリーエンコーダの入力
部分をちょいと手直しをしました。
原稿では
  「A相の↓エッジパルスをINT0に入れて割り込み
   処理でカウント」
としました。
そのため、ハード的なチャタリング除去回路
(CRを使ったディレー回路)が必要です。
INT0割り込み内の処理でチャタリングを逃げる手当は
しているのですが、激しいのが来ると、カウントを
ミスることがあるのです。
  ※立ち上がりの時に出たチャッタを
   ↓エッジが来たと勘違いすることが
   あった。
Pa13

回路をシンプルにということで、このディレーを
無くして(1000PFのコンデンサだけに)、カウント
処理をタイマー割り込み内に入れてチャタリング
除去を試してみたのです。
タイマー割り込みを2kHzにして4段のシフトレジスタ
のデータ一致で信号の安定を検出します。
この方法↓
2020年9月16日:ロータリーエンコーダーの2相パルスをタイマー割り込みで

今回のスケッチはINT0割り込みじゃなく、タイマー割り込みを
使ったのをアップしておきます。

  ・ダウンロード - p_gen16c.txt
    ※ファイルタイプを.txtにしています。(普通は.ino)

お試し下さい。

※2022-09-03 ちょっと手直し
「SW2長押し」による「duty 50%設定」。
この時も「duty hold」状態になるようにしました。

   ・ダウンロード - p_gen16c1.txt

どっちが使いやすいか・・・
「デューティ50%で使いたいんや」の時、
前のだと、「duty 50%」後に「duty hold」操作、あるいは、
「duty hold」状態で「duty 50%」操作という流れでした。
手直し後は「duty 50%」でそれを維持してくれるという
ことで、ちょい、手間が省けるのではないかと。

duty holdの解除は、
 ・再度SW3を長押し。  あるいは
 ・W:パルス幅設定モードにしてエンコーダを操作。
    Wモードにしただけでは解除しません。
    パルス幅の値を変えた=デューティを変えたぞ
    でholdを解除します。
P:周期設定モードのままだとduty 50%を維持。
   ※周期設定値が奇数だときっちり50%にはな
    りません。

  ※動き、操作の詳細はスケッチを読んでね。
   ミスを発見したり、改善点があれば、コメント
   してください。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~
※追記 タイマー割り込みでのチャタリング除去処理

まず、INT0割り込みでのカウント処理の様子。
CRフィルタがないと、こんなミスカウントが出ます。
Ww11m
↓エッジだけが欲しいんですが、A相立ち上がりで
発生したチャタリングが悪さをしてミスカウントしています。

拡大
Ww12m
INT0 ↓エッジパルス割り込み処理では、
  ・直後のA相がHならミスなので除外
  ・前回の有効↓より4ms経過してないと除外
という処理を入れているのですが、それを逃れる
パルスが出現しています。
ハード的なローパスフィルタが必要です。

それをタイマー0.5ms割り込みで処理すると、
こんな具合に。
Ww21m
中央にジャギジャギが見えていますが、
  「0.5ms×4回で安定」
という処理で、ミスカウントは逃れています。

INT0エッジ割り込みか、タイマー割り込みか、
利点、欠点はそれぞれです。
・INT0エッジ割り込み
  即応答。 素早いカウントが可能。
  チャタリング対策はハードで。
    (エンコーダの特性に合ったCRフィルタを)
  INT1を使えば4逓倍処理も可能。
・タイマー割り込みで
  ポートにつなぐだけでOK。
  2相パルスが速いと抜かすかも。
  割り込み処理でいつも実行されるので
  パルス入力がないときでも時間が食われる。
     (もったいないぞ。 タダだけど)
  8bitのシフトレジスタなんで、この2相パルス
  に加えて、あと6つの信号のチャタリング除去
  もついでにできる。


※9月12日:エンコーダを素早く回した時の処理

前回のカウントパルスから20ms未満の時(早回し)の加速処理
をテーブルにしました。
スケッチの中、「const byte enc_spd[] PROGMEM」に
0~19msの加速パルスをテーブルにしています。
ご自由に。
   ・ダウンロード - p_gen16d.txt

バージョン:P-Gen16d  2022-09-12 です。




| | コメント (17)

2022年8月 7日 (日)

パルスジェネレータを作ってみた:16MHzクロックを追加

2022年8月 5日:Arduino-UNOでパルスジェネレータを作ってみた
この続きです。
クロック源、もう一つ「速いの」を追加しました。
これまでは、
 1us  1MHz
 10us 100kHz
 0.1ms 10kHz
 1ms  1kHz
の4種類でしたが、ハード的に得られる最高クロック
16MHzを追加しました。

クロック周期が「1/16us」で、「0.0625us」が最小単位と
なります。
小数以下の表示桁が4桁に増えちゃうわけです。
1クリックで1つ増減するのではなく「0.0625」増減します。
  ※同じ4桁でも、「0/16」「1/16」・・「15/16」と
   1/16で示す方が増減が分かりやすいかと思ったん
   ですが、どうでしょう?

整数部の最大が「4095」になるので、小数点を入れても
「9桁」になり、なんとか表示できました。

こんな具合。
Ta2

タイマー1のカウント値16bitの最大65535で、
「4095.9375us」となります。
  ※1MHzクロックだと1usピッチで最大が「65.535ms」
   でした。単純に周波数16倍。

クロックを変えるとこんな感じで表示が変わります。
Ta1

16MHzでの最下位カーソル位置が0.1桁のところになり
ここに合わせたときだけ0.0625ピッチで変化します。
しかし、10^0桁~10^3桁に持ってくると、ちゃんと
10進で変化するようになっていて、操作上の違和感は
ありません。

※その後:
2022年8月24日:パルスジェネレータを作ってみた:箱に入れた
をどうぞ。

| | コメント (3)

2022年8月 5日 (金)

Arduino-UNOでパルスジェネレータを作ってみた

ラジオペンチさんとvabenecosiさんのパルスジェネレータを
真似してみました。
・ラジオペンチさん
   http://radiopench.blog96.fc2.com/blog-entry-808.html
・vabenecosiさん
   http://vabenecosi.blog.fc2.com/blog-entry-37.html

そのままでは、面白くないので、
  ・トラ技 2010年7月号 ATtiny2313を使ったハンディ方形波発生器
の方式、
 ・16bitタイマー1のクロック入力T1に
 ・タイマー2で作った方形波を入れる
ということにしてみました。

クロック源となる方形波の周波数は、
1MHz 100kHz 10kHz 1kHzの4種類。

周期だと1us 10us 0.1ms 1msとなり、
これをクロック源として16ビットで設定できるので、
最大の周期は、
  1us : 65.535ms
  10us : 655.35ms
 0.1ms : 6.5535秒
  1ms : 65.535秒
となります。
  ※ATtiny2313では3桁のデジタルスイッチだったので
   もの足りなかった。

とりあえずArduino-UNOをベースに試してみました。
こんな回路。

Pg11

操作スイッチを4つにして、ロータリーエンコーダで
設定できる入力桁位置を設定できるようにしています。
   ※←(SW2) →(SW3)がカーソルスイッチ
カーソルより右の下位桁の値をそのままにして
左側(上位桁)の数値だけを±できるようにしました。

「12.345ms」で、2の位置にカーソルがある時CCW回しすると
→11.345ms
→10.345ms
→ 9.345ms
→ 8.345ms
→ 7.345ms
と、下位3桁はそのままにして上位の数値を-1します。

  ※左右のカーソルを独立させたので、操作スイッチは
   4つになっています。

また、スイッチの拡張機能操作は「二つ同時押し」
ではなく「長押し」にしています。

こんな操作画面です。
Ga001
右上がタイマー1の供給クロック周期。 (4種類)
右下が出力パルスの極性、PosとNegで表示。
周期設定の時は周期から周波数を計算して表示。

SW1の操作で周期設定とパルス幅設定を変えます。
Ga002
パルス幅設定の時は、デューティ比を表示します。

Ga003

操作する桁を固定できるので、大きく数値を変えることが
できます。
パルスの周期を0.2秒にしてパルス幅を変えてみました。
エンコーダーのつまみをあわてずに回すと、
デューティが「100% → 0% → 100%」と変化する
様子が見えます。

Pg21
デューティ100%と0%でH、Lに張り付かせる処理も
面白いかと。

・エンコーダの回転チェック
・操作スイッチの入力
この二つを割り込みで処理しています。

エンコーダをクルクル操作すると、周期やパルス幅が変わ
るので、その値の表示だけでなく、周波数とデューティー比
を計算して表示します。

その処理時間が「7~8ms」。
  ※液晶表示に時間がかかる

エンコーダのクルクルを速くすると・・・処理が間に合わ
なくなってきます。
でも、エンコーダのカウント処理は割り込みで動いているので、
up/down値の抜けはありません。

メイン側の計算・表示処理がつまってもカウント抜けは
しないというしかけになっています。

その様子です。
エンコーダを速く回した時、表示処理がつまっている
様子が見えています。
  ※loopの頭で出力ポートをトグルしているので、
   処理が間に合わなくなると、トグルが見えるように
   なります。
   描画中はトグルが止まります。
Pg22

テスト出力しているパルスで、表示処理中も、エンコーダ
の回転を読んでいる様子が見えています。

割り込みを使わずにエンコーダを読んでいたら・・・
   カウント抜けの可能性大です。

ラジオペンチさん、vabenecosiさんの回路そのままでは
動きません。
  ・OC2A 方形波出力 → T1クロック入力のつなぎ
  ・エンコーダをINT0、INT1に。
     (INT1は割り込みでは使っていない)
ということで、液晶やスイッチのポートが変わっています。

・回路図(BSch3Vファイル) 圧縮
      ダウンロード - pgen16a.zip

・スケッチ (ファイルタイプをtxtに)
      ダウンロード - p_gen16a.txt


さて、ケース入れを考えなければなりません。
電源は電池かな。


※追記 : 参考、関連あれこれ
     (勉強のために読んでみて)
2020年2月10日:ArduinoのanalogWrite 1/255なの?
2020年1月28日:Arduinoから「タイマー0」を取り上げる(ユーザーが使う)
2022年7月25日:Atmega328P タイマー/カウンタ1の高速PWM動作
2020年8月13日:Arduino、analogWriteは捨てちゃえ。ちゃんとしたPWMを使おう
2020年8月16日:Arduino UNOのPWM出力を(ちょっと精密に)確かめる
2020年9月19日:クリック有りのロータリーエンコーダーで
2019年4月1日:ArduinoのEEPROMアクセス、「EEMEM」について
2020年2月4日:Arduinoのタイマー OCRレジスタは「n」じゃなく「n - 1」の値を設定せよ
2022年3月11日:Arduino-UNO 割り込み処理のミスあれこれ:ロータリーエンコーダー
2018年10月11日:魔法の言葉「volatile」


| | コメント (2)

2022年7月26日 (火)

割り込みと絡むクリチカルな制御 変数にはVolatileを!

魔法の言葉「Volatile」、これ大事です。
先日の Atmega328P タイマー/カウンタ1の高速PWM動作
このスケッチ、「IR_FRQ_SCAN3.ino」。
  ※たくさん空いている出力ポートにパルスを出して、
   動きをオシロでチェックできるようにしています。

大まかな処理の流れは、

・5msごとに、LED駆動周波数であるPWM周波数と
 デューティ比を計算して、変数に保存。
・タイマー1割り込み内(PWM周波数と同じ)でその値を
 タイマー1レジスタに書き込み。

これを繰り返し、22kHz~54kHzの周波数で赤外線LEDを
光らせています。

スケッチから関連部分をプックアップ。

【関連する変数】
メインルーチンでセットして割り込みでハードウェアを制御

volatile byte f_frqset; // 周波数設定フラグ
volatile word led_div; // LED駆動 分周比 ICR1に設定
volatile word led_duty; // 駆動デューティ PWM設定値 OCR1Aに設定

【関連する処理:loop内】

// 5msサイクルloop
while(1){
if(f_5ms){ // 5ms経過
f_5ms = 0;
// LED PWM周波数とデューティ
PD4_H; // (!!!)
if(led_frq == FRQ_LO){ // LO周波数のとき
PD2_H; // (!!!) パルスH/L
nop(); nop(); nop(); nop();
PD2_L; // (!!!)
}
a = (word)(160000L / (long)led_frq); // 分周比 (1)
b = (2 * a) / 3; // デューティ 1/3で (2)
cli(); // いったん割り込み禁止にして (3)
led_div = a; // LED駆動周分周比
led_duty = b; // デューティ比
f_frqset = 1; // 設定on タイマー1割り込みで処理
sei(); // 割込再開
// 周波数スキャン D/A PWM出力 220~540を0~320にして1/2
OCR2A = 255 - ((led_frq - FRQ_LO) / 2); // PWM 0~
PD4_L; // (!!!)
}

【割り込み処理】

ISR(TIMER1_OVF_vect) // タイマー1割り込み TOP値で
{
static byte cyc = 0; // LEDonサイクル
PD7_H; // (!!!)
switch(f_frqset){ // 実行区分
case 1: // PWM 設定指令
PD3_H; // (!!!)
ICR1 = led_div - 1; // LED周波数分周比 ★
OCR1A = led_duty - 1; // LED駆動デューティ 1/3 ★
cyc = 8; // 8サイクルだけLEDをon
f_frqset = 2;
PD3_L; // (!!!)
break;
case 2: // サイクル数をチェックしてオフに
if(cyc) cyc--;
if(cyc == 0){ // ダウンカウントしてゼロになった
PD3_H; // (!!!)
if(INP_SENS){ // センサー入力 H/L ?
SENS_H; // ポート出力でH保持
f_senshl = 1;
}
else{
SENS_L; // ポート出力でL保持
f_senshl = 0;
}
OCR1A = 0xFFFF; // LED駆動オフに
f_sensok = 1; // センサー状態確定
f_frqset = 0; // 次の起動設定を待つ
PD3_L; // (!!!)
}
break;
}
PD7_L; // (!!!)
}

(1) 5msごとに分周比=PWMの周波数とデューティ比を計算。
(2) 結果をいったん a と b に入れておいて。
(3) 割り込み禁止状態で変数に保存。

この部分の処理が、変数に付けた「volatileの有無」で変わって
しまうのです。

まずvolatileを付けたとき。 オシロでタイミングを見ます。
Vl09

PWMを処理しているタイマー1割り込み、この場合は
一定の周期で繰り返しています。

ところがvolatileを外すと・・・

Vl07

ch1波形、約65uほどのHレベル区間が「(1)(2)(3)」を
処理している時間です。
  ※ch2の1ms割り込みが5ms周期を知らせている。

ch3のタイマー1割り込み、新周期と新デューティ値を
変数に書いているのが「★★★」のタイミングです。
どういうわけか、割り込みが待たされてしまい、
処理の開始が遅れています。

これ、volatileを取ってしまったため、コンパイラが
  「いちいち仮変数の a と b に入れんでもエエやん」
  「直接 led_div と led_dutyに書いたろ」
  「ちょっとは早なるで
  「いちおう書き込み前に cli() はしとこ」
  「書き終わったら sei() ね」
と判断したせいです。

コンパイラの「ちょっとでも速よう実行できるほうがエエやろ」
というおせっかい。 (最適化の指示)
ただ、計算の前から割り込み禁止としたんで、時間のかかる除算
割り込み禁止の中に入ってしまい、処理が長くなってタイマー1割り込み
が待たされてしまいました。
  ※(2)の除算が割り込み禁止の中に入ってました。
   (1)は禁止前の実行でした。
割り込みが待たされるということは・・・
ICR1でPWM周波数を決めている方法では、ちょっと怖いこと
(PWM処理の一周抜け)が起こるかもしれません。

※関連
2018年10月11日:魔法の言葉「volatile」
2016年02月19日:Arduinoのタイマー処理

※追記
コンパイルされたソースファイルで比較してみると。

★volatile有

7b4: 20 91 21 01 lds r18, 0x0121 ▲led_frq
7b8: 30 91 22 01 lds r19, 0x0122
7bc: 40 e0 ldi r20, 0x00 ; 0
7be: 50 e0 ldi r21, 0x00 ; 0
7c0: c5 01 movw r24, r10
7c2: b4 01 movw r22, r8
7c4: 0e 94 94 07 call 0xf28 ; 0xf28 <__divmodsi4>
7c8: 29 01 movw r4, r18
7ca: 3a 01 movw r6, r20
7cc: c9 01 movw r24, r18
7ce: 88 0f add r24, r24
7d0: 99 1f adc r25, r25
7d2: b6 01 movw r22, r12
7d4: 0e 94 80 07 call 0xf00 ; 0xf00 <__udivmodhi4>
7d8: f8 94 cli
7da: 50 92 20 01 sts 0x0120, r5 ▲led_div
7de: 40 92 1f 01 sts 0x011F, r4
7e2: 70 93 1e 01 sts 0x011E, r23 ▲led_duty
7e6: 60 93 1d 01 sts 0x011D, r22
7ea: 30 92 1c 01 sts 0x011C, r3 ▲f_frq_set
7ee: 78 94 sei
★volatile無

7b2: 20 91 21 01 lds r18, 0x0121 ▲led_frq
7b6: 30 91 22 01 lds r19, 0x0122
7ba: 40 e0 ldi r20, 0x00 ; 0
7bc: 50 e0 ldi r21, 0x00 ; 0
7be: c5 01 movw r24, r10
7c0: b4 01 movw r22, r8
7c2: 0e 94 92 07 call 0xf24 ; 0xf24 <__divmodsi4>
7c6: f8 94 cli
7c8: 30 93 20 01 sts 0x0120, r19 ▲led_div
7cc: 20 93 1f 01 sts 0x011F, r18
7d0: c9 01 movw r24, r18
7d2: 88 0f add r24, r24
7d4: 99 1f adc r25, r25
7d6: b6 01 movw r22, r12
7d8: 0e 94 7e 07 call 0xefc ; 0xefc <__udivmodhi4> ←★
7dc: 70 93 1e 01 sts 0x011E, r23 ▲led_duty
7e0: 60 93 1d 01 sts 0x011D, r22
7e4: 70 92 1c 01 sts 0x011C, r7 ▲f_frq_set
7e8: 78 94 sei

「★volatile無」だと、「cli()割り込み禁止~sei()割り込み許可」内に
割り算が入っています。
「★volatile有」だと割り算は外に。
これが割り込み処理の遅れにつながっています。

| | コメント (0)

2022年7月25日 (月)

Atmega328P タイマー/カウンタ1の高速PWM動作

Arduino-UNOで使われているAtmega328P、この高速PWM動作
の解説が「なんのこっちゃ?」になっていませんか?
8,9,10bit固定値での高速PWMだとTOP値が,255,511,1023
と固定されるので理解しやすいかと思いますが、
ICR1あるいはOCR1AレジスタでTOP値を決めたい時に
(PWM周期を動かしたい)、データシートには
ややこしい注意が記してあります

かいつまんで、要点を。

 ・OCR1Aは2重緩衝されます
 ・ICR1は2重緩衝されません

まず、この「2重緩衝」ってなに? でしょうか。

 ・ICR1がTOP値を定義するのに使われるとき、ICR1を
  更新する手順はOCR1Aの更新と異なります。
 ・TOPを定義するのにICR1を使うことは決まった
  TOP値を使う時に上手くいきます

この解説、PWM周期を可変したいときにICR1を使うと
  「上手くいかないかも
ということと理解できます。
その理由。

 ・ICR1が小さな値に変更される場合、書かれた
  新しいICR1値がTCNT1の現在値よりも小さくな
  る危険を意味します。
 ・その後の結果はカウンタが(その回の)TOP値での
  比較一致を失うことです。

つまり、ICR1でPWM波を作っているとき、PWM周期を短く
しようとしたときが「危ない」ぞっということなのです。
もしTNCT1より小さくしてしまったら、次の一致のため
にTCNT1が一周する(FFFF→0000を通過)のを待たなけれ
ばなりません。
PWMパルスの抜けが生じてしまいます。
  ※16MHzクロックだと1/65536で244Hz。
   4m秒ほどの抜け(だんまり)が出てしまう
   わけです。

このICR1に対して、OCR1Aを使った時は、

 ・この特徴は何時でも書かれることをOCR1Aの
  I/O位置に許します。
 ・OCR1A I/O位置が書かれる時に書かれた値は
  OCR1A緩衝部に置かれます。
 ・OCR1A(比較)レジスタはその後にTCNT1がTOPと一致
  した次のタイマ/カウンタ クロック周期にOCR1A緩衝部の
  値で更新されます。
 ・基準PWM周波数が(TOP値を変更することによって)
  動的に変更される場合、OCR1Aが2重緩衝機能のため、
  TOPとしてOCR1Aを使うことは明らかに良い選択です

となって、OCR1Aを使うと、次サイクルのTOP値一致で
PWM周期が更新されるのです。
ですので、ICR1の時のように「一周抜け」の心配はあり
ません。
  ※でも、出力できるPWMは一つだけ
   なってしまいます。

その違いを
  ・周波数つながりで、赤外線受光モジュールのBPF周波数
のスケッチをちょっと変更して確かめてみました。
ICR1をTOP値にするか、OCR1AをTOPにするかを#defineで
決めます。
OCR1AをTOP値にするとPWMのデューティ設定はOCR1Bで、
つまりOC1B端子が出力になるので、出力のポートが変わります。

見ているのは、発生周波数が54kHz→22kHzに変わる
タイミングです。
  ※トリガー用のテストパルスを出しています。
こんな具合にLED駆動出力(8波)が変わります。
★1と★2が注目点です。

Cc01_20220724152301
Cc00

「▼出力開始処理」のタイミングでPWM周期とデューティを
変えています。
まず、その次の割り込み処理までの時間が異なっています。
そしてLED駆動パルスが出るタイミング(OCR1Aあるいは
OCR1Bが反映される)も違いが見えます。

「BPF周波数チェック」のスケッチでは、タイマー1
割り込み内でTOP値を変えていますので、TCNT1は
「0000」からちょっとしか進んでおらず、新設定する
ICR1がTCNT1より小さくなることはありません。

しかし、「注意せよ!」は変わりありませんので。
  ※タイミングの連続性がズレてよいのなら
   新ICR1設定前にTCNT1をクリアしてし
   まうとかかなぁ。

※サンプルスケッチ (.txtにしています)

  ・ダウンロード - ir_frq_scan3.txt


※関連
  ・2022年7月26日:割り込みと絡むクリチカルな制御 変数にはVolatileを!


| | コメント (0)

2022年7月23日 (土)

周波数つながりで、赤外線受光モジュールのBPF周波数

2022年7月10日:百均屋さんの紫外線LEDランプ
2022年7月7日:「糸ようじ」のプラケースを使うシリーズ:赤外線リモコンチェッカー
2022年7月8日:手持ちの赤外線リモコン受光モジュールを発掘してみたら・・・
ということで、周波数や波長をあれこれと考えておりました。

発掘した赤外線リモコン受光モジュールが6種類。
このセンサのBPF周波数、つまり赤外線のキャリアー周波数が
どんなものなのかを計ってみました。

通常は「38kHz」。
品種によってはもっと低い周波数や高い周波数が使われると。
そこで・・・
 ・Arduino-UNOをベースに。
 ・22kHz~54kHzの範囲で周波数を可変。
    中心を38kHzにして±16kHz 
 ・この周波数で赤外光LEDを点灯。
    駆動デューティは1/3
 ・駆動するパルスは「8波」。
 ・5msごとに周波数を200Hzステップで
  変えながらパルスを出力。
 ・160ステップなので0.8秒サイクル。
 ・赤外線モジュールからの応答を見れば
  どのあたりの周波数を拾うのか分かる。
 ・けっこう感度が良いので赤外LEDの駆動
  電流をボリュームで絞れるように。
 ・オシロスコープで観察できるように
  周波数スキャンの様子をPWMでD/Aし、
  ノコギリ波にして出力。
 ・シリアルでも出力。
   5msに間に合うよう115.2kBPSに
 ・センサーからの信号を8波パルスをオフする
  タイミングでラッチしてPB2へ出力。

まず、こんな回路。
E3

LED駆動の様子です。
E1_20220723161901
8波の駆動パルスを出して、その応答を見ます。
  反応無しなら「H」。
  反応があれば「L」。

周波数をスキャンして「L」が続くところが、
BPFの中心周波数になると想像できます。

E2

LEDとセンサーをくっつけると全周波数でLになってしまい、
応答周波数が分からなくなってしまいます。
LEDの電流を小さくして、LEDとセンサーをちょっと
離して様子を見ます。
センサーの種類により、ずいぶんと感度が異なります。
応答感度の中央が見れるよう適当に調整。
  ※センサーの出力信号はデジタルなので、
   強弱はわかりません。
   そこで応答周波数の「幅」から、中心を想像します。
  ※センサーをソケットで抜き差しできるよう
   「OUT GND VCC」の順にピンヘッダに
   ハンダしています。

実験の様子
C01_20220723163901

結果。
センサーの写真とオシロ波形。
オシロの上段波形がスキャンしている周波数に比例した
ノコギリ波。
下段がセンサーの応答。 (Lで検出)

【01】
01_20220723162401
B001_20220723162401  

【02】
02_20220723162501
B002_20220723162501

【03】
03_20220723162701
B003_20220723162801

【04】
04
B004_20220723162801

【05】
05
B005_20220723162801

【06】
06
B006_20220723162901

このように6つある中で4つの中心周波数はほぼ「38kHz」。
2つが「低い目」で、「28~32kHz」あたりになっているのが
浮かんできました。

実質、LEDをセンサーに近づけると、フィルタ周波数の
違いに関係なく応答しちゃいそうでので、到達距離を問題に
しない限り、気にしなくて良さそうです。

実行中の様子
D01

割り込み処理の様子
D04

シリアル出力の様子

 22.0kHz 1 1/727
 22.2kHz 1 1/720
   :
 37.8kHz 0 1/423
 38.0kHz 0 1/421
 38.2kHz 0 1/418
   :
 53.8kHz 1 1/297
 54.0kHz 1 1/296

22kHz~54kHzを繰り返す
先頭から、
 LED駆動周波数(8波の)
 センサー応答 0で検出
 周波数を計算する「16MHz/n」のn値

※制御スケッチ ファイルタイプを .ino→ .txtに。
    ・ダウンロード - ir_frq_scan2.txt

・タイマー0をシステムから取り上げて1msタイマー
 割り込み処理。
    ※delayやmillisが使えないので注意
・タイマー1のPWM出力でLED駆動パルスを生成。
・タイマー2のPWM出力でノコギリ波発生用D/Aを出力。

※参考
  ・Atmega328P タイマー/カウンタ1の高速PWM動作




| | コメント (0)

2022年7月18日 (月)

秋月電子16文字x2行のI2Cインターフェース液晶AQM1602Y

8文字x2行のI2Cインターフェースの液晶表示器は
  2021年7月2日:秋月の液晶表示器 ACM0802C-NLW-BBW-IIC、I2Cのプルアップ抵抗
で試していました。

今回は16文字×2行の
  AQM1602Y-FLW-FBW (秋月電子)
これをあれこれと・・・

横幅44mm×縦25.5mmと16文字表示なのにたいへん小型。
  ※ACM0802は58×32mm
標準が電源電圧3.3Vで、5V電源でも使えるぞという仕様です。
V50_10
ACM0802C-IICは昔からのHD44780と同じ制御手順ですが、
AQM1602は
 ・3.3Vと5Vで「booster circuit」を使うか使わないかを
  設定しなくちゃならない。
 ・コントラストはコマンドで設定。
    ボリュームじゃない。
 ・booster用のコンデンサが必要。
ということで、ちゃんと使おうとすると、ちょっと考えなけ
ればなりません。
  ※データシートやみなさんのサンプルを読み漁って

まず回路。 5V動作のArduino-UNOだとこんなつなぎ。
Aa22_20220718104401
まず確かめておきたかったのが、I2C(SDA、SCL)ライン
プルアップ抵抗
AQM0802-IICでは、ドライブ能力の不足を感じたんで、
2.2KΩ→10KΩにしました。
抵抗を変えて波形を見てみると、今回のAQM1602も同じ
ようなドライブ能力でした。
4.7KΩだとレベルが上がり、ちょっと気になる0.75Vほど。
10KΩで0.43V、22KΩで0.33V。
  ※10KΩでOKでしょう。

もう一つがbooster circuit:チャージポンプのコンデンサ。
3.3V電源だとこれを働かせるんで必須ですが、これをオフに
する5V電源だと省けるのではないか?という確認です。
  ※省けました。
   VOUTのまで外すと0.5V(p-p)くらいの
   リップルが見えますが、見栄えや動作に
   は関係なしでした。
Aa21_20220718104401

さらにどうなるか気になったのがコントラストの
設定値。
6bit:0~63の値で設定できます。
秋月のリーフレットには
  『5Vの場合35で少し濃いめ』と記されています。
これを見ておきます。

まず5V電源から。
  右下の「<>」内の数字がコントラスト設定値。
  右上の0~9は「生きてるよ」表示。
  1秒ごとにインクリメント。
Aq50

コントラスト設定値が5あたりから文字が出始めます。
30を越えると最大の63までほとんど変化なし。
真っ黒になって見えなくなるということはありません
でした。

次が3.3V電源
  ※5Vと同じバックライトLED駆動抵抗です
   ので、少し暗くなってます。

Aq33
20を越えたあたりから文字が見え出して(背景白から)
40を越えると背景が黒に。

5Vでも3.3Vでも、秋月の言う「35」あたりで良いのかと。

3.3V電源でのコントラストを変えると、VO電圧と
VOUT電圧が次のように変化します。

・Follower control : FON = 1 Rb2,1,0 = 1,0,0
・Power control : Bon = 1

 コントラスト VO  VOUT
  設定値   電圧 電圧
 --------------------------
    0   2.40V 6.52V
    16   3.44V 6.51V
    32   4.48V 6.49V
    48   5.52V 6.47V
    63   6.42V 6.42V

Bonをオンする3.3V動作では、電源電圧3.3Vの
約2倍(近く)の電圧がVOUTに出ています。

Bonオフの5Vでは5Vより少し低い電圧のままで
ほとんど変化はありませんでした。

・コントラスト設定テストプログラム Arduino-UNO用

   ダウンロード - test_i2c_aqm1602y_a.zip

※Arduino-IDEのシリアルモニターは1行をバッファ
 するので、単純に1文字出力する「Tera Term」など
 のターミナルを使って。
 数字入力1と0でコントラスト値を+/-。

※beginの時に文字数、行数に加えて使用電圧区分を設定
  LCD.begin(16, 2, LCD_V5R0); // LCD初期化 文字数、行数設定 (16文字x2行) 5.0V電源
 // LCD.begin(16, 2, LCD_V3R3); // LCD初期化 文字数、行数設定 (16文字x2行) 3.3V電源
 5Vの時はbooster circuitをオフ。「Bon=0」に。
 3.3Vの時にBon=1。

※「void setContrast(uint8_t n);」でコントラスト(0~63)を設定。
 「uint8_t getContrast();」で現在のコントラスト値を読み出し。

※追記
booster circuitオン時の電源電圧とVOUT電圧。
コントラスト設定値は35で。
  最大定格ではV0=7.0Vとなっています。
  booster circuitをオンすると電源電圧の約2倍の
  電圧がVOUTに出てきます。

電源電圧  VOUT電圧
------------
  3.3V   6.45V
  3.4V   6.66V
  3.5V   6.84V
  3.6V   7.04V
  3.7V   7.22V
  3.8V   7.43V
  3.9V   7.63V
  4.0V   7.82V

 ※これ以上にするのは怖かったんでストップ。

 

| | コメント (0)

2022年7月 7日 (木)

シリアル通信ボーレートとクロック周波数

デジタルテスター「FLUKE 87IV」の赤外線通信ユニット完成では
4.9152MHzの水晶を使って9600BPSのボーレートを得ました。

シリアル通信のボーレート、昔々は5単位テレテイプの45.5BPS。
マイコンになって110BPS、そして300BPS。
今は最低が1200BPSでしょうか。
9600BPSまでは2倍飛びで2400、4800BPS。
そして19.2kの中間に、9600を1.5倍した14.4kBPSが入っています。
ここから、両方で2倍飛びの値が使われます。

Arduino-UNOの場合、元クロックが16MHzです。
ボーレートのクロック分周比は、BPS値を16倍した値で16MHzを割る。
あるいは16MHz÷16÷BPS値で求められます

1200BPS~115.2kBPSまでその分周比を見てみると・・・
  ・・・割り切れません。
誤差(%)とともに表にしました。

      |   16.000MHz 
------|-------------
1200 | 833 0.04
2400 | 417 -0.08
4800 | 208 0.16
9600 | 104 0.16
14.4k | 69 0.64
19.2k | 52 0.16
28.8k | 35 -0.79
38.4k | 26 0.16
57.6k | 17 2.12
76.8k | 13 0.16
115.2k| 9 -3.55
↑ ↑
1/n 誤差(%)
   ATmega328のBRR(ボーレートレジスタ)へは
   「n-1」を設定する。

見ての通り、どのボーレートも誤差を含んでいて、
115.2kBPSはけっこう大きな値です。
10bit分で3割り越えですから、ちょっと気になります。
水晶発振子じゃなくセラミック発振子だと、素子が持つ
誤差も加わります。

  ※注:追記
   Arduino-UNOのボーレート設定、通常のX16クロックでは
   なく「X8」の倍速設定(UCSRAのU2Xをオン)が使われています。
   ですので、上の表のn値は2倍の値を設定できるということで
   割り切れなくても、微調できます。
   そして、誤差はおよそ1/2になります。
   115.2kなら16MHz/8/115.2k=17となり、
   結果、誤差は2.12%となります。(それでも大きいぞ)

5V電源での最高速20MHzでも割り切れずに誤差が発生します。

      |    20.000MHz
------|--------------
1200 | 1042 -0.03
2400 | 521 -0.03
4800 | 260 0.16
9600 | 130 0.16
14.4k | 87 -0.22
19.2k | 65 0.16
28.8k | 43 0.94
38.4k | 33 -1.36
57.6k | 22 -1.36
76.8k | 16 1.73
115.2k| 11 -1.36


誤差を生じないボーレートのクロックを作るには
ちょっと中途半端な周波数が必要です。
これらの周波数だと、1200BPSから115.2kBPSまで誤差ゼロ
が実現できます。

      | 7.3728 11.0592 14.7456 18.432
------|------------------------------
1200 | 384 576 768 960
2400 | 192 288 384 480
4800 | 96 144 192 240
9600 | 48 72 96 120
14.4k | 32 48 64 80
19.2k | 24 36 48 60
28.8k | 16 24 32 40
38.4k | 12 18 24 30
57.6k | 8 12 16 20
76.8k | 6 9 12 15
115.2k| 4 6 8 10

FLUKE 87IV - IrDAで使った4.9152MHzだと、
14.4kなどの1.5倍系のボーレートで誤差を生じます。

      | 4.608 4.9152 6.144 9.216 9.8304 19.6608
------|----------------------------------------
1200 | 240 256 320 480 512 1024
2400 | 120 128 160 240 256 512
4800 | 60 64 80 120 128 256
9600 | 30 32 40 60 64 128
14.4k | 20 - - 40 - -
19.2k | 15 16 20 30 32 64
28.8k | 10 - - 20 - -
38.4k | - 8 10 15 16 32
57.6k | 5 - - 10 - -
76.8k | - 4 5 - 8 16
115.2k| - - - 5 - -

  ※「-」は割り切れないことを示す。

   ※9.216MHzは18.432MHzの1/2

高ボーレート(通信速度が速く)になると、送受の割り込み処理時間
が問題になってきます。
115.2kだと1文字が86.8us
誤差のないメインクロックを選んだとしても、クロックを低く
してまうと、他の割込処理との輻輳を見ておかなければなりません。
送信は待ってくれますが、受信が間に合わないと文字抜けが生じ
ます。
マイコンの能力に見合ったボーレートを選ぶということで。

もうひとつ。
制御系で欲しいのがきっちりの「1ms」タイマー
割り切れるボーレート設定値と内部のタイマー設定値。
18.432MHzが魔法の数字のように思えてきます。
  ボーレートはOK。
  1msは18.432MHz ÷ 256 ÷ 72で1kHz。
  あるいは18.432MHz ÷ 1024 ÷ 18で1kHz。

この周波数、TK-80のクロックでした。
  ・2018年6月6日:マイコンのリセット回路
8224:クロックジェネレータICにつながってる
水晶がそれです。
これを9分周した2.048MHzが8080マイコンの動作周波数でした。

※追記
今までのAVRマイコンでは「分周比=16MHz÷16÷BPS値」
という計算で、分周比を求めていましたが、新しいAVRマイコン
では分周比に小数(例えば6bit分)が使えるようになっています。
結果的に分周比の計算は「16MHz×4÷BPS値」となり、
細かな設定ができるように考えられています。

| | コメント (0)

より以前の記事一覧