sobota 12. ledna 2019

Raspberry Pi programování bez OS

Před nějakou dobou jsem se pokoušel přeložit jednoduchý program pro Raspberry Pi bez takových zbytečností jako je linuxové jádro. :-) A jak na to?



Nejprve je potřeba provést inicializaci procesoru, k tomu slouží kód v start.s, tento kód jsem příliš nemodifikoval a využil přebraný z https://raw.githubusercontent.com/dwelch67/raspberrypi/066f219ada52cf8c6990589566d06318ce2de5c7/boards/pi3/aarch32/SVC/README.

Všechno potřebné je na mém Gitu, viz odkaz níže.

Dále zde bude podstatný vektor přerušení, ale to předbíhám. Nyní nás zajímá pouze řádek s "bl notmain", který obsahuje instrukci skoku (branch and link) na funkci main (ze zvyku jsem si ji přejmenoval). A pak už žijeme s programování v jazyce C. Takže si nadefinujeme registry pro GPIO piny v souboru GPIO.h (starší verze Raspberry Pi mají registry na jiné adrese, tak tedy 0x20200000 a také se u nich pouští kernel.img).

#ifndef __GPIO_H
#define __GPIO_H

#define GPIO_BASE 0x3F200000

typedef struct
{
    volatile unsigned int GPFSEL0;  /*!< GPIO Function Select 0 */
    volatile unsigned int GPFSEL1;  /*!< GPIO Function Select 1 */
    volatile unsigned int GPFSEL2;  /*!< GPIO Function Select 2 */
    volatile unsigned int GPFSEL3;  /*!< GPIO Function Select 3 */
    volatile unsigned int GPFSEL4;  /*!< GPIO Function Select 4 */
    volatile unsigned int GPFSEL5;  /*!< GPIO Function Select 5 */
    volatile unsigned int nic0;
    volatile unsigned int GPSET0;   /*!< GPIO Pin Output Set 0 */
    volatile unsigned int GPSET1;   /*!< GPIO Pin Output Set 1 */
    volatile unsigned int nic1;
    volatile unsigned int GPCLR0;   /*!< GPIO Pin Output Clear 0 */
    volatile unsigned int GPCLR1;   /*!< GPIO Pin Output Clear 1 */
    volatile unsigned int nic2;
    volatile unsigned int GPLEV0;   /*!< GPIO Pin Level 0 */
    volatile unsigned int GPLEV1;   /*!< GPIO Pin Level 1 */
    volatile unsigned int nic3;
    volatile unsigned int GPEDS0;   /*!< GPIO Pin Event Detect Status 0 */
    volatile unsigned int GPEDS1;   /*!< GPIO Pin Event Detect Status 1 */
    volatile unsigned int nic4;
    volatile unsigned int GPREN0;   /*!< GPIO Pin Rising Edge Detect Enable 0 */
    volatile unsigned int GPREN1;   /*!< GPIO Pin Rising Edge Detect Enable 1 */
    volatile unsigned int nic5;
    volatile unsigned int GPFEN0;   /*!< GPIO Pin Falling Edge Detect Enable 0 */
    volatile unsigned int GPFEN1;   /*!< GPIO Pin Falling Edge Detect Enable 1 */
    volatile unsigned int nic6;
    volatile unsigned int GPHEN0;   /*!< GPIO Pin High Detect Enable 0 */
    volatile unsigned int GPHEN1;   /*!< GPIO Pin High Detect Enable 1 */
    volatile unsigned int nic7;
    volatile unsigned int GPLEN0;   /*!< GPIO Pin Low Detect Enable 0 */
    volatile unsigned int GPLEN1;   /*!< GPIO Pin Low Detect Enable 1 */
    volatile unsigned int nic8;
    volatile unsigned int GPAREN0;  /*!< GPIO Pin Async. Rising Edge Detect 0 */
    volatile unsigned int GPAREN1;  /*!< GPIO Pin Async. Rising Edge Detect 1 */
    volatile unsigned int nic9;
    volatile unsigned int GPAFEN0;  /*!< GPIO Pin Async. Falling Edge Detect 0 */
    volatile unsigned int GPAFEN1;  /*!< GPIO Pin Async. Falling Edge Detect 1 */
    volatile unsigned int nic10;
    volatile unsigned int GPPUD;    /*!< GPIO Pin Pull-up/down Enable */
    volatile unsigned int GPPUDCLK0;    /*!< GPIO Pin Pull-up/down Enable Clock 0 */
    volatile unsigned int GPPUDCLK1;    /*!< GPIO Pin Pull-up/down Enable Clock 1 */
    volatile unsigned int nic11;
    volatile unsigned int Test;

} GPIO_TypeDef;

#endif


Ty mezery mezi registry se dají udělat i lépe, ale už jsem zapomněl, jak se to správně dělá. Pak je ještě potřeba memmap, ten jsem také přebral.

MEMORY { ram : ORIGIN = 0x8000, LENGTH = 0x10000 } SECTIONS { .text : { *(.text*) } > ram .bss : { *(.bss*) } > ram }

A nyní vlastní kód v souboru main.c


#include "GPIO.h"

GPIO_TypeDef *GPIO;

int main(void)
{
    GPIO = (GPIO_TypeDef*)GPIO_BASE;
    GPIO->GPFSEL1 |= 1UL << 18; // GPIO16 OUT - LED
    GPIO->GPSET0 |= 1UL << 16;  // GPIO16 SET - LED switch on
    while(1){}
}

Vše co je nyní potřeba udělat, je spustit následující příkazy. Překlad assembleru, překlad céčkového souboru, slinkování a vytvoření binárky. Pak binárku překopírujeme na SD kartu do /boot/ jako .img, soubor kernel, který Raspberry Pi po startu pouští a provedeme restart.

arm-none-eabi-as start.s -o start.o
arm-none-eabi-gcc -Wall -O2 -nostdlib -nostartfiles -ffreestanding -c main.c -o main.o
arm-none-eabi-ld start.o main.o -T memmap -o main.elf
arm-none-eabi-objdump -D main.elf > main.list
arm-none-eabi-objcopy main.elf -O binary main.bin
sudo cp main.bin /boot/kernel7.img
sudo reboot

A protože programátoři jsou líní, tak si napíšeme ještě makefile, který nám pouhým příkazem make, všechno zařídí. Když už jsme u toho, kdo si neudělal zálohu původní kernelu, tak si ho může stáhnout například zde.



ARMGNU ?= arm-none-eabi
#ARMGNU ?= arm-linux-gnueabi

COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding

gcc : main.bin

all : gcc clang

clean :
    rm -f *.o
    rm -f *.bin
    rm -f *.hex
    rm -f *.elf
    rm -f *.list
    rm -f *.img
    rm -f *.bc
    rm -f *.clang.s

start.o : start.s
    $(ARMGNU)-as start.s -o start.o

main.o : main.c
    $(ARMGNU)-gcc $(COPS) -c main.c -o main.o

main.bin : memmap start.o main.o
    $(ARMGNU)-ld start.o main.o -T memmap -o main.elf
    $(ARMGNU)-objdump -D main.elf > main.list
    $(ARMGNU)-objcopy main.elf -O ihex main.hex
    $(ARMGNU)-objcopy main.elf -O binary main.bin
    sudo cp main.bin /boot/kernel7.img
    sudo reboot



LOPS = -Wall -m32 -emit-llvm
LLCOPS0 = -march=arm
LLCOPS1 = -march=arm -mcpu=arm1176jzf-s
LLCOPS = $(LLCOPS1)
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding
OOPS = -std-compile-opts

clang : main.clang.bin

main.bc : main.c
    clang $(LOPS) -c main.c -o main.bc

main.clang.elf : memmap start.o main.bc
    llvm-link main.bc -o main.nopt.bc
    opt $(OOPS) main.nopt.bc -o main.opt.bc
    llc $(LLCOPS) main.opt.bc -o main.clang.s
    $(ARMGNU)-as main.clang.s -o main.clang.o
    $(ARMGNU)-ld -o main.clang.elf -T memmap start.o main.clang.o
    $(ARMGNU)-objdump -D main.clang.elf > main.clang.list

main.clang.bin : main.clang.elf
    $(ARMGNU)-objcopy main.clang.elf main.clang.hex -O ihex
    $(ARMGNU)-objcopy main.clang.elf main.clang.bin -O binary


Žádné komentáře:

Okomentovat