Knowledge | ||||||
不定期TOPIC 2003/10/03より
課題の一つであるシリアルEEPROM 24LC256をH8/3664に接続する実験を行ってみました。24LC256は、秋月電子にて購入しました。\400-でした。I2Cでなければ?(実はそうかどうかも良く知らないのです)1MBitのものもあったのですが、よくわからなかったので、I2Cと書いてある256KBit(32KByte)のものにしました。その他、理由は以下のようなものです。 ■H8/3664のSCL、SDA兼用端子について という感じで、一番、簡単そうな路線を選んでいます。1MBitの方も、コストパフォーマンスが良さそうなので、時間ができたらやってみようと思います。Best Technology社のH8 Tinyでも同じようなもんだと思いますので、参考になるかもしれません。これができるか?またアクセス速度は?というあたりが、H8/3664の換装できるかどうかの判断基準になりますので、ドキドキしてやりましたが、ドキドキしたぶん、ちょっと怪しい雲行きな結果になりました。 |
ハードウェアの説明
基本的には、24LC256とH8/3664を普通に接続するだけです。左図が回路図です。SCLとSDLですが、手元の資料ですと、Best
Technology社のH8
Tinyの場合は、CN2-10とCN2-11になるようです。
■A0〜A2
24LC256に対してアドレスを指定するピンです。すべてGNDにつないで0としています。1つのI2C
I/Fに複数24LC256を接続する場合、変更する必要があります。
■WP
Write
Protectの意味で、Vccにつなぐと書き込み不可となる。内部でプルダウン(抵抗経由でGNDに接続)されているようです。よって何もつないでいません。
■SCL/SDA
I2C
通信線です。SCLはクロック、SDAはデータです。たった2本でなんで通信できるのでしょう?不思議です。マニュアルを読むと、SDAの方は「通信速度に合わせて抵抗でVccに接続してください」とありますが、SCLの方は何も書いてありません。でもどこかで両方ともVccに接続するようなことを読んだ記憶がありますので両方とも接続しています。ちなみに、100KHz動作時は10KΩ、400kHzと1MHzは2kΩとあります。む、1kΩをつけてしまった…まあいいや、これで実験してみることにします。
ソフトウェア
GDLには、I2C
EEPROM用のアクセス関数が用意されています。すばらしい〜。ソースコードもついていたので、ちょっと見てみたのですが、初期化関数は無く、それぞれのアクセス関数の先頭に、必ず初期化してから使うようなソースコードになっています。また、どの関数も、前回書き込みがあった場合、書き込み完了するまで待ってから次の処理に移るようになっているようです。そんなわけで、自分でタイマ待ちなどを組む必要は無さそうですが、逆にいうと、書き込み関数と消去関数は、呼び出すと時間待ちも含めて終了するまでReturnしないことになります。
転送速度ですが、400kHzで書き込むようになっています。使い方は、TARGETの中の3664.txtに、サンプルは、H8
Tinyのサンプル7にあります。「メーカ・メモリサイズによっては完全に対応し切れていない場合がありますのでご注意ください」なことが書いてありますので、一応、注意して下さい(だから「できた」「できない」の参考情報をHPで流しているのさ〜)。
今回のEEPROMは、256kBitでアドレスが0になりますので、TEEPROMの初期化は次のようになります。
TEEPROM SFM_eep
= { ep256k, 0, 0 };
実験結果
さてさて、こんな感じで回路とソフト(サンプルからベンチマーク用にちょっと改造)を作って試してみました。一発目…書き込み異常。5秒に一回ぐらいこれがでる。どうしたのかとチェックしたら、1kΩの抵抗と信号線のつなぎ忘れを発見。おっと、勇み足で足滑らしました。今度はちゃんと結線チェックして再度チャレンジ。二回目はばっちりしでした。おお、簡単!
では、せっかくなので参考程度ですが、速度テストをいってみようと思います。
32kByteアクセスするのにかかった時間 (EEPROM_BlockRead()及びEEPROM_BlockWrite()を使用) | |||
テスト内容 | 32kByteアクセス時間 | 1アクセス時間 | コメント |
全消去 | 約4秒 | − | よく見たら0xffで書込してるだけでした。 |
64バイト書込 | 約4秒 | 約8msec | − |
64バイト読込 | 約3秒 |
約6msec |
− |
8バイト書込 | 約23秒 | 約6msec | − |
8バイト読込 | 約16秒 | 約4msec | − |
う、う〜む、書き込みは想像以上に速いような気もするけど(特に64バイト書き込み)、読込が、お、遅い…。こ、こんなもんなんでしょうか?何かやり方がまずいんでしょうか?この時点で、H8/3664計画の先行きに不安が〜!それにしても、8バイトと64バイトアクセスの差を見るとわかるのですが、オーバーヘッドがなんか大きい感じですね〜。64バイト読込と8バイト読込の時間差が約2msecですから、2msecの間に56バイト転送できていることになります。そう考えると、ほぼ4msec近くがオーバヘッドになっていることになります。
読込は書き込みの100倍ぐらい速いのを期待していたんですけど、全然ダメっぽいです。いや、100倍は期待しすぎですね。う〜ん、何か別の原因があるのでしょうか?やっぱ、抵抗は2kΩにしないとダメ?
この先どうしましょう?こまりました。まずは指示どおり、SCLとSDAの抵抗を2kΩにしてみようと思います。後はI2CじゃないシリアルEEPROMも試してみるか、アクセスルーチンを見直してみるか…いずれにせよ、最初のようなあま〜い考えではダメなようで…とほほ。
H8/3664
フラッシュROM書き込み回数
そんなわけで、デバッグやらなんやらで、本日3回書き込み、総計9回書き込みということになりました。100回で収まるんでしょうか???というわけでマニュアルをよく読んでみたら、1000回って書いてありました。おお、急に安堵の色にカメレオンチェ〜ンジ。まあ、あまり気にせずに、でも他の方法でのデバッグをちゃんとやりつつ〜の進めたいと思います。
うーむ。悩むI2CシリアルEEPROM。勉強勉強。
不定期TOPIC 2003/10/06より
ちょっと、TOPICの日別サブジェクトフォーマットを変えてみました。少しは見やすいかな?10月分だけ直してみました。というわけで相変わらず悩んでいるI2CシリアルEEPROMです。
そういえば、な〜んで悩んでいるかという話を書くのを忘れていました。GDL(GCC Developer Lite)でこのアクセス関数が用意されており、また私のような中身も良く知らずに適当なシリアルEEPROMをつなげているのに、ちゃんと動作してくれているということは、非常にありがたく思っています。データのバックアップなどに使用するのであれば全然問題無い状態ですので、誤解されませんよう、お願いいたします。
自分が狙っている使い方というのが、ロボットの動作データをリアルタイムに読み出すってやつでして、サーボ制御周期である20msecの中で、次の動作データを読み出したいからなんです。20msecの周期の中で複数のサーボ動作データを読み出し、計算して制御パルスを送り込む、これが必要なわけでして。従来どおり、サーボ動作の補間計算とかはする予定ですので、20msec毎にガンガン読み出すのは、もっとも速度が要求されるケースの話ですが、考慮しなければならない問題です。
SCL/SDAのプルアップしている抵抗を2kΩに変更
結果から言えば…測定誤差以上の変化はないような、全般的に遅くなったような…な結果に終わりました。でも、推奨値ですので、このまま2kΩで行くことにします。
I2CシリアルEEPROMとはなんぞや?
さてさて、ちょっと頭冷やして考え直してみることにしました。そもそもシリアルEEPROMとは?EEROM(電気で書き込みできるROM)のひとつで、I/Fがシリアルバスのものです。普通、SRAMとかをマイコンに外付けすると、アドレッシング用に16本以上、データ読み書き用に8本、または16本の線をマイコンとの間に接続して、マイコンからは、アドレス信号をどど〜んと一度に並列で出して、データも、どど〜んと並列に取り込みます。パラレルI/Fってやつですね。
対してシリアルEEPROMは、RS232C通信のようにタイミングよく、マイコンから「10010...だよ〜ん」と言うとEEPROMから「11001100...だよ〜ん」と返ってきます。H8/3664と24LC256の場合、この「10010...」というデータを順番に読み上げていくタイミングが400kHz、1秒間に40万回。すごく早口です。さすがマイコンです。これと同じような感じで、特に別電源も必要なく書き込みもできてしまうというんだから、すごいですね〜。
I2C通信とは…実はこれがよくわからないんですよね〜。よくわからないんですが、複数のI2Cデバイスを1つの通信線上に接続で接続できますので、まずは誰かがマスタになって、デバイス指定して、その後アクセス云々という感じになっていると思います。アクセスそのものは、先に書いたような「10010...だよ〜ん」と同じなようです。これが、I2Cという規格上で流れているだけだと思います。H8/3664は、この順番に流れてきたビットデータを、バイトデータにくみ上げたり、逆にバイトデータをビットデータに直して送信する機能がついています。GDLには、以下のようなアクセス関数が用意されています。
EEPROM_ByteWrite (TEEPROM *, _BYTE);
EEPROM_BlockWrite (TEEPROM *, _BYTE *, long);
EEPROM_EraseAll (TEEPROM *);
EEPROM_ByteRead (TEEPROM *, _BYTE *);
EEPROM_BlockRead (TEEPROM *, _BYTE *, long);
書き込みの方はこんなもんかなぁって思っていますので、EEPROM_BlockRead()のソースコードを読み直してみました。括弧内は、GDLで用意されているシリアルEEPROMアクセス関数の、実際の関数名です。
1. EEPROMが書き込み完了状態かチェックする(EEPROM_Idle())。
2. I/OポートをI2C用にセットアップする。
3. I2Cバスが空いているかチェックする(_iic_busbusy())。
4. I2Cバスマスタ設定をする。
5. I2Cバスの使用を開始する(_iic_start())。
6. アドレスを設定して、データ読み取りをする。
7. I2Cバスの使用を停止する(_iic_stop())。
24LC256のデータシートを読むと、どのような通信によってデータが読み出されるかが書いてあります。う、うぅ、みなさん、こんな難しいものをよんでらっしゃるんですね…。いやいや、どんな難しい書物だって、百回読めばなんとやら。15回ぐらいでなんとかです。読み合わせてみると、データシートに書いてあるのは上記6の「アドレスを設定してデータ読み取りをする」部分だけのような感じです。そんなわけで、6-xで番号を振って、以下に書き出します。
6-1. H8/3664から24LC256へ「君だ!」に 9クロック(24LC256は、同じ線に8個接続可能です)。
6-2. 24LC256からH8/3664へ「了解!」に 1クロック。
6-3. H8/3664から24LC256へ「アドレスはここだ!」に 8クロック。
6-4. 24LC256からH8/3664へ「了解!」に 1クロック。
6-5. 3と4をもう一度繰り返し(アドレスは上位下位の2回に分けて送られますので)。
6-6. 24LC256からH8/3664へ「データはこれだよん!」で 8クロック。
6-7. H8/3664から24LC256へ「了解!」に 1クロック。
6-8. 連続で読み出す場合に6,7を繰り返し(6-7.をやると、次のデータを送ってくるようである)
まあ、よくわかっていないところは置いておくとして(いいのか?)、400kHzで動いているわけだから…8バイト読むのに必要なクロック数は、全部さらっと動いたとして、100クロックで読み出せることになります。ということは、400kHzですから、0.25msec+αで読み出せるような気がします。I2Cには、通信のお作法があるようなので、+αを見ています。しっかし、ここまでデータシートをしっかり読む羽目になるとは…(速度を求める自分が悪いのかもしれませんが)。だったらI2CじゃないシリアルEEPROMを使っても同じでした(笑)。AT24C1024の方は、1MHzまでいけるようなので、速度問題が復活してきたらこちらも試してみることにします。
まずは一発簡単に改造(危険度レベルちょっとアップ?)
というわけで、アクセスルーチンを見直してみることにします。読込時、EEPROMが書き込み完了状態かどうかチェックしていますので、これを取ってしまうことにします。安全な方法とは言えないのですが、実際に使うときは、ロボット動作情報のメンテナンス時は書き込みをしますが、通常の動作状態に入ると、書き込みは行わない予定です。よって読込時、書き込み完了状態をチェックする必要は無いような気がします。読み書き両方行う場合は、アプリケーション側にて、書き込み後に5msec以上必ずあけるようなプログラムにするということにして、すかーんとはずしてみました。
結果は…
32kByteアクセスするのにかかった時間 | |||
テスト内容 | 32kByteアクセス時間 | 1アクセス時間 | コメント |
64バイト読込 | 約2.2秒 |
約4.3msec |
− |
8バイト読込 | 約10秒 | 約2.4msec | − |
調子に乗ってさらに改造(危険度レベルぐぐっとアップ?)
さらにもう一歩ということで、「どうせH8/3664がマスターに決まってるじゃん。」という理屈で、一度マスタになったらずっとマスタになるようなソースコードに変更してみました。先の説明で行くと、2〜5のセットアップから使用開始までを一度やったら、読込動作している限り、ずっとこの処理をすっとばして、終わるときに7の「I2Cバスの使用を停止する」を実行するようにしてみました。また、せっかく、ソースコードへの理解もちょっと深まったので、24LC256(256kBitのEEPROM)専用のソースコードにして、プログラムサイズも小さくしてみました。さてさて結果は???
32kByteアクセスするのにかかった時間 | |||
テスト内容 | 32kByteアクセス時間 | 1アクセス時間 | コメント |
64バイト読込 | 約0.9秒 |
約1.6msec |
− |
8バイト読込 | 約1.8秒 | 約0.44msec | − |
う〜ん、でも、AT24C1024の方がいいかなぁ。なんか、24LC256とデータシート見比べていたら、なんとなく使い方わかってきちゃったし。うーん、どうしよう。結論は…気軽に使うなら、24LC256、GDLについているライブラリでばっちり動作。ちょっとプログラムを改造すれば、ぐぐっと10倍速。さあどうする?は、結論持ち越しということで…。ま、ま、充分なところまでは来てますんで、ゆっくり考えようっと。でも、余裕があった方がいいよなぁ。みなさんならどうします?
H8/3664にAT24C1024を接続すると24LC256より読取速度アップする?
不定期TOPIC 2003/10/07より
どうしようかな〜と悩んでいた、24LC256にするかAT24C1024にするかの問題ですが、とりあえず結論を出しました。結論としては、H8/3664においては、24LC256の方がよさげである、ということです。
まずH8/3664ってどれくらいの処理ができるのかチェックしてみました。こんなソースです。
volatile unsigned long ulBCnt;
for( ulBCnt = 0; ulBCnt < 10000000; ulBCnt++ );
「volatile」は、コンパイル時、最適化しない変数という意味です。コンパイラによっては最適化するとき、「むむ、このループ(for文)は意味無いじゃん。」といって、最適化の名のもとにとっぱらってしまうものがありますので、一見、無意味そうな時間待ちループをする場合は、これをつけておくのがよいです。
さて、この単純な10M回のループ、何秒かかると思いますか?(割り込み処理などはありません)
H8/3664で実測してみたところ、約32秒かかりました。ということは、1秒間に312,500回ループしていることになります。ということは…仮にAT24C1024を読み取るための処理が単純なものだったとして、このループ1回に1ビットずつ処理をしていったとしても、このループ数より速くは動けないと想像できます。ということは、312.5kHz以上のクロックによる処理は少なくとも不可能です。実際はそれなりの処理がありますので、もっと遅くなることが予想されます。
I2Cの場合、専用のハードウェアがついていて、そちらの方でバイトデータへ組み立てたり、ビットデータにばらしたりしてくれるので、前回実験のような速度がでるのですが、AT24C1024の場合はプログラムでやることになります。ひょっとしたらH8/3664の「クロック同期式シリアルフォーマット」とやらの機能を使えばできるような気もしますけど、仮にそれができたとしても571kHzが限界(H8/3664 16MHz駆動時に生成できるシリアル用同期信号の仕様)となります。この場合、SCL/SDAの出力電圧が2.5Vであるという問題に引っかかりそうですので、何か対策がいりそうです。
そんなわけで、思いっきり推論込みですが、AT24C1024を載せても、容量アップはするものの、CPUパワーの問題であまりメリットはなさそうです。
タイマ割り込みを入れたらI2CシリアルEEPROMが読めないゾ?
不定期TOPIC 2003/10/08より
さてさて、パワーには非常に不安のあるH8/3664ですが、タイマ割り込みを入れたらどれくらいパフォーマンスが落ちるかというチェックをしてみました。タイマ割り込みは2種類使用する予定です。1つはR/C受信機のパルス読込です。これはタイマVを使うこととしました。タイマVでφ/16にてカウントアップするようにしておくと、タイマVのカウンタは8ビットなので、オーバーフローによって0.256msec毎に割り込みがかかるようになります。2つめはサーボ制御信号の生成です。これはタイマWで処理しようと思います。理由は、8ビットでは分解度に不安があるので16ビットカウンタであるタイマWの方が安全パイであるというのと、タイマWにはコンペアマッチレジスタが4つついており、多数のサーボ信号を扱う上で便利そうです。
SH7047FのときのR/C受信機パルス読込処理
ちなみに、R/C受信機のパルス読込処理ですが、SH7047Fの時はIRQ0〜3を使ってやっていました。あのCPUのIRQは、立ち上がりエッジ、下がりエッジ両方で割り込みを掛けることが可能となっており、フリーランで走らせているタイマ値を、立ち上がりで読み取っておいて、下がりでもう一度読み取るだけでパルスの幅を取ることができました。よって精度も高く、しかも負荷がかからない(1つの信号につき、2回の割り込みだけでパルス幅を取ることができる)という、非常に良い仕掛けになっていたんですけど、H8/3664は残念ながら両エッジ割り込みができないので、あきらめです。
タイマ割り込みが入っている時のH8/3664負荷測定
とりあえず負荷だけわかればいいので、割り込みルーチンの方には、それらしい簡単なコードを書いておきました。タイマVの方は0.256msecに一度オーバーフローして割り込みがかかるようにしました。またタイマWの方は、カウンタ(TCNT)の初期値を調整して2.5msecでオーバーフローがおきるようにし、それぞれのコンペアマッチレジスタにも値を設定し、タイマWだけで、オーバーフロー、GRA、GRB、GRC、GRDそれぞれのコンペアマッチにて、計5回の割り込みがかかるようにしました。
まずは全体のパフォーマンスから。測定用のソースコードはいつものこれです。
volatile unsigned long ulBCnt;
for( ulBCnt = 0; ulBCnt < 10000000; ulBCnt++ );
結果は、約36.5秒でした。割り込み無しで約32秒ですから、割り込み処理によって14%程のパフォーマンス低下が起きています。いいのか悪いのか良くわからないんですが、まあこんなもんでしょう。タイマVの方が結構速い周期?で割り込みを掛けているにしてはいいかな?っていうのが正直な感想で、メインルーチンの方で動作データの生成等の処理をするには問題なさそうです。
プロポからの信号は、SH7047Fで計測したとき、実測値で1.5msecを中心として1〜2msecでした。そのため、タイマVの割り込み周期を、スティックの動きを検知できる最低値の0.256msecにしているのですが、せっかくなのでもう半分の倍速0.128msecとさらに半分の倍々速も試してみることにしました。
タイマV割り込み数(+タイマW)と処理能力の関係 | |||
タイマV分周 | 割り込み周期 | 10M Loop時間 | 処理比(割込無し/割込在り) |
φ/16 | 0.256msec |
約36.5sec |
約86% |
φ/8 | 0.128msec | 約40.2sec | 約79% |
φ/4 | 0.064msec | 約50.4sec | 約63% |
う〜ん、今後のソフトウェアの出来具合によりますけど、とりあえずφ/8ぐらいでいってみようかな。R/C受信機の信号取り込みには、なんかもったいない使い方です。なんかいい方法ないか、もう少し考えてみることにします。
タイマ割り込みとI2CシリアルEEPROM
せっかくなので、I2CシリアルEEPROMの方のパフォーマンスもチェックしてみることにしました。
「およ?」第一声です。Read
Errorやらなんやらいろいろ出ます。あらら〜。ど、どうしよう。というわけで調査開始です。本格的にプログラミングする前にチェックしておいてよかった…ということで気を取り直して。
ひとつずつつぶしていくと、EEPROMアクセスルーチン中の次のようなソースコードのところで引っかっていることがわかりました。
while (IIC.ICCR.BIT.IRIC == 0);
このwhile文の中の条件がいつまでも満たされている(IIC.ICCR.BIT.IRICが1にならない)ことがわかりました。これは何の信号だろう???さ〜こまった。どうしたもんでしょう?な、なんで1にならないんでしょう?おお〜、やばい、やばいです。いやいや、こんなときでも落ち着いて…マニュアル100回読めばなんとやら〜。むむむ?
I2CシリアルEEPROM調査、とりあえず最終回(のつもり)
不定期TOPIC 2003/10/09より
I2CシリアルEEPROMと割り込みについて
マニュアルやらI2Cやら調べてみましたが、これだ、というところには達していません。ただ、察するに、I2Cは、H8/3664からクロックを出して、それによってシリアルな通信によってデータの受け渡しを行うわけですからタイミングが厳密であり、割り込みがかかると信号のお相手をするのが間に合わなくなる→うまくいかなくなる、ということではないか?と思えてきました。具体的には、例のIIC.ICCR.BIT.IRICってのは、なんかI2Cでやった直後にクリアして、セットされるのを待つという手順があるんですが、このとき、割り込み処理とかが入ってきちゃうと、クリアの手順が遅れて、実は既にセットされているのにクリアしてしまい、その後、いくらセットされるのを待ってもダメであるという推測です。
というわけで、EEPROMに読み書きするときは、割り込み禁止にして行おうと思います。これ自体のソースコードは簡単です。GDLの場合、最初に割り込み禁止「DI;」(Disable Interruptの略と思います)をつけて、最後に「EI;」(こちらはEnable Interruptの略でしょう)をつければOKです。これにより、この処理の間、割り込みがかからないようになります。NMIだけはかかりますので、使っている方は注意してください。
ほんとは、割り込みルーチンを高速化して、EEPROMの読込ルーチンも要所要所だけをDI/EIで囲うのがベストであると思うんですけど、H8/3664がローパワーというところもあって、その域に入ろうとおもうと、これまた敷居が高そうなので、どうにもこうにも困ったときに考えようと思います。
さて、それでロボット制御ソフトウェアが成り立つのか?という話なんですが…(しばし考え中)…いけそうですね!例えば、サーボの制御信号は、16〜20msecに一度、最大2.5msec程度のパルスを出せればいいことになります。現在、設計中のソフトウェアは、このパルスを2.5msecの間に4ch分出力し、それを4回行います(合計16chまで制御できる仕様で設計しています)。ということは、タイムテーブルは、次のようになります。
時間 | 所要時間 | 処理 |
0.0msec | 10msec | 割り込み無くてもOK時間。EEPROM読み出しでもなんでもOK。 |
10.0msec | 2.5msec | サーボ信号4ch出力 |
12.5msec | 2.5msec | サーボ信号4ch出力 |
15.0msec | 2.5msec | サーボ信号4ch出力 |
17.5msec | 2.5msec | サーボ信号4ch出力 |
次はR/C受信機の信号取り込みですが、これはどうしましょう?1〜2msecのパルス信号が、やはり16〜20msec毎に入ってきます。これの幅をどうカウントするかが難問です。今のところ、「外部回路無し」という条件では効率的な方法が思いつきません。う〜む。困りました。ま、開き直って、EEPROMから読み取りしているときは、R/C受信機の信号取り込みはスパっとやめて、読取に専念することにしましょう。
SISO-LAB Knowledge