« 2013年1月 | トップページ | 2013年3月 »

2013年2月の2件の記事

2013年2月14日 (木)

STM8Sでグラフィック液晶に文字を表示することに成功

 STM8Sで外付けフラッシュメモリーを読み書きするルーチンがひとまず動いた。このプロジェクトの当面の目標は、このフラッシュに文字フォントデータを入れて、STM8Sでグラフィック液晶に文字を表示することである。

 このモノクログラフィック液晶(AitendoのJCG12864A37 以降GLCD)を制御するソフトは、AVRで開発が済んでいる。STM8SではSPIのところを書き換えるだけで容易に動くはずだ。今度付けたフラッシュメモリは大容量(512KB)なので、日本語フォントを入れて表示することも出来る。

S_p2125686

 ここまでやれば、STM8Sの顔も立つだろう。漢字がLCDで表示できるとなると色々なアプリケーションに使える。他の用途も考えられるので、電波時計にするかを今決めてしまうこともない。

 GLCDを動かす前に、大事な作業工程が残っている。外付けフラッシュメモリにフォントデータを収容する手順である。当初はメモリをソケットから外して、SDカードが読めるAVRなどのマイコンで転送する予定だったが、途中で気が変わった。STM8SのUARTのファイル転送に挑戦する。

UARTからのファイル転送の仕様を考える(1/30/2013)
 そのきっかけは、常用のターミナルソフトであるTeraTermにファイル転送機能が標準でついていることに気づいたからである。そう言えば昔のパソコン通信全盛の頃はファイル転送は日常茶飯事のことだった。

 別のマイコンでSDカードからフォントデータを入れるにしても、そのマイコンには今のフラッシュの書き込みソフトを新たに開発しなければいけない。どうせ開発するなら難易度は少し高くなるがSTM8SでUARTのファイル転送ソフトを作ったほうが手間が省ける。

 フォントデータの大きさは、1バイトの半角フォント(6X12ドット)なら5KBほどだが、日本語フォントともなると、12ドットフォントでも240KBはある。STM8SのUARTで、この程度の規模のデータが安定して受け取れなければいけない。

 STM8S側のUART受信ソフトの開発は、厳しい信頼性(1バイトでもずれると後のデータは全滅)が求められる。しかし挑戦しがいのある開発テーマでもある。安定したファイル転送を実現するための仕様の検討に入った。

 UARTの信頼性はフロー制御をすれば飛躍的に高くなるが、ハードソフトとも、もうひと手間、複雑になるので余りやりたくない。とすると、UART受信は待ちを一切いれず一気に読み切る必要がある。

 この前の受信割り込みルーチンではフラグを立てるだけだったが、今度は、割り込みルーチンでしっかりバッファーに貯める方式に変える。読みながら、フラッシュにブロック単位に書いていくことになるので、必然的にバッファー2つを使うダブルバッファー方式ということになる。

 さらにバッファーからフラッシュへ書き込むSPIインターフェースは、必ず処理の途中でUARTから割り込まれる。処理が分断されても問題ないかどうかの確認が必要だ。それにフラッシュメモリの書き込み時間も問題である。

 STM8SはSRAMが2KBしかないのでバッファーサイズは、余り大きくは出来ない。小さくしていくと、フラッシュの書き込み時間とUARTの転送時間が近くなり、バッファーオーバーランの心配がある。

 ロジアナで見ると64バイト程度のフラッシュ書き込み(2Mbps)は2ms以下である。UARTの64バイト受信は13ms程度(38.4kbps)であるので、そう神経質になることはないが、念のため実測しておくことにした。セクター消去の時間がロジアナでは計測不能なのでこの時間も測っておきたい。

 LEDの点滅に使っているタイマーの1tick(10ms)を減らして1msとし、フラッシュ書き込みの両側にコードを入れて実際の速度を測る。最初、10ms近くあり慌てたが、UART送信時間を含めて測っていたことがわかり、書き込み直後の時間をワークに入れてやり直した。その結果、転送時間はスペックどおり1msであった。ロジアナの1.6msとも符合する。

 一方、フラッシュメモリの消去は、セクター単位(64KB)なので時間がかかる。スペックによれば全消去(512KB)が2秒なので、0.3秒程度のはずだが、実測の結果は0.6秒だった。まあ、これは、ファイル転送の前に手動でやっておけば良い話なので関係ない。

 またSPIが割り込みに耐えられるかは、STマイクロのライブラリのコードを確かめた。その結果、単にレジスターとのデータの出し入れだけで、いわゆるAtomicオペレーション(分割できない複数の命令の処理)はないことがわかった。これなら割り込みを受けても心配ない。

 安定したファイル転送のための要件は満足しているようだ。残るはオペレーション手順である。転送を指示するコマンドの入力で、PCからのUART送信をひたすら待ち(タイムアウトを設けるが)、ファイル転送が終わったあとは、暫く待って一定期間データが来なくなれば終了と判断する、というプロトコルである(これがあとで大紛糾するのだが、このときは露知らず)。

擬似コーディングでダブルバッファーシステムを設計する(2/1/2013)

 ブロックレコードの転送では定番のダブルバッファー方式である。この方式ならUARTとの衝突を心配する必要がない。バッファーを2つ用意し、片側のバッファーにUARTの受信データを貯めながら、もう一方のバッファーから余裕を持ってフラッシュに書き込んでゆく。

 ちょうど良い機会なので、擬似コーディングの始めから、少し忠実にメモに書き出してみた。以下、こんな感じである。

 ------ダブルバッファー処理の擬似コーディング開発メモ------

 バッファーの状態を 0...未使用  1...書き込み中 2...満杯で転送待ち とする。

 バッファーA、Bの状態遷移表(真理値表)は、
        A  |  B 
        -------
        0  |  0     初期状態。とりあえずAから書き始める
        1  |  0     バッファーAに書き始めたとき
        2  |  0     バッファーAが一杯になり書き込み先をBに変更
        2  |  1     バッファーBに切り替えて書き込み
        0  |  1     Aを外付けフラッシュに転送した。書き込みはBで変わらず
        0  |  2     バッファーBが満杯になった。Aに切り替え
        1  |  2     バッファーAに書いている。
        1  |  0     Bを外付けフラッシュに転送した。最初から2番目に戻る

上の条件を整理していくと、ifを使ったロジックが作れる。一旦作っておいて、条件の聞き方を工夫する。整理すると条件式を減せるときがある。

まず、UART受信割り込みルーチンでは、A、Bをバッファーのステイタスだとすると、
if(A == 0) {
    if(B == 1) バッファーBにデータを移す
    else バッファーAにデータを移す
  }
else {
    if(A == 1) バッファーAにデータを移す
    else バッファーBにデータを移す
 }

 これくらい抽象度が高いと、ソースコードで見るより一目瞭然でロジックを確認できる。バッファーが満杯になって、受信したデータの書き込み先を切り替えるところと、満杯になったデータをフラッシュに書き出すところとは、処理するところが別々でクリティカルなところなので十分動作を確かめておく。

 このあとの処理として、それぞれのバッファーのポインターの処理(満杯になれば、ポインターを0にもどし、ステイタスを2にする)もここで必要である。

 次に、メインルーチンでは、満杯になったバッファーデータをフラッシュに転送していくが、このロジックは、これより簡単になる。
if( A ==2){
バッファーAのデータをEEPROMに転送し、フラッシュアドレスを進める
バッファーステイタスAを0にする。

if( B == 2){
バッファーBのデータをEEPROMに転送し、フラッシュアドレスを進める
バッファーステイタスBを0にする。

 UART受信割り込みでは、もうひとつ重要な処理を忘れていた。ステイタス0から1にするときの処理である。バッファーAが0のときは、最初のifで判別できるので、バッファーAに移すときに、ステイタスを1にしてやればよい。

 バッファーBは、最後のバッファーBに移すif elseのところであらためて、ステイタスが0かを確認したうえで、1にする。もっとも、ここは真理値表を良く見れば、必ず0か1なので(ここのステイタスBが2ということは、オーバーランエラー)、ifで聞くくらいなら常に1にしておく方が早いかもしれない。

 最後にバッファーに満杯にならずにデータ受信が終了した時のバッファーに残った最終データの送出処理と、実際のファイル転送をやるときのオペレーションにあわせたロジックを考えておく必要がある。
           --------------開発メモ終了------------- 

念入りな擬似コーディングをしたところは完璧。他はボロボロ(2/4/2013)

 周到にロジックを考えたので、ソースコードの完成は早かった。意気揚々とファイル転送のソフトのテストに入った。とりあえず、データが来なかった時のタイムアウトは20秒、データが来たあと(ファイル転送終了)のタイムアウトは、PC側がファイル転送を中断することもありえるので5秒とした。

Dblbuf

 ところが、これが不安定なのである。うまく行く時もあるが、何故か途中でファイル転送が中断され、残ったデータが終了後のコマンドプロンプトに流れ込んでハングする。奇怪なのは、ロジアナを入れると転送が失敗することである。

 ロジアナのプローブは動いている時と止まっている時とはインピーダンスが違うのか?まさか。UART(TTL)にはプルアップ抵抗も何も入れていない。それが悪いのか。そんな馬鹿な。しかし、ロジアナを動かさない時はOKでも、動かすと駄目になることは間違いない。頭を抱えた。

Dbl_cons

 しばらく悩んだが何のことはない、こちらのミスだった。ファイル転送の開始が遅れると、5秒タイムアウトが効いてデータを受信した途端、転送終了とみなしていた。要するにロジアナを動かす時のオペレーションの単なる遅れの差だけだったのである。ロジアナさん疑って失礼しました。

 しかし、これが解決した後もしつこいトラブルに悩まされた。転送が終わったあとのUARTにゴミが出て正しく文字が出力されない。データは2KB近くまでOKになっているが、ときどき失敗し、UARTにゴミが残る。タイムアウトのタイマーは、フラッシュに書き込むときでなく、UARTのデータ受信の度にリセットするようにしたのだが、それでもうまく行かないときがある。

 擬似コーディングを念入りにやったおかげで、ダブルバッファーのロジックは完璧に動いている。うまく行く時は、2KB以上のデータを間違いなく転送するのに、うまく行かない時は、たかだか400バイト程度でも途中で転送が止まってしまう。

 原因がわからない。ファイル転送のあとのタイムアウトを5秒から10秒程度にすると少し改善されるが、それでも不安定である。ダブルバッファーのところは完全なのに、タイムアウトのリセットまわりは良い加減なロジックで組み立てたのでボロボロだ。

変数の宣言ミス。やっとのことで安定してファイル転送がOKになった(2/5/2013)
 原因究明を続けた結果、どうみても転送終了を決めるカウンターが割り込みルーチンとメインルーチンの間で正しく受け渡されていないような気がしてきた。 UART受信の度に、このカウンターは0に戻されるのだが、これがどうも0に戻っていないようである。

 ファイル転送のタイムアウトは5秒後で、もしこのカウンターを0にしていないという仮説を取り入れると、これまで起きている様々なトラブルの症状はすべて説明することができる。問題のカウンターは、外部変数のlap_cntという変数である。メインのmain.cで定義し、uart2.cのUART関数で外部参照している。コンパイラーはエラーも警告も出していない。

 ソースリストを見ていて気が付いた。メインでの変数宣言は、int16_t lap_cnt; で、uart2.cの外部参照宣言は、extern uint16_t lap_cnt; だ(傍線 筆者)。あれ、タイプが違う。ふーむ。普通は、同じ名前をつけるとエラーになるか少なくとも警告が出るが、今回は何も出ていない。

しかし、コンパイラーによっては、こういう外部変数の参照解決には甘い(厳密にチェックしない)ことがある。特にこのRaisonanceのCコンパイラーは以前もヘッダーファイルまわりで散々な目にあっている。もしかしたら、これが悪さをしているのかもしれない。

 こんなことで直るとは思えないが、タイプを揃えてテストしてみた。何と、何と、これですべてが解決したのである。タイムアウトぎりぎりに、どんな大きなファイル転送をしても問題ない。40KBを超えるファイルも1バイトの誤りもなく転送に成功した。

 要するに、UART受信の度にカウンターをリセットしているはずが、やっていなかったために、5秒以内にファイル転送を終えなければ必ず何らかの不具合が起きていたのだ。

 わかってしまえば、何ともお粗末な話である。同じ変数だと思っていたが、考えてみれば、コンパイラーにとっては、タイプが違うので全然別の変数で、外部参照宣言では同一変数名はチェックしようがないのだろう。コンパイラーを疑って悪いことをした(でも警告ぐらい出してよね、と恨み節)。

 UARTでゴミが出るのは、ターミナルソフト、PCハード、USB-UART(TTL)アダプターのどこかにハードのフロー制御がされていて、UARTの受信を途中で止めると、残っていたデータがファイル転送を終えたコマンドプロンプトに現れて字化けをしていたものと思われる。当初はUARTが壊れているかと疑っていた。UARTさん、ごめんなさい。

 いやあ反省、反省である。擬似コーディングを念入りにしたダブルバッファーのところは、ピタリと動いて、そうでないやっつけで書いたところはバグの出まくりである。いい加減なカットアンドトライでロジックを作ると碌(ろく)なことはない。今度の開発では、つくづくこのことを痛感した。

GLCDドライバーをSTM8Sに移植する(2/7/2013)
 いつもの大騒ぎながら何とかファイル転送は成功した。これでフォントデータはいつでもPCからファイル転送で、コマンド一発でフラッシュに収容することが出来る。200KB以上のデータも問題なく入り、ファイル比較ソフトで検証しても一字もエラーは出ない。好調である。

 勢いに乗って、いよいよ次の工程、AVRで開発したグラフィックLCDのドライバーST7565.cのSTM8Sへの移植にとりかかる。変更しなければならないところは、2つ。ひとつは、SPIの原始関数と、もうひとつはフォントデータの取り出し方である。

SPIの部分は機械的な変更だが、フォントの部分は少し厄介だ。オリジナルは、.oファイルでプログラムフラッシュ上に展開されているので、すべてのキャラクターのフォントデータは一発でリニアに辿り付けるが、今度はそうはいかない。

 外付けフラッシュ上のデータは、今度のメモリ(M25P40)ではリニアで読み込めるとしても、文字キャラクターを読む度に、その都度フラッシュから読み出す手順が加わる。それに複数のフォントの対応でフォントデータの属性を換えるときに、いちいちフラッシュに読みに行くことは避けたい。

 久しぶりの本格的な、C言語の開発である。フォントの属性を決めるフォント構造体を定義して、ここに各フォントの属性(縦横のフォントサイズ、フォントデータの始点フラッシュアドレスなど)を入れる。

 あらかじめ、Open_Font()のような関数でフラッシュからフォントファイルのヘッダーを読み込み、このポインターを各グラフィック関数に渡すことにした。これで、いちいちヘッダーを読み直すオーバーヘッドがなくなる。構造体のポインターはChaNさんのFatFSで盛大に使われている。時々、そのソースをカンニングしながら、せっせとコーディングに励んだ。

Stm8

SPIの移植はすぐ済んだが、フォントデータが出てくるまで一苦労(2/11/2013)
 SPIの方はすでにフラッシュメモリで動いているので、移植はGLCDのSPIの原始関数をSTM8S用に換えるだけである。GLCDの操作に必要なI/Oピンも一緒に定義する。

 フォントデータのハンドリングを含めて、ソースコードがあらかた出来上がった。ハードウエアの準備はブレッドボード上のGLCDのジャンパーを、つなぎかえるだけだから簡単に終わった。さあ、テストである。これまで書いてきたコードが思ったように動くか。わくわくどきどきの瞬間である。

 GLCDは、最初は文字ではなくグラフィックの直線が出るかのテストをする。文字フォントは画面に出す前に、UARTのコンソールにフォントデータを表示するテストルーチンを組み込んで、こちらで確認する。いつもの逐次開発法である。

 電源を入れてUARTからコマンドを打つ。画面は全く反応なし。うーむ、残念。鉄則に従って、直したところを重点的に調べる。問題なさそうだ。面倒だが、再びロジアナのプローブをつなぎ直して波形を見る。あ、あ、追加したGLCDのI/Oピンで動いていないところがある。

  ロジアナの威力は絶大である。リセットとコマンド/データ切り替えのポートの定義を忘れていた。ピンだけ定義しても動くわけはない。あわてて修正。よーし、画面に変化が出た。ゴミだらけの画面だが動いたことには変わりはない。

 フォントデータの出力はもう少し手間がかかった。最初は、読んできたフォントサイズのXYの値がでたらめで表示が出来ない。バイナリが表示できるターミナルソフトで読むと、.oファイルは、実際のファイルの前に、余分なヘッダーをつけていることがわかった。しかも、長さが実際のデータより短い。

Photo

 .oファイルでなく、.fntファイルを使えばソフトの変更はしないですむことに気づいた。長さが違うのは、ファイル転送がバイナリーモードでないため0x20以下の制御コードが無視されていた。考えてみれば、検証のときは200KBが通ったと言っても全部テキストファイルだった。やれやれ。

GLCDにグラフィックは出たが文字が乱れる。ハードエラーだった(2/12/2013)

 画面に少しづつ画が出始めてきた。画面の汚れは、初期化のミスだった。フォントのハンドリングも細かい不具合がとれて文字らしくなってきた。しかし、読むたびにフォントデータがずれる。フラッシュメモリだけのときは、こんなに不正確ではなかった。

S_p2125683

 その日は画面の記念撮影をしただけで店仕舞いする。寝ながら色々原因を考える。GLCDとSPIを共用してからのエラーだ。原因はハードくさい。はっと気づいたことがある。フラッシュメモリには、電源にパスコン(0.1μF)を入れていない。これだ。

 次の日、久しぶりにハンダごてに火を入れて、フラッシュメモリ(M25P40)にコンデンサーをハンダ付けする。さあ、どうだ。おおお、良いぞ。フォントが正しく表示される。うーむ、まだ、たまにキャラクターがずれる(その後、ブレッドボードの接触不良がわかり、ほぼ完全になった)。

 フォントが少し乱れるが、何とかSTM8SでGLCDに文字フォントを出すことには成功したようだ。達成感で胸がふくらむ。SPIにプルアップ抵抗を追加すれば、もっと正確になるかもしれない。とりあえず、このあたりで一段落することにする。ソースと回路図を公開することにしよう。

 ただ、ソースコードは、まだ開発途上の半かけで、回路図もブレッドボードを使った、あくまでも暫定的な回路図であることをお断りしておく。STM8Sの公開ソースが少なそうなので、とりあえず少しでもお役にたてたらという気持ちである。

Glcd_stm8s

 使用SRAMは、1800バイトでほぼ満杯だが(ファイル転送ルーチンを別にすればもっと下がる)、フラッシュは17KBとまだ半分しか消費していないのでアプリケーションには余裕がある。

  これからSTM8Sで開発される方の参考までに、所長がこれまでに、このRaisonanceのCコンパイラーとリンカーで遭遇した注意点(不具合の可能性もあり)をまとめておく(前回の分も含む)。

  • ヘッダーファイルを使った関数のプロトタイプ宣言で、エラーが出たり警告が出て別ファイルにした関数の引数渡しがおかしくなることがある(原因不明。当該関数をひとつのファイルにまとめて回避)。警告は同一ソースを変更せず2回コンパイルすると、なくなってしまうので注意。
  • 外部変数と外部参照宣言はタイプが違うと同じ名前にしても警告にもエラーにならない。当然別の変数として取り扱われるので大はまりとなる。これはもしかして他のコンパイラーもそうかもしれないが、重複名の定義は確かリンカーで警告を出してくれたような。
  • dataという変数名は予約語である。SRAM上に定義する変数の識別子のひとつだそうだ。エラーの説明が実態と全く違うので最初は気づかずにはまった。
  • memcpyなどの標準関数が、<string.h>などとしても正常に組み込まれていない。エラーにもならない。最初GLCDの画面が汚れていたのはこれが原因(自前でクリアする手順をつけて回避)。

 以下にSTVD(STMicro Visual Develop)のプロジェクトフォルダーを固めたものを置きます。ライブラリのリンクは、ご自分の環境に合わせて必要なファイルをプロジェクトに入れてください。(STM8S_StdPeriph_Lib_V2.1.0を使っています) STVDの環境で、glcd.stwを開けば一式が読み込まれます。Buildは最初からやってください。

「JCG12864.zip」をダウンロード

| | コメント (2) | トラックバック (0)

2013年2月 1日 (金)

STM8Sにフォントデータを入れる外部フラッシュメモリをつける

 販促基板のUSBローダー部ばかりが注目されて本体は不遇な8ビットマイコンSTM8Sに同情して始めたプロジェクトは、次はこれに外部メモリをつけてSPIで動かそうというところまで進んだ。

 前回の記事では、モノクログラフィックLCD(以降GLCD)をAVRで動かしたが、本来はこのSTM8Sで動かすためのものである。今度の外部メモリをつけるというのも、風が吹けば桶屋が儲かるという諺に近い話で少し説明がいる。

 GLCDに文字を出したいが、STM8SはGNUコンパイラーがないのでフォントファイルをフラッシュに埋め込めない。それなら外部メモリにフォントデータを入れればよい。どうせなら、いっそのこと日本語まで出ると面白いだろう、という脱線に近いプロジェクト進行である。

 しかけはSTM8SのSPIインターフェースに外部メモリをつなぐだけである。コーディング例は、例の平坂氏のSTM8SサイトにSDカード(SPI接続)を直か読みするサンプルソースにある。簡単に動くだろうと気楽に始めた。

  ところがこれがはまったのである。このブログは、ときどき「波乱万丈」と褒めていただくが、今回も、実はここに書くのも恥ずかしい思い違いをいくつかやって、見方によってはドラマチックな展開になってしまった。

 はたから見れば、何でまた、こんなくだらないことに血道をあげるのだろうと思われることばかりだが、負けず嫌いの当人にとっては、大げさに言えば自分の存在を賭けるくらいの入れ込みようになる。うまくいかないときは、率直に言って人生が暗い。生きていることがつらくて、来し方行く末をあれこれ考えるくらい落ち込む。

S_p1315669 しかし、問題が解決したとなると、暗い気持ちが一挙に吹き飛んで、まるで天下をとったような気分になる。山の頂上を極めたり、マラソンを完走したときと同じような快感だ。こういう快楽のときに分泌されるというドーパミン中毒に間違いなくかかっている。

M25P40という4メガビットメモリーをSTM8S-discovery基板につける(1/21/2013)
  このM25P40という8ピン外付けメモリーは、以前LatticeのFPGA XP2に載せるつもりで買ってあったが、XP2本体のメモリで用が足りたので使わずに置いてあったものである。SPIインターフェースで、サイズは512KBもあるから、12ドットの日本語フォントファイル(240KB)でも余裕で入る。書き込みは遅いが、読み出し速度は速い(この石は25Mbit)。

 ただ、SPIはGLCDで使う予定だ。SPIはCS(チップセレクト)で複数のディバイスを一本のSPIでつなげるとはいえ、プログラムは複雑になる。最初は、独立したI2CのEEPROMにするつもりだった。しかしI2C EEPROMの手持ちは最大が1Mビットで128KBしか入らない。それに速度がせいぜい1Mbitなのでちょっとかったるい。

 少し迷ったが、結局、M25P40を外付けメモリに使うことにした。STM8S-Discoveryの試作スペースにM25P40を実装することにした。久しぶりの基板の半田付けである。STM8S-Discoveryには、基板の一部にユニバーサル基板的な蛇の目パターンがあって、真ん中には表面実装の8ピンPSOCパターンまで用意されている。

 最初、このパターンに吸い寄せられるように、嬉々として、M25P40(PSOC 8ピン)をパターンにハンダ付けして、はたと気づいた。M25P40へのデータ移動は、SDカードをつけたマシンを経由しようと考えている。ソケットで着脱できるようにしておかねばならない。お馬鹿な話である。

S_p1305662 例の低温ハンダ(ストロベリーリナックスの低温ハンダが安い。¥1575)で取り外し、DIP用の変換基板(秋月の8ピン用¥11)に載せ換える。このM25P40の1番ピンがわかりにくい。データシートにはチップの文字が表記されていないが、正順に置かれているとして左下を1番としたが不安である。電源とグランドが、たすきがけの配置なので間違えれば一発でお陀仏になる。

 ソケットをつけたあと、UEW線で本体との配線をすませる。SPIなので3本をつなぐだけだ。電源とグランドを合わせて5本のハンダ付けである。ハードの準備はあっけなく終わった。さあ、次はソフト開発である。

STM8SのSPIはSDカードアクセスのサンプルソースを利用する(1/23/2013)
 平坂氏のサイトのSDカードのプログラムは、STマイクロ社のSDカードのサンプルコードが大元になっているようだ。 SDカードのアクセスは、ChaNさんのFatFSを移植する予定なので、SPIをドライブする基本関数部分だけを利用させて貰う。

 同時にM25P40のデータシートを読んでアクセス手順を勉強する。EEPROMは、STM32で気圧計に実装したり、AVRでも使っているので理解が早い。読み込みはページ区切り(256バイト)を無視して連続して読めるようだ。ただし、書き込みは、どこからでも書き込めるが、256バイトのページ単位で折り返される。

S_p1305656

 読み出しを止めるのは、チップセレクトだ。書き込みは送信をやめるだけで止まる。擬似コーディングを始めて、ドライバーの構成がかたまってきた。いつものように、ソースコードは3階層に分ける。

 最初のSPIのところは、GLCDと共用になることを意識して変数名を考える。ここでやることは、SPIの初期化と、1バイトの送受信関数だけである。2段目は、EEPROMのアクセスルーチンで、データ書き込み/読み込みシーケンスの基本(共通)部分をここで作る。3段目がアプリケーションレベルで、ここが実際のデータの書き込み/読み込み関数となる。

 当初、フォントデータの書き込みは、SDカードを持った別のマイコンに、このEEPROMをつないで、そこからオブジェクトファイルをEEPROMに流すつもりだった。そのために、STM8Sの基板に最初ハンダ付けでつけたEEPROMをわざわざ剥がして、ソケット付きにとりかえている。

 しかし、開発を進めていくうち、STM8SのUARTでファイル転送を使えば、そんな手間は要らないことに気づいた。データはバイナリーデータだし、送受信とも1バイトも取りこぼしが許されないので、UARTにはダブルバッファーを設定するなど、少し難しいロジックになるが、これはこれで挑戦しがいのあるプログラムだ。楽しみになってきた。

RaisonanceのCコンパイラーのエラーメッセージに惑わされる(1/25/2013)
  ソースコードはそれほど苦労せずに出来たのだが、わけのわからないコンパイルエラーが連続して、なかなか先に進まない。このRaisonanceのCコンパイラーは、無償だからあまり文句は言えないが、このコンパイラーのエラーメッセージが奇怪で、その対応に一日つぶれてしまった。

 何でもない関数のところで、わけのわからないコンパイルエラーが出る。しかも容易に解決しない。キャストが裸になっているのを括弧で閉じたり、ifの中の演算の括弧を増やしたりするといつのまにか直ってしまう。だいたいエラーメッセージが、実体を反映していない。

 エラーの大元は、
 void M25_Write_Page( uint32_t adr, uint8_t *buf, uint16_t leng)
という、adrで示されるEEPROMアドレスに、lengの長さの、bufにあるデータを書き込む関数である。全く同じ形式のM25_Read_Str(略)は、エラーにならないのに、こいつだけが通らない。全く不審である。

 しかも、出てくるエラーコードが Invalid parameter declarationになったり、syntax errorだったり不定で、funcdef requires ANSI-parameter listというわけのわからないものや、なかには、警告だが、'?' decleared. but not used.などという全く身に覚えのないメッセージが出たりする。

 そのうえ、プロトタイプ宣言をヘッダーファイルですると Invalid parameter declarationというエラーになるのに、プロトタイプ宣言をソースに持ち込むとエラーでなくなる(警告)。全くもって何のことかさっぱり見当が付かない。

 ソースをだましだまし調整して、コンパイルはやっとNO ERRORになった。大したステップ数でもないのに、およそ一日かかってしまった。先が思いやられる。

コンパイラーのご機嫌が悪い。いんちきな引数渡しをしている(1/26/2013)
 コンパイルが通った(少し警告が残るが)ので、いよいよテストに入る。テストのやり方は、UARTモニターから文字列を適当なEEPROMアドレスに送り込み、それを読み出しコマンドでUARTにデータを戻して確かめる方法である。いつもの定番のテスト方法である。

 ただ、この方法では、うまく動かなかったときのデバッグは不可能である。送信が悪いのか、受信がまずいのか、それともEEPROMがおかしいのかを簡単に特定することが出来ないからである。心配なので、最初からロジックアナライザーをプローブさせて送受信の様子をみながらテストすることとした。

S_p1305655 テストを始める。懸念したとおりいきなりハングするだけで全く動かない。ロジアナで見ると送信から動いていないことがわかる。やれやれ。今度は、UARTの出力関数を入れて最初からチェックして行く。いわゆるprintfデバッグである。あれえ、SPIの送信関数の引数がでたらめな数字になっている。なぜだ。

 関数を呼ぶ前と、関数内の引数の値が明らかに違うことを確認した。どうも警告を無視してコンパイルした結果がまずいようだ。プロトタイプ宣言をヘッダーファイルに定義すると、エラーになって、main.cに持ち込むと警告になった例の関数である。

 正しく引数が渡されていないのは明らかだ。同じような引数構成の他の関数は通っているのに、こいつだけがおかしくなる。コンパイラーの不具合の疑いが濃厚だけど、こればかりに時間をかけているわけにはいかない。

 対症療法を考える。ヘッダーファイルがらみでエラーが起きているようなので、この関数をmain.cの中に入れてしまって、1ファイルとしてコンパイルしてみた。なんだ。警告もなくなった。やっぱり関数の表記が悪いのではなかった。

 テストしてみる。うむ、パラメーターは正しく関数に送られている。ロジアナの波形も、とりあえずもっともらしい送信シーケンスになったようだ。しかし、まだ正しいデータは戻ってこない。

SPIのマスター受信って送信しないと動かないのね(1/27/2013)
 ロジアナで見る限り、正しい命令、正しいアドレスでEEPROMにデータを送っていることが確認された。しかし、受信をしても全くデータは受信できない。SPIに対する読み込み命令は正しくEEPROMに送信されている。しかしクロックパルスが出てこない。

 正しく送られているといっても、これは単にSTM8SからEEPROMに送ったというだけで、EEPROMがこれを正しく受け取ったかどうかの確認はできない。送信シーケンスのあとデータが書かれたことを確認するRead Status Registerという命令を送ってもStatus Registerが帰ってこない。もしかするとEEPROMを逆差して、壊してしまったのかもしれない。

 もういちどルーペを取り出して何度もチェックする。刻印はないが、印刷の方向はデータシートと同じで問題なさそうだ。電圧も正しくかかっている。調べるところがなくなった。状況は暗い。気分が落ち込む。しかし、こんなことくらいで引き下がるわけにはいかない。

 基本的なところから確認していくしかない。受信のとき、クロックパルスが出ないのはなぜだろうと考えていて最も基本的なことに気づいた。クロックパルスはスレーブ側のEEPROMが出すのではないのだ。あくまでもマスターのSTM8S側が出すものだ。うはあ、わかったぞ。このあいだサンプルソースの受信関数から不審な送信コマンドをコメントアウトしたことを思い出した。

 このサンプルソースは送信するときも、受信バッファーに入ったデータを受け取って呼んだルーチンに返している。てっきり全二重通信のために、受信のときも送信しているのだと思い、今度のような半二重通信には不要と考えて削除してしまっていた。

 そう言えば、これまで開発したSPIインターフェースはすべてマスター送信だけで、SPIのマスター受信というのはやったことがない。受信関数のサンプルソースに送信コマンドが入っているのは、受信クロックパルスを作るためだったのだ!何というお馬鹿な勘違い。

 I2Cのように、スレーブ側も何らかのパルスを出して交信しているのだとばっかり思っていた。SPIのスレーブというのは、クロックパルスを受けてデータを送ることしかやっていないのである。いやいや思い込みというのは恐ろしいものである。

 あわてて、受信関数にもダミーバイトを送信するコマンドを入れて、テストする。無事、ロジアナには受信パルスが復活した。ReadStatusRegsterコマンドのあとにもStatusRegisterのデータが出るようになった。

 しかし、受信したデータは意味のあるデータではなかった。StatusRegisterの値も、ロジアナではちゃんとしたデータがでているのに、受信関数を通した値は、全く違う値だ。書き込み完了が終わらず延々とループする。先はまだ遠い。

Ws000000 しかし、ロジアナのお陰で、全体の動きが完全に把握できている。もし、これがなかったらここでのデバッグは全くお手上げだっただろう。測定器の有難さをつくづく感じる。トラブルシューティングを続ける。

M25P40はEEPROMでなくフラッシュメモリーだった(1/29/2013)
 最後のオチが、ここに書くのもためらわれるお恥ずかしい思い込みだった。SPIの受信コマンドがまともに動いて、ロジアナで見る限り、受信シーケンスはちゃんと想定どおりのデータストリームとなった。しかし、送り込んだデータと似ても似つかぬデタラメなデータしか出てこない。

 データが送り込まれているらしいことは、受信するデータが少しづつ変化しているので、全く動いていないわけではなさそうだ。送ったデータより先のエリアは、0xFFで埋められているので、何らかのデータが書き込まれ、それを読んでいることは確かだ。

 ただ、データはでたらめである。もうデバッグすることがなくなった。解決を求めウェブをさ迷う。しかし検索するにも適当なキーワードが見つからないので有力な情報に行き当たらない。STM8Sのエラータ情報にSPIに関連するのが1件あるが、どうみてもそれとは関係ない。

 万策が尽きて、印刷したM25P40のデータシートを最初から少し丁寧に読み返し始めたときである。書き込みコマンドのところで妙な記述に目が止まった。

Page Program instruction allows bytes to be programmed in the memory(changing bits from 1 to 0).
 「ページ書き込み命令は、メモリのビットを1から0に変えてプログラムします。」

 なんで、わざわざ、1から0と断っているのだろう。ここで明かりが点った。あ、あ、あ、もしかして、このメモリーはこれまでのEEPROMと違って、フラッシュメモリーなのか。てっきりEEPROMだとばかり思っていたが、違うようだ。

 何のことはない。M25P40のデータシートにもちゃんとSerial Flash Memoryと書いてあり、EEPROMとは書いていない。大慌てで、改めてウェブで、EEPROM、フラッシュメモリ、NANDメモリ、NORメモリなどの外付けメモリの違いについて調べ直す(ここが詳しい)。

 大きな誤解をしていた。フラッシュメモリは、EEPROMといわれるメモリと違ってデータの書き込みでは、0から1には出来ないのだ。書き込むときは一旦、eraseで全体を0xFFにしてからでないと正しいデータにならない。

 そうか、だんだん書かれた内容が0になっていくのはそういうことだったのだ。間違いない。謎が解けた。あわてて、データシートのセクターイレーズ(Sector Erase)のコマンドを使って、メモリーを消去する手順を加える。

 テストする。はい、おめでとうございます。やっと思うようなデータが入り、それが出てきた。少し長い2バイトコードの漢字も入れてみる。問題なく日本語が戻ってきた。いやあ嬉しい。暗かった気分がすっかり晴れる。心が充実感で満たされる。周りを余裕を持って眺められるようになった。

Ws000001

 しかし、それにしても今度は長かったな。解決に少なくとも4日はかかっている。外付けメモリについて、いい加減な覚え方をしていてひどい目にあった。

 さあ、次は、UARTを使ったファイル転送だ。まだソースコードはデバッグのためのテストステートメントが山盛りになっており、機能的にはまだテストプログラムにすぎないが、とりあえずは一山を越した。ソースコードの公開は次のファイル転送を実現してからまとめてやることとしたい。今度はドジを踏まないぞ。

| | コメント (6) | トラックバック (0)

« 2013年1月 | トップページ | 2013年3月 »