commit 8c317f2904e823e6db23eecadddc085ddbe55861 Author: Thomas Kolb Date: Sat Jun 5 00:04:36 2021 +0200 Initial commit - Set up build with latest libopencm3 - Created basic project structure - Implement Charlieplexing for the LEDs and a little test sequence diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd691c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Build results +/bin +/obj + +# Vim swap files +.*.sw? diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..478d474 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "libopencm3"] + path = libopencm3 + url = https://github.com/libopencm3/libopencm3.git +[submodule "fxplib"] + path = fxplib + url = https://github.com/cfr34k/fxplib.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7bf356b --- /dev/null +++ b/Makefile @@ -0,0 +1,181 @@ +# --- START OF CONFIG --------------------------------------------------- +# Edit the following variables for your own needs + +# toolchain configuration +PREFIX ?= arm-none-eabi- +CC = $(PREFIX)gcc +LD = $(PREFIX)gcc +OBJCOPY = $(PREFIX)objcopy +OBJDUMP = $(PREFIX)objdump +GDB = $(PREFIX)gdb +SIZE = $(PREFIX)size + +OOCD = openocd +OOCD_CFG = ./oocd/lnsc-2420.cfg + +TOOLCHAIN_DIR ?= /usr/arm-none-eabi +OPENCM3_DIR ?= ./libopencm3 + +# default build configuration +# "make BUILD=release" does a release build +BUILD:=debug + +# basic build flags configuration +CFLAGS+=-Wall -std=c99 -pedantic -Wextra -Wimplicit-function-declaration \ + -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes \ + -Wundef -Wshadow \ + -fno-common -ffunction-sections -fdata-sections -mcpu=cortex-m0 -mthumb \ + -mfloat-abi=soft -MD -DSTM32F0 + +LDFLAGS+=--static \ + -L$(OPENCM3_DIR)/lib \ + -nostartfiles -Wl,--gc-sections \ + -mthumb -mcpu=cortex-m0 -mthumb -mfloat-abi=soft + +# the LD script +#LDFLAGS+=-Tldscripts/lnsc-2420-$(BUILD).ld +LDFLAGS+=-Tldscripts/lnsc-2420-release.ld + +# Flags for libopencm3 +CFLAGS+=-I$(OPENCM3_DIR)/include +LDFLAGS+=-L$(OPENCM3_DIR)/lib -lopencm3_stm32f0 + +# Flags for fxplib +CFLAGS+=-Ifxplib/include -DPOINTPOS=16 +LDFLAGS+=-Lfxplib/lib/$(BUILD) -lfxp_stm32f0 + +# generic linking +LDFLAGS+=-Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +# build type specific flags +CFLAGS_debug=-O0 -ggdb -DDEBUG +LDFLAGS_debug= + +CFLAGS_release=-Os -ggdb +LDFLAGS_release= + +# target configuration +TARGET := lnsc-2420 +VERSION := 0.0.0 +VCSVERSION := $(shell git rev-parse --short HEAD) + +# source files for the project +SOURCE := $(shell find src/ -name '*.c') +INCLUDES := $(shell find src/ -name '*.h') + +# additional dependencies for build (proper targets must be specified by user) +DEPS := build_libfxp + +# default target +all: $(TARGET) + +# user-specific targets +export CFLAGS +export BUILD +export PREFIX +export POSTFIX = stm32f0 +build_libfxp: + cd fxplib && $(MAKE) + +# --- END OF CONFIG ----------------------------------------------------- + +OBJ1=$(patsubst %.c, %.o, $(SOURCE)) +OBJ=$(patsubst src/%, obj/$(BUILD)/%, $(OBJ1)) + +VERSIONSTR="\"$(VERSION)-$(VCSVERSION)\"" + +CFLAGS+=-DVERSION=$(VERSIONSTR) + +TARGET_BASE := bin/$(BUILD)/$(TARGET) + +CFLAGS+=$(CFLAGS_$(BUILD)) +LDFLAGS+=$(LDFLAGS_$(BUILD)) + +.PHONY show_cflags: + @echo --- Build parameters: ------------------------------------------ + @echo CFLAGS\=$(CFLAGS) + @echo LDFLAGS\=$(LDFLAGS) + @echo SOURCE\=$(SOURCE) + @echo ----------------------------------------------------------------- + +$(TARGET): show_cflags $(TARGET_BASE).elf $(TARGET_BASE).hex \ + $(TARGET_BASE).lss $(TARGET_BASE).bin + @$(SIZE) $(TARGET_BASE).elf + @echo ">>> $(BUILD) build complete." + +$(TARGET_BASE).elf: $(DEPS) $(OBJ) $(INCLUDES) Makefile + @echo Linking $@ ... + @mkdir -p $(shell dirname $@) + @$(LD) -o $(TARGET_BASE).elf $(OBJ) $(LDFLAGS) + +$(TARGET_BASE).hex: $(TARGET_BASE).elf + @echo "Generating $@ ..." + @$(OBJCOPY) -Oihex $< $@ + +$(TARGET_BASE).bin: $(TARGET_BASE).elf + @echo "Generating $@ ..." + @$(OBJCOPY) -Obinary $< $@ + +$(TARGET_BASE).lss: $(TARGET_BASE).elf + @echo "Generating $@ ..." + @$(OBJDUMP) -S $< > $@ + +obj/$(BUILD)/%.o: src/%.c $(INCLUDES) Makefile + @echo "Compiling $< ..." + @mkdir -p $(shell dirname $@) + @$(CC) -c $(CFLAGS) -o $@ $< + +clean: + rm -f $(TARGET_BASE).elf + rm -f $(TARGET_BASE).hex + rm -f $(TARGET_BASE).lss + rm -f $(TARGET_BASE).bin + rm -f $(OBJ) + +program: program_jlink + +reset: reset_jlink + +/tmp/jlink_prog_script: Makefile + echo "Device STM32F030C8" > $@ + echo "connect" >> $@ + echo "S" >> $@ + echo "4000" >> $@ + echo "loadfile $(TARGET_BASE).hex" >> $@ + echo "r" >> $@ + echo "g" >> $@ + echo "exit" >> $@ + +/tmp/jlink_rst_script: Makefile + echo "Device STM32F030C8" > $@ + echo "connect" >> $@ + echo "S" >> $@ + echo "4000" >> $@ + echo "r" >> $@ + echo "g" >> $@ + echo "exit" >> $@ + +program_jlink: /tmp/jlink_prog_script $(TARGET_BASE).hex + JLinkExe ram + + /* C++ Static constructors/destructors, also used for __attribute__ + * ((constructor)) and the likes */ + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >ram + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >ram + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >ram + + /* + * Another section used by C++ stuff, appears when using newlib with + * 64bit (long long) printf support + */ + .ARM.extab : { + *(.ARM.extab*) + } >ram + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >ram + + . = ALIGN(4); + _etext = .; + + .data : { + _data = .; + *(.data*) /* Read-write initialized data */ + . = ALIGN(4); + _edata = .; + } >ram + + .bss : { + *(.bss*) /* Read-write zero initialized data */ + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + + /* + * The .eh_frame section appears to be used for C++ exception handling. + * You may need to fix this if you're using C++. + */ + /DISCARD/ : { *(.eh_frame) } + + . = ALIGN(4); + end = .; +} + +PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); + + diff --git a/ldscripts/lnsc-2420-release.ld b/ldscripts/lnsc-2420-release.ld new file mode 100644 index 0000000..0b7c6ba --- /dev/null +++ b/ldscripts/lnsc-2420-release.ld @@ -0,0 +1,12 @@ +/* Linker script for LNSC-2420: STM32F0K6T6*/ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 32K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 4K +} + +/* Include the common ld script. */ +INCLUDE cortex-m-generic.ld + diff --git a/libopencm3 b/libopencm3 new file mode 160000 index 0000000..3b89fc5 --- /dev/null +++ b/libopencm3 @@ -0,0 +1 @@ +Subproject commit 3b89fc5999874c49f6f5be65bbd5e75f7d77469c diff --git a/oocd/lnsc-2420.cfg b/oocd/lnsc-2420.cfg new file mode 100644 index 0000000..a211fd9 --- /dev/null +++ b/oocd/lnsc-2420.cfg @@ -0,0 +1,7 @@ +source [find interface/stlink-v2.cfg] + +transport select hla_swd + +source [find target/stm32f0x.cfg] + +reset_config srst_only diff --git a/src/led_chplex.c b/src/led_chplex.c new file mode 100644 index 0000000..0c1fc9d --- /dev/null +++ b/src/led_chplex.c @@ -0,0 +1,107 @@ +#include + +#include "pinout.h" + +#include "led_chplex.h" + +#define NUM_LEDs 6 + +static uint8_t led_index; +static uint8_t led_mask; + + +void led_chplex_init(void) +{ + // set up required GPIOs + gpio_clear(LED_PORT, LED_ALL_PINS); + gpio_mode_setup(LED_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, LED_ALL_PINS); + + // initialize charlieplex stepping + led_index = 0; + + // all LEDs off by default + led_mask = 0; +} + + +void led_chplex_periodic(void) +{ + // set all GPIOs to input to ensure the LEDs are properly switched off + gpio_mode_setup(LED_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, LED_ALL_PINS); + + // apply the proper signals depending on LED index + if(led_mask & (1 << led_index)) { + switch(led_index) + { + case LED_CHPLEX_IDX_SOLAR_ON: + gpio_set(LED_PORT, LED_A_PIN); + gpio_clear(LED_PORT, LED_B_PIN); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_A_PIN | LED_B_PIN); + break; + + case LED_CHPLEX_IDX_LOAD_ON: + gpio_set(LED_PORT, LED_B_PIN); + gpio_clear(LED_PORT, LED_C_PIN); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_B_PIN | LED_C_PIN); + break; + + case LED_CHPLEX_IDX_ERR_TEMP: + gpio_set(LED_PORT, LED_B_PIN); + gpio_clear(LED_PORT, LED_A_PIN); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_A_PIN | LED_B_PIN); + break; + + case LED_CHPLEX_IDX_ERR_LOAD: + gpio_set(LED_PORT, LED_C_PIN); + gpio_clear(LED_PORT, LED_B_PIN); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_B_PIN | LED_C_PIN); + break; + + case LED_CHPLEX_IDX_CHARGE_PULSE: + gpio_set(LED_PORT, LED_A_PIN); + gpio_clear(LED_PORT, LED_C_PIN); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_A_PIN | LED_C_PIN); + break; + + case LED_CHPLEX_IDX_DISCHARGE_PULSE: + gpio_set(LED_PORT, LED_C_PIN); + gpio_clear(LED_PORT, LED_A_PIN); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_A_PIN | LED_C_PIN); + break; + + default: + // unknown index => do nothing and reset index + led_index = 0; + break; + } + } + + led_index++; + if(led_index >= NUM_LEDs) { + led_index = 0; + } +} + + +void led_chplex_on(uint8_t idx) +{ + led_mask |= (1 << idx); +} + + +void led_chplex_off(uint8_t idx) +{ + led_mask &= ~(1 << idx); +} + + +void led_chplex_toggle(uint8_t idx) +{ + led_mask ^= (1 << idx); +} + + +void led_chplex_mask(uint8_t mask) +{ + led_mask = mask; +} diff --git a/src/led_chplex.h b/src/led_chplex.h new file mode 100644 index 0000000..13961c3 --- /dev/null +++ b/src/led_chplex.h @@ -0,0 +1,22 @@ +#ifndef LED_CHPLEX_H +#define LED_CHPLEX_H + +#include + +#define LED_CHPLEX_IDX_SOLAR_ON 0 +#define LED_CHPLEX_IDX_LOAD_ON 1 +#define LED_CHPLEX_IDX_ERR_TEMP 2 +#define LED_CHPLEX_IDX_ERR_LOAD 3 +#define LED_CHPLEX_IDX_CHARGE_PULSE 4 +#define LED_CHPLEX_IDX_DISCHARGE_PULSE 5 + +void led_chplex_init(void); +void led_chplex_periodic(void); + +void led_chplex_on(uint8_t idx); +void led_chplex_off(uint8_t idx); +void led_chplex_toggle(uint8_t idx); + +void led_chplex_mask(uint8_t mask); + +#endif // LED_CHPLEX_H diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..0ead9c3 --- /dev/null +++ b/src/main.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "led_chplex.h" + +volatile int wait_frame = 1; + + +static void init_gpio(void) +{ + // Set up UART TX on PB6 for debugging + /*gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6); + gpio_set_af(GPIOB, GPIO_AF0, GPIO6);*/ + +} + + +static void init_clock(void) +{ + /* Set STM32 to 48 MHz. */ + rcc_clock_setup_in_hse_8mhz_out_48mhz(); // generate 48 MHz from external 8 MHz crystal + //rcc_clock_setup_in_hsi_out_48mhz(); // generate ~48 MHz from internal RC oscillator + + // enable GPIO clocks: + // Port B is needed for the LEDs + rcc_periph_clock_enable(RCC_GPIOB); +} + + +/* Set up systick to fire freq times per second */ +static void init_systick(int freq) +{ + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); + /* clear counter so it starts right away */ + STK_CVR = 0; + + systick_set_reload(rcc_ahb_frequency / freq); + systick_counter_enable(); + systick_interrupt_enable(); +} + + +static bool ledtest(uint64_t timebase_ms) +{ + if(timebase_ms == 0) { + led_chplex_mask(0x3F); // all on + } else if(timebase_ms == 1000) { + led_chplex_mask(0x01); + } else if(timebase_ms == 1200) { + led_chplex_mask(0x02); + } else if(timebase_ms == 1400) { + led_chplex_mask(0x04); + } else if(timebase_ms == 1600) { + led_chplex_mask(0x08); + } else if(timebase_ms == 1800) { + led_chplex_mask(0x10); + } else if(timebase_ms == 2000) { + led_chplex_mask(0x20); + } else if(timebase_ms == 2200) { + led_chplex_mask(0x10); + } else if(timebase_ms == 2400) { + led_chplex_mask(0x08); + } else if(timebase_ms == 2600) { + led_chplex_mask(0x04); + } else if(timebase_ms == 2800) { + led_chplex_mask(0x02); + } else if(timebase_ms == 3000) { + led_chplex_mask(0x01); + } else if(timebase_ms == 3200) { + led_chplex_mask(0x00); + return true; + } + + return false; +} + + +int main(void) +{ + //uint32_t cpuload = 0; + uint64_t timebase_ms = 0; + bool ledtest_done = false; + + init_clock(); + init_gpio(); + + led_chplex_init(); + led_chplex_on(LED_CHPLEX_IDX_SOLAR_ON); + + init_systick(1000); + + // triggered every 1 ms + while (1) { + + if(!ledtest_done) { + ledtest_done = ledtest(timebase_ms); + } else if(timebase_ms % 500 == 0) { + led_chplex_toggle(LED_CHPLEX_IDX_DISCHARGE_PULSE); + } + + + led_chplex_periodic(); + + timebase_ms++; + + while(wait_frame) { + __WFI(); + } + + wait_frame = 1; + } + + return 0; +} + + +/* Called when systick fires */ +void sys_tick_handler(void) +{ + wait_frame = 0; +} + + +void hard_fault_handler(void) +{ + while (1); +} diff --git a/src/pinout.h b/src/pinout.h new file mode 100644 index 0000000..202729b --- /dev/null +++ b/src/pinout.h @@ -0,0 +1,14 @@ +#ifndef PINOUT_H +#define PINOUT_H + +/* LEDs */ + +#define LED_PORT GPIOB + +#define LED_A_PIN GPIO0 +#define LED_B_PIN GPIO1 +#define LED_C_PIN GPIO3 + +#define LED_ALL_PINS (LED_A_PIN | LED_B_PIN | LED_C_PIN) + +#endif // PINOUT_H diff --git a/src/system.c b/src/system.c new file mode 100644 index 0000000..2319d4b --- /dev/null +++ b/src/system.c @@ -0,0 +1,72 @@ +// This file is used to override the reset handler + +#include +#include +#include + +/* Symbols exported by the linker script(s): */ +typedef void (*funcp_t) (void); +extern funcp_t __preinit_array_start, __preinit_array_end; +extern funcp_t __init_array_start, __init_array_end; +extern funcp_t __fini_array_start, __fini_array_end; + +void main(void); +caddr_t _sbrk(int incr); + + +void __attribute__ ((naked)) reset_handler(void) +{ + volatile unsigned *src; + volatile unsigned *dest; + + funcp_t *fp; + + for (src = &_data_loadaddr, dest = &_data; + dest < &_edata; + src++, dest++) { + *dest = *src; + } + + while (dest < &_ebss) { + *dest++ = 0; + } + + /* Constructors. */ + for (fp = &__preinit_array_start; fp < &__preinit_array_end; fp++) { + (*fp)(); + } + for (fp = &__init_array_start; fp < &__init_array_end; fp++) { + (*fp)(); + } + + /* might be provided by platform specific vector.c */ + //pre_main(); + + /* Call the application's entry point. */ + main(); + + /* Destructors. */ + for (fp = &__fini_array_start; fp < &__fini_array_end; fp++) { + (*fp)(); + } + +} + +caddr_t _sbrk(int incr) +{ + extern char end; /* Defined by the linker */ + static char *heap_end; + char *prev_heap_end; + + if (heap_end == 0) { + heap_end = &end; + } + prev_heap_end = heap_end; + // if (heap_end + incr > stack_ptr) { + // // heap and stack collision -> hang + // while(1); + // } + + heap_end += incr; + return (caddr_t) prev_heap_end; +}