Help us understand the problem. What is going on with this article?

Raspberry Pi 3でBareMetalやってみた 〜UART PL011〜

はじめに

今回は2つのUARTのうちのもう片方「PL011」を使ってみたいと思います。

PL011のレジスタについて

PL011のレジスタについては、データシート資料BCM2837 ARM Peripheralsの177ページから記載されています。
BCM2835のデータのままなので、PL011ベースアドレスは0x7E20100から0x3F20100に読み替えてください。

PL011_FR

FIFOの状態を確認するフラグレジスタで、アドレスは0x3F201018です。
詳細は、データシート資料の181ページの下の方に書かれてます。

  • 4ビット(RXFE) : このビットが1でなければ送信FIFOへデータを追加できます。
  • 5ビット(TXFF) : このビットが1でなければ受信FIFOからデータを取り出せます。

PL011_DR

データレジスタで、アドレスは0x3F20100です。
詳細は、データシート資料の179ページに書かれてます。

送信FIFOへのデータ追加、受信FIFOからのデータ取り出しを行うために使用します。

設定ファイル

今回はUART設定を有効化するenable_uart=1は記述しなくても大丈夫です。

config.txt
# ARMコアをAArch64で起動させる
arm_control=0x200

UARTプログラム

MailBox関連の処理を除けば、nimi UARTとの違いはuart_init関数とput_char関数くらいです。

main.c
// RaspberryPi3 Memory Mapped I/O Base Address
#define MMIO_BASE 0x3F000000

// Memory Mapped I/O
#define IOREG(X)  (*(volatile unsigned int *) (X))

// Mailbox
volatile unsigned int __attribute__((aligned(16))) mbox[36];

#define VIDEOCORE_MBOX  (MMIO_BASE+0x0000B880)
#define MBOX_READ       IOREG(VIDEOCORE_MBOX + 0x00)
#define MBOX_POLL       IOREG(VIDEOCORE_MBOX + 0x10)
#define MBOX_SENDER     IOREG(VIDEOCORE_MBOX + 0x14)
#define MBOX_STATUS     IOREG(VIDEOCORE_MBOX + 0x18)
#define MBOX_CONFIG     IOREG(VIDEOCORE_MBOX + 0x1C)
#define MBOX_WRITE      IOREG(VIDEOCORE_MBOX + 0x20)

#define MBOX_RESPONSE   0x80000000
#define MBOX_FULL       0x80000000
#define MBOX_EMPTY      0x40000000

#define MBOX_REQUEST    0

/* channels */
#define MBOX_CH_PROP    8

/* tags */
#define MBOX_TAG_GETSERIAL      0x10004
#define MBOX_TAG_SETCLKRATE     0x38002
#define MBOX_TAG_LAST           0

// UART
#define PL011_DR        IOREG(MMIO_BASE + 0x00201000)
#define PL011_FR        IOREG(MMIO_BASE + 0x00201018)
#define PL011_IBRD      IOREG(MMIO_BASE + 0x00201024)
#define PL011_FBRD      IOREG(MMIO_BASE + 0x00201028)
#define PL011_LCRH      IOREG(MMIO_BASE + 0x0020102C)
#define PL011_CR        IOREG(MMIO_BASE + 0x00201030)
#define PL011_IMSC      IOREG(MMIO_BASE + 0x00201038)
#define PL011_ICR       IOREG(MMIO_BASE + 0x00201044)

// GPIO
#define GPFSEL1         IOREG(MMIO_BASE + 0x00200004)
#define GPPUD           IOREG(MMIO_BASE + 0x00200094)
#define GPPUDCLK0       IOREG(MMIO_BASE + 0x00200098)

///
// Mailbox

void mbox_write(unsigned char ch){
    unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF));

    /* メールボックスに書き込むまで待つ */
    do{
        asm volatile("nop");
    }while(MBOX_STATUS & MBOX_FULL);

    /* メッセージのアドレスをチャネル識別子を持つメールボックスに書き込む */
    MBOX_WRITE = r;
}

int mbox_read(unsigned char ch){
    unsigned int r = (((unsigned int)((unsigned long)&mbox) & ~0xF) | (ch & 0xF));
    while(1) {
        /* 応答を待つ */
        do{
            asm volatile("nop");
        }while(MBOX_STATUS & MBOX_EMPTY);

        /* メッセージに対する応答か判定 */
        if(r == MBOX_READ){
            /* 有効な成功応答か判定 */
            return mbox[1] == MBOX_RESPONSE;
        }
    }
    return 0;
}

///
// UART

void uart_init(){
    register unsigned int r;

    PL011_CR = 0;         // PL011をオフにする

    /* 一貫した除数値のためのクロックを設定する */
    mbox[0] = 9 * 4;
    mbox[1] = MBOX_REQUEST;
    mbox[2] = MBOX_TAG_SETCLKRATE; // クロックレート設定 (0x38002)
    mbox[3] = 12;
    mbox[4] = 8;
    mbox[5] = 2;                   // UARTクロック
    mbox[6] = 4000000;             // 4(Mhz)
    mbox[7] = 0;                   // クリアターボ
    mbox[8] = MBOX_TAG_LAST;

    mbox_write(MBOX_CH_PROP);  // MailBox書き込み
    mbox_read(MBOX_CH_PROP);   // MailBox読み込み

    /* PL011をGPIO14/15ピンにマップ */
    r = GPFSEL1;
    r &= ~((7 << 12) | (7 << 15)); // gpio14/gpio15
    r |= (4 << 12) | (4 << 15);    // alt0
    GPFSEL1 = r;
    GPPUD = 0;                     // ピン14および15を有効化する
    r = 150;
    while(r--) {
        asm volatile("nop");
    }
    GPPUDCLK0 = (1 << 14) | (1 << 15);
    r = 150;
    while(r--) {
        asm volatile("nop");
    }
    GPPUDCLK0 = 0;

    /* UARTのポート設定 */
    PL011_ICR  = 0x7FF;     // 割り込みをクリアする
    PL011_IBRD = 2;         // 115200 baud
    PL011_FBRD = 0xB;
    PL011_LCRH = 0b11 << 5; // 8/n/1
    PL011_CR   = 0x301;     // Tx, Rx, FIFOを有効化
}

void put_char(char ch){
    do{
        asm volatile("nop");
    }while(PL011_FR & 0x20);
    PL011_DR = ch;
}

void put_str(char *str){
    while(*str != '\0')
        put_char(*str++);
}

void put_hex(unsigned int num){
    unsigned int base = 16;
    unsigned int d    = 1;
    char buf[32], *bf;

    bf = buf;

    // 先頭に"0x"を付ける
    *bf++ = '0';
    *bf++ = 'x';

    while(num / d >= base){
        d *= base;
    }

    while(d != 0){
        int dgt = num / d;
        num %= d;
        d /= base;
        if(dgt > 0 || d == 0){
            *bf++ = dgt + (dgt < 10 ? '0' : ('A') - 10);
        }
    }

    *bf   = 0;

    put_str(buf);
}

///
// Main Function

int main(void){
    /* UART PL011初期化 */
    uart_init();

    put_char('A');
    put_str("\r\nHello, world!\r\n");
    put_hex(2882400018);
    put_str("\r\n");

    while(1){
        asm volatile("nop");
    }

    return 0;
}

回路図

Raspberry Piとのシリアル通信には、FTDI USB・シリアル変換ケーブル (5V)を使用しています。

実行

シリアルモニタとしてGtkTermを使用しました。

設定は「Configuration -> Port」を開き、以下のように設定。

以下、実行中の様子

QEMU 2.12であれば、以下のコマンドで確認することもできます。

qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial stdio

最後に

もう一方のUARTであるPL011を使ってみました。
こっちはMailBoxを使用するので、Mini UARTより少し難しいですね。

本記事のソースコードはGithubに置いてるので、良かったらどうぞ〜
RaspberryPiBareMetal

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away