Browse Source

Added source code.

Daniele Lacamera 4 years ago
parent
commit
c6b1b628bb
34 changed files with 3404 additions and 0 deletions
  1. 4 0
      .gdbinit
  2. 46 0
      Makefile
  3. 57 0
      adc.c
  4. 6 0
      adc.h
  5. 331 0
      button.c
  6. 16 0
      button.h
  7. 135 0
      display.c
  8. 56 0
      display.h
  9. BIN
      font_twisted.c
  10. 235 0
      i2c.c
  11. 12 0
      i2c.h
  12. 41 0
      led.c
  13. 15 0
      led.h
  14. 136 0
      main.c
  15. 37 0
      mem.c
  16. 47 0
      mutex.S
  17. 35 0
      newlib.c
  18. 105 0
      spi.c
  19. 11 0
      spi_drv.h
  20. 254 0
      spi_flash.c
  21. 9 0
      spi_flash.h
  22. 208 0
      startup.c
  23. 172 0
      system.c
  24. 503 0
      system.h
  25. 42 0
      systick.c
  26. 5 0
      systick.h
  27. 49 0
      target.ld
  28. 85 0
      timer.c
  29. 132 0
      uart.c
  30. 9 0
      uart.h
  31. 178 0
      ui.c
  32. 32 0
      ui.h
  33. 345 0
      ui_tester.c
  34. 56 0
      user_settings.h

+ 4 - 0
.gdbinit

@@ -0,0 +1,4 @@
+tar rem:3333
+file image.elf
+foc c
+

+ 46 - 0
Makefile

@@ -0,0 +1,46 @@
+CROSS_COMPILE:=arm-none-eabi-
+CC:=$(CROSS_COMPILE)gcc
+LD:=$(CROSS_COMPILE)gcc
+VERSION?=1
+
+OBJS:=startup.o main.o system.o  mem.o led.o \
+	i2c.o display.o font_twisted.o button.o systick.o newlib.o uart.o ui.o timer.o\
+	mutex.o ui_tester.o adc.o spi.o
+
+
+UMX:=lib/unicore-mx/lib/libucmx_stm32f4.a
+UMXFLAGS:=-Ilib/unicore-mx/include/ -DSTM32F4
+LIBS+=$(UMX)
+
+
+LSCRIPT:=target.ld
+
+OBJCOPY:=$(CROSS_COMPILE)objcopy
+
+CFLAGS:=-mcpu=cortex-m3 -mthumb -Wall -Wno-main -Wstack-usage=320 \
+   	-ffreestanding -Wno-unused -DBLOCK_SIZE=4096 -I. -Ilib/unicore-mx/include \
+    -ffunction-sections -fdata-sections
+CFLAGS+=-specs=nano.specs -lc -lg 
+CFLAGS+=$(UMXFLAGS)
+CFLAGS+=-O3
+#CFLAGS+=-g -ggdb3
+
+ASFLAGS:=$(CFLAGS)
+
+LDFLAGS:=-T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map -mcpu=cortex-m3 -mthumb -nostartfiles
+
+#all: image.bin
+
+image.bin: image.elf
+	$(OBJCOPY) -O binary $^ $@
+
+image.elf: $(LIBS) $(OBJS) $(LSCRIPT)
+	$(LD) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
+
+$(UMX):
+	make -C lib/unicore-mx FP_FLAGS="-O3 -mfloat-abi=soft" TARGETS=stm32/f4
+
+clean:
+	@rm -f image.bin image.elf *.o image.map $(USECFS)/src/*.o
+	make -C lib/unicore-mx clean
+

+ 57 - 0
adc.c

@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright (c) 2019
+ * Author: Daniele Lacamera <root@danielinux.net>
+ * GPL2.0
+ */
+#include <stdint.h>
+#include "adc.h"
+#include "system.h"
+
+
+int adc_init(void)
+{
+    int i;
+    uint32_t val;
+    /* Enable clock */
+    APB2_CLOCK_ER |= ADC1_APB2_CLOCK_ER_VAL;
+
+    /* Power off */
+    ADC1_CR2 &= ~(ADC_CR2_EN);
+
+    /* Set common clock prescaler */
+    ADC_COM_CCR &= ~(0x03 << 16);
+
+    /* Disable scan mode */
+    ADC1_CR1 &= ~(ADC_CR1_SCAN);
+
+    /* Set one-shot (disable continuous mode) */
+    ADC1_CR2 &= ~(ADC_CR2_CONT);
+
+    /* Set sample time for all channels */
+    val = ADC1_SMPR1;
+
+    for (i = 0; i < 10; i++) {
+            val |= ADC_SMPR_SMP_480CYC << (i * 3);
+    ADC1_SMPR1 = val;
+    val = ADC1_SMPR2;
+    for (i = 10; i < 18; i++) 
+            val |= ADC_SMPR_SMP_480CYC << ((i-10) * 3);
+    }
+    ADC1_SMPR2 = val;
+    ADC1_CR2 &= ~(ADC_CR2_EN);
+    return 0;
+}
+
+void adc_pin_val(uint32_t ch, uint16_t *val)
+{
+        ADC1_SQR3 = ch;
+        ADC1_CR2 |= ADC_CR2_EN;
+        ADC1_CR2 |= ADC_CR2_SWSTART;
+        while (ADC1_CR2 & ADC_CR2_SWSTART);;
+        while ((ADC1_SR & ADC_SR_EOC) == 0);;
+        *val= ADC1_DR;
+        printf("Channel %d val %hu\r\n", ch, *val);
+        ADC1_SQR3 = 0;
+}
+

+ 6 - 0
adc.h

@@ -0,0 +1,6 @@
+#ifndef ADC_H_INCLUDED
+#define ADC_H_INCLUDED
+int adc_init(void);
+int adc_read(void);
+void adc_pin_val(uint32_t pin, uint16_t *val);
+#endif

+ 331 - 0
button.c

@@ -0,0 +1,331 @@
+/* 
+ * (c) danielinux 2019
+ * GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "system.h"
+#include "button.h"
+#include "systick.h"
+#include "unicore-mx/stm32/gpio.h"
+#include "unicore-mx/stm32/exti.h"
+#include "unicore-mx/stm32/rcc.h"
+#include "unicore-mx/stm32/f4/rcc.h"
+#include "unicore-mx/stm32/adc.h"
+#include "unicore-mx/stm32/f4/adc.h"
+#include "unicore-mx/stm32/f4/nvic.h"
+
+// Uncomment to enable debug
+//#define BUTTON_DEBUG
+
+
+#ifdef BUTTON_DEBUG
+#   define DBG printf
+#else
+#   define DBG(...) do {} while (0)
+#endif
+
+#define BUTTON_DEBOUNCE_TIME 50
+#define BUTTON_HOLD_TIME 1500
+
+
+
+static volatile int button_press_pending = 0;
+
+static void input_run(uint32_t ev, void *arg);
+static void input_init(void);
+
+const char button_task_name[] = "Input";
+
+// PA8 + PA9 // ROTARY encoder
+// PC0 // BUTTON
+
+void pin_exti_init(void)
+{
+    int i;
+    uint32_t exti_irq;
+    rcc_periph_clock_enable(RCC_SYSCFG);
+    rcc_periph_clock_enable(RCC_GPIOA);
+    gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO9 | GPIO8 | GPIO7 | GPIO6 | GPIO5);
+    nvic_enable_irq(NVIC_EXTI9_5_IRQ);
+    nvic_set_priority(NVIC_EXTI9_5_IRQ, 1);
+    exti_select_source(GPIO5, GPIOA);
+    exti_select_source(GPIO6, GPIOA);
+    exti_select_source(GPIO7, GPIOA);
+    exti_select_source(GPIO8, GPIOA);
+    exti_select_source(GPIO9, GPIOA);
+}
+
+void pin_exti_start_read(void)
+{
+    exti_set_trigger(GPIO5|GPIO6|GPIO7|GPIO8|GPIO9, EXTI_TRIGGER_BOTH);
+    exti_enable_request(GPIO5);
+    exti_enable_request(GPIO6);
+    exti_enable_request(GPIO7);
+    exti_enable_request(GPIO8);
+    exti_enable_request(GPIO9);
+}
+
+
+static void button_setup(void)
+{
+    int i;
+    uint32_t exti_irq;
+    rcc_periph_clock_enable(RCC_SYSCFG);
+    rcc_periph_clock_enable(RCC_GPIOA);
+    rcc_periph_clock_enable(RCC_GPIOB);
+    rcc_periph_clock_enable(RCC_GPIOC);
+
+    gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO10);
+    gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO4);
+    gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO0);
+    
+    nvic_enable_irq(NVIC_EXTI4_IRQ);
+    nvic_enable_irq(NVIC_EXTI0_IRQ);
+    nvic_enable_irq(NVIC_EXTI15_10_IRQ);
+    nvic_set_priority(NVIC_EXTI4_IRQ, 1);
+    nvic_set_priority(NVIC_EXTI0_IRQ, 1);
+    nvic_set_priority(NVIC_EXTI15_10_IRQ, 1);
+    
+
+    exti_select_source(GPIO10, GPIOA);
+    exti_select_source(GPIO4, GPIOB);
+    exti_select_source(GPIO0, GPIOC);
+}
+
+enum button_state {
+    IDLE = 0,
+    PRESSING,
+    PRESSED,
+    HOLD,
+    RELEASING,
+};
+
+static volatile enum button_state button_status = 0;
+static volatile uint32_t rot_up = 0;
+static volatile uint32_t rot_down = 0;
+
+static void button_start_read(void)
+{
+    exti_set_trigger(GPIO0, EXTI_TRIGGER_RISING);
+    exti_set_trigger(GPIO10|GPIO4, EXTI_TRIGGER_RISING);
+    exti_enable_request(GPIO10);
+    exti_enable_request(GPIO4);
+    exti_enable_request(GPIO0);
+}
+
+void isr_exti_rot1(void)
+{
+    exti_reset_request(GPIO4);
+    if (gpio_get(GPIOA, GPIO10) == 0)
+        return;
+    rot_down++;
+}
+
+void isr_exti_rot0(void)
+{
+    exti_reset_request(GPIO10);
+    if (gpio_get(GPIOB, GPIO4) == 0)
+        return;
+    rot_up++;
+}
+
+void isr_exti_button(void)
+{
+    button_press_pending = !!(gpio_get(GPIOC, GPIO0));
+    exti_reset_request(GPIO0);
+}
+
+static uint32_t t0s, t0us;
+static uint32_t t1s, t1us;
+
+static uint32_t time_diff_ms(uint32_t s_a, uint32_t us_a, uint32_t s_b, uint32_t us_b)
+{
+    uint32_t res = 0;
+    res = (s_a - s_b) * 1000;
+    if (us_b > us_a) {
+        us_a += 1000000;
+        res -= 1000;
+    }
+    res += (us_a - us_b) / 1000;
+    return res;
+}
+
+typedef void (*exti_trigger_callback_fn)(uint32_t pin, uint8_t front, uint32_t s, uint32_t us);
+
+static exti_trigger_callback_fn exti_trigger_callback[5];
+
+void exti_set_callback(uint32_t pin, void (*cb)(uint32_t pin, uint8_t front, uint32_t s, uint32_t us))
+{
+    int i;
+    for (i = 0; i < 5; i++) {
+        if (Channel_Pin[i] == pin) {
+            DBG("Setting callback for EXTI on ch %d\r\n", i);
+            exti_trigger_callback[i] = cb;
+            return;
+        }
+    }
+}
+
+void exti_clear_callback(uint32_t pin)
+{
+    int i;
+    for (i = 0; i < 5; i++) {
+        if (Channel_Pin[i] == pin) {
+            DBG("Clearing callback for EXTI on ch %d\r\n", i);
+            exti_trigger_callback[i] = NULL;
+            return;
+        }
+    }
+}
+
+struct trig_timeval {
+    int channel;
+    uint32_t pin;
+    int front;
+    uint32_t tv_sec;
+    uint32_t tv_usec;
+};
+
+static struct trig_timeval trig_tv[32] = {};
+static volatile uint8_t trig_pending = 0;
+
+void isr_exti_channel(void)
+{
+    uint32_t ts, tus;
+    gettime(&ts, &tus);
+    DBG("EXTI\r\n");
+    if (exti_get_flag_status(GPIO9)) {
+        DBG("c1\r\n");
+        if (exti_trigger_callback[1]) {
+            gettime(&ts, &tus);
+            trig_tv[trig_pending].channel = 1;
+            trig_tv[trig_pending].pin = GPIO9;
+            trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO9);
+            trig_tv[trig_pending].tv_sec = ts;
+            trig_tv[trig_pending].tv_usec = tus;
+            trig_pending++;
+        }
+        exti_reset_request(GPIO9);
+    }
+    if (exti_get_flag_status(GPIO8)) {
+        DBG("c0\r\n");
+        if (exti_trigger_callback[0]) {
+            gettime(&ts, &tus);
+            trig_tv[trig_pending].channel = 0;
+            trig_tv[trig_pending].pin = GPIO8;
+            trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO8);
+            trig_tv[trig_pending].tv_sec = ts;
+            trig_tv[trig_pending].tv_usec = tus;
+            trig_pending++;
+        }
+        exti_reset_request(GPIO8);
+    }
+    if (exti_get_flag_status(GPIO7)) {
+        if (exti_trigger_callback[2]) {
+            gettime(&ts, &tus);
+            trig_tv[trig_pending].channel = 2;
+            trig_tv[trig_pending].pin = GPIO7;
+            trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO7);
+            trig_tv[trig_pending].tv_sec = ts;
+            trig_tv[trig_pending].tv_usec = tus;
+            trig_pending++;
+        }
+        exti_reset_request(GPIO7);
+    }
+    if (exti_get_flag_status(GPIO6)) {
+        if (exti_trigger_callback[3]) {
+            gettime(&ts, &tus);
+            trig_tv[trig_pending].channel = 3;
+            trig_tv[trig_pending].pin = GPIO6;
+            trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO6);
+            trig_tv[trig_pending].tv_sec = ts;
+            trig_tv[trig_pending].tv_usec = tus;
+            trig_pending++;
+        }
+        exti_reset_request(GPIO6);
+    }
+    if (exti_get_flag_status(GPIO5)) {
+        if (exti_trigger_callback[4]) {
+            gettime(&ts, &tus);
+            trig_tv[trig_pending].channel = 4;
+            trig_tv[trig_pending].pin = GPIO5;
+            trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO5);
+            trig_tv[trig_pending].tv_sec = ts;
+            trig_tv[trig_pending].tv_usec = tus;
+            trig_pending++;
+        }
+        exti_reset_request(GPIO5);
+    }
+}
+
+void exti_poll(void)
+{
+    int i;
+    uint32_t pin;
+    for (i = 0; i < trig_pending; i++) {
+        if (exti_trigger_callback[trig_tv[i].channel]) {
+            DBG("EXTI cb!\r\n");
+            exti_trigger_callback[trig_tv[i].channel](trig_tv[i].pin, trig_tv[i].front, trig_tv[i].tv_sec, trig_tv[i].tv_usec);
+        }
+    }
+    trig_pending = 0;
+}
+
+/* Button interface */
+struct user_button 
+{
+    enum button_state state;
+    uint32_t transition_start_timestamp;
+};
+
+static struct user_button Buttons[N_BUTTONS];
+
+
+
+int button_poll(void (*callback)(uint8_t press, int hold))
+{
+    int b = 0;
+    int st;
+    static uint32_t last_event = 0;
+    static uint32_t pressed_start = 0;
+    while(rot_down > 0) {
+        callback('-',0);
+        rot_down--;
+    }
+    while(rot_up > 0) {
+        callback('+',0);
+        rot_up--;
+    }
+
+    if (jiffies - last_event < BUTTON_DEBOUNCE_TIME)
+        return;
+    last_event = jiffies;
+    st = !!gpio_get(GPIOC, GPIO0);
+    if (!st) {
+        pressed_start = 0;
+    }
+
+    if ((button_press_pending) && st) {
+        if ((pressed_start == 0) || ((jiffies - pressed_start) > BUTTON_HOLD_TIME)) {
+            pressed_start = jiffies;
+            callback('*', 0);
+            return 1;
+        }
+    }
+    button_press_pending = 0;
+    return 0;
+}
+
+void button_init(void)
+{
+    memset(Buttons, 0, sizeof(struct user_button) * N_BUTTONS);
+    button_setup();
+    button_start_read();
+}
+
+

+ 16 - 0
button.h

@@ -0,0 +1,16 @@
+#ifndef BUTTON_H_INCLUDED
+#define BUTTON_H_INCLUDED
+#include "unicore-mx/stm32/gpio.h"
+
+static const uint32_t Channel[5] = { 8, 9, 7, 6, 5 };
+static const uint32_t Channel_Pin[5] = { GPIO8, GPIO9, GPIO7, GPIO6, GPIO5 };
+static const char Channel_name[5][16] = { "Red", "Purple", "Green", "Blue", "Yellow" };
+
+
+void button_init(void);
+int button_poll(void (*callback)(uint8_t press, int hold));
+int input_get_swr(void);
+
+#define N_BUTTONS 2 
+
+#endif

+ 135 - 0
display.c

@@ -0,0 +1,135 @@
+/* 
+ * (c) danielinux 2019
+ * GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "display.h"
+#include "systick.h"
+#include "system.h"
+
+
+static uint8_t contrast = 0xcf;
+
+
+void display_scroll(void *priv, uint8_t line)
+{
+    display_send_cmd1(priv, SSD1306_SETSTARTLINE, (line % 0x40) + 0x40);
+}
+
+void display_setcontrast(void *priv, uint8_t c)
+{
+    contrast = c; 
+    display_send_cmd1(priv, SSD1306_SETCONTRAST, contrast);
+}
+
+uint8_t display_getcontrast(void *priv)
+{
+    return contrast;
+}
+
+void display_clear(void *priv)
+{
+
+    int i,j;
+    uint8_t zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0 };
+    display_send_cmd(NULL, 0x00);
+    display_send_cmd(NULL, 0x10);
+
+    for (i = 0; i < 8; i++)
+    {
+        display_send_cmd2(NULL, SSD1306_PAGEADDR, i, 0x00);
+        display_send_cmd2(NULL, SSD1306_COLUMNADDR, 0, WIDTH - 1);
+        for (j = 0; j < 16; j++)
+            display_send_data(NULL, zeros, 8);
+    }
+    WFI();
+}
+
+void display_text(int row, const char *text)
+{
+    int k;
+    uint8_t zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0 };
+    int len = strlen(text);
+    if (len > 15)
+        len = 15;
+
+    display_send_cmd(NULL, 0x00);
+    display_send_cmd(NULL, 0x10);
+    WFI();
+    display_send_cmd2(NULL, SSD1306_PAGEADDR, 7 - row, 0x00);
+    display_send_cmd2(NULL, SSD1306_COLUMNADDR, 0, WIDTH - 1);
+    for(k = 0; k < len; k++)
+        display_send_data(NULL, fb_font[text[k]], 8);
+    for(; k < 16; k++)
+        display_send_data(NULL, zeros, 8);
+}
+
+void display_text_inverse(int row, const char *text)
+{
+    int k, j;
+    uint8_t inv_buf[8];
+    uint8_t ones[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+    int len = strlen(text);
+    if (len > 15)
+        len = 15;
+    display_send_cmd(NULL, 0x00);
+    display_send_cmd(NULL, 0x10);
+    WFI();
+    display_send_cmd2(NULL, SSD1306_PAGEADDR, 7 - row, 0x00);
+    display_send_cmd2(NULL, SSD1306_COLUMNADDR, 0, WIDTH - 1);
+    for(k = 0; k < len; k++) {
+        for (j = 0; j < 8; j++)
+            inv_buf[j] = ~fb_font[text[k]][j];
+        display_send_data(NULL, inv_buf, 8);
+    }
+    for(; k < 16; k++)
+        display_send_data(NULL, ones, 8);
+}
+
+int display_init(void *priv)
+{
+    int i;
+    int k = 0;
+    int row = 0;
+    volatile int j;
+    uint8_t dbuf2[64] = {};
+    int page = 0;
+    int seg = 0;
+    volatile uint32_t now;
+
+
+    for (i = 1; i < 65; i++) {
+        dbuf2[i] = 0;
+    }
+    display_send_cmd(priv, SSD1306_DISPLAYOFF);
+    display_send_cmd1(priv, 0xD6, 0x01);
+    display_send_cmd(priv, 0xA1);
+    display_setcontrast(priv, contrast);
+    display_send_cmd1(priv, SSD1306_CHARGEPUMP,  0x14);
+    display_send_cmd1(priv, SSD1306_MEMORYMODE,  0x00);
+    display_send_cmd1(priv, SSD1306_SETCOMPINS, 0x12);
+    display_send_cmd1(priv, SSD1306_SETDISPLAYOFFSET, 0x00);
+    display_send_cmd1(priv, SSD1306_SETVCOMDETECT, 0x00);
+    display_send_cmd1(priv, SSD1306_SETMULTIPLEX, 63);
+    display_send_cmd(priv, SSD1306_COMSCANINC);
+    display_send_cmd(priv, SSD1306_DISPLAYALLON_RESUME);
+    display_send_cmd(priv, SSD1306_DISPLAYON);
+    display_send_cmd(priv, 0x2E);
+    
+    display_send_cmd2(priv, SSD1306_PAGEADDR, 0, 0xFF);
+    display_send_cmd2(priv, SSD1306_COLUMNADDR, 0, WIDTH - 1);
+    display_send_cmd1(priv, SSD1306_SETSTARTLINE, 0);
+    display_send_cmd(priv, 0x00);
+    display_send_cmd(priv, 0x10);
+    for (page = 0; page < 8; page++) {
+        display_send_cmd2(priv, SSD1306_PAGEADDR, page, 0xFF);
+        for (seg= 0; seg < 32; seg++) {
+            display_send_data(priv, dbuf2, 8);
+        }
+        display_send_cmd1(priv, SSD1306_SETSTARTLINE, row);
+    }
+    return 0;
+}

+ 56 - 0
display.h

@@ -0,0 +1,56 @@
+#ifndef DISPLAY_H_INCLUDED
+#define DISPLAY_H_INCLUDED
+#include <stdint.h>
+
+#define WIDTH 128
+#define PIXEL_HEIGHT 64
+#define HEIGHT (PIXEL_HEIGHT / 8)
+extern const unsigned char fb_font[256][8];
+#define SSD1306_MEMORYMODE                              0x20
+#define SSD1306_COLUMNADDR                              0x21
+#define SSD1306_PAGEADDR                                0x22
+#define SSD1306_SETCONTRAST                             0x81
+#define SSD1306_CHARGEPUMP                              0x8D
+#define SSD1306_SEGREMAP                                0xA0
+#define SSD1306_DISPLAYALLON_RESUME                     0xA4
+#define SSD1306_DISPLAYALLON                            0xA5
+#define SSD1306_NORMALDISPLAY                           0xA6
+#define SSD1306_INVERTDISPLAY                           0xA7
+#define SSD1306_SETMULTIPLEX                            0xA8
+#define SSD1306_DISPLAYOFF                              0xAE
+#define SSD1306_DISPLAYON                               0xAF
+#define SSD1306_COMSCANINC                              0xC0
+#define SSD1306_COMSCANDEC                              0xC8
+#define SSD1306_SETDISPLAYOFFSET                        0xD3
+#define SSD1306_SETDISPLAYCLOCKDIV                      0xD5
+#define SSD1306_SETPRECHARGE                            0xD9
+#define SSD1306_SETCOMPINS                              0xDA
+#define SSD1306_SETVCOMDETECT                           0xDB
+#define SSD1306_SETLOWCOLUMN                            0x00
+#define SSD1306_SETHIGHCOLUMN                           0x10
+#define SSD1306_SETSTARTLINE                            0x40
+#define SSD1306_RIGHT_HORIZONTAL_SCROLL                 0x26
+#define SSD1306_LEFT_HORIZONTAL_SCROLL                  0x27
+#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL    0x29
+#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL     0x2A
+#define SSD1306_DEACTIVATE_SCROLL                       0x2E
+#define SSD1306_ACTIVATE_SCROLL                         0x2F
+#define SSD1306_SET_VERTICAL_SCROLL_AREA                0xA3
+
+
+
+/* driver module plug-in (SPI or I2C) */
+void display_send_data(void *priv, const uint8_t *buf, int len);
+void display_send_cmd(void *priv, uint8_t cmd);
+void display_send_cmd1(void *priv, uint8_t cmd, uint8_t arg1);
+void display_send_cmd2(void *priv, uint8_t cmd, uint8_t arg1, uint8_t arg2);
+
+int display_init(void *priv);
+void display_text(int row, const char *text);
+void display_text_inverse(int row, const char *text);
+void display_scroll(void *priv, uint8_t line);
+void display_setcontrast(void *priv, uint8_t c);
+uint8_t display_getcontrast(void *priv);
+void display_clear(void *priv);
+    
+#endif

BIN
font_twisted.c


+ 235 - 0
i2c.c

@@ -0,0 +1,235 @@
+/* 
+ * (c) danielinux 2019
+ * GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include "system.h"
+#include "i2c.h"
+#include "display.h"
+#define DISPLAY_I2C_ADDR 0x3C
+
+
+#define I2C1 (0x40005400)
+#define APB1_SPEED_IN_MHZ (42)
+#define I2C1_CR1        (*(volatile uint32_t *)(I2C1))
+#define I2C1_CR2        (*(volatile uint32_t *)(I2C1 + 0x04))
+#define I2C1_OAR1       (*(volatile uint32_t *)(I2C1 + 0x08))
+#define I2C1_OAR2       (*(volatile uint32_t *)(I2C1 + 0x0c))
+#define I2C1_DR         (*(volatile uint32_t *)(I2C1 + 0x10))
+#define I2C1_SR1        (*(volatile uint32_t *)(I2C1 + 0x14))
+#define I2C1_SR2        (*(volatile uint32_t *)(I2C1 + 0x18))
+#define I2C1_CCR        (*(volatile uint32_t *)(I2C1 + 0x1c))
+#define I2C1_TRISE      (*(volatile uint32_t *)(I2C1 + 0x20))
+
+#define I2C_CR1_ENABLE               (1 << 0)
+#define I2C_CR1_START			     (1 << 8)
+#define I2C_CR1_STOP			     (1 << 9)
+#define I2C_CR1_ACK			         (1 << 10)
+#define I2C_CR2_FREQ_MASK            (0x3ff)
+#define I2C_CCR_MASK                 (0xfff)
+#define I2C_TRISE_MASK                (0x3f)
+
+
+#define I2C_SR1_START                   (1 << 0)
+#define I2C_SR1_TX_BTF                  (1 << 2)
+#define I2C_SR1_ADDR_SENT               (1 << 1)
+#define I2C_SR1_RX_NOTEMPTY  	        (1 << 6)
+#define I2C_SR1_TX_EMPTY			    (1 << 7)
+
+
+#define I2C_SR2_MASTER              (1 << 0)
+#define I2C_SR2_BUSY                (1 << 1)
+#define I2C_SR2_XMIT                (1 << 2)
+
+#define APB1_CLOCK_ER           (*(volatile uint32_t *)(0x40023840))
+#define APB1_CLOCK_RST          (*(volatile uint32_t *)(0x40023820))
+#define I2C1_APB1_CLOCK_ER_VAL 	(1 << 21)
+
+
+static void i2c1_pins_setup(void)
+{
+    uint32_t reg;
+    AHB1_CLOCK_ER |= GPIOB_AHB1_CLOCK_ER;
+    /* Set mode = AF */
+    reg = GPIOB_MODE & ~ (0x03 << (I2C1_SCL * 2));
+    GPIOB_MODE = reg | (2 << (I2C1_SCL * 2));
+    reg = GPIOB_MODE & ~ (0x03 << (I2C1_SDA * 2));
+    GPIOB_MODE = reg | (2 << (I2C1_SDA * 2));
+
+    /* Alternate function: */
+    reg =  GPIOB_AFH & ~(0xf << ((I2C1_SCL - 8) * 4));
+    GPIOB_AFH = reg | (I2C1_PIN_AF << ((I2C1_SCL - 8) * 4));
+    reg =  GPIOB_AFH & ~(0xf << ((I2C1_SDA - 8) * 4));
+    GPIOB_AFH = reg | (I2C1_PIN_AF << ((I2C1_SDA - 8) * 4));
+}
+
+static void i2c1_reset(void)
+{
+    APB1_CLOCK_RST |= I2C1_APB1_CLOCK_ER_VAL;
+    APB1_CLOCK_RST &= ~I2C1_APB1_CLOCK_ER_VAL;
+}
+
+static void i2c1_send_start(void)
+{
+    volatile uint32_t sr1;
+    I2C1_CR1 |= I2C_CR1_START;
+    do {
+        sr1 = I2C1_SR1;
+    } while ((sr1 & I2C_SR1_START) == 0);;
+}
+
+static void i2c1_send_stop(void)
+{
+    I2C1_CR1 |= I2C_CR1_STOP;
+}
+
+void display_send_data(void *priv, const uint8_t *buf, int len)
+{
+    volatile uint32_t sr1, sr2;
+    int i;
+    volatile uint8_t drval;
+    uint32_t start_data = 0x00000040;
+    uint8_t address = DISPLAY_I2C_ADDR;
+    I2C1_CR1 &= ~I2C_CR1_ENABLE;
+    I2C1_CR1 &= ~I2C_CR1_STOP;
+    I2C1_CR1 &= ~I2C_CR1_ACK;
+    I2C1_CR1 |= I2C_CR1_ENABLE;
+
+    /* Wait if the bus is busy */
+    do {
+        sr2 = I2C1_SR2;
+    } while ((sr2 & I2C_SR2_BUSY) != 0);;
+    
+    
+    /* Send a start condition */
+    i2c1_send_start();
+
+    /* Send address + R/W = 0 */
+    I2C1_DR = (address << 1);
+
+    do {
+        sr1 = I2C1_SR1;
+    } while ((sr1 & I2C_SR1_ADDR_SENT) != I2C_SR1_ADDR_SENT);
+
+    do {
+        sr2 = I2C1_SR2;
+    } while ((sr2 & (I2C_SR2_BUSY | I2C_SR2_MASTER)) != (I2C_SR2_BUSY | I2C_SR2_MASTER));;
+
+    I2C1_DR = start_data;
+    do {
+        sr1 = I2C1_SR1;
+    } while ((sr1 & (I2C_SR1_TX_EMPTY)) == 0);
+
+    for (i = 0; i < len; i++) {
+        I2C1_DR = buf[i];
+        do {
+            sr1 = I2C1_SR1;
+            if ((sr1 & I2C_SR1_RX_NOTEMPTY) == I2C_SR1_RX_NOTEMPTY) {
+                drval = I2C1_DR;
+            }
+        } while ((sr1 & (I2C_SR1_TX_EMPTY)) == 0);
+    }
+    while ((sr1 & (I2C_SR1_TX_BTF)) == 0) {
+        sr1 = I2C1_SR1;
+    }
+    i2c1_send_stop();
+}
+
+
+int i2c1_send(uint8_t address, const uint8_t *buf, int len)
+{
+    volatile uint32_t sr1, sr2;
+    int i;
+    volatile uint8_t drval;
+    I2C1_CR1 &= ~I2C_CR1_ENABLE;
+    I2C1_CR1 &= ~I2C_CR1_STOP;
+    I2C1_CR1 &= ~I2C_CR1_ACK;
+    I2C1_CR1 |= I2C_CR1_ENABLE;
+
+    /* Wait if the bus is busy */
+    do {
+        sr2 = I2C1_SR2;
+    } while ((sr2 & I2C_SR2_BUSY) != 0);;
+    
+    
+    /* Send a start condition */
+    i2c1_send_start();
+
+    /* Send address + R/W = 0 */
+    I2C1_DR = (address << 1);
+
+    do {
+        sr1 = I2C1_SR1;
+    } while ((sr1 & I2C_SR1_ADDR_SENT) != I2C_SR1_ADDR_SENT);
+
+    do {
+        sr2 = I2C1_SR2;
+    } while ((sr2 & (I2C_SR2_BUSY | I2C_SR2_MASTER)) != (I2C_SR2_BUSY | I2C_SR2_MASTER));;
+
+    for (i = 0; i < len; i++) {
+        I2C1_DR = buf[i];
+        do {
+            sr1 = I2C1_SR1;
+            if ((sr1 & I2C_SR1_RX_NOTEMPTY) == I2C_SR1_RX_NOTEMPTY) {
+                drval = I2C1_DR;
+            }
+        } while ((sr1 & (I2C_SR1_TX_EMPTY)) == 0);
+    }
+    while ((sr1 & (I2C_SR1_TX_BTF)) == 0) {
+        sr1 = I2C1_SR1;
+    }
+    i2c1_send_stop();
+    return i;
+}
+
+void i2c1_setup(void)
+{
+    uint32_t reg;
+    i2c1_pins_setup();
+    APB1_CLOCK_ER |= I2C1_APB1_CLOCK_ER_VAL;
+    I2C1_CR1 &= ~I2C_CR1_ENABLE;
+    i2c1_reset();
+
+    reg =   I2C1_CR2 & ~(I2C_CR2_FREQ_MASK);
+    I2C1_CR2 = reg | APB1_SPEED_IN_MHZ;
+
+    reg =   I2C1_CCR & ~(I2C_CCR_MASK);
+//    I2C1_CCR = reg | (APB1_SPEED_IN_MHZ * 5);
+  I2C1_CCR = reg | 35;
+        
+    reg = I2C1_TRISE & ~(I2C_TRISE_MASK);
+    I2C1_TRISE = reg | (APB1_SPEED_IN_MHZ + 1);
+
+    I2C1_CR1 |= I2C_CR1_ENABLE;
+}
+
+void i2c_display_init(void)
+{
+    i2c1_setup();
+    display_init(NULL);
+}
+
+void display_send_cmd(void *priv, uint8_t cmd)
+{
+    uint8_t buf[2] = {0x00, cmd};
+    volatile int j;
+    i2c1_send(DISPLAY_I2C_ADDR, buf, 2);
+}
+
+void display_send_cmd1(void *priv, uint8_t cmd, uint8_t arg1)
+{
+    uint8_t buf[3] = {0x00, cmd, arg1};
+    volatile int j;
+    i2c1_send(DISPLAY_I2C_ADDR, buf, 3);
+}
+
+void display_send_cmd2(void *priv, uint8_t cmd, uint8_t arg1, uint8_t arg2)
+{
+    uint8_t buf[4] = {0x00, cmd, arg1, arg2};
+    volatile int j;
+    i2c1_send(DISPLAY_I2C_ADDR, buf, 3);
+}
+

+ 12 - 0
i2c.h

@@ -0,0 +1,12 @@
+#ifndef I2C_H_INCLUDED
+#define I2C_H_INCLUDED
+#include <stdint.h>
+
+
+void i2c1_setup(void);
+void i2c1_write(uint8_t addr, const char byte);
+uint8_t i2c1_read(uint8_t addr);
+int i2c1_send(uint8_t address, const uint8_t *buf, int len);
+int i2c1_send_data(uint8_t address, const uint8_t *buf, int len);
+
+#endif

+ 41 - 0
led.c

@@ -0,0 +1,41 @@
+/* 
+ * (c) danielinux 2019
+ * GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdint.h>
+#include "system.h"
+#include "led.h"
+
+void led_setup(uint32_t led)
+{
+    uint32_t reg;
+    AHB1_CLOCK_ER |= GPIOA_AHB1_CLOCK_ER;
+    reg = GPIOA_MODE & ~ (0x03 << (led * 2));
+    GPIOA_MODE = reg | (1 << (led * 2));
+    reg = GPIOA_PUPD & (0x03 <<  (led * 2));
+    GPIOA_PUPD = reg | (0x02 << (led * 2));
+    led_off(led);
+}
+
+void pio_on(uint32_t pio)
+{
+    GPIOA_BSRR |= (1 << pio);
+}
+
+void pio_off(uint32_t pio)
+{
+    GPIOA_BSRR |= (1 << (pio + 16));
+}
+
+void pio_toggle(uint32_t pio)
+{
+    uint32_t reg;
+    reg = GPIOA_ODR;
+    if ((reg & (1 << pio)) == (1 << pio))
+        pio_off(pio);
+    else
+        pio_on(pio);
+}
+

+ 15 - 0
led.h

@@ -0,0 +1,15 @@
+#ifndef LED_H
+#define LED_H
+
+#include <stdint.h>
+#include "system.h"
+
+void led_setup(uint32_t led);
+void pio_on(uint32_t pio);
+void pio_off(uint32_t pio);
+void pio_toggle(uint32_t pio);
+
+#define led_on pio_on
+#define led_off pio_off
+#define led_toggle pio_toggle
+#endif

+ 136 - 0
main.c

@@ -0,0 +1,136 @@
+/* 
+ * (c) danielinux 2019
+ * GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <string.h>
+#include <stdio.h>
+#include "system.h"
+#include "led.h"
+#include "display.h"
+#include "spi_flash.h"
+#include "uart.h"
+#include "systick.h"
+#include "ui.h"
+#include "button.h"
+#include "adc.h"
+extern uint32_t cpu_freq;
+
+#define BLINK_INTERVAL 500
+
+static int sdcard_detected = 0;
+
+
+static void bootlevel_0(void)
+{
+    clock_pll_on();
+    uart2_setup(115200, 8, 0, 1);
+    systick_enable();
+    WFI();
+    printf("Console on USART2 Enabled.\r\n");
+    printf("\r\n=====================\r\n");
+    printf("Entering bootlevel 0.\r\n");
+    //led_setup(LED);
+    //led_on(LED);
+    printf("System clock started.\r\n");
+    button_init();
+    //pin_exti_init();
+    printf("Buttons initialized.\r\n");
+}
+
+extern void timer_init(void);
+
+static void bootlevel_1(void)
+{
+    uint16_t spiflash_vendor;
+    int sdcard_present;
+    printf("=====================\r\n");
+    printf("Entering bootlevel 1.\r\n");
+
+    printf("Initializing ADC...\r\n");
+    adc_init();
+
+    printf("Activating I2C bus...\r\n");
+    i2c_display_init();
+    printf("Display initialized.\r\n");
+    printf("Displaying splash screen...\r\n");
+    ui_init();
+    printf("UI initialized.\r\n");
+    timer_init();
+
+    printf("System up and running.\r\n\n\n");
+}
+
+static void (*process_input_callback)(uint8_t press, int hold) = NULL;
+static void (*keepalive_callback)(void) = NULL;
+
+void set_input_callback(void (*cb)(uint8_t press, int hold))
+{
+    process_input_callback = cb;
+}
+
+void clear_input_callback(void)
+{
+    process_input_callback = NULL;
+}
+
+void set_keepalive(void (*cb)(void))
+{
+    keepalive_callback = cb;
+}
+
+void clear_keepalive(void)
+{
+    keepalive_callback = NULL;
+}
+
+void process_button(uint8_t press, int hold)
+{
+    if (process_input_callback)
+        process_input_callback(press, hold);
+    else
+        ui_button_press(press, hold);
+}
+
+extern void gettime(uint32_t *s, uint32_t *us);
+int main(void) {
+    int hb = 1;
+    int fs_on = 0;
+    int ret;
+    volatile uint32_t poll_time = 0;
+    uint32_t bstatus = 0;
+
+    const uint32_t hb_len = 400;
+    const uint32_t hb_pulse = 5;
+
+    bootlevel_0();
+    bootlevel_1();
+    //pin_exti_start_read();
+
+    while(1) {
+        hb = 1;
+        //led_on(LED);
+        poll_time = jiffies;
+
+        if (!keepalive_callback)
+            ui_keepalive(hb_len);
+        else
+            keepalive_callback();
+
+        do {
+            WFI();
+            ret = button_poll(process_button);
+            if (jiffies - poll_time > hb_pulse) {
+                if (hb) {
+                    uint32_t s, us;
+                    hb = 0;
+                    //led_off(LED);
+//                    gettime(&s, &us);
+//                    printf("TIME: %u.%05u\r\n", s, us);
+                }
+            }
+        } while ((jiffies - poll_time) < hb_len);
+    }
+    return 0;
+}

+ 37 - 0
mem.c

@@ -0,0 +1,37 @@
+/* 
+ * (c) danielinux 2019
+ * GPLv.2
+ *
+ * See LICENSE for details
+ */
+extern unsigned int _start_heap;
+#define NULL (((void *)0))
+
+
+void * _sbrk(unsigned int incr)
+{
+    static unsigned char *heap = (unsigned char *)&_start_heap;
+    void *old_heap = heap;
+    if (((incr >> 2) << 2) != incr)
+        incr = ((incr >> 2) + 1) << 2;
+
+    if (heap == NULL)
+		heap = (unsigned char *)&_start_heap;
+	else
+        heap += incr;
+    return old_heap;
+}
+
+void * _sbrk_r(unsigned int incr)
+{
+    static unsigned char *heap = NULL;
+    void *old_heap = heap;
+    if (((incr >> 2) << 2) != incr)
+        incr = ((incr >> 2) + 1) << 2;
+
+    if (old_heap == NULL)
+		old_heap = heap = (unsigned char *)&_start_heap;
+    heap += incr;
+    return old_heap;
+}
+

+ 47 - 0
mutex.S

@@ -0,0 +1,47 @@
+
+.syntax unified
+
+/* Lock function.
+ * On success, return 0. 
+ * On failure, return -1 (Locked, try again later).
+ */
+
+.global _mutex_lock
+_mutex_lock:
+   LDREX   r1, [r0]
+   CMP     r1, #0             // Test if mutex holds the value 0
+   BEQ     _mutex_lock_fail   // If it does, return -1
+   SUBS    r1, #1             // If not, decrement temporary copy
+   STREX   r2, r1, [r0]       // Attempt Store-Exclusive
+   CMP     r2, #0             // Check if Store-Exclusive succeeded
+   BNE     _mutex_lock        // If Store-Exclusive failed, retry from start
+   DMB                        // Required before accessing protected resource
+   MOVS    r0, #0             // Successfully locked.
+   BX      lr
+_mutex_lock_fail:
+   DMB
+   MOV     r0, #-1              // Already locked!
+   BX      lr
+
+/* Unlock mutex. 
+ * On success, return 0. 
+ * On failure, return -1 (Already unlocked!).
+ */
+
+.global _mutex_unlock
+_mutex_unlock:
+   LDREX   r1, [r0]
+   CMP     r1, #0               // Test if mutex holds the value 0
+   BNE     _mutex_unlock_fail   // If it does not, it's already unlocked!
+   ADDS    r1, #1               // Increment temporary copy
+   STREX   r2, r1, [r0]         // Attempt Store-Exclusive
+   CMP     r2, #0               // Check if Store-Exclusive succeeded
+   BNE     _mutex_unlock        // Store failed - retry immediately
+   DMB                          // Required before releasing protected resource
+   MOVS    r0, #0               // Successfully unlocked.
+   BX      lr
+_mutex_unlock_fail:
+   DMB
+   MOV     r0, #-1              // Already unlocked!
+   BX      lr
+

+ 35 - 0
newlib.c

@@ -0,0 +1,35 @@
+/* 
+ * (c) danielinux 2019
+ * GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdlib.h>
+#include <stdint.h>
+
+int _close(int fd)
+{
+    return -1;
+}
+
+int _fstat(int fd)
+{
+    return -1;
+}
+
+int _lseek(int fd, int whence, int off)
+{
+    return -1;
+}
+
+int _read(uint8_t *buf, int len)
+{
+    return -1;
+}
+
+int _isatty(int fd)
+{
+    return 1;
+}
+
+

+ 105 - 0
spi.c

@@ -0,0 +1,105 @@
+/* 
+ * (c) danielinux 2019
+ *
+ *      GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdint.h>
+#include "spi_drv.h"
+#include "system.h"
+
+void spi_cs_off(void)
+{
+    GPIOA_BSRR |= (1 << SPI_FLASH_PIN);
+    DMB();
+    while(!(GPIOA_ODR & (1 << SPI_FLASH_PIN)))
+        ;
+}
+
+void spi_cs_on(void)
+{
+    volatile int i;
+    GPIOA_BSRR |= (1 << (SPI_FLASH_PIN + 16));
+    DMB();
+    while(GPIOA_ODR & (1 << SPI_FLASH_PIN))
+        ;
+}
+
+
+static void spi_flash_pin_setup(void)
+{
+    uint32_t reg;
+    AHB1_CLOCK_ER |= GPIOA_AHB1_CLOCK_ER;
+    reg = GPIOA_MODE & ~ (0x03 << (SPI_FLASH_PIN * 2));
+    GPIOA_MODE = reg | (1 << (SPI_FLASH_PIN * 2));
+
+    reg = GPIOA_PUPD & (0x03 <<  (SPI_FLASH_PIN * 2));
+    GPIOA_PUPD = reg | (0x01 << (SPI_FLASH_PIN * 2));
+
+    reg = GPIOA_OSPD & ~(0x03 << (SPI_FLASH_PIN * 2));
+    GPIOA_OSPD |= (0x03 << (SPI_FLASH_PIN * 2));
+
+}
+
+static void spi1_pins_setup(void)
+{
+    uint32_t reg;
+    AHB1_CLOCK_ER |= GPIOA_AHB1_CLOCK_ER;
+    /* Set mode = AF */
+    reg = GPIOA_MODE & ~ (0x03 << (SPI1_CLOCK_PIN * 2));
+    GPIOA_MODE = reg | (2 << (SPI1_CLOCK_PIN * 2));
+    reg = GPIOA_MODE & ~ (0x03 << (SPI1_MOSI_PIN * 2));
+    GPIOA_MODE = reg | (2 << (SPI1_MOSI_PIN * 2));
+    reg = GPIOA_MODE & ~ (0x03 << (SPI1_MISO_PIN * 2));
+    GPIOA_MODE = reg | (2 << (SPI1_MISO_PIN * 2));
+
+    /* Alternate function: use low pins (5,6,7) */
+    reg = GPIOA_AFL & ~(0xf << ((SPI1_CLOCK_PIN) * 4));
+    GPIOA_AFL = reg | (SPI1_PIN_AF << ((SPI1_CLOCK_PIN) * 4));
+    reg = GPIOA_AFL & ~(0xf << ((SPI1_MOSI_PIN) * 4));
+    GPIOA_AFL = reg | (SPI1_PIN_AF << ((SPI1_MOSI_PIN) * 4));
+    reg = GPIOA_AFL & ~(0xf << ((SPI1_MISO_PIN) * 4));
+    GPIOA_AFL = reg | (SPI1_PIN_AF << ((SPI1_MISO_PIN) * 4));
+
+}
+
+static void spi1_reset(void)
+{
+    APB2_CLOCK_RST |= SPI1_APB2_CLOCK_ER_VAL;
+    APB2_CLOCK_RST &= ~SPI1_APB2_CLOCK_ER_VAL;
+}
+
+uint8_t spi_read(void)
+{
+    volatile uint32_t reg;
+    do {
+        reg = SPI1_SR;
+    } while(!(reg & SPI_SR_RX_NOTEMPTY));
+    return (uint8_t)SPI1_DR;
+}
+
+void spi_write(const char byte)
+{
+    int i;
+    volatile uint32_t reg;
+    do {
+        reg = SPI1_SR;
+    } while ((reg & SPI_SR_TX_EMPTY) == 0);
+    SPI1_DR = byte;
+    do {
+        reg = SPI1_SR;
+    } while ((reg & SPI_SR_TX_EMPTY) == 0);
+}
+
+
+void spi_init(int polarity, int phase)
+{
+    spi1_pins_setup();
+    spi_flash_pin_setup();
+    APB2_CLOCK_ER |= SPI1_APB2_CLOCK_ER_VAL;
+    spi1_reset();
+	SPI1_CR1 = SPI_CR1_MASTER | (5 << 3) | (polarity << 1) | (phase << 0);
+	SPI1_CR2 |= SPI_CR2_SSOE;
+    SPI1_CR1 |= SPI_CR1_SPI_EN;
+}

+ 11 - 0
spi_drv.h

@@ -0,0 +1,11 @@
+#ifndef SPI_DRV_H_INCLUDED
+#define SPI_DRV_H_INCLUDED
+#include <stdint.h>
+
+void spi_init(int polarity, int phase);
+void spi_write(const char byte);
+uint8_t spi_read(void);
+void spi_cs_on(void);
+void spi_cs_off(void);
+
+#endif

+ 254 - 0
spi_flash.c

@@ -0,0 +1,254 @@
+/* 
+ * (c) danielinux 2019
+ *
+ *      GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include "system.h"
+#include "spi_drv.h"
+
+#define SPI_FLASH_SECTOR_SIZE (4096)
+#define SPI_FLASH_PAGE_SIZE   (256)
+
+#define MDID            0x90
+#define RDSR            0x05
+#define WRSR            0x01
+#   define ST_BUSY (1 << 0)
+#   define ST_WEL  (1 << 1)
+#   define ST_BP0  (1 << 2)
+#   define ST_BP1  (1 << 3)
+#   define ST_BP2  (1 << 4)
+#   define ST_BP3  (1 << 5)
+#   define ST_AAI  (1 << 6)
+#   define ST_BRO  (1 << 7)
+#define WREN            0x06
+#define WRDI            0x04
+#define SECTOR_ERASE    0x20
+#define BYTE_READ       0x03
+#define BYTE_WRITE      0x02
+#define AUTOINC         0xAD
+#define EWSR            0x50
+#define EBSY            0x70
+#define DBSY            0x80
+
+
+static enum write_mode {
+    WB_WRITEPAGE = 0x00,
+    SST_AAI      = 0x01
+} chip_write_mode = WB_WRITEPAGE;
+
+static void write_address(uint32_t address)
+{
+    spi_write((address & 0xFF00) >> 8);
+    spi_read();
+    spi_write((address & 0xFF0000) >> 16);
+    spi_read();
+    spi_write((address & 0xFF000000) >> 24);
+    spi_read();
+}
+
+static uint8_t read_status(void)
+{
+    uint8_t status;
+    int i;
+    spi_cs_on();
+    spi_write(RDSR);
+    spi_read();
+    spi_write(0xFF);
+    status = spi_read();
+    spi_cs_off();
+    return status;
+}
+
+static void spi_cmd(uint8_t cmd)
+{
+    spi_cs_on();
+    spi_write(cmd);
+    spi_read();
+    spi_cs_off();
+}
+
+static inline void flash_aai_enable(void)
+{
+    spi_cmd(EBSY);
+}
+
+static inline void flash_aai_disable(void)
+{
+    spi_cmd(DBSY);
+}
+
+static void flash_write_enable(void)
+{
+    uint8_t status;
+    do {
+        spi_cmd(WREN);
+        status = read_status();
+    } while ((status & ST_WEL) == 0);
+}
+
+static void flash_write_disable(void)
+{
+    uint8_t status;
+    spi_cmd(WRDI);
+}
+static void wait_busy(void)
+{
+    uint8_t status;
+    do {
+        status = read_status();
+    } while(status & ST_BUSY);
+}
+
+static int spi_flash_write_page(uint32_t address, const void *data, int len)
+{
+    const uint8_t *buf = data;
+    int j = 0;
+    while (len > 0) {
+        wait_busy();
+        flash_write_enable();
+        spi_cs_on();
+        spi_write(BYTE_WRITE);
+        spi_read();
+        write_address(address);
+        do {
+            spi_write(buf[j++]);
+            address++;
+            spi_read();
+            len--;
+        } while ((address % SPI_FLASH_PAGE_SIZE) != 0);
+        spi_cs_off();
+    }
+    wait_busy();
+    return j;
+}
+
+static int spi_flash_write_aai(uint32_t address, const void *data, int len)
+{
+    const uint8_t *buf = data;
+    int j = 0;
+    int cont = 0;
+    wait_busy();
+    if (len < 1)
+        return -1;
+    while (len > 0) {
+        if ((address & 0x01) || (len < 2)) {
+            flash_write_enable();
+            spi_cs_on();
+            spi_write(BYTE_WRITE);
+            spi_read();
+            write_address(address);
+            spi_write(buf[j++]);
+            spi_read();
+            spi_cs_off();
+            len--;
+            address++;
+        } else {
+            if (!cont) {
+                flash_aai_enable();
+                flash_write_enable();
+            }
+            spi_cs_on();
+            spi_write(AUTOINC);
+            spi_read();
+            if (!cont) {
+                /* First AAI transaction, send address. */
+                write_address(address);
+                cont = 1;
+            }
+            spi_write(buf[j++]);
+            spi_read();
+            spi_write(buf[j++]);
+            spi_read();
+            spi_cs_off();
+            len -= 2;
+            address += 2;
+            read_status();
+        }
+    }
+    if (cont) {
+        flash_write_disable();
+        flash_aai_disable();
+    }
+    wait_busy();
+    return j;
+}
+
+/* --- */
+
+uint16_t spi_flash_probe(void)
+{
+    uint8_t manuf, product, b0;
+    int i;
+    wait_busy();
+    spi_cs_on();
+    spi_write(MDID);
+    b0 = spi_read();
+
+    write_address(0);
+    spi_write(0xFF);
+    manuf = spi_read();
+    spi_write(0xFF);
+    product = spi_read();
+    spi_cs_off();
+    if (manuf == 0xBF)
+        chip_write_mode = SST_AAI;
+    if (manuf == 0xEF)
+        chip_write_mode = WB_WRITEPAGE;
+
+#ifndef READONLY
+    spi_cmd(EWSR);
+    spi_cs_on();
+    spi_write(WRSR);
+    spi_read();
+    spi_write(0x00);
+    spi_read();
+    spi_cs_off();
+#endif
+    return (uint16_t)(manuf << 8 | product);
+}
+
+
+void spi_flash_sector_erase(uint32_t address)
+{
+    uint8_t status;
+    address &= (~(SPI_FLASH_SECTOR_SIZE - 1));
+
+    wait_busy();
+    flash_write_enable();
+    spi_cs_on();
+    spi_write(SECTOR_ERASE);
+    spi_read();
+    write_address(address);
+    spi_cs_off();
+    wait_busy();
+}
+
+int spi_flash_read(uint32_t address, void *data, int len)
+{
+    uint8_t *buf = data;
+    int i = 0;
+    wait_busy();
+    spi_cs_on();
+    spi_write(BYTE_READ);
+    spi_read();
+    write_address(address);
+    while (len > 0) {
+        spi_write(0xFF);
+        buf[i++] = spi_read();
+        len--;
+    }
+    spi_cs_off();
+    return i;
+}
+
+int spi_flash_write(uint32_t address, const void *data, int len)
+{
+    if (chip_write_mode == SST_AAI)
+        return spi_flash_write_aai(address, data, len);
+    if (chip_write_mode == WB_WRITEPAGE)
+        return spi_flash_write_page(address, data, len);
+    return -1;
+} 
+

+ 9 - 0
spi_flash.h

@@ -0,0 +1,9 @@
+#ifndef SPI_FLASH_DRI_H
+#define SPI_FLASH_DRI_H
+#include <stdint.h>
+uint16_t spi_flash_probe(void);
+
+void spi_flash_sector_erase(uint32_t address);
+int spi_flash_read(uint32_t address, void *data, int len);
+int spi_flash_write(uint32_t address, const void *data, int len);
+#endif

+ 208 - 0
startup.c

@@ -0,0 +1,208 @@
+/* 
+ * (c) danielinux 2019
+ *
+ *      GPLv.2
+ *
+ * See LICENSE for details
+ */
+
+extern unsigned int _stored_data;
+extern unsigned int _start_data;
+extern unsigned int _end_data;
+extern unsigned int _start_bss;
+extern unsigned int _end_bss;
+extern unsigned int _end_stack;
+extern unsigned int _start_heap;
+
+#define STACK_PAINTING
+
+static volatile unsigned int avail_mem = 0;
+static unsigned int sp;
+
+extern void main(void);
+extern void isr_exti_rot0(void);
+extern void isr_exti_rot1(void);
+extern void isr_exti_channel(void);
+extern void isr_exti_button(void);
+extern void isr_tim2(void);
+extern void isr_systick(void);
+extern void otg_fs_isr(void);
+extern void usb_fs_wkup_isr(void);
+
+void isr_reset(void) {
+    register unsigned int *src, *dst;
+    src = (unsigned int *) &_stored_data;
+    dst = (unsigned int *) &_start_data;
+    /* Copy the .data section from flash to RAM. */
+    while (dst < (unsigned int *)&_end_data) {
+        *dst = *src;
+        dst++;
+        src++;
+    }
+
+    /* Initialize the BSS section to 0 */
+    dst = &_start_bss;
+    while (dst < (unsigned int *)&_end_bss) {
+        *dst = 0U;
+        dst++;
+    }
+
+    /* Paint the stack. */
+    avail_mem = &_end_stack - &_start_heap;
+#ifdef STACK_PAINTING
+    {
+        asm volatile("mrs %0, msp" : "=r"(sp));
+        dst = ((unsigned int *)(&_end_stack)) - (8192 / sizeof(unsigned int)); ;
+        while ((unsigned int)dst < sp) {
+            *dst = 0xDEADC0DE;
+            dst++;
+        }
+    }
+#endif
+    /* Run the program! */
+    main();
+}
+
+void isr_fault(void)
+{
+    /* Panic. */
+    while(1) ;;
+}
+
+
+void isr_memfault(void)
+{
+    /* Panic. */
+    while(1) ;;
+}
+
+void isr_busfault(void)
+{
+    /* Panic. */
+    while(1) ;;
+}
+
+void isr_usagefault(void)
+{
+    /* Panic. */
+    while(1) ;;
+}
+        
+
+void isr_empty(void)
+{
+    /* Ignore the event and continue */
+}
+
+
+
+__attribute__ ((section(".isr_vector")))
+void (* const IV[])(void) =
+{
+	(void (*)(void))(&_end_stack),
+	isr_reset,                   // Reset
+	isr_fault,                   // NMI
+	isr_fault,                   // HardFault
+	isr_memfault,                // MemFault
+	isr_busfault,                // BusFault
+	isr_usagefault,              // UsageFault
+	0, 0, 0, 0,                  // 4x reserved
+	isr_empty,                   // SVC
+	isr_empty,                   // DebugMonitor
+	0,                           // reserved
+	isr_empty,                   // PendSV
+	isr_systick,                 // SysTick
+    
+    isr_empty,              // NVIC_WWDG_IRQ 0
+    isr_empty,              // PVD_IRQ 1
+    isr_empty,              // TAMP_STAMP_IRQ 2
+    isr_empty,              // RTC_WKUP_IRQ 3
+    isr_empty,              // FLASH_IRQ 4
+    isr_empty,              // RCC_IRQ 5
+    isr_exti_button,        // EXTI0_IRQ 6
+    isr_empty,              // EXTI1_IRQ 7
+    isr_empty,              // EXTI2_IRQ 8
+    isr_empty,              // EXTI3_IRQ 9
+    isr_exti_rot1,              // EXTI4_IRQ 10
+    isr_empty,              // DMA1_STREAM0_IRQ 11
+    isr_empty,              // DMA1_STREAM1_IRQ 12
+    isr_empty,              // DMA1_STREAM2_IRQ 13
+    isr_empty,              // DMA1_STREAM3_IRQ 14
+    isr_empty,              // DMA1_STREAM4_IRQ 15
+    isr_empty,              // DMA1_STREAM5_IRQ 16
+    isr_empty,              // DMA1_STREAM6_IRQ 17
+    isr_empty,              // ADC_IRQ 18
+    isr_empty,              // CAN1_TX_IRQ 19
+    isr_empty,              // CAN1_RX0_IRQ 20
+    isr_empty,              // CAN1_RX1_IRQ 21
+    isr_empty,              // CAN1_SCE_IRQ 22
+    isr_exti_channel,               // EXTI9_5_IRQ 23
+    isr_empty,              // TIM1_BRK_TIM9_IRQ 24
+    isr_empty,              // TIM1_UP_TIM10_IRQ 25
+    isr_empty,              // TIM1_TRG_COM_TIM11_IRQ 26
+    isr_empty,              // TIM1_CC_IRQ 27
+    isr_tim2,              // TIM2_IRQ 28
+    isr_empty,              // TIM3_IRQ 29
+    isr_empty,              // TIM4_IRQ 30
+    isr_empty,              // I2C1_EV_IRQ 31
+    isr_empty,              // I2C1_ER_IRQ 32
+    isr_empty,              // I2C2_EV_IRQ 33
+    isr_empty,              // I2C2_ER_IRQ 34
+    isr_empty,              // SPI1_IRQ 35
+    isr_empty,              // SPI2_IRQ 36
+    isr_empty,              // USART1_IRQ 37
+    isr_empty,              // USART2_IRQ 38
+    isr_empty,              // USART3_IRQ 39
+    isr_exti_rot0,               // EXTI15_10_IRQ 40
+    isr_empty,              // RTC_ALARM_IRQ 41
+    usb_fs_wkup_isr,              // USB_FS_WKUP_IRQ 42
+    isr_empty,              // TIM8_BRK_TIM12_IRQ 43
+    isr_empty,              // TIM8_UP_TIM13_IRQ 44
+    isr_empty,              // TIM8_TRG_COM_TIM14_IRQ 45
+    isr_empty,              // TIM8_CC_IRQ 46
+    isr_empty,              // DMA1_STREAM7_IRQ 47
+    isr_empty,              // FSMC_IRQ
+    isr_empty,              // SDIO_IRQ
+    isr_empty,              // TIM5_IRQ
+    isr_empty,              // SPI3_IRQ
+    isr_empty,              // UART4_IRQ
+    isr_empty,              // UART5_IRQ
+    isr_empty,              // TIM6_DAC_IRQ
+    isr_empty,              // TIM7_IRQ
+    isr_empty,              // DMA2_STREAM0_IRQ
+    isr_empty,              // DMA2_STREAM1_IRQ
+    isr_empty,              // DMA2_STREAM2_IRQ
+    isr_empty,              // DMA2_STREAM3_IRQ
+    isr_empty,              // DMA2_STREAM4_IRQ
+    isr_empty,              // ETH_IRQ
+    isr_empty,              // ETH_WKUP_IRQ
+    isr_empty,              // CAN2_TX_IRQ
+    isr_empty,              // CAN2_RX0_IRQ
+    isr_empty,              // CAN2_RX1_IRQ
+    isr_empty,              // CAN2_SCE_IRQ
+    otg_fs_isr,             // OTG_FS_IRQ
+    isr_empty,              // DMA2_STREAM5_IRQ
+    isr_empty,              // DMA2_STREAM6_IRQ
+    isr_empty,              // DMA2_STREAM7_IRQ
+    isr_empty,              // USART6_IRQ
+    isr_empty,              // I2C3_EV_IRQ
+    isr_empty,              // I2C3_ER_IRQ
+    isr_empty,              // OTG_HS_EP1_OUT_IRQ
+    isr_empty,              // OTG_HS_EP1_IN_IRQ
+    isr_empty,              // OTG_HS_WKUP_IRQ
+    isr_empty,              // OTG_HS_IRQ
+    isr_empty,              // DCMI_IRQ
+    isr_empty,              // CRYP_IRQ
+    isr_empty,              // HASH_RNG_IRQ
+    isr_empty,              // FPU_IRQ
+    isr_empty,              // UART7_IRQ
+    isr_empty,              // UART8_IRQ
+    isr_empty,              // SPI4_IRQ
+    isr_empty,              // SPI5_IRQ
+    isr_empty,              // SPI6_IRQ
+    isr_empty,              // SAI1_IRQ
+    isr_empty,              // LCD_TFT_IRQ
+    isr_empty,              // LCD_TFT_ERR_IRQ
+    isr_empty,              // DMA2D_IRQ
+
+};

+ 172 - 0
system.c

@@ -0,0 +1,172 @@
+/* 
+ * (c) danielinux 2019
+ *
+ *      GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdint.h>
+#include "system.h"
+
+
+uint32_t cpu_freq = 84000000;
+/*** FLASH ***/
+#define FLASH_BASE (0x40023C00)
+#define FLASH_ACR  (*(volatile uint32_t *)(FLASH_BASE + 0x00))
+#define FLASH_ACR_ENABLE_DATA_CACHE (1 << 10)
+#define FLASH_ACR_ENABLE_INST_CACHE (1 << 9)
+
+static void flash_set_waitstates(int waitstates)
+{
+    FLASH_ACR |=  waitstates | FLASH_ACR_ENABLE_DATA_CACHE | FLASH_ACR_ENABLE_INST_CACHE;
+}
+/*** RCC ***/
+
+#define RCC_BASE (0x40023800)
+#define RCC_CR      (*(volatile uint32_t *)(RCC_BASE + 0x00))
+#define RCC_PLLCFGR (*(volatile uint32_t *)(RCC_BASE + 0x04))
+#define RCC_CFGR    (*(volatile uint32_t *)(RCC_BASE + 0x08))
+#define RCC_CR_PLLRDY               (1 << 25)
+#define RCC_CR_PLLON                (1 << 24)
+#define RCC_CR_HSERDY               (1 << 17)
+#define RCC_CR_HSEON                (1 << 16)
+#define RCC_CR_HSIRDY               (1 << 1)
+#define RCC_CR_HSION                (1 << 0)
+
+#define RCC_CFGR_SW_HSI             0x0
+#define RCC_CFGR_SW_HSE             0x1
+#define RCC_CFGR_SW_PLL             0x2
+
+
+#define RCC_PLLCFGR_PLLSRC          (1 << 22)
+
+#define RCC_PRESCALER_DIV_NONE 0
+#define RCC_PRESCALER_DIV_2    8
+#define RCC_PRESCALER_DIV_4    9
+
+
+void clock_pll_off(void)
+{
+    uint32_t reg32;
+    /* Enable internal high-speed oscillator. */
+    RCC_CR |= RCC_CR_HSION;
+    DMB();
+    while ((RCC_CR & RCC_CR_HSIRDY) == 0) {};
+
+    /* Select HSI as SYSCLK source. */
+    reg32 = RCC_CFGR;
+    reg32 &= ~((1 << 1) | (1 << 0));
+    RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI);
+    DMB();
+
+    /* Turn off PLL */
+    RCC_CR &= ~RCC_CR_PLLON;
+    DMB();
+}
+
+
+void clock_pll_on(void)
+{
+    uint32_t reg32;
+    uint32_t plln, pllm, pllq, pllp, pllr, hpre, ppre1, ppre2, flash_waitstates;
+    
+    /* Enable Power controller */
+    APB1_CLOCK_ER |= PWR_APB1_CLOCK_ER_VAL;
+
+    /* Select clock parameters */
+    pllm = 8;
+    plln = 336;
+    pllp = 4;
+    pllq = 7;
+    pllr = 0;
+    hpre = RCC_PRESCALER_DIV_NONE;
+    ppre1 = RCC_PRESCALER_DIV_2; 
+    ppre2 = RCC_PRESCALER_DIV_NONE;
+    flash_waitstates = 2;
+
+    flash_set_waitstates(flash_waitstates);
+
+    /* Enable internal high-speed oscillator. */
+    RCC_CR |= RCC_CR_HSION;
+    DMB();
+    while ((RCC_CR & RCC_CR_HSIRDY) == 0) {};
+
+    /* Select HSI as SYSCLK source. */
+    reg32 = RCC_CFGR;
+    reg32 &= ~((1 << 1) | (1 << 0));
+    RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI);
+    DMB();
+
+    /* Enable external high-speed oscillator 12MHz. */
+    RCC_CR |= RCC_CR_HSEON;
+    DMB();
+    while ((RCC_CR & RCC_CR_HSERDY) == 0) {};
+
+    /*
+     * Set prescalers for AHB, ADC, ABP1, ABP2.
+     */
+    reg32 = RCC_CFGR;
+    reg32 &= ~(0xF0);
+    RCC_CFGR = (reg32 | (hpre << 4));
+    DMB();
+    reg32 = RCC_CFGR;
+    reg32 &= ~(0x1C00);
+    RCC_CFGR = (reg32 | (ppre1 << 10));
+    DMB();
+    reg32 = RCC_CFGR;
+    reg32 &= ~(0x07 << 13);
+    RCC_CFGR = (reg32 | (ppre2 << 13));
+    DMB();
+
+    /* Set PLL config */
+    reg32 = RCC_PLLCFGR;
+    reg32 &= ~(PLL_FULL_MASK);
+    RCC_PLLCFGR = reg32 | RCC_PLLCFGR_PLLSRC | pllm | 
+        (plln << 6) | (((pllp >> 1) - 1) << 16) | 
+        (pllq << 24); 
+    DMB();
+
+    /* Enable PLL oscillator and wait for it to stabilize. */
+    RCC_CR |= RCC_CR_PLLON;
+    DMB();
+    while ((RCC_CR & RCC_CR_PLLRDY) == 0) {};
+
+    /* Select PLL as SYSCLK source. */
+    reg32 = RCC_CFGR;
+    reg32 &= ~((1 << 1) | (1 << 0));
+    RCC_CFGR = (reg32 | RCC_CFGR_SW_PLL);
+    DMB();
+
+    /* Wait for PLL clock to be selected. */
+    while (((RCC_CFGR & ((1 << 3) | (1 << 2))) >> 2) != RCC_CFGR_SW_PLL) {};
+
+    /* Disable internal high-speed oscillator. */
+    RCC_CR &= ~RCC_CR_HSION;
+}
+
+void *__attribute__((weak)) memcpy(void *d, void *s, uint32_t len)
+{
+    uint32_t *src, *dst;
+    uint8_t *sb, *db;
+    src = s;
+    dst = d;
+    while(len > 3) {
+        *(dst++) = *(src++);
+        len -= 4;
+    }
+    sb = (uint8_t *)src;
+    db = (uint8_t *)dst;
+    while(len > 0) {
+        *(db++) = *(sb++);
+        len--;
+    }
+    return d; 
+}
+
+void panic(void)
+{
+    printf("PANIC!");
+    while(1)
+        ;
+}
+

+ 503 - 0
system.h

@@ -0,0 +1,503 @@
+#ifndef SYSTEM_H_INCLUDED
+#define SYSTEM_H_INCLUDED
+#include <stdint.h>
+/* System specific: PLL with 8 MHz external oscillator, CPU at 168MHz */
+#define PLL_FULL_MASK (0x7F037FFF)
+extern uint32_t cpu_freq;
+void panic(void);
+void printbin(const uint8_t *buf, int len); /* Defined in uart.c */
+
+extern int _mutex_lock(void *); /* defined in mutex.S */
+extern int _mutex_unlock(void *);
+
+/* PIN CONFIG TARGET */
+#define LED 5 // PA5
+
+#define SPI_FLASH_PIN  4 /* Flash CS connected to GPIOA4 */
+#define SPI1_PIN_AF 5
+#define SPI1_CLOCK_PIN 5
+#define SPI1_MISO_PIN 6
+#define SPI1_MOSI_PIN 7
+
+#define SPI2_PIN_AF 5
+#define SPI2_CLOCK_PIN 13
+#define SPI2_MISO_PIN 14
+#define SPI2_MOSI_PIN 15
+
+
+#define I2C1_PIN_AF 4
+#define I2C1_SDA 9  /* GPIOB P9 */
+#define I2C1_SCL 8  /* GPIOB P8 */
+#define GPIO_MODE_AF (2)
+
+
+/* STM32 specific defines */
+#define APB1_CLOCK_ER           (*(volatile uint32_t *)(0x40023840))
+#define APB1_CLOCK_RST          (*(volatile uint32_t *)(0x40023820))
+#define TIM2_APB1_CLOCK_ER_VAL 	(1 << 0)
+#define TIM4_APB1_CLOCK_ER_VAL 	(1 << 2)
+#define PWR_APB1_CLOCK_ER_VAL   (1 << 28)
+#define SPI2_APB1_CLOCK_ER_VAL 	(1 << 14)
+
+#define APB2_CLOCK_ER           (*(volatile uint32_t *)(0x40023844))
+#define APB2_CLOCK_RST          (*(volatile uint32_t *)(0x40023824))
+#define SYSCFG_APB2_CLOCK_ER    (1 << 14)
+#define SPI1_APB2_CLOCK_ER_VAL 	(1 << 12)
+#define SDIO_APB2_CLOCK_ER_VAL 	(1 << 11)
+#define ADC1_APB2_CLOCK_ER_VAL 	(1 << 8)
+
+#define RCC_BACKUP (*(volatile uint32_t *)(0x40023870))
+#define RCC_BACKUP_RESET        (1 << 16)
+#define RCC_BACKUP_RTCEN        (1 << 15)
+#define RCC_BACKUP_RTCSEL_SHIFT           8
+#define RCC_BACKUP_RTCSEL_MASK            0x3
+#define RCC_BACKUP_RTCSEL_NONE            0
+#define RCC_BACKUP_RTCSEL_LSE         1
+#define RCC_BACKUP_RTCSEL_LSI         2
+#define RCC_BACKUP_RTCSEL_HSE         3
+#define RCC_BACKUP_LSEMOD             (1 << 3)
+#define RCC_BACKUP_LSEBYP             (1 << 2)
+#define RCC_BACKUP_LSERDY             (1 << 1)
+#define RCC_BACKUP_LSEON              (1 << 0)
+
+#define RCC_CSR_LSION (1 << 0)
+#define RCC_CSR_LSIRDY (1 << 1)
+
+/* EXTI */
+#define EXTI_CR_BASE (0x40013808)
+#define EXTI_CR0 (*(volatile uint32_t *)(EXTI_CR_BASE + 0x00))
+#define EXTI_CR_EXTI0_MASK (0xFFFF)
+
+#define EXTI_IMR    (*(volatile uint32_t *)(EXTI_BASE + 0x00))
+#define EXTI_EMR    (*(volatile uint32_t *)(EXTI_BASE + 0x04))
+#define EXTI_RTSR   (*(volatile uint32_t *)(EXTI_BASE + 0x08))
+#define EXTI_FTSR   (*(volatile uint32_t *)(EXTI_BASE + 0x0c))
+#define EXTI_SWIER  (*(volatile uint32_t *)(EXTI_BASE + 0x10))
+#define EXTI_PR     (*(volatile uint32_t *)(EXTI_BASE + 0x14))
+
+
+/* HW RNG */
+#define RNG_BASE (0x50060800)
+#define RNG_CR (*(volatile uint32_t *)(RNG_BASE + 0x00))
+#define RNG_SR (*(volatile uint32_t *)(RNG_BASE + 0x04))
+#define RNG_DR (*(volatile uint32_t *)(RNG_BASE + 0x08))
+
+#define RNG_CR_IE (1 << 3)
+#define RNG_CR_RNGEN (1 << 2)
+
+#define RNG_SR_DRDY (1 << 0)
+#define RNG_SR_CECS (1 << 1)
+#define RNG_SR_SECS (1 << 2)
+
+
+/* SCB for sleep configuration */
+#define SCB_SCR (*(volatile uint32_t *)(0xE000ED10))
+#define SCB_SCR_SEVONPEND	(1 << 4)
+#define SCB_SCR_SLEEPDEEP		(1 << 2)
+#define SCB_SCR_SLEEPONEXIT (1 << 1)
+
+/* Assembly helpers */
+#define DMB() __asm__ volatile ("dmb")
+#define WFI() __asm__ volatile ("wfi")
+#define WFE() __asm__ volatile ("wfe")
+#define SEV() __asm__ volatile ("sev")
+
+/* Master clock setting */
+void clock_pll_on(void);
+void clock_pll_off(void);
+
+
+/* NVIC */
+/* NVIC ISER Base register (Cortex-M) */
+#define NVIC_RTC_IRQ             (3)
+#define NVIC_TIM2_IRQN          (28)
+#define NVIC_ISER_BASE (0xE000E100)
+#define NVIC_ICER_BASE (0xE000E180)
+#define NVIC_ICPR_BASE (0xE000E280)
+#define NVIC_IPRI_BASE (0xE000E400)
+#define NVIC_EXTI0_IRQ           (6)
+#define NVIC_EXTI9_5_IRQ         (23)
+#define NVIC_EXTI15_10_IRQ       (40)
+
+static inline void nvic_irq_enable(uint8_t n)
+{
+    int i = n / 32;
+    volatile uint32_t *nvic_iser = ((volatile uint32_t *)(NVIC_ISER_BASE + 4 * i));
+    *nvic_iser |= (1 << (n % 32));
+}
+
+static inline void nvic_irq_disable(uint8_t n)
+{
+    int i = n / 32;
+    volatile uint32_t *nvic_icer = ((volatile uint32_t *)(NVIC_ICER_BASE + 4 * i));
+    *nvic_icer |= (1 << (n % 32));
+}
+
+static inline void nvic_irq_setprio(uint8_t n, uint8_t prio)
+{
+    volatile uint8_t *nvic_ipri = ((volatile uint8_t *)(NVIC_IPRI_BASE + n));
+    *nvic_ipri = prio;
+}
+
+
+static inline void nvic_irq_clear(uint8_t n)
+{
+    int i = n / 32;
+    volatile uint8_t *nvic_icpr = ((volatile uint8_t *)(NVIC_ICPR_BASE + 4 * i));
+    *nvic_icpr = (1 << (n % 32));
+}
+/*** FLASH ***/
+#define FLASH_BASE (0x40023C00)
+#define FLASH_ACR  (*(volatile uint32_t *)(FLASH_BASE + 0x00))
+#define FLASH_ACR_ENABLE_DATA_CACHE (1 << 10)
+#define FLASH_ACR_ENABLE_INST_CACHE (1 << 9)
+
+/*** SPI ***/ 
+#define SPI1 (0x40013000)
+#define SPI1_CR1      (*(volatile uint32_t *)(SPI1))
+#define SPI1_CR2      (*(volatile uint32_t *)(SPI1 + 0x04))
+#define SPI1_SR       (*(volatile uint32_t *)(SPI1 + 0x08))
+#define SPI1_DR       (*(volatile uint32_t *)(SPI1 + 0x0c))
+
+#define SPI2 (0x40003800)
+#define SPI2_CR1      (*(volatile uint32_t *)(SPI2))
+#define SPI2_CR2      (*(volatile uint32_t *)(SPI2 + 0x04))
+#define SPI2_SR       (*(volatile uint32_t *)(SPI2 + 0x08))
+#define SPI2_DR       (*(volatile uint32_t *)(SPI2 + 0x0c))
+
+
+#define SPI_CR1_CLOCK_PHASE         (1 << 0)
+#define SPI_CR1_CLOCK_POLARITY      (1 << 1)
+#define SPI_CR1_MASTER	    		(1 << 2)
+#define SPI_CR1_BAUDRATE        	(0x07 << 3)
+#define SPI_CR1_SPI_EN		    	(1 << 6)
+#define SPI_CR1_LSBFIRST		    (1 << 7)
+#define SPI_CR1_SSI			        (1 << 8)
+#define SPI_CR1_SSM			        (1 << 9)
+#define SPI_CR1_16BIT_FORMAT        (1 << 11)
+#define SPI_CR1_TX_CRC_NEXT			(1 << 12)
+#define SPI_CR1_HW_CRC_EN			(1 << 13)
+#define SPI_CR1_BIDIOE			    (1 << 14)
+#define SPI_CR2_SSOE			    (1 << 2)
+
+
+
+#define SPI_SR_RX_NOTEMPTY  	        (1 << 0)
+#define SPI_SR_TX_EMPTY			        (1 << 1)
+#define SPI_SR_BUSY			            (1 << 7)
+
+
+/*** RCC ***/
+
+#define RCC_CR_PLLRDY               (1 << 25)
+#define RCC_CR_PLLON                (1 << 24)
+#define RCC_CR_HSERDY               (1 << 17)
+#define RCC_CR_HSEON                (1 << 16)
+#define RCC_CR_HSIRDY               (1 << 1)
+#define RCC_CR_HSION                (1 << 0)
+
+#define RCC_CFGR_SW_HSI             0x0
+#define RCC_CFGR_SW_HSE             0x1
+#define RCC_CFGR_SW_PLL             0x2
+
+#define RCC_PRESCALER_DIV_NONE 0
+#define RCC_PRESCALER_DIV_2    8
+#define RCC_PRESCALER_DIV_4    9
+
+
+#define RCC_PLLCFGR_PLLSRC          (1 << 22)
+#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830))
+#define GPIOA_AHB1_CLOCK_ER (1 << 0)
+#define GPIOB_AHB1_CLOCK_ER (1 << 1)
+#define GPIOC_AHB1_CLOCK_ER (1 << 2)
+#define GPIOD_AHB1_CLOCK_ER (1 << 3)
+#define GPIOE_AHB1_CLOCK_ER (1 << 4)
+#define GPIOA_BASE 0x40020000
+#define GPIOB_BASE 0x40020400
+#define GPIOC_BASE 0x40020800
+#define GPIOD_BASE 0x40020C00
+#define GPIOE_BASE 0x40021000
+
+#define AHB2_CLOCK_ER (*(volatile uint32_t *)(0x40023834))
+#define RNG_AHB2_CLOCK_ER (1 << 6)
+
+
+/* POWER CONTROL REGISTER */
+#define POW_BASE (0x40007000)
+#define POW_CR (*(volatile uint32_t *)(POW_BASE + 0x00))
+#define POW_SCR (*(volatile uint32_t *)(POW_BASE + 0x04))
+
+#define POW_CR_VOS (1 << 14)
+#define POW_CR_FPDS (1 << 9)
+#define POW_CR_DPB   (1 << 8)
+#define POW_CR_CSBF  (1 << 3)
+#define POW_CR_CWUF  (1 << 2)
+#define POW_CR_PDDS  (1 << 1)
+#define POW_CR_LPDS  (1 << 0)
+#define POW_SCR_BRE (1 << 9)
+#define POW_SCR_EWUP (1 << 4)
+#define POW_SCR_BRR (1 << 3)
+#define POW_SCR_WUF   (1 << 0)
+
+
+
+/* GPIOS */
+
+
+#define GPIOA_MODE  (*(volatile uint32_t *)(GPIOA_BASE + 0x00))
+#define GPIOA_AFL   (*(volatile uint32_t *)(GPIOA_BASE + 0x20))
+#define GPIOA_AFH   (*(volatile uint32_t *)(GPIOA_BASE + 0x24))
+#define GPIOA_OSPD  (*(volatile uint32_t *)(GPIOA_BASE + 0x08))
+#define GPIOA_PUPD (*(volatile uint32_t *)(GPIOA_BASE + 0x0c))
+#define GPIOA_BSRR (*(volatile uint32_t *)(GPIOA_BASE + 0x18))
+#define GPIOA_ODR  (*(volatile uint32_t *)(GPIOA_BASE + 0x14))
+#define GPIOA_IDR  (*(volatile uint32_t *)(GPIOA_BASE + 0x10))
+
+#define GPIOB_MODE  (*(volatile uint32_t *)(GPIOB_BASE + 0x00))
+#define GPIOB_AFL   (*(volatile uint32_t *)(GPIOB_BASE + 0x20))
+#define GPIOB_AFH   (*(volatile uint32_t *)(GPIOB_BASE + 0x24))
+#define GPIOB_OSPD  (*(volatile uint32_t *)(GPIOB_BASE + 0x08))
+#define GPIOB_PUPD (*(volatile uint32_t *)(GPIOB_BASE + 0x0c))
+#define GPIOB_BSRR (*(volatile uint32_t *)(GPIOB_BASE + 0x18))
+#define GPIOB_ODR  (*(volatile uint32_t *)(GPIOB_BASE + 0x14))
+
+#define GPIOC_MODE  (*(volatile uint32_t *)(GPIOC_BASE + 0x00))
+#define GPIOC_OTYPE (*(volatile uint32_t *)(GPIOC_BASE + 0x04))
+#define GPIOC_OSPEED (*(volatile uint32_t *)(GPIOC_BASE + 0x08))
+#define GPIOC_AFL   (*(volatile uint32_t *)(GPIOC_BASE + 0x20))
+#define GPIOC_AFH   (*(volatile uint32_t *)(GPIOC_BASE + 0x24))
+#define GPIOC_OSPD  (*(volatile uint32_t *)(GPIOC_BASE + 0x08))
+#define GPIOC_PUPD (*(volatile uint32_t *)(GPIOC_BASE + 0x0c))
+#define GPIOC_BSRR (*(volatile uint32_t *)(GPIOC_BASE + 0x18))
+#define GPIOC_ODR  (*(volatile uint32_t *)(GPIOC_BASE + 0x14))
+
+
+#define GPIOD_MODE  (*(volatile uint32_t *)(GPIOD_BASE + 0x00))
+#define GPIOD_OTYPE (*(volatile uint32_t *)(GPIOD_BASE + 0x04))
+#define GPIOD_OSPEED (*(volatile uint32_t *)(GPIOD_BASE + 0x08))
+#define GPIOD_AFL   (*(volatile uint32_t *)(GPIOD_BASE + 0x20))
+#define GPIOD_AFH   (*(volatile uint32_t *)(GPIOD_BASE + 0x24))
+#define GPIOD_OSPD  (*(volatile uint32_t *)(GPIOD_BASE + 0x08))
+#define GPIOD_PUPD (*(volatile uint32_t *)(GPIOD_BASE + 0x0c))
+#define GPIOD_BSRR (*(volatile uint32_t *)(GPIOD_BASE + 0x18))
+#define GPIOD_ODR  (*(volatile uint32_t *)(GPIOD_BASE + 0x14))
+
+#define GPIOE_MODE  (*(volatile uint32_t *)(GPIOE_BASE + 0x00))
+#define GPIOE_AFL   (*(volatile uint32_t *)(GPIOE_BASE + 0x20))
+#define GPIOE_AFH   (*(volatile uint32_t *)(GPIOE_BASE + 0x24))
+#define GPIOE_OSPD  (*(volatile uint32_t *)(GPIOE_BASE + 0x08))
+#define GPIOE_PUPD (*(volatile uint32_t *)(GPIOE_BASE + 0x0c))
+#define GPIOE_BSRR (*(volatile uint32_t *)(GPIOE_BASE + 0x18))
+#define GPIOE_ODR  (*(volatile uint32_t *)(GPIOE_BASE + 0x14))
+#define GPIO_MODE_AF (2)
+
+/* SDIO */
+#define SDIO_BASE (0x40012C00)
+#define SDIO_POWER           (*(volatile uint32_t *)((SDIO_BASE) + 0x00))
+#define SDIO_CLKCR           (*(volatile uint32_t *)((SDIO_BASE) + 0x04))
+#define SDIO_ARG             (*(volatile uint32_t *)((SDIO_BASE) + 0x08))
+#define SDIO_CMD             (*(volatile uint32_t *)((SDIO_BASE) + 0x0C))
+#define SDIO_RESPCMD         (*(volatile uint32_t *)((SDIO_BASE) + 0x10))
+#define SDIO_RESP1			 (*(volatile uint32_t *)((SDIO_BASE) + 0x14))
+#define SDIO_RESP2			 (*(volatile uint32_t *)((SDIO_BASE) + 0x18))
+#define SDIO_RESP3			 (*(volatile uint32_t *)((SDIO_BASE) + 0x1C))
+#define SDIO_RESP4			 (*(volatile uint32_t *)((SDIO_BASE) + 0x20))
+#define SDIO_DTIMER			 (*(volatile uint32_t *)((SDIO_BASE) + 0x24))
+#define SDIO_DLEN			 (*(volatile uint32_t *)((SDIO_BASE) + 0x28))
+#define SDIO_DCTRL			 (*(volatile uint32_t *)((SDIO_BASE) + 0x2C))
+#define SDIO_DCOUNT			 (*(volatile uint32_t *)((SDIO_BASE) + 0x30))
+#define SDIO_STA             (*(volatile uint32_t *)((SDIO_BASE) + 0x34))
+#define SDIO_ICR             (*(volatile uint32_t *)((SDIO_BASE) + 0x38))
+#define SDIO_MASK            (*(volatile uint32_t *)((SDIO_BASE) + 0x3C))
+#define SDIO_FIFOCNT         (*(volatile uint32_t *)((SDIO_BASE) + 0x48))
+#define SDIO_FIFO            (*(volatile uint32_t *)((SDIO_BASE) + 0x80))
+
+#define SDIO_POWER_PWRCTRL_SHIFT	0
+#define SDIO_POWER_PWRCTRL_PWROFF	(0x0 << SDIO_POWER_PWRCTRL_SHIFT)
+#define SDIO_POWER_PWRCTRL_RSVPWRUP	(0x2 << SDIO_POWER_PWRCTRL_SHIFT)
+#define SDIO_POWER_PWRCTRL_PWRON	(0x3 << SDIO_POWER_PWRCTRL_SHIFT)
+#define SDIO_CLKCR_HWFC_EN		(1 << 14)
+#define SDIO_CLKCR_NEGEDGE		(1 << 13)
+#define SDIO_CLKCR_WIDBUS_SHIFT		11
+#define SDIO_CLKCR_WIDBUS_1		(0x0 << SDIO_CLKCR_WIDBUS_SHIFT)
+#define SDIO_CLKCR_WIDBUS_4		(0x1 << SDIO_CLKCR_WIDBUS_SHIFT)
+#define SDIO_CLKCR_WIDBUS_8		(0x2 << SDIO_CLKCR_WIDBUS_SHIFT)
+#define SDIO_CLKCR_BYPASS		(1 << 10)
+#define SDIO_CLKCR_PWRSAV		(1 << 9)
+#define SDIO_CLKCR_CLKEN		(1 << 8)
+#define SDIO_CLKCR_CLKDIV_SHIFT		0
+#define SDIO_CLKCR_CLKDIV_MSK		(0xFF << SDIO_CLKCR_CLKDIV_SHIFT)
+#define SDIO_CMD_ATACMD			(1 << 14)
+#define SDIO_CMD_NIEN			(1 << 13)
+#define SDIO_CMD_ENCMDCOMPL		(1 << 12)
+#define SDIO_CMD_SDIOSUSPEND		(1 << 11)
+#define SDIO_CMD_CPSMEN			(1 << 10)
+#define SDIO_CMD_WAITPEND		(1 << 9)
+#define SDIO_CMD_WAITINT		(1 << 8)
+#define SDIO_CMD_WAITRESP_SHIFT		6
+#define SDIO_CMD_WAITRESP_NO_0		(0x0 << SDIO_CMD_WAITRESP_SHIFT)
+#define SDIO_CMD_WAITRESP_SHORT		(0x1 << SDIO_CMD_WAITRESP_SHIFT)
+#define SDIO_CMD_WAITRESP_NO_2		(0x2 << SDIO_CMD_WAITRESP_SHIFT)
+#define SDIO_CMD_WAITRESP_LONG		(0x3 << SDIO_CMD_WAITRESP_SHIFT)
+#define SDIO_CMD_CMDINDEX_SHIFT		0
+#define SDIO_CMD_CMDINDEX_MSK		(0x3F << SDIO_CMD_CMDINDEX_SHIFT)
+#define SDIO_RESPCMD_SHIFT		0
+#define SDIO_RESPCMD_MSK		(0x3F << SDIO_RESPCMD_SHIFT)
+#define SDIO_DCTRL_SDIOEN		(1 << 11)
+#define SDIO_DCTRL_RWMOD		(1 << 10)
+#define SDIO_DCTRL_RWSTOP		(1 << 9)
+#define SDIO_DCTRL_RWSTART		(1 << 8)
+#define SDIO_DCTRL_DBLOCKSIZE_SHIFT	4
+#define SDIO_DCTRL_DBLOCKSIZE_0		(0x0 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_1		(0x1 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_2		(0x2 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_3		(0x3 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_4		(0x4 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_5		(0x5 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_6		(0x6 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_7		(0x7 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_8		(0x8 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_9		(0x9 << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_10	(0xA << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_11	(0xB << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_12	(0xC << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_13	(0xD << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DBLOCKSIZE_14	(0xE << SDIO_DCTRL_DBLOCKSIZE_SHIFT)
+#define SDIO_DCTRL_DMAEN		(1 << 3)
+#define SDIO_DCTRL_DTMODE		(1 << 2)
+#define SDIO_DCTRL_DTDIR		(1 << 1)
+#define SDIO_DCTRL_DTEN			(1 << 0)
+#define SDIO_STA_CEATAEND		(1 << 23)
+#define SDIO_STA_SDIOIT			(1 << 22)
+#define SDIO_STA_RXDAVL			(1 << 21)
+#define SDIO_STA_TXDAVL			(1 << 20)
+#define SDIO_STA_RXFIFOE		(1 << 19)
+#define SDIO_STA_TXFIFOE		(1 << 18)
+#define SDIO_STA_RXFIFOF		(1 << 17)
+#define SDIO_STA_TXFIFOF		(1 << 16)
+#define SDIO_STA_RXFIFOHF		(1 << 15)
+#define SDIO_STA_TXFIFOHE		(1 << 14)
+#define SDIO_STA_RXACT			(1 << 13)
+#define SDIO_STA_TXACT			(1 << 12)
+#define SDIO_STA_CMDACT			(1 << 11)
+#define SDIO_STA_DBCKEND		(1 << 10)
+#define SDIO_STA_STBITERR		(1 << 9)
+#define SDIO_STA_DATAEND		(1 << 8)
+#define SDIO_STA_CMDSENT		(1 << 7)
+#define SDIO_STA_CMDREND		(1 << 6)
+#define SDIO_STA_RXOVERR		(1 << 5)
+#define SDIO_STA_TXUNDERR		(1 << 4)
+#define SDIO_STA_DTIMEOUT		(1 << 3)
+#define SDIO_STA_CTIMEOUT		(1 << 2)
+#define SDIO_STA_DCRCFAIL		(1 << 1)
+#define SDIO_STA_CCRCFAIL		(1 << 0)
+#define SDIO_ICR_CEATAENDC		(1 << 23)
+#define SDIO_ICR_SDIOITC		(1 << 22)
+#define SDIO_ICR_DBCKENDC		(1 << 10)
+#define SDIO_ICR_STBITERRC		(1 << 9)
+#define SDIO_ICR_DATAENDC		(1 << 8)
+#define SDIO_ICR_CMDSENTC		(1 << 7)
+#define SDIO_ICR_CMDRENDC		(1 << 6)
+#define SDIO_ICR_RXOVERRC		(1 << 5)
+#define SDIO_ICR_TXUNDERRC		(1 << 4)
+#define SDIO_ICR_DTIMEOUTC		(1 << 3)
+#define SDIO_ICR_CTIMEOUTC		(1 << 2)
+#define SDIO_ICR_DCRCFAILC		(1 << 1)
+#define SDIO_ICR_CCRCFAILC		(1 << 0)
+#define SDIO_MASK_CEATAENDIE		(1 << 23)
+#define SDIO_MASK_SDIOITIE		(1 << 22)
+#define SDIO_MASK_RXDAVLIE		(1 << 21)
+#define SDIO_MASK_TXDAVLIE		(1 << 20)
+#define SDIO_MASK_RXFIFOEIE		(1 << 19)
+#define SDIO_MASK_TXFIFOEIE		(1 << 18)
+#define SDIO_MASK_RXFIFOFIE		(1 << 17)
+#define SDIO_MASK_TXFIFOFIE		(1 << 16)
+#define SDIO_MASK_RXFIFOHFIE		(1 << 15)
+#define SDIO_MASK_TXFIFOHEIE		(1 << 14)
+#define SDIO_MASK_RXACTIE		(1 << 13)
+#define SDIO_MASK_TXACTIE		(1 << 12)
+#define SDIO_MASK_CMDACTIE		(1 << 11)
+#define SDIO_MASK_DBCKENDIE		(1 << 10)
+#define SDIO_MASK_STBITERRIE		(1 << 9)
+#define SDIO_MASK_DATAENDIE		(1 << 8)
+#define SDIO_MASK_CMDSENTIE		(1 << 7)
+#define SDIO_MASK_CMDRENDIE		(1 << 6)
+#define SDIO_MASK_RXOVERRIE		(1 << 5)
+#define SDIO_MASK_TXUNDERRIE		(1 << 4)
+#define SDIO_MASK_DTIMEOUTIE		(1 << 3)
+#define SDIO_MASK_CTIMEOUTIE		(1 << 2)
+#define SDIO_MASK_DCRCFAILIE		(1 << 1)
+#define SDIO_MASK_CCRCFAILIE		(1 << 0)
+
+/* Timers */
+#define TIM2_BASE (0x40000000)
+#define TIM2_CR1  (*(volatile uint32_t *)(TIM2_BASE + 0x00))
+#define TIM2_DIER (*(volatile uint32_t *)(TIM2_BASE + 0x0c))
+#define TIM2_SR   (*(volatile uint32_t *)(TIM2_BASE + 0x10))
+#define TIM2_CNT  (*(volatile uint32_t *)(TIM2_BASE + 0x24))
+#define TIM2_PSC  (*(volatile uint32_t *)(TIM2_BASE + 0x28))
+#define TIM2_ARR  (*(volatile uint32_t *)(TIM2_BASE + 0x2c))
+
+#define TIM4_BASE (0x40000800)
+#define TIM4_CR1    (*(volatile uint32_t *)(TIM4_BASE + 0x00))
+#define TIM4_DIER   (*(volatile uint32_t *)(TIM4_BASE + 0x0c))
+#define TIM4_SR     (*(volatile uint32_t *)(TIM4_BASE + 0x10))
+#define TIM4_CCMR1  (*(volatile uint32_t *)(TIM4_BASE + 0x18))
+#define TIM4_CCMR2  (*(volatile uint32_t *)(TIM4_BASE + 0x1c))
+#define TIM4_CCER   (*(volatile uint32_t *)(TIM4_BASE + 0x20))
+#define TIM4_PSC    (*(volatile uint32_t *)(TIM4_BASE + 0x28))
+#define TIM4_ARR    (*(volatile uint32_t *)(TIM4_BASE + 0x2c))
+#define TIM4_CCR4   (*(volatile uint32_t *)(TIM4_BASE + 0x40))
+
+#define TIM_DIER_UIE (1 << 0)
+#define TIM_SR_UIF   (1 << 0)
+#define TIM_CR1_CLOCK_ENABLE (1 << 0)
+#define TIM_CR1_UPD_RS       (1 << 2)
+#define TIM_CR1_ARPE         (1 << 7)
+
+#define TIM_CCER_CC4_ENABLE  (1 << 12)
+#define TIM_CCMR1_OC1M_PWM1  (0x06 << 4)
+#define TIM_CCMR2_OC4M_PWM1  (0x06 << 12)
+
+#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830))
+#define GPIOD_AHB1_CLOCK_ER (1 << 3)
+
+#define GPIOD_BASE 0x40020c00
+#define GPIOD_MODE (*(volatile uint32_t *)(GPIOD_BASE + 0x00))
+#define GPIOD_OTYPE (*(volatile uint32_t *)(GPIOD_BASE + 0x04))
+#define GPIOD_PUPD (*(volatile uint32_t *)(GPIOD_BASE + 0x0c))
+#define GPIOD_ODR  (*(volatile uint32_t *)(GPIOD_BASE + 0x14))
+
+/* ADC */
+
+#define ADC1_BASE       (0x40012000)
+#define ADC_COM_BASE    (0x40012300)
+#define ADC_COM_CCR     (*(volatile uint32_t *)(ADC_COM_BASE + 0x04))
+#define ADC1_SR         (*(volatile uint32_t *)(ADC1_BASE + 0x00))
+#define ADC1_CR1        (*(volatile uint32_t *)(ADC1_BASE + 0x04))
+#define ADC1_CR2        (*(volatile uint32_t *)(ADC1_BASE + 0x08))
+#define ADC1_SMPR1      (*(volatile uint32_t *)(ADC1_BASE + 0x0c))
+#define ADC1_SMPR2      (*(volatile uint32_t *)(ADC1_BASE + 0x10))
+#define ADC1_SQR3       (*(volatile uint32_t *)(ADC1_BASE + 0x34))
+#define ADC1_DR         (*(volatile uint32_t *)(ADC1_BASE + 0x4c))
+#define ADC_CR1_SCAN            (1 << 8)
+#define ADC_CR2_EN              (1 << 0)
+#define ADC_CR2_CONT            (1 << 1)
+#define ADC_CR2_SWSTART         (1 << 30)
+#define ADC_SR_EOC              (1 << 1)
+#define ADC_SMPR_SMP_480CYC     (0x7)
+
+/* Reboot */
+
+#define AIRCR *(volatile uint32_t *)(0xE000ED0C)
+#define AIRCR_VKEY (0x05FA << 16)
+#   define AIRCR_SYSRESETREQ (1 << 2)
+
+static inline void reboot(void)
+{
+    AIRCR = AIRCR_SYSRESETREQ | AIRCR_VKEY;
+}
+
+
+
+#endif

+ 42 - 0
systick.c

@@ -0,0 +1,42 @@
+/* 
+ * (c) danielinux 2019
+ *
+ *      GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include "system.h"
+#include "systick.h"
+#include <stdint.h>
+/*** SYSTICK ***/
+
+#define SYSTICK_BASE (0xE000E010)
+#define SYSTICK_CSR     (*(volatile uint32_t *)(SYSTICK_BASE + 0x00))
+#define SYSTICK_RVR     (*(volatile uint32_t *)(SYSTICK_BASE + 0x04))
+#define SYSTICK_CVR     (*(volatile uint32_t *)(SYSTICK_BASE + 0x08))
+#define SYSTICK_CALIB   (*(volatile uint32_t *)(SYSTICK_BASE + 0x0C))
+
+volatile unsigned int jiffies = 0;
+
+
+void systick_enable(void)
+{
+    SYSTICK_RVR = ((cpu_freq / 1000) - 1);
+    SYSTICK_CVR = 0;
+    SYSTICK_CSR |= 0x07;
+}
+
+void systick_disable(void)
+{
+    SYSTICK_CSR &= ~1;
+}
+
+void isr_systick(void)
+{
+    ++jiffies;
+}
+
+uint32_t HAL_GetTick(void)
+{
+    return jiffies;
+}

+ 5 - 0
systick.h

@@ -0,0 +1,5 @@
+#ifndef SYSTICK_H_INCLUDED
+#define SYSTICK_H_INCLUDED
+extern volatile unsigned int jiffies;
+void systick_enable(void);
+#endif

+ 49 - 0
target.ld

@@ -0,0 +1,49 @@
+MEMORY
+{
+    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K - 0x100
+    RAM (rw) : ORIGIN = 0x20001000, LENGTH = 60K
+    RAM_S (rw) : ORIGIN = 0x20000000, LENGTH = 4K
+}
+
+SECTIONS
+{
+    .text :
+    {
+        _start_text = .;
+        KEEP(*(.isr_vector))
+        *(.text*)
+        *(.rodata*)
+        . = ALIGN(4);
+        _end_text = .;
+    } > FLASH
+
+    .edidx :
+    {
+        . = ALIGN(4);
+        *(.ARM.exidx*)
+    } > FLASH
+
+    _stored_data = .;
+
+    .data : AT (_stored_data)
+    {
+        _start_data = .;
+        *(.data*)
+        . = ALIGN(4);
+        _end_data = .;
+    } > RAM
+
+    .bss :
+    {
+        _start_bss = .;
+        *(.bss*)
+        *(COMMON)
+        . = ALIGN(4);
+        _end_bss = .;
+        _end = .;
+    } > RAM
+
+}
+
+PROVIDE(_start_heap = _end);
+PROVIDE(_end_stack  = ORIGIN(RAM_S) + LENGTH(RAM_S));

+ 85 - 0
timer.c

@@ -0,0 +1,85 @@
+#include <stdint.h>
+#include "system.h"
+
+static uint32_t master_clock = 0;
+
+/** Use TIM4_CH4, which is linked to PD15 AF1 **/
+int pwm_init(uint32_t clock, uint32_t threshold)
+{
+    uint32_t val = (clock / 100000); /* Frequency is 100 KHz */
+    uint32_t lvl;
+    master_clock = clock;
+
+    if (threshold > 100)
+        return -1;
+
+    lvl = (val * threshold) / 100;
+    if (lvl != 0)
+        lvl--;
+    
+    APB1_CLOCK_RST |= TIM4_APB1_CLOCK_ER_VAL;
+    __asm__ volatile ("dmb");
+    APB1_CLOCK_RST &= ~TIM4_APB1_CLOCK_ER_VAL;
+    APB1_CLOCK_ER |= TIM4_APB1_CLOCK_ER_VAL;
+
+    /* disable CC */
+    TIM4_CCER  &= ~TIM_CCER_CC4_ENABLE;
+    TIM4_CR1    = 0;
+    TIM4_PSC    = 0;
+    TIM4_ARR    = val - 1;
+    TIM4_CCR4   = lvl;
+    TIM4_CCMR1  &= ~(0x03 << 0);
+    TIM4_CCMR1  &= ~(0x07 << 4);
+    TIM4_CCMR1  |= TIM_CCMR1_OC1M_PWM1;
+    TIM4_CCMR2  &= ~(0x03 << 8);
+    TIM4_CCMR2  &= ~(0x07 << 12);
+    TIM4_CCMR2  |= TIM_CCMR2_OC4M_PWM1;
+    TIM4_CCER  |= TIM_CCER_CC4_ENABLE;
+    TIM4_CR1    |= TIM_CR1_CLOCK_ENABLE | TIM_CR1_ARPE;
+    __asm__ volatile ("dmb");
+    return 0;
+}
+
+
+/* Timer 2: Use val 60000 with PSC 1400 for 1s tick (84 Mhz) */
+/* Timer 2: Use val 52500 with PSC 200 for 1/8 s tick (84 Mhz) */
+
+#define TMR2_INIT_VAL 52500
+#define TMR2_INIT_PSC 200
+
+void timer_init(void)
+{
+    uint32_t val = 0;
+    uint32_t psc = 1;
+    uint32_t err = 0;
+
+    nvic_irq_enable(NVIC_TIM2_IRQN);
+    nvic_irq_setprio(NVIC_TIM2_IRQN, 0);
+    APB1_CLOCK_RST |= TIM2_APB1_CLOCK_ER_VAL;
+    __asm__ volatile ("dmb");
+    APB1_CLOCK_RST &= ~TIM2_APB1_CLOCK_ER_VAL;
+    APB1_CLOCK_ER |= TIM2_APB1_CLOCK_ER_VAL;
+
+    TIM2_CR1    = 0;
+    __asm__ volatile ("dmb");
+    TIM2_PSC    = TMR2_INIT_PSC;
+    TIM2_ARR    = TMR2_INIT_VAL;
+    TIM2_CR1    |= TIM_CR1_CLOCK_ENABLE;
+    TIM2_DIER   |= TIM_DIER_UIE;
+    __asm__ volatile ("dmb");
+}
+
+static volatile uint32_t tim2_ticks = 0;
+void isr_tim2(void) 
+{
+    TIM2_SR &= ~TIM_SR_UIF;
+    tim2_ticks++;
+}
+
+void gettime(uint32_t *seconds, uint32_t *microseconds)
+{
+    *microseconds = ((TIM2_CNT * TMR2_INIT_PSC) / 84) + (tim2_ticks & 0x07) * 125000;
+    *seconds = (tim2_ticks >> 3);
+}
+
+

+ 132 - 0
uart.c

@@ -0,0 +1,132 @@
+/* 
+ * (c) danielinux 2019
+ *
+ *      GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include "uart.h"
+#include "system.h"
+
+#define UART2 (0x40004400)
+
+#define UART2_SR       (*(volatile uint32_t *)(UART2))
+#define UART2_DR       (*(volatile uint32_t *)(UART2 + 0x04))
+#define UART2_BRR      (*(volatile uint32_t *)(UART2 + 0x08))
+#define UART2_CR1      (*(volatile uint32_t *)(UART2 + 0x0c))
+#define UART2_CR2      (*(volatile uint32_t *)(UART2 + 0x10))
+
+#define UART_CR1_UART_ENABLE    (1 << 13)
+#define UART_CR1_SYMBOL_LEN     (1 << 12)
+#define UART_CR1_PARITY_ENABLED (1 << 10)
+#define UART_CR1_PARITY_ODD     (1 << 9)
+#define UART_CR1_TX_ENABLE      (1 << 3)
+#define UART_CR1_RX_ENABLE      (1 << 2)
+#define UART_CR2_STOPBITS       (3 << 12)
+#define UART_SR_TX_EMPTY        (1 << 7)
+#define UART_SR_RX_NOTEMPTY     (1 << 5)
+
+
+
+#define APB1_CLOCK_ER           (*(volatile uint32_t *)(0x40023840))
+#define UART2_APB1_CLOCK_ER_VAL 	(1 << 17)
+
+#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830))
+#define GPIO_MODE_AF (2)
+#define UART2_PIN_AF 7
+#define UART2_RX_PIN 2
+#define UART2_TX_PIN 3
+
+static void uart2_pins_setup(void)
+{
+    uint32_t reg;
+    AHB1_CLOCK_ER |= GPIOA_AHB1_CLOCK_ER;
+    /* Set mode = AF */
+    reg = GPIOA_MODE & ~ (0x03 << (UART2_RX_PIN * 2));
+    GPIOA_MODE = reg | (2 << (UART2_RX_PIN * 2));
+    reg = GPIOA_MODE & ~ (0x03 << (UART2_TX_PIN * 2));
+    GPIOA_MODE = reg | (2 << (UART2_TX_PIN * 2));
+
+    /* Alternate function: use low pins */
+    reg = GPIOA_AFL & ~(0xf << ((UART2_TX_PIN) * 4));
+    GPIOA_AFL = reg | (UART2_PIN_AF << ((UART2_TX_PIN) * 4));
+    reg = GPIOA_AFL & ~(0xf << ((UART2_RX_PIN) * 4));
+    GPIOA_AFL = reg | (UART2_PIN_AF << ((UART2_RX_PIN) * 4));
+}
+
+int uart2_setup(uint32_t bitrate, uint8_t data, char parity, uint8_t stop)
+{
+    uint32_t reg;
+    int pin_rx, pin_tx, pin_af;
+    /* Enable pins and configure for AF7 */
+    uart2_pins_setup();
+    /* Turn on the device */
+    APB1_CLOCK_ER |= UART2_APB1_CLOCK_ER_VAL;
+
+    /* Configure for TX */
+    UART2_CR1 |= UART_CR1_TX_ENABLE;
+
+    /* Configure clock */
+    UART2_BRR =  cpu_freq / bitrate;
+
+    /* Configure data bits */
+    if (data == 8)
+        UART2_CR1 &= ~UART_CR1_SYMBOL_LEN;
+    else
+        UART2_CR1 |= UART_CR1_SYMBOL_LEN;
+
+    /* Configure parity */
+    switch (parity) {
+        case 'O':
+            UART2_CR1 |= UART_CR1_PARITY_ODD;
+            /* fall through to enable parity */
+        case 'E':
+            UART2_CR1 |= UART_CR1_PARITY_ENABLED;
+            break;
+        default:
+            UART2_CR1 &= ~(UART_CR1_PARITY_ENABLED | UART_CR1_PARITY_ODD);
+    }
+    /* Set stop bits */
+    reg = UART2_CR2 & ~UART_CR2_STOPBITS;
+    if (stop > 1)
+        UART2_CR2 = reg & (2 << 12);
+    else
+        UART2_CR2 = reg;
+
+    /* Turn on uart */
+    UART2_CR1 |= UART_CR1_UART_ENABLE;
+
+    return 0;
+}
+
+int _write(void *r, uint8_t *text, int len)
+{
+    char *p = (char *)text;
+    int i;
+    volatile uint32_t reg;
+    text[len - 1] = 0;
+    while(*p) {
+        do {
+            reg = UART2_SR;
+        } while ((reg & UART_SR_TX_EMPTY) == 0);
+        UART2_DR = *p;
+        p++;
+    }
+    return len;
+}
+
+/* commodity to print binary buffers */
+
+void printbin(const uint8_t *buf, int len)
+{
+    int i;
+    for (i = 0; i < len; i++) {
+        if ((i % 16) == 0)
+            printf("\r\n%08x: ", i);
+        printf("%02x ", buf[i]);
+    }
+    printf("\r\n");
+}
+

+ 9 - 0
uart.h

@@ -0,0 +1,9 @@
+#ifndef UART_H_INCLUDED
+#define UART_H_INCLUDED
+#include <stdint.h>
+
+
+int uart2_setup(uint32_t bitrate, uint8_t data, char parity, uint8_t stop);
+void uart2_write(const char *text);
+
+#endif

+ 178 - 0
ui.c

@@ -0,0 +1,178 @@
+/* 
+ * (c) danielinux 2019
+ *
+ *      GPLv.2
+ *
+ * See LICENSE for details
+ */
+#include <stdint.h>
+#include "system.h"
+#include "display.h"
+#include "systick.h"
+#include "button.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "ui.h"
+
+
+/* Uncomment for device initialization */
+//#define DEVICE_INITIALIZATION
+
+static const char welcome_message0[]= "  Smells like  ";
+static const char welcome_message1[]= "fresh electrons";
+static const char welcome_message2[]= "  and coffee.  ";
+
+
+static uint32_t last_action = 0;
+static uint32_t offset = 0;
+
+#define ST_LOCKED 0
+#define ST_PIN_ENTRY 1
+#define ST_UNLOCKED 2
+#define MAX_PASSWORD 32
+
+static int fsm_state = ST_LOCKED;
+static int sleeping = 0;
+
+static char password[MAX_PASSWORD] = {};
+static int pin_idx = 0;
+
+
+#define SLEEP_TIME (5000)
+
+static void ui_sleep(void)
+{
+    display_scroll(NULL, 0);
+    display_clear(NULL);
+    sleeping = 1;
+}
+
+static int ui_wakeup(void)
+{
+    last_action = jiffies;
+    if (sleeping) {
+        sleeping = 0;
+        return 1;
+    }
+    return 0;
+}
+
+void ui_msg(const char *txt)
+{
+    ui_wakeup();
+    display_scroll(NULL, 0);
+    display_clear(NULL);
+    display_text(6, txt);
+}
+
+
+static uint8_t display_cur_v = 0;
+static uint8_t menu_selection = 0;
+
+static void ui_display_menu_refresh(void)
+{
+    const struct display_menu *menu = CurrentMenu;
+    uint8_t vline;
+    int i;
+    display_scroll(NULL, display_cur_v);
+    display_clear(NULL);
+
+    for (i = menu->entry_n - 1; i >= 0; i--) {
+        vline = i + 5;
+        if (vline > 7)
+            vline -= 8;
+        if (i == menu_selection)
+            display_text_inverse(vline, menu->entry[i].title);
+        else
+            display_text(vline, menu->entry[i].title);
+    }
+    WFI();
+}
+
+void ui_display_menu(const struct display_menu *menu)
+{
+    uint8_t i;
+    display_clear(NULL);
+    menu_selection = 0;
+    display_cur_v = 0;
+    CurrentMenu = (struct display_menu *)menu;
+    ui_display_menu_refresh();
+}
+
+void ui_button_press(uint8_t b, int hold)
+{
+    if (b == '*') {
+        int n = menu_selection;
+        menu_selection = 0;
+        if (CurrentMenu->entry[n].action) {
+            display_cur_v = 0;
+            display_clear(NULL);
+            display_scroll(NULL, display_cur_v);
+            CurrentMenu->entry[n].action(
+                    CurrentMenu->entry[n].arg);
+            
+        }
+        return;
+    }
+    if (b == '-') {
+        if (menu_selection > 0) {
+            menu_selection--;
+            if ((display_cur_v / 8) - (menu_selection > 2)) {
+                int s;
+                s = display_cur_v + 8;
+                if (s > 0x3f)
+                    s = 0;
+                display_cur_v = (uint8_t)s;
+                display_scroll(NULL, display_cur_v);
+            }
+        }
+    }
+    if (b == '+') {
+        if (menu_selection < CurrentMenu->entry_n - 1) {
+            menu_selection++;
+            if ((menu_selection - display_cur_v / 8) > 2) {
+                int s;
+                s = display_cur_v - 8;
+                if (s < 0)
+                    s = 0x40 - 8;
+                display_cur_v = (uint8_t)s;
+                display_scroll(NULL, display_cur_v);
+            }
+        }
+    }
+    ui_display_menu_refresh();
+}
+
+void ui_init(void)
+{
+    uint32_t now;
+    int i;
+#if 0
+    display_scroll(NULL, 0x3f);
+    display_text(0, welcome_message0);
+    display_text(1, welcome_message1);
+    display_text(2, welcome_message2);
+    now = jiffies;
+    for (i = 0x3f; i >= 0x20; i--) {
+        display_scroll(NULL, i);
+        while ((jiffies - now) < 20)
+            WFI();
+        now = jiffies;
+    }
+    for (i = display_getcontrast(NULL); i >= 0; i--) {
+        display_setcontrast(NULL, i);
+        while ((jiffies - now) < 10)
+            WFI();
+        now = jiffies;
+    }
+#endif
+    display_scroll(NULL, display_cur_v);
+    display_clear(NULL);
+    display_setcontrast(NULL, 0xcf);
+    ui_display_menu(CurrentMenu);
+}
+
+void ui_keepalive(uint32_t timeslice)
+{
+}

+ 32 - 0
ui.h

@@ -0,0 +1,32 @@
+#ifndef UI_H
+#define UI_H
+#include <stdint.h>
+
+void ui_init(void);
+
+struct display_menu {
+    struct display_menu *next;
+    int entry_n;
+    struct display_menu_entry {
+        char title[16];
+        void (*action)(const void *arg);
+        const void *arg;
+    } entry[8];
+
+};
+
+const struct display_menu MainMenu, MeasureMenu, SignalGenMenu,
+      ExtraMenu;
+
+void ui_display_menu(const struct display_menu *menu);
+void ui_button_press(uint8_t b, int hold);
+
+struct display_menu *CurrentMenu;
+
+/* Defined in main to intercept input controls */
+void set_input_callback(void (*cb)(uint8_t press, int hold));
+void clear_input_callback(void);
+void set_keepalive(void (*cb)(void));
+void clear_keepalive(void);
+
+#endif

+ 345 - 0
ui_tester.c

@@ -0,0 +1,345 @@
+#include <stddef.h>
+#include "ui.h"
+#include <string.h>
+#include "adc.h"
+#include "button.h"
+#include "system.h"
+
+struct display_menu *CurrentMenu = &MainMenu;
+
+
+static int pin_selected = 0;
+static int front_selected = 0;
+static void ui_sel_pin(uint8_t press, int hold);
+static int pin_t1, pin_t2, front_t1, front_t2;
+static uint32_t time_t1_s, time_t1_us;
+
+static void ui_return(uint8_t press, int hold)
+{
+    if (press == '*') {
+        display_clear(NULL);
+        clear_input_callback();
+        ui_display_menu(&MainMenu);
+    }
+
+}
+
+static void ui_action_interrupt(uint8_t press, int hold)
+{
+    if (press == '*') {
+        clear_keepalive();
+        display_clear(NULL);
+        display_text(5, "Interrupted!");
+        set_input_callback(ui_return);
+    }
+}
+
+static void display_time(int line, uint32_t s, uint32_t us)
+{
+    char time_txt[] = "     .000000 s";
+    printf("in display_time (%u.%06u)\r\n", s, us);
+    if (s >= 10000)
+        time_txt[0] = '0' + s / 10000;
+    if (s >= 1000)
+        time_txt[1] = '0' + (s % 10000) / 1000;
+    if (s >= 100)
+        time_txt[2] = '0' + (s % 1000) / 100;
+    if (s >= 10)
+        time_txt[3] = '0' + (s % 100) / 10;
+    time_txt[4] = '0' +  s % 10;
+    /* . */
+    time_txt[6] += us / 100000;
+    time_txt[7] += (us % 100000) /  10000;
+    time_txt[8] += (us % 10000) /  1000;
+    time_txt[9] += (us % 1000) /  100;
+    time_txt[10] += (us % 100) /  10;
+    time_txt[11] += (us % 10);
+    printf("out display_time (%s)\r\n", time_txt);
+    display_text(line, time_txt);
+}
+
+static void trigger_poll(void)
+{
+    exti_poll();
+}
+static void trigger_t1(uint32_t pin, uint8_t front, uint32_t s, uint32_t us)
+{
+    if ((front_t1 == 2) || (front_t1 == front)) {
+        exti_clear_callback(pin_t1);
+        time_t1_s = s;
+        time_t1_us = us;
+        display_text(5, "T1");
+    }
+} 
+
+static void ui_restart_trigger(uint8_t press, int hold);
+static void trigger_t2(uint32_t pin, uint8_t front, uint32_t s, uint32_t us)
+{
+    uint32_t delta_s, delta_us;
+    if ((front_t2 == 2) || (front_t2 == front)) {
+        exti_clear_callback(pin);
+        delta_s = s - time_t1_s;
+        if (us < time_t1_us) {
+            delta_s--;
+            us += 1000000;
+        }
+        delta_us =  us - time_t1_us;
+        display_clear(NULL);
+        display_text(5, "Time Delta:    ");
+        if (delta_s > 99999) {
+            display_text(6, "Error!");
+            set_input_callback(ui_return);
+            return;
+        }
+        clear_keepalive();
+        display_time(6, delta_s, delta_us);
+        display_text(7, "Press to restart");
+        set_input_callback(ui_restart_trigger);
+    }
+}
+
+static void ui_restart_trigger(uint8_t press, int hold)
+{
+    if (press == '*') {
+        set_input_callback(ui_action_interrupt);
+        display_clear(NULL);
+        display_text(7, "Capturing...");
+        exti_set_callback(Channel_Pin[pin_t1], trigger_t1);
+        exti_set_callback(Channel_Pin[pin_t2], trigger_t2);
+        pin_exti_init();
+        pin_exti_start_read();
+        set_keepalive(trigger_poll);
+    }
+}
+
+
+
+static void ui_sel_pin(uint8_t press, int hold)
+{
+    char TXT[] = "PIN X          ";
+    if ((press == '-') && (front_selected == 0) && (pin_selected > 0)) {
+        pin_selected--;
+        front_selected = 2;
+    }else if ((press == '+') && (front_selected == 2) && (pin_selected < 4)) {
+        pin_selected++;
+        front_selected = 0;
+    } else if ((press == '+') && (front_selected < 2))
+        front_selected++;
+    else if ((press == '-') && (front_selected > 0))
+        front_selected--;
+    
+    switch (front_selected) {
+        case 0:
+            memcpy(TXT + 6, "FALL", 4);
+            break;
+        case 1:
+            memcpy(TXT + 6, "RISE", 4);
+            break;
+        case 2:
+            memcpy(TXT + 6, "BOTH", 4);
+            break;
+    } 
+
+    TXT[4] = '0' + pin_selected;
+    
+    display_text(6, TXT);
+    display_text(7, Channel_name[pin_selected]);
+    if (press == '*') {
+        if (pin_t1 < 0) {
+            pin_t1 = pin_selected;
+            front_t1 = front_selected;
+            pin_selected = 0;
+            front_selected = 0;
+            display_text(5, "Select T2 pin:");
+        } else {
+            pin_t2 = pin_selected;
+            front_t2 = front_selected;
+            set_input_callback(ui_action_interrupt);
+            display_clear(NULL);
+            display_text(7, "Capturing...");
+            exti_set_callback(Channel_Pin[pin_t1], trigger_t1);
+            exti_set_callback(Channel_Pin[pin_t2], trigger_t2);
+            pin_exti_init();
+            pin_exti_start_read();
+            set_keepalive(trigger_poll);
+        }
+    }
+}
+
+static void ui_measure_interval(const void *arg)
+{
+    int i;
+    pin_t1 = -1;
+    pin_t2 = -1;
+    set_input_callback(ui_sel_pin);
+    for (i = 0; i < 5; i++)
+        GPIOA_MODE &= ~(0x03 << (Channel_Pin[i] * 2));
+    display_text(5, "Select T1 pin:");
+    ui_sel_pin(0,0);
+}
+
+static void display_voltage(int line, uint16_t val)
+{
+    uint32_t mV = (val * 3300) / 4096;
+    char txt[] = "0.000";
+    if (val > 1000)
+        txt[0] = '0' + mV / 1000;
+    mV %= 1000;
+    if (mV != 0)
+        txt[4] = '0' + mV % 10;
+    if (mV > 9)
+        txt[3] = '0' + ((mV / 10) % 10);
+    if (mV > 99)
+        txt[2] = '0' + (mV / 100);
+    display_text(6, txt);
+}
+
+void ui_voltage_poll(void)
+{
+    uint16_t val = 0;
+    char TXT[]="PIN X ADC";
+    
+    if (pin_selected < 2)
+        pin_selected = 2;
+    TXT[4] = '0' + pin_selected;
+    GPIOA_MODE |= (0x03 << (Channel[pin_selected] * 2));
+    adc_pin_val(Channel[pin_selected], &val);
+    display_text(5, TXT);
+    display_voltage(6, val);
+    display_text(7, Channel_name[pin_selected]);
+}
+
+static void ui_voltage_input(uint8_t press, int hold)
+{
+    if (press == '*') {
+        display_clear(NULL);
+        display_text(5, "Interrupted!");
+        clear_keepalive();
+        set_input_callback(ui_return);
+        return;
+    }
+    if (press == '+') {
+        pin_selected++;
+        if (pin_selected > 4)
+            pin_selected = 2;
+    }
+    if (press == '-') {
+        pin_selected--;
+        if (pin_selected < 2)
+            pin_selected = 4;
+    }
+    ui_voltage_poll();
+}
+
+
+static void ui_measure_voltage(const void *arg)
+{
+    set_input_callback(ui_voltage_input);
+    set_keepalive(ui_voltage_poll);
+    display_clear(NULL);
+    ui_voltage_poll();
+}
+
+static void ui_input_poll(void)
+{
+    uint32_t val = 0;
+    char Line5[] = "P0: 0   P1: 0";
+    char Line6[] = "P2: 0   P3: 0";
+    char Line7[] = "P4: 0 ";
+    
+    val = gpio_get(GPIOA, GPIO8|GPIO9|GPIO7|GPIO6|GPIO5);
+
+    if (val & GPIO8)
+        Line5[4] = '1';
+    if (val & GPIO9)
+        Line5[12] = '1';
+    if (val & GPIO7)
+        Line6[4] = '1';
+    if (val & GPIO6)
+        Line6[12] = '1';
+    if (val & GPIO5)
+        Line7[4] = '1';
+
+    display_text(5, Line5);
+    display_text(6, Line6);
+    display_text(7, Line7); 
+
+
+}
+
+static void ui_measure_input(const void *arg)
+{
+    int i;
+    for (i = 0; i < 5; i++)
+        GPIOA_MODE &= ~(0x03 << (Channel_Pin[i] * 2));
+    set_input_callback(ui_action_interrupt);
+    set_keepalive(ui_input_poll);
+    ui_input_poll();
+}
+
+static void ui_measure_frequency(const void *arg)
+{
+
+
+}
+
+
+static void ui_generate_dc(const void *arg)
+{
+
+}
+
+
+static void ui_generate_pwm(const void *arg)
+{
+
+}
+
+
+static void ui_submenu(const void *arg)
+{
+    ui_display_menu(arg);
+}
+
+const struct display_menu SignalGenMenu = {
+    .entry_n = 3,
+    .entry = {
+        { "Generate DC     ", ui_generate_dc, NULL },
+        { "Generate PWM    ", ui_generate_pwm, NULL },
+        { "Back to Main    ", ui_submenu, &MainMenu },
+        { "", NULL, NULL}
+    }
+};
+
+const struct display_menu ExtraMenu = {
+    .entry_n = 1,
+    .entry = {
+        { "TODO!           ", ui_submenu, &MainMenu},
+        { "", NULL, NULL}
+    }
+};
+
+const struct display_menu MeasureMenu = {
+    .entry_n = 5,
+    .entry = {
+        { "Digital input", ui_measure_input, NULL },
+        { "Time Delta", ui_measure_interval, NULL },
+        { "Voltage ", ui_measure_voltage, NULL },
+        { "Frequency", ui_measure_frequency, NULL },
+        { "Back to Main    ", ui_submenu, &MainMenu },
+        { "", NULL, NULL}
+    }
+};
+
+const struct display_menu MainMenu = {
+    .entry_n = 3,
+    .entry = {
+        { "Measure        ", ui_submenu, &MeasureMenu },
+        { "Signal Gener.  ", ui_submenu, &SignalGenMenu },
+        { "Extra          ", ui_submenu, &ExtraMenu },
+        { "", NULL, NULL}
+    }
+};
+
+
+

+ 56 - 0
user_settings.h

@@ -0,0 +1,56 @@
+/* user_settings.h
+ */
+
+#ifndef H_USER_SETTINGS_
+#define H_USER_SETTINGS_
+
+/* System */
+#define WOLFSSL_GENERAL_ALIGNMENT 4
+#define SINGLE_THREADED
+//#define WOLFSSL_SMALL_STACK
+#define WOLFCRYPT_ONLY
+#define TFM_TIMING_RESISTANT
+#define HAVE_CHACHA
+#define HAVE_SHA256
+#define HAVE_PBKDF2
+#define HAVE_AES
+#define HAVE_AES_CBC
+#define HAVE_AES_DECRYPT
+
+#define CUSTOM_RAND_GENERATE random_uint32
+#define CUSTOM_RAND_TYPE uint32_t
+
+
+#define WOLFSSL_SP
+#define WOLFSSL_SP_SMALL
+#define WOLFSSL_SP_MATH
+#define SP_WORD_SIZE 32
+#define SINGLE_THREADED
+
+/* Disables - For minimum wolfCrypt build */
+#define NO_CMAC
+#define NO_CODING
+#define NO_RSA
+#define NO_BIG_INT
+#define NO_ASN
+#define NO_RC4
+#define NO_SHA
+#define NO_DH
+#define NO_DSA
+#define NO_MD4
+#define NO_RABBIT
+#define NO_MD5
+#define NO_SIG_WRAPPER
+#define NO_CERT
+#define NO_SESSION_CACHE
+#define NO_HC128
+#define NO_DES3
+#define NO_WRITEV
+#define NO_DEV_RANDOM
+#define NO_FILESYSTEM
+#define NO_MAIN_DRIVER
+#define NO_OLD_RNGNAME
+#define NO_WOLFSSL_DIR
+#define WOLFSSL_NO_SOCK
+
+#endif /* !H_USER_SETTINGS_ */