get improved project files from LED clock

This commit is contained in:
King Kévin 2016-08-14 18:36:27 +02:00
parent 618de224e0
commit cebbf579e0
2 changed files with 147 additions and 117 deletions

View File

@ -13,7 +13,7 @@
## the make file provide rule to compile and flash firmware for STM32F1 micro-controllers
## it uses libopencm3 and STM32duino-bootloader
## it uses libopencm3
# be silent per default, but 'make V=1' will show all compiler calls.
ifneq ($(V),1)
@ -26,10 +26,11 @@ BINARY = firmware
# which development board is used
# source files
CSRC = $(wildcard *.c)
CHDR = $(wildcard *.h)
OBJ = $(patsubst %.c,%.o,$(CSRC))
# figure out based on the includes which library files are used in the main CSRC files
DEPENDENCIES = $(patsubst %.c,,$(CSRC))
@ -39,6 +40,7 @@ LIB = lib
# the library files to use
# this will be populated using includes based DEPENDENCIES
LIB_CHDR = $(patsubst %.c,%.h,$(LIB_CSRC))
LIB_OBJ = $(patsubst %.c,%.o,$(LIB_CSRC))
# populates LIB_CSRC based on the library files used
-include $(DEPENDENCIES)
@ -72,7 +74,7 @@ DEFS += -DSTM32F1 -D$(BOARD)
# C flags
CFLAGS += -Os -g
CFLAGS += -Wall -Werror -Wundef -Wextra -Wshadow -Wimplicit-function-declaration -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes
CFLAGS += -std=c99 -Wpedantic -Wall -Werror -Wundef -Wextra -Wshadow -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes -Wstrict-overflow=5
CFLAGS += -fno-common -ffunction-sections -fdata-sections
CFLAGS += -I. -I$(INCLUDE_DIR) $(patsubst %,-I%,$(LIB))
@ -99,39 +101,34 @@ endif
# used libraries
LIBNAME = opencm3_stm32f1
LDLIBS += -lm -l$(LIBNAME)
LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group
# device specific flags
FP_FLAGS ?= -msoft-float
ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd
# SWD adapter used
# supported are : st-link v2 (STLINKV2), black magic probe (BMP)
# OpenOCD configuration
OOCD ?= openocd
OOCD_INTERFACE ?= stlink-v2
OOCD_TARGET ?= stm32f1x
else ifeq ($(SWD_ADAPTER),BMP)
# the black magic probe has a SWD controller built in
BMPPORT ?= /dev/ttyACM0
# which USB CDC ACM port is used bu the device, so we can reset it
ACMPORT = /dev/ttyACM0
else ifeq ($(SWD_ADAPTER),BMP)
ACMPORT = /dev/ttyACM2
ACMPORT_EXISTS = $(shell [ -e $(ACMPORT) ] && echo 1 || echo 0 )
# board specific USB DFU bootloader
BOOTLOADERS = STM32duino-bootloader
BOOTLOADER = $(BOOTLOADERS)/STM32F1/binaries/generic_boot20_pa1.bin
else ifeq ($(BOARD),BLUE_PILL)
BOOTLOADER = $(BOOTLOADERS)/STM32F1/binaries/generic_boot20_pc13.bin
else ifeq ($(BOARD),MAPLE_MINI)
BOOTLOADER = $(BOOTLOADERS)/STM32F1/binaries/maple_mini_boot20.bin
# verify if STM32duino-bootloader has been downloaded
BOOTLOADER_EXISTS = $(shell [ -f $(BOOTLOADER) ] && echo 1 || echo 0 )
$(info run "git submodule init" and "git submodule update" before runnig make)
$(error STM32duino-bootloader repository is not initialized)
# compile target rules
all: elf
@ -141,13 +138,10 @@ hex: $(BINARY).hex
srec: $(BINARY).srec
list: $(BINARY).list
%.bin: %.elf
%.hex: %.elf
%.srec: %.elf
%.bin %.hex %.srec: %.elf
$(Q)$(OBJCOPY) -Osrec $(<) $(@) %.elf
%.list: %.elf %.list: %.elf
$(Q)$(OBJDUMP) -S $(<) > $(@)
%.elf: $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a $(OBJ) $(LIB_OBJ)
@ -155,7 +149,7 @@ list: $(BINARY).list
$(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJ) $(LIB_OBJ) $(LDLIBS) -o $(@)
$(Q)size $(@)
%.o: %.c
%.o: %.c $(CHDR) $(LIB_CHDR)
$(Q)$(CC) $(CFLAGS) $(ARCH_FLAGS) -o $(@) -c $(<)
# generate dependencies
@ -167,6 +161,10 @@ list: $(BINARY).list %.d
$(Q)grep -o -e " ${LIB}\/[^ ]*\.h" $(<) | sed -e 's/\.h$$/.c/g' -e 's/^/LIB_CSRC +=/' > $(@)
# doxygen documentation
doc: Doxyfile $(CSRC) $(CHDR) $(LIB_CSRC) $(LIB_CHDR)
$(Q)doxygen $(<)
$(Q)$(RM) $(BINARY).elf $(BINARY).bin $(BINARY).hex $(BINARY).map $(OBJ) $(LIB_OBJ) $(LIB)/*.o $(DEPENDENCIES)
@ -175,15 +173,13 @@ $(LIB_DIR)/lib$(LIBNAME).a:
$(info compiling libopencm3 library)
bootloader: $(BOOTLOADER)
$(info flashing USB DFU bootloader $(<))
$(Q)$(OOCD) --file interface/$(OOCD_INTERFACE).cfg --file target/$(OOCD_TARGET).cfg --command "init" --command "reset init" --command "flash write_image erase $(<) 0x08000000" --command "reset" --command "shutdown" $(NULL)
flash: flash-dfu
flash-swd: $(BINARY).hex
flash: $(BINARY).hex
$(info flashing $(<) using SWD)
$(Q)$(OOCD) --file interface/$(OOCD_INTERFACE).cfg --file target/$(OOCD_TARGET).cfg --command "init" --command "reset init" --command "flash write_image erase $(<)" --command "reset" --command "shutdown" $(NULL)
else ifeq ($(SWD_ADAPTER),BMP)
$(Q)$(GDB) --eval-command="target extended-remote $(BMPPORT)" --eval-command="monitor version" --eval-command="monitor swdp_scan" --eval-command="attach 1" --eval-command="load" --eval-command="detach" --eval-command="kill" --eval-command="quit" $(<)
# reset device by setting the data width to 5 bis on the USB CDC ACM port
@ -191,13 +187,14 @@ ifeq ($(ACMPORT_EXISTS), 1)
$(Q)stty --file $(ACMPORT) 115200 raw cs5
$(Q)sleep 0.5
flash-dfu: $(BINARY).bin reset
$(info flashing $(<) using DFU)
$(Q)dfu-util --device 1eaf:0003 --cfg 1 --intf 0 --alt 2 --reset --download $(<) $(NULL)
# debug using jtag (openOCB+GDB)
# debug using GDB
debug: $(BINARY).elf
$(Q)$(GDB) --eval-command="target remote | $(OOCD) --file interface/stlink-v2.cfg --file target/stm32f1x.cfg --command \"gdb_port pipe; log_output /dev/null; init\"" --eval-command="monitor reset halt" --eval-command="load" --eval-command="monitor reset init" $(<)
# for GDB to work with openOCD the firmware needs to be reloaded
$(Q)$(GDB) --eval-command="target remote | $(OOCD) --file interface/$(OOCD_INTERFACE).cfg --file target/$(OOCD_TARGET).cfg --command \"gdb_port pipe; log_output /dev/null; init\"" --eval-command="monitor reset halt" --eval-command="load" --eval-command="monitor reset init" $(<)
else ifeq ($(SWD_ADAPTER),BMP)
$(Q)$(GDB) --eval-command="target extended-remote $(BMPPORT)" --eval-command="monitor version" --eval-command="monitor swdp_scan" --eval-command="attach 1" $(<)
.PHONY: clean elf bin hex srec list libraries bootloader $(BOOTLOADER) flash flash-swd reset flash-dfu
.PHONY: clean elf bin hex srec list libraries flash reset

View File

@ -1,107 +1,140 @@
this firmware template is designed for development boards based around [STM32 F1 series micro-controller](
The LED clock is an add-on for round wall clocks.
The purpose is to have LEDs on the circumference of the clock to show the progress of the time using coloured light.
For that you will need:
the source code uses the [libopencm3 library](, designed for such micro-controllers.
it also uses the [STM32duino-bootloader]( for easier flashing
- a WS2812B RGB LEDs strip (long enough to go around the clock)
- a development board with a STM32F103 micro-controller equipped with a 32.768 kHz oscillator for the Real Time Clock (such as the [blue pill](, or using a external [Maxim DS1307]( RTC module
- a coin cell battery to keep the RTC running (optional)
- a GL5528 photo-resistor to adjust the LED brightness (optional)
- a DCF77 module to set and update the time automatically (salvaged from a radio controlled digital clock)
both project are already git submodules.
to initialize and get them you just need to run once:
git submodule init
git submodule update
The time will be shown as arc progress bars, in addition to the original hands of the clock pointing at the current time.
The hours passed since the beginning of the midday are shown using blue LEDs.
The minutes passed sine the beginning of the hour are shown using green LEDs.
Whichever progress is higher will be shown on top of the other.
For example if it's 6:45, the first half of the circle will be blue, and an additional quarter will be green.
The seconds passed since the beginning of the minute are shown using a running red LED, similar to the seconds hand.
The red colour might be added on top of the blue, or green colour, then showing as violet or orange.
The (gamma corrected) brightness of the last LED shows how much of the hour, minute, or second has passed.
The brain of this add-on is a [STM32 F1 series micro-controller]( (based on an ARM Cortex-M3 32-bit processor).
To keep track of the time a Real Time Clock (RTC) is used.
If the board includes a 32.768 kHz oscillator (such as a [blue pill]( the micro-controller will use the internal RTC.
Otherwise connect an external [Maxim DS1307]( RTC module to the I2C port and set `EXTERNAL_RTC` in `main.c` to `true`.
Also connect the external RTC square wave output in order to have a sub-second time precision.
Connect a DCF77 module (e.g. salvaged from a radio controlled clock) to the micro-controller.
This will allow to automatically get precise time (at least in Europe) when booting.
Since the RTC is drifting, the time will get updated using DCF77 every hour to keep <0.5 s time precision.
Alternatively set the time using serial over the USB port (providing the CDC ACM profile) or USART port and enter "time HH:MM:SS".
Power the board using an external 5 V power supply (e.g. through the USB port).
This will power the micro-controller, and the LEDs (a single LED consumes more energy than the micro-controller).
To keep the correct time in case the main power supply gets disconnected optionally connect a 3 V coin battery on the VBAT pin for the internal RTC, or in the module for the external RTC.
For the LEDs use a 1 meter LED strip with 60 red-green-blue WS2812B LEDs.
Tape the LED strip along the border/edge of the clock.
Ideally the wall clock has a diameter of 32 cm for a 1 m LED strip to completely fit.
Otherwise change the number of actually used LEDs in the source files.
Connect the 5 V power rail of the LED strip to the 5 V pin of the board.
Connect the DIN signal line of the LED strip to the MISO pin of the micro-controller on PA6.
SPI is used to efficiently shift out the LED colour values to the WS2812B LEDs.
A custom clock is provided for this operation using channel 3 of timer 3 on pin PB0.
Simply connect this clock to the SPI CLK input on pin PA5.
The brightness of the LEDs is dependant on the ambient luminance.
To measure the ambient luminance a GL5528 photo-resistor is used.
Connect one leg of the photo-resistor to ADC channel 1 and the other to ground.
Connect one leg of a 1 kOhm resistor to ADC channel 1 and the other to a 3.3 V pin.
This voltage divider allows to measure the photo-sensor's resistance and determine the luminance.
If you don't want to use this feature, connect PA1 to ground for the highest brightness or Vcc for the lowest brightness.
currently the following development boards are supported:
The current implementation uses a [blue pill](
The underlying template also supports following board:
- [Maple Mini](, based on a STM32F103CBT6
- [System Board](, based on a STM32F103C8T6
- [blue pill](, based on a STM32F103C8T6
- [System Board](, based on a STM32F103C8T6
- [blue pill](i, based on a STM32F103C8T6
**you need to define which board you are using in the Makefile**
**Which board is used is defined in the Makefile**.
This is required:
this is required:
- for the linker script to know the memory layout (flash and RAM)
- to flash the corresponding bootloader
- map the user LEDs and buttons provided on the board
- map the user LED and button provided on the board
the `Makefile` offers two ways of flashing the firmware on the board:
- over the SWD port (Serial Wire Debug)
- using the USB DFU interface (Device Firmware Upgrade)
Connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment):
the default mechanism `make flash` uses DFU.
- USART1_TX; PA9; RX; UART RX; optional, same as over USB ACM
- USART1_RX; PA10; TX; UART TX; optional, same as over USB ACM
- I2C1_SDA; PB7; DS1307 SDA; SDA; optional, when using external RTC
- I2C1_SCL; PB6; DS1307 SCL; SCL; optional, when using external RTC
- TIM2_CH1_ETR; PA0; DS1307 SQ; square wave output; optional, when using external RTC
- ADC12_IN1; PA1; GL5528; photo-resistor + 1 kOhm to 3.3 V; without GL5528 photo-resistor connect to ground for highest brightness or Vcc for lowest brightness
- TIM3_CH3; PB0; PA5; SPI1_SCK; generated clock for WS2812B transmission
- SPI1_MISO; PA6; WS2812B DIN; DIN; WS2812B LED strip data stream
- GPIO; PA2; DCF77 PO; \#EN; DCF77 enable on low
- GPIO; PA3; DCF77 TN; DCF77; DCF77 high bit pulses
All pins are configured using `define`s in the corresponding source code.
to flash over SWD you need an SWD adapter.
the `Makefile` uses a ST-Link V2, along with the OpenOCD software.
the main firmware will be placed after the bootloader.
thus you first need to flash the bootloader first (see below), else the main firmware will not be started.
to flash the bootloader run `make bootloader`.
SWD is nice because it will always work, even if USB is buggy, or the code on the board is stuck.
it also does not require to press on any reset button.
to flash using SWD run `make flash-swd`
SWD also allows you to debug the code running on the micro-controller using GDB.
to start the debugging session use `make debug`.
to flash using DFU you just need to connect the USB port.
when booting the micro-controller will start the STM32duino-bootloader bootloader.
this configures the USB to accept firmware updates.
after a short timeout (<1s) it will start the main firmware.
the main firmware will not be started if the bootloader is missing.
you only have to flash the bootloader once, using the SWD method.
to flash the bootloader run `make bootloader`.
to then flash using DFU run `make flash-dfu`.
this will try to reset the board to start the bootloader.
else you will need to reset the board manually using the reset button.
The source code uses the [libopencm3]( library.
The projects is already a git submodules.
To initialize and it you just need to run once: `git submodule init` and `git submodule update`.
the firmware provides basic example code for various peripherals.
To compile the firmware run `make`.
to compile the firmware run `make`
To generate doxygen documentation run `make doc`.
if a button is present on the board, pressing it will toggle the LED.
whatever you send over UART (USART1) will be echoed back (also over USB).
The firmware will be flashed using SWD (Serial Wire Debug).
For that you need an SWD adapter.
The `Makefile` uses a ST-Link V2 along OpenOCD software (per default), or a Black Magic Probe.
To flash using SWD run `make flash`.
SWD also allows to debug the code running on the micro-controller using GDB.
To start the debugging session run `make debug`.
the firmware also offer serial communication over USB using the CDC ACM device class.
since the micro-controller first starts the bootloader, it is recognised a DFU device.
to provide the CDC ACM interface the host needs to re-enumerate the USB device.
for this a disconnect disconnect is simulated by pulling USB D+ low for a short time (in software or using a dedicated circuit).
then the host will re-enumerate the USB device and see the CDC ACM interface.
whatever you send over USB (CDC ACM) will be echoed back (also over UART).
additionally you can reset the board by setting the serial width to 5 bits.
this allows to restart the bootloader and flash new firmware using DFU.
to reset the board run `make reset`.
this only works if the USB CDC ACM run correctly and the micro-controller isn't stuck.
The firmware offers serial communication over USART1 and USB (using the CDC ACM device class).
You can also reset the board by setting the serial width to 5 bits over USB.
To reset the board run `make reset`.
This only works if the USB CDC ACM is running correctly and the micro-controller isn't stuck.