« STM8Sでグラフィック液晶に文字を表示することに成功 | トップページ | STM8Sでグラフィック液晶に日本語文字が表示できた »

2013年3月17日 (日)

リニアPCMプレーヤーのデバッグで電子工作に戻る

 前回の記事以来一ヶ月が経ってしまった。これほど更新が滞ったことはブログ開設以来始めてのことである。電子工作自体のモチベーションが下がってきたこともあるが、この時期、行事が立て続けに続いたことも大きな原因である。

 例年いつも2月から3月にかけては、所属する団体の全体集会の準備と実施、恒例のスキー合宿、確定申告と、毎年必ず決まっている行事で電子工作どころではないのだが、今年は特にそれに輪をかけて別の行事がいくつも重なって多忙を極めた(え、いや現役のときに較べれば、屁のような話ですが)。

 学生時代の友人の新年会が遅れに遅れて、雪見酒を楽しむ会になったことを含めて予定外の飲み会が3つ、さらに義理を欠いていた旧職場の同窓会に久しぶりに出たり、古い友人が突然電話してきて飲むことになったり、さらに長女の縁談で関係者が集まったりして、このあいだ数えたら1ヶ月たらずの間に、10いくつも行事が重なっていた。

 で、電子工作の方は、STM8Sという8ビットプロセッサーにフラッシュメモリをつけて、グラフィック液晶に文字フォントを描いたところで止まったままである。書く事がないとはいえ、備忘録を兼ねているこのブログを放置しておくわけにはいかない。

P3175746

 それに、昔のリニアPCMプレーヤーのトラブルの問い合わせがあり、全体集会(浜松)から帰って、行事が一段落したので久しぶりにこのトラブルシューティングにはまった。原因究明は結構、大変だったが何とか収束することができたので、これを合わせてブログをアップすることにした。(プレーヤーの件は、記事の最後に)。

ダンプリストコマンドは完成したが(2/15/2013)

 これまでのテーマ、STM8Sの話である。STM8S基板(STM8S-Discovery)にフラッシュメモリをつけてモノクログラフィック液晶(Aitendo JCG12864A37)に文字を出す開発は、前回までに1バイトの半角文字を表示するところまできた。フラッシュメモリは2Mバイトの大容量のメモリである。折角だから日本語フォントまで出してみようということになっている。

 日本語フォントデータは、半角に比べれば圧倒的に量が多く、その取り扱いは簡単ではない。今のところ想定している12X12ドットのフォントでも240KBもありコード体系も複雑だ。そのため、デバッグの効率を高めるため、フラッシュメモリ内のデータを16進で表示するユーティリティを作ることにした。

 こういう周到な環境整備が、結局は成功への早道になることが多い。ダンプリストが簡単に出るかでないかでデバッグの効率は全然違う、などと一人で能書きを垂れながら、せっせと、UARTに16進表示を出すコードを書いた。ChaNさんのFatFSのサンプルプログラムにも良い例がある。しかし、これは書式付出力関数を多用されているので余り参考にならない。

Stm8s_hex ほどなく、ダンプリストのユーティリティが完成した。UARTのコマンドに入れて、半角のフォントのエリアをキーボードのキーで表示を止めたり、再開させたりして遊ぶ。左側に16進、右側にANKコードが出る本格的なダンプリストである。

 環境整備は出来たが、肝腎の日本語2バイトコードの表示関数の開発は、まだ手が出ない。このSTM8Sでは、やり残していることがまだいくつかある。その一つがスクロールである。AVRでは動かしたが、STM8Sには移植していない。どうもやる気が起こらない。

 画面が小さくスクロールの見栄えが良くないので気が進まないのだ。128X64ドットの画面だと、小さな文字(5X10ドット)でも、たかだか5行、20文字程度しか表示できない。これでは、スクロールすると、あっと言う間に文字が見えなくなってしまう。苦労の割には効果が薄い。STM8Sでは先送りすることにした。

 それでも、日本語フォント実装に向けて、少しづつ作業を進めている。フォントは2つまで入れ、複数フォントの動作を確認した。日本語フォントは、12X12の蕨(わらび)フォントをダウンロードした。しかし、フラッシュメモリ上のこれまでの1バイト系と整合性のある仕様が決められないのでフラッシュに書き込むところまでは行っていない。

STM8SのUART立ち上がりハングは直ってしまった(2/18/2013)
 もうひとつのSTM8Sの問題は、UARTがハングすることである。STVD(STマイクロの統合開発環境)でコンパイルしテストする分には全く問題がないが、そこを経由せず、単に電源を入れて動かすと、PCコンソールとのUARTがハングアップする。

 つまり、単独では動かない。実用的に使おうというときには大問題になる。始め、STVDに何かオプションがあって、単独で使うときのバイナリーは、統合環境のときと違うものを作るのではないかと一生懸命探したが何もない。

 他の方々の使用例を調べてみても、こういうことは全く書いておらず、問題なく単独で動いているようだ。どうも良くわからない。そのうちいつのまにか動いたり、また動かなくなったり、わけのわからない状態になった。

 ダンプリスト開発が一段落したので、少し、本腰を入れて調べ始めた。ときどき動くと言うのが曲者である。ソフトではなくハードを疑う。GLCDの表示はうまく動いているし、UARTも何か送受信をしているが字化けだけが原因のようだ。

 UARTの初期化が疑われる。試しにボーレートを下げて設定してみたが変わらない。こんなことで変るわけはないとは思いながら、今度は、UARTの初期化ルーチンをプログラムのメインループの直前まで遅らせてみた。

 ところが、なんと、これで直ったのである。今までのトラブルがうそのように快調にUARTが動く。要するに、電源投入後、すぐにUARTを初期化すると、ハングすることがわかった。STVDでは最初デバッガーが動くので、十分な時間の余裕が生まれていたのだ。

 STM8SのUARTを動かす時は、スタートのあと100μsくらいの待ち時間をUARTの初期化の前に入れる。というのが教訓である。クロックがまだ安定していないのだろう。

画面描画で遊ぶ。円を描くのが結構難しい(2/22/2013)
 行事が重なってきて、まとまった時間を電子工作に割けなくなってきた。懸案の次のテーマ、日本語フォント表示に踏み込む時間の余裕がない。と言って全く時間がないわけでもない。こまぎれの時間ならいくらでもある。

 そういうときは、TVの前で時間をつぶさず(下らない番組を見ていると自分がどんどん馬鹿になる気がする)、なるべくPCでSTM8Sの開発画面に向かうようにした。まとまったコードは書けないので、気の付いた細かな、とりとめもない開発を少しづつやっては楽しむ。

 グラフィックディスプレイ(GLCD)の描画速度が気になっていたので、調べてみることにした。どれくらいの早さで画像が出せるのだろう。そら。さんから頂いたソースコードには、点と線の表示関数はあるが、それ以上の関数の用意はない。

 そこで、まず、直線を組み合わせて四角枠(レクタングル)を描画する関数を作って表示してみた。どうせなので、乱数を使う。感心にも、RaisonanceのCコンパイラーは、GNUのRand()を実装していた。フォトフレームで使ったSTM32の乱数関数を参考に、ランダムなレクタングルを画面に出してみる。

 描画速度は余り早くない。SPIで送られたデータ転送速度より明らかに描画が遅い。描画の間に待ち時間をはさまないと、残像で画面が見えなくなり、レクタングルはスムーズに移動しなくなる。

S_p2245695  こんどは、円を描画してみた。円を表示するには、本来なら実数で平方根を計算する必要がある。しかし、128X64ドットくらいのGLCDで、浮動小数演算をして座標を求めても表示する場所は整数単位なので意味がない。

 こういうときは、みなさんどうしているのだろう。整数型の平方根算出法をウェブで探してみた。ウェブの力はすごい。あっという間に沢山の解答例が出てきた。だいたいは、ニュートン法である。

 しかし、これでも大げさだ。これくらいの平方根はニュートンの近似式を使うまでもない。平方根の小数1位まで出せるように(四捨五入するため)、元の数を100倍して、それに近い冪を求めれば簡単に、小数一位までの平方根が得られる。

 ロジックは良かったが、なかなか数字が合わない。調べてみたら、このやり方では、26を超える平方根を出すだけでも16ビット(65534)を簡単にオーバーフローしてしまうことがわかった(26X26=676でこれを100倍すると67600)。あわてて32ビットにして事なきを得た。

 画面に円が出た。しかし整数計算した円は、単純に描画すると、急激に値が変わる部分は、空白になってしまう。つまり、円が立ち上がる最初と最後の部分は、値が飛ぶのでその間は描画できない。

Photo  そうか、ちゃんとした円を描くには、最初Y軸から描きはじめて、45°まで行ったら、今度は、X軸を増やしていって描画していく必要があるのだ(図参照)。

 何度か試行錯誤のあと、やっと、円らしい画像が描けた。ただ、このLCDの表示は遅い。ちょっと動画らしく動かしてみたが、全体が白っぽくなるだけでまるで動画にならない。やはりFSTNで動画は厳しいようだ。

 ここに、以上のダンプリスト、円などの描画が出来るSTM8SのソースリストをSTVDのプロジェクトの形で置きます。この前と同様、必要なライブラリは同梱されていませんので各自サイトから落としてリンクさせてください。試作プログラムなので当記事と違う描画をしているところがあります。ご了承ください。

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

日本語フォント表示関数の検討に入る(2/26/2013)
 グラフィック画像の表示にもあきたので、いよいよ、日本語2バイトコードのデコードロジックを検討し始めた。検討だけならメモ用紙と筆記具さえあれば、どこでも出来る。スキーに行く時も一式を持っていったが、結局今回は、その時間がなかった。電子工作に熱中していた時は、行き帰りのバスの中でロジックを考え、良いアイデアが生まれた時もあったのだが。

 言い訳になってしまうが、STM8Sの開発の進捗が遅いのは、実はモチベーションが下がっているからではない。このSTM8Sの使用用途が決まっていないというのが一番の原因である。仕様を決めるときに「何に使うか」が明確でないので、色々迷ってなかなか決められないからである。

 今度の日本語フォント表示開発も同じことだ。JJYの標準時計にするなら、大げさな表示ルーチンは必要ない。大容量フラッシュメモリも必要ない。必要な日本語のビットマップデータを用意するだけで出来てしまうはずだ。

 日本語ファイル名や、テキストデータの表示という目的なら、本格的な日本語フォント表示関数を開発する意義があるのだが、あいにく、そういう用途は考えていない。

 まあ、あまり固いことを言っても始まらない。所詮アマチュアの工作だ。好きなように作れば良いというのが結論だが、つい昔の「問題解決」の癖が出て、「効率」を考えてしまう。

 それはともかく、日本語フォント表示関数の仕様である。シフトJISは、第1バイトで、このデータがシフトJISの1バイト目であることを識別できるので、フォント表示関数は、ANKの1バイトと共用にすることができる。

 こちらは、フォントグリフを縦から横に変換する処理(大抵のグラフィック液晶はこの処理が必要なようだ)が、かなりコードを喰うので、ANK(半角)も2バイト(日本語)コードも一緒の関数にしたいのだが、大抵のこのあたりのプログラムは、1バイトコードと2バイトコードのフォント出力関数は別のものを作っている。その理由がどうもわからない。

 入力を値参照にするかアドレス参照にするかも頭を悩ませる問題だ。1バイト関数でアドレス参照にしている例は殆どない。開発に手間がかかるだけに、このあたりの仕様は良く考えてから決めたい。そんなことで、中々検討が先に進まない。

 それと関数が出来上がって、GLCD上に日本語(漢字)データをどうやって画面上に見せていくかというのも何が良いか迷っている。やっぱりスクロールを生かして、画面上を移動させていくのが一番見栄えがするような気もする。うーむ、悩むだけで決まらないなあ。

LPCMプレーヤーの不具合を調べる(3/6/2013)
 そうこうする内に、3年前に作ったリニアPCMプレーヤーの不具合報告がコメントにあがった。こういう問い合わせは大抵、忙しい時に限ってくるものである。今度もそうだった。ステレオの音の左右のチャネルがばらけるのだという。

 最初、メディアによって違うと言うのでI/Oエラーか、リッピングのエラーだと答えておいたが、その後、詳しい調査報告があり、pause(一時停止)でも起きると言う。これは大変だ。ソフトの不具合の可能性が高い。

 いくらオープンソースとはいえ、発表したソフトに不具合があるのを放置しておくわけには行かない。久しぶりに3年前のリニアPCMプレーヤーのソースコードを開いて調べてみた。

 pauseは、DAC割り込みルーチンの割り込みをマスクすることによって、再生を止めている。ありゃあ、こりゃいかん。タイマーはpauseをかけても動きっぱなしで、LRのトグルはレジスターがコンペアマッチする度に動き続けている。

 V41、V42以前のバージョンのときはLRのトグルは割り込みルーチンの中の命令でやっていたので、割り込みを停止すればLRのトグルは止まるが、V41,V42ではハードでトグルをするようになっている。ジッターはなくなったのは良いが、pauseで再生を止めると、延々とトグルを続ける。

 このままでは、pauseのあとの次の再開が前の状態を保持して動く保証はない。50%の確率で左右が逆転する。生録(SL機関車の録音など)や、オペラなど、音の場所に敏感な音源を聞かない人にとって左右のチャネルの逆転は気づきにくい。それにしても、よくここまでわからなかったものだ。

 ただ、今は電子工作以外の事で忙しく、とてもこれに関わる余裕がない。調査してくださった、n_piezoさんにお断りのコメントを返すのが精一杯だった。折り返し、修正のお願いのコメントが返ってきた。まあ、半年も1年も放置する積もりはない。出来るだけ早く、pauseのバグだけでもとっておこう。

S_p3165741  スタート時点でのLR逆転はまだ原因は突き止められていない。ここも、LRのトグルはハードで行っているので(V41から)、もしかしたら、ソフトの可能性もありうる。しかし、時間がないので、そこまで調べるゆとりがない。

検証する音源データを作る(3/12/2013)
 全体集会の浜松出張から帰ってきて、やっと仕事が一段落した。早速、ブレッドボードに常備しているLPCMプレーヤーでデバッグを開始した。少なくともpauseの時のバグの修正方法はわかっている。コーディングに入る前にテストデータの作成にとりかかる。

 プログラムは考えたようには動かない。書いたようにしか動かない、というのが鉄則である。どんなに完全だと思っても、本当のテストデータで確認しないと安心できない。テストデータはデバッグの前に必ず作る。このあたりが長年ソフト開発をしてきた経験から生まれたノウハウである。

 数年前、音楽演奏の録音用に買ってあったRolandのデジタルレコーダーR-05を取り出した。最初、自分の声でレコーダーの前を歩きながら、「左です」「右です」などの声を入れたテストデータを作った。

 しかし、再生してみると内蔵マイクがちゃちなせいか、分離が悪くてどっちが左なのか右なのか全然使い物にならない。仕方がないのでPCで適当な音楽を鳴らし、PCのミクサーで左右チャネルを振り、ラインアウトを通してレコーダーに録音した。

S_p3165739  pauseの時の不具合修正は簡単である。割り込みのマスクではなく、単にタイマー(Timer0)をストップさせる(TCCR0B = 0)。ただし、このレジスターはプリスケールを決めるレジスターなので、再開のため、ソースの種類(ステレオ、モノラル、44.1/22.05khzなど)によって決まるプリスケール値を保存しておく必要がある。

 pauseで左右が逆転することは全くなくなった。この部分は解決した。しかし、起動時の左右逆転は、たまにどころか、かなりの頻度で起きていることがわかる。あわてて、再生直前に、トグルピンを初期化するコードを加える。

 これで、電源投入直後の再生は、全くぶれがなくなった。ところが、再生を中止したり、別の曲を再生して(ここが逆転しているかはわからないが)、テスト曲に戻ると、何回かの割合で左右が逆転する。トグルピンを初期化しているのにおかしい。

迷走している(3/13/2013)
 そもそも、V41からは、DACにデータを送る割り込みルーチン、SDカードを読むsystick割り込み、それにLCDに経過時間を表示するバックグラウンドタスクと3段のマルチタスクになっている。構造が複雑な上、曲の再生開始のタイミングも、曲データがバッファーにある程度貯まったところというクリティカルな条件がある。デバッグは容易ではない。

 電源を入れた直後はうまく行くのに、そのあとがうまくいかないというのが気に入らない。片っ端から電源投入直後の初期化コードを、繰り返し処理の中に入れてテストするが、改善されない。これはBU9480Fが悪いのかと疑い始めた時、AVRのリセットでも、うまく行くことがわかって、その疑いは晴れた。

 結局、再生開始直前のトグルピンの初期化がどうも、うまくされていないという仮説が一番有力になってきた。ロジアナを取り出して確認しようと準備し始めた頃、たまたまMega168のデータシートを見ていて、意外な記述を見つける。

「COM0A1~0ビットの1つまたは両方が1を書かれるとOC0A出力はそのI/Oピンの通常ポート機能を無効にし、そのI/Oピンに接続されます。」(p64 14.9.1. タイマ/カウンタ0制御レジスタA )

 うはあ、トグル出力にしているピンは、タイマーにつなぐと、外からはいじれないのだ。それならと、タイマーの定義、TCCR0A=0で、一旦ピンをタイマーからはずし、通常のPORTD=(1<<PD6)などで、ピンの値を固定した後、再度、タイマーのトグルピンとして(bit0=0, bi1=1)、TCCR0Aを設定する。これでトグルピンは初期化されたはずだ。さあ、どうだ。

 いや、やっぱり駄目だ。まだ左右がふらつく。どうもタイマーの方でトグルの情報を残していて、それが復活してしまうようだ。これ以上直すところがなくなった。暗礁に乗り上げる。2日間悩んでいた。前の記事以来もう一ヶ月が経とうとしている。そろそろ記事を上げたいが、この状態のまま経過報告するのもしゃくだ。何とかすっきりする結果にしてしまいたい。

考えれば智恵が出るものだ。やっと正しいチャンネルで再生成功(3/14/2013)
 デバッグに疲れると、他の事をやって気を紛らわす。メモにこれまでの経過を書きとめて、あれこれ仮説をたてる。しかし解決策は生まれてこない。そろそろ、あきらめてブログにこれまでの経過を報告し、完全に直せなかったことを謝ろうと文案を考え始めたころ、ふっと方法を思いついた。

 タイマーのトグルにしたピンを勝手にいじれないのなら、トグルをデコードとは分けて何回かまわし(要するに空振り)、所定の位置に揃えてから再生を始めれば良いのではないか。トグルするピンの状態は、PINを読めばわかるはずだ。

 何か、光が差してきた。コードはそれほど難しくない。DAC割り込み部に空振りするロジックを加え、再生開始の方ではトグルピンを見て左チャネル(最初)になったのを確かめて、空振りをやめて再生に入る。うむ、これはうまくいきそうだぞ。期待に胸が膨らむ。

 あせる手でコンパイルする。何十回とやったテストを始める。おおー、良いようだ。何度やってもチャネルはぶれない。今までなら4~5回で必ず逆になる状態が20回以上やっても同じだ。もう大丈夫だろう。

いやあ、久しぶりの爽快感に体中が満たされていく。気分の良いことこの上ない。難しいパズルを解いたときの達成感である。苦労が大きければ大きいほど、この解決した時の喜びは何ものにも替えがたい。

 単なるデバッグでバージョンを上げるのは少々気が引けるが、混乱をさけるためV43としてプログラムソースを公開することにする。V41以降をお使いの方は、是非、このV43を使っていただきたい(V41以前はトグルを自前でやっているので問題ないと思われる)。また、この微妙な不具合を発見、報告していただいたn_piezoさんに改めて御礼を申し上げる。

ここに例によって、AVRStudioのプロジェクトフォルダーを固めたソースコード一式を置きます。元の記事にも
リンクを貼る予定です。上段が2号機、3号機の小さな液晶をつかったタイプ、下段が1号機のソースコードですなお、双方とも、ソースファイル名は前と変わっていないので注意してください。

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

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


|

« STM8Sでグラフィック液晶に文字を表示することに成功 | トップページ | STM8Sでグラフィック液晶に日本語文字が表示できた »

AVR」カテゴリの記事

STM8S」カテゴリの記事

コメント

いつも楽しく拝見させてもらってますー

整数のルートですが、参考になる資料として、
http://elm-chan.org/docs/avrlib/sqrt32.S
↑のリンクをお勧めします。

AVR のアセンブラコードですが、アルゴリズムは理解できると思います。
※足し算、引き算、シフト命令しか使われていませんw
これはアセンブラやハードロジックに適した方法で、割り算とあまり変わらないコストで高速に計算でき、「余り」がある為、精度も好きなだけ計算できます。
※展開法と言うらしいですが、コンピューターが開発される以前に既に開発されたアルゴリズムのようです。

投稿: hira | 2013年3月21日 (木) 11時27分

みなさん、色々な方法の紹介、ありがとうございました。

>tomozhさん、ブレゼンハムというアルゴリズムを始めて知りました。面白いですね。

>shuji009さん、これで円が描けるんですね。驚きです。

>O-Familyさん、おおー、読んでいただいているんですね。光栄です。
やっぱりsinのテーブルを作るのが早いですかね。

TCCR0BのFOC0Aビットの機能のご紹介ありがとうございます。これを使えば、わざわざ割り込みルーチンを空振りさせなくても出来そうですね。ただ、トグルの「強制変更」って、どっちに行くのか、やっぱり調べないと駄目か。

投稿: がた老 | 2013年3月18日 (月) 11時56分

がた老さん、お疲れ様です。

円ですが、前にアップした動画で使っている方法が高速なので、マイコンと小さい画面用途になら、お勧めです。

void gr_circle(int xc, int yc, int r, int color)
{
  int x, y;
  
  x = r; y = 0;
  while (x >= y) {
    gr_dot(xc + x, yc + y, color);
    gr_dot(xc + x, yc - y, color);
    gr_dot(xc - x, yc + y, color);
    gr_dot(xc - x, yc - y, color);
    gr_dot(xc + y, yc + x, color);
    gr_dot(xc + y, yc - x, color);
    gr_dot(xc - y, yc + x, color);
    gr_dot(xc - y, yc - x, color);
    if ((r -= (y++ << 1) - 1) < 0)
      r += (x-- - 1) << 1;
  }
}

元ネタは、
http://oku.edu.mie-u.ac.jp/~okumura/algo/

http://oku.edu.mie-u.ac.jp/~okumura/algo/archive/
algo.lzhのcircle.cですが、色々な場面で使われています。
Apple][でお馴染みのlode runnerのマップ画面がでてくるとき、閉じるときのアルゴリズムもこの方式でした。

投稿: shuji009 | 2013年3月18日 (月) 00時00分

こんにちは。
私もこの手の処理をする時は、Sin等の関数テーブルを多用します。
BASCOM-AVRのライブラリも、17バイトのSinテーブルで4象限を同時に描画するので、プログラムはとても簡単です。

Timerのトグル動作の出力状態は、私も位相差を作る発振器の時に悩みました。
解決は、OC0AとOC0Bポートの状態を読み出してから、[TCCR0B]の[FOC0A],[FOC0B]ビットでOC0AとOC0Bを任意の状態に設定できました。
ご参考までに!

投稿: O-Family | 2013年3月17日 (日) 22時44分

初めまして。いつも興味深く拝見しております。
円の描画ですが、僕はsin/cosを使ってやってました。
ちょうど、ここのサイトで紹介されている方法です。
http://www.ami3s.net/about_nsb/index18.html
sinはπ/2の分をテーブルに持って、線はブレゼンハムで描画していました。

投稿: tomozh | 2013年3月17日 (日) 16時57分

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック


この記事へのトラックバック一覧です: リニアPCMプレーヤーのデバッグで電子工作に戻る:

« STM8Sでグラフィック液晶に文字を表示することに成功 | トップページ | STM8Sでグラフィック液晶に日本語文字が表示できた »