前回、 親知らずの日記: 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;
}
要素技術
- UART 出力
- '数値 -> 文字列' 変換
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 カード抜き差しが非常に厳しい...)
以上。