ラベル C言語 の投稿を表示しています。 すべての投稿を表示
ラベル C言語 の投稿を表示しています。 すべての投稿を表示

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

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 に電源供給

以上。

参考資料

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

2010/12/08

[Java][C]JNIのヘッダを作るメイクファイル

JNI のヘッダを作ってくれる Makefile

OUTPUT_DIR=jni/
CLASSPATH=bin/
PACKAGE_BASE=jp.dip.oyasirazu.jnitest.
DIRECTORY_BASE=jp/dip/oyasirazu/jnitest/
HEADER_BASE=${OUTPUT_DIR}jp_dip_oyasirazu_jnitest_

all: ${HEADER_BASE}JNITest.h \
     ${HEADER_BASE}TestClass.h

${HEADER_BASE}%.h : ${CLASSPATH}${DIRECTORY_BASE}%.class
   javah -classpath ${CLASSPATH} -d ${OUTPUT_DIR} ${PACKAGE_BASE}$(subst ${HEADER_BASE},,$*)
ちょうきもちわるい。
ヘッダは '_' 区切りだし、パッケージは '.' 区切りだし、ディレクトリは '/' 区切りだし...。
何か良い方法無いのかね?
make doc でシグネチャ一覧 txt をどこかに出力とかも便利かもねー。
きちんと調べれば ndk-build とか .mk とか編集するとできる気もする。
いつかきっと調べる。
でもこの辺がころころ変わることなんて滅多にないはずだから無駄な努力だった気もする。

2009/06/04

[ロボット]うれしさ1割、情けなさ8割

残り1割は何だ...?

とりあえず、itoa使って変換するよりは、sprintf使って変換したほうがよいことはわかった。
itoaの使い方間違いに気づくのに約12時間かかるとかもう...orz

とりあえず、ロボット側のコマンド受付プログラムは完成。
後はコマンドを送る側を作れば見栄えはよくなるね。

.........teratermでいいんじゃね?って気持ち7割。

2006/12/27

[C言語]バッファオーバーフローの練習

■今回のプログラム

長さ5のchar配列を作成。
gets()で標準入力。
printf()で標準出力。

■ソースファイル
#include <stdio.h>
#define NUM 5

int main(void)
{
//文字列バッファ宣言(長さは5)
char s[NUM];

printf("s address:%X¥n", &s);
printf("s length:%d¥n¥n", sizeof(s));
printf("input string:");
gets(s);//バッファのサイズを渡せないのでチェックのしようが無い
printf("%s¥n", s);
}
■実行結果
$ ./BOFTest
s address:BFFFFCBB
s length:5

warning: this program uses gets(), which is unsafe.
input string:fdsafdas
fdsafdas

親切にwarningが出てますが無視して大量入力。
宣言した領域をはみ出して書き込んでますね。
printf()も領域完全無視で読みにいってるみたいです。
fscanf()が嫌われてるのもこれが理由らしいです。
作るのは簡単(よりもたちが悪い)なだけに対策が難しいですね。

[C言語]文字列の検索

■処理の流れ

string1からstring2[i]の文字列を探す。
それだけ。

■ソースファイル
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
//検索される文字列
char string1[] = "Hey mommy where is my shoes.";

//検索する文字列(する、されるじゃいまいちわかり難いね)
char *string2[] = {"Hey", "mommy", "where", "is", "my", "shoes", NULL};

//string2のインデックス用
int i = 0;

//string1の表示と先頭アドレスの表示
printf("string1 = ¥"%s¥"¥n", string1);
printf("string1はここから始まってますよ:%X¥n",&string1);

//string2の単語を先頭から検索、NULLになったらやめる
while(string2[i] != NULL){
printf("%sはメモリの%X番地から始まってますよ。¥n", string2[i], (int)strstr(string1, string2[i]));
i++;
}
}
■実行結果
$ ./SearchTest
string1 = "Hey mommy where is my shoes."
string1はここから始まってますよ:BFFFFC8F
HeyはメモリのBFFFFC8F番地から始まってますよ。
mommyはメモリのBFFFFC93番地から始まってますよ。
whereはメモリのBFFFFC99番地から始まってますよ。
isはメモリのBFFFFC9F番地から始まってますよ。
myはメモリのBFFFFC96番地から始まってますよ。
shoesはメモリのBFFFFCA5番地から始まってますよ。

1文字1バイト=一つの番地に収まる。
という訳でこういう場合わかりやすいね。
あとはstrlen()と組み合わせていろいろできそう。

[C言語]ファイルに出力

■何となく流れっぽいもの

コマンドライン引数を取得。
引数一つを1行にしてoutput.txtに出力する。

■ソースファイル
#include <stdio.h>

int main(int argc, char *argv[])
{
int i;

//ファイルポインタ宣言
FILE *fp;

//ファイルオーオプン
fp = fopen("output.txt","w");

if(fp == NULL){
printf("Can not open...");
return 1;
}

//書き込む
fputs("書き込み開始\n", fp);
for(i=1; i <= argc; i++){
fputs(argv[i], fp);
fputs("\n", fp);
}
fputs("書き込み終了\n", fp);

//別な書き込み方
fprintf(fp, "書き込み開始\n");
for(i=1; i <= argc; i++){
fprintf(fp, "%s\n", argv[i]);
}
fprintf(fp, "書き込み終了\n");
//ファイルクローズ
fclose(fp);
}
■実行結果
$ ./FileOutputTest This is a pen.
$ cat output.txt
書き込み開始
This
is
a
pen.
(null)
書き込み終了
書き込み開始
This
is
a
pen.
(null)
書き込み終了
■ソース書くだけでいっぱいいっぱい

別に良いよね、需要無いし。
(こういう考え方がいけないんだ…)

2006/12/21

[C言語]コマンドライン引数を使ってみる

■mainの第一引数に引数の数、第二引数に文字列配列

具体的にはこんな感じ。

#include <stdio.h>

int main(int argc,char *argv[])//argcが引数の数、argv[]が実際の引数
{
int i;
//第一引数から最後の引数まで順番に表示
for(i = 1; i <= argc; i++){
printf("第%d引数:%s\n",i,argv[i]);
}
return 0;
}

ちなみに、第零引数は実行ファイルへのパス。 (多分カレントディレクトリからの相対パス)

■実行結果
$ ./InputEcho Is this a pen ?
第1引数:Is
第2引数:this
第3引数:a
第4引数:pen
第5引数:?
第6引数:(null)

[C言語]ファイルから入力

■ファイル入力テスト

ソースファイルはこちら

#include <stdio.h>
#define NUM 256

int main(void)
{
FILE *fp;
char string[NUM];
char s1[NUM],s2[NUM];

//ファイルオープン
fp = fopen("inputfile.txt","r");

//オープン成否判定
if(fp == NULL){
printf("ファイルが開けませんよ?\n");
return 1;
}

printf("ファイル読み出し開始\n");
//ファイル読み出し
while(fgets(string, NUM, fp) != NULL){
printf("%s", string);
}
printf("ファイル読み出し終了\n");

//現在位置(どこから読み込むか)を先頭に戻す
fseek(fp, 0, 0);

printf("二度目のファイル読み出し開始\n");
//ファイル読み出し2
while(fscanf(fp, "%s %s", &s1, &s2) != EOF){
printf("名:%s 姓:%s\n", s1, s2);//fscanfの場合最後の改行は空白に変換されるらしい
}
printf("二度目のファイル読み出し終了\n");

//ファイルクローズ
fclose(fp);
return 0;
}

読み込むファイルはこれ

命 大雪
板 かまぼこ
だんご 笹

実行結果はこれ

ファイル読み出し開始
命 大雪
板 かまぼこ
だんご 笹
ファイル読み出し終了
二度目のファイル読み出し開始
名:命 姓:大雪
名:板 姓:かまぼこ
名:だんご 姓:笹
二度目のファイル読み出し終了

fscanfは嫌われているみたいね。
扱い方が独特らしい。 あとエラー処理が面倒くさくなるみたい。

2006/12/19

[C言語]C言語入門してみる

■USBメモリをなくしたので…
Wiki2Html(Java)も一緒に行方不明です。
どうせもう一度作るなら勉強もかねてほかの言語にしましょうかね。
というわけで、C言語入門してみる。


■なぜ今C言語なのか?
  • 『C言語は現代のアセンブラ』って誰かが言ってた。(検索しても見つからず…)
  • ポインタ、構造体を知る=PCのメモリの使い方を知る?
     →クラス、メソッドとかの理解にも役立つ?
  • 古い技術を使うのはなんか格好良い(古いなんていっちゃだめか?)

■必要な機能
  • HTTPリクエスト
  • HTMLソース書き換え
  • 文字コード変換
  • ファイルへ保存
  • URLリストからDLする
  • 上階層を自動保存
  • WEBの構造も保存(フォルダ自動作成)
Javaでは以外に簡単(だけど面倒)だったけれどもCだとどうでしょうね。 ネットワーク関係がすごい面倒なんだっけ?


■ぼちぼち
やっていきます。