STC8G1K08A 超小型 349 バイト UART hello world デモ

関連記事:

前回の投稿 STC8G1K08A 最小限 UART hello world デモでは、FwLib_STC8 を使って STC8G1K08A 向けに 2.2 KB 強のシンプルな UART hello world アプリケーションを作成する方法を紹介しました。

この例は非常にシンプルですが、SDCC の機能セットに起因する大きな問題があります: 現時点で SDCC はリンク出力から未使用関数を取り除くことをサポートしていません。 つまり、UART ライブラリのどれか 1 つの関数を使うとライブラリ全体がリンクされ、コードサイズのオーバーヘッドが大きくなります。

これを解決するために、コードをインライン化してある程度最適化できます。

main.c
#include "fw_uart.h"

/* ボーレートを一箇所にまとめ、Timer1 のリロード式を分かりやすくする。 */
#define UART1_BAUD_RATE 115200UL

/* ランタイムコードを呼ばずにライブラリの SYSCLK 計算を再現する。 */
#define UART1_SYSCLK_HZ (__CONF_FOSC / ((__CONF_CLKDIV == 0) ? 1UL : (unsigned long)__CONF_CLKDIV))

/* UART1 モード 1 は Timer1 のオーバーフローを 4 で割ったものをボーレートクロックとして使う。 */
#define UART1_TIMER1_RELOAD (65536UL - (UART1_SYSCLK_HZ / (4UL * UART1_BAUD_RATE)))

/* メッセージ全体を 1 つのメモリ領域に置き、ライブラリがストリーム出力できるようにする。 */
static unsigned char uart_message[] = "Hello, world from UART1!\r\n";

static void UART1_SendString(unsigned char *str)
{
    while (*str)
    {
        SBUF = *str++;
        TI = 0;
        while (!TI)
        {
        }
    }
}

#define TICKS_MS (__CONF_FOSC / ((__CONF_CLKDIV == 0) ? 1UL : (unsigned long)__CONF_CLKDIV) / 9000UL)

static void delay_ms(unsigned int ms)
{
    unsigned int i;

    do
    {
        i = TICKS_MS;
        while (--i)
        {
        }
    } while (--ms);
}

static void UART1_Init(void)
{
    /*
     * 拡張 SFR バンクを開き、UART1 を起動する前に CLKDIV を書き込めるようにする。
     * B7 = 1: 拡張 SFR 領域へのアクセスを有効化。
     * B6..B0 = 0: 他のバンク切り替えとペリフェラルルーティング制御は変更しない。
     */
    P_SW2 = 0x80;

    /* 設定されたクロック分周比をレジスタ全体への直接書き込みで適用する。 */
    CLKDIV = __CONF_CLKDIV;

    /*
     * クロック分周器がプログラムされたので通常の SFR バンクに戻る。
     * B7 = 0: 拡張 SFR アクセスを再度無効化。
     * B6..B0 = 0: この最小限デモでは残りの P_SW2 制御を非アクティブに保つ。
     */
    P_SW2 = 0x00;

    /*
     * トリム値が調整された高速内部 RC 発振器バンドを選択する。
     * 0x03 はバンド選択ビットである IRCBAND[1:0] のみを保持する。
     * 設定値の上位ビットは破棄され、文書化されたバンドフィールドのみを変更する。
     */
    IRCBAND = (__CONF_IRCBAND & 0x03);

    /* その RC バンドに対応する電圧トリムバイトをロードする。 */
    VRTRIM = __CONF_VRTRIM;

    /* 高速 RC 微調整バイトをロードする。 */
    IRTRIM = __CONF_IRTRIM;

    /*
     * このデモは高速内部クロックで動作するが、低速 RC トリムフィールドもロードする。
     * 0x03 は有効な低速トリムビットである LIRTRIM[1:0] のみを保持する。
     * 未定義の値をそのレジスタに書き込まないよう上位ビットは強制的に 0 にする。
     */
    LIRTRIM = (__CONF_LIRTRIM & 0x03);

    /*
     * UART1 を標準の 8 ビット非同期モードにし、受信機を動作させる。
     * 0x50 = 0101 0000b。
     * SM0 = 0, SM1 = 1: UART モード 1(可変ボーレートの通常の 8 ビット UART)を選択。
     * SM2 = 0: マルチプロセッサアドレスフィルタを無効化し、受信したすべてのバイトを受け付ける。
     * REN = 1: UART 受信機を有効化。
     * TB8 = 0, RB8 = 0: モード 1 では 9 ビット目を使わないため、送受信の 9 ビット目の状態をクリアして開始。
     * TI = 0, RI = 0: 最初の文字の前に送信・受信ステータスフラグを両方クリア。
     */
    SCON = 0x50;

    /*
     * UART1 がカウント元にするボーレートジェネレータとして Timer1 を設定する。
     * 0x40 = 0100 0000b。
     * B6 = 1: Timer1 を 1T モードで動作させ、12 クロックごとではなくシステムクロックごとにインクリメントする。
     * B0 = 0: UART1 のボーレート源を Timer2 に切り替えず Timer1 のままにする。
     * B5..B1 = 0: このデモでは無関係な AUXR 機能を無効のままにする。
     */
    AUXR = 0x40;

    /*
     * Timer1 を FwLib の UART コードパスが期待するリロードモードにする。
     * 0x00 = 0000 0000b。
     * GATE1 = 0: Timer1 は外部 INT1 ピンではなく TR1 のみで開始・停止する。
     * C/T1 = 0: Timer1 は外部イベントではなくクロックティックをカウントする。
     * M1_1 = 0, M0_1 = 0: この STC8 ライブラリが UART ボーレート生成に使う Timer1 モードを選択。
     * このデモは Timer0 を使わないため、TMOD の Timer0 側半分もクリアする。
     */
    TMOD = 0x00;

    /* コンパイル時 Timer1 リロード値の上位バイトをロードする。 */
    TH1 = (unsigned char)(UART1_TIMER1_RELOAD >> 8);

    /* 最初のオーバーフローから正しいボーレートを使えるよう下位バイトをロードする。 */
    TL1 = (unsigned char)(UART1_TIMER1_RELOAD & 0xFF);

    /* UART1 がビットタイミングを導出できるよう Timer1 を開始する。 */
    TR1 = 1;
}

void main(void)
{
    /* ゼロ初期化された unsigned ループ変数はコンパクトな全範囲ディレイになる。 */
    unsigned int settle = 0;

    UART1_Init();
    
    /* NUL 終端のメッセージバッファ全体をローカルの文字列送信ヘルパーに渡す。 */
    while (1)
    {
        UART1_SendString(uart_message);
        delay_ms(1000);
    }
}

書き込み方法

stcgal を使った STC8G1K08A の書き込み方法の投稿をご覧ください。

シリアル出力の確認方法

次のコマンドで出力を確認します

monitor.sh
picocom -b 115200 /dev/ttyUSB0

メモリ使用量

memory_usage.txt
===== Memory usage summary for uart_minimal_size_demo =====
Internal RAM layout:
      0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00:|0|0|0|0|0|0|0|0|S|S|S|S|S|S|S|S|
0x10:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x20:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x30:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x40:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x50:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x60:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x70:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x80:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x90:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xa0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xb0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xc0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xd0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xe0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xf0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0-3:Reg Banks, T:Bit regs, a-z:Data, B:Bits, Q:Overlay, I:iData, S:Stack, A:Absolute

Stack starts at: 0x08 (sp set to 0x07) with 248 bytes available.
No spare internal RAM space left.

Other memory:
   Name             Start    End      Size     Max     
   ---------------- -------- -------- -------- --------
   PAGED EXT. RAM                         0      256   
   EXTERNAL RAM     0x0001   0x0020      32     1024   
   ROM/EPROM/FLASH  0x0000   0x015c     349     8192   
==========================================================

SDCC 4.2.0 #13081 の場合、349 バイトのフラッシュは前回の例の 2.2 KB と比較して大幅な改善です。

FwLib の関数をプロジェクトにそのままコピーするか(FwLib_STC8 の寛容な Apache ライセンス条項で許可されていますが、著作権表示を保持する必要がある場合があります)、あるいはコードをさらに圧縮するために完全に「インライン」化するかを個別に選択できることに注意してください。上記のコードでは両方を組み合わせることにしました。


同じカテゴリの類似記事をチェック: STC8 組み込み C/C++