Meross MSS310

Part 4: Running my own blink application

Since we now know how to control the LEDs, it’s time to write our very first standalone application to be run on the MSS310. Inspired from Nicholas FitzRoy-Dale’s bare-metal hello world, the following code parts resulted in a working blink application:

File ‘‘mipsregs.h’’:

#define t0 $8  /* temporary values */
#define sp $29 /* stack pointer */

File ‘‘start.s’’:

#include "mipsregs.h"
.globl _start
.ent _start
.text
_start:
    li sp, 0x1100000
    la t0, entrypoint
    jr t0
    nop
.end _start

File ‘‘blink.c’’, the application. Cycles through all LED on/off combinations. Pressing the device’s button resets the device.

#include <stdint.h>

#define RSTCTL          0x10000034
#define GPIO2_MODE      0x10000064
#define GPIO_CTRL_1     0x10000604
#define GPIO_DATA_1     0x10000624
#define GPIO_DSET_1     0x10000634
#define GPIO_DCLR_1     0x10000644

#define SYS_RST            (1 << 0)

#define GPIO_RELAY         (1 << 0)
#define GPIO_LED_PIN_RED   (1 << 1)
#define GPIO_LED_PIN_GREEN (1 << 2)
#define GPIO_BUTTON        (1 << 3)

static inline void write_l(unsigned int addr, unsigned int val) {
    volatile uint32_t *ptr = (uint32_t *)(addr);
    *ptr = val;
}

static inline unsigned int read_l(unsigned int addr) {
    volatile uint32_t *ptr = (uint32_t *)(addr);
    return *ptr;
}

static void reset_if_button_pressed() {
    if (!(read_l(GPIO_DATA_1) & GPIO_BUTTON)) {
        write_l(RSTCTL, SYS_RST);
    }
}

static void delay() {
    volatile int i, j;
    for (i = 0; i < 1000; i++) {
        reset_if_button_pressed();
        for (j = 0; j < 500; j++) /* do nothing */;
    }
}

void entrypoint(void) {
    write_l(GPIO2_MODE, 0x05550550);
    write_l(GPIO_CTRL_1, GPIO_RELAY | GPIO_LED_PIN_RED | GPIO_LED_PIN_GREEN);
    while (1) {
        write_l(GPIO_DCLR_1, GPIO_LED_PIN_RED);
        delay();
        write_l(GPIO_DCLR_1, GPIO_LED_PIN_GREEN);
        delay();
        write_l(GPIO_DSET_1, GPIO_LED_PIN_RED);
        delay();
        write_l(GPIO_DSET_1, GPIO_LED_PIN_GREEN);
        delay();
    }
}

File ‘‘linker.lds’’.

OUTPUT_ARCH(mips)
ENTRY(_start)
SECTIONS {
    . = 0x1000000;     /* Our base address */
    .text : {          /* Code */
        *(.text)
    }
    .rodata : {        /* Static data */
        *(.rodata)
        *(.rodata.*)
    }
    .data : {          /* non-static data */
        *(.data*)
    }
}

Finally, the ‘‘Makefile’’ to glue all this together.

XTOOLS = /opt/x-tools/mipsel-unknown-elf/bin

AS = $(XTOOLS)/mipsel-unknown-elf-as -mips32
CC = $(XTOOLS)/mipsel-unknown-elf-gcc
LD = $(XTOOLS)/mipsel-unknown-elf-ld
OBJCOPY = $(XTOOLS)/mipsel-unknown-elf-objcopy
CFLAGS = -Os

OBJS = start.o blink.o

blink.bin: blink.elf
    $(OBJCOPY) -O binary $< $@

blink.elf: $(OBJS)
    $(LD) -T linker.lds -o $@ $+

%.o: %.[Sc]
    $(CC) $(CFLAGS) -c -o $@ $<

clean:
    rm -f *.o *.elf *.bin

Build blink.bin, then run loadb 1000000 from the u-boot command line and use kermit to upload the bin file. Run go 1000000 and voilà.

Now it’s time to forget all this messy code (which just serves as a proof-of-concept) and start writing a real application. In addition, we still need to find out how to correctly talk to the chip’s embedded WiFi.