「電子工作な日々」カテゴリーアーカイブ

電子工作に関連するお話など・・

RL78/G13のミラー領域

UARTの実装を行っている過程で、文字列を出力する簡単なプログラムが、思ったよう
に動作しないで、随分悩んだ・・・

調べると、ミラー領域に関する認識が甘かったようで、データフラッシュ領域と当たって
いた事が判った。

8/16ビットマイコンでは、ポインターは16ビットサイズで、基本64キロバイトの
領域しかダイナミックにアクセス出来ない。

RL78では、物理的なメモリー空間は1Mバイト(64Kが16ページ)ある。
プログラム領域は、0番地から始まり、128Kバイト品(R5F100LGAFB)の場合、
0x1FFFFまで使える。

RAM領域は、128キロバイト品の場合、12キロバイトあり、0xFCF00から
始まる。

データフラッシュは128キロバイト品の場合、8キロバイトあり、0xF1000
から0xF2FFFまである。
※64キロバイト品では、半分の4キロバイト

RL78のgccでは、R/Wアクセスする領域は、常に0xF0000~
0xFFFFFをベースとしていて、RAM領域をベースにアクセスするようになって
いる。
ここで問題になるのが、プログラム領域に置かれた読み出し専用データのアクセスとなる。
RL78では、「ミラー領域」を使ってこれを解決していて、RAM領域、
データフラッシュ領域以外は、ROM領域のアクセスと同等となる
仕組みがある。

ここで、以前に修正したリンカースクリプトが問題となった・・・

 .rodata (MAX(__romdatastart + __romdatacopysize, 0x2000)) : {

この「0x2000」のオフセットが何なのか、判ってなかった為、これを無効
(0x0000)としていた・・・
※0x2000の領域が「空く」のは勿体無い・・

当然、「0x0000」にすると、文字列データは、先頭から配置される為、テストして
いたプログラムでは、0x2000以下の領域に配置される、ミラー領域では、このエリ
アは、データフラッシュ領域の為、0xF1000~0xF1FFFがアクセスされてい
た。
それには、途中で気がついて、オフセットを0x2000に戻したが、それでも、直らな
い、良く調べると、128KB品は、データフラッシュが倍の8Kバイトあり、
0xF1000~0xF2FFFまでとなっていた。
そこで、オフセットを「0x3000」とする事で、解決した。

 .rodata (MAX(__romdatastart + __romdatacopysize, 0x3000)) : {

※リンカースクリプトは修正済みでプッシュしてある。

つまり文字列などの固定データは、0x3000以降、0xCF00までに置く必要があ
る。
まぁ、このくらいの領域があれば、当面困る事は無いと思われる。

先頭の空いた領域には、「.lowtext」セクションが割り当ててあるので、ここには、割り込
みルーチンなどを優先的に配置するのが良さそうだと思う。
※割り込みルーチンは、ベクターが16ビット固定なので、必ず64K以内に配置する必要
がある。

また、64K以降のエリアへは、「__far」を使う事で、ポインターを32ビット扱いとして
アクセスする事も出来るが、C++ ではこのキーワードを上手く扱えないようで、悩んでる。
※とりあえず、Cの関数だけで操作するしか無い。

Cの関数宣言では、ポインターが32ビット扱いになり、逆アセンブルリストを観た感じでは
ESレジスター付き20ビットでアクセスするコードになっている。

「コード」の呼び出しは、20ビット対応の「call」命令が使われているので、全域に対して
アクセスでき、64Kの壁を意識する必要は無いようだ。

 12c:   fc be 32 00        call    !!32be <_main>

※コードが64Kを超えて伸びていっても問題は無いと思われる。

-----
RL78は、Z80のようだと思ったら、実際Z80(78K0)がベースのようだwww

※昔、ゲームボーイのプログラムで、Z80のアセンブラを随分扱ったので、馴染みがある。
でも、32MHzで動くZ80となると、かなり話が違う。

RL78/G13スタートアップルーチンと起動テスト

フラッシュへの書き込みプログラムを先に作ろうと思ったけど、まず、動かしてみない
事には始まらない~

gcc はビルドしてあるので、スタートアップルーチンを作って起動するまでの道を作る。

(1)まず、リンカースクリプトを精査する。

/usr/local/rl78-elf/rl78-elf/lib/rl78.ld

を雛形にして改造すれば良さそうだ~
※標準のリンカースクリプトは、RL78/G13 (Flash:64K / RAM:4K) 仕様のようだ。
このリンカースクリプトを参考に、4つのデバイス用を作成
※オリジナルでは、0x0000 ~ 0x1FFF までは、開発用のブート領域のようで、使わない
ので、それを無効にした。

    .rodata (MAX(__romdatastart + __romdatacopysize, 0x2000)) : {

    .rodata (MAX(__romdatastart + __romdatacopysize, 0x0000)) : {

追記:
この0x2000は、ミラー領域内で、データフラッシュ領域をバイパスするオフセットで、
重要な事が判明、又、128KB、256KB品では、データフラッシュ領域は、倍なので、
オフセットを0x3000にする必要がある・・

※「ミラー領域」の事が良く判ってなかった・・・

また、ハードウェアーベクターセクションに多少の問題がある。

  .vec :
  {
    *(.vec)
  } > VEC

通常、ハードウェアーベクターはソフトウェアーからは参照されないので、何もしないと、
最適化で、省かれてしまう・・・

  .vec :
  {
    KEEP(*(.vec))
  } > VEC

そこで、「KEEP」キーワードで囲む必要がある。
※「ivec」(割り込みベクターテーブル)も同じ。

また、各種デバイス用に、ROM、RAM の開始アドレス、長さなど設定して、4つのデバイス用
リンカースクリプトを作成した。

R5F100LCAFB:   32K (0x00000 - 0x07FFF) /  2K (0xFF700 - 0xFFEFF) / 4K (0xF1000 - 0xF1FFF)
R5F100LEAFB:   64K (0x00000 - 0x0FFFF) /  4K (0xFEF00 - 0xFFEFF) / 4K (0xF1000 - 0xF1FFF)
R5F100LGAFB:  128K (0x00000 - 0x1FFFF) / 12K (0xFCF00 - 0xFFEFF) / 8K (0xF1000 - 0xF2FFF)
R5F100LJAFB:  256K (0x00000 - 0x3FFFF) / 20K (0xFAF00 - 0xFFEFF) / 8K (0xF1000 - 0xF2FFF)

RL78/G13リンカースクリプト
※データフラッシュ領域は記述が無いので、何らかの対応を行う必要があると思う。

(2)次にスタートアップルーチン
今までは、独自に、スタックをセットするとか、アセンブラで書いたのだけど、ライブラリー
に含まれる標準のスタートアップオブジェクト「crt0.o」を、objdump でアセンブルソースを
出力して、参考にする方法が確実で簡単な事が判った。

rl78-elf-objdump -h -S crt0.o > crt0.lst

※これなら、まねるだけなので、アセンブラを詳細に理解する必要がほぼ無い。
※最低限の知識は必要だが、手本があれば、非常に簡単だ。
「crt0.o」は、以下の構成のようだ。

・ハードウェアースタックの設定
※スタックの開始アドレスは、リンカースクリプトで指定されたラベルを使う、また、
スタックの深さは、リンカースクリプトで指示する。(通常は、RAM 領域の最後から取る)
・ROM 領域から RAM 領域への転送
※定数などを読み書き可能な変数として設定している場合は、変数はRAM上に配置されるの
で、初期値をコピーしておく必要がある。
・.bss セクションのクリア
・C++ コンストラクター呼び出し
※rl78_run_preinit_array()、rl78_run_init_array()、rl78_run_fini_array()
※C++ では、main が始まる前にコンストラクターを走らせて、初期化しておく必要性がある。
・init 関数呼び出し
・main 関数呼び出し
・exit 関数呼び出し

とりあえず、適等に切り貼りして、「start.s」を作成。
呼び出し部は、「init.c」で実行。
最後に、リセットベクターに、start.sの開始アドレスを指示する必要がある。「vect.c」

const void* vec_[] __attribute__ ((section (".vec"))) = {
    start,
};

これで、全て準備が整った、後は、自分のプログラムをコンパイルして、先に作ったプログラムを
リンクするだけで起動するはず。

-nostartfiles    ----->    標準のスタートファイルを使わない
-T xxxxx    ----->    xxxxx のリンカースクリプトを使う

リンカーオプションでは、以上の二つが重要となる。

(3)LED点滅を書いて、実行してみる・・
以上で、main 関数が実行される準備が整った。
今回、LEDを接続するポートとして、P43を使った。
最初、LEDは点灯したままだったが、無効ループが短すぎたようだ、そこで、以下のように無効
ループの回数を増やした。
※「int」型は16ビットなので、「uint32_t」を使った。

int main(int argc, char* argv[])
{
    device::PM4.B3 = 0;  // output

    bool f = false;
    while(1) {
        for(uint32_t i = 0; i < 100000; ++i) {
	    asm("nop");
        }
        device::P4.B3 = f;
        f = !f;
    }
}

※無効ループ内では、「asm("nop")」を実行する、そうしないと最適化で、ループ自体が無くな
ってしまう。

これで、無事にLEDの点滅まで出来た。

プロジェクト全体のソースコードは、GitHub に全てある、

  cd FIRST_test
  make

で、実行バイナリー、「first_sample.mot」が出来るので、これを、「Flash Programmer」でデバイス
に書き込む。
※書き込み後、リセットが有効になっているので、書き込み機のリセットラインをオフラインにする
必要がある。

IMG_0798s

RL78を始める~ gcc の構築から~

R8C/M120 は確かにコスパが高く、パッケージも手頃で、良いのだけど、RAM容量が少なく、もう少しだけリッチなマイコンが欲しいと思っていたのだった・・・
※メモリーはせめて2Kバイトくらいは欲しい、128×64のビットマップLCDとかを扱う場合、フレームメモリーで1Kバイト消費するし、SDカードでファイルを扱う場合も、バッファ
がある程度必要だし・・

現在の自分の環境では、R8C の上位は、いきなり RX になってしまう、まぁそれはそれで良いのかもしれないけど、RX では高機能過ぎるし、デバイスが少しコスト高なので、AVR 328 くらいのデバイスも扱えれば良いなぁーって思っていた。

R8C/M120 の上位を使えば、もう少し話は早いけど、R8C 系は、既に古いデバイスで、上位のデバイスは割安感が無い、そこで、やはりと言うか、RL78 に落ち着く訳だけど、値段、デバイスの種類、機能など、バランスが良い事に改めて気づく。
ただ、自作で使っている人は少ないのか、製作記時は少ないようだ・・
※パッケージが基本、フラットパッケージのみなので敬遠されているのかもしれない。

何故、ARM を選択しないのかと思うかもしれないが、日本人だからルネサスを使うだけの事で、少し意地になっているかもしれないけど、ルネサスのラインナップは豊富だし、入手性も良く、値段もこなれていて安定している、マニュアルも日本語なので、開発のハードルは低いと感じている。
逆に、「日本人」なのに、何故海外製のマイコンに走るのか、聞いてみたい。
※ AVR は随分使ったけど、マイクロチップに買収されてしまったしなぁ・・

RL78 のラインナップは凄まじく多くて、どれを使うか非常に迷うけど、入手性と値段で、「G13」グループをとりあえず選択してみた。
「秋月電子」で安く購入出来る。

プログラムフラッシュ/RAM/データフラッシュ
・R5F100LCAFB:  32K/ 2K/4K @250円
・R5F100LEAFB:  64K/ 4K/4K @290円
・R5F100LGAFB: 128K/12K/8K @340円
・R5F100LJAFB: 256K/20K/8K @400円
・R5F100LGAFB搭載変換モジュール     @420円

早速、290円、340円を数個購入してみた、後で気がついたけど、340円のデバイスが、変換基板にハンダ付けされたタイプが420円で売られていた、これは安い!
※0.5mmピッチのハンダ付けはコツがいるので、自信の無い人は、モジュールを選べば良いだろう~
IMG_0795s
※変換基板にピンを立てて、ソケット化すると、交換が出来て便利ではあるけど、ソケットのコストが痛いので、このような安いデバイスは、直で、ユニバーサル基板に乗せている。

まず gcc を構築してみた、以前に R8C、RX で行った方法がそのまま使える。
※ gcc-4.9.3 を使った。
詳しい方法は、以下のリンクを参照して欲しい。
My GitHub RL78

次に、フラッシュプログラミング環境を整える。
最初、シリアルポートで簡単に接続出来ると思ったのだが、ドキュメントを読むと、多少の付加回路が必要な事が判った。
RL78 には、プログラミング時の専用端子「TOOL0」があり、「/RESET」のタイミングで、通信を行う事で、プログラミングモードに移行する。
※通常動作では、抵抗を介してプルダウンしておく。

・「ルネサスの参考回路」
リンクの参考回路(タイプB)では、トライステートバッファ(オープンドレインゲートとして利用)と、インバーターを使っている、少し考えて、トライステートをダイオードで置き換え、
インバーターをNチャネルのFETで置き換え、簡易回路を作成してみた。
RL78_FlashProgrammer
※自分が使った部品は、3.3V~5Vの環境に対応している、電圧降下が少ないショットキーダイオードを使ったが、リークの少ない物を使用している。
※/RESET のプルアップ抵抗は、デバイスに直接取り付けてあるので、省く。

※同じような変換回路を既に製作していた~ 「簡易UART書き込み器」

RL78/G13のデバイスは電源が少なくて、配線が楽だ!
・Vss、Vdd、EVss、EVddに0.1uFのパスコンを付け電源に接続する。
・REGCは0.47~1uFのコンデンサでVssに接続する。
※1uFを選択した。

IMG_0796s

とりあえず、「ルネサスの Flash Programmer 」と接続して、認識できる事を確認出来た。
※3.3Vの電源を接続

今回はここまで・・
次は、書き込みプログラムを実装してみようと思う。(MacBook で使いたい!)

俺俺 RX マイコンボードを作ろう!(その2)

以前、RX630Nで始めた、RXマイコンボード開発・・
途中、R8Cに注力してた事もあり、頓挫してました。
それと、例のピッチがズレた、秋月製、変換ボードの事もあり、あやふやな状態になっていました・・・
IMG_0665s

そんなこんなで、気分をリセットして、もう一度始めようかと思ってたら、RX64Mがオンライ
ンになっていました。
「RX71M」を待つってゆー選択もアリですがー、そんな事言ってたら、いつまでたっても始ま
らないのは明白なので、賞味期限が切れないうちにチャッチャと始めようと思います。

RX64M、2Mバイトフラッシュ版は、チップ1ストップで、2000円くらいです。
それに、内臓RAMは512Kバイト(+32K+8K)もあり、120MHz動作で、RXv2
コアで、パフォーマンスが高いです~
値段は、RX63Nに比べると少しは高いのですが、色々考えた末、RX64Mで作りなおす事に
しました。
IMG_0784s
※10個買うと、@1600円で、送料無料なので、10個買いました・・
※10個なら、トレイに入ってくると思ったので、それも動機になってます。
※チップ単体で欲しい方は、実費でお分けします。(1600+消費税+送料)

そして、変換基板は、アイテムラボさんの基板を使ってみました。
この基板、490円で、安くて、品質が高いです、169ピンでも極限まで小さくしているのも
良いです!
※送料が無料になるオーバー3000円分、他の基板も色々買ってしまいました。

いつも多少苦労する、0.5mmピッチのハンダ付け、今まで、大きな失敗は無いですが、視力
が落ちている事もあり、失敗すると、チップも変換基板も無駄になるのでストレスがかかる作業
です。
やり方は色々だと思いますが、自分の方法を簡単に書いておきます。
・最初に、チップを正確にパッドに乗せて、ルーペで良く観て、少しでもズレていたら妥協せず、
確実に中心に置く。
・プリント基板用フラックスをまんべんなく端子に塗り、とりあえず、1ピンだけハンダ付け。
・その状態でズレがないか再度確認。
・対角線上のもう1ピンを半田付け。(2点付けると、もうやり直しはほとんど出来ないので注意)
・隅の8点を、付けて、全体をハンダ付けしていく。(ハンダが多すぎるとブリッジするので、
少なめに、足りない場合は、少しコテ先にハンダを乗せて、塗ったフラックスに頼って流す感じ)
・ブリッジしたら、フラックスを塗って、コテ先で、こするように再加熱すると、意外と綺麗に
取れる。(パッケージの根元のピンにコテ先が触れて、ブリッジすると取れにくいので注意)
・それでも、ブリッジが取れない場合は、吸い取り腺を使うが、少しだけ呼び水的にハンダを
吸い取り腺にも溶かしておくと良い。
・吸い取り腺でハンダを除く場合、限度を超えて長い時間熱しないように注意する。
・ルーペで斜めから確認して、パッドと、ピンの「きわ」にハンダが流れているかを3度くらい
確認。
・もし「怪しい」ピンを見つけたら、フラックスを塗って、再度ハンダを流す。
・全て確認して、大丈夫と思ったら、パーツクリーナーなどで洗浄して、エアーダスターで、
ゴミなどを飛ばす。
・最後に、綺麗になった基板を、もう一度ルーペで確認して、問題無ければ終了!

IMG_0786s

この基板は、スルホールが裏にあるので、基板を直付けする場合、ショートするかもしれない
ので、絶縁しておきます。
最近は、ピンヘッダーは使わなくなっています、意外とコストがかかるのと、外して、別基板
で使う可能性が低い事(新規にデバイスを付けた方が安上がり)、高さが邪魔になるとか、色
々な事を考えて、大きい基板に直付けしています。

と、ゆー事で、今回はここまで。

RX マイコン用リンカースクリプトとスタートアップ

RX マイコン用 gcc を更新したので、リンカースクリプトも更新してみた。

gcc のリンカースクリプトの記述仕様は「不明」の部分が多く、それでも、C 言語
なら、そう問題にならないが、C++ の場合、事前準備が必要なので、そう簡単では
無いと思える。
また、gcc の新しい機能として、新規のセクションが必要な場合もあるかもしれず、
gcc のバージョンを変更したら、その gcc がインストールされたディレクトリーに
ある基準となるリンカースクリプトをベースにしておく必要がある。

組み込みマイコン用に、自前で gcc をビルドして使っている人の中には、かなり
昔に作られた、乖離したリンカースクリプトを使っている場合もあるようだけど、
これは、少し問題がある、何か新しい機能が新設された場合、ライブラリーが正しく
動作する保障は無いと言える。
※ C 言語のみの場合は、あまり問題にならない場合が多いかもしれないけど・・

自分の環境では:(/usr/local/rx-elf)
gcc 付属のリンカースクリプトは、「/usr/local/rx-elf/rx-elf/lib/rx.ld」にあり、
それを、基準にしている。

・「rx.ld」を観ていこう~
(1) これは、RAM、ROM領域、及びスタックを定義している、自分が使うマイコンの
メモリーマップに従って、領域を設定する。
※スタックは、下位アドレスに向かって消費するので、「LENGTH」に大きな意味は無いものと
思う。

/* This memory layout corresponds to the smallest predicted RX600 chip.  */
MEMORY {
	RAM (w)   : ORIGIN = 0x00000000, LENGTH = 0x00020000 /* 128k */
	STACK (w) : ORIGIN = 0x00020000, LENGTH = 16 /* top of RAM */

	ROM (w)   : ORIGIN = 0xfff40000, LENGTH = 0x000bffd0 /* 768k */
/* This is the largest RX6000: */
/*	ROM (w)   : ORIGIN = 0xffe00000, LENGTH = 0x001fffd0 */ /* 2Mb */
}

(2) このセクションは、プログラムコードが収められている。

  .text :
  {
    PROVIDE (_start = .);
    *(.text P .stub .text.* .gnu.linkonce.t.*)
    KEEP (*(.text.*personality*))
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
    *(.interp .hash .dynsym .dynstr .gnu.version*)
    PROVIDE (__etext = .);
    PROVIDE (_etext = .);
    PROVIDE (etext = .);
    . = ALIGN(4);
    KEEP (*(.init))
    KEEP (*(.fini))
  } > ROM

(3) このセクションは、読み出し専用データが収められている。

  .rodata : {
    . = ALIGN(4);
    *(.plt)
    *(.rodata C C_2 C_1 W W_2 W_1 .rodata.* .gnu.linkonce.r.*)
    *(.rodata1)
    *(.eh_frame_hdr)
    KEEP (*(.eh_frame))
    KEEP (*(.gcc_except_table)) *(.gcc_except_table.*)
    . = ALIGN(4);
    PROVIDE(__romdatastart = .);
  } > ROM

(4) このセクションは、起動時、RAM にコピーしておく必要のあるデータなどで、ROM 領域
に配置する。
※「__romdatacopysize」が定義されている。
この中で、「.ctors」、「.dtors」と二つの重要な部分がある、これは、アプリケーション
を起動する前に準備しておく必要のあるコンストラクター、及び、アプリケーション終了時
に呼び出す、デストラクター関係である。
※通常組み込みでは、アプリケーションは終了しないので、デストラクターは呼び出す必要
が無い、もちろん、複数のアプリケーションを動的に呼び出すような機構を備えたシステム
では、デストラクターを呼び出す事は、必須であるけど。

  .data : {
    . = ALIGN(4);
    PROVIDE (__datastart = .); /* IF_ROROM */
    PROVIDE (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE (__preinit_array_end = .);
    PROVIDE (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array))
    PROVIDE (__init_array_end = .);
    PROVIDE (__fini_array_start = .);
    KEEP (*(.fini_array))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE (__fini_array_end = .);
    LONG(0); /* Sentinel.  */

    /* gcc uses crtbegin.o to find the start of the constructors, so
       we make sure it is first.  Because this is a wildcard, it
       doesn't matter if the user does not actually link against
       crtbegin.o; the linker won't look for a file to match a
       wildcard.  The wildcard also means that it doesn't matter which
       directory crtbegin.o is in.  */
    KEEP (*crtbegin*.o(.ctors))

    /* We don't want to include the .ctor section from from the
       crtend.o file until after the sorted ctors.  The .ctor section
       from the crtend file contains the end of ctors marker and it
       must be last */
    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))

    KEEP (*crtbegin*.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
    KEEP (*(.jcr))
    *(.data.rel.ro.local) *(.data.rel.ro*)
    *(.dynamic)

    *(.data D .data.* .gnu.linkonce.d.*)
    KEEP (*(.gnu.linkonce.d.*personality*))
    SORT(CONSTRUCTORS)
    *(.data1)
    *(.got.plt) *(.got)

    /* We want the small data sections together, so single-instruction offsets
       can access them all, and initialized data all before uninitialized, so
       we can shorten the on-disk segment size.  */
    . = ALIGN(4);
    *(.sdata .sdata.* .gnu.linkonce.s.* D_2 D_1)

    . = ALIGN(4);
    _edata = .;
    PROVIDE (edata = .);
    PROVIDE (__dataend = .);
  } > RAM AT> ROM

  /* Note that __romdatacopysize may be ZERO for the simulator, which
     knows how to intialize RAM directly.  It should ONLY be used for
     copying data from ROM to RAM; if you need to know the size of the
     data section, subtract the end symbol from the start symbol.  */
  /* Note that crt0 assumes this is a multiple of four; all the
     start/stop symbols are also assumed long-aligned.  */
  PROVIDE (__romdatacopysize = SIZEOF(.data));

(5) このセクションは、変数などの領域、通常、起動時に「0」クリアしておく。

  .bss : {
    . = ALIGN(4);
    PROVIDE (__bssstart = .);
    *(.dynbss)
    *(.sbss .sbss.*)
    *(.bss B B_2 B_1 .bss.* .gnu.linkonce.b.*)
    . = ALIGN(4);
    *(COMMON)
    . = ALIGN(4);
    PROVIDE (__bssend = .);
    _end = .;
    PROVIDE (end = .);
  } > RAM
  PROVIDE (__bsssize = SIZEOF(.bss) / 4);

(6) このセクションは、スタック領域
RX マイコンの場合、スタックは2つあり、ハードウェアー依存のスタック(主に、割り込み
発生の場合などで、ステータスやレジスターを退避する)と、ユーザー領域依存のスタックが
ある。

  .stack (ORIGIN (STACK)) :
  {
    PROVIDE (__stack = .);
    *(.stack)
  }

(7) このセクションは、ハードウェアー依存のベクターテーブル専用領域。
※RX マイコンでは、「リセット」信号がアサートされると、「_start」から起動する。
※他に、ハードウェアー依存の例外が発生した場合に起動するベクターを記述する。

  /* Providing one of these symbols in your code is sufficient to have
     it linked in to the fixed vector table.  */

  PROVIDE (__rx_priviledged_exception_handler = 0x00000000);
  PROVIDE (__rx_access_exception_handler = 0x00000000);
  PROVIDE (__rx_undefined_exception_handler = 0x00000000);
  PROVIDE (__rx_floating_exception_handler = 0x00000000);
  PROVIDE (__rx_nonmaskable_exception_handler = 0x00000000);

  .vectors (0xFFFFFFD0) :
  {
    PROVIDE (__vectors = .);
    LONG (__rx_priviledged_exception_handler);
    LONG (__rx_access_exception_handler);
    LONG (0);
    LONG (__rx_undefined_exception_handler);
    LONG (0);
    LONG (__rx_floating_exception_handler);
    LONG (0);
    LONG (0);
    LONG (0);
    LONG (0);
    LONG (__rx_nonmaskable_exception_handler);
    LONG (_start);
  }

・それでは、「_start」関数から、「main」までにどのような手順を踏んだら良いのか?
同じディレクトリーに、「crt0.o」がありこのコードが基準となる。
このオブジェクトは、既にバイナリーになっているので、objdump などで、逆にアセン
ブリコードに戻す事で、内容を確認出来る。

  rx-elf-objdump -S crt0.o

crt0.o:     file format elf32-rx-le

Disassembly of section P:

00000000 <_start>:
	.text

	.global _start
_start:
.LFB2:
	mvtc	#0, psw
   0:	fd 77 00 00                   	mvtc	#0, psw
	/* Enable the DN bit - this should have been done for us by
           the CPU reset, but it is best to make sure for ourselves.  */	
	mvtc    #0x100, fpsw
   4:	fd 7b 03 00 01                	mvtc	#256, fpsw
   9:	fb 02 00 00 00 00             	mov.l	#0, r0
	mov	#__stack, r0
   f:	fd 73 0c 00 00 00 00          	mvtc	#0, intb
	mvtc	#__vectors, intb
  16:	fb 12 00 00 00 00             	mov.l	#0, r1

	mov	#__datastart, r1
  1c:	fb 22 00 00 00 00             	mov.l	#0, r2
	mov	#__romdatastart, r2
  22:	fb 32 00 00 00 00             	mov.l	#0, r3
	mov	#__romdatacopysize, r3
	smovf
  28:	7f 8f                         	smovf
  2a:	fb 12 00 00 00 00             	mov.l	#0, r1

	mov	#__bssstart, r1
	mov	#0, r2
  30:	66 02                         	mov.l	#0, r2
  32:	fb 32 00 00 00 00             	mov.l	#0, r3
	mov	#__bsssize, r3
	sstr.l
  38:	7f 8a                         	sstr.l
  3a:	fb d2 00 00 00 00             	mov.l	#0, r13
	/* Initialise the constant data pointer and small data pointers.  */
	mov	#__pid_base, r13
	mov	#__gp, r12
#else
	/* Initialise the small data area pointer.  */
	mov	#__gp, r13
  40:	05 00 00 00                   	bsr.a	40 <_start+0x40>
	mov	# _start, r1
	mov	# _etext, r2
	bsr.a	__monstartup
#endif

	mov	#0, r1 /* argc */
  44:	66 01                         	mov.l	#0, r1
	mov	#0, r2 /* argv */
  46:	66 02                         	mov.l	#0, r2
	mov	#0, r3 /* envv */
  48:	66 03                         	mov.l	#0, r3
  4a:	05 00 00 00                   	bsr.a	4a <_start+0x4a>
	bsr.a	_main
  4e:	05 00 00 00                   	bsr.a	4e <_start+0x4e>

00000052 <_rx_run_preinit_array>:
        mov      r1, r13       ; Save return code.
	bsr.a	__mcleanup
        mov     r13, r1
#endif

	bsr.a	_exit
  52:	fb 12 00 00 00 00             	mov.l	#0, r1

	.global	_rx_run_preinit_array
	.type	_rx_run_preinit_array,@function
_rx_run_preinit_array:
	mov	#__preinit_array_start,r1
  58:	fb 22 00 00 00 00             	mov.l	#0, r2
	mov	#__preinit_array_end,r2
  5e:	04 25 00 00                   	bra.a	83 <_rx_run_inilist>

00000062 <_rx_run_init_array>:
	bra.a	_rx_run_inilist
  62:	fb 12 00 00 00 00             	mov.l	#0, r1

	.global	_rx_run_init_array
	.type	_rx_run_init_array,@function
_rx_run_init_array:
	mov	#__init_array_start,r1
  68:	fb 22 00 00 00 00             	mov.l	#0, r2
	mov	#__init_array_end,r2
	mov	#4, r3
  6e:	66 43                         	mov.l	#4, r3
  70:	04 13 00 00                   	bra.a	83 <_rx_run_inilist>

00000074 <_rx_run_fini_array>:
	bra.a	_rx_run_inilist
  74:	fb 22 00 00 00 00             	mov.l	#0, r2

	.global	_rx_run_fini_array
	.type	_rx_run_fini_array,@function
_rx_run_fini_array:
	mov	#__fini_array_start,r2
  7a:	fb 12 00 00 00 00             	mov.l	#0, r1
	mov	#__fini_array_end,r1
	mov	#-4, r3
  80:	fb 36 fc                      	mov.l	#-4, r3

00000083 <_rx_run_inilist>:
	/* fall through */

_rx_run_inilist:
next_inilist:
	cmp	r1,r2
  83:	47 12                         	cmp	r1, r2
  85:	20 17                         	beq.b	9c 
	beq.b	done_inilist
	mov.l	[r1],r4
  87:	ec 14                         	mov.l	[r1], r4
	cmp	#-1, r4
  89:	75 04 ff                      	cmp	#-1, r4
  8c:	20 0c                         	beq.b	98 
	beq.b	skip_inilist
	cmp	#0, r4
  8e:	61 04                         	cmp	#0, r4
  90:	20 08                         	beq.b	98 
	beq.b	skip_inilist
	pushm	r1-r3
  92:	6e 13                         	pushm	r1-r3
	jsr	r4
  94:	7f 14                         	jsr	r4
	popm	r1-r3
  96:	6f 13                         	popm	r1-r3

00000098 :
skip_inilist:
	add	r3,r1
  98:	4b 31                         	add	r3, r1
  9a:	2e e9                         	bra.b	83 <_rx_run_inilist>

0000009c :
	bra.b	next_inilist
done_inilist:
	rts
  9c:	02                            	rts

Disassembly of section .fini:

00000000 <__rx_fini>:
	.text

	.global _start
_start:
.LFB2:
	mvtc	#0, psw
   0:	05 00 00 00                   	bsr.a	0 <__rx_fini>

00000003 :
	...

ここから、必要な部分をコピーして、また、自分のシステムに必要な部分を追加して、スタートアップ
ルーチンを構築するのが良いと思う。

で、RX63T(RAM: 8KB, ROM: 64KB) 用のリンカースクリプトと、スタートアップルーチンを作成してみた。

RX63T 用リンカースクリプト
スタートアップルーチン「start.s」
初期化関数「init.c」
※自分の実装では、「スタートアップ」は、「start.s」と「init.c」に分けている。

RXマイコン用、フラッシュ書き込みプログラム(RX63T)

先日、gcc-4.9.3 を使って、R8C の開発環境を整えた。

RXマイコンにおいても、gcc-4.9.3 で、構築でき、そのコンパイラでビルド
したプロジェクトも問題無く動作する事を確認した。

以前に R8C 用のフラッシュ書き込みプログラムを作成して、もはや、ルネサス
のツールを一切利用する事なく、オープンソースだけで開発出来るようになった。
※Mac-Book でも、R8C マイコンの開発が可能となった。

RXマイコン用のフラッシュ書き込みツールも、ブート時のプロトコルが公開さ
れている為、実装は可能だと思っていた。
そこで、RX63T のハードウェアーマニュアルに記載されたプロトコルを理解しな
がら、少しづつ、機能を実装して、書き込みまで確認出来たので、とりあえず公開
する。
同じルネサスでも、R8C とは、構成が違うので、基本的な部分以外は書き直しとな
った。
※RX63T の仕様を元にしているが、他のRXシリーズにも、そのまま、又は少しの
修正で使えると思う。

いつものように GitHub に公開している。
※R8C のソースと重複するソースコードが含まれているが、多少更新している。
そのうち、重複する部分は、別にまとめておきたいと思う。

-----
まず参考にした資料は、「RX63Tグループ ユーザーズマニュアル ハードウェア編」
で、ルネサスからダウンロードできる。

細かい仕様は、マニュアル「40.8 ブートモード」に詳細に書かれているので、それを
理解すれば、誰でも実装する事が出来る。
16ビットや32ビットの値を送る場合や、読み込む場合は、ビッグ・エンディアン
になっているようだ。
※これは、マニュアルには書かれていなかった。

現在使っている、デバイスは、RX63Tの64ピン版で、USBを持たないので、
シリアル通信に限定している。

また、コードプロテクト機能を使わない場合でも、フラッシュ書き込みモード(P/E)
に移行すると、内部フラッシュは自動で消去されてしまう為、内部フラッシュの状態を
読み出す必要がある場合は、必ず、プロテクトIDを設定する必要があるようだ。

ソースコードをマルチプラットホームにしたいので、RS232C の入出力は、「termios.h」
を使って実現しているので、MSYS2 環境でコンパイルする必要があり、また、boost を
使っている為、「/usr/local/」以下に boost のソースを配置しておく必要がある。
※ライブラリーを使っていないのでヘッダーだけあれば良い。
MacOS や Linux でもコンパイルする事が出来ると思う。
※現状では「termios.h」は、MSYS2 環境にのみ存在する。
※ MSYS2 環境では、boost はインストーラーで管理していないみたい。
※ボーレートの設定範囲は、環境に依存する様なので、どの環境でもサポートされている
ボーレートのみを使っている為、上限は、115200 となっている。

IMG_0782

フラッシュへの書き込みは、必ず256バイトのブロック毎に行う仕様で、ベリファイの
コマンドは無いので、比較を行う場合は、P/E モード中に読み出して、比較する必要がある。

コードプロテクト行った場合は、フラッシュの消去は自動では無いので、書き込む前に消去
を行う必要がある。

まだ、未完成ではあるが、とりあえず、RX63T に、書き込めるようになったので、とりあえず
公開しておく。

R8C 用コンパイラの構築 gcc-4.9.3

Windows の開発環境で、MSYS2 がメインになってから、
長い間、最新の gcc で m32c(R8C)のコンパイラの構築に失敗していた。
コンパイラの構築には時間がかかる為、オプションの影響などもあまり正確に認識していな
かった。
休みに入ったので、コンパイラと格闘してみた結果、
gcc-5.2.0 で、m32c 用 gcc の構築に成功したので、「要点」を書いておこうと思う。
gcc-5.2.0 の構築は出来たものの、バイナリーを作成してデバイスに書き込んでも、正常に動作しなかったので、
とりあえず、動作が確認されているバージョン(gcc-4.8.3、gcc-4.9.3)を使う。

※「gcc-4.7.4」は現状では、MSYS2 のカレントのコンパイラが 4.9 系では、正常にコンパイル
できない~
いつもながら、クロス gcc の構築には、苦労する・・・

Windows、OS-X で試したので、多分再現性は高いと思う。

Windows では、MSYS2 環境で行い、OS-X では、macport で行う。
基本的に、MSYS2 環境の方が、多少ハードルが高いけど、MSYS2 は、ツールのインストール
が pacman で出来るので、以前より簡単となっている。

まず、MSYS2をインストールする。
※詳しいインストールの方法は、ネットで探すと沢山出てくるので、参考にされたい。
※MSYS2には、3つの実行環境があり、今回は、「msys2_shell.bat」環境で行う。
・まず、システムをアップデート

% update-core

完了したら、ターミナルをクローズして、再度ターミナルを開き、残りを更新。

% pacman -Su

次に、ビルドに必要なツール類をインストールする。(MSYS2)

% pacman -S gcc
% pacman -S texinfo
% pacman -S mpc-devel
% pacman -S diffutils
% pacman -S automake

※他にもあるかもしれないので、適宜インストールする。
※もし、何かツールが無くてコンパイルに失敗したら、ビルドディレクトリーを消して、
「configure」からやり直すのが「確実」だと思う。

・ソースコードのアーカイブを取っておく、今回は以下の組み合わせで行った。
※「gcc-4.8.3」を使う場合は、「4.9.3」を「4.8.3」に読み替えて行う。

    binutils-2.25.1.tar.gz
    gcc-4.9.3.tar.gz
    newlib-2.2.0.tar.gz

・binutils-2.25.1 の構築

% cd
% tar xfvz binutils-2.25.1.tar.gz
% cd binutils-2.25.1
% mkdir m32c_build
% cd m32c_build
% ../configure --target=m32c-elf --prefix=/usr/local/m32c-elf --disable-nls
% make
% make install

・「/usr/local/m32c-elf/bin」へPATHを通して、シェルを起動しなおす。

・C コンパイラの構築
※少し古い gcc のソースコードでは、MSYS2 環境を認識する定義が無い為、「automake-1.15」の
設定をコピーする。(その時の最新をコピーすれば良いと思われる、今回は 1.15 を使った)

% cd
% tar xfvz gcc-4.9.3.tar.gz
% cd gcc-4.9.3
% cp /usr/share/automake-1.15/config.guess .
% mkdir m32c_build
% cd m32c_build
% ../configure --prefix=/usr/local/m32c-elf --target=m32c-elf --enable-languages=c --disable-libssp --with-newlib --disable-nls --disable-threads --disable-libgomp --disable-libmudflap --disable-libstdcxx-pch --disable-multilib --disable-bootstrap
% make
% make install

・newlib-2.2.0 の構築

% cd
% tar xfvz newlib-2.2.0.tar.gz
% cd newlib-2.2.0
% mkdir m32c_build
% cd m32c_build
% ../configure --target=m32c-elf --prefix=/usr/local/m32c-elf
% make
% make install

・C++ コンパイラの構築

% cd
% cd gcc-4.9.3
% cd m32c_build
% ../configure --prefix=/usr/local/m32c-elf --target=m32c-elf --enable-languages=c,c++ --disable-libssp --with-newlib --disable-nls --disable-threads --disable-libgomp --disable-libmudflap --disable-libstdcxx-pch --disable-multilib --disable-bootstrap
% make
% make install

エラー無く終了すれば、完了!

r8cprog の構築も MSYS2 で出来るので、これでやっと R8C の開発環境が全て MSYS2 で完結する~

一応、I2C_RTC_test をビルドして、M120 に書き込んでテストしてみた。
※問題なく動作しているようである。

※「configure」で、「m32c」を「rx」に変更すれば、RX マイコン用 gcc が構築出来る。
※ rx マイコン用 gcc の構築では、4.9.3 を使う。(4.8.3 は C コンパイラの構築中エラーで止まる)

参考リンク:
最低限のクロスコンパイラの作り方
MSYS2における正しいパッケージの更新方法

R8C RTC に DS3231 を追加

最近、アマゾンで、中華の Arduino 用モジュールが色々入手出来る。
※かなり前からかな・・

これらのモジュール基板、多くの種類があり、値段がもの凄く安い!
※品質はそれなりかもしれないが、この値段なら十分だと思える。
※部品単体で買っても、こんなに安くは買えない。

で、気になった「基板」をいくつか買ってみた~
※RTC、ジャイロ・加速度センサー、リチウムイオン充電モジュールなど

・DS3231(RTC)
このモジュールは、マキシム社のRTCで、I2Cインターフェースを備えている。
温度補償水晶発振器(TCXO)および水晶を内蔵していて、超高精度。

このモジュールには、32KのEEPROM、リチウムイオン電池(CR2032)による
バックアップ付きで、電池まで付いている。

これで、たったの、200円
※degi-key で調べると、デバイス単体で890円

ありえない値段でビックリ!
※2個買ったけど、どちらも機能するようだ。

とりあえず、以前にDS1371でRTCのテストプロジェクトを作成したので、DS3231用
クラスを実装してみた。
※しょうもないバグで、数時間悩んだけど・・

内臓レジスターは、BCDになっており、非常に面倒な仕様となっている。
クラスでは、レジスターを読み出してから、time_t に変換している。
逆にレジスターに書き込むには、time_t から、tm 形式に変換してから行う。
※そうしないと、多くの場合、不便極まりないので・・・
本当は、こんな「クソ」仕様のRTCは使いたくないのだが、安いので仕方無い・・・
※この仕様のRTCは「うるう年」を厳密には正しく扱えない、まぁ今は既に2015年なので、
2099年までの範囲なら、まぁ問題無いか・・

common/time.c は、タイムゾーンを含めた、時間関係で最低限必要な関数を実装してある。
通常は、libc に含まれるが、R8C で、time 関係のライブラリーをリンクすると、非常に大きな
容量となるので、独自に実装した関数郡を用意してある。
標準の「time.h」をインクルードすると、「当たる」ので注意が必要。

// 対象の RTC を有効にする
// #define DS1371
#define DS3231

※「main.cpp」で、どちらかを有効にする事で、RTCのタイプを切り替えできる。

IMG_0781s

I2C_RTC_test

100円で買える R8C の C++ と開発環境

この記事は C++ Advent Calendar 2015 の 15日目の記事です.
前の日は amedama41 さんの「io_service の使い方」でした.

R8C/M120AN,R8C/M110AN の紹介

このマイコンは、秋月電子などで、1個100円で売られているマイコンで、それぞれ、DIP20
DIP14パッケージになっています。

※DIP20、DIP14とは、以下のような物で、電子工作では、ユニバーサル基板に組んだり、
ブレッドボードに組んだりできるので、重宝するパッケージです。
(昔ながらのパッケージです)
IMG_0777s

このマイコンに注目するのは、大きな理由があります。
カタログスペックでは、ProgramFlash:2キロバイト(メインプログラムを格納する領域)、
RAM:256バイト(スタックやワーク領域)、でも実際には・・

ProgramFlash:64キロバイト、RAM:1280バイト
の容量を持っていて、制限なく使う事ができます。

このくらいあると、プログラムの幅が広がり、C++ でプログラミングして動作させるのにも
問題ありません。
※RAM が1280バイトでは、記憶割り当てが使えない為、大きな制限がある事はあるので
すが・・・
通常マイコンには、様々なバリエーションのデバイスがあり、基本的には、Flash、RAM、内部
機能の違いで、付加価値を変えており販売価格が違います。
しかしながら、内部のシリコンを個別に設計するのは莫大なコストがかかる為、多くは共通に
なっているものと思います。
通常では、デバイスの許す領域以外はアクセスしても、無効なエリアとなるような仕組みと専
用ヒューズが用意されている
ものと思いますが、このデバイスは安価なバリエーションに属するデバイスで、ツール側の対
応で、行う方針だったように推測します。
Flash へプログラムを書き込む場合、専用の書き込みツールを使う為、通常は、デバイスの範
囲を超えて書き込む事はできませんが、書き込みソフトも実装したので、全ての容量を使う事
が出来ます。
※書き込みツールは Mac や Linux(試していませんが・・)でも使えるはずです。

始め、この話を聞いた時、そんな「旨い」話があるはず無いとも思っていましたが、実際に試
したら、使えたので、小躍りした思い出がありますww
※最初は32Kまで(0x8000 ~ 0xffff)確認できましたが、その後の調査で、もう32Kあ
る事が発覚しました。(0x10000 ~ 0x17fff)
※R8C は基本、8ビットのマイコンで、基本的に64Kバイトを超える領域にアクセスするに
は、特別な方法が必要ですが割と簡潔な方法で、拡張されたエリアを使う事ができます。

開発環境の準備


ルネサスエレクトロニクスが、提供する、IDE(無料版には制限があります)を使う事も出来ますが、
Windows だけなので、自分で開発環境を用意します。
と、言っても、gcc などのソースコードを取得してコンパイルするだけです。
R8C マイコンは、M32C マイコンのサブセットで、M32C のコンパイラを構築して、デバイスオプ
ションで切り替えます。
gcc の構築については、以下のリンクを参照下さい。
・m32c gcc の構築
※このバージョンで構築した gcc は、4.7.4 ですが、「-std=c++0x」でほぼ C++11 でプログラ
ム出来ます。

※ m32c のクロスコンパイラは、メンテナンスされていない為、最新の gcc ではコンパイルに失
敗するようです。

※ 時間とエンジニアリングがあれば、最新の gcc や、clang でコンパイル可能な物も作ってみた
いです。

※gcc-4.9.3 を使えます。

プログラムの書き込み

組み込みマイコンが、「組み込み」たる所以は、「プログラムを書き込んで、単独で動作する事」
です。
このマイコンには、モード端子があり、それを切り替える事で、内臓フラッシュへの書き込みモー
ドと、単独でブートするモードがあります。
内臓フラッシュの書き込みでは、データを入出力する為の簡単なプログラムが内臓されており、シ
リアル接続を使って、簡単なプロトコルを使い内臓フラッシュへデータを書き込めるようになって
います。

以下は、代表的な USB シリアル変換回路の回路図です。
SerialModule
※USB シリアル変換は、安価に入手出来るので、自分で作る必要はありませんが、上記の回路なら、500円くらいで作れます。

R8C のフラッシュライター(参考回路)
R8C_Writer

USB シリアル変換と、ゼロプレッシャーソケットを組み合わせた書き込み機
IMG_0744

デバイスのプログラムは、通常、開発基板のソケットからデバイスを外して、ライターの
ソケットにセットして、プログラムを書き込み、ライターから、開発基板にデバイスを戻
す。
などの、サイクルで行うのですが、プログラムを変更する度に、ソケットから外して、戻
してだと非常に面倒です、そこで、開発基板にシリアル入出力用のコネクター(写真では
6ピン)を付けておき、スイッチ(白い小さなスライドスイッチ)で切り替えて、基板に
デバイスを乗せた状態でプログラム出来るように工夫してあります。
IMG_0779s

書き込みプログラムは、一応、Windows、Mac でコンパイルできるように実装してあります。
r8cprog
※Windows でコンパイルする為には、termios.h が必要です。(特定の環境に付属します)

r8c_prog -d R5F2M120 -s 57600 -e -w -v led_test.mot

この例では、「led_test.mot」ファイルを「R5F2M120」へ書き込んでいます。
その際、「-e」でイレース、「-w」で書き込み、「-v」でベリファイ、のオプションを
追加しています。
マイコンのフラッシュプログラムは、他にみ色々な機能がありますので、コマンドのヘルプ
などを参考にして下さい。
Windows 版の GUI 付き書き込みプログラムが、千秋ゼミで公開されています。
※登録が必要です。

I/O定義の構築

メーカーがサンプルで提供している、I/O定義ファイルは、非常にチープで、解りにくい代物
です。
そこで、R8C/M11Aグループ、R8C/M12Aグループ データシートに記述されているレジスター名に一致して、扱い易いようI/O定義
を C++ のテンプレートで作成しました。
このテンプレートは、以前にRXマイコンで養ったテクニックを盛り込んだ物ですが、まぁ60
点くらいの出来だと思います。
R8C I/O 定義ヘッダー集
GitHub に上げてあります。
組み込みマイコンで C++ ぽぃ書き方が出来ますww

メーカーがサンプルなどで提供するI/O定義は、C言語向けのもので、特定のコンパイラでしか
正しく機能しない物が多く、判りにくく、エラーに対する許容度が低く使う気になりませんでした。

ネットには、同じような組み込みマイコン向けのI/O定義をテンプレート化するコードはありま
したが、色々調べると、機能的に満足出来ない部分もあり、結局、学習も含めて自分で実装する事
にしました。
開発過程で、コンパイルされたアセンブリコードを眺めて、ある程度許容されたコードが出るよう
に工夫しました。
また、最適化無しでも、最適化しても、動作するコードが出るように工夫してあります。

たとえば、P1ポート・レジスターのB2を「1」にする場合は

    P1.B2 = 1;

と書けます。

読み出す場合は

    bool value = P1.B2();

とします。
「()」オペレーターを使って、「読み出し動作」を明確に分けています。
こうしないと、最適化した場合と、しない場合で、問題のあるコードがでてしまう場合があります。

他に、「enum class」でポートの機能切り替えを定義しておき、機能を切り替えるようにしてみま
した。

#include "common/port_map.hpp"

    utils::PORT_MAP(utils::port_map::P10::AN0);
    utils::PORT_MAP(utils::port_map::P11::AN1);
↑ P10、P11 をアナログ入力 AN0、AN1 に指定

    utils::PORT_MAP(utils::port_map::P12::TRCIOB);
    utils::PORT_MAP(utils::port_map::P13::TRCIOC);
↑ P12 を TRCIOB、P13 を TRCIOC に設定

デバイス操作クラス

組み込みマイコンには、色々なハードウェアーリソースがあり、これらを簡潔に使う為に、小さなク
ラスライブラリーを提供しています。
※C++ のテンプレートは、このような用途に最適で、非常に柔軟性のある判り易い実装ができ、最適
化にも有利です。
R8C/M120/M110 では以下のような機能があります。

(1) 入出力ポート:17本 (LED 駆動用ポート含む)
(2) 外部割り込み入力:8本
(3) 16ビット多機能タイマ(タイマ RJ2) ---> trj_io
(4) 8ビットプリスケーラ付8ビット多機能タイマ(タイマRB2 ):1 ---> trj_io
(5) 16ビットインプットキャプチャ/アウトプットコンペアタイマ(タイマRC ):1 ---> trc_io
(6) UART/クロック同期形シリアルインタフェース:1チャネル ---> uart_io
(7) 10ビットA/Dコンバータ:6チャネル ---> adc_io
(8) コンパレータ:2回路 ---> comp_io
(9) ウォッチドッグタイマ
(10) クロック発生回路:XINクロック発振回路、オンチップオシレータ(高速/低速)
(11) データフラッシュ:2KB ---> eeprom_io

他に、I2C インターフェースをソフトでエミュレーションするクラス。
特定のデバイス向けクラスなど色々実装してあります。
※GitHub 参照

拡張領域の利用法

リンクローダーに与えるローダースクリプトに、0x10000 ~ 0x17fff までのエリア
(セクション)を宣言しておき、プログラムソースで、

__attribute__ ((section (".exttext")))

※この場合、拡張エリアは「exttext」としてセクションを定義してあります。
セクションを切り替え、コードが格納される場所を分散すれば、後はコンパイラが
64K を超える領域への呼び出しを、R8C が持つ、20 ビットアドレスが可能な命令
に置き換えてくれます。

M120AN/m120an.ld  --->  リンカースクリプト
ADC_test/main.cpp  --->  main 関数が、exttext エリアに指定

アセンブリコード:
    80a2:	fd 00 00 01 	jsr.a 10000 <_main>

Disassembly of section .exttext:

00010000 <_main>:
   10000:	7c f2 d1    	enter #0xd1
   10003:	7e 9f 98 00 	bset:g 0,0x13

サンプルプログラム

組み込みマイコンで、良く使われる機能に絞って、色々サンプルプロジェクトを開発しました。

ADC_test/          A/D コンバーターのテスト
COMP_test/         コンパレーター入力のテスト
ENCODER_test/      エンコーダースイッチを使うテスト
FLASH_test/        内臓データ EEPROM を操作するテスト
I2C_EEPROM_test/   I2C 接続の EEPROM を操作するテスト
I2C_RTC_test/      I2C 接続の RTC を読み書きするテスト
LCD_test/          LCD(Bitmap Graphics)を描画するテスト
LED_test/          LED の点滅テスト(よく言う「Lチカ」)
PLUSE_INP_test/    パルス入力周期を測定するテスト
PLUSE_OUT_LCD/     任意のパルス出力を行うテスト(エンコーダー入力、LCD 表示)
PLUSE_OUT_test/    任意のパルス出力を行うテスト
PSG_test/          ※開発中
PWM_test/          PWM 出力を行うテスト
RC_SERVO_test/     ラジコン用サーボを動作させるテスト
RGB_LED_test/      ※開発中
SD_monitor/        SDカードの読み書きモニター
SD_test/           簡単なSDカードの読み書きテスト
SD_WAV_play/       SDカードにあるWAVファイルを再生するテスト(PWM信号としてアナログ的に出力)
SWITCH_test/       スイッチ入力テスト
TIMER_test/        タイマー機能のテスト
UART_test/         UART を使った、シリアル入出力テスト

各プロジェクトには、Makefile があり、

make

とすれば、コンパイルが行われます、その際、従属規則も同時に生成するようにし
ています。
プログラムを書き込み際には

make run

とします。
※「Makefile」参照

まとめ

小さくて、機能豊富なマイコンなら、アイデア次第で、色々な「オリジナル電子小物」を作る事が
でき楽しいものです~
上記のクラスライブラリーを使えば、最低限の手間と理解で、簡潔にやりたい事が出来ると思いま
す。
興味があったら、何か作ってみてはいかがですか?
質問等あったら気軽にメールでも下さい。

IMG_0775s

R8Cを使ってRCサーボを動かす

ホビーでよく使うアイテムとして、ラジコン用のサーボモーターがある。

R8C/M120AN で動かしてみたので、簡単に、解説する。

まず、テスト用にアマゾンで、一番安いサーボモーター買ってみた。
IMG_0768s

HKSCM9-6 RCサーボ(ホビーキング製)
コネクタタイプ:JR
GND:茶
+V: 赤
信号: 橙
最大定格: 6V

※このサーボは意外と小さくて、そんなに大きいトルクを出せないが、テスト用に丁度良い。
※もちろん、他のサーボでもかまわない。
※フタバタイプでは、微妙に信号の仕様が異なるので、調整が必要。
※R8Cを5Vで駆動

ラジコン用サーボを動かす信号は、メーカーにより微妙に異なるが、ほぼ同一で、PWM で、
パルス幅と角度が比例関係にあり、JR系では、

周期: 50Hz(20ms)
ニュートラル: 1500uS(マイクロ秒)
※フタバタイプでは、1520uS(マイクロ秒)となっている。
可動範囲: +-600uS、900uS~2100uS(マイクロ秒)

となっているようだが、周期については、ある程度許容範囲があるようだ、当然ながら、
周期を短くした方が、応答は良くなるものと思われる。

・R8CのCチャネルで、20MHzを8分周すると、50000カウントで、20mSで、
50Hzとなり、16ビットの範囲に収まる。

    bool pfl = 0;  // 0->1
    uint8_t ir_level = 2;
    timer_c_.start_pwm(50000, trc_type::divide::f8, pfl, ir_level);

・ニュートラルは、1500uSなので、3750となる。
・可動範囲は+-600uSなので、+-1500となる。
・PWM出力をP1_2(18):TRCIOB、P1_3(17)TRCIOCにして、
2個のRCサーボを別々に動かす。
※TRCIODを使えば、3個まで駆動できる。

サンプルプログラムでは、AN0、AN1でA/D変換した結果(0~1023)を、使って
サーボの可動範囲±1500(±600uS)になるようにしている。

IMG_0769s

プロジェクトのソースコード