あなたの天然記念物
ホーム更新雑談Perl鉄ゲタランドナーコースガイド自転車Linuxリンク経歴連絡先
LPCXpressoでI2C液晶表示 (2011.04.02)

将来のポケコンを夢見て、まずは液晶表示をしてみました。
マイコンLPCXpressoは秋月電子通商から購入。
超小型I2C液晶はストロベリーリナックスから購入。

パソコンを用意。
LPCXpressoの開発環境はWindowsXP以降で動作、サブPCはWindows2000なので使えない。メインPCはAmigaOSなので(笑)。部屋に転がっている待機中のパソコンがBIOS画面を表示できる所まで確認。PCのスペックはAMD Athlon 64 X2 6000+(3GHz) メモリ1GB。AmazonでWindowsXP Home Edition SP3を購入、インストール。NOD32(ウイルスチェック)の追加ライセンスを購入、インストール。LPCXpressoの開発環境をインストール。

Googleでわかる内容は全部省略。

液晶にコマンドを送った直後は液晶が処理中で、次のコマンドを受けられないから時間を空けます。マイコンのクロックを下げても、一定の時間を空けられるようにSysTick_Handlerで1ミリ秒単位をカウントします。液晶へのコマンドは「コントロール」と「データ」の2種類があってコントロールの各バイトの前に0x00を送信、データは0x40を送信します。著名な液晶パネルはRS信号で区別しているそうですが、I2CはRS信号がなく、それに代わるものです。コントロール送信後、液晶の最長処理時間は200ミリ秒、こんなに長いのは1種類だけですけれど、全コントロールで送信後、200msウエイト(SysTick_Handlerで200カウント)入れます。データ送信後は1ミリ秒以下ですが2ミリ秒ウエイト入れます。

マイコン起動直後にSysTickを設定していて、これの安定時間が不明なのと液晶の起動が40ミリ秒必要なので1000ミリ秒(1秒)。もうウエイトばっかり。この辺のことは英文マニュアルに目を通すと掲載されていると思いますけれど、私に把握は無理っていうか、1秒単位でゆっくり動作してくれたらいいと思ってますから(笑)。

LPCXpressoは備え付けのLEDが3個あって、そのうちの一つLED2が電源ONと同時に点灯します。これ、うるさい(無音だけど)ので消灯させます。それにはLED2と接続してるピンを制御します。

メーカー提供の回路図を追跡、LED2はポート0/ピン22に接続されています。初めに、ポート0/ピン22を「機能番号0/プルアップ/オープンドレインなし」で設定します。これでGPIO(一般的な0/1動作)、ピンと電源を抵抗で接続されました。あ「プルアップ」の事です。次にポート0/ピン22の入出力を「出力」に最後に0を出力してLED2は消灯します。※ホントは0の出力前から消えるけれど、マイコンの初期状態の仕様に依存するようなコードは書きません。将来、この処理を別なタイミングで使うようにした際に、0出力が抜けていると期待した動作にならなくて頭抱えるからね。

I2C用のピンを設定します。I2C0、I2C1、I2C2のうち私はI2C1を使います。I2C1のピンの位置を追跡した上で、ポート0/ピン0を「機能番号3/トライステート/オープンドレイン」ポート0/ピン1を「機能番号3/トライステート/オープンドレイン」機能番号3でI2Cになり、プルアップ抵抗は外付のためトライステート、あとマニュアルが「オープンドレインにしろ」と言ってた(笑)。

液晶に初期化のコントロールとデータを送信して、おしまい。

動作させるとこんな感じ

関数mainから戻った後は?→mainを呼んだ初期処理の中でインコアループしてます。他にすることもないし、問題ナッシング。

最後にソースをご覧ください。
2010.11.25 下記のソースからLCD処理を抜き出してライブラリ化してみると、何だかAPIが変だ。 それと言うのも、1ポート(8ビット)の中で1ビットだけフラグを変更したい場合に、 他のビットを0のまま希望のビットを1にすると他のビットが0になってしまい、意図した動作と異なってしまう。 他のビットを変化させずに希望のビットだけ1にするには、他のビットを最後に出力した時の0/1を記録しておき、希望のビットと合成してポートに出力しなくては。 という事は、記録や合成の仕事はライブラリが引き受ける訳だから、8ビットまるごと指定するインターフェースから、1ビット単位で指定するものに変更を。 そんなインターフェースは、おなじみだなー。思い出す事でもなく、それはAmigaOSのAPIそのまんま。 OSのサイズ(マスクROM)が供に512KBと同じなんだよね。 2010.11.28 AmigaのAPIに似せた形式にしてみた。こんな奴↓です。   TAGITEM_TYPE testicon[] = {     {SIL_ICON, TRUE},     {TAG_END,  TAG_NONE}   }; これ、TAG形式といってlong値(32ビット)を2個1組のペアにしたものを配列に持ち、1個目(SIL_ICON)はキー、2個目(TRUE)は値です。 末尾のキーはTAG_ENDというルールがあるため、不定長の配列を扱えます。 処理速度を気にする方もいらっしゃると思いますけれど1985年発売のAmiga(CPUクロック7.14MHz、メモリ512KB)で採用している形式です。 このマイコンは72MHzで動作していますからAmigaの10倍と余裕釈々ですね。 関数のパラメタに並べると、OSのバージョンが上がってパラメタを追加してしまうと、 旧バージョンのOS用のアプリケーションが新バージョンでパラメタ不足で誤動作する。 構造体で渡すと構造体のサイズが変わってしまい、パラメタと同様になる。 ※某うへぇなOS(サブルーチン集?)では構造体の第一メンバが構造体のサイズという腹抱えて笑ってしまうAPIがあります。 AmigaのアプリケーションとOSとの構造体は、OSが初期値入りの構造体を確保してアプリケーションに渡す暗黙のルールがあります。 C++のnewとdeleteみたいな奴が、構造体ごとに用意されています。 例えば、構造体Windowの場合、確保はNewWindowで、削除はDeleteWindowです(そのまんま)。 この方法で将来、構造体のサイズが増えても、アプリケーションはそのまま動作できます。 あと液晶表示のコントロールに入れるウエイトを1μS単位にしました。 SystemTickは1ms単位を1000分の1にして超高速tickに。 ただし、ソースの前提クロックは100MHzなので1.39μS単位じゃないかと思う。 もっとシビアなタイミング制御が必要になったら、正確な数字を求めるのとマイコン起動時に自分のクロックを計測する方法を考えよう。 このページをご覧の皆さんは、上のプログラム→下のプログラムと見ていただきましたけれど、 Amigaで下→パソコンのソフトハウスで上、と逆順を経験すると、パソコンの職業プログラマは10年と持ちませんでした(実話)。 マイクロソフトがAmiga買収してAPIがスッキリさわやかになったら、パソコンの職業プログラマに転職してもよいな。 2010.12.03 RTC(Real Time Clock)で遊び思わぬ結果が。これは勉強になった。 RTCは、俗に言うカレンダー/時計機能です。 LPCXpressoでは腕時計用の32,768Hzの水晶発振子(クォーツ)を使って正確な1秒を生成、電源が独立できる専用回路でカウントしています。 一般電源とRTC専用電源との自動切換付きなので専用電源なしでもUSB(LPCLink)給電中は動作しています。 ちなみにCPUのクロックは製品によって仕様が変わるから使うものではありません。 マニュアル読むとたったの1μAという極低消費電力でカウントできる回路らしい(要専用電源)。 アースに触れていない人なら50μA位の電流は流れて当然だから、クオーツに触れるとカウント失敗しますので触れないように。 このオーダー(極低消費電力)なら回路は高インピーダンス(えーと5MΩ程度かな?)で動作しているから、誘導やノイズに弱いハズ。 キーボードやマウスのUSBケーブルをLPCXpressoの上に乗せると、これまたカウント失敗します。 どーしてこんな事がわかったかと言うと、SystemTickのカウントを「RTCの1秒割込」で差分求めて液晶にリアルタイム表示させてみたから。 クオーツの頭を指で触れただけで1,001→1,300や、指でつまむと1,001→2,000とか、USBケーブルを乗せると1,001→1,400とかカウントが変化する。 SystemTickは1ms設定なので1,000カウント/sのはずが、基準の1sであるRTCが遅くなって1,300カウントとかになった訳です。 ちなみに、この実験で初めてSystemTickが正しく1msだと判明しました(苦笑)。 腕時計のクオーツだから金属ケースに収まって初めて動作仕様を満たすから、きちんとRTCを使うためにはハウジング(?)も注意しなくちゃ。 それにしても和気あいあいのmbedに比べ、LPCXpressoのほうは鉄ゲタ状態だなぁ。これでも昔の開発環境から見たら天国なのでしょう。 mbedとLPCXpressoで迷わずLPCXpressoを選択、それがAmigaクオリティ<おまえは京浜急行か(正しくは京急クオリティ) だってROMサイズ512KB、英文マニュアル、開発が面倒(あ。言っちゃった)…Amigaユーザならわかるハズ、わかってくれるといいな、わかってください。 時代が進む同時にデジタルは連続的にアナログへ近づいている。<あこがれのアナログエンジニア様ブログ風(岡山 努 様) 2010.12.14 プログラムのリリースバージョンを入れて動作させると、時計表示が最初のまま更新されないバグが露見。 最初の表示まで正常なので、1秒後のウエイトか、2回目の表示のバグ。 ウエイトは「RTCの1秒割込」処理で行なっていて、ウエイト側はグローバル変数の変化をインコアループで監視、割込ルーチンがグローバル変数を更新。 あ、そうか。RTCの処理を独立したソースに移行、管理用の構造体を用意した際にvolatileを外したんだった。 volatileって不揮発メモリか何かだっけ。そりゃNVRAMのVの字だ。Non Volatile Random Access Memory、惜しい。 volatileなしの変数をインコアループで監視したら、コンパイラが最適化して無限ループにする。なんじゃこりゃー(松田優作さんの声で)。 これまで28年間、PC-8001(パソコン)からS-820/80(ベクトル型スパコン)のレンジでプログラミングしてきたけれど、これほどヒドい話は初めてだ。 CGレンダー(DirectXみたいな奴)をFORTRANでコーディングするよりヒドい(笑)。 もう頭に来た。 AmigaOSのAPIモドキを用意して、徹底的に読めるソースにしてやる。名前はAmigaOS…はマズいので「ksrk」に。KickStart Rom Kernelの略です。 CPUクロックがたったの7MHzでウソみたいに速い完全マルチタスクなAmigaで使ってるAPIを10倍速いマイコンで使えば鉄壁だ。 具体的にはこんな感じ。 RTCはksrkからシグナルのフラグを割当ててもらい、アプリに返す。 アプリはそのフラグを使ってksrkにウエイトを要求。ksrkはフラグ監視。 RTCの割込でシグナルのフラグ立て。 ksrkがフラグを倒し、アプリに戻る。 この方式のミソはウエイト要求がフラグ渡しだから、複数のフラグを指定できる点にあります。 今後、RTC以外のウエイト要求があれば、それも合成したフラグで「同時にウエイト」できます。 この方式が1985年発売のパソコンで普通に行なわれていたんです。25年も昔の話なのよ。 2011.03.11 ソリトンウエーブから512KBライセンスを購入しました。 LPCXPressoはハードが512KBのROMを持っていても、開発環境が128KBまでの制約があります。 512KBを全部使えるライセンスを買いました。約50,000円です。 色々なサイトを訪れてみますと、ライセンスを買わないで512KBを使おうとがんばっている人もいて、色んな考え方の人が居るなぁと思います。 以前買った開発環境(?)はMSDN、その前はSAS/C、これはAmiga用商業Cコンパイラです。 MSDNは当時のOS全部入りで100,000円弱、SAS/Cは80,000円だったかな。 SAS/Cのおかげで「出す物(お金)を出しゃあ、快適な環境を得られる」というのを体が覚えましたから 基本的に「値段を付けているソフトは買うもの」と考えています。 あと趣味の世界でお金をケチらないというのもあります。 その分、ボロアパート、着たきりスズメ、無免許(車なし)という点で異様なくらいケチっています(笑)。 2011.04.02 512KBライセンスの確認をしてみました。 プログラムを512KB作る技量はありませんので音声データを400KB程度用意して、DACで再生させました。 たこルカ版「ぽっぴっぽー」をモノラル/7kHz/符号なし8bitsのrawデータをcの配列(ソース)にしてDACのサンプルプロジェクトに追加しました。 サンプルプロジェクトはDMAを使っていたけど取っ払って7kHzウエイト&DAC直出しで。 できれば、バイナリをそのままプロジェクトに追加、ファイル名をラベルにして名称解決してくれる仕掛があったらうれしいな。 でも、組み込みでデータ400KBを突っ込む奴がおかしい(笑)。 90秒で再生が終わるとつまらないので、末尾まで再生したらループしてます。 ただ先頭から再生しても芸がなく末尾ときれいにつながるループポイントを探して、ピンポイントでループ掛けてます。 今どきな人はループポイントなんて知らないだろうな。再生メモリが極小だったころのハイテク(苦笑)です。はい、Audio Master使いでした。 詳細はヤホーでググってください←最近は、こういう言い回しが流行ってるんだって? 「googleってください」でいいじゃないか。←漢字、ひらがな、カタカナ、アルファベット、ギリシャ文字、キリル文字(ロシア)を使える日本人に敵はいない。
<HR>
LCDでRTC表示
<HR>
/*
===============================================================================
 Name        : main.c
 Author      : 
 Version     :
 Copyright   : Copyright (C) 
 Description : main definition
===============================================================================
*/

#include "lpc17xx_pinsel.h"
#include "lpc17xx_rtc.h"
#include "system_LPC17xx.h"

#include "ksrk.h"

#include "core_cm3.h"
#include "tags.h"
#include "sil.h"
#include "rtc.h"

#include <stdint.h>
#include <string.h>

void get_time(RTC_TYPE *pRtc, uint8_t *pBuff)
{
    TAGITEM_TYPE get_time[] = {
            {RTC_YEAR, 0},
            {RTC_MONTH, 0},
            {RTC_DAY, 0},
            {RTC_HOUR, 0},
            {RTC_MINUTE, 0},
            {RTC_SECOND, 0},
            {TAG_END, TAG_NONE}
    };
    rtc_getvalue(pRtc, get_time);

    // printf("%1d/%02d/%02d %02d:%02d:%02d", ...);
    //
    // 0123456789012345
    // Y/MM/DD hh:mm:ss
    uint8_t *p = pBuff;
    p = ltoa(get_time[0].value % 10, p);
    *(p++) = '/';
    p = set_2digits(get_time[1].value, p);
    *(p++) = '/';
    p = set_2digits(get_time[2].value, p);
    *(p++) = ' ';
    p = set_2digits(get_time[3].value, p);
    *(p++) = ':';
    p = set_2digits(get_time[4].value, p);
    *(p++) = ':';
    p = set_2digits(get_time[5].value, p);
}

int main(void)
{
    ksrk_init();

    TAGITEM_TYPE inittags[] = {
            {SIL_SDA,            PINSEL_PIN_0},
            {SIL_CONTLAST,        45},
            {SIL_DISPLAY,        TRUE},
            {SIL_CURSOR,        TRUE},
            {SIL_POSITION,        TRUE},
            {SIL_ICON_BATT_BASE,    TRUE},
            {SIL_ICON_BATT_1,        TRUE},
            {SIL_ICON_BATT_2,        TRUE},
            {SIL_ICON_BATT_3,        TRUE},
            {TAG_END,            TAG_NONE}
    };
    SIL_TYPE *pSil = sil_init(inittags);

    sil_return_home(pSil);    // change write to DDRAM

    uint8_t message[] = {
        0xce,    //'P',
        0xdf,    //'o',
        0xaf,    //small tsu
        0xcb,    //'P',
        0xdf,    //'i',
        0xaf,    //small tsu
        0xce,    //'P',
        0xdf,    //'o',
        0xb0,    //kana '-'
        ' ',
        '>',
        0x1e,    // omega
        '<',
        0x00    // EOD
    };
    sil_write_string(pSil, message);

    uint8_t linebuff[32];
    memset(linebuff, 0x00, sizeof(linebuff));

    TAGITEM_TYPE rtc_items[] = {
#if 0
            {RTC_YEAR,        2010},
            {RTC_MONTH,        12},
            {RTC_DAY,        13},
            {RTC_HOUR,        3},
            {RTC_MINUTE,    5},
            {RTC_SECOND,    0},
#endif
            {TAG_END,        TAG_NONE}
    };
    RTC_TYPE *pRtc = rtc_init(rtc_items);

    uint32_t sig_wait = 0x00000000;

    TAGITEM_TYPE rtc_sig[] = {
            {RTC_SIGBIT,    0x00000000},
            {TAG_END,        TAG_NONE}
    };
    rtc_getvalue(pRtc, rtc_sig);
    uint32_t sig_rtc = rtc_sig[0].value;
    sig_wait |= sig_rtc;

    for (;;)
    {
        uint32_t sig_recive = ksrk_wait(sig_wait);
        if (sig_recive & sig_rtc)
        {
            get_time(pRtc, linebuff);
            sil_locate(pSil, 0, 1);
            sil_write_string(pSil, linebuff);
        }
    }

    return 0;
}