diff --git a/Makefile b/Makefile index 2f85639..5f3fdbc 100644 --- a/Makefile +++ b/Makefile @@ -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 # supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL -BOARD = MAPLE_MINI +BOARD = BLUE_PILL # 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,%.inc,$(CSRC)) @@ -39,6 +40,7 @@ LIB = lib # the library files to use # this will be populated using includes based DEPENDENCIES LIB_CSRC = +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)) CFLAGS += $(DEFS) @@ -99,39 +101,34 @@ endif # used libraries LIBNAME = opencm3_stm32f1 -LDLIBS += -l$(LIBNAME) +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) +SWD_ADAPTER ?= STLINKV2 +ifeq ($(SWD_ADAPTER),STLINKV2) # 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 +endif # which USB CDC ACM port is used bu the device, so we can reset it +ifeq ($(SWD_ADAPTER),STLINKV2) ACMPORT = /dev/ttyACM0 +else ifeq ($(SWD_ADAPTER),BMP) +ACMPORT = /dev/ttyACM2 +endif ACMPORT_EXISTS = $(shell [ -e $(ACMPORT) ] && echo 1 || echo 0 ) -# board specific USB DFU bootloader -BOOTLOADERS = STM32duino-bootloader -ifeq ($(BOARD),SYSTEM_BOARD) -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 -endif - -# verify if STM32duino-bootloader has been downloaded -BOOTLOADER_EXISTS = $(shell [ -f $(BOOTLOADER) ] && echo 1 || echo 0 ) -ifeq ($(BOOTLOADER_EXISTS), 0) -$(info run "git submodule init" and "git submodule update" before runnig make) -$(error STM32duino-bootloader repository is not initialized) -endif - # 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 $(<) $(@) -%.map: %.elf -%.list: %.elf +%.map %.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 %.inc: %.d $(Q)grep -o -e " ${LIB}\/[^ ]*\.h" $(<) | sed -e 's/\.h$$/.c/g' -e 's/^/LIB_CSRC +=/' > $(@) +# doxygen documentation +doc: Doxyfile README.md $(CSRC) $(CHDR) $(LIB_CSRC) $(LIB_CHDR) + $(Q)doxygen $(<) + clean: $(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) $(Q)$(MAKE) -C $(OPENCM3_DIR) -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) +ifeq ($(SWD_ADAPTER),STLINKV2) $(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" $(<) +endif # reset device by setting the data width to 5 bis on the USB CDC ACM port reset: @@ -191,13 +187,14 @@ ifeq ($(ACMPORT_EXISTS), 1) $(Q)stty --file $(ACMPORT) 115200 raw cs5 $(Q)sleep 0.5 endif - -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" $(<) +ifeq ($(SWD_ADAPTER),STLINKV2) + # 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" $(<) +endif -.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 diff --git a/README.md b/README.md index d671115..6de3b17 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,140 @@ -this firmware template is designed for development boards based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031). +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. -dependencies -============ +For that you will need: -the source code uses the [libopencm3 library](http://libopencm3.org/), designed for such micro-controllers. -it also uses the [STM32duino-bootloader](https://github.com/rogerclarkmelbourne/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](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill)), or using a external [Maxim DS1307](https://www.maximintegrated.com/en/products/digital/real-time-clocks/DS1307.html) 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: -```bash -git submodule init -git submodule update -``` +project +======= + +summary +------- + +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. + + +technology +---------- + +The brain of this add-on is a [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031) (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](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill)) the micro-controller will use the internal RTC. +Otherwise connect an external [Maxim DS1307](https://www.maximintegrated.com/en/products/digital/real-time-clocks/DS1307.html) 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. board ===== -currently the following development boards are supported: +The current implementation uses a [blue pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill). + +The underlying template also supports following board: + - [Maple Mini](http://leaflabs.com/docs/hardware/maple-mini.html), based on a STM32F103CBT6 -- [System Board](http://www.aliexpress.com/item/stm32f103c8t6-stm32f103-stm32f1-stm32-system-board-learning-board-evaluation-kit-development-board/2042654667.html), based on a STM32F103C8T6 -- [blue pill](http://www.aliexpress.com/item/1pcs-STM32F103C8T6-ARM-STM32-Minimum-System-Development-Board-Module-For-arduino/32478120209.html), based on a STM32F103C8T6 +- [System Board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#system_board), based on a STM32F103C8T6 +- [blue pill](ihttps://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), 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 -flash -===== +connections +=========== -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 -SWD ---- +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. +code +==== -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`. +dependencies +------------ -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`. - -DFU ---- - -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](http://libopencm3.org/) 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`. firmware -======== +-------- -the firmware provides basic example code for various peripherals. +To compile the firmware run `make`. -to compile the firmware run `make` +documentation +------------- -button ------- +To generate doxygen documentation run `make doc`. -if a button is present on the board, pressing it will toggle the LED. - -UART +flash ----- -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`. + +debug +----- + +SWD also allows to debug the code running on the micro-controller using GDB. +To start the debugging session run `make debug`. USB --- -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.