Arduino

2021年11月27日 (土)

EEPROM I2Cのクロックを早くすると・・・

これが「Wire」ライブラリのデフォルト、100kHz
でのSDA、SCL信号波形。
6バイトのデータを書き込んでいます。
Ee003
プルアップ抵抗は2.2kΩ。
それを400kHzにすると。
Ee001
およそ4倍に増速。

2kバイトのチップなので、コマンドにアドレスの
上位3ビットを混ぜ、その後に下位アドレス8ビット
が続きます。

ch1の波形は、書き込み開始から終わりまでの時間。
ch1がHになってSCLが出てくるまでの時間が、
関数内での前処理にかかっている時間になります。

波形を拡大。まず100kHz。
Ee004
400kHzだと波形の立ち上がりが鈍って見えます。
Ee002
SDA、SCLラインのプルアップ抵抗を大きくすると
鈍りが大きくなります。
4.7kΩにした波形です。
このくらいならまだ大丈夫。
Ee000
プルアップ、1kΩだと電流を流しすぎの感じだし、
4.7kΩで400kHzだと立ち上がりの鈍りが気になります。
その中間で2.2kΩにしました。

その後、2kバイトのEEPROMから8kバイトのFRAMに交換。
「FM24C64」になると「コマンド+アドレス上位+アドレス下位」
の順で書き込みが始まり、その後にデータが続きます。

6バイトのデータを連続書き込みすると、ページ境界で分断される
ことがあるので、たまに、2回に分けて書き込みが行われます。
その時、FRAMだと「書き込み完了待ち」をしなくてすむので、
連続書き込みがスムーズに進みます。
  ※EEPROMだと4msほど待たなければならない。

あえて「書き込み完了チェック」を入れた様子がこの波形。
      (ACK、NACKの確認)
Ee101
6バイトのうち、前半で4バイト、後半で2バイトの書き込み
をしています。

FRAMなので、後半書き込み前の待ち時間が不要になります。
1回の書き込み完了確認で済んでいる様子が見えています。
   ※FRAMを使う前提ならこの待ちは不要
EEPROMだとACkが返ってくるまで4msほど待ちが続きます。

EEPROMよりFRAMのほうが便利、というところでしょうか。


| | コメント (0)

2021年11月26日 (金)

EEPROM、 2kバイトと4kバイトの間には壁がある (24LC16で)

タクトスイッチを押してみる …何万回も その4 続き
この回路に、外付けメモリー(FRAM:強誘電体メモリ)を増設。
スケッチをゴソゴソしてました。
   ※スケッチ:基がArduinoなんであえてこう呼ぼう

手持ちのFRAMで容量が大なのが16kビット=2kバイト。
サイプレスの「FM24C16B」という型番。
  ※32kバイトのFRAMはちょい高価。
   そのうち手配しときますわ。
使い方はI2C EEPROMの24LC16なんかと同じなんですが、
I2C EEPROMには「2kバイトと4kバイトの間に壁」がありま
すんで要注意。
これをちょっとまとめておきます。

よく使うEEPROM、32kバイトの24LC256でしょうか。
しかし、このスケッチをそのまま24LC16には移せません。
アドレスの指定方法が異なるのです。

32kバイトの24LC256のアドレスは0x0000~0x7FFF15bit
2kバイトの24LC16だと0x0000~0x07FF11bitになります。

24LC256だと、メモリアドレスの2バイトをこんな具合にH,Lバイト
2回に分けてコマンド送出します。

E14

ところが24LC16(これより小さいメモリ)だと、送出アドレスは
1バイト(下位の8bit:0x00~0xFF)だけになり、アドレスの上位は
コマンドの下位3ビットに含ませて指定しなければなりません。

E12

それが、「2kバイトと4kバイトの間の壁」です。
これを理解していないと、小容量EEPROMが使いこなせません。

2kバイト以下のはデバイスアドレスの下位3bitにメモリアドレスの
上位を混ぜるのです。
E11_20211126144301
メモリの大きさで、アドレスとして有効となるビット位置が
異なります。

さらに、この「壁」が、読み出しの時にも関係してくるのです。

読み出しのアドレスも、書き込みと同じように上位側はコマンドの
下位3ビットで指定します。
続いて読み出しコマンドを送出するのですが、この時にも書き込みと
同じように、デバイスアドレスに下位3ビットを加えておかなくては
ならないのです。
  ※長いこと使ってないと、これを忘れてしまって、
   「ありゃ?」っとなってしまうわけでして・・・

E13
この処理を忘れてデバイスアドレス(0xA0)だけを読み出し
コマンドに送ってしまうと、ページ0のデータが出てきて
しまい、0x01FF~0x03FFのページ1,2,3のデータが
読み出せません。

最初のアドレス指定コマンドと読み出し開始コマンドの
下位3bitは同じ値にしておかなくてはならないのです。

4kバイト以上の24LC32や24LC64、24LC256では2バイトで
アドレスを渡すので、ここらは気にしなくてかまいません。

・4kバイト以上のEEPROMでの書き込み例
 Wire.beginTransmission(DEV_ADR); // デバイスアドレス ★1
 Wire.write(mem_adr >> 8);     // メモリアドレス MSB
 Wire.write(mem_adr & 0xFF);    // LSB
 Wire.write(data);         // 1バイトデータ書き込み
 Wire.endTransmission();      // 転送開始
 delay(10);            // 書き込み時間待ち

・4kバイト以上のEEPROMでの連続読み出し例
 Wire.beginTransmission(DEV_ADR); // デバイスアドレス ★1
 Wire.write(mem_adr >> 8);     // メモリアドレス MSB
 Wire.write(mem_adr & 0xFF);    // LSB
 Wire.endTransmission();      // アドレスを転送
 Wire.requestFrom(DEV_ADR, qty)  // qtyバイトデータ要求 ★2
 for(i = 0; i < qty; i++){     // loop
   if(Wire.available())  bff[i] = Wire.read(); // bffにコピー
 }

2kバイト以下のEEPROMの場合、★1と★2を加工しなければなりません。
そして、忘れるのが★2の処理。
これをDEV_ADRだけで実行してしまうと、欲しいアドレスのデータが
出てこずにページ0のメモリが出てきちゃうのです。

なお、連続読み出しする場合のページ境界は意識しなくて大丈夫です。
0x00FFの次は0x0100のデータが、0x02FFの次は0x0300のが出て
きます。
  ※書き込みはページサイズが関係しますんで、
   連続書き込みは別の処理が必要です。 (ページライト処理)

  ※wireライブラリ、I2Cの送受バッファが32バイトなんで、
   長大な連続読み書きは一度にはできません。

  ※ライブラリWireのI2Cクロックは100kHzで、ちょい遅い。
   24LC256は400kHzで動作ok。
   setup()内でこんな具合にして高速化。
    Wire.begin();   // 外付けEEPROM用I2C
    Wire.setClock(400000L);  // 100kHz→400kHzに ちょい早く

小容量のEEPROMやFRAMを使う時は、ちょっと注意が必要
ということで。

※表はMICROCHIPの「I2C. シリアルEEPROM ファミリ データシート」
 より。

※もうひとつ: デバイスアドレスについて
一般的な24LC256などのEEPROM、データシートを見ると
0xA0 : 0b10100000」がデバイスアドレス(制御コード)
になってます。
ところが・・・ArduinoのWireライブラにに食わせるアドレスは
0x50 : 0b01010000」。
右に1ビットシフトした値を与えます。

これ、ライブラリ「twi.c」の中で
 twi_slarw = TW_WRITE;
 twi_slarw |= address << 1;
と、address値が左シフト処理されてI2C関連レジスタに
渡されるのです。
わざわざ、「なんでこうなった?」が不明。
誰か教えて!

※参考
ArduinoでI2Cの外付けEEPROMを使う(radiopench.blog)
Arduinoで外付けEEPROMを使う 続編

| | コメント (0)

2021年11月16日 (火)

タクトスイッチを押してみる …何万回も その4

タクトスイッチを押してみる …何万回も その3の続き。
スケッチにちょっと手を加えて。

・先日の続きから。
   スイッチはそのままで換えていない。

・「10回」の接触不良検出(デジタル入力ポートでのオフ検知)
 (連続しなくても)で、異常発生と判定して停止。

・リングバッファに200回分のサイクルとA/D値をログ。
   記録するA/D値は「2以上のもの」(0と1は飛ばす)

・10bit A/D値のMSB部(空いている)を使って、接触不良
 回数を同時に記録。

・内蔵EEPROMに接触不良が生じたサイクル番号を記録。
   (10コ)

結果、こんなデータが得られました。
まず、リングバッファ(200データ)の様子。

#  cyc A/D off
 193378  2 リングバッファの先頭
 193382  3
 193904  2
  :
 193905  5
  :
 193934  4
 193936  2
 193938  4
 193939  2
 193942  5
 193945  2
 193963  2
 193988  6
 193989  6
 194016  2
  :
 199110  2
 199118  7
 199120  14
 199121  3
 199125  8
 199127  3
 199128  14
 199129  14
 199130  17
 199131  7
 199132  9
 199138  6
 199140  4
 199143  2
 199146  15
 199151  3
 199154  12
 199155  20
 199156  12
 199157  13
 199158  12
 199159  10
 199160  4
 199161  6
 199162  13
 199163  10
 199166  6
 199167  14
 199169  10
 199173  3
 199665  7
 199666  2
 199673  17
 200010  2
  :
 205996  3
 205998  2
 206005  5
 206298 1023  2 2回目の接触不良検出
 206500  2
 206510  2
 206517  2
 206518  2
 206549  2
 206668 1023  3 連続して接触不良が発生
 206669 1023  4
 206670 1023  5
 206671 1023  6
 206672 1023  7
 206673 1023  8
 206674 360
 206675 1023  9
 206676 1023 10 接触不良10回検出で停止

接触状況が悪くなっているのでしょう、2と3が多数出現
しています。(途中省略)
そして、 206668サイクルから連続して接触不良が起こりました。

EEPROMに記録した接触不良が生じたサイクル数。

#off   cyc
# 1  51745
# 2  206298
# 3  206668
# 4  206669
# 5  206670
# 6  206671
# 7  206672
# 8  206673
# 9  206675
# 10  206676

最初の接触不良、51745サイクルのは、リングバッファ内に
は入っていません。

出てきたデータをグラフにしてみました。
縦軸がA/D値。 対数目盛にしています。
右上端に固まって出ている「○」が異常検出のタイミングです。
Cap003_20211116094201
A/D値が接触抵抗を示すわけですが、今回検出したのは
大きくても2桁。

さて、新品のスイッチに換えて実験を継続します。

※スケッチ
  ・ダウンロード - sw_life_test3b.txt

※GNUPLOTのスクリプト
  ・ダウンロード - taktsw4.txt

※リングバッファのデータ:cyc数とA/D値
  ・ダウンロード - 20.txt

※試したタクトスイッチの中 (秋月の各色100コ入)
11_20211116115901
中心より外周がこすれている感じ。
12_20211116115901

このテストより先、すでにン十万回の開閉を行っています。
でも、異常停止するまでに、ちょいと回数がかかりました。
1回目と2回目の間も15万回。
なにかの拍子、接触状態が回復するのかも。
接触不良を検知した直後、ちょっとだけ開閉を休むとかしたら
もっと回数が伸びるとか。

ソレノイドの下端が押しボタンを押す力も影響しそうです。
   ※押し過ぎはきっと良くないでしょう。
「何グラムで押す」とか、うまく規制できれば良いんでしょうが、
メカ細工をどうしたものか・・・

どんなもんでしょね。

※11月17日 08時50分
昨日に交換した新しいスイッチでの試験、すでに30万回。
まだ大丈夫です。

発端はこのトラブル↓
2021年11月6日:トラブル遭遇:3点  スイッチ、プラグ、電線
回路試験用のジグで使っていた秋月のスイッチでの生じた
接触不良です。
普通に指先で押さえるんで、ン万回も押してません。
内蔵プルアップ抵抗という軽い負荷で接触不良が発生。
グニグニすればオンしていたんですが、普通の操作(指先でpush)
では反応しないということが続いたんで、交換。
そして、この実験につながりました。

なんなんでしょね。
単にon/off回数でもなさそうだし。
ハンダ付けの熱とも考えにくい。

新品スイッチの保管、ビニール袋に入れて部品箱に入れてます。
それを回路に組み込んだら、室内ですが、そのへんにほったらかし。
仕事場、そりゃたまにはパネルの穴開けで油も使いますし、潤滑剤
として「シリコンスプレー」も使います。
洗浄に「IPA」や「大阪魂」も使います。
置いてある基板には直接はかかりませんが、たまに室内で使うケミカル
用品のガスが悪さをしてるのかな?

それとも、ほんとにたまたま調子の悪いスイッチに遭遇してる?

| | コメント (2)

2021年11月15日 (月)

タクトスイッチを押してみる …何万回も その3

タクトスイッチを押してみる …何万回も その2 の続き。
スケッチの変更概要
 ・A/DのVrefを5Vから内蔵1.1Vに変更。
  10bit A/D 0~1023で0~6kΩの接触抵抗を見る
  ことになる。 1LSBで6Ωくらい。

 ・リングバッファへの記録を、A/D値が0と1は飛ばして、
  2以上の時にサイクル数とともに記録。

 ・リングバッファは「160データ」。
  オフ検出まで記録。

「その2」で試したスイッチのまま(26万回開閉)、取り替えずに
試運転した結果がこれ。 (A/D値2以上を記録。 0と1は飛ばし)
開始してから1万8千515サイクルまで、A/D値は0か1。

#  cyc A/D
  18516  47
  18981  63
  19596 159
  19617  63
  19696 687
  19771 1023 ←PC1デジタル入力オフ検出で停止

接触抵抗が増大した値が連続し出すのではなく、パラパラと
大きなA/D値(スイッチの接触状態を示す)が出ています。

接触不良発生でいきなり使えなくなるのではなく、
「徐々に反応が悪くなる」という状態が見えます。

今回のテスト、負荷はマイコンの内蔵プルアップ抵抗。
電流はわずか。
少々接触抵抗が増大しても「オン」を検出してほしいところ
なんですが・・・

オン・オフのタイミングはこんな具合。
A/Dの平均回数64 = 50mSを1単位に処理しています。

25_20211115090401
1回の「オフ検出」で有無を言わせず「試験を停止」するん
じゃなく、何回かの「オフ検出」を待つ方が良いかな?

・今回のスケッチ:ダウンロード - sw_life_test2.txt

 

| | コメント (1)

2021年11月13日 (土)

タクトスイッチを押してみる …何万回も その2

2021年11月6日:トラブル遭遇:3点  スイッチ、プラグ、電線
のタクトスイッチの接触不良を受けて、
2012年08月20日:タクトスイッチを押してみる …何万回も
で作った回路を改造して試してみました。
以前の回路は14pinのATtiny24でしたが、Arduino-UNOで
プログラムを組めるようATmega328Pに交換。

こんな回路。

22_20211113174601

21_20211113174701

0.25秒周期(毎秒4回)でソレノイドを駆動して
スイッチのボタンを押し、オン・オフを繰り返します。

・スイッチを押してから50ms後にA/D変換を開始。
・次の50ms間にA/Dを64回して平均化。
・A/Dが確定して(押してから100ms後)から、スイッチの
 オン・オフ状態(デジタル入力)をチェック。
・A/D値を記録。
・オンなら正常でソレノイドをオフして150ms休憩。
・オフなら異常検出で停止。

これを繰り返します。
PC0ポートがA/D入力。
PC1ポートが、内蔵プルアップを有効にしたデジタル入力。
PC0とPC1を並列につないで、PC1のオン・オフをチェック
します。

A/Dの基準電源は5Vにしています。。
内蔵プルアップが30kΩほどなんで、A/D 1bitの分解能は
30Ωほどになります。
  短絡電流は0.15mAちょいと非常に小さいわけで。
  これで接種不良が発生したので、やっかい・・・

全データを記録するわけにも行かないので、リングバッファで
480サイクル分のデータ(2分間)のA/D値を保存しています。

「オンしたのにオフになった」を検出したら、サイクルを
止めて、このバッファのデータを見ると異常発生直近の
2分間の様子が観察できるだろうとの目論見です。

しかし・・・
結果はこんな具合。

  cyc  A/D
 263396  0
 263397 543
 263398  0
  :
 263608  0
 263609 287
 263610  0
  :
 263627  0
 263628  0
 263629  0
 263630 1022 ←SWオフ検出

2分間(480サイクル)、ほぼズ~と「0」が続いて、0以外の
異常値が出たのは2発だけ。

そして、26万3千630サイクル目に、いきなり「スイッチオフ」
を検出して停止。
徐々に接触状態が悪くなってくる様子が観察できませんでした。

異常記録の方法、直近の480サイクルじゃなく、閾値を超えた
A/D値を残すようにするほうが良いかも。
今はA/D値の2バイトだけ。 480x2で960バイト。
閾値を超えたのとなると、4バイトのサイクル数もいるので
1データが6バイトになって、160個ぐらいでしょうか。

・スケッチ:ダウンロード - sw_test1.txt
   (ファイルタイプを「.ino」にしてArduino IDEで読んでください。)

 

| | コメント (4)

2021年10月16日 (土)

LED劣化調査実験、新回路案

http://act-ele.c.ooco.jp/trouble/ledrekka/LEDTEST2a.png
この回路は、常時点灯しない比較用LEDの明るさも記録して
おこうという主旨で製作しました。 参:■LEDの劣化
測定系1chあたり、同じLEDを2つ装着します。
そして、LEDに流す電流のために定電流回路を装備する
というちょっと大げさな構成になっています。
先日の、可変抵抗器の「陽極酸化」実験回路 #4
は、同じATmega328P:Arduino-UNOベースで、入力8ch+温度。
電流測定のため、10倍アンプを仕込みましたが、大げさな
ものじゃありません。

これと同様の回路を「LED劣化実験用」に作っておこうかと
思っているのです。
制御ソフトは、24時間に1回の記録サイクルで同じで可。
定電流回路や1chあたり2LEDという制御も止めにして、
簡単な構成に。

・LEDに流す電流は、LED個別に抵抗を直列に
 入れて設定。

・LED・VF変化の影響を受けにくいようで、LED点灯
 用電源は制御用5Vではなく、12Vなど少し高めの
 電圧の電源を用意。
  (2直になったLEDも見ることができる)

・LED1つを常時点灯。
 比較用LEDの切り替えはやめる。

・温度測定、(ついでに)あれば面白いか。

・1日1回、0時の記録だけで良いか?
   ※悩みどころ

 1日間での最高、最低、平均も欲しいか?
 現状、1日1回なら、温度も含めて9データで
 18バイト。
 24LC256(32kバイト)なら4.98年の記録。
 最高、最低、平均となると、1日あたり
 3倍の54バイトで32kのEEPROMだと1.66年。
 そうなると、128kバイトのEEPROMにする
 ほうが良いか? 32kで十分か?
   ※シリアル送出にかかる時間が長くなる
 それともSDカードに記録するか?

 

| | コメント (0)

2021年10月13日 (水)

可変抵抗器の「陽極酸化」実験 スタート

今日からスタートします。
ほこりよけに、百均屋さんでフタ付容器を買ってきました。
基板に半固定抵抗を8個並べて、コネクタで接続。
測定回路もいっしょに容器に入れ込みました。
   ※右から4つ目の青いのが、東コスのサーメット。
    他は秋月で買ったのカーボン半固定。
112_20211013175001
    (クリックで拡大↑)

水色+黒色の電線につながっているのがサーミスタ103JT。
基板に貼り付けてます。

可変抵抗器の「陽極酸化」実験回路 #4 の回路図で示した
電流値に調整。
10bitのA/D値が940~960くらいになっています。
  変化を見るんで、合わせ込みはざっとで。
  調整角度から、なかなかむつかしいのがある。

深夜0時に最初のデータをログします。
1日1回の記録。
EEPROMの容量は1820データなんでざっと5年。


| | コメント (2)

2021年10月11日 (月)

可変抵抗器の「陽極酸化」実験回路 #4

2021年10月8日:可変抵抗器の「陽極酸化」実験回路 #3の続き。

液晶へのデータの表示方法ですが、
2015年02月07日:またまた長期間実験を始めます これの
http://act-ele.c.ooco.jp/blogroot/igarage/img/img_box/img20150207170132040.jpg
のように、8コのデータを20文字x4行液晶画面の
右端に並べることにしました。
11_20211011125301
データ番号を示す「1:」~「8:」は、液晶コントローラの
キャラジェネ設定で。
温度「℃」は「゚C」と、2文字で表示。

最終的にできあがった回路
Vr_chk2

・二つ使ってたオペアンプ、ゼロドリフトアンプをやめて
 2コ入りのMCP6072に。
    MCP6072、最大電源電圧6Vまでという規制は
    あるけれど、低消費電流で入出力rail to rail。
    そして、低オフセット電圧という特徴があります。
・手持ち液晶がバックライト無しだったので図に反映。

動くスケッチもできましたし、ボリュームが届いて配線してから
「置き場所」を考えて、長期実験に入ります。

 

| | コメント (0)

2021年10月 8日 (金)

可変抵抗器の「陽極酸化」実験回路 #3

2021年10月 4日:可変抵抗器の「陽極酸化」実験回路 #2の続き。
Arduino-UNOベースでプログラム(スケッチだぁ)をごそごそ。
  ※20文字x4行の液晶、バックライト無しのしか
   手持ちが無かった。
これにどうやって8つのA/D値(0~1023の4桁)を表示するかが
悩みどころ。

似たような表示に、
  2015年02月07日:またまた長期間実験を始めます
LEDの劣化実験。
これも10bit A/D値を表示してます。
この時は、液晶画面右側に「4行x2」で表示しています。

表示数字、0~1024と4桁ですが、隣との区切りにスペースが
一つ必要ですんで、実質は5文字。
横2行に並べると5文字x4x2行と20文字に入ります。
でも、左端のスペースにch番号を示す文字を入れるとややこしく
なります。
その数字、表示データの数字と区別できるよう、液晶のCGデータ
設定を用いてみました。
A11_20211008140901
キャラジェネデータ、「1:」と「5:」を作って、左端に配置。
A12_20211008140901
これで、どの順でデータが並んでいるのかがわかるかと。
ついでに温度表示用に「℃」も。
A13_20211008140901
「 ゚C 」だと2文字必要なのが1文字幅で表記できます。

※数字の「0」、プログラムリストで使う時は「斜線の入ったゼロ」は
歓迎なんですが、こういった数字だけの表示の時は斜線無しのほうが好き。
1文字ずつ時前で処理する時(出来合いのライブラリを使わず)は、
「ゼロ」を「オー」に変換していました。


外付けEEPROMの書き込みについて追記

Arduinoでごそごそしていると、ライブラリとして使わせて
もらっているプログラムをどこから「もらってきた」のかが
わからなくなってしまいます。
 ※忘れても、いつでも見つかる(はず)
  という安心感が原因か。

今回の「陽極酸化検証スケッチ」の中でも、
  「これの原典、どこだっけ」
というのが外付けEEPROMの読み書き処理
Wire」を使ってこんな関数を使っているんですが、
「これ、どこからもらってきたの?」になっちゃい
ました。
   ※「Wire」のどこかのサンプルだろうけど

/***** EEPROM 1バイト書き込み *****/
void eepwrite( int deviceaddress,
       unsigned int eeaddress,
       byte data ){
int rdata = data;
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));  // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.write(rdata);
  Wire.endTransmission();
  delay(10);     // wait 10ms
}
/***** EEPROM ページ書き込み  *****/
// 24LC256 では1ページ64バイト
void eepwritep( int deviceaddress,
       unsigned int eeaddresspage,
       byte* data, byte length ) {
byte c;
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddresspage >> 8));  // MSB
  Wire.write((int)(eeaddresspage & 0xFF)); // LSB
  for ( c = 0; c < length; c++){
    Wire.write(data[c]);
  }
  Wire.endTransmission();
  delay(10);      // wait 10ms
}

この関数の書き込み後の待ち時間「delay(10)」。
たいていのEEPROM、書き込み待ち時間のmaxが「5ms」。
余裕を見て10ms待ちにしていると想像できます。
私の場合、この「無駄待ちdelay」がキライ

ですんで、今回もこんなふうに手直ししてます。
   ※ちょっと端折って記します。
void eepwritep( int・・・){
byte c;
  while(tm_eep);   // 書き込み時間待ち
  Wire.beginTransmission(deviceaddress);
   :
  Wire.endTransmission();
  tm_eep = 6;     // 6ms (EEPROMのスペックは5ms)
}

1バイト書き込みの手順ですが、
  ・書き込み完了の時間待ちは処理前に。
  ・EEPROMの読み出しにも、書き込み完了時間待ちを
   頭に入れる。
  ・時間待ちタイマー「tm_eep」は1msタイマー割込で
   デクリメント。 タイムアップでゼロに。

こうすると、EEPROMに書き込んだ直後、別の処理をすぐに
実行できます。
もちろん、連続して書き込みが続く場合の待ち時間は同じ
です。
でも、時間をおいた単発の書き込みだと、無駄待ちを
しなくてすみます。
  ※10msというのは結構長い。
   スイッチのスキャンだとミスすることも。
   ブザー報知だとタイミングがおかしくなったりと。

そして、時間待ちを実行の前でという処理、
EEPROMのページ書き込みで役立ちます。

使ったEEPROMは32kバイトの24LC256。
1ページが64バイトで、同一ページを書く限りは、1バイトでも
64バイトでも待ち時間は5ms(max)

今回のデータは「18バイト」と中途半端ですんで、ページを
またいでの書き込みが発生します。
  ※9ch分の2バイトデータを保存するんで
4回に1回は、「ページまたぎ」が起こって、2回の書き込み
完了待ちが発生するわけです。

時間待ちを書き込み実行の前でという処理にしておくと、
1回目の書き込み完了後に実行する別の処理は待ち時間
なしでok。
ページをまたがなければ、次の記録指令が来るまで
無駄待ちなし(delayなし)で処理を進めれます。

「ページまたぎ」が起こった時は、次の処理を2回目の
書き込み前、つまり時間待ちの前に行っておけば、
「何もしないで待っている」から逃れることができます。
  ※応答を良くしたいときなど、これがありがたい。

オシロ波形で見てみましょう。
上からI2CのSCLとSDA。 (EEPROMの信号)
そして、1msタイマー割込と直後のA/D変換割込で
タイミングチェックのために作っているパルス。
H期間が割り込み処理の長さです。
   (この波形では細くて測れませんが)
一番下がシリアル出力。
9600BPSのシリアルデータです。
Bb000_20211008163501
↑のは同一ページの書き込みで、1回のアクセスで
18バイトを書いています。
そして、書き込み直後に、シリアルデータを出力。
待ち時間なく、シリアル出力処理が行われています。

次のは「ページまたぎ」の処理が発生して18バイトの
書き込みが2分割された様子です。
Bb001_20211008163801

1回目の書き込み直後にシリアル出力処理を行い、
それが終わってから2回目の書き込み(待ち時間発生)です。
シリアル出力は、1回目の直後から始まっているので、
「ページまたぎによる時間待ち」が見えません。

シリアル出力は割込で処理されるので、じっさいに
かかっている時間はわずかです。
しかし、液晶表示だといがいと時間がかかりますんで、
2回目の待ちの間に表示処理を進められるでしょう。

タイマー割込によるタイマー管理が必須になります、
「無駄待ちはキライだ」を実現するには「これしき」です。

| | コメント (0)

2021年8月11日 (水)

サーミスタ温度計、何ビットのA/Dコンバータがいるか?

サーミスタで温度測定するとき、
 ・A/Dコンバータの分解能の違いで、何度の桁まで測れるか?
あるいは、
 ・何度の桁まで計りたい時は何ビットのA/Dが必要か?

この答えのグラフがトランジスタ技術2012年1月号p89
図3に出ていました。
 ●特集:エレクトロニクス格言集
  3-6 抵抗分圧比をA-D変換する時の
  基準電源ICは無駄遣い  著者:星聡

Ad01
8ビット分解能でも1℃ステップで測れないことはない
というキャプション。

先日作った温度計で使った「103JT」サーミスタの
温度特性から、これと同じような表を作ってみました。

セミテック103JTの温度:抵抗値表

#SEMITEC 103JT 温度・抵抗特性
#温度 抵抗値
#℃ Ω
-20 71020.0
-10 43670.0
0 27700.0
10 18070.0
20 12110.0
25 10000.0
30 8301.0
40 5811.0
50 4147.0
60 3011.0
70 2224.0
80 1668.0
85 1451.0
90 1267.0
100 975.3
110 759.7
120 598.1


それを「gawk」に食わせて1℃ごとのテーブルを作成

#####   サーミスタ抵抗値計算プログラム    #####
# 下間憲行  jh3dbo@jarl.com
# 10℃ごとの温度,抵抗値テーブルから
# 1℃ごとの抵抗値データを作成
# メモ awkのlogは自然対数

BEGIN{
# 定数指定
K_273 = 273.15 # ケルビン温度
Rref = 10.0e3 # 直列抵抗 10kΩ
Aref = 65536 # 16bit A/D max
# タイトル表示
print("* サーミスタ抵抗値計算 (2015-06-10)\n") > "/dev/stderr"
nbr = 1 # 読み込みデータ数
}
# 「BEGIN」おわり (以下、ファイル読み出し処理)

##### 温度,抵抗値テーブルを読み出す #####
# 10℃ごとの温度と抵抗値を順に読む
{
if( $0 ~/^#/ ) next # 先頭文字が「#」ならコメント
# 数値入力
temp[nbr] = $1 # 温度
ohm[nbr] = $2 # 抵抗値
# printf("%d %s %s\n", nbr , temp[nbr], ohm[nbr])
nbr++ # 配列 +1
}
# ファイル処理おわり (以下、END処理)

##### 10℃ごとのB定数計算して1℃ごとの処理 #####
# ※基準は25℃なので、20~25~30℃となっているので注意
END{
# B定数計算 10℃ごと
print("# B定数")
for(i = 1; i < (nbr - 1); i++){
t1 = temp[i]
t2 = temp[i+1]
r1 = ohm[i]
r2 = ohm[i+1]
B[i] = (log(r1 / r2)) / ((1 / (t1+K_273)) - (1 / (t2+K_273)))
printf("# %d℃~%d℃ %3.1fΩ~%3.1fΩ B=%3.1f\n",
t1, t2, r1, r2, B[i])
}
# 1℃ごとの抵抗値を計算
print("# ℃ Ω")
m = 1 # 1℃ごと (0.1℃ステップなら10に)
for(i = 1; i < (nbr - 1); i++){ # 10℃のテーブル
n = (temp[i+1] - temp[i]) * m # 表の温度差
for(j = 0; j < n; j++){ # 1℃ピッチで
R = tohm(temp[i] + (j / m), temp[i], ohm[i], B[i])
ad = Aref * (R / (Rref + R)) # A/D値
printf("%6.1f %12.2f %6d", # 温度と抵抗値
temp[i] + (j / m), R, ad)
if(f){
printf(" %8.2f %8.2f", # 抵抗値とA/D値の差分
r0 - R, ad0 - ad)
}
printf("\n")
r0 = R;
ad0 = ad;
f = 1;
}
}
}

##### 温度による抵抗値計算 #####
# T1 = 計算する温度
# Tm = 基準温度
# Rm = 基準温度での抵抗値,
# Bconst:B定数, 273:ケルビン温度
# 1 1
# R = Rm * exp( Bconst * (--------- - -----------) )
# t1 + 273 Tm + 273
function tohm(T1, Tm, Rm, Bconst){
return(Rm * exp(Bconst*((1.0 / (T1 + K_273)) - (1.0 / (Tm + K_273)))))
}


その結果を「gnuplot」で処理

set term wxt 0
set ytics nomirror
set y2tics
set xrange [-20:110]
set yrange [0.001:10]
set y2range [0:800]
set title "A/D値が1LSB変化したときの温度変化 【サーミスタ103JT Rs=10kΩ】"
set xlabel "温度 (℃)"
set ylabel "1LSB変化したときの温度変化(℃)"
set y2label "16 bit A/Dでの1℃あたりの変換値変化"
set grid
set xtics 10
#set ytics 500
set key right top
set pointsize 0.5
set logscale y
set label "" at second -8, 770
set label "16bit A/D値変化(右目盛)" at second 2, 660
set label "8bit A/D" at second 11, 560
set label "10bit A/D" at second 12, 440
set label "12bit A/D" at second 13, 320
set label "14bit A/D" at second 14, 200
set label "16bit A/D" at second 15, 80
plot "ohm3.txt" using 1:5 with line lw 2 ti "" axes x1y2,\
"ohm3.txt" using 1:(1 / ($5/256)) with line lw 2 ti "",\
"ohm3.txt" using 1:(1 / ($5/64)) with line lw 2 ti "",\
"ohm3.txt" using 1:(1 / ($5/16)) with line lw 2 ti "",\
"ohm3.txt" using 1:(1 / ($5/4)) with line lw 2 ti "",\
"ohm3.txt" using 1:(1 / $5) with line lw 2 ti ""


こんなグラフが得られます。
Cap005_20210811095101
8,10,12,14,16ビットのA/Dコンバータを使った
時の分解能の変化を示しています。
   ※A/D変換器や周辺回路の精度が加味されますんで、
    1bitの変動でもきびしい状態が浮かんでくるかと。

10℃~25℃あたりが良くて、1℃あたりのA/D変換値変化量
が減る高温域(70℃を越える)になると分解能が悪化するのが
見えます。
  ※8bitA/Dで「水温・湯温」を計ろうとしたとき、
   0℃~40℃くらいまでは0.5℃ピッチで読めます。
   しかし、80℃を越えると1℃ピッチでの読みが
   しんどくなります。
   昔々・・・4bitマイコンで電気温水器の仕事をした
   ときは(東芝の8bit A/D内蔵品)、出てきた
   8bit値(256バイト)をそのまま1℃単位の温度に変換する
   テーブルを作って処理しました。
     水温が正しくない上下の値はエラー処理
     (氷温以下と沸騰温度以上)

分解能16bitのA/Dを使っても、A/Dの誤差を考えると「0.01℃」の
測定はなかなかむつかしいということで。


| | コメント (0)

より以前の記事一覧