AVRマイコン

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進で変化するようになっていて、操作上の違和感は
ありません。

ケースに入れる回路案です。
電池2本でDC-DCで5Vに昇圧かと。

Aa11_20220807163101
出力バッファなしでダイレクトに出力。
MOS-FETはリレーなどの駆動用に。

| | コメント (2)

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」


| | コメント (1)

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月 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)

2022年7月 6日 (水)

デジタルテスター「FLUKE 87IV」の赤外線通信ユニット完成

2022年6月29日:デジタルテスター「FLUKE 87IV」の赤外線通信
で、「こりゃ作らなあかんなぁ~」と思い立った
IrDAによるデータ吸い上げユニット」、できました。

こんな回路です。
Ab1_20220706153701

秋月のIrDAモジュール:AE-RPM851A  と
USBシリアル変換モジュール:AE-CH340E-TYPEC
それに 「MCP2122」と 「ATTINY841」 マイコン。

クロックは水晶発振子。
通信速度が9600BPSだけということで、4.9152MHz。
  16MHzで動いているArduino-UNOの処理速度、
  けっこう速く感じますが、4.9152MHzだと
  「ちょっと遅いな~」っという感じがします。

小さなプラケース(ダイソーで入手したけど、もう売ってない)
に入れました。
Ab22
赤外線部は穴をあけなくてもプラケースを通してやりとり
できています。
50cmほど離しても大丈夫。
  でも、1mは届きません。

Ab21

電源はUSBから供給。
PCを起動しなくてもSDカードでシリアルデータロガー
が使えるように、TTLレベルでも出力しています。

通信している間は、テスターのオートパワーオフは延長されていて
勝手に電源が切れるということはありませんでした。

・起動メッセージ タイトル表示
 # FLUKE model 87IV IrDA com reader
 # Op-key       (2022-07-05)
 # 1 : Start measure  ←PCでのキー操作は0,1,2の3つ
 # 2 : Stop
 # 0 : Clear cyc counter
 # FLUKE 87, V1.01,75530000 ←IDコマンドで機種名取得
   0 +0.0576 V AC  ←勝手にデータ取得開始
   1 +0.0576 V AC
   2 +0.0576 V AC  ←1秒サイクルで読み出し
   3 +0.0576 V AC
   4 +0.0576 V AC
 # Stop.  ←「2」キーで停止
 # Start.  ←「1」キーで再開
   12 +0.0575 V AC  ←停止中もサイクル秒値を進めてる
   13 +0.0575 V AC
   14 +0.0575 V AC
 # Clr cyc.  ←「0」キーでサイクル秒値をクリア
   0 +0.0576 V AC ←測定は継続
   1 +0.0576 V AC
   2 +0.0576 V AC
 # Stop.     ←停止
 # Clr & Start.  ←クリアして測定再開
   0 +0.0577 V AC
   1 +0.0578 V AC
   2 +0.0578 V AC
   3 +0.0578 V AC
   4 +0.0578 V AC
   5 +0.0708 mV AC ←途中でレンジを変えている
   6 +30.080 mV AC
   7 +24.909 mV AC
   8 +40.721 mV AC
   9 + Out of range. V DC ←オーバーレンジの表示
   10 +0.0000 V DC
   11 +0.0000 V DC
   12 +0.0000 V DC
   13 +0.0000 mV DC
   14 +00.022 mV DC
   15 +00.023 mV DC
   16 +00.023 mOhms
   17 + Out of range. kOhms
   18 + Out of range. MOhms ←抵抗レンジ オープンだ
   19 + Out of range. MOhms
   20 +01.474 mV DC
   21 +01.758 mV DC
   22 +0.0008 V DC
   23 +0.0008 mV AC
   24 +40.148 mV AC
 # Err  ←通信遮断でエラー表示
   41 + Out of range. mV AC ←通信回復で自動的に測定再開
   42 +43.682 mV AC  ←サイクル秒値を見ればエラーが
   43 +43.174 mV AC   生じていた時間が分かる
 
・起動直後、テスターとの通信
   赤外線でやりとりできるか?
   機番を読めるか? をチェック
(太字がテスターからの応答)
<CR> 「0x0d」を送って応答を見る。
 1
<CR>  4回実行
 1
<CR>
 1
<CR>
 1
ID<CR>  「ID」コマンドを送って機番を待つ
 0
 FLUKE 87, V1.01,75530000
QM<CR>  測定開始「QM」コマンドでデータ要求
 0
 QM,+0.0434 V AC
QM<CR>
 0
 QM,+0.0435 V AC
QM<CR>
 0
 QM,+0.0435 V AC
    応答文字の「0」と「QM,」を除いてから、
   測定データとしてPCに送信しています。
起動直後あるいは測定中に、テスターとの通信が失敗しても
1秒ごとに「QM」コマンドを送っています。
テスターから応答が返ってくればそこから自動的に測定が
再開されます。

※制御プログラム
  Atmel studio 7.0を使いました。
  ソースは「main.c」1本。
  関連ファイルといっしょに圧縮しています。
    ダウンロード - fluke87_irda_tn841.zip

  割り込み処理しているのは2つのシリアル送受だけ。
  タイマーはフラグを見ています。
  メインの中でタイムアップをチェックして計時処理。

| | コメント (0)

2022年6月30日 (木)

AVRマイコン、ややこしいぞ。

アトメルからマイクロチップになったせいなのでしょうか、
AVRマイコンの品揃えが増えてややこしくなっています。
  ※私の情報収集が追いついてい
   ないだけなんでしょうけど・・・

・ATmega328PB
Arduino-UNOのチップがATmega328P
28pinのDIPなんで便利なんですが、ATmega328PBという
のが出てます。
パッケージは32pinのQFP。
拡張されたのが、
  16bitタイマー 1つが3つに
  シリアル   1つが2つに (SPI,I2Cも)
そして安価に。
16bitタイマーの増設はありがたい。
ラジオペンチさんとこの、
  Arduino のタイマー1 インプットキャプチャでパルス幅を精密測定
が、外付けハードなしでできそう。

・ATtiny824

14pinのAVRマイコン。
これまでATtiny24、44、84を使ってた。
それが、ATtiny441、841になって、シリアルとタイマーが
2つに増えて便利に。

そして、今回気付いたのが、安価版のATtiny824
残念なのが外付け水晶発振(16MHzなどの高周波用)ができない。
  ※32.768kHzの時計用水晶は可
さらに、もうひとつ安いのが、ATtiny814
ただしシリアルは1つ。

「安価」なのは魅力だけど、高周波水晶発振は必要かと思う。

※追記

発振子が使える従来のマイコンが便利か、
安価だけど外部クロックが欲しい時は発振器が
必要なマイコン。
どっちがエエんでしょうね。

Arduino-UNOでも使われている16MHzの発振子、
Digi-Keyをちょいと見てみると・・・ 金額はざっと
・胴長の水晶 HC-49U   100円
・足長の水晶 HC-49U/S  50円
・面実装水晶      60円
・C付足有セラミック   50円
・C付面実装セラミック  40円

・足有発振器      400円
・面実装発振器     180円

「発振子」ならマイコンの電源電圧で使えますが、
発振器となると、使える電源電圧に注意が必要です。
「定格5V」と記されているのもありますし、
「1.6~3.6V」と5Vでは使えないものもあります。

水晶発振子が使えないマイコン、安価になっても
安定したクロックが欲しい時は外部からのクロック
供給が必要です。
その発振器の価格がプラスされ、電源に気を使うと
いう事態になっちゃいます。
どうしたものでしょね。
まぁ、適材適所ということで。
  ※従来品の安定供給が心配か・・・

※追記
14pinのATtiny441 ATtiny841、入力ポートのプルアップ方法
が変わっているので要注意です。
今までと同じやり方だとプルアップしてくれません。

同じ14pinのATtiny24,44,84は昔ながらの方法。
DDRレジスタを0にして入力にしたとき、PORT
レジスタが1ならプルアップがオンという、これまで
どおりのAVRマイコン。

ところが、441、841はPUEA、PUEBというプルアップ
指定専用のレジスタが追加になっています。
  ※SBI、CBI命令の範囲外アドレス。

| | コメント (0)

2022年6月25日 (土)

AVRマイコンのSUT、BODヒューズ

Arduino-UNOのようにIPLがプログラムされた出来合いの
マイコンを使う時はなにも考えなくて済むのですが、
「UART←→IrDA」変換IC、MCP2122のクロック供給 #2
でのATtiny13AやATtiny25~85のようにAVRマイコンを
「裸」のままプログラムする時に忘れてはならないのが
ヒューズビットの書き換えです。

  ※これをしないままだと・・・
    「動いたけど、なんか遅いぞ
    「なにやら、音が低い
   なんてことが生じます。
   これはCKDIV8ヒューズ
   デフォルトである1/8になったまま
   だから。  よくある話です。

今回、ATtiny13Aをゴソゴソしたので、
BODレベル」と「SUT:Start up time」の様子を調べて
おきました。

この設定、二つのヒューズデータに分かれています。
Ee31
Ee32

BODが電圧低下検出回路の電圧設定。
Ee1_20220625101601

SUT
ではBOD有効、Fast rising powerか
Slowly rising powerかを切り替えます。
Ee2_20220625101601

この表を見て・・・疑問が生じませんか?
「SUT 0,0」だと「BOD enabel」。
ところが「0,1」や「1,0」になるとrising powerのことが
書かれていますが、BODがどうなるか記されていません。

この表の読み方として、
  ・BODを使いたい時は「SUT 0,0」に。
     これはこう読めます。

  ・SUT 0,1や1,0だとBODはオフになるのか、
   それともオンにしてくれるのか?
     表からの情報はありません。

SUT 0,1 1,0でどうなるのかを実際に確かめてみました。

上側の波形はタイマーコンペアマッチ出力(153.6kHz出力)。
初期設定でパルスを出しています。
下側が電源電圧。 ゆっくり5Vまで上昇させてからオフ。

BODのヒューズは4.3Vに設定。
まず、SUT 0,0で「BOD enable」に設定。
Ee41

電源電圧がBODに到達後、リセットが解除されて
すぐにプログラムが走り始め、出力パルスが出ています。

次にSUT 1,0にしてSlowly rising powerに。
Ee42

電源電圧がBODに到達後、少し遅れてから
リセットが解除されて走り始めている様子が見えます。

つまり、SUT 0,1 1,0でも設定したBODが有効になる
ということで、BODは無視されるのかという心配は無用
なりました。

| | コメント (0)

2022年6月24日 (金)

「UART←→IrDA」変換IC、MCP2122のクロック供給 #2

MCP2122のクロック供給(ボーレートの16倍の周波数)、8ピン・マイコンで
処理しようとすると、こんな感じかな。

まず、外部発振子が使える「ATtiny25~85」。
Cal20
「4.9152MHz」の発振子から素直に153.6kHzが作れます。
水晶でもセラロックでもOKというところ。

内部クロックしかダメな「ATtiny13A」の場合、
「工場出荷」状態のクロック精度が「10%」。
Cal12
ちょっときびしい。

ATtiny13Aだと、OSCCAL値を変えることでクロック周波数が
変化します。
Cal11
代表特性だと、9.6MHzで90くらいでしょうか。
  ※しかし、これがけっこう変動

そこで・・・まず問題。
9.6MHzから153.6kHzを出したいんだけれど・・・割り切れない。
クロック 9.6MHz÷153.6kHz = 62.5
デューティ50%の方形波駆動なのでタイマーの設定は1/62。
  9.6MHz÷62 = 154.839kHz
解決方法として、内蔵クロックを9.6MHzにするのではなく、
  153.6kHz × 62 = 9.5232MHz
  153.6kHz × 64 = 9.8304MHz
に調整すると1/62あるいは1/64で誤差が出なくなる
分周比が使えるようになります。
  9.8304MHzはATtiny25で図示した
  4.9152MHzの倍の周波数。

問題はこの校正方法。
こんな回路にしてみました。
Cal21
小さい字で書いていますが、
TP2に「1kHzパルス」を入れ込んでリセットしたら、
1kHzの周期を数えてそこから9.8304MHz近傍の
OSCCAL値を出そうという試み。
結果は内蔵フラッシュROMに自分書きして保存。
ATtiny13Aでの「Self-programming」は初体験。
  ※何かいけないようなことをしている気分・・・
EEPROMでも良かったのですが、
  プログラムの中に置いたデータを書き換えるのは
  どうすれば?
  ATtiny13AのSPM命令、どう使うんだ?
  SPMCSRレジスタ、制御の手順は?
の検証です。
ATtiny13AのROMメモリーは、わずかに1kバイト。
制御はとうぜんですが、アセンブラー。
  なかなか楽しい。

※ATtiny13A制御のソースファイル(圧縮)
  ・ダウンロード - irda_x16_1.zip
ちょっと手直しして↓
  ・ダウンロード - irda_x16_1a.zip

※ATtiny25を使う場合 (XTAL必要)
  ・ダウンロード - irda_tn25_1.zip

     ご自由にどうぞ。

・ヒューズの設定はソースファイル内に記しています。
   正しく設定しないと動きません。

・ATtiny25だとプログラムはわずか58バイト。
 RAMエリアの初期化ルーチンもありません。

・AVRマイコン・ATtiny13Aのフラッシュメモリ
 「自己プログラミング」の例題になるかと思います。
 この時しか使わない「AVRマイコンのSPM命令」
 その使い方のサンプルにどうぞ。
 アセンブラで書けばマイコンへ命令している
 様子がよくわかるかと。
 「C」はいろんな関数があって便利なんですが、
 あれこれ余計にところ(いろんなマイコンに対応)まで
 記述されていて「ほんまはどう動いてんねん!?」が
 隠れてしまうことがありますんで、困ったものです。

そうそう。「自己プログラミング」する時は
  AVRマイコンのSUT、BODヒューズ
の中の「SELFPRGEN」ヒューズを「許可=0に」しなくちゃ
いけません。

※1kHzクロックパルス供給によるOSCCALの校正実行
校正処理、こんなタイミングです。
2秒ほどで終了。
Cal11_20220625113301
OSCCALに設定する値を0から順に+1。(maxは127)
その時の1kHz↓エッジパルス間隔をソフトのループでカウント。
目標9.8304MHzと処理サイクル数から欲しいカウント値を
越えたところで停止。
今のと直前のカウント値を見て、どちらのOSCCAL設定を
使うかを決めています。
オシロ波形、PB4に出してる確認パルスを数えれば、
どれだけOSCCALを変えたかが分かります。
その値をフラッシュROMに記録(自己プログラミング)して、
次回の起動はその値を使ってという流れになります。
  フラッシュROMの空きには、そのあたりの
  情報も書いてますんで、読み出せば校正の結果が
  見えます。

※実験(デバッグ)してる様子
12_20220625155501
SOPのATtiny13Aを4つ用意して(DIP変換基板にハンダ)
あれこれと。

※ATtiny13Aでの校正値の取得方法
 MCP2122への供給クロックは153.6kHz。
 153.6kHz* 64 = 9.8304MHz。←これに合わせ込む

(1) OSCCALを0から順に+1。 maxは127。
(2) INT0入力1kHzパルスの↓エッジ間の時間を
  ループカンタで計測。
(3) 目標の「9.8304MHz」は1msで=9830クロック。
  そして、ループのサイクル数が7。
(4) 9830/7で「1404」が判定するカウント値。
(5) この値と同じか大きくなったらOCSCALの
  インクリメントを停止。
(6) 直前のカウント値と停止したカウント値、
  どちらが「1404」に近いかを判断して、
  どちらのOSCCALを使うか(今のか直前のか)
  を判断。
(7) 結果をフラッシュROM(0x01F0~0x01FF)に残す。

・フラッシュROMに残すデータ(wordで示す)

0x01F0: 1kHz校正データ これをOSCCALとして使う。(a)
    CNT_H検出時のOSCCALデータ。
0x01F1: 工場出荷時のOSCCALLデータ。
    0x00
0x01F2: 1kHzチェックカウンタ L側。
    カウント越え直前のカウント値
0x01F3: 1kHzチェックカウンタ H側。
    1404カウントを越えた時のカウント値

   ※欲しいのは(a)のデータだけ。
    あとはチェック用。

・校正例 チップ#2
工場出荷時のOSCCAL値    65
工場出荷時OSCCALでの周波数 149.9kHz
校正後のOSCCAL       67
校正後の周波数       153.1kHz
直前のカウント値      1397 ←1404に近い
直前のOSCCAL値       67  これを使う
検出カウント値       1413
検出OSCCAL値        68

・チップ#4
工場出荷時のOSCCAL値    77
工場出荷時OSCCALでの周波数 142.0kHz
校正後のOSCCAL       84
校正後の周波数       153.3kHz
直前のカウント値      1392
直前のOSCCAL値       83
検出カウント値       1407 ←1404に近い
検出のOSCCAL値       84  これを使う

 

| | コメント (1)

2020年8月13日 (木)

Arduino、analogWriteは捨てちゃえ。ちゃんとしたPWMを使おう

※検索「arduino analogWrite」。

ArduinoのanalogWrite、「ちょっとおかしい」のではと書いたことがあります。
2020年2月10日:ArduinoのanalogWrite 1/255なの?

内容は、
・設定値に対し、デューティーサイクルの変化がおかしい
 場所がある。
 (これではアナログ値を出力する関数とは名乗れないぞ)

今回は、このあたりを掘り下げて自前の処理を目指します。
  ※とりあえずタイマー0を触りますが、このタイマー、
   システムが使っているんであれこれ利用できない
   関数が出てきます。
   そのあたりは、
   2020年1月28日:Arduinoから「タイマー0」を取り上げる(ユーザーが使う)
   を参考に。

現状のanalogWriteのおかしい所・・・
  0~255の8bit値でデューティ0%(全区間L)~100%(全区間H)を
  制御している。
  8bitのD/A値なら、1/256が分解能。
  5V電源で0Vからスタートして0~255まで値を変化させた時、
  「5V * (255 / 256)」が最大値。
  5Vより5/256V低い電圧(約20mV)が値255の時に出てくるはず
  でそこで打ち止めなんだけれど、これ(255の時)を無理やり
  5Vとしている。
  どこかで1/256狂ってる
  これをanalogWriteというのはちょっと。

※どんな処理がされてるかは前記事をご覧ください。

その根本なのが「TOP値255の高速PWM動作」(モード3)にイニシャル
されたタイマー0と「非反転動作での出力制御」かと。

8bit D/Aコンバータの代わりになるようなPWMだと、どんな信号に
なるかというと。
  0 → デューティ0%で 全区間L
  1 → デューティ 1/256%
  2 → デューティ 2/256%
  :
 128 → デューティ 128/256% = 50% 方形波
  :
 254 → デューティ 254/256%
 255 → デューティ 255/256%
  ここでおしまい。 全区間Hはなし

ところが現状のanalogWriteは「1が2/256」「2が3/256」になっ
ていて、そのまま続いて「255で全区間H」となっています。
  ※わずか「1/256」の違いと言うなかれ。
   「0.4%」の「誤差」ですぞ!

この「1が2/256」になってしまう原因、これが「高速PWMモード」
の「非反転動作での出力制御」なんです。

このモードで「0」をタイマーのOCR(output compare register)に
書いても「全区間L」の出力は得られず、「0でも1/256」、
「1で2/256」になってしまうのです。

そして「255」なら強制的な全区間Hの処理は必要なく、タイマー
のOCRを255にするだけで、出力は256/256になって全区間Hの信号
が出てくるのです。

その対策。(本来、AVRマイコンの定石のはずなんだけど)
「非反転動作」ではなく「反転動作」の出力制御を使います。
すると・・・
  0 → 255/256
  1 → 254/256
  :
 254 → 1/256
 255 → 0/256 全区間L

反転動作をさせて、8bitの入力値val(0~255)をTOP値(255)から
減算した値をOCRに書くのです。

val=0は255となって全区間Lに。
1は254で1/256に、254は254/256、255は255/256となり
欲していた8bit D/Aの動作が得られます。

残るのは「全区間H」。
これは簡単です。
analogWriteの入力valの型は「int」。
だもんで、8bit内の0~255は0/256~255/256のデューティーで出力。
8bitを越える値は全区間H出力(ポート操作で)。
入力valの値、「0~255だけじゃなく、256も許容しちゃえ」という
全区間Hを得るための処理。
不正確さに目をつむるより、これもありでしょう。

「反転動作の出力制御」とこの数値判断だけで、正確なPWMによる
アナログ出力と出力H固定の処理ができます。

・・・言葉で言ってもよく分からないんで、データシートのこの図
を見てもらいながら、実際に動かしてみます。

A2_20200813132901

※テストプログラム : ダウンロード - test_pwm0_2.c
  (ファイルタイプ、.inoじゃなく.cにしています)

Arduino UNOはこんな回路に。

A1_20200813132901

・0~9を設定できるロータリーDIP SWをつなぐ
・8bitのタイマー0とタイマー2を使い、タイマー0を
 非反転出力動作で、タイマー2を反転出力動作で。
・TOP値が255だとわかりにくいので、波形生成モードを
 「7」のTOP値をOCR0Aで設定する高速PWM動作に。
トップ値は0~9をカウントする「9」に
・PWM出力はOCR0BとOCR2Bに出てくるのでこれを観察。
・OCR0AとOCR2Aはオシロのトリガー源に。トップ値でトグル。

ロータリーDIP SWを回すとこんな波形が得られます。

A3_20200813133001
上側の非反転動作が現状の処理。
下側の反転動作が今回の信号処理。
TOP-valの値(右側の(9-n)の値)で、正しいD/Aの動きが得ら
れるのがわかるかと。
半値5で、ちゃんと50%デューティになっています。

実際の信号をオシロで見るとこんな具合。
信号は上からOCR0B、OCR0A、OCR2B、OCR2A。
  (OCR0AとOCR2Aはトリガー源で方形波)

非反転と反転動作の違いは一番上と3番目に注目。

まずスイッチ=0。 デューティ10%と90%に。
B000

スイッチ1になると、デューティ20%と80%。
B001

スイッチ4で両方ともデューティ50%。
B004

スイッチ8で90%と10%。
B008

スイッチ9で全Hと全Lに。
B009

いかがでしょうか。
反転動作を使ってTOP値からの減算データをOCRに与えれば
まっとうなD/Aになるということが。

analogWriteの処理のように非反転動作のままでということなら、
 ・0の時は強制的L出力。
 ・1~255は「-1」して処理。これで1/256~255/256に。
 ・全H出力したいのなら、提示したval=256を許容する
  処理で。
というふうになるでしょうか。

0~255でまっとうな8bit D/A出力をということなら、
反転動作で「255-val」をOCRにというのがシンプルな
考え方で正解かと。
  ※分解能の高い↓のタイマー1の処理にも絡みます。

さて、示したのは8bitのタイマー0とタイマー2でしたが、
タイマー1は16bitです。
レジスタを直接操作すれば、ちょいと高性能な12bitや14bitの
D/Aコンバータが簡単に構成できます。
ここではなおさら「反転動作」でのD/A処理が正解かと。

2020年5月21日:JIS C8708:2019充放電実験回路
の制御プログラムを見てもらえれば、タイマー2のPWMで
12bit D/Aコンバータを作り、充電電流と放電電流を制御している
様子が分かるかと。
精度が上がると基準電圧も大事で、どうやってPWMから得ているか
回路図も参考にどうぞ。

これも参考に

2020年8月11日:14pinのAVRマイコン、ATtiny24が動かん!
 タイマー0をTOP値99のPWMで使ってます。

2020年8月10日:オリンパス OM-D E-M1mk2の充電器の充電表示ランプを判別その2
 R/2RのD/AをやめてPWMに変更。

| | コメント (7)