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

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

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

R8CでSDカード

さて、LCDも何とか動くようになったので、避けては通れないSDカードのアクセス
をやってみたいと思います。

先日 Flash が64Kある事が判ったので、RAM容量は心細いですが、
SDカードアクセスに不安はありません。
組み込みでSDカードと言ったら ChaN さんでしょうー、えるむさんが実装して、ソース
を公開しているSDカードのソリューションは非常に柔軟性があり、細部まで良く考えら
れており、組み込みマイコンにおけるSDカードスタンダードだと思います。
少ないリソースでも動作するように、プチ fatfs もあり、今回のR8Cに最適な
ソリューションです。(感激!感謝です!)

SDカードですが、FlashAir を使えるように、標準SDカードソケットを使います。
標準SDカードソケットなら、直接ユニバーサル基板にハンダ付けできますが、スイッチ
関係のパッドが狭く、その部分は、絶縁テープで保護して、直接配線します。
SDカードソケットは、ヒロセ製(DM1B-DSF-PEJ)を使いました。
このソケットは、価格は高いですが、機能と品質が非常に優れています。

事前に考えておかなければならない点として、ポートにどのように、各信号を割り当てる
かです。
まず・・
・「/RESET(3)」は、ポートにも割り当てが出来るけど、一応キープ。
・「MODE(8)」端子は当然のようにキープ。
・「TXD(16)、RXD(15)」もシリアル通信で必須なのでキープ
・「AN0(20)、AN1(19)、AN2(18)、AN3(17)」は、とりあえずキープ
・「XIN(6)」は、高精度なクロックを入れて使う場合があるのでキープ
・「TRJIO(13)」は、周期測定用としてキープ

// P4_2(1):   LCD_SCK  ,SD_CLK(5)
// P3_7(2):   LCD_/CS
// /RES(3):  (System reset)
// P4_7(4):            ,SD_DO/DAT0(7)
// VSS:(5)   (Power GND)
// P4_6(6):   XIN (高精度なクロック用)
// VCC(7):   (Power +V)
// MODE(8):  (System mode)
// P3_5(9):   I2C_SDA
// P3_4(10):           ,SD_/CS(1)
// P1_0(20):  AN0 (keep)
// P1_1(19):  AN1 (keep)
// P1_2(18):  AN2 (keep)
// P1_3(17):  AN3 (keep)
// P1_4(16):  TXD0
// P1_5(15):  RXD0
// P1_6(14):  LCD_A0 (share)
// P1_7(13):  TRJIO (keep)
// P4_5(12):  LCD_SDA  ,SD_DI/CMD(2)
// P3_3(11):  I2C_SCL

こんな感じになりました、これで、ほぼ全てのポートが埋まりました。
「P1_6(16)」は、Flash 書き込み時に使うので注意が必要です。
※無難な、LCD_A0(コマンド/データ切り替え信号)にしています。
※AN0、AN1、AN2、AN3 はアプリによって自由に使えるポートとしています。
IMG_0751s
※電源は3.3Vで動かします!
R8Cは3.3Vで動かしても、20MHzで動作します、フラッシュプログラムも問題なくできます。

ぷち FatFsはソースコードを展開して、pffconf.h で基本的な設定を行います。

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/

#define	_USE_READ	1	/* Enable pf_read() function */
#define	_USE_DIR	1	/* Enable pf_opendir() and pf_readdir() function */
#define	_USE_LSEEK	1	/* Enable pf_lseek() function */
#define	_USE_WRITE	1	/* Enable pf_write() function */

#define _FS_FAT12	1	/* Enable FAT12 */
#define _FS_FAT16	1	/* Enable FAT16 */
#define _FS_FAT32	1	/* Enable FAT32 */

とりあえず、全て「1」で良いでしょう。
FAT12、FAT16 は「0」でも問題は少ないですが、古いSDカードは「FAT12」や「FAT16」
でフォーマットされており、それらを使う必要があるなら「1」にする必要があります、
但し、実行バイナリーは多少大きくなります。(おおよそ1Kバイト増加)
※FAT32でフォーマットをやり直せば、使える物もあるかと思います。

/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/

#define _USE_LCC	1	/* Allow lower case characters for path name */

#define	_CODE_PAGE	932

「_USE_LCC」を「1」にして
コードページは932にしておきます。

Sample プロジェクトの generic を参考に、「diskio.c」シリアル/パラレル変換の部分を書き換えます。
自分のシステムでは、I/O ポートの制御部分は C++ のクラスで構成されているので、
「diskio.cpp」として、メインのポート定義「port_def.hpp」を使って、実装します。
※port_def で宣言しているポートクラスの実体は、「diskio.cpp」で宣言していますが、
このクラスは、最適化されインラインで組み込まれるものと思います。
他に、「マイクロ秒」単位の時間待ちルーチンが必要で、「common/delay.hpp」に該当する
関数があるので、それを使います。
※「utils::delay::micro_second(n);」

// マイクロ秒単位の、「時間待ち」
static void dly_us(unsigned int n)

/*-----------------------------------------------------------------------*/
/* Transmit a byte to the MMC (bitbanging)                               */
/*-----------------------------------------------------------------------*/
static void xmit_mmc(BYTE d)

/*-----------------------------------------------------------------------*/
/* Receive a byte from the MMC (bitbanging)                              */
/*-----------------------------------------------------------------------*/
static BYTE rcvr_mmc(void)

/*-----------------------------------------------------------------------*/
/* Receive a byte from the MMC (bitbanging)                              */
/*-----------------------------------------------------------------------*/
static BYTE rcvr_mmc()

基本的に以上の4つの関数を実装すれば良く、他に、元のソースでは、コマンド
の定義を、「#define」で行っていたので、「static const BYTE」に換えました、C++
では、この定数は、コンパイル時にのみ使われ、メモリー上に置かれる事はありません。

これで完了です。
簡単なテストとして、カードをマウントして、ディレクトリーをリストしてみます。

    FATFS fatfs;
    bool mount = false;
    // pfatfs を開始
    {
        if(pf_mount(&fatfs) != FR_OK) {
            sci_puts("SD mount error\n");
        } else {
            sci_puts("SD mount OK!\n");
            mount = true;
        }
    }

    if(mount) {
        DIR dir;
        if(pf_opendir(&dir, "") != FR_OK) {
            sci_puts("Can't open dir\n");
        } else {
            for(;;) {
                FILINFO fno;
                // Read a directory item
                if(pf_readdir(&dir, &fno) != FR_OK) {
                    sci_puts("Can't read dir\n");
                    break;
                }
                if(!fno.fname[0]) break;

                if(fno.fattrib & AM_DIR) {
                    utils::format("          /%s\n") % fno.fname;
                } else {
                    utils::format("%8d  %s\n") % static_cast<uint32_t>(fno.fsize) % fno.fname;
                }
            }
        }

SD_test_0

GitHub R8C

R8C I2C で EEPROM アクセス

先日、I2C接続のRTCが動いたのだが、対象のデバイスがローカルすぎて、
誰も追試できるような状況ではなかったです。

それで、I2Cとしては、もっと汎用的なデバイスでも試してみたかったので、
EEPROM用の入出力を実装してみたが、どうも、動作しない。
読み込みは出来てるようだけど、書き込みが出来ない・・・

どうも、I2C の STOP コンディションが、正しく機能していないようで、
調査したら、ACK を受け取った後に、SDA を「0」レベルに保つ必要があっ
たのに、抜けてたようだった。
※SDA を「0」に戻さないと、STOP コンディションが正しく機能しない。
それで、これを修正したら、無事書き込みが出来るようになった。

EEPROM は、STOP コンディションをトリガーにして、実際に書き込む為
動作しなかったようだー。

i2c_io クラスでは、なるべくシンプルにした方が良いだろうと思って、単純に
データを転送する関数と、受け取る関数のみ用意していたけど・・

//-----------------------------------------------------------------//
/*!
    @brief  受信(リード)
    @param[in] address スレーブアドレス(7ビット)
    @param[out]	dst    先
    @param[in]  num    数
    @return 失敗なら「false」が返る
*/
//-----------------------------------------------------------------//
bool recv(uint8_t address, uint8_t* dst, uint8_t num) const;

//-----------------------------------------------------------------//
/*!
    @brief  送信(ライト)
    @param[in] address スレーブアドレス(7ビット)
    @param[in] src      元
    @param[in] num      数
    @return 失敗なら「false」が返る
*/
//-----------------------------------------------------------------//
bool send(uint8_t address, const uint8_t* src, uint8_t num) const;

内部動作では、EEPROMにデータを書き込む場合は、データアクセスのアドレス
を、指定する必要がある。
通常の実装だと、テンポラリーを用意して、アドレスとデータをコピーしてから、
送らないとならない。
しかし、それでは、効率が悪いので、冗長ではあるけど、関数を追加する事にした。
アドレス指定は、1バイトと2バイト用に二種類がある。

//-----------------------------------------------------------------//
/*!
    @brief  送信(ライト)
    @param[in] address スレーブアドレス(7ビット)
    @param[in] first    ファーストデータ
    @param[in] src      元
    @param[in] num      数
    @return 失敗なら「false」が返る
*/
//-----------------------------------------------------------------//
bool send(uint8_t address, uint8_t first, const uint8_t* src, uint8_t num) const;

//-----------------------------------------------------------------//
/*!
    @brief  送信(ライト)
    @param[in] address スレーブアドレス(7ビット)
    @param[in] first   ファースト・データ
    @param[in] second  セカンド・データ
    @param[in] src     元
    @param[in] num     数
    @return 失敗なら「false」が返る
*/
//-----------------------------------------------------------------//
bool send(uint8_t address, uint8_t first, uint8_t second, const uint8_t* src, uint8_t num) const;

内部で行っている事は単純だけど、EEPROMへの書き込みはシンプルとなった。
又、EEPROMの書き込みでは、最後のSTOPコンディションがトリガーとなり、
ページバッファにあるデータを実際に書き込む、書き込み時間は比較的長く、5ミリ
秒とかかかる、その為、書き込みが終了するまで、待機する必要があり、今回はポー
リングで待つ事にした。
ポーリングは、比較的簡単で、書き込んだアドレスを読み出しに行けば、書き込み中
なら、正規のACKが返らないので、読み出しが失敗する、それを利用している。
もし、規定時間以上、ACKが返らない場合は、「書き込みが失敗した」として、
ストールさせる。
ポーリングで待つ場合だと、EEPROMの持っている性能を最大限生かせて、
スループットは最大となる。

あと、重要なのは、ページ境界をまたがった書き込みの場合で、start 関数で指定した
ページサイズを超えた書き込みが発生した場合は、2回に分けて書き込むように配慮
した。

さらに、もう一つの仕様として、ページサイズ以内で、ページを超えない書き込みの
場合は、書き込み終了を待たずに、関数は、直ぐに終了する。

アプリ側で、ページサイズ以内で、書き込み待ちが起こらないような間隔で書き込むよ
うに配慮してあれば、「待ち」の為にポーリングしなくて済むので、CPU時間を有効
に利用できる。
この場合、書き込みの発行を分けて、複数回行うようなコードを書くと、二回目の
書き込みで「失敗」してストールする。
※これは、回避する事も出来るけど、余分なワークを消費するので、仕様とした。
一応、書き込み中か、判定する関数も用意してある。

//-----------------------------------------------------------------//
/*!
    @brief    書き込み状態の検査
    @param[in]  adr    検査アドレス
    @return 「false」なら、書き込み中
 */
//-----------------------------------------------------------------//
bool get_write_state(uint32_t adr) const;

IMG_0751s
先日、実験したLCD基板で、実験中~

I2C_EEPROM_test
実際のテストでは、ターミナルとの対話形式で、書き込み、読み出しのテストを行えるようにした。
ボーレートは19200、8ビット、1ストップビット

Start R8C EEPROM monitor
# help
speed KBPS (KBPS: 10 to 1000 [Kbps])
read ADRESS [LENGTH]
write ADRESS DATA ...
fill ADDRESS LENGTH DATA ...

EX:

# speed 1000

1000Mbps (1M bps) の指定

# read 1000

1000 番地から16バイト表示

# read 1000 10ff

1000 番地から、10ff 番地までダンプ

# write 127e fc ba 95 67 8e

127E 番地より、「FC BA 95 67 8E」を書き込み
※最大8バイトまで

# fill 0 1ff 1 23 45 6 78 9a b

00000 ~ 001FF 番地までを、「01 23 45 06 78 9A 0B」で埋める。
※最大8バイトまで

R8C GitHub

R8CでLCDテスト

I2Cの前にSPI接続を試しておく予定だったのですが、前後してしまった。

で、早速、SPI接続の簡単なテストから始めてみました~

とりあえず、書き込みだけで、動作を確認できそうなので、LCDで実験してみます。
基板付きFSTN液晶モジュール(SPI) [M-G0812P7567]
このLCDモジュールは、128×32ピクセルで、LEDバックライト内臓のタイプです。

IMG_0750
※3.3Vで動作させる為、バックライトLED用の電源は、HT7750Aで昇圧しています。
※先日作ったシリアルモジュールを繋なげるようにして、フラッシュ書き込みのモードスイッチ、
リセットスイッチも設けました。

ビットマップグラフィックスでは、描画の利便性から、どうしても、解像度と同じメモリーが必要
です、R8C/M120AN の場合、RAM が1280バイトしか無いので、128×32だと512バイトの
RAMを消費します、この容量が、限界ギリギリのとこでしょうか・・・
LCDにデータを送ったら、このRAMは他の用途に使えるので、シェアリングする事も出来ると
思いますが、その場合を考えて、システムを設計しておかなければなりません・・

LCDの制御コードは、U8glib を参考にしましたが、ST7567の制御コードは無かったので、
多分近そうなST7565の初期化パターンを参考にしました。
※ST7567のデータシートはネットを探すとあるのですが、いくつかの流動的パラメーター
があり、とりあえず、どのようにするのか目安が必要です。
※Aitendoさんの商品は、安くて、魅力的なのが多いのですが、参考資料が少ないのが難点です。

カットアンドトライで、コードを変えながら、1時間くらいかかって、ようやく画面がまともに
出るとこまで来ました。
※まだ多少不安定な事があり、原因を究明中・・・

SPIでは、SCL、SDA_OUT、SDA_INPの3本が、ベースで、デバイスセレクトや、
アドレスセレクトは、別に考える必要があります、複数のSPIデバイスを繋ぐ場合、ベースの
制御信号は、シェアする為です。

spi_io クラスは、非常にシンプルで、基本、write、read のみです。(まだ write のみの実装)
※R8C/M120AN には、UARTの同期モードがあり、これを使うと良さそうですが、そうすると、
UARTが使えなくなってしまう為、ソフト制御としました。

lcd_io(ST7567の制御)では、LCD制御を含めて、SPIを制御します。

今回のLCDでは4本の制御線が必要です。
SPI(共通部分):
・SDA シリアルデータ
・SCL シリアルクロック

LCD(固有部分):
・A0  アドレスセレクト(コマンドレジスタとデータレジスタの切り替え)
・/CS チップセレクト
※RES信号は、R8Cのリセットに接続しています。

SPI制御ポートの定義

struct spi_base {
    void init() const {
        device::PD4.B2 = 1;
        device::PD4.B5 = 1;
    }
    void scl_out(bool b) const { device::P4.B2 = b; }
    void sda_out(bool b) const { device::P4.B5 = b; }
};

LCD制御ポート(A0、/CS)の定義

struct spi_ctrl {
    void init() const {
        device::PD3.B3 = 1;
        device::PD3.B7 = 1;
    }
    void a0_out(bool b) const { device::P3.B3 = b; }
    void cs_out(bool b) const { device::P3.B7 = b; }
};

IMG_0749s

GitHub R8C

R8C I2C(RTC)のテスト

「R8C M120AN/M110AN」には、I2C専用ハードは無いので、ポートを制御する事で
同等の機能を実現するしかない。

個人的には、I2Cは嫌いだ、ポートの制御が面倒で複雑だし、速度もあまり速くなく、
デバイスによっては、特別なケアが必要な場合もある。
SPIはもっと簡単で高速だ、デバイスを沢山繋げるとポートを消費するけど、少なく
とも、DI、DOは共通化できるので、デバイスセレクトのみで、I2Cのように、
ポートの入出力がこまめに変化したりしない。
2本の制御線でやりとりするなら、より良い方法が他にあると思う、しかしながら、業
界標準的な位置づけになると、規格がはびこってしまい、どれもがI2C通信となる
困った状況だ・・・
ここまで、I2Cが流通してしまうと、規格が悪いとか言ってられなくなるのも事実で、
とりあえず、ソフト制御のI2Cクラスを実装する事とした。

とりあえず、手持ちのI2Cデバイスでテストしてみた、今回はDS1371を選んだ
DS1371は32ビットのバイナリーカウンターを内臓したRTCで、以前に、マキ
シム社にサンプルをもらって評価したデバイスの余りで、RTCの中でも構成としては
使いやすいデバイスである。(32ビットのカウンターが基本で、1.7V~5.5Vで動作)
※但し、購入するとなると、安くは無い事が判る、もっと複雑で扱いが面倒な、秒、分、
時、日、月、年などで構成されたタイプの方が安い。
degikey の場合で、377円
※1個100円くらいだと助かるのだが・・(R8Cより高いので違和感があるのか)
以前にもRXにこのデバイスを接続した時のブログで紹介したが、時間管理のデバイス
では、バイナリーカウンターがあればそれで十分で、他の構成は、使いにくいだけで、
最悪だと思う。
結局、まっとうな時間管理が必要な場合は、秒単位にシリアライズしなければならない。
※ハードを設計する人が、ソフトの都合をまるで判っていない例の一つだと思う。

さて、I2C専用ハードの場合と違って、プロトコルを細部まで理解する必要がある。
※ここで、大きなミスは、ネットにある情報が、自分の知りたい部分以外が多くて、
重要な部分を理解しないまま、「多分こうだろう」的な感じで、実装してしまい、
それが悪くて、中々通信が出来なかった事。
最終的には、DS1371の英文マニュアルの一部を読んでいて気がついた・・・
スレーブからのリード時のACKの振る舞いが判ってなかった・・・

IMG_0747

I2C関係の構成としては、以下のようになっている。

common/i2c_io.hpp  --->  I2C基本制御クラス
common/ds1371_io.hpp  --->  DS1371制御クラス
I2C_test/main.cpp  --->  SCL、SDA、ポート定義クラス

ポートの定義

// DS1371 I2C ポートの定義クラス
// P1_B7: SCL
// P4_B5: SDA
struct scl_sda {
    void init() const {  // オープン・ドレイン設定
        device::POD1.B7 = 1;
        device::POD4.B5 = 1;
    }
    void scl_dir(bool b) const { device::PD1.B7 = b; }  // SCL 方向 (0:in, 1:out)
    void scl_out(bool b) const { device::P1.B7 = b; }   // SCL 出力
    bool scl_inp() const { return device::P1.B7(); }    // SCL 入力
    void sda_dir(bool b) const { device::PD4.B5 = b; }  // SDA 方向 (0:in, 1:out)
    void sda_out(bool b) const { device::P4.B5 = b; }   // SDA 出力
    bool sda_inp() const { return device::P4.B5(); }    // SDA 入力
};

このクラス定義は、少し冗長だけど、I2CのSCL、SDAをどのポートに対応さ
せるかを実装する。
※詳細なテストはしていないが、オープンドレインポートじゃなくても動作すると思う。

デバイスの制御
「ds1371_io.hpp」はテンプレートクラスで上のポート定義を渡す。
内部的には、ポート定義は、i2c_io クラスにそのまま渡している。
デバイスの制御では
・初期化 ---> DS1371 の制御レジスターを設定する。
・リード ---> 32ビットカウンターの読み出し(time_t で受け渡しをする)
※RTC特有の部分として、確率的に読み出すタイミングでカウントアップが行われた際、
全てのカウンタ値が確定する前に不正な値を読み出してしまう場合があり、複数回読み出
して比較する事でこれを回避している。
・ライト ---> 32ビットカウンターへ書き込み(time_t で受け渡しをする)
※他のデバイスを制御するなら、このソースを参考にすれば、少しの手間で、色々なI2C
デバイスに対応できると思う。

I2C制御
基本的に、初期化と、読み出し、書き込みなどの関数しか無い。
また、7ビットのデバイスアドレスしか対応していないが、10ビットのアドレスを使って
いるデバイスは少ないようなので問題無いだろう。
一応、100KBPS、400KBPS を切り替えられるようにしてあるが、ソフトウェアーループによる
クロック生成なので、あまり正確ではなく、実際より10%以上遅いと思われる。

I2Cの仕様として、スレーブデバイスが、内部動作の影響で、応答出来ない場合に、SCL
を強制的に「0」にする事で、動作を遅延させる事が出来る。
初期設定では、その「待ち時間」の最大を「200」マイクロ秒としているが、

//-----------------------------------------------------------------//
/*!
    @brief  スレーブデバイスの「待ち」時間の最大値を設定
    @param[in]	busy	待ち時間(単位マイクロ秒)
*/
//-----------------------------------------------------------------//
void set_busy(uint16_t busy) { busy_ = busy; } 

関数で、大きな待ち時間を設定できるようにしてある。
この待ち時間を越えてSCLが引き伸ばされると、各関数はストールして、falseを返す。

------
libc の時間関数はかなり巨大な為、必要最小限な実装をした関数群を用意した。
・うるう年の補正、曜日の計算など含まれている。
・time_t から tm 構造体、双方向(シリアライズ、デシリアライズ)で変換可能
・タイムゾーンは+9時間(東京)にしてあり、ハードコーディングしてある。
・time_t はグリニッチ標準時間で管理している。

common/time.h
common/time.c

GitHub R8C

R8Cライターモジュール作成

R8Cの書き込みプログラム完成を受けて、とりあえず、まともなライターを製作してみた。
今回はライターモジュール側の製作です。

売られている比較的安価なR8C用ライターは、USBシリアルモジュールが乗っている。
しかし、シリアルモジュールは単独で動作させる事も多いので、別にしたいと思う。
これは、前回のブログの通りです。

一応、ライターモジュールを KiCAD で回路を引いてみた。
6Pのスイッチを設けて、「WRITER」モードと、「RUN」モードを切り替えている。
TXD 信号の扱いが、WRITE/RUN で代わるので、その切り替えも兼ねている。
「RUN」モード時、シリアルの入出力だけが、具体的に繋がる機能だけなので、LEDや、A/D
用のボリューム、スイッチなど取り付けるつもりだが、今回は、ケースの加工が面倒なので
今後の課題とする。
今回、TEXTOOL のICソケットを使って、ライターらしく仕上げてみた。

R8C_Writer
※KiCAD の関係ファイルは、GitHub にプッシュしてある。

IMG_0742

RXD、TXD は、どちら側から見ているのかで、逆の意味となる、今回は、シリアルモジュール側から見た、
端子名をそのまま使っているので、方向に十分注意してほしい。

FTDI:RXD <--- R8C:TXD
FTDI:TXD ---> R8C:RXD

又、TXD 端子は、ライターモードでは、P1_6 に接続している、これは、R8Cの書き込みモードではそ
のようになっているもので、何故そうなのかは不明。

IMG_0744

-----
FTDI 社の FT231X だけど、Windows7 ではデバイスドライバーのインストールが必要だったが、OS-X で
は、そのまま認識して、直ぐに使えた、ちょっとビックリ・・

FTDI 社のデバイスは、中国製のコピーデバイスが安価で出回っており、それを掴むと、純正ドライバー
では、コピーチップと認識して、弾くらしいので注意が必要。

R8Cフラッシュプログラムとりあえずベータ完成

まだ、機能的に欠けている部分もあるし、あまりにヒドイ実装の部分もあり、
手直しをしたいのだがーとりあえず、書き込み、読み出し、消去など、一通り
の機能は揃っているので、この辺で、ライタープログラムは、止めて、
R8C本体のプログラムなどを充実したい感じです〜
※ヒドイ実装とは、設定ファイルのパースとか、機能が分離出来ていない
モトローラーファイルフォーマットの入出力など。

※その為、バージョンは「0.72b」としている。

Renesas R8C Series Programmer Version 0.72b
Copyright (C) 2015, Hiramatsu Kunihito (hira@rvf-rc45.net)
usage:
r8c_prog[options] [mot file] ...

Options :
-d, --device=DEVICE		Specify device name
-e, --erase			Perform a device erase to a minimum
    --erase-all, --erase-chip	Perform rom and data flash erase
    --erase-rom			Perform rom flash erase
    --erase-data		Perform data flash erase
-i, --id=xx:xx:xx:xx:xx:xx:xx	Specify protect ID
-P, --port=PORT			Specify serial port
-a, --area=ORG,END		Specify read area
-r, --read			Perform data read
-s, --speed=SPEED		Specify serial speed
-v, --verify			Perform data verify
    --device-list		Display device list
-V, --verbose			Verbose output
-w, --write			Perform data write
    --progress			display Progress output
-h, --help			Display this

MacBook Pro でシリアル接続して、R8Cの書き込みや、消去、を色々試したけど、
とりあえず、何の問題も無い、十分に使えると思う。
自分としては、俺俺フレームワークでGUI版も作りたいけど、それはまた、後で
考えるとして・・

ソースコード一式は、GitHubにプッシュ済み、一応 MSYS2 でコンパイルと簡単な動作は
確認済みなので、Windows でも使えると思う、MSYS2 の clang では、シリアル制御関係の
ヘッダーが無い為、コンパイル出来ない、Makefile 内で、MSYS2 環境では gcc を使うよ
うにしてある。

ifeq ($(OS),Windows_NT)

R8C関係 GitHub

さて、R8Cは、メモリーが64K使える事が判ると、SDカードの操作も楽に出来そうだし、
色々と夢が膨らむ〜

R8C本体に行く前に、先週、買ってきたもので、ちゃんとしたライターを作っておこうと思う。
以前の書き込みハードはあまりにショボイ・・
色々考えた末、USBシリアル部分と、R8Cのライター部分を分離した構造にした。
USBシリアルは、色々な場合に使う事が多いので、分離しておけば、便利に利用できるだろう。

まず、USBシリアル変換、モジュールが比較的安く売られているので、それを使っても良いが、
小さいケースに収めて、綺麗に作りたかったので、部品をバラで買ってきた、デバイスは
FTDI の FT231XS、単品だと安くて210円だった。
SSOP20 ピンのハンダ付けが、少しだけ厳しい・・
それと、マイクロUSBコネクター、調べると、ミニUSBコネクターより、マイクロUSB
コネクターの方が、抜き差しの耐用が高いらしい。
また、スマホがマイクロUSBコネクターを採用している事が多く、ケーブルも調達がしやすい。
RTS、CTS、DTR、DSR など制御線を出しておきたいので10ピンのコネクターを出す。
それと、5V、3.3Vを切り替え出来るようにしておく。
3.3Vは、ICにレギュレーターが内蔵されているけど、取り出せる電流が少ないので、別途
三端子レギュレーターを載せる。

ケースは、「タカチ」の「TW4-2-6」を使った。

以前から、採用している構造で、非常に具合が良いのだが、L型の10ピンコネクターを横に出す
構造にして、反対側は、マイクロUSBコネクターの先端で受けるようにする事で、基板を抑える
事が出来、シンプルに収まる。

IMG_0733
IMG_0734
IMG_0735

一応KiCADで回路を引いた、この関係ファイルもGitHubにプッシュしてある。
SerialModule

IMG_0740
IMG_0741
IMG_0739

さて、これでシリアルモジュールが出来たー、次は本命のR8Cライターなんだけど、これは次回にでも~

R8C に新たなフラッシュ領域見つかる〜

100円で買えるマイコンとして、売られているR8Cですが、データシート上は、

RAM256バイト
フラッシュ2Kバイトです

ところが、実際は・・

RAM1366バイト
フラッシュ32Kバイト

まで使えると判り、小躍りしていました~

OS-Xでも、R8Cの開発環境を整える目的で、フラッシュ書き込みプログラムを開発していましたが、
その過程で、R8Cのフラッシュ領域には、まだ拡張された領域があるのでは無いかと思い、良く調べてみました。

すると・・・

0x10000~0x17FFFに、さらに32Kバイトの領域があり、実際に使える事が判明しました。

つまり、フラッシュ2キロバイトとは、見せ掛けの容量で、実際には64キロバイトの領域があります。
※市場では、容量が多く(最大16KB)値段が高いデバイスが存在しますが、基本的に中身は全て同一
なのでしょう・・

考えるに、「ATtiny2313」など100円マイコンと同じレンジのマイコンがラインナップに無いので、
それに相当するデバイスを揃える為に既存のデバイスを再利用したのだと思います、その時、本当の
RAM、ROM容量では、他のラインナップと競合してしまうので、苦肉の索として、100円に釣り
合うと思われる小さい容量で売り出したのでしょう・・

現在のところ、最強の100円マイコンだと思います(笑)

さて、領域がある事が判ったのですが、R8Cのポインターは16ビットなので、基本的には64KB以上の
領域にアクセスするには、工夫が必要です、R8Cは20ビットのアドレス空間までアクセス出来る仕組みが
あるので、アセンブラなら、下駄を履かせてアクセスする方法がありますが、 gcc ではどうでしょうか?
調べてみると、64KB以上のアドレスの関数をコールする為の仕組みがあり、それを使う事で、拡張領域を
使う事が割と簡単に出来るようです。

まず、リンカースクリプトに拡張領域を追加します。

MEMORY {
    RAM (w)  : ORIGIN = 0x00300, LENGTH = 0x00400
    ROM (r)  : ORIGIN = 0x08000, LENGTH = 0x07FD8
    EXT (r)  : ORIGIN = 0x10000, LENGTH = 0x08000
    VVEC (r) : ORIGIN = 0x0FED8, LENGTH = 256
    FVEC (r) : ORIGIN = 0x0FFD8, LENGTH = 40
}

次に、セクション領域を宣言しておきます。

  .exttext   :
  {
    *(.exttext .stub .exttext.* .gnu.linkonce.t.*)
    KEEP (*(.exttext.*personality*))
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
    *(.interp .hash .dynsym .dynstr .gnu.version*)
  } > EXT =0

これで、ソースコードで拡張領域のセクション指定すれば、通常領域から押し出す事ができます。
※ADC_testで main 関数を押し出してみました。

__attribute__ ((section (".exttext")))
int main(int argc, char *argv[])

コンパイル、リンクも普通に通り、出来上がったバイナリーをデバイスに書き込んでみましたが、普通に動きます。

Motolola Sx format load map:
  0x008000 to 0x009225 (4646 bytes)
  0x00FF00 to 0x012DB6 (11959 bytes)
  Total (16605 bytes)

R8C フラッシュプログラム

OS-X 用に R8C 用 gcc もビルド出来た事だし、フラュシュライターを実装する事にした。
これが出来れば、MacBook などで R8C の開発が全て完結する〜

R8C/M120AN、R8C/M110AN の場合、mode 端子を Low にしてリセットをかけると、ブートモードになり
シリアル通信で、コマンドとデータを送る事で、内部フラッシュメモリーをプログラミング出来る。

尚、ライターは簡単に自作出来るが、出来合いもあり、これがお勧めかも・・
R8C/M12A ライタ&LEDキット【MR8C-TRM】

通信プロトコルは、以下のリンクから資料を取得した。
R8C/1x、2xシリーズ 標準シリアル入出力モードプロトコル仕様書
R8C/Mxシリーズ、LAxAグループ 標準シリアル入出力モードプロトコル仕様書

最初のハードルは、OS−Xって、どうやって「シリアル通信」すんの?
だった、でも、ちょっと調べたら、どうやらPOSIX準拠のシリアル通信APIをサポートしており、要はLinuxと同じ
コードで、大丈夫のようだったー、これは、MinGWでもサポートしているようなので、簡単にマルチプラットホーム
に出来るおまけ付きだ!
※ただ、デバイスファイル名は、OS依存なのだと思う。

とりあえず、FTDIのシリアルUSBブリッジを接続したら、/dev/tty*にそれらしいデバイスが認識される。

crw-rw-rw-  1 root  wheel   18,   6  5  7 21:18 /dev/tty.usbserial-A600e0xq

これを使って、簡単な通信を行いテストすると、普通に動作する〜
「rs232c_io.hpp」に必要な機能を盛り込んだ〜

それではと、色々ガリガリプログラムを作成して、とりあえず、出来上がった〜
まだ機能追加中で、中途半端な状態ではあるけれども、一応動作しているようだ。
GitHubで公開している。

仕様的には、千秋ゼミで公開されているr8cprogを元にしている。
※このプログラムをダウンロードするには、登録が必要。

ただ、C++ でスクラッチから組んだものなので、動作は、オリジナルとは違うので注意。
俺俺フレームワークとして、「file_io、string_utils、sjis2_utf16」など同梱している。

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

現在は、デバイス名は無視されています。
ポートは、何も指定しないと、上記のデバイスパスが使われるようにしてあります。
また、コンパイルには boost が必要です。

Windows 環境では、テストされていません。
Msys2環境でコンパイル、実行を試しました。
※select の挙動が、OS−Xと違っていて、少し悩みましたが、とりあえず、問題無く動作するようになりました。

R8Cコンパレーターのテスト

R8Cの機能を一通り実装、テストしている。

今回は、コンパレータークラス。

テストプログラムでは、ICOMP3、IVREF3 を電圧比較をして、その結果、LEDの点滅速度が変わると言うもの。
※実験では、REF側に、VCCを1/2に分圧し、COMP側にボリュームを接続、確認した。

R8Cには2チャネルのコンパレーターがある、機能的には、単純な物だが、割り込みを使った時に、実行する
処理をどのような構成にするか、考えてみた。
以前にタイマークラスを実装した時に、タイマー割り込みから、何らかの処理を実行できるように、

void (*task_)(void);

実行ポインターを用意した、ところが、こんな事をすると、コンパイラは、「task_」で何が実行されるか不明
な為、メイン側で利用しているライブラリーのワークを全てスタックに積むようコードを生成する。
※タスク内で、メイン側と同じライブラリーを使った場合に備える為で、強引な方法だが、動作は正しい。
しかし、これでは、スタックが直ぐに限界を超え深くなるのは容易に想像出来る、その為、「実行ポインター」
でのタスク管理は不採用とした。

そこで、コンパイル時に静的な関数が実行できるような仕組みを追加した。
関数オブジェクトを使い。

class task {
    public:
    void operator() () {

    .....

    }
};

()オペレーター内に、実行したい手続きを実装して、それをテンプレートの引数とする。
関数オブジェクト内の手続きが何も無い場合、コンパイラは、その部分一切を削除する。

template <class TASK1, class TASK2>
class comp_io {

    static TASK1 task1_;
    static TASK2 task2_;

    public:
        static INTERRUPT_FUNC void itask1() {
            task1_();
            WCB1INTR.WCB1F = 0;
    }

    .....

};

これなら、コンパイラが、テンプレートクラスを展開する時に、割り込み内手続きを考慮するので、ライブラリー
で使われる変数の退避も最小限となる。

COMP_test ソースコード

-----
そして、R8CのI/O定義は、ほぼ全てを網羅した。

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にソースをプッシュしました。