「ソフトウェアー・エンジニアリング」カテゴリーアーカイブ

ソフトウェアー関係の話題など・・

最近は、github にプッシュしてます~

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

組み込みでも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関係ソースコード

俺俺フレームワークのWindowsへの対応

OS-X 対応で、Windows はまぁとりあえずスルーしても良いかなぁーって思ったけど、やっぱり折角動くもんなら、
少しの労力でマルチプラットホームになる事だしサポートしようと思い、少しばかり修正した。

MSYS の Mingw 環境では、C++11 の thread 関係 API をサポートしていない。
そこで、それら API を使わないように修正をするだけの事で、Mac、Windows 両対応となった。

内部ソースには、機種依存の条件分岐が色々あるのだが、それらは、今後少しづつ無くしていき、クリーンな状態
へ移行する。

以前のブログで、Mingw 環境の構築を少しだけ載せたけど、新しいフレームワークでは、Mac 対応で、少しばかり
環境を更新したので、その辺りを少し解説しておく。

(1) MP3 タグ情報ライブラリー
以前のシステムでは、tag 情報を取得するライブラリーとして、id3tag ライブラリーを使っていて、機能的にどう
しても不足していた部分やバグを修正して使っていたのだけど、色々探したけど、修正したソースが無い事が判った・・
そこで、少し探して、簡単に使えそうなライブラリーとして、taglib を検討してみた、ソースは C++ なんだけど、
かなり、昔の C++ って感じで、イマイチ感があったけど、他の候補を色々探して、検討するのも時間がかかりそう
なので、とりあえず、このライブラリーを使わせてもらう事にして、実装した。
なので、Windows でも、このライブラリーをコンパイルしてインストールする必要がある。
※taglib-1.9.1 を使った。

(2) OpenJPEG ライブラリー
macport でインストールした OpenJPEG ライブラリーは 2.1 で、API も少し違う為、Windows も 2.1 にアップ
デートした。

(3) JPEG ライブラリー
macport では、MMX で高速化した JPEG ライブラリーとして、libjpeg-turbo がある、またこのライブラリーは
若干の拡張がしてある。(Alpha チャネルを扱える)
そこで、Windows でもこのライブラリーを使いたいのだけど、コンパイルする為に、nasm が必要で、インストール
が複雑になるので、とりあえず、Windows では以前のライブラリーを使う事にした。
※アルファ付き画像をセーブした場合、アルファチャネルは無くなる。

(4) Makefile の改修
Windows では、Makefile を共有する事が難しい為、Makefile.win と別にした。
※この辺りも、cmake を使うとか、今後工夫が必要と思う。

以上の対応で、音楽プレイヤーは同等の動作をするようになった。

※最新版ソースコードは GitHub に push してある。

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

最近は、フルタイムで仕事をしている関係で、趣味の時間が凄く制限されていたのだが、仕事もだいぶ安定してきて、趣味の時間を持てるようになってきた。
それと、「この暑さ」、休みの多くは、ファミレスにノートを持ち込んで、主にソフトの制作などを行っていた。
最近、少し暑さも和らいだので、ハードの作業もしようかと思案していたら、秋月で良い物を見つけた。

128Mbits SDRAM 133MHz
まぁ、別にそんな珍しい物でも無いけど、デバイスを単体で扱っているのは珍しい、数個単位で買えるのはありがたい。
※多分、秋月で、SDRAMが乗ったマイコンボードでも出るのだと思う。

そして、マルツパーツで 176ピン、フラットパッケージ版 RX63N が単体で買える事が判ったのが最後の一押しとなった。

「176ピン、フラットパッケージ」は重要。
・0.5mm ピッチなので、ギリギリ、手ハンダができる。
・32ビットバスでSDRAMを接続出来る。(フルスピードで動かしても、性能があまり落ちない)
・内蔵 RTC のバックアップが出来る。
・外部バスを使っても、残りの空いたピンはかなり潤沢で、色々活用出来る。

そして、前から、RX マイコンの大容量メモリーを積んだボードが欲しかった。
・開発の効率などを考えると、プログラムをRAMに転送して実行したい。
・RX マイコン用の LLVM を作ってみたい、それには、ある程度メモリーが潤沢に使えるボードが必要。
・組み込み機器でも、メモリーが潤沢に使える環境が欲しい。
※大容量メモリーが無いと、画像ファイルなどを扱うのが難しい。
・PC 用フレームワークのソースコードをそのまま利用したい。
・安いボードじゃないと、色々な物に使う気になれない。
※「なひたふ」さんとこで、究極の RX62N ボードを扱っているのだけど、値段が高くて手が出せない。
※構成的には、同じようなものになってしまうのだけど・・・

他にも色々な動機がある〜

もうそろそろ、RX64M シリーズが流通しそうな感じなのだが、KiCAD を使ってボードも作ってみたかったので、汎用的で、応用が効く俺俺ボードを自分で作ってみる事にした。

とりあえず、構成はこんな感じにする予定〜
・RX63N(R5F563NEDDFC#V0)176 ピンパッケージ(内蔵、256K RAM、2048K Flash)
・内蔵 RTC 用外部バッテリーバックアップ
・128M ビット SDRAM x 2、32ビットバス接続(32Mバイト)
・10/100 インサーネット(PHY層: LAN8720AI)※別モジュールにするかも・・
・マイクロSDカードインターフェース
・リチウムイオン/ポリマー、充電コントローラーと DC/DC コンバーター
・A/D 入力用バッファアンプと電圧リファレンスなど
・オーディオインターフェース(VS1053B)
・USB インターフェース(2チャンネル分)
・未使用ポート用ピンヘッダー
・J-TAGコネクター
ちょっと盛り過ぎな感じもするのだが、使わない物は載せなければ良いと思うので・・・
※完成したら、ボード単体で販売する予定(時価)だが、欲しい人は少ないかもしれないねwww
※SDRAM は、@300なので、2個載せても600円、しかも32Mバイトの空間を使えるのは便利

そもそも、R-PiやBBBが5千円くらいで買える現実を考えると、0.1GHz程度のマイコンなんてと思うのだが、OSが無く、電源投入で、いきなりアプリを動かせ、電源をいきなり切れるような、組み込みの用途は必要だと感じているし、0.1GHzで十分な用途はけっこう多い。
またLinuxベースで、ハードウェアーに依存した細かい制御を書くのは、面倒でもあるし、リアルタイム的な制御が難しい場合もある。
※内蔵カウンターを使ったタイマーや、PWM 出力、割り込み制御など。

別の動機として、販売されている組み込み用ワンボードマイコンが高価過ぎるのも問題と思う。
趣味では無く、会社の形態として販売する場合、あのような値段になる事は理解出来るので、自分で作るしか無い。
イニシャルコストはかかるのだが、3枚、同じボードを使う事を考えたら、作った方が安い事になる。
※この場合、制作に関わる人件費を計上してないのだけどwww

まず、マルツパーツで、R5F563NEDDFC#V0(RX63N, Flash: 2MB, RAM: 128K) を注文した、1912円もしたけど、数個単位で買えるのはありがたい。
数日で、物は届いたのだが、梱包が微妙、足が曲がっている感じがする・・・
とりあえず、交換の手配をメールで問い合わせたのだが、写真を送って欲しいとある。
0.5mm ピッチだと、ルーペで拡大しても、何とも言えないのだが、梱包のやり方に難があるように思う。

IMG_0658s

導電スポンジにチップを載せた状態で、ラップして内部の空気を抜いてあるのだが、空気を抜く事でスポンジが縮み、ピンにストレスがかかっていて、反っているじゃないかと思う。
ラップを破れば、空気が入って、反りは戻るかもしれないが、微妙に反った状態になるかもしれず、非常に微妙・・・
普通は、もっと安全確実な方法で梱包すると思うのだが、デリケートな品物だけに、強引すぎると思う。
※以前に RX63T を注文した時は、こんな強引な梱包をしてこなかった。

「ピンが曲がっていたら交換してくれる」と言う事なので、意を決して、パッケージを開けてみたー
ピンが曲がっていたと思えたのは、ピンが導電スポンジにめり込んでいる為だったようで、購入した3個、全て問題無かった。
でも、もっと「やんわり」梱包する方法はあるハズで、そこは改善して欲しいとこ。

早速、変換基板にハンダ付けしてみた。
※変換基板は、今まではダイセン製を好んで使っていたが、秋月で良さそうな物を売っていたので購入してみた、値段は安いけど、品質は満足のいく代物。
やっぱ、0.5mmピッチは厳しいなぁー、176ピンもあるので大変、それと、ハンダコテの温度設定が低くなってて、難儀した。
ハンダ吸い取り線による、ブリッジの除去も、温度が低かったせいか、難儀した・・・

IMG_0664s

ルーペで拡大して、念入りに確認したので、まぁ、大体大丈夫と思う、多少怪しい部分もあるのだけど・・・

追記:(2014年9月21日)
この変換基板、重大な問題がある事が判りましたー、まぁ176ピンの変換基板を使う人は限られると思いますが・・

IMG_0665s

↑写真で判るように、縦と横のピンヘッダー取り付け部分のグリッドが 2.54mm ピッチでは無く、そのままでは、一般的な
蛇の目基板に取り付け出来ません・・・

※twitterでツイートしたら沢山の方にリツイートしてもらいました・・・
※秋月には、報告しません、どうせしても無駄だから・・・、以前にSDカードモジュールの結線ミスに気がついて、報告しましたが、
何の返信もありませんし、商品のQ&Aに情報が共有される事もなく無視されました、なので、今回は何もしません。
秋月は、商品の価格が安く、それゆえ、自作をするものにはメリットが大きいし、利用している人は多いのですが、
アフターケアに関しては、全く駄目なように思います、それを判った上で最大限利用するしかありません。
※現在は、ピッチがズレている事が明記されているようです。

※以前に秋月にメールした全文

お疲れ様です。

K-05818「SDカードスロットDIP化モジュール」について
参考資料で、CD(Card Detect)となっている信号は、ライトプロテクト信号のようです。
この商品に使われているソケットは、「Card Detect」用の端子がありますが、(4番)、GND(5番)とショートされており、使う事が出来ない仕様となっています。
カッターなどで、パターンを切って、(4番)を引き出すと、「Card Detect」として使う事ができました。
Q&A等に、情報として記入する事をお勧めしたいです。

それでは宜しくお願いします。

-----
普通は、いきなり基板を設計するかもしれないが、個人制作なので、とりあえず、ユニバーサル基板で、試作してみる事にする。
まぁ時間はかかるけど、作って動かしてみないと判らない事も多くあるのではと思う。

C++11 future の実装依存?

C++11 では標準ライブラリーでスレッド関係の API が用意されている。

future テンプレートクラスは、お手軽に、平行処理で結果を受け取る方法としてコンビニエンスなやり方だ。

std::string task_(const std::string in)
{
    std::string text;

    // 何かの処理・・・

    return text;
}

std::string in;

std::future<std::string> f = std::async(std::launch::async, task_, in);

こんな感じで、簡単に実行委譲が出来る。

------
最近、俺俺フレームワーク内で、音楽ファイルのタグ情報を future を使って取得する実装を書いた。
しかしながら、思ったようには動作せず、色々調べたり、実験を行った。

結論を言うと、これは、OS-X、clang の C++11 ライブラリーに起因した問題のように思う。

症状としては:
・メインプログラムから、future を使って、タスクを起動しても、メインプログラムが全然軽くならない・・・
※ future で起動したタスクは、メインプログラムの負荷として加算される感じ。
・フラグがあって、それで「別スレッド」を指定してもだ・・( std::async::lanuch )
※ Android でも試したが、そもそも「別スレッド」を指定出来ない(コンパイルエラーとなる)ので、これは正しい動作なのかもしれない。
※これは clang3.4 で使っているライブラリーの問題なのかもしれないが、情報は探せなかった。
※ Windows の gcc や clang では、C++11 thread API 関係が実装されていない事と何か関係がありそうだー

結局、pthread を使って、やりたい事は出来たのだが、「future 便利だー」って盛り上がってたのに・・
何ともかんとも・・・
※ C++11 にも thread 関係があるのだけど、future で懲りたので、あえて、pthread を使った、pthread でもそんなに複雑化せずに平行処理を書ける。
ただし、future を使った場合と比べると実装量は増えるが、厳密な処理負荷は、少ないだろう。
もうしばらくしたら、C++11 の thread を本格運用して試す事とする・・
※石橋を叩いて渡る〜