前回に引き続きラズパイ用の開発環境を構築し実機でHello Worldを表示(シリアル経由)するまでをやってみる。
実機は初期モデルからメモリが2倍の512MBになったVer。
最近のラズパイに比べたら装備も性能も劣るけれど個人的にはこっちのシンプルな方が好き。
Raspberry Pi Type B 512MB
環境構築続き
gatewayの設定変更
前回の作業ではホストオンリーアダプタにgatewayの設定を入れてしまい、
NAT経由で名前解決ができずapt-getが出来ない凡ミスを犯していたため修正した。
◆ネットワーク設定ファイルの修正とdefault gatewayの確認
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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. |
クロスコンパイラが取ってこれたらパスを追記しておく。
1 2 |
user@user-VirtualBox ~ $ vim ~/.bashrc PATH=$PATH:/usr/local/tools/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin を追記 |
その他、ビルドしたファイルのファイル転送用にFTPサーバと純粋なviが使いずらいのでvimを追加する。
1 2 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
/* 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/* 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# 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 $@ |
コンパイル
1 2 3 4 5 |
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)