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 カード抜き差しが非常に厳しい...)

以上。

2016/12/24

Windows 10 に mikutter 3.4.8 をインストールした

わけあって moguno/mikutter-windows に頼らずに windows にインストールしてみたいと思い、実際やってみたので作業を記録する。

環境まとめ

  • OS: Windows 10 Pro
  • Ruby: 2.3.3

Ruby 入手

  • RubyInstaller Downloads
    • Ruby 2.3.3 (x64) をダウンロード
      • パスを通す設定をしてください
    • DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe をダウンロード

mikutter ソースコード入手

git インストール済みなのでクローンする。

git clone git://toshia.dip.jp/mikutter.git

mikutter に必要な gem たちのインストール

DevKit を展開してできた msys.bat を実行。

cd /PATH/TO/mikutter
gem install bundler
bundle install --path vendor/bundler

mikutter 実行

bundle exec で起動。

cd /PATH/TO/mikutter
bundle exec ruby mikutter.rb

ショートカット作成

まずはスタート用のバッチファイルを作る。

cd /PATH/TO/mikutter
echo start rubyw mikutter.rb> mikutter.bat

作成したバッチファイルへのショートカットをデスクトップに作る。

TODO

  • display_display_requirements
  • 別窓で画像開くやつの画像が表示されない
  • 音を出す
    • 今は音が鳴らない moguno/mikutter-windows のサウンドプラグイン単独配布してくれないかなぁ...。
  • アイコン
    • ショートカットアイコンに png 指定できないの辛い

音とアイコンについては moguno/mikutter-windows で実現されているので、真似して作業していく。

Raspberry Pi 3 Model B+ でベアメタル UART 出力

前回に引き続き、今回も下記サイトを参考にベアメタルプログラミングをしていく。

今回は UART でのデータ送受信。

参考ページは下記。

必要なハードウェアの調達

UART するには通信ケーブルが必要なので調達した。

ドライバは下記ページを参考にした。

UART を使用するための設定

config.txt を作成し、 enable_uart=1 を記述。 あとで SD カードのルートに格納する。

この辺は今のところ「おまじない」としておく。

このおまじないをすると、 GPU が Mini UART を使えるようにしてくれるようになるらしい。 そのため、我々は、 UART の初期化コードを書くことなくプログラミングできるっぽい。

UART を使うためのレジスタの確認

使うレジスタ

BCM-ARM-Peripherals.pdf の 8 ページ目から UART のレジスタについて説明されている。

今回使うのは、 AUX_MU_LSR_REG(Mini Uart Line Status)AUX_MU_IO_REG(Mini Uart I/O Data)

Raspberry Pi 3 なので、上位アドレスを読みかえ、それぞれ 0x3F215054, 0x3F215040 となる。

ステータスレジスタについて

AUX_MU_LSR については、BCM-ARM-Peripherals.pdf の 15 ページ目に説明が書いてあるみたい。

プログラム作成

/**
 * 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)
#define MU_LSR_TX_IDLE  (1U << 6) /* Transmitter idle のビットマスク */
#define MU_LSR_TX_EMPTY (1U << 5) /* Transmitter empty のビットマスク */
#define MU_LSR_RX_RDY   (1U << 0) /* Data ready のビットマスク*/

int main(void) {

    // 受信文字列格納用
    volatile char ch;

    while (1) {
        // 受信するまでビジーループ
        while (!(MU_LSR & MU_LSR_RX_RDY));

        // 受信データ記録
        ch = (char)MU_IO;

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

        // IO レジスタにデータ書き込み
        MU_IO = (unsigned int)ch;
    }

    return 0;
}

配線

元ネタのページにも記載されているが、ピン番号はこちらで確認した。

まじめな人はこれと BCM2835-ARM-Peripherals.pdf を見比べて、 仕様書の見方に慣れていくのが良いかと。 (多分 102 ページ目からの説明とか 176 ページ目からの説明とかが対応する記述なのだと思う。)

購入した変換ケーブルの説明を見ると、下記のようになっている。

ブラックケーブル ----- GND
グリーンケーブル ----- TXD
ホワイトケーブル ----- RXD
レッドケーブル ------- VCC - 5V

というわけで、下記感じで配線する。

PIN  6(GND) ----- ブラック(GND)
PIN  8(GPIO14) -- ホワイト(RXD)
PIN 10(GPIO15) -- グリーン(TXD)

動作確認

  1. SD カードに bootcode.bin, start.elf, config.txt, kernel8.img をコピーして、Raspberry Pi に挿す
  2. PC に変換ケーブルを挿す
  3. シリアルコンソールを起動し、 Com の設定を行う(後述)
  4. Raspberry Pi に電源供給
  5. 入力文字がエコーバックされれば OK

シリアルコンソールについて

今回は、 RLogin を使用することとした。 sixel 関係で使っていて、使い慣れていたので。

設定は、下記のようにすれば OK のはず。

  • ビット/秒 : 115200
  • データビット : 8
  • パリティ : なし
  • ストップビット : 1
  • フロー制御 : ハードウェア

以上。

2016/12/19

Raspberry Pi 3 Model B+ で、 OS 無し L チカした

Raspberry Pi でベアメタルプログラミンしたかったので、 いろいろ調べた作業記録を残す。

参考資料を探す

とりあえず、自分のレベルに合っていて、 かつ、わかりやすそうなサイトを探す。

...で、見つかったのがこのサイト。

基本ここを参考に環境構築とかしていくこととする。

必要なハードウェア類の調達

L チカに必要なものを買い集める。

必要なソフトウェア類の調達

ビルド環境構築

ダウンロードした gcc-linaro-6.2.1-2016.11-i686-mingw32_aarch64-linux-gnu.tar.xz を適当な場所に展開し、 bin ディレクトリにパスを通す。

L チカプログラムの作成

プログラムを作ってビルドする。

ファイル作成

こんな配置でファイルを作成する。

led_blink_GPIO16
├── main.c
├── Makefile
└── start.S

start.S

/* 現時点ではおまじないスタート地点を調整しているはず */
mov sp, #0x80000
bl  main

main.c

#define GPFSEL1 0x3F200004 /* GPIO のピン設定をするためのレジスタ */
#define GPSET0  0x3F20001C /* GPIO を HIGH にするためのレジスタ */
#define GPCLR0  0x3F200028 /* GPIO を LOW  にするためのレジスタ */

typedef unsigned char bool;

#define TRUE  1
#define FALSE 0

#define WAIT_COUNT 3000000

/*
 * @fn wait_count だけビジーウェイトする
 * @param (wait_count) このカウント数だけビジーウェイトする
 */
void busy_wait(int wait_count);

/*
 * @fn L チカする
 */
int main(void) {

    // GPIO 出力に設定。
    // Peripheral specification の
    // 92 ページ、Field Name が FSEL16 の行の
    // Bit(s) を見ると、18 から 20 ビット目までに
    // 0x001 を設定すればよいことがわかる感じ。
    *(volatile unsigned int*)GPFSEL1 = (1 << (18));

    // セットして待つ、クリアして待つ、を繰り返す。
    while (1) {
        *(volatile unsigned int*)GPSET0 = (1 << 16);
        busy_wait(WAIT_COUNT);
        *(volatile unsigned int*)GPCLR0 = (1 << 16);
        busy_wait(WAIT_COUNT);
    }

    return 0;
}

void busy_wait(int wait_count) {
    volatile unsigned int i;

    for (i = 0; i < WAIT_COUNT; i++);
}

Makefile

CC=aarch64-linux-gnu-gcc
LD=aarch64-linux-gnu-ld
AS=aarch64-linux-gnu-as
OBJCOPY=aarch64-linux-gnu-objcopy

OBJECTS=start.o main.o

all: build link objcopy

objcopy: kernel8.img

kernel8.img: kernel8.elf
    $(OBJCOPY) -O binary kernel8.elf kernel8.img


link: kernel8.elf

kernel8.elf: $(OBJECTS)
    $(LD) -Ttext 0x80000 -o kernel8.elf $(OBJECTS)


build: $(OBJECTS)

start.o: start.S
    $(AS) -o start.o start.S

main.o: main.c
    $(CC) -c -o main.o main.c


clean:
    rm *.o *.elf *.img

ビルド

make して kernel8.img を作成する。

$ make
aarch64-linux-gnu-as -o start.o start.S
aarch64-linux-gnu-gcc -c -o main.o main.c
aarch64-linux-gnu-ld -Ttext 0x80000 -o kernel8.elf start.o main.o
C:\Users\mikoto\app\gcc-linaro-6.2.1-2016.11-i686-mingw32_aarch64-linux-gnu\bin\aarch64-linux-gnu-ld.exe: warning: cannot find entry symbol _start; defaulting to 0000000000080000
aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img

リンカスクリプトを作っていないため、 警告が出ているけど、今回動かす分には問題ない。

Raspberry Pi の準備

ファイルの配置

  1. ダウンロードした firmware_armstub.zip を展開し、bootcode.bin, start.elf を SD カードのルートにコピーする
  2. ビルドした、 kernel8.img を SD カードのルートにコピーする

最終的に、 SD カードに下記感じでファイルを配置する。

SD カード
├── bootcode.bin
├── start.elf
└── kernel8.img

配線

下記感じで配線する。

PIN 36(GPIO16) ─ 抵抗 ─ LED
PIN 6(GND) ────────┘

実行

  1. Raspberry Pi に SD カードを入れる
  2. Raspberry Pi に電源供給

以上。

参考資料

この辺を見て試したはず...。ぬけもれあるかも。

2016/12/13

vim のアウトライン表示プラグイン outline.vim を作った

c 言語の関数一覧が欲しかったので作ったので、 そのあたりの作業について備忘メモしておく。

名前負けはご愛敬。

目的

c 言語の関数一覧が欲しかった。

何を考えたか?

c 言語の関数一覧が欲しいけど、 自分で抽出処理はかけない。

そんなわけで、方針としては

以下、各要素について書いていく。

アウトライン抽出

アウトライン抽出は ctags を使う。

ctags -x FILE_NAME --sort=no

これで member, struct, function のリストが表示される。 ここから member を取り除き、awk で無用な列を削除する。

ctags -x FILE_PATH --sort=no | grep -v member | awk '{$1=\"\";$2=\"\";$3=\"\" + $3;$4=\"\";print}'

これを、vim のバッファに読み込ませるには、 read! コマンドを使う。

silent execute "read !ctags -x " . file_path . " --sort=no | grep -v member | awk '{$1=\"\";$2=\"\";$3=\"\" + $3;$4=\"\";print}'"

これで、 ctags の実行結果を成形したものをバッファに読み込むことができる。

対象にジャンプ

ctags を成形した結果は、各行が 行番号 関数名 となっているので、 split してリストの 0 番目をとってきて、元のバッファに戻って [count]gg すれば OK。

で、できたのが下記な感じ。

以上。

2016/12/04

ファイル検索プラグイン file_selector.vim を作った

ファイル一覧を絞り込んで選択するやつを作ったので、 そのあたりの作業について備忘メモしておく。

目的

netrw でファイルを開くのが面倒。 プロジェクト内のファイル一覧から、文字列打ち込んで絞り込みつつファイル選択したい。 この欲望を満たすプラグインを作る。

何を考えたか?

ファイル絞り込みの構成要素としては、

  1. ファイル一覧取得
  2. 入力文字列取得
  3. 入力文字列を使った絞り込み処理
  4. バッファへの反映

みたいなのがあると思っていて、 2 と 4 のイメージがついてなくって途方に暮れていたところ、 @pink_bangbi さんにつぶやきを拾っていただいた。

そんなわけで、方針としては

  • 文字列取得は InsertCharPre から一文字ずつ取得
  • バッファは全部書き直し

の方針で考えた。

構成要素の実現方法を考える

それぞれ考えていく。

ファイル一覧取得

単純に glob("./**/*") とする。

入力文字列取得

autocmdInsertCharPre を捕まえる。

入力文字列を使った絞り込み処理 + バッファへの反映

v/文字列/d で、文字列が含まれていない行を削除できるので、

  1. バッファクリア
  2. glob("./**/*") で取得した一覧をバッファに流し込む
  3. v/文字列/d で関係ない行を削除

を、文字列入力・削除が行われるたびに実行する。

文字列のハンドリング

InsertCharPre で取得するのはいいけど、それどう管理するの?」を考える。

  • ファイル一覧
    • ファイル一覧はスクリプト変数
    • ファイル絞り込み用バッファを開くときに、``glob("./**/*") する
  • 絞り込み文字列
    • 絞り込み文字列はスクリプト変数
    • ファイル絞り込み用バッファを開くときに、空文字で初期化する
    • InsertCharPre で取得した文字列を絞り込み文字列に追加する
    • <BS> 押下で絞り込み文字列の末尾の文字を消す

file_selector をクラスとして考えると、下図のような感じ。

  • OpenFileSelector() を呼ぶたびに s:patterns:all_file_list を初期化する。
  • 文字入力されるたび( InsertCharPre が呼ばれるたび) AddChar()s:pattern 更新
    • InsertCharPre 内でバッファの更新ができないようなので、バッファの更新は TextChangedI で行う
  • <BS> が押されたら、 DelChar() -> UpdateBuffer() する。

上記のような方針で実装して、下記のような挙動が実現できた。

所感

「OpenFileSelector() を呼び出したバッファでファイルを開きたい」とか、 すでに気に入らないところがあるが、とりあえず絞り込み UI の習作として使用・改良していきたい。

リポジトリはこちら。

以上。