Compare commits
126 Commits
Author | SHA1 | Date | |
---|---|---|---|
93cc46ba83 | |||
624006500f | |||
5c78aa498e | |||
485fcd186b | |||
82952ddda3 | |||
0e3247bf73 | |||
6ebdb31e07 | |||
61a427c18a | |||
e44110d315 | |||
b4234fedd9 | |||
426cd48d0f | |||
bccf187c1a | |||
79d8860cc2 | |||
09ed3c91ea | |||
68def6b234 | |||
d5e63eefd2 | |||
f83af4bab8 | |||
3cdebf1cc8 | |||
1d93451981 | |||
99b2ccd62d | |||
2028e485d8 | |||
de5ef8ed55 | |||
9c9893e1da | |||
a716cb10cf | |||
0a18d73197 | |||
8c01cbd918 | |||
019b82d384 | |||
a89d8abb38 | |||
ced5582a1a | |||
4d53d868fe | |||
ce0848343d | |||
863fd744d7 | |||
d85d345ec4 | |||
35a614750d | |||
b3cf0d0302 | |||
3a6a64928d | |||
99d66f4e4e | |||
66521e1981 | |||
210fab8eae | |||
7318d70dcd | |||
0010c5e046 | |||
6b3b55839e | |||
6bed3ab0fb | |||
9a7c51f80e | |||
7a74f9709f | |||
fa29cfc29f | |||
2248ba1762 | |||
ac255816a1 | |||
95b63a06f5 | |||
7656c699bf | |||
c6a4f58b93 | |||
b82520fa9b | |||
25fcf8fe0b | |||
01eaa5cfab | |||
793611d629 | |||
ad52abc26b | |||
b0f5f127f6 | |||
a449b9b7ff | |||
4c6e9a4fda | |||
789b36fc21 | |||
c8861f40c4 | |||
77415cb41f | |||
11f5bc9771 | |||
8526dc084b | |||
fea286914b | |||
cfcc8a1bb6 | |||
510c82d00f | |||
26f6de3015 | |||
a9461b53f5 | |||
d7b6300a50 | |||
d0bd71b266 | |||
a46b6a1630 | |||
b100c4ae13 | |||
a0f9b4a530 | |||
e32e27100d | |||
5b0523f751 | |||
d6cac41b78 | |||
adc62ebb9a | |||
d9a15f2daa | |||
c4af940975 | |||
c58d27cf2e | |||
c3d7711258 | |||
78cb85421a | |||
ff5fbc847d | |||
51e0bfd188 | |||
c411d552a1 | |||
ceff33ea0e | |||
68955ddfec | |||
40ee01ce67 | |||
0b2bbf8c97 | |||
87af738378 | |||
e4ce622f15 | |||
dbd0ea4d27 | |||
a878a1ad9c | |||
aff4275478 | |||
609188d74e | |||
63a2e5e5ff | |||
ac1bea1d45 | |||
7b7f26ee47 | |||
3d00bdf3c0 | |||
319a02d2b4 | |||
cc8be1f278 | |||
e255573b1e | |||
0fe7e1fd39 | |||
2249f460e3 | |||
aae4009fbe | |||
a9284b7154 | |||
777fd7afb9 | |||
31079d95dd | |||
ced714129c | |||
4fcfd29d2b | |||
06de8d0be9 | |||
de36c7f3a2 | |||
8918b97618 | |||
0bb2be3727 | |||
a781fc5b3b | |||
00ef5d9344 | |||
9fbf5b4aad | |||
46083bdf5e | |||
8a165c4d71 | |||
9db9ea9dc1 | |||
4b514c6801 | |||
6a34352914 | |||
35c441355d | |||
9751880813 | |||
e58614002c |
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,4 +1,4 @@
|
||||
[submodule "libopencm3"]
|
||||
path = libopencm3
|
||||
url = https://github.com/libopencm3/libopencm3
|
||||
url = https://github.com/libopencm3/libopencm3/
|
||||
ignore = all
|
||||
|
44
README.md
44
README.md
@ -1,4 +1,4 @@
|
||||
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).
|
||||
This firmware template is designed for development boards based around [STM32 F4 series micro-controller](https://www.st.com/en/microcontrollers-microprocessors/stm32f4-series.html).
|
||||
|
||||
project
|
||||
=======
|
||||
@ -16,36 +16,17 @@ technology
|
||||
board
|
||||
=====
|
||||
|
||||
The current implementation uses a [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board).
|
||||
|
||||
The underlying template also supports following board:
|
||||
|
||||
- [Maple Mini](http://leaflabs.com/docs/hardware/maple-mini.html), based on a STM32F103CBT6
|
||||
- [System Board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#system_board), based on a STM32F103C8T6
|
||||
- [blue pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6
|
||||
- [black pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#black_pill), based on a STM32F103C8T6
|
||||
- [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board), based on a STM32F103C8T6
|
||||
- [ST-LINK V2 mini](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2), a ST-LINK/V2 clone based on a STM32F101C8T6
|
||||
- [USB-Blaster](https://wiki.cuvoodoo.info/doku.php?id=jtag#armjishu_usb-blaster), an Altera USB-Blaster clone based on a STM32F101C8T6
|
||||
- [WeAct MiniF4](https://github.com/WeActTC/MiniF4-STM32F4x1), based on a STM32F401CCU6
|
||||
|
||||
**Which board is used is defined in the Makefile**.
|
||||
This is required to map the user LED and button provided on the board
|
||||
|
||||
The ST-LINK V2 mini clone has SWD test points on the board.
|
||||
Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware.
|
||||
To remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
|
||||
|
||||
The Altera USB-Blaster clone has a pin header for SWD and UART1 on the board.
|
||||
SWD is disabled in the main firmware, and it has read protection.
|
||||
To be able to flash using SWD (or the serial port), the BOOT0 pin must be set to 1 to boot the system memory install of the flash memory.
|
||||
To set BOOT0 to 1, apply 3.3 V on R11, between the resistor and the reference designator, when powering the device.
|
||||
The red LED should stay off while the green LED is on.
|
||||
Now you can remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
|
||||
|
||||
connections
|
||||
===========
|
||||
|
||||
Connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment):
|
||||
Connect the peripherals the following way (STM32F4xx signal; STM32F4xx pin; peripheral pin; peripheral signal; comment):
|
||||
|
||||
- *list board to peripheral pin connections*
|
||||
|
||||
@ -81,15 +62,20 @@ The `bootloader` is started first and immediately jumps to the `application` if
|
||||
The `application` image is the main application and is implemented in `application.c`.
|
||||
It is up to the application to advertise USB DFU support (i.e. as does the provided USB CDC ACM example).
|
||||
|
||||
The `bootloader` image will be flashed using SWD (Serial Wire Debug).
|
||||
The simplest way do flash the `bootloader` image is using the embedded bootloader.
|
||||
By pressing the BOOT0 button (setting the pin low) while powering or resetting the device, the micro-controller boot its embedded UART/USB DFU bootloader.
|
||||
Connect a USB cable and run `rake dfu_bootloader`.
|
||||
|
||||
Once the `bootloader` is flashed, it is possible to flash the `application` over USB using the DFU protocol by running `rake flash` (equivalent to `rake dfu_application`.
|
||||
To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board.
|
||||
Note: I use my own DFU bootloader instead of the embedded bootloader because I was not able to start the embedded USB DFU bootloader from the application.
|
||||
|
||||
The images can also be flash using SWD (Serial Wire Debug) in case the firmware gets stuck and does not provide USB functionalities.
|
||||
For that you need an SWD adapter.
|
||||
The `Makefile` uses a ST-Link V2 programmer along OpenOCD software (default), or Black Magic Probe.
|
||||
To flash the `booltoader` using SWD run `rake flash_booloader`.
|
||||
If the development board uses the CKS32 chip STM32 alternative, use `CPUTAPID=0x2ba01477 rake flash_booloader`.
|
||||
|
||||
Once the `bootloader` is flashed it is possible to flash the `application` over USB using the DFU protocol by running `rake flash`.
|
||||
To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board.
|
||||
It is also possible to flash the `application` image using SWD by running `rake flash_application`.
|
||||
To flash the `bootloader` using SWD run `rake swd_bootloader` (this will also erase the application).
|
||||
To flash the `application` using SWD run `rake swd_application` (or `rake swd`).
|
||||
To erase all memory and unlock read/write protection, run `rake remove_protection`.
|
||||
|
||||
debug
|
||||
-----
|
||||
|
96
Rakefile
96
Rakefile
@ -1,8 +1,8 @@
|
||||
# encoding: utf-8
|
||||
# ruby: 2.4.2
|
||||
=begin
|
||||
Rakefile to manage compile CuVoodoo STM32F1 firmware.
|
||||
the firmware is for development board based around a STM32F1xx micro-controller.
|
||||
Rakefile to manage compiling CuVoodoo STM32F4 firmware.
|
||||
the firmware is for development boards based around a STM32F4xx micro-controller.
|
||||
the firmware uses the libopencm3 library providing support for this micro-controller.
|
||||
=end
|
||||
require 'rake'
|
||||
@ -14,15 +14,27 @@ APPLICATION = "application"
|
||||
FIRMWARES = [BOOTLOADER, APPLICATION]
|
||||
|
||||
# which development board is used
|
||||
# supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL, BLACK_PILL, CORE_BOARD, STLINKV2, BLASTER, BUSVOODOO
|
||||
BOARD = ENV["BOARD"] || "BLUE_PILL"
|
||||
# supported are: WeAct MiniF4 with STM32F401
|
||||
BOARD = ENV["BOARD"] || "MINIF401"
|
||||
# get MCU from board
|
||||
DEVICE = case BOARD
|
||||
when "MINIF401"
|
||||
# the genuine MINIF401 with stm32f401cc is discontinued and has been replaced with stm32f401ce (just more RAM/flash), but knockoffs with stm32f401cc are still common
|
||||
"stm32f401cc"
|
||||
else
|
||||
raise "unknown MCU for board #{BOARD}"
|
||||
end
|
||||
|
||||
# libopencm3 definitions
|
||||
LIBOPENCM3_DIR = "libopencm3"
|
||||
LIBOPENCM3_INC = LIBOPENCM3_DIR+"/include"
|
||||
LIBOPENCM3_LIB = LIBOPENCM3_DIR+"/lib"
|
||||
# STM32F1 library used for this project provided by libopencm3
|
||||
STM32F1_LIB = "opencm3_stm32f1"
|
||||
LIBOPENCM3_LIBS = LIBOPENCM3_DIR+"/lib"
|
||||
# get libopencm3
|
||||
unless File.file?("./#{LIBOPENCM3_DIR}/scripts/genlink.py") then
|
||||
sh "git submodule init"
|
||||
sh "git submodule update"
|
||||
end
|
||||
LIBOPENCM3_LIB = "opencm3_" + `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} FAMILY`
|
||||
|
||||
# source code used by the firmware
|
||||
SRC_DIRS = [".", "lib"]
|
||||
@ -43,7 +55,7 @@ cflags = [ENV["CFLAGS"]]
|
||||
# optimize for size
|
||||
cflags << "-Os"
|
||||
# add debug symbols (remove for smaller release)
|
||||
cflags << "-ggdb"
|
||||
cflags << "-ggdb3"
|
||||
# use C99 (supported by most an sufficient)
|
||||
cflags << "-std=c99"
|
||||
# have strict warning (for better code)
|
||||
@ -59,7 +71,8 @@ cflags += SRC_DIRS.collect {|srd_dir| "-I #{srd_dir}"}
|
||||
# include libopencm3 library
|
||||
cflags << "-I #{LIBOPENCM3_INC}"
|
||||
# add defines for micro-controller and board
|
||||
cflags << "-DSTM32F1 -D#{BOARD}"
|
||||
cflags << "-D#{BOARD}"
|
||||
cflags << `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} CPPFLAGS`
|
||||
# render cflags
|
||||
cflags = cflags.compact*' '
|
||||
|
||||
@ -76,14 +89,17 @@ ldflags_linker = ["--gc-sections"]
|
||||
# show memory usage
|
||||
ldflags_linker << "--print-memory-usage"
|
||||
# add libopencm3 libraries
|
||||
library_paths = [LIBOPENCM3_LIB]
|
||||
library_paths = [LIBOPENCM3_LIBS]
|
||||
# project libraries
|
||||
ldlibs = [STM32F1_LIB]
|
||||
ldlibs = [LIBOPENCM3_LIB]
|
||||
# general libraries (gcc provides the ARM ABI)
|
||||
ldlibs_linker = ["m", "c", "nosys", "gcc"]
|
||||
|
||||
# target micro-controller information (ARM Cortex-M3 supports thumb and thumb2, but does not include a floating point unit)
|
||||
archflags = "-mthumb -mcpu=cortex-m3 -msoft-float"
|
||||
# target micro-controller information (ARM Cortex-M4 supports thumb and thumb2, but does not include a floating point unit)
|
||||
archflags = "-mthumb"
|
||||
archflags += " -mcpu=" + `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} CPU`
|
||||
archflags += " -mfloat-abi=" + `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} FPU`.split("-")[0]
|
||||
archflags += " -mfpu=" + `./#{LIBOPENCM3_DIR}/scripts/genlink.py #{LIBOPENCM3_DIR}/ld/devices.data #{DEVICE} FPU`.split("-")[1..-1]*"-"
|
||||
|
||||
desc "compile firmwares"
|
||||
task :default => FIRMWARES
|
||||
@ -101,17 +117,17 @@ end
|
||||
|
||||
# get dependencies of a file
|
||||
# done is a list of already known dependencies
|
||||
def dependencies(source, done=[])
|
||||
def dependencies(source, done = [])
|
||||
d_path = source.ext("d") # get the dependency file
|
||||
Rake::Task[d_path].invoke # ensure the dependency file exists
|
||||
d_file = IO.read(d_path) # read the dependencies from dependency file
|
||||
d_file = d_file.split(': ')[1].gsub("\n",'').gsub('\\ ','').gsub(/\s+/,' ').split(' ') # get a list of dependencies
|
||||
d_file = d_file.split(': ')[1].gsub("\n", '').gsub('\\ ', '').gsub(/\s+/, ' ').split(' ') # get a list of dependencies
|
||||
d_list = [] # list of dependencies
|
||||
# only save dependencies which are in our source directories
|
||||
d_file.each do |d|
|
||||
SRC_DIRS.each do |dir|
|
||||
if File.dirname(d)==dir then
|
||||
d_list << d
|
||||
if File.dirname(d) == dir then
|
||||
d_list << d unless d.end_with?(".inc")
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -127,14 +143,8 @@ def dependencies(source, done=[])
|
||||
return done
|
||||
end
|
||||
|
||||
desc "get libopencm3"
|
||||
file LIBOPENCM3_DIR+"/Makefile" do
|
||||
sh "git submodule init"
|
||||
sh "git submodule update"
|
||||
end
|
||||
|
||||
desc "compile libopencm3"
|
||||
file "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a" => LIBOPENCM3_DIR+"/Makefile" do
|
||||
file "#{LIBOPENCM3_LIBS}/lib#{LIBOPENCM3_LIB}.a" do
|
||||
sh "make --directory #{LIBOPENCM3_DIR}"
|
||||
end
|
||||
|
||||
@ -143,17 +153,17 @@ task :doc => ["Doxyfile", "README.md"] do |t|
|
||||
end
|
||||
|
||||
desc "compile source into object"
|
||||
rule '.o' => ['.c', proc{|f| File.file?(f.ext("h")) ? f.ext("h") : []}, proc{|f| dependencies(f).collect{|d| File.file?(d.ext("h")) ? d.ext("h") : []}}, "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a"] do |t|
|
||||
rule '.o' => ['.c', proc{|f| File.file?(f.ext("h")) ? f.ext("h") : []}, proc{|f| dependencies(f).collect{|d| File.file?(d.ext("h")) ? d.ext("h") : []}}, "#{LIBOPENCM3_LIBS}/lib#{LIBOPENCM3_LIB}.a"] do |t|
|
||||
sh "#{CC} #{cflags} #{archflags} -o #{t.name} -c #{t.prerequisites[0]}"
|
||||
end
|
||||
|
||||
desc "generate dependencies"
|
||||
rule '.d' => ['.c', "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a"] do |t|
|
||||
rule '.d' => ['.c', "#{LIBOPENCM3_LIBS}/lib#{LIBOPENCM3_LIB}.a"] do |t|
|
||||
sh "#{CC} #{cflags} #{archflags} -MM -MF #{t.name} -c #{t.prerequisites[0]}"
|
||||
end
|
||||
|
||||
desc "link binary"
|
||||
rule '.elf' => ['.o', proc{|f| dependencies(f)}, '.ld', "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a"] do |t|
|
||||
rule '.elf' => ['.o', proc{|f| dependencies(f)}, '.ld', "#{LIBOPENCM3_LIBS}/lib#{LIBOPENCM3_LIB}.a"] do |t|
|
||||
sh "#{LD} #{archflags} #{ldflags.join(' ')} #{t.prerequisites[0..-3].join(' ')} -T#{t.name.ext('ld')} #{ldflags_linker.collect{|flag| "-Wl,"+flag}.join(' ')} #{library_paths.collect{|path| "-L"+path}.join(' ')} #{ldlibs.collect{|lib| "-l"+lib}.join(' ')} -Wl,--start-group #{ldlibs_linker.collect{|lib| "-l"+lib}.join(' ')} -Wl,--end-group --output #{t.name}"
|
||||
end
|
||||
|
||||
@ -190,42 +200,50 @@ OOCD = ENV["OOCD"] || "openocd"
|
||||
# openOCD adapted name
|
||||
OOCD_INTERFACE = ENV["OOCD_INTERFACE"] || (SWD_ADAPTER=="STLINKV2" ? "stlink" : "")
|
||||
# openOCD target for the micro-controller
|
||||
OOCD_TARGET = "stm32f1x"
|
||||
OOCD_TARGET = "stm32f4x"
|
||||
# Black Magic Probe port
|
||||
BMP_PORT = ENV["BMP_PORT"] || "/dev/ttyACM0"
|
||||
# set CPUTAPID (0x1ba01477 for STM32, 0x2ba01477 for CKS32/APM32)
|
||||
CPUTAPID = ENV["CPUTAPID"] || "0x1ba01477"
|
||||
|
||||
desc "flash application"
|
||||
task :flash => :dfu_application
|
||||
|
||||
desc "flash application using USB DFU"
|
||||
task :flash => APPLICATION+".bin" do |t|
|
||||
task :dfu_application => APPLICATION+".bin" do |t|
|
||||
sh "dfu-util --device 1209:4356 --download #{t.source}"
|
||||
end
|
||||
|
||||
desc "remove STM32F1 protection using SWD"
|
||||
desc "flash application using USB DFU"
|
||||
task :dfu_bootloader => BOOTLOADER+".bin" do |t|
|
||||
sh "dfu-util --device 0483:df11 --cfg 1 --intf 0 --alt 0 --dfuse-address 0x08000000 --download #{t.source} --reset"
|
||||
end
|
||||
|
||||
desc "remove STM32F4 protection using SWD"
|
||||
task :remove_protection do
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'stm32f1x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f1x options_write 0 SWWDG NORSTSTNDBY NORSTSTOP' --command 'reset init' --command 'stm32f1x mass_erase 0' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'stm32f2x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f2x mass_erase 0' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor option erase' --eval-command='monitor erase_mass' --eval-command='kill' --eval-command='quit'"
|
||||
end
|
||||
end
|
||||
|
||||
desc "flash bootloader using SWD"
|
||||
task :flash_bootloader => BOOTLOADER+".hex" do |t|
|
||||
task :swd_bootloader => BOOTLOADER+".hex" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor erase_mass' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
|
||||
end
|
||||
end
|
||||
|
||||
task :swd => :swd_application
|
||||
|
||||
desc "flash application using SWD"
|
||||
task :flash_application => APPLICATION+".hex" do |t|
|
||||
task :swd_application => APPLICATION+".hex" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'adapter speed 100' --command 'init' --command 'halt' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --file target/#{OOCD_TARGET}.cfg --command 'adapter speed 100' --command 'init' --command 'halt' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
|
||||
end
|
||||
@ -237,7 +255,7 @@ task :debug => APPLICATION+".elf" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
# for GDB to work with openOCD the firmware needs to be reloaded
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --command \"set CPUTAPID #{CPUTAPID}\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' #{t.source}")
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' #{t.source}")
|
||||
when "BMP"
|
||||
exec("#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}")
|
||||
end
|
||||
@ -249,7 +267,7 @@ task :debug_bootloader => BOOTLOADER+".elf" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
# for GDB to work with openOCD the firmware needs to be reloaded
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --command \"set CPUTAPID #{CPUTAPID}\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset init' #{t.source}")
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset init' #{t.source}")
|
||||
when "BMP"
|
||||
exec("#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}")
|
||||
end
|
||||
|
417
application.c
417
application.c
@ -1,8 +1,8 @@
|
||||
/** STM32F1 application example
|
||||
/** STM32F4 application example
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @date 2016-2021
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
@ -24,13 +24,11 @@
|
||||
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
|
||||
#include <libopencm3/stm32/desig.h> // design utilities
|
||||
#include <libopencm3/stm32/flash.h> // flash utilities
|
||||
#include <libopencm3/usb/dwc/otg_fs.h> // USB definitions
|
||||
|
||||
/* own libraries */
|
||||
#include "global.h" // board definitions
|
||||
#include "print.h" // printing utilities
|
||||
#if !defined(STLINKV2)
|
||||
#include "uart.h" // USART utilities
|
||||
#endif
|
||||
#include "usb_cdcacm.h" // USB CDC ACM utilities
|
||||
#include "terminal.h" // handle the terminal interface
|
||||
#include "menu.h" // menu utilities
|
||||
@ -38,58 +36,42 @@
|
||||
/** watchdog period in ms */
|
||||
#define WATCHDOG_PERIOD 10000
|
||||
|
||||
/** set to 0 if the RTC is reset when the board is powered on, only indicates the uptime
|
||||
* set to 1 if VBAT can keep the RTC running when the board is unpowered, indicating the date and time
|
||||
*/
|
||||
#if defined(CORE_BOARD)
|
||||
#define RTC_DATE_TIME 1
|
||||
#else
|
||||
#define RTC_DATE_TIME 0
|
||||
#endif
|
||||
|
||||
/** number of RTC ticks per second
|
||||
* @note use integer divider of oscillator to keep second precision
|
||||
*/
|
||||
#define RTC_TICKS_SECOND 4
|
||||
|
||||
#if defined(RTC_DATE_TIME) && RTC_DATE_TIME
|
||||
/** the start time from which to RTC ticks count
|
||||
* @note this allows the 32-bit value to reach further in time, particularly when there are several ticks per second
|
||||
*/
|
||||
const time_t rtc_offset = 1577833200; // We 1. Jan 00:00:00 CET 2020
|
||||
#endif
|
||||
|
||||
/** RTC time when device is started */
|
||||
static time_t time_start = 0;
|
||||
/** wakeup frequency (i.e. least number of times per second to perform the main loop) */
|
||||
#define WAKEUP_FREQ 16
|
||||
|
||||
/** @defgroup main_flags flag set in interrupts to be processed in main task
|
||||
* @{
|
||||
*/
|
||||
static volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
|
||||
static volatile bool wakeup_flag = false; /**< flag set when wakeup timer triggered */
|
||||
static volatile bool second_flag = false; /**< flag set when a second passed */
|
||||
/** @} */
|
||||
|
||||
/** number of seconds since boot */
|
||||
static uint32_t boot_time = 0;
|
||||
|
||||
size_t putc(char c)
|
||||
{
|
||||
size_t length = 0; // number of characters printed
|
||||
static char last_c = 0; // to remember on which character we last sent
|
||||
if ('\n' == c) { // send carriage return (CR) + line feed (LF) newline for each LF
|
||||
if ('\r' != last_c) { // CR has not already been sent
|
||||
#if !defined(STLINKV2)
|
||||
uart_putchar_nonblocking('\r'); // send CR over USART
|
||||
#endif
|
||||
usb_cdcacm_putchar('\r'); // send CR over USB
|
||||
length++; // remember we printed 1 character
|
||||
}
|
||||
}
|
||||
#if !defined(STLINKV2)
|
||||
uart_putchar_nonblocking(c); // send byte over USART
|
||||
#endif
|
||||
usb_cdcacm_putchar(c); // send byte over USB
|
||||
length++; // remember we printed 1 character
|
||||
last_c = c; // remember last character
|
||||
return length; // return number of characters printed
|
||||
}
|
||||
|
||||
// only print when debug is enabled
|
||||
#if DEBUG
|
||||
#define puts_debug(x) puts(x)
|
||||
#else
|
||||
#define puts_debug(x) {}
|
||||
#endif
|
||||
|
||||
/** display available commands
|
||||
* @param[in] argument no argument required
|
||||
*/
|
||||
@ -98,29 +80,190 @@ static void command_help(void* argument);
|
||||
/** show software and hardware version
|
||||
* @param[in] argument no argument required
|
||||
*/
|
||||
static void command_version(void* argument);
|
||||
static void command_version(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
printf("firmware date: %04u-%02u-%02u\n", BUILD_YEAR, BUILD_MONTH, BUILD_DAY); // show firmware build date
|
||||
printf("device serial: %08x%08x%08x\n", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0); // show complete serial (different than the one used for USB)
|
||||
}
|
||||
|
||||
/** convert RTC date/time to number of seconds
|
||||
* @return number of seconds since 2000-01-01 00:00:00
|
||||
* @warning for simplicity I consider every month to have 31 days
|
||||
*/
|
||||
static uint32_t rtc_to_seconds(void)
|
||||
{
|
||||
rtc_wait_for_synchro(); // wait until date/time is synchronised
|
||||
const uint8_t year = ((RTC_DR >> RTC_DR_YT_SHIFT) & RTC_DR_YT_MASK) * 10 + ((RTC_DR >> RTC_DR_YU_SHIFT) & RTC_DR_YU_MASK); // get year
|
||||
uint8_t month = ((RTC_DR >> RTC_DR_MT_SHIFT) & RTC_DR_MT_MASK) * 10 + ((RTC_DR >> RTC_DR_MU_SHIFT) & RTC_DR_MU_MASK); // get month
|
||||
if (month > 0) { // month has been initialized, but starts with 1
|
||||
month--; // fix for calculation
|
||||
}
|
||||
uint8_t day = ((RTC_DR >> RTC_DR_DT_SHIFT) & RTC_DR_DT_MASK) * 10 + ((RTC_DR >> RTC_DR_DU_SHIFT) & RTC_DR_DU_MASK); // get day
|
||||
if (day > 0) { // day has been initialized, but starts with 1
|
||||
day--; // fix for calculation
|
||||
}
|
||||
const uint8_t hour = ((RTC_TR >> RTC_TR_HT_SHIFT) & RTC_TR_HT_MASK) * 10 + ((RTC_TR >> RTC_TR_HU_SHIFT) & RTC_TR_HU_MASK); // get hours
|
||||
const uint8_t minute = ((RTC_TR >> RTC_TR_MNT_SHIFT) & RTC_TR_MNT_MASK) * 10 + ((RTC_TR >> RTC_TR_MNU_SHIFT) & RTC_TR_MNU_MASK); // get minutes
|
||||
const uint8_t second = ((RTC_TR >> RTC_TR_ST_SHIFT) & RTC_TR_ST_MASK) * 10 + ((RTC_TR >> RTC_TR_SU_SHIFT) & RTC_TR_SU_MASK); // get seconds
|
||||
const uint32_t seconds = ((((((((year * 12) + month) * 31) + day) * 24) + hour) * 60) + minute) * 60 + second; // convert to number of seconds
|
||||
return seconds;
|
||||
}
|
||||
|
||||
/** show uptime
|
||||
* @param[in] argument no argument required
|
||||
*/
|
||||
static void command_uptime(void* argument);
|
||||
static void command_uptime(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
const uint32_t uptime = rtc_to_seconds() - boot_time; // get time from internal RTC
|
||||
printf("uptime: %u.%02u:%02u:%02u\n", uptime / (24 * 60 * 60), (uptime / (60 * 60)) % 24, (uptime / 60) % 60, uptime % 60);
|
||||
}
|
||||
|
||||
#if RTC_DATE_TIME
|
||||
/** show date and time
|
||||
* @param[in] argument date and time to set
|
||||
*/
|
||||
static void command_datetime(void* argument);
|
||||
#endif
|
||||
static void command_datetime(void* argument)
|
||||
{
|
||||
char* datetime = (char*)argument; // argument is optional date time
|
||||
const char* days[] = { "??", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}; // the days of the week
|
||||
|
||||
// set date
|
||||
if (datetime) { // date has been provided
|
||||
// parse date
|
||||
const char* malformed = "date and time malformed, expecting YYYY-MM-DD WD HH:MM:SS\n";
|
||||
if (strlen(datetime) != (4 + 1 + 2 + 1 + 2) + 1 + 2 + 1 + (2 + 1 + 2 + 1 + 2)) { // verify date/time is long enough
|
||||
printf(malformed);
|
||||
return;
|
||||
}
|
||||
if (!(isdigit((int8_t)datetime[0]) && isdigit((int8_t)datetime[1]) && isdigit((int8_t)datetime[2]) && isdigit((int8_t)datetime[3]) && \
|
||||
'-' == datetime[4] && \
|
||||
isdigit((int8_t)datetime[5]) && isdigit((int8_t)datetime[6]) && \
|
||||
'-' == datetime[7] && \
|
||||
isdigit((int8_t)datetime[8]) && isdigit((int8_t)datetime[9]) && \
|
||||
' ' == datetime[10] && \
|
||||
isalpha((int8_t)datetime[11]) && isalpha((int8_t)datetime[12]) && \
|
||||
' ' == datetime[13] && \
|
||||
isdigit((int8_t)datetime[14]) && isdigit((int8_t)datetime[15]) && \
|
||||
':' == datetime[16] && \
|
||||
isdigit((int8_t)datetime[17]) && isdigit((int8_t)datetime[18]) && \
|
||||
':' == datetime[19] && \
|
||||
isdigit((int8_t)datetime[20]) && isdigit((int8_t)datetime[21]))) { // verify format (good enough to not fail parsing)
|
||||
printf(malformed);
|
||||
return;
|
||||
}
|
||||
const uint16_t year = strtol(&datetime[0], NULL, 10); // parse year
|
||||
if (year <= 2000 || year > 2099) {
|
||||
puts("year out of range\n");
|
||||
return;
|
||||
}
|
||||
const uint8_t month = strtol(&datetime[5], NULL, 10); // parse month
|
||||
if (month < 1 || month > 12) {
|
||||
puts("month out of range\n");
|
||||
return;
|
||||
}
|
||||
const uint8_t day = strtol(&datetime[8], NULL, 10); // parse day
|
||||
if (day < 1 || day > 31) {
|
||||
puts("day out of range\n");
|
||||
return;
|
||||
}
|
||||
const uint8_t hour = strtol(&datetime[14], NULL, 10); // parse hour
|
||||
if (hour > 24) {
|
||||
puts("hour out of range\n");
|
||||
return;
|
||||
}
|
||||
const uint8_t minute = strtol(&datetime[17], NULL, 10); // parse minutes
|
||||
if (minute > 59) {
|
||||
puts("minute out of range\n");
|
||||
return;
|
||||
}
|
||||
const uint8_t second = strtol(&datetime[30], NULL, 10); // parse seconds
|
||||
if (second > 59) {
|
||||
puts("second out of range\n");
|
||||
return;
|
||||
}
|
||||
uint8_t week_day = 0;
|
||||
for (uint8_t i = 1; i < LENGTH(days) && 0 == week_day; i++) {
|
||||
if (days[i][0] == toupper(datetime[11]) && days[i][1] == tolower(datetime[12])) {
|
||||
week_day = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (0 == week_day) {
|
||||
puts("unknown week day\n");
|
||||
return;
|
||||
}
|
||||
uint32_t date = 0; // to build the date
|
||||
date |= (((year - 2000) / 10) & RTC_DR_YT_MASK) << RTC_DR_YT_SHIFT; // set year tenth
|
||||
date |= (((year - 2000) % 10) & RTC_DR_YU_MASK) << RTC_DR_YU_SHIFT; // set year unit
|
||||
date |= ((month / 10) & RTC_DR_MT_MASK) << RTC_DR_MT_SHIFT; // set month tenth
|
||||
date |= ((month % 10) & RTC_DR_MU_MASK) << RTC_DR_MU_SHIFT; // set month unit
|
||||
date |= ((day / 10) & RTC_DR_DT_MASK) << RTC_DR_DT_SHIFT; // set day tenth
|
||||
date |= ((day % 10) & RTC_DR_DU_MASK) << RTC_DR_DU_SHIFT; // set day unit
|
||||
date |= (week_day & RTC_DR_WDU_MASK) << RTC_DR_WDU_SHIFT; // time day of the week
|
||||
uint32_t time = 0; // to build the time
|
||||
time = 0; // reset time
|
||||
time |= ((hour / 10) & RTC_TR_HT_MASK) << RTC_TR_HT_SHIFT; // set hour tenth
|
||||
time |= ((hour % 10) & RTC_TR_HU_MASK) << RTC_TR_HU_SHIFT; // set hour unit
|
||||
time |= ((minute / 10) & RTC_TR_MNT_MASK) << RTC_TR_MNT_SHIFT; // set minute tenth
|
||||
time |= ((minute % 10) & RTC_TR_MNU_MASK) << RTC_TR_MNU_SHIFT; // set minute unit
|
||||
time |= ((second / 10) & RTC_TR_ST_MASK) << RTC_TR_ST_SHIFT; // set second tenth
|
||||
time |= ((second % 10) & RTC_TR_SU_MASK) << RTC_TR_SU_SHIFT; // set second unit
|
||||
// write date
|
||||
pwr_disable_backup_domain_write_protect(); // disable backup protection so we can set the RTC clock source
|
||||
rtc_unlock(); // enable writing RTC registers
|
||||
RTC_ISR |= RTC_ISR_INIT; // enter initialisation mode
|
||||
while (!(RTC_ISR & RTC_ISR_INITF)); // wait to enter initialisation mode
|
||||
RTC_DR = date; // set date
|
||||
RTC_TR = time; // set time
|
||||
RTC_ISR &= ~RTC_ISR_INIT; // exit initialisation mode
|
||||
rtc_lock(); // protect RTC register against writing
|
||||
pwr_enable_backup_domain_write_protect(); // re-enable protection now that we configured the RTC clock
|
||||
}
|
||||
|
||||
// show date
|
||||
if (!(RTC_ISR & RTC_ISR_INITS)) { // date has not been set yet
|
||||
puts("date/time not initialized\n");
|
||||
} else {
|
||||
rtc_wait_for_synchro(); // wait until date/time is synchronised
|
||||
const uint8_t year = ((RTC_DR >> RTC_DR_YT_SHIFT) & RTC_DR_YT_MASK) * 10 + ((RTC_DR >> RTC_DR_YU_SHIFT) & RTC_DR_YU_MASK); // get year
|
||||
const uint8_t month = ((RTC_DR >> RTC_DR_MT_SHIFT) & RTC_DR_MT_MASK) * 10 + ((RTC_DR >> RTC_DR_MU_SHIFT) & RTC_DR_MU_MASK); // get month
|
||||
const uint8_t day = ((RTC_DR >> RTC_DR_DT_SHIFT) & RTC_DR_DT_MASK) * 10 + ((RTC_DR >> RTC_DR_DU_SHIFT) & RTC_DR_DU_MASK); // get day
|
||||
const uint8_t week_day = ((RTC_DR >> RTC_DR_WDU_SHIFT) & RTC_DR_WDU_MASK); // get week day
|
||||
const uint8_t hour = ((RTC_TR >> RTC_TR_HT_SHIFT) & RTC_TR_HT_MASK) * 10 + ((RTC_TR >> RTC_TR_HU_SHIFT) & RTC_TR_HU_MASK); // get hours
|
||||
const uint8_t minute = ((RTC_TR >> RTC_TR_MNT_SHIFT) & RTC_TR_MNT_MASK) * 10 + ((RTC_TR >> RTC_TR_MNU_SHIFT) & RTC_TR_MNU_MASK); // get minutes
|
||||
const uint8_t second = ((RTC_TR >> RTC_TR_ST_SHIFT) & RTC_TR_ST_MASK) * 10 + ((RTC_TR >> RTC_TR_SU_SHIFT) & RTC_TR_SU_MASK); // get seconds
|
||||
printf("date: 20%02d-%02d-%02d %s %02d:%02d:%02d\n", year, month, day, days[week_day], hour, minute, second);
|
||||
}
|
||||
}
|
||||
|
||||
/** reset board
|
||||
* @param[in] argument no argument required
|
||||
*/
|
||||
static void command_reset(void* argument);
|
||||
static void command_reset(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
scb_reset_system(); // reset device
|
||||
while (true); // wait for the reset to happen
|
||||
}
|
||||
|
||||
/** switch to system memory (e.g. embedded bootloader)
|
||||
* @param[in] argument no argument required
|
||||
*/
|
||||
static void command_system(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
system_memory(); // jump to system memory
|
||||
}
|
||||
|
||||
/** switch to DFU bootloader
|
||||
* @param[in] argument no argument required
|
||||
*/
|
||||
static void command_bootloader(void* argument);
|
||||
static void command_bootloader(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
dfu_bootloader(); // start DFU bootloader
|
||||
}
|
||||
|
||||
/** list of all supported commands */
|
||||
static const struct menu_command_t menu_commands[] = {
|
||||
@ -148,7 +291,6 @@ static const struct menu_command_t menu_commands[] = {
|
||||
.argument_description = NULL,
|
||||
.command_handler = &command_uptime,
|
||||
},
|
||||
#if RTC_DATE_TIME
|
||||
{
|
||||
.shortcut = 'd',
|
||||
.name = "date",
|
||||
@ -157,7 +299,6 @@ static const struct menu_command_t menu_commands[] = {
|
||||
.argument_description = "[YYYY-MM-DD HH:MM:SS]",
|
||||
.command_handler = &command_datetime,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.shortcut = 'r',
|
||||
.name = "reset",
|
||||
@ -166,6 +307,14 @@ static const struct menu_command_t menu_commands[] = {
|
||||
.argument_description = NULL,
|
||||
.command_handler = &command_reset,
|
||||
},
|
||||
{
|
||||
.shortcut = 's',
|
||||
.name = "system",
|
||||
.command_description = "reboot into system memory",
|
||||
.argument = MENU_ARGUMENT_NONE,
|
||||
.argument_description = NULL,
|
||||
.command_handler = &command_system,
|
||||
},
|
||||
{
|
||||
.shortcut = 'b',
|
||||
.name = "bootloader",
|
||||
@ -183,81 +332,13 @@ static void command_help(void* argument)
|
||||
menu_print_commands(menu_commands, LENGTH(menu_commands)); // print global commands
|
||||
}
|
||||
|
||||
static void command_version(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
printf("firmware date: %04u-%02u-%02u\n", BUILD_YEAR, BUILD_MONTH, BUILD_DAY); // show firmware build date
|
||||
printf("device serial: %08x%08x%04x%04x\n", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0 & 0xffff, DESIG_UNIQUE_ID0 >> 16); // not that the half-works are reversed in the first word
|
||||
}
|
||||
|
||||
static void command_uptime(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
const uint32_t uptime = (rtc_get_counter_val() - time_start) / RTC_TICKS_SECOND; // get time from internal RTC
|
||||
printf("uptime: %u.%02u:%02u:%02u\n", uptime / (24 * 60 * 60), (uptime / (60 * 60)) % 24, (uptime / 60) % 60, uptime % 60);
|
||||
}
|
||||
|
||||
#if RTC_DATE_TIME
|
||||
static void command_datetime(void* argument)
|
||||
{
|
||||
char* datetime = (char*)argument; // argument is optional date time
|
||||
if (NULL == argument) { // no date and time provided, just show the current day and time
|
||||
const time_t time_rtc = rtc_get_counter_val() / RTC_TICKS_SECOND + rtc_offset; // get time from internal RTC
|
||||
const struct tm* time_tm = localtime(&time_rtc); // convert time
|
||||
const char* days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}; // the days of the week
|
||||
printf("date: %s %d-%02d-%02d %02d:%02d:%02d\n", days[time_tm->tm_wday], 1900 + time_tm->tm_year, 1 + time_tm->tm_mon, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
||||
} else { // date and time provided, set it
|
||||
const char* malformed = "date and time malformed, expecting YYYY-MM-DD HH:MM:SS\n";
|
||||
struct tm time_tm; // to store the parsed date time
|
||||
if (strlen(datetime) != (4 + 1 + 2 + 1 + 2) + 1 + (2 + 1 + 2 + 1 + 2)) { // verify date/time is long enough
|
||||
printf(malformed);
|
||||
return;
|
||||
}
|
||||
if (!(isdigit((int8_t)datetime[0]) && isdigit((int8_t)datetime[1]) && isdigit((int8_t)datetime[2]) && isdigit((int8_t)datetime[3]) && '-' == datetime[4] && isdigit((int8_t)datetime[5]) && isdigit((int8_t)datetime[6]) && '-' == datetime[7] && isdigit((int8_t)datetime[8]) && isdigit((int8_t)datetime[9]) && ' ' == datetime[10] && isdigit((int8_t)datetime[11]) && isdigit((int8_t)datetime[12]) && ':' == datetime[13] && isdigit((int8_t)datetime[14]) && isdigit((int8_t)datetime[15]) && ':' == datetime[16] && isdigit((int8_t)datetime[17]) && isdigit((int8_t)datetime[18]))) { // verify format (good enough to not fail parsing)
|
||||
printf(malformed);
|
||||
return;
|
||||
}
|
||||
time_tm.tm_year = strtol(&datetime[0], NULL, 10) - 1900; // parse year
|
||||
time_tm.tm_mon = strtol(&datetime[5], NULL, 10) - 1; // parse month
|
||||
time_tm.tm_mday = strtol(&datetime[8], NULL, 10); // parse day
|
||||
time_tm.tm_hour = strtol(&datetime[11], NULL, 10); // parse hour
|
||||
time_tm.tm_min = strtol(&datetime[14], NULL, 10); // parse minutes
|
||||
time_tm.tm_sec = strtol(&datetime[17], NULL, 10); // parse seconds
|
||||
time_t time_rtc = mktime(&time_tm); // get back seconds
|
||||
time_rtc -= rtc_offset; // remove start offset
|
||||
time_start = time_rtc * RTC_TICKS_SECOND + (rtc_get_counter_val() - time_start); // update uptime with current date
|
||||
rtc_set_counter_val(time_rtc * RTC_TICKS_SECOND); // save date/time to internal RTC
|
||||
printf("date and time saved: %d-%02d-%02d %02d:%02d:%02d\n", 1900 + time_tm.tm_year, 1 + time_tm.tm_mon, time_tm.tm_mday, time_tm.tm_hour, time_tm.tm_min, time_tm.tm_sec);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void command_reset(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
scb_reset_system(); // reset device
|
||||
while (true); // wait for the reset to happen
|
||||
}
|
||||
|
||||
static void command_bootloader(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
// set DFU magic to specific RAM location
|
||||
__dfu_magic[0] = 'D';
|
||||
__dfu_magic[1] = 'F';
|
||||
__dfu_magic[2] = 'U';
|
||||
__dfu_magic[3] = '!';
|
||||
scb_reset_system(); // reset system (core and peripherals)
|
||||
while (true); // wait for the reset to happen
|
||||
}
|
||||
|
||||
/** process user command
|
||||
* @param[in] str user command string (\0 ended)
|
||||
*/
|
||||
static void process_command(char* str)
|
||||
{
|
||||
// ensure actions are available
|
||||
if (NULL == menu_commands || 0 == LENGTH(menu_commands)) {
|
||||
if (0 == LENGTH(menu_commands)) {
|
||||
return;
|
||||
}
|
||||
// don't handle empty lines
|
||||
@ -279,7 +360,6 @@ static void process_command(char* str)
|
||||
void main(void);
|
||||
void main(void)
|
||||
{
|
||||
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
|
||||
|
||||
#if DEBUG
|
||||
// enable functionalities for easier debug
|
||||
@ -295,62 +375,77 @@ void main(void)
|
||||
#endif
|
||||
|
||||
board_setup(); // setup board
|
||||
#if !defined(STLINKV2)
|
||||
uart_setup(); // setup USART (for printing)
|
||||
#endif
|
||||
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
|
||||
puts("\nwelcome to the CuVoodoo STM32F1 example application\n"); // print welcome message
|
||||
OTG_FS_GCCFG |= OTG_GCCFG_NOVBUSSENS | OTG_GCCFG_PWRDWN; // disable VBUS sensing
|
||||
OTG_FS_GCCFG &= ~(OTG_GCCFG_VBUSBSEN | OTG_GCCFG_VBUSASEN); // force USB device mode
|
||||
puts("\nwelcome to the CuVoodoo STM32F4 example firmware\n"); // print welcome message
|
||||
|
||||
#if DEBUG
|
||||
// show reset cause
|
||||
if (RCC_CSR & (RCC_CSR_LPWRRSTF | RCC_CSR_WWDGRSTF | RCC_CSR_IWDGRSTF | RCC_CSR_SFTRSTF | RCC_CSR_PORRSTF | RCC_CSR_PINRSTF)) {
|
||||
puts("reset cause(s):");
|
||||
puts_debug("reset cause(s):");
|
||||
if (RCC_CSR & RCC_CSR_LPWRRSTF) {
|
||||
puts(" low-power");
|
||||
puts_debug(" low-power");
|
||||
}
|
||||
if (RCC_CSR & RCC_CSR_WWDGRSTF) {
|
||||
puts(" window-watchdog");
|
||||
puts_debug(" window-watchdog");
|
||||
}
|
||||
if (RCC_CSR & RCC_CSR_IWDGRSTF) {
|
||||
puts(" independent-watchdog");
|
||||
puts_debug(" independent-watchdog");
|
||||
}
|
||||
if (RCC_CSR & RCC_CSR_SFTRSTF) {
|
||||
puts(" software");
|
||||
puts_debug(" software");
|
||||
}
|
||||
if (RCC_CSR & RCC_CSR_PORRSTF) {
|
||||
puts(" POR/PDR");
|
||||
puts_debug(" POR/PDR");
|
||||
}
|
||||
if (RCC_CSR & RCC_CSR_PINRSTF) {
|
||||
puts(" pin");
|
||||
puts_debug(" pin");
|
||||
}
|
||||
putc('\n');
|
||||
puts_debug("\n");
|
||||
RCC_CSR |= RCC_CSR_RMVF; // clear reset flags
|
||||
}
|
||||
#endif
|
||||
#if !(DEBUG)
|
||||
// show watchdog information
|
||||
printf("setup watchdog: %.2fs", WATCHDOG_PERIOD / 1000.0);
|
||||
if (FLASH_OBR & FLASH_OBR_OPTERR) {
|
||||
puts(" (option bytes not set in flash: software wachtdog used, not automatically started at reset)\n");
|
||||
} else if (FLASH_OBR & FLASH_OBR_WDG_SW) {
|
||||
puts(" (software watchdog used, not automatically started at reset)\n");
|
||||
} else {
|
||||
puts(" (hardware watchdog used, automatically started at reset)\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// setup RTC
|
||||
puts("setup internal RTC: ");
|
||||
#if defined(BLUE_PILL) || defined(STLINKV2) || defined(BLASTER) // for boards without a Low Speed External oscillator
|
||||
// note: the blue pill LSE oscillator is affected when toggling the onboard LED, thus prefer the HSE
|
||||
rtc_auto_awake(RCC_HSE, 8000000 / 128 / RTC_TICKS_SECOND - 1); // use High Speed External oscillator (8 MHz / 128) as RTC clock (VBAT can't be used to keep the RTC running)
|
||||
#else // for boards with an precise Low Speed External oscillator
|
||||
rtc_auto_awake(RCC_LSE, 32768 / RTC_TICKS_SECOND - 1); // ensure internal RTC is on, uses the 32.678 kHz LSE, and the prescale is set to our tick speed, else update backup registers accordingly (power off the micro-controller for the change to take effect)
|
||||
puts_debug("setup RTC: ");
|
||||
rcc_periph_clock_enable(RCC_RTC); // enable clock for RTC peripheral
|
||||
if (!(RCC_BDCR && RCC_BDCR_RTCEN)) { // the RTC has not been configured yet
|
||||
pwr_disable_backup_domain_write_protect(); // disable backup protection so we can set the RTC clock source
|
||||
rtc_unlock(); // enable writing RTC registers
|
||||
#if defined(MINIF401)
|
||||
rcc_osc_on(RCC_LSE); // enable LSE clock
|
||||
while (!rcc_is_osc_ready(RCC_LSE)); // wait until clock is ready
|
||||