Raspberry Pi で Hello World
前回に引き続きラズパイ用の開発環境を構築し実機でHello Worldを表示(シリアル経由)するまでをやってみる。
実機は初期モデルからメモリが2倍の512MBになったVer。
最近のラズパイに比べたら装備も性能も劣るけれど個人的にはこっちのシンプルな方が好き。
Raspberry Pi Type B 512MB
[rtoc_mokuji title=”” title_display=”” heading=”h3″ list_h2_type=”” list_h3_type=”” display=”” frame_design=”” animation=””]
環境構築続き
gatewayの設定変更
前回の作業ではホストオンリーアダプタにgatewayの設定を入れてしまい、
NAT経由で名前解決ができずapt-getが出来ない凡ミスを犯していたため修正した。
◆ネットワーク設定ファイルの修正とdefault gatewayの確認
user@user-VirtualBox ~ $ cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
auto enp0s8
iface enp0s8 inet static
address 192.168.100.101
netmask 255.255.255.0
user@user-VirtualBox ~ $
user@user-VirtualBox ~ $ ip route show
default via 10.0.2.2 dev enp0s3 proto static metric 100
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15 metric 100
169.254.0.0/16 dev enp0s3 scope link metric 1000
192.168.100.0/24 dev enp0s8 proto kernel scope link src 192.168.100.101
クロスコンパイラ(+その他)の取得
下記を参考にクロスコンパイラをgitで取ってくる。取得先は/usr/local/配下とした。
Kernel building – Raspberry Pi Documentation
user@user-VirtualBox ~ $ sudo apt-get install git
user@user-VirtualBox ~ $ cd /usr/local
user@user-VirtualBox ~ $
user@user-VirtualBox /usr/local $ sudo git clone https://github.com/raspberrypi/
tools
[sudo] user のパスワード:
Cloning into 'tools'...
remote: Counting objects: 25343, done.
remote: Total 25343 (delta 0), reused 0 (delta 0), pack-reused 25343
Receiving objects: 100% (25343/25343), 606.96 MiB | 1.48 MiB/s, done.
Resolving deltas: 100% (15035/15035), done.
Checking connectivity... done.
Checking out files: 100% (19058/19058), done.
クロスコンパイラが取ってこれたらパスを追記しておく。
user@user-VirtualBox ~ $ vim ~/.bashrc
PATH=$PATH:/usr/local/tools/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin を追記
その他、ビルドしたファイルのファイル転送用にFTPサーバと純粋なviが使いずらいのでvimを追加する。
user@user-VirtualBox ~ $ sudo apt-get install vsftpd
user@user-VirtualBox ~ $ sudo apt-get install vim
起動イメージの作成
ビルド環境が整ったので早速 “Hello World” してみる。
具体的には電源ON後にHello Worldのメッセージを表示して終了するだけのバイナリ(kernel.img)を作る。
ソースコード
main.c
#include "uart.h"
#define UNUSED(x) (void)(x)
const char hello[] = "\r\nHello World\r\n";
const char halting[] = "\r\n*** system halting ***";
// kernel main function, it all begins here
void kernel_main(void) {
uart_init();
uart_puts(hello);
// Wait a bit
for(volatile int i = 0; i < 10000000; ++i) { }
uart_puts(halting);
}
uart.c
/* uart.c - UART initialization & communication */
/* Reference material:
* http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
* Chapter 13: UART
*/
#include "mmio.h"
#include "uart.h"
enum {
// The GPIO registers base address.
GPIO_BASE = 0x20200000,
// The offsets for reach register.
// Controls actuation of pull up/down to ALL GPIO pins.
GPPUD = (GPIO_BASE + 0x94),
// Controls actuation of pull up/down for specific GPIO pin.
GPPUDCLK0 = (GPIO_BASE + 0x98),
// The base address for UART.
UART0_BASE = 0x20201000,
// The offsets for reach register for the UART.
UART0_DR = (UART0_BASE + 0x00),
UART0_RSRECR = (UART0_BASE + 0x04),
UART0_FR = (UART0_BASE + 0x18),
UART0_ILPR = (UART0_BASE + 0x20),
UART0_IBRD = (UART0_BASE + 0x24),
UART0_FBRD = (UART0_BASE + 0x28),
UART0_LCRH = (UART0_BASE + 0x2C),
UART0_CR = (UART0_BASE + 0x30),
UART0_IFLS = (UART0_BASE + 0x34),
UART0_IMSC = (UART0_BASE + 0x38),
UART0_RIS = (UART0_BASE + 0x3C),
UART0_MIS = (UART0_BASE + 0x40),
UART0_ICR = (UART0_BASE + 0x44),
UART0_DMACR = (UART0_BASE + 0x48),
UART0_ITCR = (UART0_BASE + 0x80),
UART0_ITIP = (UART0_BASE + 0x84),
UART0_ITOP = (UART0_BASE + 0x88),
UART0_TDR = (UART0_BASE + 0x8C),
};
/*
* delay function
* int32_t delay: number of cycles to delay
*
* This just loops <delay> times in a way that the compiler
* wont optimize away.
*/
static void delay(int32_t count) {
int32_t i;
for (i = 0; i < count * 1000000; i++) {
}
//__asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay%=\n"
// : : [count]"r"(count) : "cc");
}
/*
* Initialize UART0.
*/
void uart_init() {
// Disable UART0.
mmio_write(UART0_CR, 0x00000000);
// Setup the GPIO pin 14 && 15.
// Disable pull up/down for all GPIO pins & delay for 150 cycles.
mmio_write(GPPUD, 0x00000000);
delay(150);
// Disable pull up/down for pin 14,15 & delay for 150 cycles.
mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15));
delay(150);
// Write 0 to GPPUDCLK0 to make it take effect.
mmio_write(GPPUDCLK0, 0x00000000);
// Clear pending interrupts.
mmio_write(UART0_ICR, 0x7FF);
// Set integer & fractional part of baud rate.
// Divider = UART_CLOCK/(16 * Baud)
// Fraction part register = (Fractional part * 64) + 0.5
// UART_CLOCK = 3000000; Baud = 115200.
// Divider = 3000000/(16 * 115200) = 1.627 = ~1.
// Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
mmio_write(UART0_IBRD, 1);
mmio_write(UART0_FBRD, 40);
// Enable FIFO & 8 bit data transmissio (1 stop bit, no parity).
mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6));
// Mask all interrupts.
mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) |
(1 << 6) | (1 << 7) | (1 << 8) |
(1 << 9) | (1 << 10));
// Enable UART0, receive & transfer part of UART.
mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9));
}
/*
* Transmit a byte via UART0.
* uint8_t Byte: byte to send.
*/
void uart_putc(uint8_t byte) {
// wait for UART to become ready to transmit
while (1) {
if (!(mmio_read(UART0_FR) & (1 << 5))) {
break;
}
}
mmio_write(UART0_DR, byte);
}
/*
* print a string to the UART one character at a time
* const char *str: 0-terminated string
*/
void uart_puts(const char *str) {
while (*str) {
uart_putc(*str++);
}
}
uart.h
/* uart.h - UART initialization & communication */
#ifndef UART_H
#define UART_H
#include "local.h"
/*
* Initialize UART0.
*/
void uart_init(void);
/*
* Transmit a byte via UART0.
* uint8_t Byte: byte to send.
*/
void uart_putc(uint8_t byte);
/*
* print a string to the UART one character at a time
* const char *str: 0-terminated string
*/
void uart_puts(const char *str);
#endif // #ifndef UART_H
mmio.h
/* mmio.h - access to MMIO registers */
#ifndef MMIO_H
#define MMIO_H
#include "local.h"
// write to MMIO register
static inline void mmio_write(uint32_t reg, uint32_t data) {
uint32_t *ptr = (uint32_t*)reg;
__asm volatile("str %[data], [%[reg]]" : : [reg]"r"(ptr), [data]"r"(data));
}
// read from MMIO register
static inline uint32_t mmio_read(uint32_t reg) {
uint32_t *ptr = (uint32_t*)reg;
uint32_t data;
__asm volatile("ldr %[data], [%[reg]]"
: [data]"=r"(data) : [reg]"r"(ptr));
return data;
}
#endif // #ifndef MMIO_H
boot.S
/* boot.S - assembly startup code */
// To keep this in the first portion of the binary.
.section ".text.boot"
// Make Start global.
.globl Start
// Entry point for the kernel.
// r15 -> should begin execution at 0x8000.
// r0 -> 0x00000000
// r1 -> 0x00000C42
// r2 -> 0x00000100 - start of ATAGS
// preserve these registers as argument for kernel_main
Start:
// Setup the stack.
mov sp, #0x8000
// Clear out bss.
ldr r4, =_bss_start
ldr r9, =_bss_end
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0
b Second
First:
// store multiple at r4.
stmia r4!, {r5-r8}
// If we are still below bss_end, loop.
Second:
cmp r4, r9
blo First
// Call kernel_main
ldr r3, =kernel_main
blx r3
// halt
halt:
wfe
b halt
バイナリのビルド
Makefile
# Makefile - build script */
# build environment
PREFIX ?= /usr
ARMGNU ?= $(PREFIX)/local/tools/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin/arm-bcm2708-linux-gnueabi
# source files
SOURCES_ASM := $(wildcard *.S)
SOURCES_C := $(wildcard *.c)
# object files
OBJS := $(patsubst %.S,%.o,$(SOURCES_ASM))
OBJS += $(patsubst %.c,%.o,$(SOURCES_C))
# Build flags
DEPENDFLAGS := -MD -MP
INCLUDES := -I./
BASEFLAGS := -O2 -fpic -pedantic -pedantic-errors -nostdlib
BASEFLAGS += -nostartfiles -ffreestanding -nodefaultlibs
BASEFLAGS += -fno-builtin -fomit-frame-pointer -mcpu=arm1176jzf-s
WARNFLAGS := -Wall -Wextra -Wshadow -Wcast-align -Wwrite-strings
WARNFLAGS += -Wredundant-decls -Winline
WARNFLAGS += -Wno-attributes -Wno-deprecated-declarations
WARNFLAGS += -Wno-div-by-zero -Wno-endif-labels -Wfloat-equal
WARNFLAGS += -Wformat=2 -Wno-format-extra-args -Winit-self
WARNFLAGS += -Winvalid-pch -Wmissing-format-attribute
WARNFLAGS += -Wmissing-include-dirs -Wno-multichar
WARNFLAGS += -Wredundant-decls -Wshadow
WARNFLAGS += -Wno-sign-compare -Wswitch -Wsystem-headers -Wundef
WARNFLAGS += -Wno-pragmas -Wno-unused-but-set-parameter
WARNFLAGS += -Wno-unused-but-set-variable -Wno-unused-result
WARNFLAGS += -Wwrite-strings -Wdisabled-optimization -Wpointer-arith
WARNFLAGS += -Werror
ASFLAGS := $(INCLUDES) $(DEPENDFLAGS) -D__ASSEMBLY__
CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
CFLAGS += -std=c99
# build rules
all: kernel.img
include $(wildcard *.d)
kernel.elf: $(OBJS) link-arm-eabi.ld
$(ARMGNU)-ld $(OBJS) -Tlink-arm-eabi.ld -o $@
kernel.img: kernel.elf
$(ARMGNU)-objcopy kernel.elf -O binary kernel.img
clean:
$(RM) -f $(OBJS) kernel.elf kernel.img
dist-clean: clean
$(RM) -f *.d
# C.
%.o: %.c Makefile
$(ARMGNU)-gcc $(CFLAGS) -c $< -o $@
# AS.
%.o: %.S Makefile
$(ARMGNU)-gcc $(ASFLAGS) -c $< -o $@
コンパイル
user@user-VirtualBox ~ $ make
user@user-VirtualBox ~ $ ls
Makefile boot.o link-arm-eabi.ld main.d tags uart.h
boot.S kernel.elf local.h main.o uart.c uart.o
boot.d kernel.img main.c mmio.h uart.d
その他必要なファイル
SDカードにkernel.imgと合わせてbootcode.binとstart.elfが必要なので下記から拝借する。
raspberrypi/firmware (github.com)
実機起動
SDカードに下記3ファイルを配置する。SDをラズパイに挿入し電源を入れると上から順に実行されていく。
- bootcode.bin
- start.elf
- kernel.img
実行結果
結線はこんな感じ。

Teratermの画面にメッセージが表示されれば成功。(ボーレートは115200)

コメント