2016/12/25

Raspberry Pi Model B+ でベアメタル UART 出力ユーティリティ化

前回、 親知らずの日記: Raspberry Pi 3 Model B+ でベアメタル UART 出力 では、 UART で受信した文字をエコーバックするプログラムを作成した。

プログラム内で任意の文字列を出力させるため、ユーティリティ化したい。

目的

デバッグ目的で UART に任意文字列を出力したい。

こんな感じで使いたい。

int main(void) {

    println("Hello, World!");

    error_code = function();
    print("error_code: ");
    print(error_code);
    println(".");

    return 0;
}

要素技術

  1. UART 出力
  2. '数値 -> 文字列' 変換

UART 出力

こんな感じになった。

uart.h

#ifndef __UART_H__
#define __UART_H__

/**
 * Mini Uart 送受信に使用するレジスタ。
 *
 * 書き込むと送信 FIFO にプッシュ,
 * 読み込むと 受信 FIFO からポップしてくれる素敵仕様らしい。
 */
#define MU_IO (*(volatile unsigned int *)0x3F215040)

/**
 * Mini Uart 送信 FIFO の状態確認をするための情報が入ったレジスタ。
 *
 * 6 ビット目 : Transmitter idle, アイドル状態か?
 *              1: アイドル状態, 0: ビジー状態
 * 5 ビット目 : Transmitter empty, 1 バイト以上送信受付可能か?
 *              1: 可能, 0: 不可能
 * 0 ビット目 : Data ready, 1 バイト以上受信しているか?
 *              1: 受信している, 0: 受信していない
 */
#define MU_LSR          (*(volatile unsigned int *)0X3F215054)

/* Transmitter idle のビットマスク */
#define MU_LSR_TX_IDLE  (1U << 6)

/* Transmitter empty のビットマスク */
#define MU_LSR_TX_EMPTY (1U << 5)

/* Data ready のビットマスク*/
#define MU_LSR_RX_RDY   (1U << 0)

void println(const char* chars);
void print(const char* chars);
void uart_put(const char c);

#endif // __UART_H__

uart.c

#include "uart.h"

/* あとで types.h に追い出す */
typedef unsigned int size_t;

void println(const char* chars) {
    print(chars);
    print("\n\r");
}

void print(const char* chars) {
    char* char_ptr = (char*) chars;

    while (*char_ptr != '\0') {
        uart_put(*char_ptr);
        char_ptr++;
    }
}

void uart_put(const char c) {
    // 送信受付可能状態になるまでビジーループ
    while (!(MU_LSR & MU_LSR_TX_IDLE) && !(MU_LSR & MU_LSR_TX_EMPTY));

    // IO レジスタにデータ書き込み
    MU_IO = c;
}

'数値 -> 文字列' 変換

これはこんな感じ。

string.h

#ifndef __STRING_H__
#define __STRING_H__

unsigned char ltoa_10(const long l, char* str, const int max_length);

#endif // __STRING_H__

string.c

#include "string.h"

/**
 * @fn 10 進数値を文字列へ変換する
 * @param (l)          変換対象数値
 * @param (str)        変換後の文字列を格納するバッファへのポインタ
 * @param (max_length) 変換後の文字列を格納するバッファの長さ
 * @return エラーコード
 *         0 : 正常終了
 *         1 : 変換後文字列を格納するバッファの長さが足りなかった
 */
unsigned char ltoa_10(const long l, char* str, const int max_length) {
    unsigned int  i;
    unsigned long tmp_long_value = l < 0 ? -l : l;
    unsigned char tmp_digit_value;
    unsigned int  length = 0;

    // 桁の数値と文字を対応付けする配列
    const char cmap[]
            = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

    // ゼロ判定, ゼロならさっさと返却する
    if (l == 0) {
        if (max_length < 2) {
            return 1;
        }

        str[0] = '0';
        str[1] = '\0';

        return 0;
    }

    // 桁数判定
    while (tmp_long_value != 0) {
        tmp_long_value = tmp_long_value / 10;
        length++;
    }
    if (l < 0) {
        length++;
    }

    // バッファ長上限判定(NULL 文字を含めるので +1 する)
    // 上限を超えていたら 1 を返却
    if (length + 1 > max_length) {
        return 1;
    }

    // 文字列変換

    // 文字列変換開始インデックス
    i = length - 1;

    // NULL 文字追加
    str[length] = '\0';

    // 各桁の変換
    tmp_long_value = l < 0 ? -l : l;
    while (tmp_long_value != 0) {
        tmp_digit_value = tmp_long_value % 10;
        str[i] = cmap[tmp_digit_value];
        i--;
        tmp_long_value = tmp_long_value / 10;
    }

    // 負数処理
    if (l < 0) {
        str[0] = '-';
    }

    return 0;
}

使い方

こんな感じで使えるようになった。

#include <string.h>
#include <uart.h>

#define BUF_LENGTH 5

int main(void) {

    char cbuf[BUF_LENGTH];

    println("Hello, World!");

    error_code = function();
    ltoa_10(error_code, cbuf, BUF_LENGTH);

    print("error_code: ");
    print(error_code);
    println(".");

    return 0;
}

成果物

この辺りをユーティリティとしてまとめたうえで、 gcc で挙動テスト(string.c のみ)するようにしたものが下記 URL にある。

これでデバッグプリントできるようになったので、 HDMI 出力できない問題を追いかけていく。 (しかし SD カード抜き差しが非常に厳しい...)

以上。

0 件のコメント: