hira のすべての投稿

立川から茂木に行くルート

依然から良く茂木に行きますが、「有料道路を一切使わない」とか、色々「縛り」を設けて、
冗長な車の運転に少しでも面白みを見出してますwww

その過程で見つけた色々な事を記しておきます。

まず、一般的な標準ルートは・・

立川
大泉インター
外観
三郷
常磐道
水戸北スマート
茂木北ゲート、又は南ゲート

良い点:
・空いていれば時間が意外とかからない(2.5時間~3時間弱)
問題点:
・距離が長い
・帰りは、三郷の手前とかで渋滞する
・高速料金
・大泉から立川間の一般道(新青梅街道の流れの悪さ)
・レストラン倉井に寄れない

全て下道で行くと、どうなるか?
過去に、色々なルートを試してみました。
そこで、淘汰され残ったルートで、最も合理的で最短時間なのが以下のルートです。

立川
川越(国道16号)川越市内を適当に・・
県道12号
県道370号
県道151号
県道370号
県道346号
県道60号
県道46号
国道354号(川越市内からここまで、ほぼ道なりです)
県道9号
県道11号
国道50号
新4号バイパス
県道47号(真岡市内へ)
国道408号バイパス
国道121号
県道163方面へ(ファミマがある交差点を道なり)
星の宮交差点
北中交差点
大沢橋東交差点
木幡交差点
国道123号
県道51号
茂木南ゲート

※川越から国道354号までも多少複雑なようですが、ほぼ道なりです。
※国道121号から、茂木までは、複雑のようですが、ほぼ道なりで(「はがグリーンコリドール」と呼ばれる広域農道です)
良い点:
・高速料金0
・休憩ポイント、コンビニなど多く、ゆったりまったり行ける
・ほぼ直線なので、距離が短く、ガソリンも少なくて済む
問題点:
・それなりに時間がかかる(深夜帯で3.5時間~4時間弱)
・祭日の昼間など多少混む場所がある。(裏道で逃げ切る)

そこで、圏央道を有効に使い、少しだけ短縮すると・・

立川
国道16号
圏央道入間インター
桶川北本インター
県道12号(後同じ)

これだと、3時間弱~3時間強
標準ルートとあまり変らない!
高速料金1230円!

圏央道が繋がり(今年中に繋がるらしい)、4号バイパスまで延びれば、さらなる短縮になります~

-----
下道でお勧めポイント
レストラン倉井:
※ここは、上のルートから少し離れていますが、帰りには是非寄りたいスポットです。
※夜9時がラストオーダーなので、9時までに店に入ればセーフです。
※水曜定休
・安い!
・ホルモン定食がクセになる!
※注意:
調子にのって、ニンニクのゴマ油漬けを沢山入れると、次の日に響くかもwww
食べた夜は、ニオイが厳しく、奥さん、子供の不評を買う場合もあるようです・・
(リア充の人は、彼女と一緒に食べれば問題無し)
※倉井から国道50号に出るのは、農道を通るのがお勧め、信号無く、渋滞一切無し!
※4号線を通るのは最悪で、昼間は凄く渋滞します。

R8C PWM出力(タイマーC)

R8C関係のリソースもかなり色々揃って来ました。

組み込みマイコンでは、外せないPWM機能の実装です。

一般的に、組み込みマイコンのハードウェアータイマーの機能は、多機能で、複雑です。
R8C/M120の、タイマーCでも、PWMは二通りのモードがあり、その他に、インプットキャプチャなど
色々な機能があります。

今回は、3チャネルの出力が可能な、PWMモードの実装を行いました。
※今後必要になったら、PWMモード2や、インプットキャプチャーなど、他の設定も組み込む予定です。

PWMの場合、デューティ可変範囲を切りのよい2のn乗にしたい場合と、あくまでも周波数(周期)で設定
したい場合がありますので、引数の違う2つの設定を用意してあります。

今回、必要無かったので、割り込みを使っていません。

このタイマーCのPWM出力は、設定値とカウンターの一致を利用しているので、設定値を書き換えるタイミングに注意
する必要がありますが、現状の実装では考慮されていません。

    // PWMモード設定
    {
        PML1.P12SEL = pml1_t::P12TYPE::TRCIOB;
        PML1.P13SEL = pml1_t::P13TYPE::TRCIOC;
        PML1.P10SEL = pml1_t::P10TYPE::TRCIOD;
        bool pfl = 0;  // 0->1
        timer_c_.start_pwm(10000, pfl);
        uint16_t n = timer_c_.get_pwm_limit();
        timer_c_.set_pwm_b(n >> 2);  // 25%
        timer_c_.set_pwm_c(n - (n >> 2));  // 75%
    }

↑の例では、PWM周波数を10KHzとしています。
B出力には25%、C出力には75%のデューティで出力します。

    if(adc_.get_state()) {
        uint32_t v = adc_.get_value(1);
        v *= timer_c_.get_pwm_limit();
        timer_c_.set_pwm_d(v >> 10);
	adc_.start();
    }

又、C出力にはLEDが接続してあるので、AN1入力にボリュームを繋げて、0~100%まで
デューティを変化させられますのでLEDの明るさが変化します。

-----
I/Oポートの定義を見直し、ポートのマッピングに「固有の型」を導入しています。
その為、設定が判りやすく、誤設定も減ると思います。

        PML1.P12SEL = pml1_t::P12TYPE::TRCIOB;
        PML1.P13SEL = pml1_t::P13TYPE::TRCIOC;
        PML1.P10SEL = pml1_t::P10TYPE::TRCIOD;

※この型には「enum class」を使っている為、C++11対応コンパイラでコンパイルする必要があります。
現在 gcc-4.7.4 なので、「-std=c++0x」オプションを使っています。

PWM_test

GitHubにソースをプッシュしました。

R8Cデータフラッシュの操作

さて、今回はデータフラッシュメモリーを扱います。

R8Cには、プログラムフラッシュとは別に、データの記録用の小規模なフラッシュメモリーが別にあります。

3000番地から始まり、容量2048バイトです。
※1024バイト毎にバンク0、バンク1と分かれています。
フラッシュメモリーは、「1」のビットを「0」にする事は出来ますが、「0」のビットを「1」にする場合は、バンク毎に
イレースコマンドを発行して、全て「1」の状態に戻します。

この辺りのマネージメントが、多少難しいのですが、電源を切っても、記録を残す事が出来るので、非常に有益なメモリーと言えます。

テストプログラムでは、バイト単位での読み出し、書き込み、イレースなどが対話形式で行えるようにしてあります。
※シリアルポートにターミナルを接続します。

Start R8C FLASH monitor
# ?
erase bank[01]
r xxxx
write xxxx yy
# r 3
FF
# write 3 56
# r 3
56

操作説明:
? ---> 簡単な説明
r xxx ---> xxx のデータをリード
write xxx yy ---> xxx へ yy を書き込み
erase bank0 ---> バンク0を消去(0x3000 to 0x33ff)
erase bank1 ---> バンク1を消去(0x3400 to 0x37ff)

※xxx アドレスは、0 から 7ff までの領域です
※値は全て16進数で行います

本来は、プログラム領域の操作関数も含めたいところですが、それは、また次の機会(必要になったら)とします。

-----
R8Cへのプログラム書き込みには、ルネサスのFDTを使っていましたが、コマンドラインから気軽にプログラム出来れば便利です。
又、FDTは Windows 環境にしか対応しておらず、Linuxや、OS-X での開発には不向きでしたが、先日、AVR関係でお世話になっている
YCITのsenshuさん
の掲示板で、コマンドラインで使える、R8C 用プログラムコマンドが公開されました。
元々は、ゆきさん開発の r8cprog を元にしていて、M120AN 32K Flash に対応した
ものですが、使いやすいように、その他の小規模な改造も行っており、安定性も増しています。
私は、コマンドラインから使えるのが便利ですが、GUI 版も用意されており、シンプルで良く精査されたシンプルな設計により、予備知識が無く
ても直ぐに使えるようになっています。
Linux にも対応している事から、OS-X にも対応可能と思います、こちらは、時間が出来たら挑戦してみるつもりです。

R8C割り込みの注意点

ADCを動作検証するサンプルで、UARTとタイマーの割り込みを使うようになった。
他、色々複雑化しているのだが・・

非常に気になる事案が発生するようになった、数時間程度稼動させていると、ハングアップするのだ・・

スタックかと思い調べたが問題無い・・

全く原因が掴めないので、しばらく放置していたが、たまたま確認の為、R8Cのマニュアルを観ていたら
気になる事項が・・

5.7.3 割り込み制御レジスタの変更
(1) 割り込み制御レジスタは、そのレジスタに対応する割り込み要求が発生しない箇所で変更してく
ださい。割り込み要求が発生する可能性がある場合は、割り込みを禁止した後、割り込み制御レジ
スタを変更してください。
(2) 割り込みを禁止して割り込み制御レジスタを変更する場合、使用する命令に注意してください。
IRビット以外のビットの変更
命令の実行中に、そのレジスタに対応する割り込み要求が発生した場合、IR ビットが“1”(割り込み
要求あり)にならず、割り込みが無視されることがあります。このことが問題になる場合は、次の命令
を使用してレジスタを変更してください。
対象となる命令…A N D 、O R、BCLR、BSET
IRビットの変更
IR ビットを“0”(割り込み要求なし)にする場合、使用する命令によってはIR ビットが“0”にならな
いことがあります。IR ビットはM O V 命令を使用して“0”にしてください。

は!?、うーーーん、そうか・・・
これは、要するに、リードモディファイライト命令など、リードからライトへの遅延が短い場合に、
フラグのリセットに失敗するのだろう、しかし、わかり難いのは、常に失敗する訳では無く、何か
の条件が重なると失敗するようだ。
もしかしたらコレかもしれない、そこで、割り込みフラグのリセットを全般的に書き換えた。

※タイマーの割り込みフラグのクリア部分

以前は、
    TRBIR.TRBIF = 0;

だったのを・・

    volatile uint8_t r = TRBIR();
    TRBIR = TRBIR.TRBIF.b(false) | (r & TRBIR.TRBIE.b());

とした。
※同じように、UARTの制御クラスにも修正を加えた。

ここで、TRBIR レジスターを1度読み出しているのだが、「volatile」を指定しないと、最適化されて、
AND 命令とかでI/Oを直接書き換えるようなコードが出てしまう。

この検証には時間がかかる・・・
24時間走らせたが、今度は順調なので、多分解消されたと思う。
割り込みフラグを「消す」場合には注意が必要だ・・

ただ、コンパイルされたアセンブリコードには多少無駄な部分がある、しかしこれはコントロールが難しい
ので、我慢するか、アセンブラで書き直すしかない。
折角、可読性を重視して C++ を活用しているので、まぁ我慢しておく事にする。

元のソース
    uint8_t r = TRBIR();
    TRBIR = TRBIR.TRBIF.b(false) | (r & TRBIR.TRBIE.b());

    8266:	97 80 e7 00 	and.b:s #-128,0xe7
「volatile」修飾子を使った場合
    volatile uint8_t r = TRBIR();
    TRBIR = TRBIR.TRBIF.b(false) | (r & TRBIR.TRBIE.b());

    826b:	75 c4 e7 00 	mov.w:g #231,a0
    826f:	72 60       	mov.b:g [a0],r0l
    8271:	02 ff       	mov.b:s r0l,-1[fb]
    8273:	0a ff       	mov.b:s -1[fb],r0l
    8275:	94 80       	and.b:s #-128,r0l
    8277:	72 06       	mov.b:g r0l,[a0]

R8C A/D変換

R8CのA/D変換も出来ました。

ただ、別の部分で、工夫が必要な事が判りました。

工夫と言うのは、バイナリーサイズが肥大化しているのです・・
問題になってるのは「format」クラスです。
このクラスは、「printf」に代わるものとして実装した物ですが、16キロバイトにもなります。
フラッシュメモリーは32キロまで使えるとは言え、流石に半分も消費するのでは問題です。
RXマイコンで実装した物をほとんどそのまま使っているのですがー、RXマイコンではそこそこのサイズでしたが、R8Cでは巨大になるのです・・

動作は、設計の通りですが、小さくする工夫をする必要があります。

しばしば、小さな組み込みでは、libc の printf が巨大になるので、縮小版を作る人が多いですが、自分もその口です。
そもそも、printf は、引数がスタックベースなので、フォーマットと、引数が食い違う場合に簡単にクラッシュするか、
意図しない微妙な動きになる場合があります。
コンパイラーが整合性をチェックしますが、完全ではありません。
C++では、そんな危険を冒さなくても、安全な方法が色々あります。
「format.hpp」は、オペレーターの機能を使って、boost::format のような実装を行った物です、boost::format 版は、std::cout が必要な為、バイナリーが肥大化して使えませんので、縮小版です。

  int i = 123;
  printf("%d\n", i);

が、

  uint32_t i = 123;
  format("%d\n") % i;

のように書けます。
※R8Cでは「int」が16ビットなので、in32_t、uint32_t を使います。
※「%」オペレーターをオーバーロードしています。

調べると、現在の実装では、文字の出力や、定型サイズなどをテンプレートの引数として受け取るのですが、これが肥大化の要因のようです。
そこで、テンプレートをやめ、通常のクラスとして定義しなおし、エラーレポートもオフにしてみました、その結果、半分以下になりました。
これなら、許容範囲と思いますと言いたいのですが、もう少し何とかしないと駄目な感じです・・
R8C の gcc はメンテされていないのと、基本16ビットのCPUなので、コンパイラのコード生成に問題があるのかもしれません・・
※R8Cでは、フラッシュは64キロ以内なので、64キロを超えてジャンプする機構は必要無いのですが、コンパイル
されたマシンコードには、そのキックコードが含まれています、コンパイラのスイッチで排除出来ると思いますが、少し調べた限りでは判りませんでした・・

さて、本題はA/D変換です、R8Cに内臓されているA/Dコンバーターは10ビットの分解能で、2チャネル、アナログ入力は6チャネルあります。
変換速度は高速で、最大で2.3uSです。
アナログ用電源はピン数の制限から、デジタル電源と共通ですが、ルネサス系のCPUは電源ノイズも少なく、A/D変換の精度もAVR系より優れている感覚があります。
※AVR系ではサンプルホールドが無い為、変換結果に誤差が乗り易く、正確な変換を行うのが難しいと思えます。

format クラスには、A/D変換などの表示に適した、固定小数点表示「%y」を実装してあります。
A/D変換結果は大抵、電圧や電流などですが、表示する場合、小数点付きで10進表記で表示した方が判りやすい
ですが、通常だと実数と少数部を分けて、別々に計算をする必要があり、多少面倒です。

固定少数点表記機能を使うと、例えば、1023を5Vとすると、実数1桁、小数2桁、小数点以下8ビットの精度としてー

  uint16_t v = adc_.get_value();
  format("%1.2:8y") % static_cast(((v + 1) * 10) >> 3);

とすれば、v が1023の場合に「5.00」と表示できます。

A/D変換のサンプルコード

浮動小数点の計算は、リソースを食うし、FPUが無いとスピードも出ませんので、固定小数点の計算を簡単に行えると便利だと思います。
※何故10倍して8で割っているのか、良く考えれば判ります。

現在の「format.hpp」は「float、double」の実装がまだで、中途です、IEEE754 の表示を浮動少数点を使わずに整数計算だけで実装したいのですが、苦労しています、道まだ半ばです・・・

型指定子にどんなものが使えるか、「format.hpp」を観れば直ぐに解ると思います。
※8進数は、通常あまり使わないので、オプションとしています。

R8C RB2を使ったタイマー

R8C もようやくタイマー関係の定義が出来ました。

R8C には3つのタイマーモジュールが内臓されています。
それぞれ、RC、RJ、RB です。

AVRに比べると、複雑で、機能を使うには、マニュアルの意図を理解するのに時間がかかりますが、
高機能で、AVRと両方使って思うのは、ルネサス系のタイマーの方が、細かい部分が良くできている点です。
インプットキャプチャー機能などは、ダブルバッファになっていて、正確な値を取得できますし、PWM機能は、
多くの場面を想定していて、十分な使い方が出来ます。

この中で、比較的単純な構造なのはRBです。
単純と言っても、色々な仕組みがあるのですが、とりあえず、16ビットのタイマーとして使い、周期的に割り込みをかけるのに使う事にします。

タイマークラスでは、登録した関数を割り込み内から呼び出す仕組みを設けてありますが、通常「禁止」としています。
これを「有効」にすると、ライブラリー関数の静的変数を全てスタックへ退避するコードが追加される為、注意が必要です。
※そうしないと、メイン側で、あるライブラリー関数を実行していた場合、それを、割り込み側でも呼ぶと、問題が起こります。

周期的な割り込みを使うと、プログラム全体の時間管理が単純に行えるので便利です。
これは、同期式とも言います、ゲームなどに向いた方法で、画像の表示、書き換えを同期させ、常に一定の速度で全体を動作させます。
又、シングルタスクでありながら、マルチタスク的な並列動作を行うのに便利な方式です。
※厳密な意味での「並列」ではありませんが。

組み込みでも、「同期式」は、色々な面で、都合が良い場合が多くあります。

簡易的なマルチタスク的な動作が出来ます。

一つの例として、スイッチのチャタリング除去などが上げられます。
周期的な割り込みを行い、その周期で I/O ポートの値を読み込む事で、デジタルフィルター的な効果が付加され、結果的にチャタリングを除去
します。
この周期はスイッチの機械的特性によりますが、60Hz~100Hzくらいが適当です。
※スクリーンのリフレッシュレートが60Hzなので、その値が使われます。

当然ながら、1秒に60回以上のOn/Offを検出出来なくなりますが、通常は問題ありません。

また、個々の処理を時分割で動作するように実装して、それを周期的に呼び出す事で、並列動作が実現出来ます。

通常は、カーネルがタイムスライスを行い各スレッドを管理しますが、小さなシステムでは、マルチスレッドは複雑になりすぎる場合があり、
「同期式」でも十分な場合があります。

ゲームを作っている人には、常識的な実装ですが、初めて触れる人には新鮮で、判り難いかもしれませんが、馴れておくと良いと思います。
今まで、複雑で、難解な実装が、シンプルに出来る場合があり、モジュール的に他の処理と分離する事ができます。

ここで、今までに実装したR8C関係クラスなどをまとめておきます。

M120AN/
  m120an.ld     ---> リンカースクリプト
  system.hpp    ---> システム制御
  clock.hpp     ---> クロック発生回路
  port.hpp      ---> I/O ポート
  intr.hpp      ---> 割り込み
  uart.hpp      ---> シリアルインターフェース
  timer_rb.hpp  ---> タイマRB
  timer_rc.hpp  ---> タイマRC
  timer_rj.hpp  ---> タイマRJ

I/O制御関係では、以下のファイルがあります。

common/
  start.s        ---> R8C起動アセンブラソース
  vect.[hc]      ---> 割り込み、割り込みベクター関連
  init.c         ---> 初期化関連
  io_utils.hpp   ---> レジスター定義ユーティリティーテンプレート
  uart_io.hpp    ---> シリアルインターフェース制御クラス
  trb_io.[hc]pp  ---> タイマRB制御クラス

各デバイス毎にテストしたプロジェクトがあります。

R8C/
L_chika     ---> 基本の基本LED点滅(最小限の実装)
UART_test   ---> シリアルインターフェースのテスト(ポーリング、割り込み)
TIMER_test  ---> タイマーRBのテスト(ポーリング、割り込み)

これからも、随時追加していく予定です。

R8C UART の制御

凄い長い間、間が空いたけど、再びR8Cです。
※WR250Xを買ったせいで、休みは、出かける事も多く、体調を崩した事もあり、進んでませんでした・・

UARTですが、M120、M110には1チャネルのUARTが備わっています。
R8CではSCIと言わないのが、何となく系統の違いを感じますが、内部の機構や構成は、RXマイコンなどと殆ど同じです。
※良く調べたら、送信割り込みの機構はかなり違っていて、RXマイコンやSH用SCIのプログラムはそのままの構成では流用できませんでした。
※もしかしたら、これが「UART」と呼ぶ事なのかもしれません・・

クロックジェネレーターの構成により、あまり高いボーレートを使う場合は苦しいかもしれません。
※内部発振器は通常20MHzで発振しますが、調整機構があり、18.432MHzで発振させる事が出来るようで、ボーレートクロックに適しています。
※19.968MHzならもっと良かったのに・・

追加したファイルは、以下です。

R8C/M120AN/uart.hpp
R8C/common/uart_io.hpp
R8C/UART_test/*

現在のところ、ポーリングによる動作しか実装していませんが、そのうち割り込み動作も追加するつもりです。
R8Cは、RAMのリソースに余裕が無い為、割り込みベクターをROM側に置いておく必要があり、その為のしくみを考え中です。

M120以外のR8Cでも流用が出来るように、UART0だけに特化していません。
※今のところ、M120以外のR8Cを使う予定はありませんが・・

以前にRXマイコン用に作った、sci_io.hpp をR8C/UART 用に焼きなおしただけで、基本的な構成は似ています。

・テンプレート関数内 static 宣言
テンプレートクラスで static クラスを宣言すると、実態が必要なので、uart_io.hpp の最後の方で実体を宣言してあります。
※以前、この宣言方法が判らなくて、少し苦労しました・・

template<class UART, uint16_t recv_size, uint16_t send_size>
	utils::fifo<recv_size> uart_io<UART, recv_size, send_size>::recv_;
template<class UART, uint16_t recv_size, uint16_t send_size>
	utils::fifo<send_size> uart_io<UART, recv_size, send_size>::send_;

何故、static にする必要があるかと言えば、割り込みが関係しています。
割り込みが発生した場合、テンプレートクラスのリソースにアクセスする為には、テンプレートクラスの実体のアドレスが必要ですが、
通常の方法では、簡単に得られません(クロックを消費します)、そこで、static 変数にしておく事で、コンパイル時にアドレスが決定される為、
割り込み関数から、容易にアクセスできるのです。
static にしてありますが、テンプレート引数に依存しているので、UART チャネルが違う場合は、別のリソースとして割り当てられます。

・最適化の功名
main 関数内で、UART にボーレートを設定している関数がありますが、内部でボーレートジェネレーターに設定する値などを計算しています、
しかしこれは定数を指定している為、コンパイル時に最適化によって計算部分が最適化され、ダイレクトに定数がレジスターに設定されるような
アセンブリコードが出力されます。
※機能としてボーレートを変更できるような仕様だと、省く事が出来ない為、計算するコードが含まれる事になります。

・基本的な使い方
UART0を使うには、モジュールを有効にして、I/Oのマッピングを設定する必要があります。

    MSTCR.MSTUART = 0;  // モジュールスタンバイ制御
    PMH1E.P14SEL2 = 0;
    PMH1.P14SEL = 1;
    PMH1E.P15SEL2 = 0;
    PMH1.P15SEL = 1;

※M120の場合、I/Oポートの方向レジスターの設定は不要のようです。

M120のシリアルフラッシュライターでは、TXDのポートは、本来のTXD0とは違うポートを使っている為、フラッシュライターのハード
を使って、シリアル入力のテストを行うには、P1:B6とP1:B5をブリッジする必要があります、今回は適当な抵抗でブリッジしています。

※使い方は、main.cpp を参考にして下さい。

いつものように GitHub にプッシュしてあります。

追記:
割り込みにも対応するべく、色々やっていました。
色々なバグなども見つけて、安定度なども向上しつつあります。

リンカースクリプトに記述してあるスタックのアドレスを間違ってました・・

それを修正して、割り込みベクターの設定ハード用のテンプレートを実装したら、受信割り込みは簡単に動いたのだけど、
送信割り込みがーー、R8Cでは、送信割り込みハードの実装が微妙で、スマートな方法が使えないのです。

普通、送信割り込みはバッファが「空」になったら発生するので、ソフトとの連携も楽なのですが、R8Cではそうなっておらず、
少し苦労しましたが、何とか方法を考えて実装しました、以外と簡単な対処ですが、ロジックに誤りがあれば、普段は良くても、何か違う状況
の場合などに微妙に正しく動作しない可能性があります。
※バッファに送信データが残るとか、送られないデータが出るとか・・
割り込みを含んだロジックは、隠れて見えない部分が多いものなので、しばらく注意して観ておく必要があります。

送信割り込みで運用したロジックは以下のような物です。
・リングバッファが空で、UARTのレジスタも空なら、送る物が何も無い状態なので、直接送信レジスターにデータを書く。
・リングバッファが空では無いか、UARTのレジスタが埋まっていれば、送信中なので、リングバッファにデータをいれる。
・送信割り込みでは、リングバッファが空なら、送る物が無いので、何もしないで終了とする。
・送信割り込みでは、リングバッファに何かデータがあれば、それをUARTのレジスターに書いて終了する。

R8Cスタートアップの手順と、Lチカ

さて、R8C用 gcc が出来たのでー、早速、最初の一歩、スタートアップルーチンの実装です~

RX マイコン用 gcc で色々学習したので、今回はかなり楽です。
ただ、R8C のアセンブラは初めてなので、少し苦労しそう・・・

全体の構成としてー

・start.s ---> CPU リセット時に開始するルーチン
・init.c ---> 初期化関係
・vect.c ---> ベクターアドレス関係
・xxx.ld ---> リンカースクリプト

この4つの構成になります。


「start.s」

	.text
	.global _reset_start
_reset_start:
	.global	_start
_start:
	/* 割り込みスタック設定 */
	.extern _isp_init
	ldc #_isp_init,isp

	/* ユーザースタック設定 */
	.extern _usp_init
	fset u
	ldc #_usp_init,sp

	/* 割り込み許可 */
	fset i

	.extern _init
	jmp.w _init

	.global _exit
_exit:
	jmp.w	_exit

・「_reset_strat」、「_start」は同一アドレスに向けておきます。
※リンク時、「-nostartfiles」を指定しても、「_start」のシンボルは必要なようです。
・「_isp_init」、「_usp_init」はリンカースクリプトで指定してあります。
※それぞれ、割り込み用スタック、ユーザースタックです、プログラムの性質に合わせて、少ないメモリーのどこにどれくらい配置するか考える必要があります。


「init.c」

int main(int argc, char**argv);

extern short _datainternal;
extern short _datastart;
extern short _dataend;

extern short _bssstart;
extern short _bssend;

extern short _preinit_array_start;
extern short _preinit_array_end;
extern short _init_array_start;
extern short _init_array_end;

int init(void)
{
	// R/W-data セクションのコピー
	{
		short *src = &_datainternal;
		short *dst = &_datastart;
		while(dst < &_dataend) {
			*dst++ = *src++;
		}
	}

	// bss セクションのクリア
	{
		short *dst = &_bssstart;
		while(dst < &_bssend) {
			*dst++ = 0;
		}
	}

	// 静的コンストラクターの実行(C++ )
	{
		short *p = &_preinit_array_start;
		while(p < &_preinit_array_end) {
			void (*prog)(void) = (void *)*p++;
			(*prog)();
		}
	}
	{
		short *p = &_init_array_start;
		while(p < &_init_array_end) {
			void (*prog)(void) = (void *)*p++;
			(*prog)();
		}
	}

	// main の起動
	static int argc = 0;
	static char **argv = 0;
	int ret = main(argc, argv);

	return ret;
}

void __dso_handle(void)
{
}

初期化ルーチンでは、一連の準備をする必要があります。
・メモリーのクリア(通常、bss セクション)
・初期値が設定してあるメモリーにROMエリアからコピー
・静的コンストラクターの実行(C++ の場合のみ必要)
・最後に「main」関数の起動


「vect.c」

#include <stdlib.h>

#define INTERRUPT_FUNC __attribute__ ((interrupt))

extern void reset_start(void);

// 未定義命令
INTERRUPT_FUNC void undef_inst_(void)
{
}

// null interrupt TASK
INTERRUPT_FUNC void null_task_(void)
{
}

// R8C M110AN, M120AN の場合、ポインターは2バイト、ベクターテーブルは4バイト単位のアドレスを想定
const void* fixed_vectors_[] __attribute__ ((section (".vec"))) = {
// 0xFFD8  予約領域 (with OSF2)
	(const void*)0xffff, (const void*)0xffff,
// 0xFFDC  未定義命令 (with ID1)
    undef_inst_, (const void*)0xff00,
// 0xFFE0  オーバーフロー (with ID2)
	null_task_,  (const void*)0xff00,
// 0xFFE4  BRK 命令
	null_task_,  (const void*)0xff00,
// 0xFFE8  アドレス一致 (with ID3)
	null_task_,  (const void*)0xff00,
// 0xFFEC  シングルステップ (with ID4)
	null_task_,  (const void*)0xff00,
// 0xFFF0  ウオッチドッグタイマ、発振停止検出、電圧監視1 (with ID5)  
	null_task_,  (const void*)0xff00,
// 0xFFF4  予約 (with ID6)
	(const void*)0xffff,  (const void*)0xffff,
// 0xFFF8  予約 (with ID7)
	(const void*)0xffff,  (const void*)0xffff,
// 0xFFFC  リセット (with OFS)
	reset_start, (const void*)0xff00,
};

※R8C/M110AN、R8C/M120AN では、ポインターは2バイトです、ですが、ハードウェアーベクターは4バイト構成になっています。
※また、R8Cのアーキテクチャー的には、最大1Mバイトの空間までアクセス出来るので、ベクターは3バイトあれば十分です、最上位バイトは、別の機能(「IDコードチェック機能」、「オプション機能選択」などのレジスター)として使われています、通常0xFFにしておけば良いようです。
※間違って書き換えてしまうと、フラッシュの書き換えが出来なくなる場合があるようなので、注意が必要です


「リンカースクリプト」(抜粋)


MEMORY {
	RAM (w) : ORIGIN = 0x0300, LENGTH = 0x0480
	ROM (r) : ORIGIN = 0x8000, LENGTH = 0x7FD8
	VEC (r) : ORIGIN = 0xFFD8, LENGTH = 40
}
SECTIONS
{
        _usp_init = 0x07E0 - 2;
        _isp_init = 0x0800 - 2;

...

  .rodata : {
    . = ALIGN(2);
    *(.plt)
    KEEP (*(.init))
    KEEP (*(.fini))
    *(.rodata .rodata.* .gnu.linkonce.r.*)
    *(.rodata1)
    *(.eh_frame_hdr)
    KEEP (*(.eh_frame))
    KEEP (*(.gcc_except_table)) *(.gcc_except_table.*)
    . = ALIGN(2);
    PROVIDE(__romdatastart = .); /* IF_ROROM */
  } > ROM

  PROVIDE( __datainternal = ABSOLUTE(LOADADDR(.data)));

...

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

...

・リンカースクリプトは、通常 gcc のバージョンによって構造が異なります、今回 gcc-4.7.4 ベースの M32C-elf をビルドしましたが、
m32c-elf/m32c-elf/lib/r8c.ld に基本になるリンカースクリプトがありますので、これを修正して使っています。
※正しい書き方や、意味を完全に理解している訳では無いので、カットアンドトライ的ですが、とりあえず、現状のスクリプトで大丈夫と思います。


「main.cpp」Lチカのプログラムです。

#include "port.hpp"

int main(int argc, char *ragv[])
{
	using namespace device;

	PD1.B0 = 1;

	while(1) {
		for(int i = 0; i < 1000; ++i) P1.B0 = 0;
		for(int i = 0; i < 1000; ++i) P1.B0 = 1;
	}
}

・初期状態の内臓発振器を想定したソフトループとなっています。
・port.hpp がポートのテンプレートクラス定義です。
・Makefile により、自動で、ファイルの従属規則を生成しています。

IMG_0712
IMG_0713

とりあえず、ソースコードは、GitHub にプッシュしてありますので参考にして下さい。
※定義は I/O ポートの部分しか無いので、ちょくちょく更新、及び追加すると思います。

R8Cマイコン用 gcc (m32c-elf) の構築

さてー、M110AN、M120ANが、もの凄く使えるやつだって判ったのでー

まず、お決まりの gcc ビルドです。
色々調べると、R8Cは、M32Cマイコンのサブセットのようです、そこで、M32Cマイコン用 gcc をビルドします。
M110AN、M120AN は R8C の中でも(Tiny)と呼ばれる縮小セット版のようです。
※コンパイルオプションとして「-mcpu=r8c」などとします。

毎回、gcc のビルドは苦労します。

Windows7 MSYS(MinGW) の場合:
※ MSYS に、gcc ビルドに必要な、「gmp、mpfr、mpc」がインストールしてある事を前提とします。

・gmp-5.1.3
・mpfr-3.1.2
・mpc-1.0.1

OS-X の場合:
・mac brew を使ってするのが楽です。
※他に、「MacPorts」が同じようなシステムですが、こちらは、メインツリー以外のバージョンをインストールする
場合など、全く融通が利かないので、mac brew を推します。
※mac brew のインストールについては、詳しいサイトが沢山あるので、そちらを参照下さい。
OS-X では、標準コンパイラは clang で、gcc コマンドがオーバーライドされています、gcc をインストールしておき、シンボリックリンクを貼り直しておきます。

brew install gcc
sudo ln -sf /usr/local/bin/gcc-4.9 /usr/local/bin/gcc
sudo ln -sf /usr/local/bin/g++-4.9 /usr/local/bin/g++
brew install gmp
brew install mpfr
brew install mpc

※確認

gcc --version
gcc (Homebrew gcc 4.9.2_1) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

まず、binutils-2.24 のビルド

binutils-2.24 のソースを展開したら、
※gcc-4.9 では途中でビルドを失敗するので、binutils-2.25 を使います。

cd binutils-2.24
# 直でビルドしないで、専用のディレクトリーで作業するのが普通です、失敗したらディレクトリーを削除してやり直せる。
mkdir m32c_build
cd m32c_build
../configure --target=m32c-elf --prefix=/usr/local/m32c-elf --disable-nls
make
make install

アセンブラのパスを通して、パスを有効にする為コンソールを開きなおします、そして、最初のCコンパイラをビルドします。
※この段階では、C コンパイラのみビルドします、C++ は、後でビルドします。
gcc-4.7.4 のソースを展開したら、(とりあえず、C++11 完全対応の gcc4.8 系を使わずに枯れたバージョンで試します。)
※ gcc は 4.8 系でかなり大きく構造が変わったようで、メンテナンスがされていない CPU の場合、4.8 系ソースでは、コンパイルに失敗する事が多い為です。
※C++11 完全対応ではありませんが、C++0x オプションがありますから、ほぼ遜色無く C++11 の機能を使えます。

cd gcc-4.7.4
mkdir m32c_build
cd m32c_build
../configure --prefix=/usr/local/m32c-elf --target=m32c-elf --enable-languages=c --disable-libssp --with-newlib --disable-nls
make

windows:
make install

OS-X:
sudo make install

次に newlib をビルドします。

newlib-2.1.0 のソースを展開したら

cd newlib-2.1.0
mkdir m32c_build
cd m32c_build
../configure --target=m32c-elf --prefix=/usr/local/m32c-elf
make

windows:
make install

OS-X:
sudo make install

最後に C++ をビルドします。

cd gcc-4.7.4
cd m32c_build
../configure --prefix=/usr/local/m32c-elf --target=m32c-elf --enable-languages=c,c++ --disable-libssp --with-newlib --disable-nls --disable-libstdcxx-pch
make

windows:
make install

OS-X:
sudo make install

とりあえず、C++ のビルド中、怪しい動作がありましたが、最後まで到達しました。(Windows)
簡単なテストは、とりあえずOKで、詳細なテストはこれからです。

スタートアップルーチンは、デバイスに合わせて書く事になる為、しばらくは、ハードウェアーマニュアル、ソフトウェアーマニュアルと「にらめっこ」です・・
また、R8C のアセンブラもある程度理解する必要があります・・

-cpu=r8c でR8C を選択した場合、「int」のサイズは16ビットのようです。
※この CPU によって int のサイズを16にしたり32にしたりする慣わしって誰が始めたのか知らないけど、ホントクズ仕様だよなぁー・・・
なので、int32_t、とかがあるんだけど、short があるんだから、int は32ビットで良くない!?

gcc のビルドは、手間もそうですが、時間もそれなりにかかるので、「めんどぃ」って人は、
今回ビルドしたバイナリーを以下のリンクに置いておきますので活用下さい~
MSYS(MinGW)版 M32C-gcc-elf
※通常は、「/usr/local/」で、アーカイブを解凍すれば、
「/usr/local/m32c-elf」が出来ますので、「/usr/local/m32c-elf/bin」にパスを通します。
これで、

m32c-elf-gcc
m32c-elf-g++

などが利用可能です。

R8C/M11A、M12Aの秘密

M11A、M12Aについて~

低価格なマイコンと言えば、AVRですが、ルネサスにも、CPの高いマイコンがあります。
それは、R8Cマイコンで、R8C/M12A/M11Aです。
「@100円、確かに安いけど、AVRもあるし、PICもあるしで、そんなにインパクト無いけどーー」と言いたくなるかもしれませんが・・
このマイコン、裏事情があって、表向きは
・プログラムメモリ(フラッシュ):2KB
・RAM:256バイト
となっていますが、実際には、
・プログラムメモリ(フラッシュ):32KB
・RAM:1280バイト
時代に逆行するアセンブラ屋さんの調査による。

これが、現在出回っているデバイスでも有効に出来るのなら、今までの常識が一遍しそうです。

AVRのATTiny2313は、安くて使いやすいマイコンですが、A/Dコンバーターが無いのが痛いとこです。
最近秋月の価格を観ると@150になっていますね・・

M12Aの場合は:
・最大20MHz動作(2.7V以上)
・1.8V~5.5V動作
・入出力ポート:17本 (LED駆動用ポート含む)
・外部割り込み入力:8本
・16ビット多機能タイマ(タイマRJ2):1
・8ビットプリスケーラ付8ビット多機能タイマ(タイマRB2):1
・16ビットインプットキャプチャ/アウトプットコンペアタイマ(タイマRC):1
・UART/クロック同期形シリアルインタフェース:1チャネル
・10ビットA/Dコンバータ:6チャネル
・コンパレータ:2回路
・ウォッチドッグタイマ
・クロック発生回路:XINクロック発振回路、オンチップオシレータ(高速/低速)
この充実ぶりです・・

この機能と、プログラムエリア32Kバイト、RAMエリア1280バイトとなると、かなりの用途にマッチします。


フラッシュの書き込みボードの作成

・配線は、ハードウェアーマニュアルに詳しく書かれていますので参考にして下さい。
・フラッシュ書き込みモードで電源投入後、FDTとの接続が時間内に無いと、接続出来なくなるので、「リセットスイッチ」は必要です。
・フラッシュの書き込みは、3.3Vでも行えるようです。
このDIPタイプは、シリアルインターフェースで接続出来る為、非常に手軽に書き込みが行えます。
IMG_0705

購入したデバイス
デバイスの表記では(下に書かれているのはロット番号と思います)


 M120AN
0432N01

 M110AN
0432N01

となっていました。


書き込みソフトのインストールと設定ファイルの修正

書き込みソフトは、FDT4.09 を使います。
インストール後、以下の設定ファイルを書き換えます。(Windows7 64 ビットの環境です)

/c/Program Files (x86)/Renesas/FDT4.09/kernels/ProtD/R5F2M110A/Renesas/1_0_00/R5F2M110A.fcf
/c/Program Files (x86)/Renesas/FDT4.09/kernels/ProtD/R5F2M120A/Renesas/1_0_00/R5F2M120A.fcf

「R5F2M110A.fcf」の修正箇所

[Header]
Description = Flash Development Toolkit Configuration File
File Type = 2.1

[Device Info]
EVB Name = R5F2M110A
EVB Kernel Suffix = R5F2M110A

...

FLASH ROM Size = 32768

...

Secret Flash Memory = 
Erase Block Count = 10
Memory Area Count = 2
Reserved Area Count = 0

...

[Erase Block 2]
Block Name = EB8
Start Address = 32768
Start (Hex) = 0x00008000
Block Size = 4096
Size (Hex) = 0x00001000
EBR0 = 0
EBR1 = 0
EBR2 = 0
Overlay = 0
Lockable = False

[Erase Block 3]
Block Name = EB7
Start Address = 36864
Start (Hex) = 0x00009000
Block Size = 4096
Size (Hex) = 0x00001000
EBR0 = 0
EBR1 = 0
EBR2 = 0
Overlay = 0
Lockable = False

[Erase Block 4]
Block Name = EB6
Start Address = 40960
Start (Hex) = 0x0000A000
Block Size = 4096
Size (Hex) = 0x00001000
EBR0 = 0
EBR1 = 0
EBR2 = 0
Overlay = 0
Lockable = False

[Erase Block 5]
Block Name = EB5
Start Address = 45056
Start (Hex) = 0x0000B000
Block Size = 4096
Size (Hex) = 0x00001000
EBR0 = 0
EBR1 = 0
EBR2 = 0
Overlay = 0
Lockable = False

[Erase Block 6]
Block Name = EB4
Start Address = 49152
Start (Hex) = 0x0000C000
Block Size = 4096
Size (Hex) = 0x00001000
EBR0 = 0
EBR1 = 0
EBR2 = 0
Overlay = 0
Lockable = False

[Erase Block 7]
Block Name = EB3
Start Address = 53248
Start (Hex) = 0x0000D000
Block Size = 4096
Size (Hex) = 0x00001000
EBR0 = 0
EBR1 = 0
EBR2 = 0
Overlay = 0
Lockable = False

[Erase Block 8]
Block Name = EB2
Start Address = 57344
Start (Hex) = 0x0000E000
Block Size = 4096
Size (Hex) = 0x00001000
EBR0 = 0
EBR1 = 0
EBR2 = 0
Overlay = 0
Lockable = False

[Erase Block 9]
Block Name = EB1
Start Address = 61440
Start (Hex) = 0x0000F000
Block Size = 4096
Size (Hex) = 0x00001000
EBR0 = 0
EBR1 = 0
EBR2 = 0
Overlay = 0
Lockable = False

[Memory Area 0]
Block Name = UA0
Start Address = 32768
Start (Hex) = 0x00008000
Block Size = 32768
Size (Hex) = 0x00008000
Area Type = User Flash
Overlay = 0

...

「R5F2M120A.fcf」の修正箇所

[Header]
Description = Flash Development Toolkit Configuration File
File Type = 2.1

[Device Info]
EVB Name = R5F2M120A
EVB Kernel Suffix = R5F2M120A

...
 
FLASH ROM Size = 32768

...

Secret Flash Memory = 
Erase Block Count = 10
Memory Area Count = 2
Reserved Area Count = 0

...

[Erase Block 2]
 Block Name = EB8
 Start Address = 32768
 Start (Hex) = 0x00008000
 Block Size = 4096
 Size (Hex) = 0x00001000
 EBR0 = 0
 EBR1 = 0
 EBR2 = 0
 Overlay = 0
 Lockable = False

 [Erase Block 3]
 Block Name = EB7
 Start Address = 36864
 Start (Hex) = 0x00009000
 Block Size = 4096
 Size (Hex) = 0x00001000
 EBR0 = 0
 EBR1 = 0
 EBR2 = 0
Overlay = 0
Lockable = False

 [Erase Block 4]
 Block Name = EB6
 Start Address = 40960
 Start (Hex) = 0x0000A000
 Block Size = 4096
 Size (Hex) = 0x00001000
 EBR0 = 0
 EBR1 = 0
 EBR2 = 0
 Overlay = 0
 Lockable = False

 [Erase Block 5]
 Block Name = EB5
 Start Address = 45056
 Start (Hex) = 0x0000B000
 Block Size = 4096
 Size (Hex) = 0x00001000
 EBR0 = 0
 EBR1 = 0
 EBR2 = 0
 Overlay = 0
 Lockable = False

 [Erase Block 6]
 Block Name = EB4
 Start Address = 49152
 Start (Hex) = 0x0000C000
 Block Size = 4096
 Size (Hex) = 0x00001000
 EBR0 = 0
 EBR1 = 0
 EBR2 = 0
 Overlay = 0
 Lockable = False

 [Erase Block 7]
 Block Name = EB3
 Start Address = 53248
 Start (Hex) = 0x0000D000
 Block Size = 4096
 Size (Hex) = 0x00001000
 EBR0 = 0
 EBR1 = 0
 EBR2 = 0
 Overlay = 0
 Lockable = False

 [Erase Block 8]
 Block Name = EB2
 Start Address = 57344
 Start (Hex) = 0x0000E000
 Block Size = 4096
 Size (Hex) = 0x00001000
 EBR0 = 0
 EBR1 = 0
 EBR2 = 0
 Overlay = 0
 Lockable = False

 [Erase Block 9]
 Block Name = EB1
 Start Address = 61440
 Start (Hex) = 0x0000F000
 Block Size = 4096
 Size (Hex) = 0x00001000
 EBR0 = 0
 EBR1 = 0
 EBR2 = 0
 Overlay = 0
 Lockable = False

[Memory Area 0]
 Block Name = UA0
 Start Address = 32768
 Start (Hex) = 0x00008000
 Block Size = 32768
 Size (Hex) = 0x00008000
 Area Type = User Flash
 Overlay = 0

書き込みプロジェクトの設定

・デバイスを指定する際、チェックサムが合わない為、警告が出ますが、そのまま進みます。
・とりあえずこの修正で、32Kまでプログラムメモリーが使えると思います。
※MSYS のコンソールから、emacs でファイルを直接編集したのですが、パーミッションの関係で、実際には更新されない事が判りました。
エクスプローラーなどで、編集後のファイルを上書き保存する必要があります。(その際、警告のダイアログが出ます)


謝辞

・「時代に逆行するアセンブラ屋」さんの「toida」さんには、設定内容などお世話になりました。

・M110AN 設定ファイル
・M120AN 設定ファイル