hira のすべての投稿

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 設定ファイル

WR250Xカスタム(その1)

最初はノーマルで乗ろうと思ったけど、やっぱし改造してしまう・・・

先日オークションを観てたら、ヨシムラのスリップオンマフラーがそこそこの値段で出ていたー
見た目、大きな傷も無いし、即決価格がそんなに高くなかったので、ポチってしまった・・・

WR250用のサイレンサーは色々出ているけど、各社物色したり、色々な情報を総括して、「RS-4J」はベストな選択だと思う。
チタンカバーで、カーボンエンド、これがやっぱり良さそうで、創りは流石に「ヨシムラ」隅々まで非常に良く出来ている。

中古なので、まぁ色々あるのが普通だけど、「写真の部品が全て」と書いてあったがあまり気にせず、落札したが、「物」が届いてみると、エキパイのクランプバンドが無い・・。
出品者に連絡したけど、「無い」との事、「何で???」多少怪しさを感じながらも、ヨシムラジャパンに電話した。
そして、部品をオーダーしようとしたのだが、衝撃的な事実が・・
「部品は今欠品中で最短でも来年の2月くらい」との返事・・・、えーーー、そしたら、「43-47」のクランプバンドなら、付きますよ!
との事で、探してみると・・あった!、これだ!「キジマ(Kijima) マフラーバンド 43-47mm」早速アマゾンで注文した。
※毎度の事だけど、アマゾンさん、何で、コレだけ送るのにあんな大きなボックスで送るかなーーwww(一番小さいらしいけど・・)

マフラーのガスケットも基本新品が良いので、近くのYSPで注文した、特殊な形状のガスケットで、1166円だった(高い!)・・・

そして、早速組んでみたー、多少、組み付けで悩んだが、何とか取り付けできた~
アイドリング中は、音は爆音でもなく(ノーマルより若干大きい程度)、回すとけっこう気持ちよい音がするー。
※12月に入って急に寒くなった、でも早速テスト走行と思ったけど、雨が降ってきたので、今日は試せなかった、残念・・・

で、次の日、ちょっと奥多摩駐車場まで・・前日は、イベントやってたらしい、この時期寒くて「5度」・・どんだけー

IMG_0701

排気デバイスが無いので、低速トルクが無くなったけど、アクセル開けると凄く気持ちよく回るー、これはタコが欲しい~
※流石に回すとそれなりの音がするー

-----
モタードやモトクロスの倒立では、インナーパイプを保護する為カバーが付いている、なので、インナーパイプを掃除出来ない。
なので、外して掃除。
フロントフォークのカバーを止めているボルトはスチールの安っすいボルト・・
多分錆びると思い、ステンレスのキャップボルトを買ってきた、このまま付けるのもイマイチなので、旋盤でテーパー加工してみた。

まず、ボルトをチャックに銜える為に、アルミカラーを作り三等分、そしてひたすら加工。

IMG_0694

IMG_0695

WR250Xは、峠楽しい!

組み込みでもC++

この記事はC++ Advent Calender 2014の参加記事です。

感覚的には、組み込み関係の業界では、C++の取り組みはかなりお寒いものがあります。

現代の組み込みでは、32ビットCPU、FPU内蔵、100MHz動作、そんなデバイスが539円、gcc で開発でき、もちろんC++を使えます、STLやboostは限定的に使えます。
※メモリーが少ないので、メモリーを多く必要なライブラリーに起因したクラスは使えません(たとえば、iostream 関係は、非常に多くメモリーを消費する為、デバイスによっては使えません)

去年は「WinAVR C++ の実力とは!?」を書きました。
AVRマイコンは8ビットのRISCマイコンで、扱えるリソースが少ない為もあり、STLが使えないなど制限がありました。
今回は、ルネサス エレクトロニクスの32ビットマイコンであるRX600シリーズで、C++の開発環境や、C++で実装するI/O定義クラスの紹介をしたいと思います。

RX621マイコンボード
IMG_0448s
・搭載マイコン:R5F56218BDFP(32ビットRXコア)
・プログラム用フラッシュメモリ:512Kバイト
・データ用フラッシュメモリ:32Kバイト
・RAM:96Kバイト(ノーウェイト動作)
・動作周波数:96MHz(12MHzをPLLにより8逓倍動作)

RXマイコンは、CISC型の32ビットマイコンで、低消費電力、安価、高機能、高速など、色々なメリットがある国産のマイコンです。
組み込み系マイコンと言えば、ARMの人気がもの凄く高く、色々なメーカーが採用している為、バリエーションも多く価格も安く、開発環境も整っていると言えるでしょう。
しかしながら、RXマイコンは、自分が評価した価格ゾーンでは、ARMより多くのメリットがあると感じます。
日本のメーカーにエールを送りたいとゆーのもありますが、コストが同じ程度なら、性能、入手性、日本語マニュアルなど、より扱いやすい物を使う方が効率が良いと感じます。
それでも、本当はARMの方が優位だとは思いますが、天邪鬼なんでRX押しです!www

開発環境:

ルネサスでは、開発ツールとして、自社開発の統合開発環境を用意しており、無料版(64キロバイト以上のバイナリーを作れない)もあります。
又、GNUを使った無料の統合開発環境「KPIT」も使う事が出来ますが、ルネサス提供のサンプルプログラムをコンパイル出来ないなど、冷遇されており、自分のようにコマンドラインで、コンパイラ、リンカー、Makefile、emacs で開発を進めるスタイルにも合いません。
・統合開発環境では多くの場合、裏で、目に見えない「何か」をやっており、「つぶし」が利かない。
・サターンの時代(SH2)に、自社開発のコンパイラを使った経験から、あまり良い印象が無いので、最初から使う気にもならず評価もしていません。
・新しい統合開発環境の操作を学ぶのに時間がかかる。
・少し大きなプログラムを作ったら、簡単に64キロバイトを超えてしまい、有料版ツールが必須になるが、数十万円の開発ツールは買う気にはなりません。
・ gcc ならソースコードを取ってきて、自分でコンパイルすれば、RX用の gcc を使う事が出来ます。
・自分で開発環境を用意する場合、デバッグ環境も自分で整えなければなりませんが、そこは、経験とアイディアで乗り切りますwww
・マイコンのフラッシュメモリーにプログラムを書き込むツールは、無料版があり(Windows環境のみ)こちらは、大きな制限はありません。
・メーカー専用のコンパイラは、gcc より、進んだ最適化が出来るとか言いますが、非常に限られた場合の評価しか宣伝していない為、懐疑的です。
・メーカー製IDEの良い部分としては、J−TAGプローブなどで行うリアルタイムのデバッグ環境などです。

マイコン用クロス開発用 gcc のビルドは、以下の3つのバージョンがマッチしないと、コンパイルに失敗する場合があります。
RXマイコンの場合、最新版は、以下の3つの組み合わせになるようです。
GCC 4.8.3
Binutils 2.24
Newlib 2.1.0

※まだ、自分は4.7.3を使っているので、試していません。

Windows の cygwin 環境では、コンパイルを通す為には、色々なオプションを付ける必要があるようで、大抵標準のオプションだけではコンパイルに失敗します。
又、コンパイルに必要な、ツールのバージョンにも影響するようで、再現性に乏しく困難なので、最近は MSYS(MinGW) でコンパイルしています、こちらは、比較的普通にコンパイルに成功します。
MacOSやLinuxでは、何の苦労も無くコンパイル出来ます、ただし、デバイス内臓のフラッシュROMに書き込むツールが対応していないので、現実的には Windows を使う事になります、現在、コマンドラインから、フラッシュへ書き込むプログラムを独自開発している最中です。

組み込みマイコン用の gcc が、PCの gcc と違う点は、スタートアップルーチンをデバイス毎に用意する必要があり(デバイスによって、持っているRAM、ROMのサイズが違い、初期化を行う方法が異なっているのが普通です)、多少複雑です、マイコンの場合、コールドスタートした状態では、割り込みは禁止され、最低限の設定状態で起動しています。
なので、最初に行うべきは、デバイスの必要な部分を初期化して、スタックや、記憶割り当てが使うメモリーの領域設定、C++の場合は、静的なクラスのコンストラクターを呼び出して初期化するなど、行う必要があります。

rx-elf/lib/rx.ld
RX用 gcc をビルドしたら、上記パスに、スタートアップに必要なファイルが用意されています。
「rx.ld」は、リンカースクリプトで、データセクション、プログラムセクション、など、初期化が必要な領域、初期化を必要とするクラスのコンストラクターやデストラクターのエントリーテーブルなど必要なリンク情報が入っています。
また、リンカースクリプトで、デバイスに適合するRAMやROM領域のアドレスを指定します。

以下は、リンカースクリプトの一部です。

OUTPUT_ARCH(rx)
ENTRY(_start)
MEMORY {
RAM(rwx) : org = 0x00000000, len = 0x00002000
ROM(rx) : org = 0xFFFF0000, len = 0x00010000
}
SECTIONS {
    _usp_init = 0x00001f00;
    _isp_init = 0x00002000;
    .fvectors 0xFFFFFFD0 : {
        vect.o(.fvectors)
    }

...

※ハードウェアーベクターテーブルの指定もあります。

マイコンは電源が入るとリセット信号がアサートされ、ハードウェアーベクターにあるリセットベクター先に飛び、プログラムを開始します。
以下はスタートルーチンで、マイコン独自の命令を実行する為、アセンブラで記述してあります。
※この部分だけはアセンブラを使う必要があります。

	.global _power_on_reset
_power_on_reset:
.global _start
_start:
# スタックの設定
    .extern _usp_init
    mvtc #_usp_init, usp
    .extern _isp_init
    mvtc #_isp_init, isp
# 割り込みベクタの設定
    .extern _interrupt_vectors
    mov.l #_interrupt_vectors, r5
    mvtc r5,intb
    mov.l #0x100, r5
    mvtc r5,fpsw
# Iレジスタを設定し、割り込みを許可する
    mov.l #0x00010000, r5
    mvtc r5,psw
# PMレジスタを設定し、ユーザモードに移行する
    mvfc psw,r1
    or	#0x100000, r1
    push.l r1
# UレジスタをセットするためにRTE命令を実行する
    mvfc pc,r1
    add	#0x0a,r1
    push.l r1
    rte
    nop
    nop
# init() 関数から開始
    .extern _init
    bsr	_init

    .global _set_intr_level
_set_intr_level:
    and	#15,r1
    shll	#24,r1
    or	#0x100000,r1
    mvtc r1,psw
    rts
    nop

    .global _exit
_exit:
    wait
    bra _exit

これは、「init.c」で、main に飛ぶ前に行う初期化を含んだものです。
※RXマイコンでは割り込みベクターのベースアドレスを変更できるので、自分のシステムでは、利便性を考慮して、RAM上に置いてあります。

int main(int argc, char**argv);
extern int _datainternal;
extern int _datastart;
extern int _dataend;
extern int _bssstart;
extern int _bssend;
extern int _preinit_array_start;
extern int _preinit_array_end;
extern int _init_array_start;
extern int _init_array_end;
extern void (*interrupt_vectors[256])(void);
extern void null_task_(void);
int init(void)
{
    // 割り込みベクターテーブルの初期化
    for(int i = 0; i < 256; ++i) {
        interrupt_vectors[i] = null_task_;
    }

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

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

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

    {
        int *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;
}

これで、main から実行でき、後は、通常のアプリケーションとほぼ同じとなります、最低限これだけあれば、後は、工夫次第で何とでもなります。
※リンカーで、独自のリンカースクリプトを使い、標準のスタートアップを行わないので、「-nostartfiles」を指定します。

最低限のデバッグとして、シリアルインターフェースを繋いで、ターミナルで文字の入出力が必要な場合、stdin、stdout に相当する部分を実装する必要があります。
RX63Tでは無理ですが、RX621では、それらの実装で、iostreamクラスを使え、通常のPCアプリとほぼ遜色無く動作します。
※PCでは、メモリーの消費はあまり気にしませんが、組み込みでは、注意する必要があります。

I/O定義:

組み込みマイコンでのデバイスドライバーに相当する部分は、通常は、Linux のようなシステムとは違い、カーネルによるしばりは無く、自由に直接プログラム出来ます。
であるにも係わらず、未だに、「C」でプログラムする事が当たり前のような状況になっています。
デバイスメーカーが提供するサンプルプログラムも、C++で作られたのは見た記憶がありません。(ARMではあるようです)
※それ以前に、サンプルプログラムの品質が低いと感じます。
趣味で、組み込みマイコンのプログラムをする場合でも、C++は敬遠されている感じがします。

さて、組み込みマイコンでは、I/O(シリアルコミュニケーション、A/D 変換、ポートなど)の操作を直接行いますが、最近のI/Oは高機能で、非常に多くの機能があります。
定義は複雑で肥大化しており、マイコンのバリエーションにより、機能が微妙に異なったりする場合もあります、もはや、一つのヘッダーでは、管理できないと思われます。

たとえば、ルネサスは、マイコン内のI/Oの機能を定義したヘッダーを提供していますが、これは、処理系依存で実装されている為、gcc でそのまま使う事は出来ません。
また、基本的に「C」言語を想定した定義なので、C++でプログラミングしたい場合にあまりメリットがありません。

これは、RX62Nの定義ヘッダーの一部です(A/D変換の部分です)

struct st_ad {
	unsigned short ADDRA;
	unsigned short ADDRB;
	unsigned short ADDRC;
	unsigned short ADDRD;
	char           wk0[8];
	union {
		unsigned char BYTE;
		struct {
			unsigned char :1;
			unsigned char ADIE:1;
			unsigned char ADST:1;
			unsigned char :1;
			unsigned char CH:4;
		} BIT;
	} ADCSR;
	union {
		unsigned char BYTE;
		struct {
			unsigned char TRGS:3;
			unsigned char :1;
			unsigned char CKS:2;
			unsigned char MODE:2;
		} BIT;
	} ADCR;
	union {
		unsigned char BYTE;
		struct {
			unsigned char DPSEL:1;
		} BIT;
	} ADDPR;
	unsigned char  ADSSTR;
	char           wk1[11];
	union {
		unsigned char BYTE;
		struct {
			unsigned char :6;
			unsigned char DIAG:2;
		} BIT;
	} ADDIAGR;
};

ビットフィールドが使われています、これは、処理系依存で、gcc では、LSB、MSBが逆になります。
また、最適化した場合に、適切なビット幅のアクセス(32ビットのI/Oに8ビットのアクセスなど)が行われない場合があり、RO(リードのみ)、WO(ライトのみ)などの表現も無く、読みにくく、使いやすいとは言えないと思います。

そこで、C++をもっと積極的に使って定義ファイルを作ったらどうかと考えます。
下の実装は、上のビットフィールドから、テンプレートを使った実装です。(少し冗長な部分があります)

//=====================================================================//
/*!	@file
	@brief	RX62N, RX621 グループ・AD 定義 @n
			Copyright 2013 Kunihito Hiramatsu
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "io_utils.hpp"

namespace device {

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  AD 定義
        @param[in]  base  ベース・アドレス
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    template <uint32_t base>
    struct ad_t {

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDRA レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        io16<base + 0x00> ADDRA;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDRB レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        io16<base + 0x02> ADDRB;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDRC レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        io16<base + 0x04>	ADDRC;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDRD レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        io16<base + 0x06>	ADDRD;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADCSR レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        typedef io8<base + 0x10> adcsr_io;
        struct adcsr_t : public adcsr_io {
            using adcsr_io::operator =;
            using adcsr_io::operator ();
            using adcsr_io::operator |=;
            using adcsr_io::operator &=;

            bits_t<adcsr_io, 0, 4>	CH;
            bit_t<adcsr_io, 5>	ADST;
            bit_t<adcsr_io, 6>	ADIE;
        };
        static adcsr_t	ADCSR;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADCR レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        typedef io8<base + 0x11> adcr_io;
        struct adcr_t : public adcr_io {
            using adcr_io::operator =;
            using adcr_io::operator ();
            using adcr_io::operator |=;
            using adcr_io::operator &=;

            bits_t<adcr_io, 0, 2>	MODE;
           bits_t<adcr_io, 2, 2>	CKS;
            bits_t<adcr_io, 5, 3>	TRGS;
        };
        static adcr_t	ADCR;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDPRA レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        typedef io8<base + 0x12> addpra_io;
        struct addpra_t : public addpra_io {
            using addpra_io::operator =;
            using addpra_io::operator ();
            using addpra_io::operator |=;
            using addpra_io::operator &=;

            bit_t<addpra_io, 7>	DPSEL;
        };
        static addpra_t	ADDPRA;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADSSTR レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        static io8<base + 0x13> ADSSTR;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDIAGR レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        typedef io8<base + 0x15> addiagr_io;
        struct addiagr_t : public addiagr_io {
            using addiagr_io::operator =;
            using addiagr_io::operator ();
            using addiagr_io::operator |=;
            using addiagr_io::operator &=;

            bits_t<addiagr_io, 0, 2>	DIAG;
        };
        static addiagr_t	ADDIAGR;

    };
    typedef ad_t<0x00088040>	AD0;
    typedef ad_t<0x00088060>	AD1;
}

io_utils.hppは、git にありますので参照の事。
今までにこのような取り組みは多少あり、同じような考えによる実装はいくつか参考にした事がありますが、不満な部分などがあり、結局自分で考えて実装したものです。
・ビットと機能の対比がわかりやすい。
・8、16、32ビットアクセス、RO、WO、RWを表現できる。
・最適化レベルに関係無く、厳密なアクセスを行える。
・エンディアンに依存しない。
・処理系に依存しない。(実際は gcc 以外で試していませんが、x86 の clang でシュミレーターで実験しています)
・最適化されたアセンブラソースを確認して、これなら満足できると思う。
※但し、レビューもされていませんし、もっと良い方法、適切な実装があるかもしれません。
※参照RX、I/O関係
※RO、WO、RWを分ける必要があるので、読み出しの場合は、()オペレーターを使っています。

クラス化した事で色々な恩恵を受けられます。
たとえば、シリアルコミュニケーションは、通常複数チャネルがありますが、I/O部分のアクセスをクラス化した事で、かなりスッキリ書けるようになり、異なったチャネルの操作もテンプレートで簡単に記述できるようになりました。

I/O操作をC++で書けると何がありがたいかと言うと、テンプレートが使える事で、微妙な違いを、隠蔽して、判りやすくて使いやすい入出力クラスをシンプルに書けます。

組み込みでC++を使うべき理由の一つです。

最後に:

組み込みの世界では、何故か、C++が敬遠される傾向にあると感じがしますが、C++こそ、組み込みに適した言語と思えてなりません。

これらRXマイコン関連の成果は、以下のリンクで公開していますので、興味ある人はどうぞ。

RX関係ソースコード

ウィンカーの LED 化(その1)

前から改造してみたかった、ウインカーのLED化を行っている。

まず、電球をLED化する、球切れの心配が無くなり、消費電力が減る。
それとLEDは、点灯、消灯がハッキリしていて視認性も良くなるのではと思う。

改造したバイクはヤマハのWR250Xモタードで、標準的には10Wの電球が付いていた。
IMG_0679s
ポジションは無く、1灯式バルブ。
※LED化する事でウィンカーリレーも自作するので、ポジションランプ機能も加えようと思う。

口金は「BA15s」と言うタイプで、ピンが水平に出ている。
※千石電商で購入

自作する場合に考えなくてはならない事は、以下のような点・・
・頑丈で、振動などで、点灯不良にならない構造。(接着剤での接着などを極力避ける)
・最低でも4個は必要なので、作りやすさ、同じ物が作れる(再現性の確保)事。
・売っているLEDバルブには無い機能と性能など。

以上のような点だろうか・・

最初に問題となるのは、口金にLEDをどのように固定するか?
そこで、以前に自動車の車内を照らすシーリングライトをLED化した時の手法を応用した。
まず、口金にユニバーサル基板をピッタリサイズで切り出し差し込む、この時、スルホールが、口金の金属と近くなるようにする。
基板には、垂直に基板を固定する為のボスを作っておく。
IMG_0676s

基板を中に入れて、スルホールにハンダを流し、口金の金属部分にもハンダを流して固定する。
※それなりにハンダを流し込めば、ある程度の強度は確保出来ると思う。
IMG_0678s

蓋をするように基板を切り出す、大体円になるように基板を削る、中心も、先ほどの基板のボスが入るようにリューターで削る。
※10Wの電球は外形19mmくらいなので、それ以下の大きさにする。
IMG_0680s

IMG_0681s

さて、LEDはどのような物を選ぶのが良いか?、何個くらいを並列にするか?

LEDの性能は、日進月歩で、日増しに効率が高くなっている為、「使いたい時にまとめて買う!」のが良いと思う。
今回選択したのは、このLED「OSWX4EZ4E1P」リンクは秋月のものだが、適当なLED屋さんでまとめて買った方が安いと思う(自分は千石の手前のLED専門ショップで50個1000円で買った)

このLEDは、3個の白色LEDが直列になっていて、90mAまで電流が流せる、その場合30ルーメンと言う明るさ。
LEDは、 全ての範囲で電流と明るさが比例するわけではなく、効率の良い電流値があるものだ、その電流なら、発熱も少ない。
今回は実験して、10Wの電球との明るさの違いなどを調べ、1個辺り、約20mA~30mA程度にし、全部で9個を使う(大体全体で200ルーメン程度だと思う)のが丁度良いと思った。
※LEDを並列にして使うので、特性が揃っている必要がある為、同じロットの物を選ぶ必要がある。
※それでも10Wの電球よりかなり明るい。
※ある程度数を多くして分散させた方が、光が集中しないで電球と同じように拡散させる事が出来る。

IMG_0683s
実験用として作ったLEDアレイ

LEDに流れる電流の制限抵抗は、以下のように計算した。

・電源の電圧を13Vとする。
※LED保護と汎用性を考えて、ショットキーダイオードによるブリッジを組む、こうすると、極性が事実上無くなるので、扱いが楽になる。
・ショットキーダイオードの順方向電圧降下を0.4Vとする。
・LEDのVFを3.1Vとする。

25mAの電流を流すには?

13-0.4×2-3.1×3 / (25mA×9) = 13オーム

※適当な抵抗が無かったので47オームを4本並列とした。

まず、下の列、5個を直線で繋ぎ~
IMG_0684s

適当に曲げて五角形にする。
IMG_0685s

それを、口金のベースに並べて端子をハンダ付け。
IMG_0686s

最後に正面用アレイ:
まず、2個のLED、サイドを斜めに削って、斜めに密着させる。
IMG_0687s

それを2組作り、やはりサイドを斜めに削り、密着させて4個のアレイにする。

それを、上に載せて隙間からハンダを流して接続、出来上がり~
IMG_0689s
※順番を考えながら、ショートに注意して組み立てる、失敗すると修正が大変なので注意。
※4隅の角を少し削って19mmの直径に収まるようにする。

やっと1個出来た・・・、あと3個、とりあえず今日はここまで。