hira のすべての投稿

KIT-RX65N、AUDIOプレイヤー更新

ファイラーがそれなりに動くようになったので、オーディオプレイヤーを更新、
PCMファイルのサポートも追加した。

操作は、ファミコンパッドで行うのだが、もうそろそろ、タッチスクリーンを有効
にしないと駄目な感じだ・・・
タッチスクリーンで使用しているICはFT5206という物らしい。

I2Cは、専用ポートしか考えていなかったので、専用のドライバーが既に動作し
ているが、FT5206はSCI6に接続されている。
なので、SCIの簡易I2Cを実装するしかない。
一応、ハードウェアーマニュアルを見て、実装してみたけど動作しない・・・
※ルネサス提供のドライバーは設定が面倒で、使いたくないし、読みにくいので参
考にするのも億劫だ。

割り込みでやるのは、設定が複雑になり、ハードルが高いと思ったので、とりあえ
ずポーリングでやってみたが、どうにも動作しない。
波形を見たいところだが、このボードでは、プローブを接続するのが難しいので、
他のボードで確認してみる事にしようか・・・
と思ったけど、ソフトI2Cで動作の確認だけしてみたー
動いたー(当たり前だけど)
※最初、動かないなぁーって思ったけど、とあるレジスターの応答が自分が思って
たのと違うだけで、ちゃんと動作してた(最近、こんな、早とちり的な事が多い)

ソフトI2Cで動作し、FT5206のマネージメントもそれなりになったので、
SCIの簡易I2Cをもう一度詳細に確認。
どうも、ポーリングの場合、受信動作は問題無いが、送信動作では、送信レジスタ
ーがバッファされているので、フラグを見る場合は、多少の工夫が必要な事が判っ
た。

while(SCI::SSR.TDRE() == 0) {
    sleep_();
}
while(SCI::SSR.TEND() == 0) {
    sleep_();
}

それを修正して動作するのかと思ったが、受信動作がどうも、うまくない・・・
よくよくレジスターのダンプを見ると、1バイトずれて読めてるような・・・
ルネサスのハードウェアーマニュアルの説明に沿って実装したものの、どうやら
本家の説明に間違いがあるようだー、SCIをI2Cとして使う場合、送信と受
信は連結されている、I2Cのスレーブアドレスを送信後に、受信レジスタを空
読みしないと、スレーブアドレスが残っている。

SCI::TDR = (adr << 1) | 1;  // R/W = 1 (read)
while(SCI::SSR.TDRE() == 0) {
    sleep_();
}
while(SCI::SSR.TEND() == 0) {
    sleep_();
}
volatile uint8_t tmp = SCI::RDR();  // ダミーリード

if(SCI::SISR.IICACKR() != 0) {
    utils::format("I2C Recv: Ack fail...\n");
    i2c_stop_();
    return false;
}

空読みするコードを追加したら正しく受信動作した、ヤレヤレ。

I2Cのドライバーは、全体的に考え直す必要のあるデバイスだ(SPIもそうだ
が・・)特に120MHzで動作しているプロセッサの場合は、ポーリングでデー
タを取得するような低速ドライバーは問題外と思う。(もったいない)
ただ、デバイスによって、通信の手順が色々なので、単純に割り込みにしただけで
は駄目で、柔軟性のある機能を提供する必要がある。
また、手順が面倒で、新規のI2Cデバイスドライバーを実装する手間が大変だと、
それはそれで、痛いので、よくよく考えて、全体を設計しておく必要がある。

とりあえず、受信と送信データはバッファリングして、それぞれの終了で設定した
コールバックを呼び出すようにすれば良さそうだ。

割り込み対応のI2Cドライバーは、次の機会に紹介する。

ソフトI2Cドライバー
SCI/I2C ドライバー
タッチスクリーンテストサンプル

-----
C++ に関するコラム:
C++ で C のキャストを使う人が多い・・・
以前に理由を聞いたら「冗長」だからーとか言ってた人がいるが、機能が違うので、
そんな理由で、使っては駄目だ。
前提として、キャストは、なるべく使わないように設計する必要がある。
「const」を取り除くような方法もいけない。
C 言語ベースの人はこの辺りを全く気にしない人が多いので困る。

KIT-RX65N、ファイラーの実装

文字表示が出来たところで、基本中の基本、ファイル選択を実装してみた。

「ファイル選択」は、NESエミュレーター、オーディオプレイヤーなど、
使用頻度が高いものなので、汎用性を高く実装する必要がある。

C++ の使いどころの一つに「テンプレート」がある。
C++ を始めたばかりの時は、テンプレートは難解で、とっつきにくく、敬遠
していた事や、簡単なシンプルなものに限られていたものの、馴れてくると、
利用範囲が広がり、最近は、テンプレート無しではプログラムが作れなくな
っている。
・便利すぎて、Cでプログラムする気がおきなくなる。

C++ の良いところは「型」に忠実である点がある。
但し、「より厳密な方法で実装」していない場合、細かいエラーが出て、先
に進めない。
この辺りは経験的な部分が大きいものの、反復する事で、考えなくても自然
に厳密な書き方が行えるようになると思う。
※Arduino のスケッチが駄目だと思うのは、C++ なのに、未だに「#define」
を多様していたり、C++11 以降の構文を使っていない部分、そんなスケッチ
が氾濫している。(動けば良いという考え方は良くないと思う)
※及第点を付けられるような、見本になるソースコードが少ないと思う。

※テンプレートプログラムの推薦書籍

使い始め、エラーが出た時に、そのエラーの意味や解消法、どうしてエラー
になるのかなど、意味不明が続くが、エラーが出なくなるまで、より簡単で
シンプルな構造にして、エラーの原因を見極めると、より複雑な構造に対処
できるようになる。
ネットにあるサンプルプログラムは、ある程度参考になるが、コピーペース
トだけでは限界がある、自分で考えて、実装する必要があると思う。
また、boost などの便利なテンプレートや STL を利用したいところでもある
が、メモリーの少ないマイコンでは、それも使える場合と使えない場合があり、
やはり、その場合は自分で何とかするしかない。
独学で勉強するには限界があると思える、勉強会などに参加して、誰かに習う
のが、やはり習得するには必要かと思える。
※この辺り、ある程度Cなどでプログラムが組める人には大きなハードルなの
かと思う。

今回のファイラーは、SDカードの操作クラス(SDC)、グラフィックス描画
クラス(RDR)の型が必要なので、テンプレートとなっている。

template <class SDC, class RDR>
class filer {

    SDC&    sdc_;
    RDR&    rdr_;

 public:
    filer(SDC& sdc, RDR& rdr) noexcept : sdc_(sdc), rdr_(rdr),
        ctrl_(0), open_(false),
        rdr_st_(rdr_)
    { }

...
};

これらクラスは、参照のコピーを内部に置いておくので、コンストラクター
で参照を渡す必要がある。

    typedef utils::sdc_man SDC;
    SDC     sdc_;

    typedef graphics::font8x16 AFONT;
    typedef graphics::kfont<16, 16, 32> KFONT;
    KFONT   kfont_;

    typedef graphics::render<uint16_t, 480, 272, AFONT, KFONT> RENDER;
    RENDER      render_(reinterpret_cast(0x00000000), kfont_);

    typedef graphics::filer<SDC, RENDER> FILER;
    FILER    filer_(sdc_, render_);

上記のように、型の定義が複雑なので、途中に反故があると、コンパイルは全く
機能しない、その場合に出るエラーは「意味不明」な場合が多く、対処にはコツ
が必要となる場合が多い。

「graphics::render」クラスは、ピクセルの型「uint16_t」、横幅、高さ、アス
キーフォント、漢字フォントをテンプレートパラメーターとしている。
又、コンストラクターでは、フレームバッファのアドレス、漢字フォントクラス
の参照を渡す必要がある。
「graphics::kfont」クラスは、フォントサイズと、フォントキャッシュの数を
テンプレートパラメーターとしている。


※ディレクトリーは青で表示される、又、左端に「/」を表示している、通常は
右終端に表示すべきであるが、ディレクトリー名が長い場合に、画面の外に表示
されてしまう為。
上下スクロールはできるが、左右スクロール機能は無いので、画面に表示しきれ
ない場合がある。
また、操作はファミコンパッドで操作するようになっている、今後画面タッチで
も操作出来るようにする予定。

if(chip::on(data, chip::FAMIPAD_ST::UP)) {
    graphics::set(graphics::filer_ctrl::UP, ctrl);
}
if(chip::on(data, chip::FAMIPAD_ST::DOWN)) {
    graphics::set(graphics::filer_ctrl::DOWN, ctrl);
}
if(chip::on(data, chip::FAMIPAD_ST::LEFT)) {
    graphics::set(graphics::filer_ctrl::BACK, ctrl);
}
if(chip::on(data, chip::FAMIPAD_ST::RIGHT)) {
    graphics::set(graphics::filer_ctrl::SELECT, ctrl);
}

ファイラーの制御は、制御ビットで行うので、上記のようにファミコンパッドの
操作をファイラーの制御ビットに変換している。

-----
漢字フォントのビットマップデータは、250K程あるので、現在は、SDカード
上に置いて、キャッシュ機構を設けてあるのだが、RX65Nの場合、2Mバイト
のプログラムフラッシュがあるので、そこに置いても良いと思い、rx-elf-objcopy
を使い、「kfont16.bin」をオブジェクトに変換してアクセスしてみた。
しかしながら、何故か、fread 関数が正常動作しなくなった・・・
原因に思い当たる部分がなく、とりあえず、SDカード上に置くようにしてある。

KIT-RX65N、漢字表示

「ENVISION KIT-RX65N」の液晶は480×272サイズもある。
これだけ大きいと、フォントもある程度の大きさが必要だ。

以前は、128×64の白黒液晶用として、6×12、12×12のフォントを使っていたが、480×272の液晶には小さすぎなので、8×16、16×16のフォントを用意した。
※8x12アスキーフォントは自分でデザインしたもの、12×12は東雲12。

用意したのは、「東雲(しののめ)16」フォントで、フリーなビットマップフォントとして知名度が高い。
ただ、BDF形式で配布されている為、組み込み系で使うには、何らかの加工が必要となる。
これらの目的で、「bmc」(ビットマップコンバーター)を公開しているが、コンパイルするには、MSYS2 に色々とツールをインストールしなければならない為、ハードルが高いかもしれない。
※コンパイルの方法は、GitHub のREADME を参照。
※「bmc」は基本、コマンドラインだが、「-pre」オプションを付けるとプレビュー表示できる。

変換は以下のように行う。
(1) 「東雲16フォント」をダウンロード
(2) ASCII フォントのテキストへの変換:

./bmc -bdf -text fonts/japanese-bitmap-fonts-0.4.5/shinonome16/shnm8x16a.bdf fonts.txt

(3) 漢字フォントのバイナリー変換:

./bmc -bdf fonts/japanese-bitmap-fonts-0.4.5/shinonome16/shnmk16.bdf kfont.bin

変換したバイナリーコードへのアクセスは以下のコードを使って行える。

static uint16_t sjis_to_liner_(uint16_t sjis)
{
    uint16_t code;
    uint8_t up = sjis >> 8;
    uint8_t lo = sjis & 0xff;
    if(0x81 <= up && up <= 0x9f) {
        code = up - 0x81;
    } else if(0xe0 <= up && up <= 0xef) {
        code = (0x9f + 1 - 0x81) + up - 0xe0;
    } else {
        return 0xffff;
    }
    uint16_t loa = (0x7e + 1 - 0x40) + (0xfc + 1 - 0x80);
    if(0x40 <= lo && lo <= 0x7e) {
        code *= loa;
        code += lo - 0x40;
    } else if(0x80 <= lo && lo <= 0xfc) {
        code *= loa;
        code += 0x7e + 1 - 0x40;
        code += lo - 0x80;
    } else {
        return 0xffff;
    }
    return code;
}

※返った値は正規化されているので、16×16フォントなら32倍すれば、アドレスを得る事が出来る。
※-1(0xffff)が返った場合は範囲外のコード

アスキーフォントは、容量も大きく無いので、コード上に展開しているものの、漢字フォントは16ピクセルだと255Kもあるので、SDカード上に置き、キャッシュしながら描画するようにしている。
※将来的には、ボード上の EEPROM(32Mバイト)に置いておくのが良いだろう。
また、fatfs には、SJIS と UTF-16 の変換テーブルを内臓しているので、通常のコード体系は UTF-8 を使って統一している。
※昨今、SJIS のコード体系を使うのは色々な意味で痛い。

しかしながら、漢字フォントを扱うには SJIS コードの方が、都合が良い部分もある、SJIS だと、フォントをリニアに並べて、アクセスしやすい特性がある。
SJIS のコードを正規化して、簡単に目的のアドレスを計算出来る。
※これが UTF-16 とかだと、広範囲に散っているので、難しい。

とりあえず、東雲16フォントを SJIS 並びにしてまとめたバイナリーファイルを用意してあり、アスキーフォントは、ソースコードに直接展開してある。
※これらは、GitHub にプッシュ済み。

漢字フォントは、テンプレートライブラリになっていて、「typedef」する必要がある。

 typedef graphics::kfont<16, 16, 64> KFONT;
    KFONT       kfont_;

※「64」は、キャッシュ数

現在、フォントの描画はソフトで行っているが、RX65N の場合、将来的には、drw2d を使う事になると思うが、ソフトの描画は簡単(速度を気にしなければ)なので、一応必要そうな物をまとめてある。

    typedef graphics::font8x16 AFONT;
    typedef graphics::kfont<16, 16, 64> KFONT;
    KFONT       kfont_;

    typedef graphics::base<uint16_t, 480,="" 272,="" afont,="" kfont=""> GRAPH;
    GRAPH       graph_(reinterpret_cast<uint16_t*>(0x00000000), kfont_);</uint16_t*></uint16_t,>

RXマイコン・グラフィックス関係

アスキーフォントは、ビットマップの実態が必要なので、必要なフォントのソースをリンクする必要がある。
アスキーフォントも「東雲16」だが、プロポーショナル表示用に幅テーブルを用意してあり、それなりに表示出来る。
ただ、元々「等幅」用にデザインされているので、幅の調整は、
「I: -1, i: -2, l: -2」
だけになっているが、無いよりは「マシ」だと思える。

Renesas ENVISION KIT-RX65N 漢字表示サンプル

KIT-RX65Nでnesemu(≒ファミコンエミュレーター)の動作に成功

最近はESP32のM5Stackが流行っているようですがー、日本人なら、
ルネサスの「ENVISION KIT-RX65N」 ですよ!

Space Invaders は、元々、モノクロで、8080の2MHzくらいのシンプルなハード
なので、まぁ動くと思っていたが、nesemu は、カラーで、スプライトや、BGなど、
CPU(6502、1.7MHz)以外にも、かなり多くのハードウェアーをエミュレ
ーションする必要があるので、正直、普通に動作するとは思っていなかったが、駄目元
で、一応動作テストをしてみた。

-----

以前に、ESP32をやろうと思い、ソースコードのアーカイブを観ていたら、nesemu
のコードがあり、興味を持った。
とりあえず、nesemu 部分のソースを取ってきて、実験的に、Windows 上の OpenGL 環境
で動作試験を行った。
※その過程で、nesemu 関係のコードで、ポーティングに不都合な部分を色々修正した。
当然ながら、Windows 環境では問題無く動作するようになり、OS-Xでも試したが、
どうにも、サウンドストリームを適切に流す事ができず(ノイズが入る)に、中途な状
態で放置してあった。

nesemu のソースはCなので、丸ごと持ってきて、追加してコンパイルが通るまでは直ぐ
出来た、また、カラーテーブル付インデックスカラーを、RX65Nのフレームバッファ
にレンダリングする部分も一瞬で出来たが、動作するにはそこそこの時間が必要だった。
その過程で、POSIXのファイル操作関数にバグがある事が判り、修正したら、直ぐ
に走る事が判った。
※ファイルのシーク関数のステート判定に誤りがあった。

前から判っていた事ではあるが、STLを使うと、非常に多くのメモリーを消費する。
「vector だけならまぁいいか」と思っていたが、実際には、vector を使うには、他の
クラスも必要で、何だかんだ100K近くのRAMを消費して、ROMも250K程余
分に消費する・・・
※I/Oストリームなどを入れると500K近く消費する・・・
なので、vector を諦め、malloc で実装して、余分なメモリーをダイエットした。

RX65Nは二つのメモリーブロックがある。
一つは256K(0x00000000から始まる)、もう一つは384K
(0x800000から始まる)、以前は、RX64Mなどと同等に最初のブロックを
メインメモリにしていた。
LCDのフレームバッファとして16BPPなら255K(480×272×2)のメモ
リーはどうしても必要なので、これを、先頭の256Kに割り当て、メインRAMは後
半の384Kを使うようにリンクローダのスクリプトを修正した。
※この過程で、「start.s」のRAMクリアルーチンにバグがある事が判り修正した。
※RAMは0番地から始まる前提だった・・・

次に、サウンドのバインドを行った。
ファミコンのサウンドをソフトでエミュレーションするのは、そこそこ大変で、処理負
荷も高くなる。
サウンドのエミュレーションを有効にしたら、処理落ちする・・
また、1MビットROMのカートリッジはメモリー不足で動作しなくなった・・
※これは、サウンド関係でメモリを多く消費する為のようだ・・

            if(nesrom_) {
                apu_process(audio_buf_, audio_len_);
                nes_emulate(1);
            }
// nesemu.hpp の apu_process をコメントアウトすると、1Mカットリッジのゲームが走る(音無)

現状のコードでは、D/A出力用のコードが追加された為1Mビットのカートリッジは、
音無でも動作しない。

「処理落ち」を解消する為、一番処理負荷が高そうな部分に見当を付け、最適化してみ
た・・
本来、ファミコンエミュレーターは8ビットのインデックスカラーなので、LCDコン
トローラーの設定をそうすれば良さそうだが、ドライバーを手直しする必要があり、時
間がかかりそうなので、とりあえず、ビデオのコピー処理を最適化した。
それだけで、サウンドも大体スローダウンする事なく鳴るようになった。

Dragon Quest
GRADIUS

まだ、ソフトの完成度はあまり高く無いが、とりあえず、動作する・・

-----
ファミコンのパッドは、C-MOS4021B、シフトレジスターを使ったもので、
簡単に繋げる。
自分は互換ファミコンのパッドを流用した、5本のコードで接続する。
赤:VCC(本来は5Vだが、3.3Vでも動作する)
黄:GND
青:P/S(P60に接続)
茶:CLK(P61に接続)
白:OUT(P62に接続)

※良く考えたら、ファミコンパッドで使っているICはCMOSで電流をほとんど消費し
ないと思うので、配線を簡略化する為、ポートから供給する事にしたので、ピンアサイン
を変更。
赤:VCC(P60に接続)
黄:GND(P61に接続)
青:P/S(P62に接続)
茶:CLK(P65に接続)
白:OUT(P73に接続)

※このポートは、CN2を使う。

-----
将来的には、「.NES」ファイルを選択したり、ステートのセーブなど、必要な GUI を揃
えたりする予定だが、まだ色々パーツ(ソフト)が揃っていない為、少し時間がかかると
思う。
なので、現状、どのように動作させるか、簡単に記しておく。
・SDカードのインターフェースが必要。(NESファイルを読み込む為)
・D/Aコンバーター出力をアンプに繋ぐ必要がある。(音が欲しいなら)
※ファミコンはモノラルだが、ステレオ出力にしてある。
・ファミコン互換のジョイパッドを接続する必要がある。
・「.NES」ファイルを用意する必要がある。(ぐぐって欲しい)
現在、どのNESファイルを起動するか、「nesemu.hpp」に直接記述してあるので、それ
を修正する。

void service(void* org, uint32_t xs, uint32_t ys)
{
    if(delay_ > 0) {
        --delay_;
        if(delay_ == 0) {
            open("GALAXIAN.NES");
//          open("GRADIUS.nes");
//          open("DragonQuest_J_fix.nes");
//          open("Dragon_Quest2_fix.nes");
//          open("Solstice_J.nes");
//          open("Zombie.nes");
         }
     }
.
.
.

ソースコード

KIT-RX65N、LCDの初期化まで出来た。

ずいぶん長い間放置されていたが、最近、ようやくLCDの初期化が出来た。

画像は出ているようだが、「赤」、「青」、「緑」、「白」、「チェッカー」、
「ストライプ」などと、数秒おきに画面が自動で切り替わるナゾ動作が続く。
RX65NのGLCDC関係の部分を読んでも、この「現象」に対するヒント
は見つけられなかった。
この動作は、GLCDCの何かの機能なのだと思って、それを「OFF」にす
る方法をひたすら探したが、それらしい記述はどこにも載っていない。

結局、BGENレジスタを設定したら、このテストモードのような状態を抜け
て、想定した表示が得られた・・・
※BGENレジスタの設定が抜けていただけだった・・・

とりあえず、フレームバッファに適当に書いて、表示してみた。

ちゃんと表示するようだが、ガンマ補正がちゃんとしていない為か発色が少し変
な感じがする。(これは、これから解析するとして・・)
また、LCDは、そこそこコントラストがあるが、視野角が狭いようだ・・

もっとダイナミックな表示が欲しくなり、スペースインベーダーのエミュレータ
ーを動かしてみた。(あまりにあっさり動作したので拍子抜けだ・・)
このエミュレーターは「side」と言うオープンソースで、8080CPUと、
スペースインベーダーのアーケード基板をエミュレーションするもので、メモリ
ーも、内蔵メモリで足りるので動作の確認に便利だと思う。
※動作には、スペースインベーダーのROMデータが必要。
・invaders.h, invaders.g, invaders.f, invaders.e (invaders.zip)
今回は、それを、SDカードに書き込んで、SDカードから読み出している。
・inv_roms/
※コンパイルは「-O3」で最適化してある。
・エミュレーションにかかる処理負荷を計測したら、およそ60%程度を消費し
ているようだ、8080、2MHz程度でこの負荷は思ったより大きいが、その
気になって最適化すれば、20%くらいは下げられる感じはある。


現状では、音も無いし、操作も出来ないが、今後、それらを実装したい。
少し苦労したけど、音は鳴るようにした。

YouTube

ソースコードは、GitHubへコミット済み。

-----
このボードは5000円くらいで買えて、非常に使い勝手が良い、これから、
グラフィックス関係を強化して、色々なガジェットを作ってみたい!

追記:
・GLCDCのVPOS割り込みを設定して、画面更新の同期を行った。
・LCDの表示タイミングを調整して、画面の更新レートを60Hz近くにした。

追記:
・ファミコンパッド接続で、操作できるようにした。
※「SELECT」でコイン、「START」1Pスタート、左右キー、Aボタン

KIT-RX65NでもMP3再生の確認

動作は確実だろうけど、一応、確認だけしてみた。
※LCDの動作はまだもう少し・・
SDカードを使えるようにする必要がある。

「ENVISION KIT-RX65N」では、D/A出力は、それぞれ、
JoyStick、SW2に接続されているので、仮に配線して音出しの確認
をしてみた。
全く問題なくMP3の再生が出来た。
※将来的には、SPI通信とかで16ビットのD/Aをに出力する予定。

ソースコードは GitHub にプッシュ済み。

このボードは「E2-Lite」オンボードなので、プログラムの書き込みは
多少大きいサイズでも、短い時間で書けるのが良い。(Windows 環境のみだけ
ど・・)

D/A出力には、オペアンプによるボルテージフォロワを入れてある、3.3V
の中間の電圧が0Vとなっている。
参考回路
写真の物はPWM出力のRCフィルターが付いているが、まぁ問題無い。

-----
libmad のライセンスは、GPL なので、多少注意が必要と思う。
まぁ、自分のコードはそれより緩いMITライセンスなので、問題は無いと思
うが・・

RX64MでMP3ファイルのデコードとリアルタイム再生

WAVファイルの再生は出来た、しかしこれは当たり前すぎて、面白みが無い。

既に、RXマイコンでMP3ファイルの再生は実装した人がいたと思うのだが、
自分も確認の意味も含めて、実験してみた。

MP3のデコードには、libmad ライブラリを使う(これも先人と同じ)。
このライブラリ、かなり低負荷でMP3のデコードが出来るので、色々なプラ
ットホームで使われている。
※凄く昔に、i486DX100とかで、MP3をデコードするのがカツカツだ
ったように思う、それから考えると、ワンチップマイコンでデコードできるのは
かなり画期的な事だと思う。

DMACの制限により、1024ワード以上のバッファを転送するには別の工夫
が必要な事から、MP3デコーダーとD/Aのバッファの間にもう一つバッファ
を設ける事にした。
※そうしないと、MP3デコード処理の遅延により、バッファオーバーランが発
生して、再生が途切れてしまう。
それ以前に、120MHzのRXマイコンで、MP3をデコードした場合の処理
負荷を検証してみる。
MP3デコーダーにMP3ファイルを食わせて、回してみた、約4分くらいのフ
ァイルを1/2くらいの時間でデコードした、これなら、十分実用になる。
ただ、ビットレートやMP3をエンコードした条件により、処理負荷はかなり変
動するので、その変動時間を吸収できる量のバッファが必要と思われる。
簡単な実験では4096ワードでオーバーランが微妙に発生する感じなので、倍
の8192ワード設けるようにした。
※色々な曲を再生して聴いていたら、それでも怪しいファイルがあったので、さ
らに倍にした。

MP3の別の問題として、タグの問題がある。
MP3のタグは、ID3V1、ID3V2など複数のフォーマットがあり、ファ
イルの先頭にある場合(V2、可変長)、ファイルの終端にある場合(V1、固
定長)、バージョンの違い、文字コードの種類など、かなり面倒な仕様となって
いる、オープンソースのタグのパーサーはいくつかあるのだが、組み込みマイコ
ンのような少メモリの設計では無いのが痛い、結局は、「車輪の再発明」となる。

libmad はタグには感知しないので、もしID3V2タグが先頭にある場合、デコ
ーダーに食わせる前に、タグを見つけ出して、その部分を飛ばす必要がある。
以前にOpenGLのアプリで音楽プレイヤーを作った経験があり、その時に、
MP3のID3タグを色々とサポートした、その時は、libtag を利用したが、
やはり、組み込みマイコンにポートするには大掛かり過ぎる。
※とりあえず、ID3V2を見つけて、スキップするだけの実装を行った。
内部のフレームをパースするのは、暇なときにでも進める事とする・・

残念と思うのは、RAMの使用量が意外と大きい(現在81Kくらい)ので、
RX621、RX62NなどのRAMが少ない(確か64K)マイコンでは使え
ないと思う。(外部メモリーを実装すれば問題無いだろうか)
※プログラムサイズは、280K程度。
※gcc-6.4.0

-----
☆ 自作RX64Mボード

☆ GR-KAEDE

自作のRX64Mボードは、プログラムの書き込みをシリアル経由で行う為、
360Kのプログラムを書くのがかなり遅い、E1を持っている(借り物だけど)
のだから、J-TAGインターフェースを配線した方が良さそうだ・・・

必要なソースコードは、GitHub にプッシュしてある。
※libmad は、gcc-6.4.0 でコンパイル、ライブラリとヘッダーを上げてある。

RX64MでWAVファイルの再生

DMACを使い、D/A出力に気を良くして、WAVファイルの再生も行って
みた。

以前にRL78/G13を使い、8ビットPWM変調を使った、WAVファイ
ルの再生を行った事があり、その時のリソースを使ったので、とても簡単だっ
た。
RX64MはRL78/G13より桁違いに強力なので、処理負荷的には全く
問題無い。

SDカードの読み込みは、SDHIのポートに接続しているけれども、SDH
Iの無いRXマイコンなので、ソフトSPIで読み込みをしている、イマイチ
パフォーマンスが悪い(読み込み速度が遅い)が、それでも、120MHz動
作は伊達ではない。

各種サンプルレートに対しては、単純にサンプリングレートを変化させて行っ
た。
RX64Mにはサンプリングレート変換のハードウェアーがあるのだが、RX
65Nには無いので、それも踏まえている。
※RTK5_RX65Nでオーディオプレイヤーを作るのが本命。

DMACのトリガーとなる、TPUタイマーのクロック(60MHz)では、
44.1KHzは割り切れないので正確ではない。(44.118KHz)
※48KHzは割り切れるので、正確なタイムベースとなっていると思う。
※22.05KHzや38KHzもやはり割り切れないので正確では無い。
まぁ、12ビットのD/Aを使った簡易再生なので、多少の事は妥協する。
それでも、聴いた感じは、そんなに悪くは無い。(8ビットのPWMでもかな
り良い音がする事に驚く)
44.1KHzを48KHzにアップコンバートするのは、簡易的な方法なら
そんなに難しく無いので、44.1から48へソフトでアップコンバートする
のでも良いかもしれない。

コンソールのコマンドは
・dir
・cd [directory]
・pwd
・play file-name
・help

「play *」で、ディレクトリーの WAV ファイルを順番に再生する。
※WAV形式では無いファイルは無視する。

再生が始まる時、コンソールにヘッダー情報を表示し、時間経過をコンソール
に出力する。

ISFT: Lavf57.3.100
File:  '03 愛Dee.wav'
Size:   50807808
Rate:   44100
Chanel: 2
Bits:   16
Time:   00:04:48
00:04:47

IART: minato (流星P) Feat. 初音ミク
ICRD: 2010
IGNR: Pop
INAM: FAIRYLAND -mintia mix-
IPRD: magnet -favorites plus-
IPRT: 4/15
ISFT: Lavf57.3.100
ITCH: iTunes 9.0.2.25
File:  '04 FAIRYLAND -mintia mix-.wav'
Size:   39518208
Rate:   44100
Chanel: 2
Bits:   16
Time:   00:03:44
00:01:27

再生中は、
・「Space」:一時停止
・「>」:次の曲
・「<」:曲の先頭
で、プレイヤーを制御できる。

WAVファイルは、48KHz、44.1KHz、38KHz、22.05KHz、
8、16ビット、モノラル、ステレオのPCM形式ファイルに対応する。
※8ビットやモノラルはあまり実験していないので不具合があるかもしれない。
※GR-KAEDEでも簡単な修正で動かす事ができる、方法を知りたい場合
は、コメント欄にどうぞ。
※GR-KAEDEは、SDカードのハードウェアーに致命的な問題がある、
回避方法はあるのだが・・

「main.cpp」で、「GR_KAEDE」を有効にしてコンパイルすれば、GR-KAEDE でも
動作すると思うが、いくつか問題がある。
・SDカードのハードウェアー「DO」端子のプルダウン問題を回避する必要がある。
※色々な解決策があると思うが、「main.cpp」に具体的な回避策を載せてあるので参考
にして下さい。
・D/A出力0がLED4に接続されているので、ジャンパーを飛ばす必要がある。

GitHub AUDIO Player

RXマイコンで、DMACを使う

RX65NのLCDが中々進まない・・・
※もう少しな感じなのだが、初期化後に、表示はしている感じだが、謎の動作をす
る・・・

-----

LCDの制御でも使う事になるので、DMAC関係のテンプレートライブラリを実装
してる。
応用範囲が広いので、継続して機能追加、拡張する事になると思う、本来色々な利用
状況を想定して、必要な機能を実装しておくのが普通だが、他のテンプレートクラス
と連携して動作させる事が必須なので、どのような設計が適しているのか良い案が思
い浮かばないので、「仮」とする。

とりあえず、メモリー転送や、メモリーFillなど、DMAの起動をソフトで行う
物で、実験して動作を確認した。

実際には、DMACは、何かのペリフェラルデータ転送を行うハードとして利用する
のが普通なので、それらから利用できるような仕組みを盛り込む必要があるのだが、
どのような構成にしたら良いか迷うとこだ・・・

DMACで、fifo のような機能がソフトの介入無しに出来れば、かなり応用範囲が
広がると思えるのだが・・

とりあえず、D/AやA/D変換でDMACを使うような想定で、全体の構成を考え
るのが良さそうだ。
まず、タイマー割り込みでD/Aの値を書き換えて、サイン波を出力してみた。
これは、問題無くできた。
※サンプリングは48KHzとした。

※流石12ビットもあると、それなりの精度で出力出来る~

-----

次にDMA版、TPUタイマー起動でDMAをスタートして、D/Aのレジスタから、
メモリに転送する、バッファは有限なので、適切なサイズで、先頭に戻るようにする。
この時、DMACはリピート転送モードを使う。
リピート転送モードでは、最大でも1024ワードの転送しかサポートされないのが、
痛いところだ・・
※D/Aチャネル0、1が連続した32ビットデータなので、32ビット転送として
いる。

今回、48KHzをサンプリングとしたオーディオのストリーム再生を想定した、
48KHzで1024サンプルだと、21ms分なので、リングバッファの書き換えを
考えた場合、半分の10msくらいでサービスする必要がある。
今回の場合なら、100Hzの割り込み(10ms)を作成して、そのタイミングでサ
ービスすれば問題は無いが、もっと大きいバッファが必要な場合はどうするのか?
普通に考えて、DMAC終了割り込みで、ターゲットのアドレスを書き換えるなどのサ
ービスが必要となるなど、この仕様は色々痛い・・・
この場合、サービスにかかるオーバーヘッドが大きいと、次のDMA起動とオーバーラ
ップしてしまう場合が考えられる・・
その場合は、DMACをもう1チャネル使って、ピンポン式にすれば何とかなりそうで
はある・・
※何で、1024ワードに制限してるんだよぅ・・・

C++ のテンプレートでファンクタ・クラスを引数としているので、割り込み内から呼ぶ
ユーザー作成のタスク呼び出しは、展開され、関数ポインターを使ったコールバックの
ようなオーバーヘッドを生まないのが救いだ。
※これは、非常に短いスパンで割り込みが発生するような場合に効果が大きい。

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  TPU I/O クラス
        @param[in]  TPU チャネルクラス
        @param[in]  TASK    タイマー動作ファンクタ・クラス
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    template <class TPU, class TASK = utils::null_task>
    class tpu_io {
    ...

-----
設定が悪くて、思ったように動作しなかったが、ようやく思った動作になった。

呼び出しのプロトタイプはこんな感じになった~

//-----------------------------------------------------------------//
/*!
    @brief  割り込み要因による開始
    @param[in]  trg     転送開始要因
    @param[in]  tft     転送タイプ
    @param[in]  src     元アドレス
    @param[in]  dst     先アドレス
    @param[in]  lim     転送リミット(※カウント数なので注意)
    @param[in]  ilvl    転送完了割り込みレベル(0以上)@n
                        ※無指定(0)なら割り込みを起動しない。
    @return 成功なら「true」
*/
//-----------------------------------------------------------------//
bool start(ICU::VECTOR trg, trans_type tft, uint32_t src, uint32_t dst, uint32_t lim,
           uint32_t ilvl = 0) noexcept

最後に、以前に紹介した、整数計算を使った三角関数クラスを使って、リアルタイムに
計算して、周波数ジェネレーター的な実装を行い連続的な再生ができるているかを検証
してみた。


    X' = X * COS(s) - Y * SIN(s)
    Y' = X * SIN(s) + Y * COS(s)
    ※角度「s」が十分小さい場合、以下のように近似できる。
    COS(s) ≒ 1
    SIN(s) ≒ s
    X' = X   - Y・s
    Y' = X'・s + Y

CH0、CH1でsin、cosを出力している。

ターミナルから、周波数を入力すると、その周波数を出力する。
※サンプリングが48KHzなので、高い周波数だとエイリアシングが目立つ。

RX64M D/A ストリーム・サンプル

RTK5RX65N、未実装部品、SDカードの利用

次はLCDと宣言してたが、その前に未実装部品についてまとめておきたい。
また、SDカードを使う為にカードソケットを追加で取り付けた。

このボード、未実装の部品がある。
(1)SDカードソケット「SD/MMCカードソケット:101-00565-64(AMPHENOL COMMERCIAL PRODUCTS製)」
(2)SDカード電源用ゲートIC「ISL61861BIBZ」
(3)JOYスティック「SW3(JOYSTICK):SKQUDBE010(ALPS製)」
(4)イーサーネットコネクタ「RJ45_J3011G21DNL」
(5)イーサーネットPHY「LAN8720A」
※主に、上記部品で、SDカードソケット、JOYスティックは、部品が判らなかったので、
ルネサスに問い合わせをした。

もちろん、自分で部品を実装したら、保障は受けられなくなるのだが、機能があるのに使わな
いのは勿体無い。
一番困難なのは、表面実装部品で、DNFパッケージだが、これは、後々、手順などを整理し
たいと思う。(基板の実装屋さんなら簡単だと思う)

多少問題なのは、上記部品で、SDカードソケット、JOYスティックは、製造が終了してお
り、現在部品の入手が難しい。(入手できても割高になってしまう)
※代替部品を探したが、適当な物を探せなかった、また、日本では、やはり入手が困難だと思
われる。

特に、SDカードは、このような機器では必須のI/Oなので、マイクロSDカードを使って
代用させる方法を考えた。
ソケットは、秋月電子製、マイクロSDカードスロットDIP化キットを使う。
多少細かい、配線は比較的簡単ではあるが、強引な方法なのでお勧めできないのだが、他に良
い方法を思いつかない。
また、RTK5RX65Nでは、SDカードの電源をマイコンによって制御しているのだが、
電源制御につかっているIC(インターシル製)は、入手できるが割高なので、とりあえず
直接接続しておく事にする。

※自分は、「セメダイン SuperX」で、基板を接着した。
※強力両面テープでも良いかもしれない。

※マイクロSDには、ライトプロテクトが無いのでオープンにしてある。
※ほぼ、ストレートな結線なので配線は簡単だが、電源系は、熱が拡散するので、容量の大
きいハンダコテが必要と思う。

SDカード関係のソフトSPIでの定義

    // カード電源制御は使わない場合、「device::NULL_PORT」を指定する。
//  typedef device::PORT<device::PORT6, device::bitpos::B4> SDC_POWER;
    typedef device::NULL_PORT SDC_POWER;

    // Soft SDC 用 SPI 定義(SPI)
    typedef device::PORT<device::PORT2, device::bitpos::B2> MISO;  // DAT0
    typedef device::PORT<device::PORT2, device::bitpos::B0> MOSI;  // CMD
    typedef device::PORT<device::PORT2, device::bitpos::B1> SPCK;  // CLK

    typedef device::spi_io2<MISO, MOSI, SPCK> SPI;  // Soft SPI 定義

    typedef device::PORT<device::PORT1, device::bitpos::B7> SDC_SELECT;  // DAT3 カード選択信号
    typedef device::PORT<device::PORT2, device::bitpos::B5> SDC_DETECT;  // CD   カード検出

    typedef fatfs::mmc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> MMC;   // ハードウェアー定義

    MMC     sdh_(spi_, 20000000);

-----
以前に自作のRX64Mボードで、SDHIのドライバーを実装しようと、色々やったが、
どうやっても動作せず、また、ルネサスのサンプルも無いようだったので、あきらめていた。
だが、よくよく調べると、RX64Mマイコンには、SDHIインターフェース内臓版と、そう
では無いバージョンがある事がわかった。
実験で使ったデバイス「R5F64MFCFDC」は、SDHIは「無し」なので動くハズは
無い。
※SDHIインターフェースのバージョンを取得するレジスターは読めて、それらしい値だった
ので、てっきりSDHIがあるものと思っていた。

RTK5R65NボードのRXマイコン「R5F565NEDDFB」は、SDHI「あり」の
デバイスなので、SDHIインターフェースを使ってSDカードのアクセスを行う事が出来る。
4ビットモードを製品として使う場合は、SDアソシエーションとのライセンス締結が必要なよ
うだが、自分がリリースしているテンプレートドライバー、フレームワークは無保証なので、こ
れらには関知しない。
※SDHIには、ライセンスが必要無い1ビットモードもある。

現在、SDHIドライバーは開発中であり、とりあえず、動作確認の為、SPIのソフトウェア
SPIドライバーを使って、動作を確認してある。
RTK5 SD カードアクセス、サンプル