Raspberry Pi Betriebssystem | Assembler

Hallo.

Aktuell beschäftige ich mich mit dem Raspberry Pi und dem enthaltenen BCM2835-Chip, sowie mit ARM-Assembler. Dazu verfolge ich dieses Tutorial der Cambridge University [1]. Das Tutorial führt in das Thema Assembler ein, und zeigt einige interessante Wege den Pi einfache Sachen tun zu lassen. Angefangen davon eine GPIO-LED blinken zu lassen, bis hin zur Benutzung von Maus und Tastatur sowie Bildschirm. Das Tutorial ist gut und anfängerfreundlich geschrieben und lässt euch auch die Möglichkeit selbst zu probieren und selbst Code zu schreiben, und damit zu testen ob ihr die Theorie verstanden habt.

Der gebaute Kern (siehe dazu unter Downloads das Makefile) wird auf eine SD-Karte vom Raspberry Pi kopiert, wo sich bereits ein Raspbian Kernel befunden hat. Dort wird kernel.img überschrieben. Das führt dazu, dass der enthaltene Bootloader euren Kernel anstelle den Linux-Kern bootet.

Der Assembler-Code von ARM ist recht simpel (sind ja auch RISC-CPUs). Ein sehr gutes Nachschlagewerk dafür ist [2].

Ich kann jedem Informatiker empfehlen sich mit der Hardware und der Programmierung eines Raspberry Pis auf der Ebene auseinander zu setzen. Die Hardware ist hinreichend gut beschrieben [3]. Mit dem Tutorial der Cambridge University ist ein erster Anfang geschaffen, den ihr weiter ausbauen könnt und ein komplettes Betriebssystem schreiben könnt. Natürlich könnt ihr auch sowohl Assembler als auch eine höhere Sprache, wie C benutzen.

[1] http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/
[2] http://www.heyrick.co.uk/armwiki/Main_Page
[3] https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/README.md

Folgendes Beispiel lässt ein SOS-Morsecode auf der ACT-LED blinken:

main.s

.section .init
.globl _start

_start:
    /* enabling the ACT-Pin */
    bl gpio_address
    mov r0, #16
    mov r1, #1
    bl gpio_pinmode

    // loading the pattern into r3, mask into r2
    ldr r5, =pattern
    ldr r5, [r5]
    ldr r6, =0x80000000

blinkloop:
    /* sets or resets according to the current pattern-value */
    mov r0, #16
    and r1, r5, r6
    bl gpio_write

    ldr r0, =250000
    bl delay_ms
    ror r6, #1
    b blinkloop

.section .data
// ldr works only if the data is set on an address multiple of 4
.align 2
pattern:
    .int 0b10010010111001110011100100100100

gpio.s

.globl wait
.globl gpio_address
.globl gpio_write
.globl gpio_pinmode

/**
 * \brief waits for about r0 seconds
 * \param r0 amount of time the pi should wait (in seconds)
 * \return NONE
 **/
wait:
    cmp r0, #0
    beq wait_back
    sub r0, r0, #1

    mov r2, #0x800000
innerloop:
    sub r2,#1
    cmp r2,#0
    bne innerloop;
    b wait
wait_back:    
    /* jump back */
    mov pc, lr

/********************/
/** GPIO - SECTION **/
/********************/

/**
 * \brief returns the GPIO-base address in r0
 * \return r0 GPIO-base address
 **/
gpio_address:
    ldr r0, =0x20200000;
    mov pc, lr;

/**
 * \brief writes a value to the GPIO-Pin (i.e. writes 1 to set or 1 to clear)
 * \param r0 GPIO-Pin
 * \param r1 output-value (1 for on, 0 for off)
 **/
gpio_write:
    cmp r0, #53
    movhi pc, lr

    //cmp r1, #1
    //movhi pc, lr

    mov r2, r0
    push {lr}
    bl gpio_address
    // r0 -> GPIO-base address
    // r1 -> output value
    // r2 -> GPIO-Pin

    //! still r1 is referenced here
    cmp r1, #0
    addhi r0, #28
    addls r0, #40

    cmp r2, #31
    subhi r2, #32
    addhi r0, #4

    mov r3, #1
    lsl r3, r2

    str r3, [r0]

    pop {pc}

/**
 * \brief sets the PIN into the mode we want
 * \param r0 GPIO-Pin
 * \param r1 GPIO-Mode
 * \return NONE
 **/
gpio_pinmode:
    /** if (r0>53 || r1>7) return **/
    cmp r0, #53
    cmpls r1, #7
    movhi pc, lr

    push {lr};

    // save r0 (the pinnumber)
    mov r2, r0
    bl gpio_address;
    // r0 => GPIO_address
    // r1 => mode
    // r2 => pin

    pincalc:
        cmp r2, #9
        subhi r2, #10
        addhi r0, #4
        bhi pincalc

    // r0 => correct address for the pin
    // r1 => mode (0-7)
    // r2 => pin-offset (0-9)

    // r2 = r2*3, because we need to shift r1 by r2
    add r2, r2, lsl #1
    lsl r1, r2

    push {r4,r6}
    mov r6, #7 // 000...00111
    lsl r6, r2
    mvn r6, r6    

    // get the currently set GPIO-register-values 
    ldr r4, [r0,r2]
    // mask the according values to 0
    and r4, r4, r6
    // add the mode to the mode-vector
    orr r4, r4, r1
    // store the mode-vector back to the GPIO-memory
    str r4, [r0]

    pop {r4,r6}
    // return
    pop {pc}

timer.s

.globl delay_ms
.globl delay

/**
 * \brief returns the base address of the timer
 **/
counteraddr:
    ldr r0, =0x20003000
    mov pc, lr

/**
 * \brief waits for the given amount of seconds
 * \param r0 amount of seconds 
 * \note is maybe not that exact :/
 **/
delay:
    push {r5}
    mov r5, r0
    // using ldr, not 'mov r0, #1000000' because immediate
    // is not big enough to hold decimal 1000000
    ldr r0, =1000000
delay_inner:
    cmp r5, #0    
    bls delay_out;

    sub r5, #1
    b delay_ms
    b delay_inner

delay_out:
    pop {r5}
    mov pc, lr

/**
 * \brief waits for the given amount of microseconds
 * \param r0 amount of microseconds
 **/
delay_ms:
    push {lr}
    push {r6,r7}
    mov r4, r0
    bl counteraddr
    ldr r2, [r0, #4]
    ldr r3, [r0, #8]
    add r1, r2, r4
    addcs r3, #1

delay_ms_inner:    
    ldr r6, [r0, #4]
    ldr r7, [r0, #8]
    cmp r1, r6
    cmpls r3, r7
    bhi delay_ms_inner
    pop {r6, r7}
    pop {pc}

Letzte Bearbeitung: 30.12.2015 14:39