Points Delicats was my final project for a class in Computer Organization and Machine Language. The assignment required we build a device that interacts a Raspberry Pi with hardware, with the caveat that all GPIO interactions must be written in assembly. Since one of my teammates is interested in building devices for the visually impaired, we settled on displaying a braille message on a LED matrix based upon text entered into a console. Points Delicats acts mostly as a proof of concept since displaying braille as LED light may not be the most useful thing ever made. This post details my contributions to the project.

The Project

We settled on Python as our main language due to the ease of string parsing and built-in library support. Converting an ASCII character to a braille character in python is as simple as adding a 2-D list of 0s and 1s together to represent whether a dot in the 2x3 braille character grid is turned on or off. Due to time constraints, we decided to convert all letters to lowercase and limit the characters to letters and numbers, while inserting a question mark for any invalid characters.

Doing all of this is rather trivial in Python and allowed us to focus on getting the assembly interacting with the GPIO. In fact, This project inspired me to write two different tutorials based upon this aspect of the project – extending python with assembly and interacting with the Raspberry Pi GPIO in ARMv7. To get our Python Code playing nicely with the GPIO, we wrote assembly functions for the individual steps of accessing the GPIO, which end up linked together in a shared library that extends python. The assembly functions is based heavily upon Robert G. Plantz’s code for solving a similar problem to our own.

Here is the assembly code for setting a particular pin to a digital high state:

// gpio_set.s
// Sets a gpio pin. Assumes that GPIO registers have been mapped to programming
// memory
// Calling scheme:
//      r0, GPIO_ADDR
//      r1, PIN_NUMBER
//      bl gpioSet

// Define Raspi
    .cpu    cortex-a53
    .fpu    neon-fp-armv8
    .syntax unified

// Constants
    .equ    PIN, 1
    .equ    PINS_IN_REG, 32
    .equ    GPSET0, 0x1c            // set register offset

.text
.align 2
.global gpioSet
.type gpioSet, %function

gpioSet:
    sub     sp, sp, #24
    str     r4, [sp, #0]
    str     r5, [sp, #4]
    str     r6, [sp, #8]
    str     fp, [sp, #12]
    str     lr, [sp, #16]
    add     fp, sp, #16
                                    // (for the example math)
    mov     r5, r1                  // pin number = 34

    add     r4, r0, GPSET0          // r4 = 0x1c <--offset for *GPSETn regs

// compute addres of GPSET register and pin field
    mov     r3, PINS_IN_REG         // r3 = 32
    udiv    r0, r5, r3              // *GPSETn r0 = r5 / r3 = 1

    mul     r1, r0, r3              // r1 = r0 * r3 = 32
    sub     r1, r5, r1              // r1 = r5 - r1 = 2

    lsl     r0, r0, #2              // r0 = r0 << 2 = 0x04
    add     r0, r0, r4              // r0 = r0 + *GPSET = 0x20 (*GPSET1)

    ldr     r2, [r0]                // r2 = *GPSET1
    mov     r3, PIN                 // r3 = 1
    lsl     r3, r3, r1              // r3 = r3 << r1 = 0x00000002
    orr     r2, r2, r3              // r2 = r2 | r3 (write the pin)
    str     r2, [r0]                // GPSET1 = r2

    mov     r0, #0                  // return 0
    ldr     r4, [sp, #0]            // restore the stack
    ldr     r5, [sp, #4]
    ldr     r6, [sp, #8]
    ldr     fp, [sp, #12]
    ldr     lr, [sp, #16]

    add     sp, sp, #24
    bx      lr

This code is wrapped in a Python C extension. The C Extension is linked to an object file built from the assembly code, which allows it to be brought in to the Python library like so:

static PyObject *method_pin_write(PyObject *self, PyObject *args) {
    uint32_t pin, value;
    extern void gpioSet(uint32_t*, int);
    extern void gpioClr(uint32_t*, int);
    if (!PyArg_ParseTuple(args, "ip", &pin, &value)) {
        Py_INCREF(Py_None);
        return Py_None;
    }
    if (value) {
        gpioSet(gpio_memory, pin);
    }
    else {
        gpioClr(gpio_memory, pin);
    }
    Py_INCREF(Py_None);
    return Py_None;
}

This allows us to call a function pin_write() in Python which made the logic turning a pin on or off much simpler. The rest of the code is available on the team’s Github.

The Hardware

The hardware uses two shift-registers to control an 8x8 LED Matrix. The circuit design is by Freenove’s example on how to use an 8x8 LED Matrix. The shift registers are cycled extremely quickly for a set amount of time to display the correct dots on the Matrix.