Compare commits
79 Commits
error-mem-
...
43abca4c51
Author | SHA1 | Date | |
---|---|---|---|
43abca4c51 | |||
00c796c58f | |||
4d0f963585 | |||
708fdea058 | |||
ebb1383957 | |||
3345004213 | |||
afadd539c8 | |||
71315b7c92 | |||
410a5d4dd1 | |||
528db7a581 | |||
384e127085 | |||
fe0bde5c32 | |||
2beaccbe32 | |||
b6760ff426 | |||
1b2dac21f2 | |||
97f154d3b9 | |||
ee5dda4a33 | |||
afb8e93b13 | |||
9bd0dd194b | |||
6322c3728b | |||
174bf4220e | |||
566436201e | |||
61e3b58992 | |||
01b445a0fb | |||
5437a323c3 | |||
28e42d3306 | |||
08606689b4 | |||
5776feee85 | |||
6273c68821 | |||
3381840bba | |||
cf35ba735f | |||
3f31acfada | |||
77251cc1bc | |||
f2972903d5 | |||
31b17dfd8d | |||
9f1a791be2 | |||
54416a6350 | |||
9c94428144 | |||
81155887de | |||
8309cef5ec | |||
1a76a69b6d | |||
e50e3f0ace | |||
5fb1612773 | |||
72735915ee | |||
08ec458e8f | |||
d962110823 | |||
bfdc3d3246 | |||
dca839ce2e | |||
eea0826c7b | |||
6e5627fde2 | |||
0f239dc39d | |||
08eee66d30 | |||
7c9d296e34 | |||
533656ca28 | |||
d146b10569 | |||
8576bf4231 | |||
6ebd74cb31 | |||
46125ba752 | |||
6c9f90c986 | |||
74defd5384 | |||
5deac33949 | |||
666353e3b7 | |||
df40ab1be7 | |||
4c3574c2e2 | |||
289f49204d | |||
4bc98e6baf | |||
e50ce0d541 | |||
78b63c853f | |||
a25b249d77 | |||
e815442617 | |||
60104df30e | |||
5ab911b4b6 | |||
08427cc589 | |||
ba6e0880b4 | |||
6a71416d2a | |||
1ecd5edd93 | |||
1b4eba1871 | |||
e3e4a6d926 | |||
93ff4959a2 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,3 +10,6 @@
|
||||
path = stm-firmware/base64-lib
|
||||
url = https://git.shimatta.de/mhu/base64-lib.git
|
||||
branch = master
|
||||
[submodule "stm-firmware/linklist-lib"]
|
||||
path = stm-firmware/linklist-lib
|
||||
url = https://git.shimatta.de/mhu/linklist-lib.git
|
||||
|
BIN
doc/source/hardware/hw_watchdog_delay.png
Normal file
BIN
doc/source/hardware/hw_watchdog_delay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
doc/source/hardware/hw_watchdog_startup.png
Normal file
BIN
doc/source/hardware/hw_watchdog_startup.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
15
doc/source/reflow-profiles/reflow-245.tpr
Normal file
15
doc/source/reflow-profiles/reflow-245.tpr
Normal file
@@ -0,0 +1,15 @@
|
||||
# This is a comment
|
||||
pid_conf 10 0.3 5 60 2.5 0.5
|
||||
temp_set 50
|
||||
wait_temp 45
|
||||
temp_set 45
|
||||
temp_ramp 150 120
|
||||
temp_ramp 190 90
|
||||
pid_conf 9 0.5 6 60 2.5 0.5
|
||||
temp_set 250
|
||||
wait_temp 245
|
||||
temp_off
|
||||
beep 1
|
||||
wait_time 1
|
||||
beep 0
|
||||
|
1
stm-firmware/.gitignore
vendored
1
stm-firmware/.gitignore
vendored
@@ -15,3 +15,4 @@ reflow-controller.includes
|
||||
*.config
|
||||
*.files
|
||||
*.user.*
|
||||
*.user
|
||||
|
109
stm-firmware/CMakeLists.txt
Normal file
109
stm-firmware/CMakeLists.txt
Normal file
@@ -0,0 +1,109 @@
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
set(CMAKE_CROSSCOMPILING 1)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE "arm-none-eabi-gcc.cmake")
|
||||
|
||||
project(reflow-controller)
|
||||
|
||||
if(NOT WIN32)
|
||||
string(ASCII 27 Esc)
|
||||
set(ColorReset "${Esc}[m")
|
||||
set(ColorBold "${Esc}[1m")
|
||||
set(Red "${Esc}[31m")
|
||||
set(Green "${Esc}[32m")
|
||||
set(Yellow "${Esc}[33m")
|
||||
set(Blue "${Esc}[34m")
|
||||
set(Magenta "${Esc}[35m")
|
||||
set(Cyan "${Esc}[36m")
|
||||
set(White "${Esc}[37m")
|
||||
set(BoldRed "${Esc}[1;31m")
|
||||
set(BoldGreen "${Esc}[1;32m")
|
||||
set(BoldYellow "${Esc}[1;33m")
|
||||
set(BoldBlue "${Esc}[1;34m")
|
||||
set(BoldMagenta "${Esc}[1;35m")
|
||||
set(BoldCyan "${Esc}[1;36m")
|
||||
set(BoldWhite "${Esc}[1;37m")
|
||||
endif()
|
||||
|
||||
find_package(Git)
|
||||
if (GIT_FOUND)
|
||||
message("Git found")
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --always --tags --dirty
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
message("${BoldGreen}Git based version number: ${GIT_DESCRIBE}${ColorReset}")
|
||||
else (GIT_FOUND)
|
||||
set(GIT_DESCRIBE "v0.0.0-unknown")
|
||||
message("${BoldRed}No git installation found. It is highly recommended using git to generate the version number")
|
||||
message("Version is set to: ${GIT_DESCRIBE}${ColorReset}")
|
||||
endif (GIT_FOUND)
|
||||
|
||||
set(ELFFILE ${PROJECT_NAME}.elf)
|
||||
set(HEXFILE ${PROJECT_NAME}.hex)
|
||||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/stm32f407vet6_flash.ld)
|
||||
|
||||
add_compile_options(-Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter)
|
||||
add_compile_options(-mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles -Wimplicit-fallthrough=3 -Wsign-compare)
|
||||
|
||||
|
||||
set(GIT_DESCRIBE "${GIT_DESCRIBE}")
|
||||
add_definitions(-DBASE64_LOOKUP_TABLE_SECTION=\".ccm.bss\" -DSHELLMATTA_HELP_ALIAS=\"?\" -DGIT_VER=${GIT_DESCRIBE} -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4)
|
||||
|
||||
add_subdirectory(updater/ram-code)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
|
||||
IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DDEBUGBUILD)
|
||||
add_compile_options(-O0 -g)
|
||||
ELSE()
|
||||
add_compile_options(-O3 -g)
|
||||
add_link_options(-Wl,--gc-sections)
|
||||
ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
|
||||
if (UART_ON_DEBUG_HEADER)
|
||||
add_definitions(-DUART_ON_DEBUG_HEADER)
|
||||
message("${BoldRed}UART forced to debug header${ColorReset}")
|
||||
endif (UART_ON_DEBUG_HEADER)
|
||||
|
||||
add_subdirectory(base64-lib)
|
||||
add_subdirectory(linklist-lib)
|
||||
|
||||
aux_source_directory("." MAIN_SOURCES)
|
||||
aux_source_directory("config-parser" CFG_PARSER_SRCS)
|
||||
aux_source_directory("ui" UI_SRCS)
|
||||
aux_source_directory("fatfs" FAT_SRCS)
|
||||
aux_source_directory("fatfs/shimatta_sdio_driver" SDIO_SRCS)
|
||||
aux_source_directory("boot" BOOT_SRCS)
|
||||
aux_source_directory("setup" SETUP_SRCS)
|
||||
aux_source_directory("stm-periph" STM_PERIPH_SRCS)
|
||||
aux_source_directory("settings" SETTINGS_SRCS)
|
||||
aux_source_directory("safety" SAFETY_SRCS)
|
||||
aux_source_directory("shellmatta/src" SHELLMATTA_SRCS)
|
||||
aux_source_directory("updater" UPDATER_SRCS)
|
||||
|
||||
add_executable(${ELFFILE} ${MAIN_SOURCES} ${CFG_PARSER_SRCS} ${UI_SRCS}
|
||||
${FAT_SRCS} ${SDIO_SRCS} ${BOOT_SRCS} ${SETUP_SRCS}
|
||||
${STM_PERIPH_SRCS} ${SETTINGS_SRCS} ${SAFETY_SRCS} ${SHELLMATTA_SRCS} ${UPDATER_SRCS}
|
||||
)
|
||||
|
||||
add_dependencies(${ELFFILE} updater-ram-code-header-blob)
|
||||
|
||||
target_include_directories(${ELFFILE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/shellmatta/api ${CMAKE_CURRENT_SOURCE_DIR}/config-parser/include)
|
||||
target_link_options(${ELFFILE} PRIVATE -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles -T${LINKER_SCRIPT} -Wl,--print-memory-usage)
|
||||
target_link_libraries(${ELFFILE} base64-lib linklist-lib)
|
||||
target_include_directories(${ELFFILE} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/updater/ram-code/include/")
|
||||
|
||||
set(HEX_PATH "${CMAKE_CURRENT_BINARY_DIR}/${HEXFILE}")
|
||||
add_custom_target(update-image ALL DEPENDS ${HEX_PATH})
|
||||
add_custom_command(
|
||||
DEPENDS ${ELFFILE}
|
||||
OUTPUT ${HEX_PATH}
|
||||
COMMAND ${CMAKE_OBJCOPY} -O ihex ${ELFFILE} ${HEX_PATH})
|
@@ -1,224 +0,0 @@
|
||||
################################Shimatta Makefile####################################
|
||||
#CPU: STM32F407VET6
|
||||
#Compiler: arm-none-eabi
|
||||
#####################################################################################
|
||||
#Add Files and Folders below#########################################################
|
||||
CFILES = main.c syscalls.c setup/system_stm32f4xx.c systick.c boot/startup_stm32f407vx.c
|
||||
ASFILES =
|
||||
INCLUDEPATH = -Iinclude
|
||||
|
||||
OBJDIR_BASE = obj
|
||||
TARGET_BASE = reflow-controller
|
||||
LIBRARYPATH = -L.
|
||||
LIBRARIES =
|
||||
|
||||
DEFINES = -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4 -DHSE_VALUE=8000000UL
|
||||
MAPFILE_BASE = memory-mapping
|
||||
LINKER_SCRIPT=stm32f407vet6_flash.ld
|
||||
|
||||
export GIT_VER = $(shell git describe --always --dirty --tags)
|
||||
DEFINES += -DGIT_VER=$(GIT_VER)
|
||||
|
||||
ifneq ($(VERBOSE),true)
|
||||
QUIET=@
|
||||
else
|
||||
QUIET=
|
||||
endif
|
||||
|
||||
##Custom Files###
|
||||
CFILES += adc-meas.c
|
||||
|
||||
# Shellmatta
|
||||
CFILES += shellmatta/src/shellmatta.c shellmatta/src/shellmatta_autocomplete.c shellmatta/src/shellmatta_escape.c shellmatta/src/shellmatta_history.c shellmatta/src/shellmatta_utils.c shellmatta/src/shellmatta_opt.c shell.c
|
||||
INCLUDEPATH += -Ishellmatta/api
|
||||
DEFINES += -DSHELLMATTA_HELP_ALIAS=\"?\"
|
||||
|
||||
# RCC Manager
|
||||
CFILES += stm-periph/rcc-manager.c
|
||||
CFILES += stm-periph/uart.c stm-periph/dma-ring-buffer.c stm-periph/backup-ram.c
|
||||
CFILES += stm-periph/rng.c stm-periph/spi.c
|
||||
CFILES += digio.c
|
||||
CFILES += stm-periph/unique-id.c
|
||||
CFILES += calibration.c
|
||||
CFILES += temp-converter.c
|
||||
CFILES += rotary-encoder.c button.c
|
||||
CFILES += ui/lcd.c ui/menu.c ui/gui.c
|
||||
CFILES += fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c fatfs/shimatta_sdio_driver/shimatta_sdio.c
|
||||
CFILES += pid-controller.c oven-driver.c
|
||||
CFILES += settings/settings.c settings/settings-sd-card.c settings/spi-eeprom.c settings/settings-eeprom.c
|
||||
CFILES += stm-periph/crc-unit.c
|
||||
CFILES += safety/safety-adc.c safety/safety-controller.c safety/watchdog.c safety/fault.c safety/safety-memory.c safety/stack-check.c
|
||||
CFILES += hw-version-detect.c
|
||||
CFILES += config-parser/config-parser.c
|
||||
CFILES += updater/updater.c
|
||||
INCLUDEPATH += -Iconfig-parser/include
|
||||
|
||||
CFILES += base64-lib/src/base64-lib.c
|
||||
INCLUDEPATH += -Ibase64-lib/include
|
||||
DEFINES += -DBASE64_LOOKUP_TABLE_SECTION="\".ccm.bss\""
|
||||
|
||||
|
||||
DEBUG_DEFINES = -DDEBUGBUILD
|
||||
RELEASE_DEFINES = -DUART_ON_DEBUG_HEADER
|
||||
|
||||
###################################################################################
|
||||
ifeq ($(CROSS_COMPILE),)
|
||||
CROSS_COMPILE=arm-none-eabi-
|
||||
endif
|
||||
|
||||
CC=$(CROSS_COMPILE)gcc
|
||||
OBJCOPY=$(CROSS_COMPILE)objcopy
|
||||
OBJDUMP=$(CROSS_COMPILE)objdump
|
||||
SIZE=$(CROSS_COMPILE)size
|
||||
|
||||
CFLAGS_RELEASE = -O3 -g
|
||||
CFLAGS_DEBUG = -O0 -g
|
||||
|
||||
LFLAGS_RELEASE = -Wl,--gc-sections
|
||||
LFLAGS_DEBUG =
|
||||
|
||||
CFLAGS =
|
||||
LFLAGS =
|
||||
|
||||
ifneq ($(DEBUGBUILD),true)
|
||||
DEFINES += $(RELEASE_DEFINES)
|
||||
CFLAGS += $(CFLAGS_RELEASE)
|
||||
LFLAGS += $(LFLAGS_RELEASE)
|
||||
target = $(TARGET_BASE)-release
|
||||
OBJDIR = $(OBJDIR_BASE)/release
|
||||
MAPFILE = $(MAPFILE_BASE)-release
|
||||
else
|
||||
DEFINES += $(DEBUG_DEFINES)
|
||||
target = $(TARGET_BASE)-debug
|
||||
CFLAGS += $(CFLAGS_DEBUG)
|
||||
LFLAGS += $(LFLAGS_DEBUG)
|
||||
OBJDIR = $(OBJDIR_BASE)/debug
|
||||
MAPFILE = $(MAPFILE_BASE)-debug
|
||||
endif
|
||||
|
||||
LFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
|
||||
LFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles
|
||||
LFLAGS += -T$(LINKER_SCRIPT) -Wl,-Map=$(MAPFILE).map -Wl,--print-memory-usage
|
||||
|
||||
CFLAGS += -c -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
|
||||
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles
|
||||
CFLAGS += -Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter -Wimplicit-fallthrough=3 -Wsign-compare
|
||||
|
||||
####################################################################################
|
||||
|
||||
OBJ = $(CFILES:%.c=$(OBJDIR)/%.c.o)
|
||||
ASOBJ += $(ASFILES:%.S=$(OBJDIR)/%.S.o)
|
||||
|
||||
default: $(target).elf
|
||||
|
||||
all: debug release
|
||||
|
||||
release:
|
||||
$(QUIET)$(MAKE) DEBUGBUILD=false
|
||||
|
||||
debug:
|
||||
$(QUIET)$(MAKE) DEBUGBUILD=true
|
||||
|
||||
%.bin: %.elf
|
||||
$(QUIET)$(OBJCOPY) -O binary $^ $@
|
||||
%.hex: %.elf
|
||||
$(QUIET)$(OBJCOPY) -O ihex $^ $@
|
||||
|
||||
|
||||
#Linking
|
||||
$(target).elf: $(OBJ) $(ASOBJ) $(LINKER_SCRIPT)
|
||||
@echo [LD] $@
|
||||
$(QUIET)$(CC) $(LFLAGS) $(LIBRARYPATH) -o $@ $(OBJ) $(ASOBJ) $(LIBRARIES)
|
||||
$(QUIET)$(SIZE) $@
|
||||
@echo "Built Version $(GIT_VER)"
|
||||
|
||||
$(OBJDIR)/updater/updater.c.o: updater/ram-code/updater-ram-code.bin.h
|
||||
|
||||
#Compiling
|
||||
$(OBJ):
|
||||
@echo [CC] $@
|
||||
$(eval OUTPATH=$(dir $@))
|
||||
@mkdir -p $(OUTPATH)
|
||||
$(QUIET)$(CC) $(CFLAGS) -MMD -MT $@ $(INCLUDEPATH) $(DEFINES) -o $@ $(@:$(OBJDIR)/%.c.o=%.c)
|
||||
$(ASOBJ):
|
||||
@echo [AS] $@
|
||||
$(eval OUTPATH=$(dir $@))
|
||||
@mkdir -p $(OUTPATH)
|
||||
$(QUIET)$(CC) $(CFLAGS) -MMD -MT $@ $(INCLUDEPATH) $(DEFINES) -o $@ $(@:$(OBJDIR)/%.S.o=%.S)
|
||||
|
||||
|
||||
.PHONY: qtproject-legacy qtproject qtproject-debug clean mrproper objcopy disassemble program program-debug updater/ram-code/updater-ram-code.bin.h
|
||||
|
||||
updater/ram-code/updater-ram-code.bin.h:
|
||||
$(QUIET)$(MAKE) -C updater/ram-code updater-ram-code.bin.h
|
||||
|
||||
program-debug:
|
||||
$(QUIET)$(MAKE) DEBUGBUILD=true program
|
||||
|
||||
program: $(target).elf
|
||||
./program-device.sh $<
|
||||
|
||||
disassemble: $(target).elf
|
||||
$(QUIET)$(OBJDUMP) -D -s $< > $(target).lss
|
||||
|
||||
objcopy: $(target).bin $(target).hex
|
||||
|
||||
mrproper: clean
|
||||
ifneq ($(DEBUGBUILD),true)
|
||||
@echo "Purging RELEASE project files"
|
||||
else
|
||||
@echo "Purging DEBUG project files"
|
||||
endif
|
||||
$(QUIET)rm -f $(target).pro $(target).creator $(target).files $(target).cflags $(target).cxxflags $(target).includes $(target).config
|
||||
ifneq ($(DEBUGBUILD),true)
|
||||
$(QUIET)$(MAKE) DEBUGBUILD=true mrproper
|
||||
endif
|
||||
|
||||
|
||||
clean:
|
||||
@echo -n "Cleaning up derived files for "
|
||||
ifneq ($(DEBUGBUILD),true)
|
||||
@echo "RELEASE build"
|
||||
else
|
||||
@echo "DEBUG build"
|
||||
endif
|
||||
$(QUIET)rm -f $(target).elf $(target).bin $(target).hex $(OBJ) $(ASOBJ) $(mapfile).map $(CFILES:%.c=$(OBJDIR)/%.c.d) $(ASFILES:%.S=$(OBJDIR)/%.S.d)
|
||||
$(QUIET)rm -rf $(OBJDIR)/*
|
||||
$(MAKE) -C updater/ram-code clean
|
||||
ifneq ($(DEBUGBUILD),true)
|
||||
$(QUIET)$(MAKE) DEBUGBUILD=true clean
|
||||
endif
|
||||
|
||||
qtproject-legacy:
|
||||
echo -e "TEMPLATE = app\nCONFIG -= console app_bundle qt" > $(target).pro
|
||||
echo -e "SOURCES += $(CFILES) $(ASFILES)" >> $(target).pro
|
||||
echo -ne "INCLUDEPATH += " >> $(target).pro
|
||||
echo "$(INCLUDEPATH)" | sed "s!-I!./!g" >> $(target).pro
|
||||
echo -ne "HEADERS += " >> $(target).pro
|
||||
find -name "*.h" | tr "\\n" " " >> $(target).pro
|
||||
echo -ne "\nDEFINES += " >> $(target).pro
|
||||
echo "$(DEFINES)" | sed "s/-D//g" >> $(target).pro
|
||||
|
||||
qtproject-debug:
|
||||
@echo "Generating debug build project"
|
||||
$(QUIET)$(MAKE) DEBUGBUILD=true qtproject
|
||||
|
||||
qtproject:
|
||||
$(QUIET)rm -f $(target).files $(target).cflags $(target).config $(target).creator $(target).includes $(target).creator.user
|
||||
@echo "Generating source file list"
|
||||
$(QUIET)echo "$(CFILES)" | tr ' ' '\n' > $(target).files
|
||||
@echo -n "Appending found header files from folders "
|
||||
@echo `echo $(INCLUDEPATH) | sed "s/-I//g"`
|
||||
$(QUIET)for dir in `echo $(INCLUDEPATH) | sed "s/-I//g"`; do \
|
||||
find `echo "$${dir}"` -name "*.h" >> $(target).files; \
|
||||
done
|
||||
@echo "Generating $(target).cflags"
|
||||
$(QUIET)echo "" > $(target).cflags
|
||||
@echo "Generating $(target).includes"
|
||||
$(QUIET)echo $(INCLUDEPATH) | sed "s/-I/,/g" | tr , '\n' | sed "/^$$/d" > $(target).includes;
|
||||
@echo "Generating $(target).config"
|
||||
$(QUIET)echo $(DEFINES) | sed "s/-D/,#define /g" | tr , '\n' | sed "/^$$/d" > $(target).config
|
||||
@echo "Generating $(target).creator"
|
||||
$(QUIET)echo "[GENERAL]" > $(target).creator
|
||||
|
||||
-include $(CFILES:%.c=$(OBJDIR)/%.c.d) $(ASFILES:%.S=$(OBJDIR)/%.S.d)
|
@@ -232,7 +232,7 @@ int adc_pt1000_get_current_resistance(float *resistance)
|
||||
*resistance = adc_pt1000_apply_calibration(pt1000_res_raw_lf);
|
||||
|
||||
if (safety_controller_get_flags_by_mask(ERR_FLAG_MEAS_ADC_OFF | ERR_FLAG_MEAS_ADC_OVERFLOW |
|
||||
ERR_FLAG_MEAS_ADC_WATCHDOG)) {
|
||||
ERR_FLAG_MEAS_ADC_WATCHDOG | ERR_FLAG_TIMING_MEAS_ADC)) {
|
||||
ret_val = -100;
|
||||
goto return_value;
|
||||
}
|
||||
|
28
stm-firmware/arm-none-eabi-gcc.cmake
Normal file
28
stm-firmware/arm-none-eabi-gcc.cmake
Normal file
@@ -0,0 +1,28 @@
|
||||
if(MINGW OR CYGWIN OR WIN32)
|
||||
set(UTIL_SEARCH_CMD where)
|
||||
elseif(UNIX OR APPLE)
|
||||
set(UTIL_SEARCH_CMD which)
|
||||
endif()
|
||||
|
||||
set(TOOLCHAIN_PREFIX arm-none-eabi-)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${UTIL_SEARCH_CMD} ${TOOLCHAIN_PREFIX}gcc
|
||||
OUTPUT_VARIABLE BINUTILS_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
get_filename_component(ARM_TOOLCHAIN_DIR ${BINUTILS_PATH} DIRECTORY)
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
|
||||
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
|
||||
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
|
||||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
|
||||
|
||||
set(CMAKE_OBJCOPY ${ARM_TOOLCHAIN_DIR}/${TOOLCHAIN_PREFIX}objcopy CACHE INTERNAL "objcopy tool")
|
||||
set(CMAKE_SIZE_UTIL ${ARM_TOOLCHAIN_DIR}/${TOOLCHAIN_PREFIX}size CACHE INTERNAL "size tool")
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${BINUTILS_PATH})
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
Submodule stm-firmware/base64-lib updated: 80090b4b20...251e90abf3
126
stm-firmware/boot/startup-tests.c
Normal file
126
stm-firmware/boot/startup-tests.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "startup-tests.h"
|
||||
|
||||
uint32_t startup_test_perform_ccm_ram_check(void)
|
||||
{
|
||||
const void *ccmram_base = (void *)0x10000000UL;
|
||||
const uint32_t ccmram_size = 64U * 1024UL;
|
||||
volatile uint32_t *word_ptr;
|
||||
uint32_t target_val;
|
||||
uint32_t idx;
|
||||
uint32_t ret = 0UL;
|
||||
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 1 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 2 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0xFF */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0xFFFFFFFFUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = 0xFFFFFFFFUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0x00 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0x0UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = 0x0UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exit_ret_address:
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t startup_test_perform_system_ram_check()
|
||||
{
|
||||
const void *ram_base = (void *)0x20000000UL;
|
||||
const uint32_t ram_size = 128U * 1024UL;
|
||||
volatile uint32_t *word_ptr;
|
||||
uint32_t target_val;
|
||||
uint32_t idx;
|
||||
uint32_t ret = 0UL;
|
||||
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 1 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 2 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0xFF */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0xFFFFFFFFUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = 0xFFFFFFFFUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0x00 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0x0UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = 0x0UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
exit_ret_address:
|
||||
return ret;
|
||||
}
|
25
stm-firmware/boot/startup-tests.h
Normal file
25
stm-firmware/boot/startup-tests.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef _STARTUP_TESTS_H_
|
||||
#define _STARTUP_TESTS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Do a RAM check of the CCM RAM.
|
||||
*
|
||||
* Loop over the whole CCM memory area and check it.
|
||||
*
|
||||
* @return 0 if successful. Else the defect address is returned.
|
||||
* @warning This will completely corrupt this memory!
|
||||
* You have to ensure to set it to sane values afterwards!
|
||||
*/
|
||||
uint32_t startup_test_perform_ccm_ram_check(void);
|
||||
|
||||
/**
|
||||
* @brief Do a RAM check of the stnadard SRAM regions
|
||||
* @return 0 if successful. If an error is found, the faulty address is returned
|
||||
* @warning This completely destroys all content in the memory!
|
||||
* @warning Ensure that the stack pointer is moved to a different memory reagion (CCM RAM)!
|
||||
*/
|
||||
uint32_t startup_test_perform_system_ram_check(void);
|
||||
|
||||
#endif /* _STARTUP_TESTS_H_ */
|
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "startup-tests.h"
|
||||
|
||||
/* C++ library init */
|
||||
# if defined(__cplusplus)
|
||||
@@ -288,7 +289,7 @@ extern unsigned int __ld_eheap;
|
||||
|
||||
#define CPACR (*((volatile uint32_t *)0xE000ED88))
|
||||
|
||||
void Reset_Handler(void) {
|
||||
void __attribute__((noreturn)) Reset_Handler(void) {
|
||||
/* Stack is already initialized by hardware */
|
||||
|
||||
/* The first thing we do here, is to initialize the FPU
|
||||
@@ -297,6 +298,9 @@ void Reset_Handler(void) {
|
||||
*/
|
||||
CPACR |= (0xF << 20);
|
||||
|
||||
/**
|
||||
* Prepare RAM etc for the System Init function. This ensures, the RAM tests execute at max speed.
|
||||
*/
|
||||
|
||||
/* Copy .data section */
|
||||
__init_section(&__ld_load_data, &__ld_sdata, &__ld_edata);
|
||||
@@ -311,6 +315,41 @@ void Reset_Handler(void) {
|
||||
|
||||
/* Set clocks, waitstates, ART operation etc. */
|
||||
SystemInit();
|
||||
|
||||
if (startup_test_perform_ccm_ram_check()) {
|
||||
/* Hang forever in case of an error. We cannot handle this (yet?) */
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* Move the Stack pointer to CCMRAM
|
||||
* This allows us to perform a RAM test on the main RAM.
|
||||
* Note: sp is not required to be inside the clobber list!
|
||||
*/
|
||||
__asm__ __volatile__ ("mov sp, %0\n\t" :: "r"(0x10000000UL + (64U * 1024UL)) :);
|
||||
|
||||
if (startup_test_perform_system_ram_check()) {
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* Move the stack pointer back. Note: This only works if this function does not use the stack for variables.
|
||||
* Otherwise everything will be broken.
|
||||
*/
|
||||
__asm__ __volatile__ ("mov sp, %0\n\t" :: "r"(&__ld_top_of_stack) :);
|
||||
|
||||
/**
|
||||
* RAM tests destroyed our values. So we have to copy them again...
|
||||
*/
|
||||
|
||||
/* Copy .data section */
|
||||
__init_section(&__ld_load_data, &__ld_sdata, &__ld_edata);
|
||||
/* Fill bss with zero */
|
||||
__fill_zero(&__ld_sbss, &__ld_ebss);
|
||||
/* Fill Heap with zero */
|
||||
__fill_zero(&__ld_sheap, &__ld_eheap);
|
||||
/* Fill static CCM memory with zeroes */
|
||||
__fill_zero(&__ld_sbss_ccm, &__ld_ebss_ccm);
|
||||
/* Init CCM RAM data section */
|
||||
__init_section(&__ld_load_ccm_data, &__ld_sdata_ccm, &__ld_edata_ccm);
|
||||
|
||||
/* C++ init function */
|
||||
#if defined(__cplusplus)
|
||||
|
242
stm-firmware/config-parser/temp-profile-parser.c
Normal file
242
stm-firmware/config-parser/temp-profile-parser.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 Mario Hüttel <mario.huettel@gmx.net>
|
||||
*
|
||||
* This file is part of the Reflow Oven Controller Project.
|
||||
*
|
||||
* The reflow oven controller is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the reflow oven controller project.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <reflow-controller/config-parser/temp-profile-parser.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fatfs/ff.h>
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
|
||||
struct pl_command_list_map {
|
||||
enum pl_command_type command;
|
||||
const char * const token;
|
||||
uint8_t expected_param_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This list stores the command tokens and the expected number of arguments for each command
|
||||
*/
|
||||
static const struct pl_command_list_map cmd_list_map[_PL_NUM_CMDS] = {
|
||||
{PL_PID_CONF, "pid_conf", 6u},
|
||||
{PL_SET_TEMP, "temp_set", 1u},
|
||||
{PL_WAIT_FOR_TEMP, "wait_temp", 1u},
|
||||
{PL_WAIT_FOR_TIME, "wait_time", 1u},
|
||||
{PL_SET_RAMP, "temp_ramp", 2u},
|
||||
{PL_LOUDSPEAKER_SET, "beep", 1u},
|
||||
{PL_OFF, "temp_off", 0u}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Read a line in the file until a line break is detected or a comment is started.
|
||||
*
|
||||
* In case a comment is detected, it is still read form the file to ensure
|
||||
* the next line is read afterwards
|
||||
*
|
||||
* @param f File to read from
|
||||
* @param buffer Buffer to read in
|
||||
* @param buffsize buffer size
|
||||
* @return 0 if successful, -1 if disk error,
|
||||
*/
|
||||
static int read_line_until_comment(FIL *f, char *buffer, uint32_t buffsize)
|
||||
{
|
||||
char *ptr;
|
||||
uint32_t i;
|
||||
|
||||
if (!f || !buffsize || !buffer)
|
||||
return -1000;
|
||||
|
||||
buffer[0] = 0;
|
||||
/* Read the line from file */
|
||||
ptr = f_gets(buffer, (int)buffsize, f);
|
||||
if (!ptr) {
|
||||
buffer[0] = 0;
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Go through the line until we encounter a # sign or the end of line*/
|
||||
for (i = 0; i < buffsize; i++) {
|
||||
if (buffer[i] == '\n' || buffer[i] == '#') {
|
||||
buffer[i] = 0;
|
||||
break;
|
||||
} else if (buffer[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pl_command_list_map *string_to_command(const char *str)
|
||||
{
|
||||
uint32_t i;
|
||||
const struct pl_command_list_map *ret = NULL;
|
||||
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
for (i = 0u; i < _PL_NUM_CMDS; i++) {
|
||||
if (!strcmp(str, cmd_list_map[i].token)) {
|
||||
ret = &cmd_list_map[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_line(char *line, struct pl_command *cmd)
|
||||
{
|
||||
uint8_t token_idx = 0;
|
||||
char *token;
|
||||
const char * const delim = " \t";
|
||||
const struct pl_command_list_map *map = NULL;
|
||||
char *endptr;
|
||||
struct pl_command c;
|
||||
|
||||
if (!line || !cmd)
|
||||
return -1000;
|
||||
|
||||
token = strtok(line, delim);
|
||||
|
||||
if (!token) {
|
||||
/* Empty line or command line */
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (token && token_idx <= PROFILE_LANG_MAX_NUM_ARGS) {
|
||||
switch (token_idx) {
|
||||
case 0:
|
||||
map = string_to_command(token);
|
||||
c.cmd = map->command;
|
||||
break;
|
||||
default:
|
||||
if (!map) {
|
||||
/* No valid command found */
|
||||
return -1;
|
||||
}
|
||||
c.params[token_idx - 1] = strtof(token, &endptr);
|
||||
if (endptr == token) {
|
||||
/* Invalid parameter */
|
||||
return -2;
|
||||
}
|
||||
if (token_idx > map->expected_param_count)
|
||||
return -3;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
token = strtok(NULL, delim);
|
||||
token_idx++;
|
||||
}
|
||||
|
||||
if (!map || (token_idx - 1 < map->expected_param_count)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
memcpy(cmd, &c, sizeof(struct pl_command));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SlList *copy_and_append_command_to_list(SlList *list, const struct pl_command *cmd)
|
||||
{
|
||||
struct pl_command *alloced_cmd;
|
||||
|
||||
alloced_cmd = (struct pl_command *)malloc(sizeof(struct pl_command));
|
||||
memcpy(alloced_cmd, cmd, sizeof(struct pl_command));
|
||||
list = sl_list_append(list, alloced_cmd);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
SlList **command_list,
|
||||
uint32_t max_len,
|
||||
uint32_t *cmds_parsed)
|
||||
{
|
||||
FIL script_file;
|
||||
FRESULT fres;
|
||||
int res;
|
||||
enum pl_ret_val ret = PL_RET_SUCCESS;
|
||||
char workbuff[256];
|
||||
struct pl_command temp_command;
|
||||
uint32_t cmd_idx;
|
||||
|
||||
if (!filename || !command_list || !max_len || !cmds_parsed)
|
||||
return PL_RET_PARAM_ERR;
|
||||
|
||||
|
||||
fres = f_open(&script_file, filename, FA_READ);
|
||||
if (fres != FR_OK) {
|
||||
ret = PL_RET_DISK_ERR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
cmd_idx = 0;
|
||||
*cmds_parsed = 0;
|
||||
|
||||
do {
|
||||
/* read in the line */
|
||||
res = read_line_until_comment(&script_file, workbuff, sizeof(workbuff));
|
||||
if (res < 0) {
|
||||
ret = PL_RET_DISK_ERR;
|
||||
goto exit_close;
|
||||
}
|
||||
|
||||
/* Check if list already full */
|
||||
if (cmd_idx >= max_len) {
|
||||
ret = PL_RET_LIST_FULL;
|
||||
goto exit_close;
|
||||
}
|
||||
|
||||
/* Parse the line */
|
||||
res = parse_line(workbuff, &temp_command);
|
||||
if (res < 0) {
|
||||
ret = PL_RET_SCRIPT_ERR;
|
||||
goto exit_close;
|
||||
} else if (res == 0) {
|
||||
cmd_idx++;
|
||||
*cmds_parsed = cmd_idx;
|
||||
|
||||
/* Append the temp_command to the list */
|
||||
*command_list = copy_and_append_command_to_list(*command_list, &temp_command);
|
||||
}
|
||||
|
||||
|
||||
} while (!f_eof(&script_file));
|
||||
|
||||
exit_close:
|
||||
(void)f_close(&script_file);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void delete_pl_command(void *cmd)
|
||||
{
|
||||
if (cmd)
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
void temp_profile_free_command_list(SlList **list)
|
||||
{
|
||||
sl_list_free_full(*list, delete_pl_command);
|
||||
*list = NULL;
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2019 */
|
||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
@@ -12,14 +12,8 @@
|
||||
#include "shimatta_sdio_driver/shimatta_sdio.h"
|
||||
|
||||
/* Definitions of physical drive number for each drive */
|
||||
#define DEV_SD 0 /* Example: Map MMC/SD card to physical drive 0*/
|
||||
/*
|
||||
DSTATUS SDIO_status();
|
||||
DSTATUS SDIO_initialize();
|
||||
DRESULT SDIO_disk_read(BYTE *buff, DWORD sector, UINT count);
|
||||
DRESULT SDIO_disk_write(const BYTE *buff, DWORD sector, UINT count);
|
||||
DRESULT SDIO_disk_ioctl(BYTE cmd, void* buff);
|
||||
*/
|
||||
#define DEV_SD 0
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
@@ -116,4 +110,3 @@ DRESULT disk_ioctl (
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem Module R0.14 /
|
||||
/ FatFs - Generic FAT Filesystem Module R0.14a /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2019, ChaN, all right reserved.
|
||||
/ Copyright (C) 2020, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#if FF_DEFINED != 86606 /* Revision ID */
|
||||
#if FF_DEFINED != 80196 /* Revision ID */
|
||||
#error Wrong include file (ff.h).
|
||||
#endif
|
||||
|
||||
@@ -1134,13 +1134,12 @@ static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
|
||||
if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */
|
||||
/* Create FSInfo structure */
|
||||
mem_set(fs->win, 0, sizeof fs->win);
|
||||
st_word(fs->win + BS_55AA, 0xAA55);
|
||||
st_dword(fs->win + FSI_LeadSig, 0x41615252);
|
||||
st_dword(fs->win + FSI_StrucSig, 0x61417272);
|
||||
st_dword(fs->win + FSI_Free_Count, fs->free_clst);
|
||||
st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);
|
||||
/* Write it into the FSInfo sector */
|
||||
fs->winsect = fs->volbase + 1;
|
||||
st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */
|
||||
st_dword(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */
|
||||
st_dword(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */
|
||||
st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */
|
||||
st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */
|
||||
fs->winsect = fs->volbase + 1; /* Write it into the FSInfo sector (Next to VBR) */
|
||||
disk_write(fs->pdrv, fs->win, fs->winsect, 1);
|
||||
fs->fsi_flag = 0;
|
||||
}
|
||||
@@ -1235,7 +1234,8 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFF
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* go to default */
|
||||
val = 1; /* Internal error */
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
val = 1; /* Internal error */
|
||||
@@ -1266,7 +1266,7 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
|
||||
|
||||
if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */
|
||||
switch (fs->fs_type) {
|
||||
case FS_FAT12 :
|
||||
case FS_FAT12:
|
||||
bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */
|
||||
res = move_window(fs, fs->fatbase + (bc / SS(fs)));
|
||||
if (res != FR_OK) break;
|
||||
@@ -1280,16 +1280,16 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
|
||||
fs->wflag = 1;
|
||||
break;
|
||||
|
||||
case FS_FAT16 :
|
||||
case FS_FAT16:
|
||||
res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
|
||||
if (res != FR_OK) break;
|
||||
st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */
|
||||
fs->wflag = 1;
|
||||
break;
|
||||
|
||||
case FS_FAT32 :
|
||||
case FS_FAT32:
|
||||
#if FF_FS_EXFAT
|
||||
case FS_EXFAT :
|
||||
case FS_EXFAT:
|
||||
#endif
|
||||
res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
|
||||
if (res != FR_OK) break;
|
||||
@@ -1821,7 +1821,7 @@ static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DEN
|
||||
|
||||
static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
|
||||
DIR* dp, /* Pointer to the directory object */
|
||||
UINT nent /* Number of contiguous entries to allocate */
|
||||
UINT n_ent /* Number of contiguous entries to allocate */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
@@ -1836,16 +1836,16 @@ static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
|
||||
res = move_window(fs, dp->sect);
|
||||
if (res != FR_OK) break;
|
||||
#if FF_FS_EXFAT
|
||||
if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {
|
||||
if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */
|
||||
#else
|
||||
if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {
|
||||
if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */
|
||||
#endif
|
||||
if (++n == nent) break; /* A block of contiguous free entries is found */
|
||||
if (++n == n_ent) break; /* Is a block of contiguous free entries found? */
|
||||
} else {
|
||||
n = 0; /* Not a blank entry. Restart to search */
|
||||
n = 0; /* Not a free entry, restart to search */
|
||||
}
|
||||
res = dir_next(dp, 1);
|
||||
} while (res == FR_OK); /* Next entry with table stretch enabled */
|
||||
res = dir_next(dp, 1); /* Next entry with table stretch enabled */
|
||||
} while (res == FR_OK);
|
||||
}
|
||||
|
||||
if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */
|
||||
@@ -2527,19 +2527,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too
|
||||
FRESULT res;
|
||||
FATFS *fs = dp->obj.fs;
|
||||
#if FF_USE_LFN /* LFN configuration */
|
||||
UINT n, nlen, nent;
|
||||
UINT n, len, n_ent;
|
||||
BYTE sn[12], sum;
|
||||
|
||||
|
||||
if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */
|
||||
for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */
|
||||
for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
|
||||
res = dir_alloc(dp, nent); /* Allocate directory entries */
|
||||
n_ent = (len + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
|
||||
res = dir_alloc(dp, n_ent); /* Allocate directory entries */
|
||||
if (res != FR_OK) return res;
|
||||
dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */
|
||||
dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the allocated entry block offset */
|
||||
|
||||
if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */
|
||||
dp->obj.stat &= ~4;
|
||||
@@ -2580,19 +2580,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too
|
||||
}
|
||||
|
||||
/* Create an SFN with/without LFNs. */
|
||||
nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */
|
||||
res = dir_alloc(dp, nent); /* Allocate entries */
|
||||
if (res == FR_OK && --nent) { /* Set LFN entry if needed */
|
||||
res = dir_sdi(dp, dp->dptr - nent * SZDIRE);
|
||||
n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */
|
||||
res = dir_alloc(dp, n_ent); /* Allocate entries */
|
||||
if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */
|
||||
res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE);
|
||||
if (res == FR_OK) {
|
||||
sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */
|
||||
do { /* Store LFN entries in bottom first */
|
||||
res = move_window(fs, dp->sect);
|
||||
if (res != FR_OK) break;
|
||||
put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum);
|
||||
put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum);
|
||||
fs->wflag = 1;
|
||||
res = dir_next(dp, 0); /* Next entry */
|
||||
} while (res == FR_OK && --nent);
|
||||
} while (res == FR_OK && --n_ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2778,7 +2778,10 @@ static void get_fileinfo (
|
||||
/* Pattern matching */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static DWORD get_achar ( /* Get a character and advances ptr */
|
||||
#define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */
|
||||
|
||||
|
||||
static DWORD get_achar ( /* Get a character and advance ptr */
|
||||
const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
|
||||
)
|
||||
{
|
||||
@@ -2809,41 +2812,43 @@ static DWORD get_achar ( /* Get a character and advances ptr */
|
||||
}
|
||||
|
||||
|
||||
static int pattern_matching ( /* 0:not matched, 1:matched */
|
||||
static int pattern_match ( /* 0:mismatched, 1:matched */
|
||||
const TCHAR* pat, /* Matching pattern */
|
||||
const TCHAR* nam, /* String to be tested */
|
||||
int skip, /* Number of pre-skip chars (number of ?s) */
|
||||
int inf /* Infinite search (* specified) */
|
||||
UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */
|
||||
UINT recur /* Recursion count */
|
||||
)
|
||||
{
|
||||
const TCHAR *pp, *np;
|
||||
DWORD pc, nc;
|
||||
int nm, nx;
|
||||
const TCHAR *pptr, *nptr;
|
||||
DWORD pchr, nchr;
|
||||
UINT sk;
|
||||
|
||||
|
||||
while (skip--) { /* Pre-skip name chars */
|
||||
while ((skip & 0xFF) != 0) { /* Pre-skip name chars */
|
||||
if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */
|
||||
skip--;
|
||||
}
|
||||
if (*pat == 0 && inf) return 1; /* (short circuit) */
|
||||
if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */
|
||||
|
||||
do {
|
||||
pp = pat; np = nam; /* Top of pattern and name to match */
|
||||
pptr = pat; nptr = nam; /* Top of pattern and name to match */
|
||||
for (;;) {
|
||||
if (*pp == '?' || *pp == '*') { /* Wildcard? */
|
||||
nm = nx = 0;
|
||||
do { /* Analyze the wildcard block */
|
||||
if (*pp++ == '?') nm++; else nx = 1;
|
||||
} while (*pp == '?' || *pp == '*');
|
||||
if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
|
||||
nc = *np; break; /* Branch mismatched */
|
||||
if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */
|
||||
if (recur == 0) return 0; /* Too many wildcard terms? */
|
||||
sk = 0;
|
||||
do { /* Analyze the wildcard term */
|
||||
if (*pptr++ == '?') sk++; else sk |= 0x100;
|
||||
} while (*pptr == '?' || *pptr == '*');
|
||||
if (pattern_match(pptr, nptr, sk, recur - 1)) return 1; /* Test new branch (recursive call) */
|
||||
nchr = *nptr; break; /* Branch mismatched */
|
||||
}
|
||||
pc = get_achar(&pp); /* Get a pattern char */
|
||||
nc = get_achar(&np); /* Get a name char */
|
||||
if (pc != nc) break; /* Branch mismatched? */
|
||||
if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */
|
||||
pchr = get_achar(&pptr); /* Get a pattern char */
|
||||
nchr = get_achar(&nptr); /* Get a name char */
|
||||
if (pchr != nchr) break; /* Branch mismatched? */
|
||||
if (pchr == 0) return 1; /* Branch matched? (matched at end of both strings) */
|
||||
}
|
||||
get_achar(&nam); /* nam++ */
|
||||
} while (inf && nc); /* Retry until end of name if infinite search is specified */
|
||||
} while (skip && nchr); /* Retry until end of name if infinite search is specified */
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3290,23 +3295,37 @@ static DWORD make_rand (
|
||||
|
||||
/* Check what the sector is */
|
||||
|
||||
static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Valid BS but not FAT, 3:Invalid BS, 4:Disk error */
|
||||
static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */
|
||||
FATFS* fs, /* Filesystem object */
|
||||
LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */
|
||||
)
|
||||
{
|
||||
WORD w, sign;
|
||||
BYTE b;
|
||||
|
||||
|
||||
fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */
|
||||
if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */
|
||||
|
||||
if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot signature (always here regardless of the sector size) */
|
||||
|
||||
if (FF_FS_EXFAT && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */
|
||||
|
||||
if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */
|
||||
if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */
|
||||
if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */
|
||||
sign = ld_word(fs->win + BS_55AA);
|
||||
#if FF_FS_EXFAT
|
||||
if (sign == 0xAA55 && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */
|
||||
#endif
|
||||
b = fs->win[BS_JmpBoot];
|
||||
if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */
|
||||
if (sign == 0xAA55 && !mem_cmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) return 0; /* It is an FAT32 VBR */
|
||||
/* FAT volumes formatted with early MS-DOS lack boot signature and FAT string, so that we need to identify the FAT VBR without them. */
|
||||
w = ld_word(fs->win + BPB_BytsPerSec);
|
||||
if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS) { /* Properness of sector size */
|
||||
b = fs->win[BPB_SecPerClus];
|
||||
if (b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size */
|
||||
&& (fs->win[BPB_NumFATs] == 1 || fs->win[BPB_NumFATs] == 2) /* Properness of number of FATs */
|
||||
&& ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root entry count */
|
||||
&& ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size */
|
||||
return 0; /* Sector can be presumed an FAT VBR */
|
||||
}
|
||||
}
|
||||
}
|
||||
return 2; /* Valid BS but not FAT */
|
||||
return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or invalid BS) */
|
||||
}
|
||||
|
||||
|
||||
@@ -3698,7 +3717,7 @@ FRESULT f_open (
|
||||
DIR dj;
|
||||
FATFS *fs;
|
||||
#if !FF_FS_READONLY
|
||||
DWORD cl, bcs, clst;
|
||||
DWORD cl, bcs, clst, tm;
|
||||
LBA_t sc;
|
||||
FSIZE_t ofs;
|
||||
#endif
|
||||
@@ -3765,8 +3784,10 @@ FRESULT f_open (
|
||||
#endif
|
||||
{
|
||||
/* Set directory entry initial state */
|
||||
tm = GET_FATTIME(); /* Set created time */
|
||||
st_dword(dj.dir + DIR_CrtTime, tm);
|
||||
st_dword(dj.dir + DIR_ModTime, tm);
|
||||
cl = ld_clust(fs, dj.dir); /* Get current cluster chain */
|
||||
st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */
|
||||
dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */
|
||||
st_clust(fs, dj.dir, 0); /* Reset file allocation info */
|
||||
st_dword(dj.dir + DIR_FileSize, 0);
|
||||
@@ -4705,9 +4726,9 @@ FRESULT f_findnext (
|
||||
for (;;) {
|
||||
res = f_readdir(dp, fno); /* Get a directory item */
|
||||
if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */
|
||||
if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */
|
||||
if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) break; /* Test for the file name */
|
||||
#if FF_USE_LFN && FF_USE_FIND == 2
|
||||
if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */
|
||||
if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) break; /* Test for alternative name if exist */
|
||||
#endif
|
||||
}
|
||||
return res;
|
||||
@@ -5376,10 +5397,12 @@ FRESULT f_getlabel (
|
||||
if (res == FR_OK) {
|
||||
switch (fs->fs_type) {
|
||||
case FS_EXFAT:
|
||||
di = BPB_VolIDEx; break;
|
||||
di = BPB_VolIDEx;
|
||||
break;
|
||||
|
||||
case FS_FAT32:
|
||||
di = BS_VolID32; break;
|
||||
di = BS_VolID32;
|
||||
break;
|
||||
|
||||
default:
|
||||
di = BS_VolID;
|
||||
@@ -5677,7 +5700,7 @@ FRESULT f_forward (
|
||||
|
||||
#if !FF_FS_READONLY && FF_USE_MKFS
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Create an FAT/exFAT volume */
|
||||
/* Create FAT/exFAT volume */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */
|
||||
@@ -5685,12 +5708,12 @@ FRESULT f_forward (
|
||||
#define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */
|
||||
|
||||
|
||||
/* Create partitions on the physical drive */
|
||||
/* Create partitions on the physical drive in format of MBR or GPT */
|
||||
|
||||
static FRESULT create_partition (
|
||||
BYTE drv, /* Physical drive number */
|
||||
const LBA_t plst[], /* Partition list */
|
||||
UINT sys, /* System ID (for only MBR, temp setting) and bit8:GPT */
|
||||
BYTE sys, /* System ID (for only MBR, temp setting) */
|
||||
BYTE* buf /* Working buffer for a sector */
|
||||
)
|
||||
{
|
||||
@@ -5801,7 +5824,7 @@ static FRESULT create_partition (
|
||||
|
||||
st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */
|
||||
st_dword(pte + PTE_SizLba, n_lba32); /* Number of sectors */
|
||||
pte[PTE_System] = (BYTE)sys; /* System type */
|
||||
pte[PTE_System] = sys; /* System type */
|
||||
|
||||
cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start cylinder */
|
||||
hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */
|
||||
@@ -5966,10 +5989,9 @@ FRESULT f_mkfs (
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
if (fsty == FS_EXFAT) { /* Create an exFAT volume */
|
||||
DWORD szb_bit, szb_case, sum, nb, cl, tbl[3];
|
||||
DWORD szb_bit, szb_case, sum, nbit, clu, clen[3];
|
||||
WCHAR ch, si;
|
||||
UINT j, st;
|
||||
BYTE b;
|
||||
|
||||
if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume for exFAT? */
|
||||
#if FF_USE_TRIM
|
||||
@@ -5990,12 +6012,12 @@ FRESULT f_mkfs (
|
||||
if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */
|
||||
if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */
|
||||
|
||||
szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
|
||||
tbl[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */
|
||||
szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
|
||||
clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */
|
||||
|
||||
/* Create a compressed up-case table */
|
||||
sect = b_data + sz_au * tbl[0]; /* Table start sector */
|
||||
sum = 0; /* Table checksum to be stored in the 82 entry */
|
||||
sect = b_data + sz_au * clen[0]; /* Table start sector */
|
||||
sum = 0; /* Table checksum to be stored in the 82 entry */
|
||||
st = 0; si = 0; i = 0; j = 0; szb_case = 0;
|
||||
do {
|
||||
switch (st) {
|
||||
@@ -6006,10 +6028,10 @@ FRESULT f_mkfs (
|
||||
}
|
||||
for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */
|
||||
if (j >= 128) {
|
||||
ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */
|
||||
ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 chars */
|
||||
}
|
||||
st = 1; /* Do not compress short run */
|
||||
/* go to next case */
|
||||
/* FALLTHROUGH */
|
||||
case 1:
|
||||
ch = si++; /* Fill the short run */
|
||||
if (--j == 0) st = 0;
|
||||
@@ -6028,16 +6050,15 @@ FRESULT f_mkfs (
|
||||
sect += n; i = 0;
|
||||
}
|
||||
} while (si);
|
||||
tbl[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */
|
||||
tbl[2] = 1; /* Number of root dir clusters */
|
||||
clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */
|
||||
clen[2] = 1; /* Number of root dir clusters */
|
||||
|
||||
/* Initialize the allocation bitmap */
|
||||
sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */
|
||||
nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */
|
||||
sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of bitmap sectors */
|
||||
nbit = clen[0] + clen[1] + clen[2]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */
|
||||
do {
|
||||
mem_set(buf, 0, sz_buf * ss);
|
||||
for (i = 0; nb >= 8 && i < sz_buf * ss; buf[i++] = 0xFF, nb -= 8) ;
|
||||
for (b = 1; nb != 0 && i < sz_buf * ss; buf[i] |= b, b <<= 1, nb--) ;
|
||||
mem_set(buf, 0, sz_buf * ss); /* Initialize bitmap buffer */
|
||||
for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
sect += n; nsect -= n;
|
||||
@@ -6045,20 +6066,20 @@ FRESULT f_mkfs (
|
||||
|
||||
/* Initialize the FAT */
|
||||
sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */
|
||||
j = nb = cl = 0;
|
||||
j = nbit = clu = 0;
|
||||
do {
|
||||
mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write index */
|
||||
if (cl == 0) { /* Set FAT [0] and FAT[1] */
|
||||
st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
|
||||
st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
|
||||
mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write offset */
|
||||
if (clu == 0) { /* Initialize FAT [0] and FAT[1] */
|
||||
st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++;
|
||||
st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++;
|
||||
}
|
||||
do { /* Create chains of bitmap, up-case and root dir */
|
||||
while (nb != 0 && i < sz_buf * ss) { /* Create a chain */
|
||||
st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
|
||||
i += 4; cl++; nb--;
|
||||
while (nbit != 0 && i < sz_buf * ss) { /* Create a chain */
|
||||
st_dword(buf + i, (nbit > 1) ? clu + 1 : 0xFFFFFFFF);
|
||||
i += 4; clu++; nbit--;
|
||||
}
|
||||
if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */
|
||||
} while (nb != 0 && i < sz_buf * ss);
|
||||
if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get next chain length */
|
||||
} while (nbit != 0 && i < sz_buf * ss);
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
sect += n; nsect -= n;
|
||||
@@ -6072,13 +6093,13 @@ FRESULT f_mkfs (
|
||||
st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */
|
||||
buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */
|
||||
st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */
|
||||
st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */
|
||||
st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* cluster */
|
||||
st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */
|
||||
sect = b_data + sz_au * (tbl[0] + tbl[1]); nsect = sz_au; /* Start of the root directory and number of sectors */
|
||||
sect = b_data + sz_au * (clen[0] + clen[1]); nsect = sz_au; /* Start of the root directory and number of sectors */
|
||||
do { /* Fill root directory sectors */
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect;
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
mem_set(buf, 0, ss);
|
||||
mem_set(buf, 0, ss); /* Rest of entries are filled with zero */
|
||||
sect += n; nsect -= n;
|
||||
} while (nsect);
|
||||
|
||||
@@ -6094,7 +6115,7 @@ FRESULT f_mkfs (
|
||||
st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
|
||||
st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */
|
||||
st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */
|
||||
st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */
|
||||
st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */
|
||||
st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */
|
||||
st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */
|
||||
for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */
|
||||
@@ -6520,7 +6541,7 @@ static void putc_bfd (putbuff* pb, TCHAR c)
|
||||
WCHAR hs, wc;
|
||||
#if FF_LFN_UNICODE == 2
|
||||
DWORD dc;
|
||||
TCHAR *tp;
|
||||
const TCHAR *tp;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -6562,7 +6583,7 @@ static void putc_bfd (putbuff* pb, TCHAR c)
|
||||
return;
|
||||
}
|
||||
}
|
||||
tp = (TCHAR*)pb->bs;
|
||||
tp = (const TCHAR*)pb->bs;
|
||||
dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
|
||||
if (dc == 0xFFFFFFFF) return; /* Wrong code? */
|
||||
wc = (WCHAR)dc;
|
||||
@@ -6650,7 +6671,7 @@ static int putc_flush (putbuff* pb)
|
||||
if ( pb->idx >= 0 /* Flush buffered characters to the file */
|
||||
&& f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
|
||||
&& (UINT)pb->idx == nw) return pb->nchr;
|
||||
return EOF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -6754,7 +6775,7 @@ int f_printf (
|
||||
d = c;
|
||||
if (IsLower(d)) d -= 0x20;
|
||||
switch (d) { /* Atgument type is... */
|
||||
case 'S' : /* String */
|
||||
case 'S': /* String */
|
||||
p = va_arg(arp, TCHAR*);
|
||||
for (j = 0; p[j]; j++) ;
|
||||
if (!(f & 2)) { /* Right padded */
|
||||
@@ -6764,21 +6785,26 @@ int f_printf (
|
||||
while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */
|
||||
continue;
|
||||
|
||||
case 'C' : /* Character */
|
||||
putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
|
||||
case 'C': /* Character */
|
||||
putc_bfd(&pb, (TCHAR)va_arg(arp, int));
|
||||
continue;
|
||||
|
||||
case 'B' : /* Unsigned binary */
|
||||
r = 2; break;
|
||||
case 'B': /* Unsigned binary */
|
||||
r = 2;
|
||||
break;
|
||||
|
||||
case 'O' : /* Unsigned octal */
|
||||
r = 8; break;
|
||||
case 'O': /* Unsigned octal */
|
||||
r = 8;
|
||||
break;
|
||||
|
||||
case 'D' : /* Signed decimal */
|
||||
case 'U' : /* Unsigned decimal */
|
||||
r = 10; break;
|
||||
case 'D': /* Signed decimal */
|
||||
case 'U': /* Unsigned decimal */
|
||||
r = 10;
|
||||
break;
|
||||
|
||||
case 'X' : /* Unsigned hexdecimal */
|
||||
r = 16; break;
|
||||
case 'X': /* Unsigned hexdecimal */
|
||||
r = 16;
|
||||
break;
|
||||
|
||||
default: /* Unknown type (pass-through) */
|
||||
putc_bfd(&pb, c); continue;
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.14 /
|
||||
/ FatFs - Generic FAT Filesystem module R0.14a /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2019, ChaN, all right reserved.
|
||||
/ Copyright (C) 2020, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 86606 /* Revision ID */
|
||||
#define FF_DEFINED 80196 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -345,10 +345,6 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86606 /* Revision ID */
|
||||
#define FFCONF_DEF 80196 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
@@ -33,7 +33,7 @@
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define FF_USE_FIND 0
|
||||
#define FF_USE_FIND 1
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
@@ -205,8 +205,8 @@
|
||||
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
|
||||
|
||||
|
||||
#define FF_MIN_GPT 0x100000000
|
||||
/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and
|
||||
#define FF_MIN_GPT 0x10000000
|
||||
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
|
||||
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
#define FF_FS_NORTC 0
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2019
|
||||
#define FF_NORTC_YEAR 2020
|
||||
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
|
@@ -0,0 +1,82 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 Mario Hüttel <mario.huettel@gmx.net>
|
||||
*
|
||||
* This file is part of the Reflow Oven Controller Project.
|
||||
*
|
||||
* The reflow oven controller is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the reflow oven controller project.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__
|
||||
#define __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
|
||||
enum pl_command_type {
|
||||
PL_PID_CONF = 0,
|
||||
PL_SET_TEMP,
|
||||
PL_SET_RAMP,
|
||||
PL_WAIT_FOR_TEMP,
|
||||
PL_WAIT_FOR_TIME,
|
||||
PL_LOUDSPEAKER_SET,
|
||||
PL_OFF,
|
||||
_PL_NUM_CMDS,
|
||||
};
|
||||
|
||||
enum pl_ret_val {
|
||||
PL_RET_SUCCESS = 0,
|
||||
PL_RET_DISK_ERR,
|
||||
PL_RET_PARAM_ERR,
|
||||
PL_RET_LIST_FULL,
|
||||
PL_RET_SCRIPT_ERR,
|
||||
};
|
||||
|
||||
#define PROFILE_LANG_MAX_NUM_ARGS (8)
|
||||
|
||||
struct pl_command {
|
||||
enum pl_command_type cmd;
|
||||
float params[PROFILE_LANG_MAX_NUM_ARGS];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Parse a temperature profile from file and generate the command list
|
||||
*
|
||||
* Commands are parsed into the list given by \p command_list. If \p max_len is reached,
|
||||
* the function returns with @ref PL_RET_LIST_FULL
|
||||
*
|
||||
* In any case, the command list not cleared afterwards. The user has to clear the command list manually
|
||||
* using @ref temp_profile_free_command_list.
|
||||
*
|
||||
* @param filename File to parse
|
||||
* @param[in, out] command_list Command list to output @ref pl_command elements in.
|
||||
* @param max_len maximum number of commands.
|
||||
* @param cmds_parsed Number of parsed commands
|
||||
* @return
|
||||
*/
|
||||
enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
SlList **command_list,
|
||||
uint32_t max_len,
|
||||
uint32_t *cmds_parsed);
|
||||
|
||||
/**
|
||||
* @brief Fully free a comamnd list including hte sotred command structures.
|
||||
*
|
||||
* \p list's destination is set to NULL to indicate the empty list.
|
||||
*
|
||||
* @param[in, out] list Pointer to list.
|
||||
*/
|
||||
void temp_profile_free_command_list(SlList **list);
|
||||
|
||||
#endif /* __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__ */
|
@@ -35,8 +35,6 @@ void oven_driver_set_power(uint8_t power);
|
||||
|
||||
void oven_driver_disable(void);
|
||||
|
||||
void oven_pid_ack_errors(void);
|
||||
|
||||
void oven_pid_init(struct pid_controller *controller_to_copy);
|
||||
|
||||
void oven_pid_handle(void);
|
||||
|
@@ -47,6 +47,8 @@
|
||||
|
||||
#define SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT 32UL
|
||||
|
||||
#define SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE 256U
|
||||
|
||||
/**
|
||||
* @brief Safety memory header
|
||||
*/
|
||||
@@ -55,6 +57,7 @@ struct safety_memory_header {
|
||||
uint32_t boot_status_offset; /**< @brief Offset of the safety_memory_boot_status struct (in 32 bit words)*/
|
||||
uint32_t config_overrides_offset; /**< @brief Offset address of override entries */
|
||||
uint32_t config_overrides_len; /**< @brief Length of override entries in words */
|
||||
uint32_t firmware_update_filename; /**< @brief Filename of the firmware update. This string is at maximum 256 bytes long including the 0 terminator */
|
||||
uint32_t err_memory_offset; /**< @brief Offset of the error memory */
|
||||
uint32_t err_memory_end; /**< @brief End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */
|
||||
uint32_t crc; /**< @brief CRC of the header */
|
||||
@@ -240,13 +243,35 @@ int safety_memory_insert_config_override(struct config_override *config_override
|
||||
int safety_memory_get_config_override_count(uint32_t *count);
|
||||
|
||||
/**
|
||||
* @brief Get a config ovveide entry
|
||||
* @brief Get a config entry
|
||||
* @param idx Index of the requested entry
|
||||
* @param[out] config_override READ override
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override);
|
||||
|
||||
/**
|
||||
* @brief Read the set update filename from the safety backup memory
|
||||
*
|
||||
* \p filename has to be large enough to hold the filename (255 chars) plus the '\0'-terminator.
|
||||
*
|
||||
* @param[out] filename Array to fill in file name. May be NULL to only read the length.
|
||||
* @param[out] outlen String length of the filename. May be NULL to only read the file name.
|
||||
* @note \p filename may be NULL. In this case this function can be used to read the currently set filename's length.
|
||||
* @warning Function will fail if both parameters are NULL
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int safety_memory_get_update_filename(char *filename, size_t *outlen);
|
||||
|
||||
/**
|
||||
* @brief Set the filename of the update file
|
||||
* @param[in] filename Filename to set. Must be 255 chars at max (256 including null terminator)
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int safety_memory_set_update_filename(const char *filename);
|
||||
|
||||
#ifndef SAFETY_MEMORY_STRIPOUT_DUMP
|
||||
|
||||
/**
|
||||
* @brief Get a base64 dump of the whole safety memory.
|
||||
* @param[out] buffer Buffer to write the base 64 dump into.
|
||||
@@ -256,6 +281,8 @@ int safety_memory_get_config_override(uint32_t idx, struct config_override *conf
|
||||
*/
|
||||
int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size);
|
||||
|
||||
#endif /* SAFETY_MEMORY_STRIPOUT_DUMP */
|
||||
|
||||
#endif /* __SAFETY_MEMORY_H__ */
|
||||
|
||||
/** @} */
|
||||
|
@@ -0,0 +1,52 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 Mario Hüttel <mario.huettel@gmx.net>
|
||||
*
|
||||
* This file is part of the Reflow Oven Controller Project.
|
||||
*
|
||||
* The reflow oven controller is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the reflow oven controller project.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TEMP_PROFILE_EXECUTER_H__
|
||||
#define __TEMP_PROFILE_EXECUTER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <reflow-controller/config-parser/temp-profile-parser.h>
|
||||
|
||||
#define MAX_PROFILE_LENGTH 64
|
||||
|
||||
enum tpe_status {
|
||||
TPE_OFF,
|
||||
TPE_RUNNING,
|
||||
TPE_ABORT,
|
||||
};
|
||||
|
||||
struct tpe_current_state {
|
||||
enum tpe_status status;
|
||||
float setpoint;
|
||||
uint64_t start_timestamp;
|
||||
uint32_t step;
|
||||
uint32_t profile_steps;
|
||||
enum pl_command_type current_command;
|
||||
};
|
||||
|
||||
enum pl_ret_val temp_profile_executer_start(const char *filename);
|
||||
|
||||
int temp_profile_executer_handle(void);
|
||||
|
||||
const struct tpe_current_state *temp_profile_executer_status(void);
|
||||
|
||||
int temp_profile_executer_stop(void);
|
||||
|
||||
#endif /* __TEMP_PROFILE_EXECUTER_H__ */
|
@@ -21,6 +21,8 @@
|
||||
#ifndef _GUI_H_
|
||||
#define _GUI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Handle the reflow controller's LCD Menu
|
||||
* @return 0 if no delay is requested, 1 if delay is requested
|
||||
@@ -29,4 +31,8 @@ int gui_handle(void);
|
||||
|
||||
void gui_init(void);
|
||||
|
||||
void gui_root_menu_message_set(const char *heading, const char *text);
|
||||
|
||||
void gui_lcd_write_direct_blocking(uint8_t line, const char *text);
|
||||
|
||||
#endif /* _GUI_H_ */
|
||||
|
@@ -21,6 +21,8 @@
|
||||
#ifndef __LCD_H__
|
||||
#define __LCD_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LCD_DPORT (GPIOD)
|
||||
#define LCD_RCC_MASK RCC_AHB1ENR_GPIODEN
|
||||
#define LCD_DATA_BIT_OFFSET (8)
|
||||
@@ -48,6 +50,8 @@ void lcd_init(void);
|
||||
|
||||
void lcd_string(const char *data);
|
||||
|
||||
void lcd_setcursor(uint8_t x, uint8_t y);
|
||||
|
||||
void lcd_home(void);
|
||||
|
||||
enum lcd_fsm_ret lcd_fsm_write_buffer(const char (*display_buffer)[21]);
|
||||
|
@@ -87,4 +87,6 @@ void menu_list_scroll_up(struct menu_list *list);
|
||||
|
||||
void menu_list_enter_selected_entry(struct menu_list *list, struct lcd_menu *menu);
|
||||
|
||||
uint32_t menu_list_get_currently_selected(struct menu_list *list);
|
||||
|
||||
#endif /* __MENU_H__ */
|
||||
|
@@ -25,7 +25,14 @@
|
||||
|
||||
/**
|
||||
* @brief Start the RAM Code of the updater. This function will never return!
|
||||
*
|
||||
* This function is called at startup when the controller detects, that an update should
|
||||
* be performed.
|
||||
*
|
||||
* @note You prabably want to call @ref start_updater function to update.
|
||||
*/
|
||||
void __attribute__((noreturn)) start_updater(void);
|
||||
void __attribute__((noreturn)) start_updater_ram_code(void);
|
||||
|
||||
void __attribute__((noreturn)) updater_update_from_file(const char *filename);
|
||||
|
||||
#endif /* __UPDATER_UPDATER_H__ */
|
||||
|
1
stm-firmware/linklist-lib
Submodule
1
stm-firmware/linklist-lib
Submodule
Submodule stm-firmware/linklist-lib added at 18b3ab377a
@@ -37,7 +37,7 @@
|
||||
#include <stm-periph/stm32-gpio-macros.h>
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <stm-periph/uart.h>
|
||||
#include <reflow-controller/shell-uart-config.h>
|
||||
#include <reflow-controller/periph-config/shell-uart-config.h>
|
||||
#include <reflow-controller/oven-driver.h>
|
||||
#include <fatfs/ff.h>
|
||||
#include <reflow-controller/ui/gui.h>
|
||||
@@ -46,7 +46,7 @@
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include <reflow-controller/safety/fault.h>
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
|
||||
#include <reflow-controller/temp-profile-executer.h>
|
||||
#include <reflow-controller/settings/spi-eeprom.h>
|
||||
|
||||
static void setup_nvic_priorities(void)
|
||||
@@ -162,7 +162,17 @@ static inline void handle_boot_status(void)
|
||||
led_set(0, 1);
|
||||
led_set(1, 1);
|
||||
|
||||
start_updater();
|
||||
gui_lcd_write_direct_blocking(0, "Updating...");
|
||||
|
||||
start_updater_ram_code();
|
||||
}
|
||||
|
||||
if (status.code_updated) {
|
||||
status.code_updated = 0x0UL;
|
||||
safety_memory_set_boot_status(&status);
|
||||
|
||||
/* Display notification on GUI */
|
||||
gui_root_menu_message_set("Firmware updated", "[Press Key]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +228,7 @@ int main(void)
|
||||
shellmatta_handle_t shell_handle;
|
||||
int menu_wait_request;
|
||||
uint64_t quarter_sec_timestamp = 0ULL;
|
||||
static uint64_t IN_SECTION(.ccm.bss) main_loop_iter_count;
|
||||
|
||||
setup_system();
|
||||
|
||||
@@ -253,6 +264,9 @@ int main(void)
|
||||
menu_wait_request = gui_handle();
|
||||
handle_shell_uart_input(shell_handle);
|
||||
|
||||
/* Execute current profile step, if a profile is active */
|
||||
temp_profile_executer_handle();
|
||||
|
||||
safety_controller_handle();
|
||||
if (oven_pid_get_status() == OVEN_PID_RUNNING) {
|
||||
oven_pid_handle();
|
||||
@@ -264,6 +278,7 @@ int main(void)
|
||||
__WFI();
|
||||
else
|
||||
__NOP();
|
||||
main_loop_iter_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
3
stm-firmware/obj/.gitignore
vendored
3
stm-firmware/obj/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
*
|
||||
*.*
|
||||
!.gitignore
|
@@ -65,8 +65,8 @@ static void calculate_integral(struct pid_controller *pid, float deviation)
|
||||
/* Saturate integral term to specified maximum */
|
||||
if (pid->integral > pid->integral_max)
|
||||
pid->integral = pid->integral_max;
|
||||
else if (pid->integral < -pid->integral_max)
|
||||
pid->integral = -pid->integral_max;
|
||||
else if (pid->integral < 0.0f)
|
||||
pid->integral = 0.0f;
|
||||
}
|
||||
|
||||
float pid_sample(struct pid_controller *pid, float deviation)
|
||||
|
@@ -22,7 +22,11 @@
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/crc-unit.h>
|
||||
#include <stm-periph/backup-ram.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SAFETY_MEMORY_STRIPOUT_DUMP
|
||||
#include <base64-lib/base64-lib.h>
|
||||
#endif /* SAFETY_MEMORY_STRIPOUT_DUMP */
|
||||
|
||||
static int word_to_error_memory_entry(uint32_t entry_data, struct error_memory_entry *out)
|
||||
{
|
||||
@@ -99,7 +103,9 @@ static enum safety_memory_state safety_memory_get_header(struct safety_memory_he
|
||||
res++;
|
||||
if (header->config_overrides_len > SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT)
|
||||
res++;
|
||||
if (header->err_memory_offset < header->config_overrides_offset + header->config_overrides_len)
|
||||
if (header->firmware_update_filename < header->config_overrides_offset + header->config_overrides_len)
|
||||
res++;
|
||||
if (header->err_memory_offset < header->firmware_update_filename + (SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE / 4))
|
||||
res++;
|
||||
if (header->err_memory_end >= backup_ram_get_size_in_words() || header->err_memory_end < header->err_memory_offset)
|
||||
res++;
|
||||
@@ -133,7 +139,8 @@ static void safety_memory_write_new_header(void)
|
||||
header.boot_status_offset = wordsize_of(struct safety_memory_header);
|
||||
header.config_overrides_len = SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT;
|
||||
header.config_overrides_offset = header.boot_status_offset + wordsize_of(struct safety_memory_boot_status);
|
||||
header.err_memory_offset = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT;
|
||||
header.firmware_update_filename = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT;
|
||||
header.err_memory_offset = header.firmware_update_filename + (SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE / 4);
|
||||
header.err_memory_end = header.err_memory_offset;
|
||||
header.magic = SAFETY_MEMORY_MAGIC;
|
||||
|
||||
@@ -608,6 +615,8 @@ int safety_memory_get_config_override(uint32_t idx, struct config_override *conf
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef SAFETY_MEMORY_STRIPOUT_DUMP
|
||||
|
||||
int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size)
|
||||
{
|
||||
uint32_t safety_mem_size;
|
||||
@@ -633,3 +642,66 @@ int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size)
|
||||
*used_size = output_size + 1u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SAFETY_MEMORY_STRIPOUT_DUMP */
|
||||
|
||||
int safety_memory_get_update_filename(char *filename, size_t *outlen)
|
||||
{
|
||||
struct safety_memory_header header;
|
||||
unsigned int i;
|
||||
volatile char *ptr;
|
||||
size_t len = 0u;
|
||||
|
||||
/* If filename and outlen are both NULL, we don't do anything */
|
||||
if (!filename && !outlen)
|
||||
return -1;
|
||||
|
||||
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
|
||||
return -2000;
|
||||
|
||||
/* Get the filename */
|
||||
ptr = (volatile char *)backup_ram_get_base_ptr();
|
||||
ptr += (unsigned int)(header.firmware_update_filename * 4);
|
||||
for (i = 0; i < SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE; i++) {
|
||||
if (filename)
|
||||
filename[i] = *ptr;
|
||||
if (*ptr)
|
||||
len++;
|
||||
else
|
||||
break;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (outlen)
|
||||
*outlen = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int safety_memory_set_update_filename(const char *filename)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t len;
|
||||
unsigned int i;
|
||||
struct safety_memory_header header;
|
||||
volatile char *ram_ptr;
|
||||
|
||||
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
|
||||
return -2000;
|
||||
|
||||
if (!filename)
|
||||
return -1001;
|
||||
|
||||
len = strnlen(filename, SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE - 1);
|
||||
ram_ptr = backup_ram_get_base_ptr();
|
||||
ram_ptr += header.firmware_update_filename * 4;
|
||||
|
||||
for (i = 0u; i < len; i++) {
|
||||
ram_ptr[i] = filename[i];
|
||||
}
|
||||
ram_ptr[i] = 0;
|
||||
|
||||
ret = safety_memory_gen_crc();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -41,6 +41,8 @@
|
||||
#include <reflow-controller/safety/fault.h>
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include <reflow-controller/hw-version-detect.h>
|
||||
#include <reflow-controller/temp-profile-executer.h>
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
|
||||
#ifndef GIT_VER
|
||||
#define GIT_VER "VERSION NOT SET"
|
||||
@@ -51,28 +53,6 @@ static shellmatta_instance_t shell;
|
||||
static char shell_buffer[512];
|
||||
static char IN_SECTION(.ccm.bss) history_buffer[512];
|
||||
|
||||
static bool check_opt(const char *args, uint32_t len, const char *opt_to_check)
|
||||
{
|
||||
(void)len;
|
||||
char str[128];
|
||||
const char *ptr;
|
||||
static const char * const tokens = "\t ";
|
||||
|
||||
strncpy(str, args, sizeof(str));
|
||||
str[sizeof(str) - 1] = 0;
|
||||
|
||||
/* Tokenize the string */
|
||||
ptr = strtok(str, tokens);
|
||||
|
||||
while (ptr) {
|
||||
if (strcmp(ptr, opt_to_check) == 0)
|
||||
return true;
|
||||
ptr = strtok(NULL, tokens);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static shellmatta_retCode_t shell_cmd_ver(const shellmatta_handle_t handle,
|
||||
const char *arguments,
|
||||
uint32_t length)
|
||||
@@ -181,7 +161,7 @@ static shellmatta_retCode_t shell_cmd_pt1000_res(const shellmatta_handle_t han
|
||||
pt1000_status = adc_pt1000_get_current_resistance(&resistance);
|
||||
|
||||
if (pt1000_status == 2) {
|
||||
strcat(display_status, " UNSTABLE ");
|
||||
strcat(display_status, "UNSTABLE");
|
||||
} else if (pt1000_status) {
|
||||
strcpy(display_status, "ERROR");
|
||||
} else {
|
||||
@@ -343,14 +323,36 @@ static shellmatta_retCode_t shell_cmd_read_flags(const shellmatta_handle_t handl
|
||||
uint32_t i;
|
||||
char name[64];
|
||||
bool flag;
|
||||
bool tryack;
|
||||
bool tryack = false;
|
||||
int status;
|
||||
enum safety_flag flag_enum;
|
||||
struct analog_monitor_info amon_info;
|
||||
struct timing_monitor_info timing_info;
|
||||
char *argument;
|
||||
uint32_t len;
|
||||
char option;
|
||||
shellmatta_retCode_t opt_ret;
|
||||
static const shellmatta_opt_long_t options[] = {
|
||||
{"ack", 'a', SHELLMATTA_OPT_ARG_NONE},
|
||||
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE}
|
||||
};
|
||||
|
||||
do {
|
||||
opt_ret = shellmatta_opt_long(handle, options, &option, &argument, &len);
|
||||
if (opt_ret != SHELLMATTA_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case 'a':
|
||||
tryack = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
|
||||
|
||||
/* Check for the --ack option */
|
||||
tryack = check_opt(arguments, length, "--ack");
|
||||
|
||||
shellmatta_printf(handle, "Error Flags\r\n"
|
||||
"-----------\r\n");
|
||||
@@ -630,13 +632,35 @@ shellmatta_retCode_t shell_cmd_update(const shellmatta_handle_t handle, const ch
|
||||
(void)handle;
|
||||
(void)arguments;
|
||||
(void)length;
|
||||
struct safety_memory_boot_status status;
|
||||
shellmatta_retCode_t opt_stat;
|
||||
char option;
|
||||
char *argument;
|
||||
uint32_t arg_len;
|
||||
const char *update_file = NULL;
|
||||
|
||||
safety_memory_get_boot_status(&status);
|
||||
status.reboot_to_bootloader = 0xFFFFFFFFUL;
|
||||
safety_memory_set_boot_status(&status);
|
||||
const shellmatta_opt_long_t options[] = {
|
||||
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE},
|
||||
};
|
||||
|
||||
NVIC_SystemReset();
|
||||
while (1) {
|
||||
opt_stat = shellmatta_opt_long(handle, options, &option, &argument, &arg_len);
|
||||
if (opt_stat != SHELLMATTA_OK)
|
||||
break;
|
||||
switch (option) {
|
||||
case '\0':
|
||||
update_file = argument;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!update_file || !strlen(update_file)) {
|
||||
shellmatta_printf(handle, "Please specify a valid update file!\r\n");
|
||||
return SHELLMATTA_ERROR;
|
||||
}
|
||||
|
||||
updater_update_from_file(update_file);
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
@@ -650,6 +674,8 @@ shellmatta_retCode_t shell_cmd_overtemp_cfg(const shellmatta_handle_t handle, co
|
||||
char option;
|
||||
bool temp_passed = false;
|
||||
bool persistent = false;
|
||||
(void)args;
|
||||
(void)len;
|
||||
|
||||
static const shellmatta_opt_long_t options[] = {
|
||||
{"persistent", 'p', SHELLMATTA_OPT_ARG_NONE},
|
||||
@@ -683,6 +709,87 @@ shellmatta_retCode_t shell_cmd_overtemp_cfg(const shellmatta_handle_t handle, co
|
||||
return ret;
|
||||
}
|
||||
|
||||
shellmatta_retCode_t shell_cmd_execute(const shellmatta_handle_t handle, const char *args, uint32_t len)
|
||||
{
|
||||
enum pl_ret_val res;
|
||||
const struct tpe_current_state *state;
|
||||
static bool running = false;
|
||||
char *data;
|
||||
uint32_t dlen;
|
||||
(void)args;
|
||||
(void)len;
|
||||
int opt_stat;
|
||||
char option;
|
||||
char *argument;
|
||||
uint32_t arg_len;
|
||||
const char *script_name = NULL;
|
||||
const shellmatta_opt_long_t options[] = {
|
||||
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE},
|
||||
};
|
||||
|
||||
while (1) {
|
||||
opt_stat = shellmatta_opt_long(handle, options, &option, &argument, &arg_len);
|
||||
if (opt_stat != SHELLMATTA_OK)
|
||||
break;
|
||||
switch (option) {
|
||||
case '\0':
|
||||
script_name = argument;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!script_name) {
|
||||
shellmatta_printf(handle, "No script name specified!\r\n");
|
||||
return SHELLMATTA_ERROR;
|
||||
}
|
||||
|
||||
shellmatta_read(handle, &data, &dlen);
|
||||
|
||||
if (!running) {
|
||||
res = temp_profile_executer_start(script_name);
|
||||
|
||||
if (res != PL_RET_SUCCESS) {
|
||||
switch (res) {
|
||||
case PL_RET_DISK_ERR:
|
||||
shellmatta_printf(handle, "Error reading file\r\n");
|
||||
break;
|
||||
case PL_RET_LIST_FULL:
|
||||
shellmatta_printf(handle, "Script too long!\r\n");
|
||||
break;
|
||||
case PL_RET_SCRIPT_ERR:
|
||||
shellmatta_printf(handle, "Error in script\r\n");
|
||||
break;
|
||||
default:
|
||||
shellmatta_printf(handle, "Unspecified error occured\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return SHELLMATTA_ERROR;
|
||||
}
|
||||
running = true;
|
||||
} else {
|
||||
state = temp_profile_executer_status();
|
||||
if (state->status != TPE_RUNNING) {
|
||||
shellmatta_printf(handle, "Profile executed.\r\n");
|
||||
running = false;
|
||||
if(state->status == TPE_ABORT) {
|
||||
shellmatta_printf(handle, "Profile execution aborted!\r\n");
|
||||
}
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
|
||||
if (dlen > 0 && *data == '\x03') {
|
||||
temp_profile_executer_stop();
|
||||
running = false;
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return SHELLMATTA_CONTINUE;
|
||||
}
|
||||
|
||||
//typedef struct shellmatta_cmd
|
||||
//{
|
||||
// char *cmd; /**< command name */
|
||||
@@ -692,7 +799,7 @@ shellmatta_retCode_t shell_cmd_overtemp_cfg(const shellmatta_handle_t handle, co
|
||||
// shellmatta_cmdFct_t cmdFct; /**< pointer to the cmd callack function */
|
||||
// struct shellmatta_cmd *next; /**< pointer to next command or NULL */
|
||||
//} shellmatta_cmd_t;
|
||||
static shellmatta_cmd_t cmd[20] = {
|
||||
static shellmatta_cmd_t cmd[21] = {
|
||||
{
|
||||
.cmd = "version",
|
||||
.cmdAlias = "ver",
|
||||
@@ -792,7 +899,7 @@ static shellmatta_cmd_t cmd[20] = {
|
||||
{
|
||||
.cmd = "save-calibration",
|
||||
.cmdAlias = "save-cal",
|
||||
.helpText = "",
|
||||
.helpText = "Permanently save the calibration to EEPROM",
|
||||
.usageText = "",
|
||||
.cmdFct = shell_cmd_save_cal,
|
||||
.next = &cmd[13],
|
||||
@@ -840,8 +947,8 @@ static shellmatta_cmd_t cmd[20] = {
|
||||
{
|
||||
.cmd = "update",
|
||||
.cmdAlias = NULL,
|
||||
.helpText = "Update Firmware",
|
||||
.usageText = "",
|
||||
.helpText = "Update Firmware from HEX file",
|
||||
.usageText = "update </path/to/update.hex>",
|
||||
.cmdFct = shell_cmd_update,
|
||||
.next = &cmd[19],
|
||||
|
||||
@@ -852,6 +959,14 @@ static shellmatta_cmd_t cmd[20] = {
|
||||
.helpText = "Overtemperature Config",
|
||||
.usageText = "",
|
||||
.cmdFct = shell_cmd_overtemp_cfg,
|
||||
.next = &cmd[20],
|
||||
},
|
||||
{
|
||||
.cmd = "execute",
|
||||
.cmdAlias = NULL,
|
||||
.helpText = "Execute Temp Profile",
|
||||
.usageText = "execute /path/to/script.tpr",
|
||||
.cmdFct = shell_cmd_execute,
|
||||
.next = NULL,
|
||||
}
|
||||
};
|
||||
|
@@ -18,13 +18,13 @@
|
||||
* --------------------------------------------------------------------
|
||||
* FLASH: 512K
|
||||
* RAM: 128K
|
||||
* CCM RAM: 64L
|
||||
* CCM RAM: 64K
|
||||
* FPU: fpv4-sp-d16
|
||||
*/
|
||||
|
||||
/* USER PARAMETERS */
|
||||
__ld_stack_size = 0x3000;
|
||||
__ld_heap_size = 0x4200;
|
||||
__ld_stack_size = 0x3500;
|
||||
__ld_heap_size = 0x5000;
|
||||
__stack_corruption_area_size = 128;
|
||||
|
||||
/* END OF USER PARAMETERS */
|
||||
|
268
stm-firmware/temp-profile-executer.c
Normal file
268
stm-firmware/temp-profile-executer.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 Mario Hüttel <mario.huettel@gmx.net>
|
||||
*
|
||||
* This file is part of the Reflow Oven Controller Project.
|
||||
*
|
||||
* The reflow oven controller is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the reflow oven controller project.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <reflow-controller/temp-profile-executer.h>
|
||||
#include <reflow-controller/systick.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <reflow-controller/oven-driver.h>
|
||||
#include <reflow-controller/temp-converter.h>
|
||||
#include <reflow-controller/adc-meas.h>
|
||||
#include <reflow-controller/digio.h>
|
||||
|
||||
static struct tpe_current_state IN_SECTION(.ccm.data) state = {
|
||||
.status = TPE_OFF,
|
||||
.start_timestamp = 0,
|
||||
};
|
||||
static bool IN_SECTION(.ccm.bss) pid_should_run;
|
||||
struct pid_controller IN_SECTION(.ccm.bss) pid;
|
||||
bool IN_SECTION(.ccm.bss) cmd_continue;
|
||||
|
||||
static SlList *command_list = NULL;
|
||||
|
||||
static void tpe_abort(void)
|
||||
{
|
||||
temp_profile_executer_stop();
|
||||
state.status = TPE_ABORT;
|
||||
}
|
||||
|
||||
enum pl_ret_val temp_profile_executer_start(const char *filename)
|
||||
{
|
||||
uint32_t parsed_count = 0;
|
||||
enum pl_ret_val res;
|
||||
|
||||
state.setpoint = 0.0f;
|
||||
state.start_timestamp = 0ULL;
|
||||
state.setpoint = 0.0f;
|
||||
state.step = 0;
|
||||
state.profile_steps = 0;
|
||||
|
||||
oven_pid_stop();
|
||||
pid_should_run = false;
|
||||
state.status = TPE_OFF;
|
||||
state.profile_steps = 0;
|
||||
cmd_continue = false;
|
||||
|
||||
/* This should never happen... But who knows */
|
||||
if (command_list) {
|
||||
temp_profile_free_command_list(&command_list);
|
||||
}
|
||||
|
||||
res = temp_profile_parse_from_file(filename, &command_list, MAX_PROFILE_LENGTH, &parsed_count);
|
||||
if (res == PL_RET_SUCCESS) {
|
||||
state.profile_steps = parsed_count;
|
||||
state.status = TPE_RUNNING;
|
||||
state.start_timestamp = systick_get_global_tick();
|
||||
} else {
|
||||
if (command_list)
|
||||
temp_profile_free_command_list(&command_list);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool cmd_wait_temp(struct pl_command *cmd, bool cmd_continue)
|
||||
{
|
||||
(void)cmd_continue;
|
||||
float resistance;
|
||||
int res;
|
||||
float temp;
|
||||
|
||||
res = adc_pt1000_get_current_resistance(&resistance);
|
||||
if (res < 0) {
|
||||
tpe_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
(void)temp_converter_convert_resistance_to_temp(resistance, &temp);
|
||||
|
||||
if (ABS(cmd->params[0] - temp) < 3.0f)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cmd_wait_time(struct pl_command *cmd, bool cmd_continue)
|
||||
{
|
||||
static uint64_t temp_tick = 0UL;
|
||||
|
||||
if (cmd_continue) {
|
||||
if (systick_ticks_have_passed(temp_tick,
|
||||
(uint64_t)(cmd->params[0] * 1000.0f)))
|
||||
return true;
|
||||
} else {
|
||||
temp_tick = systick_get_global_tick();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void reactivate_pid_if_suspended(void)
|
||||
{
|
||||
if (oven_pid_get_status() == OVEN_PID_DEACTIVATED)
|
||||
oven_pid_init(&pid);
|
||||
|
||||
pid_should_run = true;
|
||||
}
|
||||
|
||||
static bool cmd_set_temp(struct pl_command *cmd, bool cmd_continue)
|
||||
{
|
||||
(void)cmd_continue;
|
||||
|
||||
reactivate_pid_if_suspended();
|
||||
oven_pid_set_target_temperature(cmd->params[0]);
|
||||
state.setpoint = cmd->params[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cmd_ramp(struct pl_command *cmd, bool cmd_continue)
|
||||
{
|
||||
static uint64_t IN_SECTION(.ccm.bss) start_timestamp;
|
||||
static float IN_SECTION(.ccm.bss) start_temp;
|
||||
static float IN_SECTION(.ccm.bss) slope;
|
||||
float secs_passed;
|
||||
bool ret = false;
|
||||
|
||||
if (!cmd_continue) {
|
||||
/* Init of command */
|
||||
start_temp = state.setpoint;
|
||||
slope = (cmd->params[0] - start_temp) / cmd->params[1];
|
||||
reactivate_pid_if_suspended();
|
||||
oven_pid_set_target_temperature(start_temp);
|
||||
start_timestamp = systick_get_global_tick();
|
||||
} else {
|
||||
secs_passed = ((float)(systick_get_global_tick() - start_timestamp)) / 1000.0f;
|
||||
if ((state.setpoint <= cmd->params[0] && start_temp < cmd->params[0]) ||
|
||||
(state.setpoint >= cmd->params[0] && start_temp > cmd->params[0])) {
|
||||
state.setpoint = start_temp + secs_passed * slope;
|
||||
} else {
|
||||
state.setpoint = cmd->params[0];
|
||||
ret = true;
|
||||
}
|
||||
oven_pid_set_target_temperature(state.setpoint);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int temp_profile_executer_handle(void)
|
||||
{
|
||||
struct pl_command *current_cmd;
|
||||
static uint64_t last_tick = 0UL;
|
||||
bool advance;
|
||||
|
||||
uint32_t next_step;
|
||||
|
||||
|
||||
/* Return if no profile is currently executed */
|
||||
if (state.status != TPE_RUNNING)
|
||||
return -1;
|
||||
|
||||
/* Abort profile execution if oven PID is aborted. This is most likely due to some error flags */
|
||||
if (oven_pid_get_status() == OVEN_PID_ABORTED && pid_should_run) {
|
||||
tpe_abort();
|
||||
oven_pid_stop();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Execute Temp Profile every 100 ms. If not yet due, return */
|
||||
if (!systick_ticks_have_passed(last_tick, 100))
|
||||
return 0;
|
||||
|
||||
current_cmd = (struct pl_command *)sl_list_nth(command_list, state.step)->data;
|
||||
next_step = state.step;
|
||||
|
||||
switch (current_cmd->cmd) {
|
||||
case PL_WAIT_FOR_TIME:
|
||||
advance = cmd_wait_time(current_cmd, cmd_continue);
|
||||
break;
|
||||
case PL_WAIT_FOR_TEMP:
|
||||
advance = cmd_wait_temp(current_cmd, cmd_continue);
|
||||
break;
|
||||
case PL_SET_TEMP:
|
||||
advance = cmd_set_temp(current_cmd, cmd_continue);
|
||||
break;
|
||||
case PL_LOUDSPEAKER_SET:
|
||||
loudspeaker_set((uint16_t)current_cmd->params[0]);
|
||||
advance = true;
|
||||
break;
|
||||
case PL_OFF:
|
||||
oven_pid_stop();
|
||||
pid_should_run = false;
|
||||
advance = true;
|
||||
break;
|
||||
case PL_PID_CONF:
|
||||
pid_init(&pid, current_cmd->params[0], /* Kd */
|
||||
current_cmd->params[1], /* Ki */
|
||||
current_cmd->params[2], /* Kp */
|
||||
0.0f, 0.0f,
|
||||
current_cmd->params[3], /* Int max */
|
||||
current_cmd->params[4], /* Kd tau */
|
||||
current_cmd->params[5]); /* Period */
|
||||
oven_pid_init(&pid);
|
||||
advance = true;
|
||||
pid_should_run = true;
|
||||
break;
|
||||
case PL_SET_RAMP:
|
||||
advance = cmd_ramp(current_cmd, cmd_continue);
|
||||
break;
|
||||
default:
|
||||
tpe_abort();
|
||||
advance = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (advance)
|
||||
next_step++;
|
||||
|
||||
if (next_step != state.step) {
|
||||
state.step = next_step;
|
||||
if (next_step >= state.profile_steps) {
|
||||
(void)temp_profile_executer_stop();
|
||||
} else {
|
||||
cmd_continue = false;
|
||||
}
|
||||
} else {
|
||||
cmd_continue = true;
|
||||
}
|
||||
last_tick = systick_get_global_tick();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct tpe_current_state *temp_profile_executer_status(void)
|
||||
{
|
||||
return &state;
|
||||
}
|
||||
|
||||
int temp_profile_executer_stop(void)
|
||||
{
|
||||
if (state.status == TPE_RUNNING) {
|
||||
state.status = TPE_OFF;
|
||||
oven_pid_stop();
|
||||
}
|
||||
|
||||
/* Free the command list */
|
||||
if (command_list)
|
||||
temp_profile_free_command_list(&command_list);
|
||||
|
||||
loudspeaker_set(0);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -28,6 +28,7 @@
|
||||
#include <reflow-controller/safety/safety-controller.h>
|
||||
#include <reflow-controller/settings/settings.h>
|
||||
#include <reflow-controller/temp-converter.h>
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/unique-id.h>
|
||||
#include <stddef.h>
|
||||
@@ -36,6 +37,10 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <reflow-controller/oven-driver.h>
|
||||
#include <fatfs/ff.h>
|
||||
#include <reflow-controller/temp-profile-executer.h>
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static char IN_SECTION(.ccm.bss) display_buffer[4][21] = {0};
|
||||
static struct lcd_menu IN_SECTION(.ccm.bss) reflow_menu;
|
||||
@@ -198,7 +203,7 @@ static void gui_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry ent
|
||||
uptime_secs = new_uptime_secs;
|
||||
menu_lcd_output(menu, 0, "Uptime:");
|
||||
menu_lcd_outputf(menu, 1, "%lu day%s %02lu:%02lu:%02lu",
|
||||
uptime_days, (uptime_days == 1 ? "" : "s"), uptime_hours, uptime_mins, uptime_secs);
|
||||
uptime_days, (uptime_days == 1 ? "" : "s"), uptime_hours, uptime_mins, uptime_secs);
|
||||
menu_lcd_output(menu, 3, "Page 5/5");
|
||||
}
|
||||
break;
|
||||
@@ -215,7 +220,7 @@ static void gui_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry ent
|
||||
button_ready = true;
|
||||
|
||||
if (button_ready &&
|
||||
(push_button == BUTTON_SHORT_RELEASED || push_button == BUTTON_LONG)) {
|
||||
(push_button == BUTTON_SHORT_RELEASED || push_button == BUTTON_LONG)) {
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
}
|
||||
@@ -372,6 +377,204 @@ static void gui_menu_constant_temperature_driver(struct lcd_menu *menu, enum men
|
||||
|
||||
}
|
||||
|
||||
static void delete_file_list_entry(void *obj)
|
||||
{
|
||||
if (obj)
|
||||
free(obj);
|
||||
}
|
||||
|
||||
static SlList *load_file_list_from_sdcard(int *error, const char *file_pattern)
|
||||
{
|
||||
DIR directory;
|
||||
FILINFO finfo;
|
||||
FRESULT fres;
|
||||
|
||||
SlList *list = NULL;
|
||||
char *name;
|
||||
|
||||
/* find the frist file */
|
||||
fres = f_findfirst(&directory, &finfo, "/", file_pattern);
|
||||
while (fres == FR_OK && finfo.fname[0]) {
|
||||
name = malloc(strlen(finfo.fname) + 1);
|
||||
strcpy(name, finfo.fname);
|
||||
list = sl_list_append(list, name);
|
||||
fres = f_findnext(&directory, &finfo);
|
||||
}
|
||||
|
||||
if (fres != FR_OK) {
|
||||
sl_list_free_full(list, delete_file_list_entry);
|
||||
list = NULL;
|
||||
if (error)
|
||||
*error = -1;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void gui_menu_temp_profile_execute(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void* parent)
|
||||
{
|
||||
static void *my_parent;
|
||||
const struct tpe_current_state *state;
|
||||
static uint64_t last_tick;
|
||||
float temperature;
|
||||
float resistance;
|
||||
int res;
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
|
||||
my_parent = parent;
|
||||
menu_display_clear(menu);
|
||||
last_tick = 0ULL;
|
||||
}
|
||||
|
||||
if (systick_ticks_have_passed(last_tick, 250)) {
|
||||
state = temp_profile_executer_status();
|
||||
if (state->status == TPE_RUNNING) {
|
||||
menu_lcd_outputf(menu, 0, "Executing...");
|
||||
menu_lcd_outputf(menu, 1, "Step %u/%u", state->step, state->profile_steps);
|
||||
(void)adc_pt1000_get_current_resistance(&resistance);
|
||||
res = temp_converter_convert_resistance_to_temp(resistance, &temperature);
|
||||
menu_lcd_outputf(menu, 2, "Temp: %s%.1f " LCD_DEGREE_SYMBOL_STRING "C",
|
||||
(res < 0 ? "<" : (res > 0 ? ">" : "")), temperature);
|
||||
if (oven_pid_get_status() == OVEN_PID_RUNNING) {
|
||||
menu_lcd_outputf(menu, 3, "Target: %.0f " LCD_DEGREE_SYMBOL_STRING "C", state->setpoint);
|
||||
} else {
|
||||
menu_lcd_outputf(menu, 3, "Temp Off");
|
||||
}
|
||||
} else if (state->status == TPE_OFF) {
|
||||
menu_lcd_outputf(menu, 0, "Finished!");
|
||||
menu_lcd_outputf(menu, 1, "Press button");
|
||||
menu_lcd_outputf(menu, 2, "to return.");
|
||||
(void)adc_pt1000_get_current_resistance(&resistance);
|
||||
res = temp_converter_convert_resistance_to_temp(resistance, &temperature);
|
||||
menu_lcd_outputf(menu, 3, "Temp: %.1f ", LCD_DEGREE_SYMBOL_STRING "C", temperature);
|
||||
} else {
|
||||
menu_lcd_outputf(menu, 0, "Profile aborted!");
|
||||
menu_lcd_outputf(menu, 1, "Check flags!");
|
||||
menu_lcd_outputf(menu, 2, "");
|
||||
menu_lcd_outputf(menu, 3, "Press button");
|
||||
}
|
||||
|
||||
last_tick = systick_get_global_tick();
|
||||
}
|
||||
|
||||
if (menu_get_button_ready_state(menu)) {
|
||||
if (menu_get_button_state(menu) != BUTTON_IDLE) {
|
||||
temp_profile_executer_stop();
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gui_menu_temp_profile_select(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
static void *my_parent;
|
||||
static SlList *file_list = NULL;
|
||||
static int file_error = 0;
|
||||
static enum pl_ret_val profile_ret_val = PL_RET_SUCCESS;
|
||||
static uint8_t currently_selected = 0U;
|
||||
static uint8_t loaded;
|
||||
int16_t delta;
|
||||
enum button_state button;
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
|
||||
menu_display_clear(menu);
|
||||
my_parent = parent;
|
||||
file_error = 0;
|
||||
loaded = 0;
|
||||
|
||||
if (file_list) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
}
|
||||
|
||||
file_list = load_file_list_from_sdcard(&file_error, "*.tpr");
|
||||
|
||||
currently_selected = 0u;
|
||||
profile_ret_val = PL_RET_SUCCESS;
|
||||
loaded = sl_list_length(file_list);
|
||||
menu_lcd_outputf(menu, 0, "Select:");
|
||||
} else if (entry_type == MENU_ENTRY_DROPBACK) {
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (menu_get_button_ready_state(menu)) {
|
||||
delta = menu_get_rotary_delta(menu);
|
||||
button = menu_get_button_state(menu);
|
||||
|
||||
if (button == BUTTON_LONG) {
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
|
||||
if (file_error) {
|
||||
menu_lcd_outputf(menu, 0, "Disk Error");
|
||||
menu_lcd_outputf(menu, 1, "SD inserted?");
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
return;
|
||||
} else if (loaded == 0) {
|
||||
menu_lcd_outputf(menu, 0, "No profiles");
|
||||
menu_lcd_outputf(menu, 1, "found");
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
return;
|
||||
} else if (profile_ret_val != PL_RET_SUCCESS) {
|
||||
menu_lcd_outputf(menu, 0, "ERROR");
|
||||
switch (profile_ret_val) {
|
||||
case PL_RET_SCRIPT_ERR:
|
||||
menu_lcd_outputf(menu, 1, "Syntax Error");
|
||||
break;
|
||||
case PL_RET_DISK_ERR:
|
||||
menu_lcd_outputf(menu, 1, "Disk Error");
|
||||
break;
|
||||
case PL_RET_LIST_FULL:
|
||||
menu_lcd_output(menu, 1, "Too many com-");
|
||||
menu_lcd_output(menu, 2, "mands in file");
|
||||
break;
|
||||
default:
|
||||
menu_lcd_output(menu, 1, "Unknown error");
|
||||
break;
|
||||
}
|
||||
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
return;
|
||||
} else if (currently_selected < loaded) {
|
||||
/* Show currently selected profile */
|
||||
menu_lcd_outputf(menu, 1, "%s", sl_list_nth(file_list, currently_selected)->data);
|
||||
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
/* Execute selected profile */
|
||||
profile_ret_val = temp_profile_executer_start(sl_list_nth(file_list, currently_selected)->data);
|
||||
if (profile_ret_val == PL_RET_SUCCESS) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_enter(menu, gui_menu_temp_profile_execute, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (delta >= 4) {
|
||||
menu_ack_rotary_delta(menu);
|
||||
if (currently_selected < (loaded - 1))
|
||||
currently_selected++;
|
||||
} else if (delta <= -4) {
|
||||
menu_ack_rotary_delta(menu);
|
||||
if (currently_selected > 0)
|
||||
currently_selected--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gui_menu_constant_temperature_driver_setup(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
static void IN_SECTION(.ccm.bss) *my_parent;
|
||||
@@ -405,6 +608,121 @@ static void gui_menu_constant_temperature_driver_setup(struct lcd_menu *menu, en
|
||||
}
|
||||
}
|
||||
|
||||
static void gui_update_firmware(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
static void *my_parent;
|
||||
static SlList *file_list = NULL;
|
||||
static int error;
|
||||
enum button_state button;
|
||||
static uint32_t currently_selected_file;
|
||||
uint32_t previously_selected_file;
|
||||
static uint32_t list_length;
|
||||
const char *fname;
|
||||
int16_t rotary;
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
|
||||
my_parent = parent;
|
||||
error = 0;
|
||||
list_length = 0;
|
||||
if (file_list)
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = load_file_list_from_sdcard(&error, "*.hex");
|
||||
if (error) {
|
||||
if (file_list)
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
} else {
|
||||
list_length = sl_list_length(file_list);
|
||||
}
|
||||
currently_selected_file = 0;
|
||||
menu_display_clear(menu);
|
||||
}
|
||||
|
||||
if (menu_get_button_ready_state(menu)) {
|
||||
button = menu_get_button_state(menu);
|
||||
rotary = menu_get_rotary_delta(menu);
|
||||
|
||||
if (error) {
|
||||
menu_lcd_output(menu, 0, "Error reading");
|
||||
menu_lcd_output(menu, 1, "file list");
|
||||
if (button == BUTTON_LONG || button == BUTTON_SHORT_RELEASED)
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
} else {
|
||||
previously_selected_file = currently_selected_file;
|
||||
|
||||
/* Display the list */
|
||||
if (rotary <= -4) {
|
||||
menu_ack_rotary_delta(menu);
|
||||
if (currently_selected_file > 0) {
|
||||
currently_selected_file--;
|
||||
}
|
||||
} else if (rotary >= 4) {
|
||||
menu_ack_rotary_delta(menu);
|
||||
if (currently_selected_file < (list_length - 1)) {
|
||||
currently_selected_file++;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER ||
|
||||
previously_selected_file != currently_selected_file) {
|
||||
fname = sl_list_nth(file_list, currently_selected_file)->data;
|
||||
menu_lcd_output(menu, 0, "Select File:");
|
||||
if (fname)
|
||||
menu_lcd_output(menu, 1, fname);
|
||||
}
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
fname = sl_list_nth(file_list, currently_selected_file)->data;
|
||||
menu_display_clear(menu);
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
|
||||
updater_update_from_file(fname);
|
||||
} else if (button == BUTTON_LONG) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *overlay_heading = NULL;
|
||||
static char *overlay_text = NULL;
|
||||
|
||||
static void gui_menu_overlay_entry(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
static void *my_parent;
|
||||
enum button_state button;
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
|
||||
my_parent = parent;
|
||||
menu_display_clear(menu);
|
||||
if (overlay_heading)
|
||||
menu_lcd_output(menu, 0, overlay_heading);
|
||||
if (overlay_text) {
|
||||
menu_lcd_output(menu, 2, overlay_text);
|
||||
if (strlen(overlay_text) > 16) {
|
||||
menu_lcd_output(menu, 3, &overlay_text[16]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (menu_get_button_ready_state(menu)) {
|
||||
button = menu_get_button_state(menu);
|
||||
menu_ack_rotary_delta(menu);
|
||||
|
||||
if (button != BUTTON_IDLE) {
|
||||
if (overlay_heading)
|
||||
free(overlay_heading);
|
||||
if (overlay_text)
|
||||
free(overlay_text);
|
||||
overlay_heading = NULL;
|
||||
overlay_text = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
(void)parent;
|
||||
@@ -412,16 +730,20 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
|
||||
bool menu_changed = false;
|
||||
static const char * const root_entry_names[] = {
|
||||
"Constant Temp",
|
||||
"Temp Profile",
|
||||
"Monitoring",
|
||||
"Error Flags",
|
||||
"About",
|
||||
"Update",
|
||||
NULL
|
||||
};
|
||||
static const menu_func_t root_entry_funcs[] = {
|
||||
gui_menu_constant_temperature_driver_setup,
|
||||
gui_menu_temp_profile_select,
|
||||
gui_menu_monitor,
|
||||
gui_menu_err_flags,
|
||||
gui_menu_about,
|
||||
gui_update_firmware,
|
||||
};
|
||||
enum button_state push_button;
|
||||
int16_t rot_delta;
|
||||
@@ -443,23 +765,30 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
|
||||
push_button = menu_get_button_state(menu);
|
||||
rot_delta = menu_get_rotary_delta(menu);
|
||||
|
||||
if (menu_get_button_ready_state(menu) && push_button == BUTTON_SHORT_RELEASED) {
|
||||
/* Enter currently selected menu_entry */
|
||||
menu_list_enter_selected_entry(&list, menu);
|
||||
if (menu_get_button_ready_state(menu)) {
|
||||
if (menu_get_button_ready_state(menu) && push_button == BUTTON_SHORT_RELEASED) {
|
||||
/* Enter currently selected menu_entry */
|
||||
menu_list_enter_selected_entry(&list, menu);
|
||||
}
|
||||
|
||||
if (rot_delta >= 4) {
|
||||
menu_list_scroll_down(&list);
|
||||
menu_ack_rotary_delta(menu);
|
||||
menu_changed = true;
|
||||
} else if (rot_delta <= -4) {
|
||||
menu_list_scroll_up(&list);
|
||||
menu_ack_rotary_delta(menu);
|
||||
menu_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rot_delta >= 4) {
|
||||
menu_list_scroll_down(&list);
|
||||
menu_ack_rotary_delta(menu);
|
||||
menu_changed = true;
|
||||
} else if (rot_delta <= -4) {
|
||||
menu_list_scroll_up(&list);
|
||||
menu_ack_rotary_delta(menu);
|
||||
menu_changed = true;
|
||||
}
|
||||
|
||||
if (menu_changed)
|
||||
/* Display the message overlay in case it is set */
|
||||
if (overlay_heading || overlay_text) {
|
||||
menu_entry_enter(menu, gui_menu_overlay_entry, true);
|
||||
return;
|
||||
} else if (menu_changed) {
|
||||
menu_list_display(&list, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
int gui_handle()
|
||||
@@ -490,5 +819,37 @@ void gui_init()
|
||||
button_init();
|
||||
lcd_init();
|
||||
|
||||
if (overlay_heading)
|
||||
free(overlay_heading);
|
||||
if (overlay_text)
|
||||
free(overlay_text);
|
||||
|
||||
overlay_heading = NULL;
|
||||
overlay_text = NULL;
|
||||
|
||||
menu_init(reflow_menu_ptr, gui_menu_root_entry, update_display_buffer);
|
||||
}
|
||||
|
||||
void gui_root_menu_message_set(const char *heading, const char *text)
|
||||
{
|
||||
if (heading) {
|
||||
overlay_heading = (char *)malloc(strlen(heading) + 1);
|
||||
strcpy(overlay_heading, heading);
|
||||
}
|
||||
|
||||
if (text) {
|
||||
overlay_text = (char *)malloc(strlen(text) + 1);
|
||||
strcpy(overlay_text, text);
|
||||
}
|
||||
}
|
||||
|
||||
void gui_lcd_write_direct_blocking(uint8_t line, const char *text)
|
||||
{
|
||||
if (!text)
|
||||
return;
|
||||
if (line > 3)
|
||||
return;
|
||||
|
||||
lcd_setcursor(0, line);
|
||||
lcd_string(text);
|
||||
}
|
||||
|
@@ -39,7 +39,8 @@ void menu_handle(struct lcd_menu *menu, int16_t rotary_encoder_delta, enum butto
|
||||
|
||||
tmp = menu->active_entry;
|
||||
|
||||
if (menu->active_entry_type == MENU_ENTRY_FIRST_ENTER && push_button != BUTTON_IDLE) {
|
||||
if ((menu->active_entry_type == MENU_ENTRY_FIRST_ENTER || menu->active_entry_type == MENU_ENTRY_DROPBACK)
|
||||
&& push_button != BUTTON_IDLE) {
|
||||
menu->inputs.button_ready = false;
|
||||
}
|
||||
|
||||
@@ -273,3 +274,10 @@ void menu_display_clear(struct lcd_menu *menu)
|
||||
for (i = 0; i < 4; i++)
|
||||
menu->update_display(i, "");
|
||||
}
|
||||
|
||||
uint32_t menu_list_get_currently_selected(struct menu_list *list)
|
||||
{
|
||||
if (!list)
|
||||
return 0;
|
||||
return list->currently_selected;
|
||||
}
|
||||
|
48
stm-firmware/updater/ram-code/CMakeLists.txt
Normal file
48
stm-firmware/updater/ram-code/CMakeLists.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
project(updater-ram-code)
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
set(CMAKE_CROSSCOMPILING 1)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE "arm-none-eabi-gcc.cmake")
|
||||
|
||||
set(ELFFILE ${PROJECT_NAME}.elf)
|
||||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/ram-link.ld)
|
||||
|
||||
set(ELFFILE "${PROJECT_NAME}.elf")
|
||||
aux_source_directory("." SRCS)
|
||||
aux_source_directory("fatfs" FATFS_SRCS)
|
||||
aux_source_directory("fatfs/shimatta_sdio_driver" SDIO_SRCS)
|
||||
set(STM_PERIPH_SRCS "../../stm-periph/backup-ram.c" "../../stm-periph/rcc-manager.c" "../../stm-periph/crc-unit.c")
|
||||
set(SAFETY_MEMORY_SRCS "../../safety/safety-memory.c")
|
||||
|
||||
add_executable(${ELFFILE} ${SRCS} ${FATFS_SRCS} ${SDIO_SRCS} ${STM_PERIPH_SRCS} ${SAFETY_MEMORY_SRCS})
|
||||
|
||||
target_include_directories(${ELFFILE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ../../include)
|
||||
target_compile_options(${ELFFILE} PRIVATE -Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter)
|
||||
target_compile_options(${ELFFILE} PRIVATE -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles -Wimplicit-fallthrough=3 -Wsign-compare -Os -g3)
|
||||
target_compile_definitions(${ELFFILE} PRIVATE -DGIT_VER=${GIT_DESCRIBE} -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4 -DSAFETY_MEMORY_STRIPOUT_DUMP)
|
||||
target_link_options(${ELFFILE} PRIVATE -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles -T${LINKER_SCRIPT} -Wl,--print-memory-usage)
|
||||
set(GEN_HEADER_PATH "${CMAKE_CURRENT_BINARY_DIR}/include/generated")
|
||||
set(GEN_HEADER_FILE "${GEN_HEADER_PATH}/${PROJECT_NAME}.bin.h")
|
||||
target_include_directories(${ELFFILE} INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include/")
|
||||
|
||||
set(GEN_HEADER_FILE "${CMAKE_CURRENT_BINARY_DIR}/include/generated/${PROJECT_NAME}.bin.h")
|
||||
|
||||
add_custom_target(updater-ram-code-header-blob DEPENDS ${GEN_HEADER_FILE})
|
||||
|
||||
add_custom_command(DEPENDS
|
||||
${ELFFILE}
|
||||
OUTPUT
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin"
|
||||
COMMAND
|
||||
${CMAKE_OBJCOPY} -O binary ${ELFFILE} "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin"
|
||||
)
|
||||
|
||||
add_custom_command(DEPENDS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin"
|
||||
OUTPUT
|
||||
${GEN_HEADER_FILE}
|
||||
COMMAND
|
||||
mkdir -p ${GEN_HEADER_PATH} && python "${CMAKE_CURRENT_SOURCE_DIR}/bin2carray.py" "${GEN_HEADER_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin"
|
||||
)
|
@@ -1,82 +0,0 @@
|
||||
RAM_CODE_TARGET = updater-ram-code
|
||||
target = $(RAM_CODE_TARGET)
|
||||
OBJDIR = obj
|
||||
CFILES = main.c startup.c hex-parser.c
|
||||
CFILES += fatfs/ff.c fatfs/diskio.c fatfs/ffsystem.c fatfs/ffunicode.c fatfs/shimatta_sdio_driver/shimatta_sdio.c
|
||||
LINKER_SCRIPT = ram-link.ld
|
||||
MAPFILE = $(RAM_CODE_TARGET)
|
||||
PREFIX = arm-none-eabi-
|
||||
|
||||
CC = $(PREFIX)gcc
|
||||
OBJCOPY = $(PREFIX)objcopy
|
||||
SIZE = $(PREFIX)size
|
||||
|
||||
ifneq ($(VERBOSE),true)
|
||||
QUIET=@
|
||||
else
|
||||
QUIET=
|
||||
endif
|
||||
|
||||
|
||||
DEFINES = -DSTM32F407xx -DSTM32F4XX -DHSE_VALUE=8000000UL
|
||||
INCLUDEPATH = -Iinclude
|
||||
|
||||
LFLAGS = -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
|
||||
LFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles
|
||||
LFLAGS += -T$(LINKER_SCRIPT) -Wl,-Map=$(MAPFILE).map -Wl,--print-memory-usage -g3
|
||||
|
||||
CFLAGS = -c -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Os -g3
|
||||
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles
|
||||
CFLAGS += -Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter -Wimplicit-fallthrough=3 -Wsign-compare
|
||||
|
||||
OBJ = $(CFILES:%.c=$(OBJDIR)/%.c.o)
|
||||
|
||||
default: $(RAM_CODE_TARGET).bin.h
|
||||
|
||||
all: $(RAM_CODE_TARGET).bin.h
|
||||
|
||||
%.bin.h: %.bin
|
||||
@echo "[BIN2H] $@"
|
||||
$(QUIET)python bin2carray.py $@ $^
|
||||
|
||||
$(RAM_CODE_TARGET).bin: $(RAM_CODE_TARGET).elf
|
||||
@echo "[ELF2BIN] $@"
|
||||
$(QUIET)$(OBJCOPY) -O binary $^ $@
|
||||
|
||||
$(RAM_CODE_TARGET).elf: $(OBJ) $(LINKER_SCRIPT)
|
||||
@echo [LD] $@
|
||||
$(QUIET)$(CC) $(LFLAGS) $(LIBRARYPATH) -o $@ $(OBJ) $(ASOBJ) $(LIBRARIES)
|
||||
$(QUIET)$(SIZE) $@
|
||||
|
||||
$(OBJ):
|
||||
@echo [CC] $@
|
||||
$(eval OUTPATH=$(dir $@))
|
||||
@mkdir -p $(OUTPATH)
|
||||
$(QUIET)$(CC) $(CFLAGS) -MMD -MT $@ $(INCLUDEPATH) $(DEFINES) -o $@ $(@:$(OBJDIR)/%.c.o=%.c)
|
||||
|
||||
.PHONY: clean qtproject
|
||||
|
||||
clean:
|
||||
@echo [CLEAN]
|
||||
$(QUIET)rm -f $(OBJ) $(MAPFILE).map $(CFILES:%.c=$(OBJDIR)/%.c.d) $(RAM_CODE_TARGET).bin $(RAM_CODE_TARGET).elf $(RAM_CODE_TARGET).bin.c
|
||||
|
||||
qtproject:
|
||||
$(QUIET)rm -f $(target).files $(target).cflags $(target).config $(target).creator $(target).includes $(target).creator.user
|
||||
@echo "Generating source file list"
|
||||
$(QUIET)echo "$(CFILES)" | tr ' ' '\n' > $(target).files
|
||||
@echo -n "Appending found header files from folders "
|
||||
@echo `echo $(INCLUDEPATH) | sed "s/-I//g"`
|
||||
$(QUIET)for dir in `echo $(INCLUDEPATH) | sed "s/-I//g"`; do \
|
||||
find `echo "$${dir}"` -name "*.h" >> $(target).files; \
|
||||
done
|
||||
@echo "Generating $(target).cflags"
|
||||
$(QUIET)echo "" > $(target).cflags
|
||||
@echo "Generating $(target).includes"
|
||||
$(QUIET)echo $(INCLUDEPATH) | sed "s/-I/,/g" | tr , '\n' | sed "/^$$/d" > $(target).includes;
|
||||
@echo "Generating $(target).config"
|
||||
$(QUIET)echo $(DEFINES) | sed "s/-D/,#define /g" | tr , '\n' | sed "/^$$/d" > $(target).config
|
||||
@echo "Generating $(target).creator"
|
||||
$(QUIET)echo "[GENERAL]" > $(target).creator
|
||||
|
||||
-include $(CFILES:%.c=$(OBJDIR)/%.c.d)
|
||||
|
@@ -4,6 +4,7 @@
|
||||
# bin2carray <output file> <input file>
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
@@ -18,7 +19,7 @@ with open(source_file, "rb") as src:
|
||||
data = src.read()
|
||||
|
||||
with open(dest_file, "w") as dest:
|
||||
header_guard = "__" + dest_file.replace('.', '_').replace('-', '_') + "_H__"
|
||||
header_guard = "__" + os.path.basename(dest_file).replace('.', '_').replace('-', '_') + "_H__"
|
||||
dest.write("#ifndef %s\n" % (header_guard))
|
||||
dest.write("#define %s\n" % (header_guard))
|
||||
dest.write("static const char binary_blob[%d] = {\n" % (len(data)))
|
||||
|
134
stm-firmware/updater/ram-code/flash-writer.c
Normal file
134
stm-firmware/updater/ram-code/flash-writer.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "flash-writer.h"
|
||||
#include <stm32/stm32f4xx.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static bool flash_op_busy(void)
|
||||
{
|
||||
return !!(FLASH->SR & FLASH_SR_BSY);
|
||||
}
|
||||
|
||||
static void lock_flash_cr(void)
|
||||
{
|
||||
FLASH->CR |= FLASH_CR_LOCK;
|
||||
}
|
||||
|
||||
void flash_writer_enable_access(void)
|
||||
{
|
||||
const uint32_t key1 = 0x45670123UL;
|
||||
const uint32_t key2 = 0xCDEF89ABUL;
|
||||
|
||||
FLASH->KEYR = key1;
|
||||
FLASH->KEYR = key2;
|
||||
}
|
||||
|
||||
void flash_writer_perform_mass_erase(void)
|
||||
{
|
||||
flash_writer_enable_access();
|
||||
while (flash_op_busy());
|
||||
|
||||
FLASH->CR = DMA_SxCR_PSIZE_1;
|
||||
FLASH->CR |= FLASH_CR_MER;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
|
||||
while(flash_op_busy());
|
||||
lock_flash_cr();
|
||||
}
|
||||
|
||||
uint32_t flash_writer_get_flash_size(void)
|
||||
{
|
||||
uint32_t flash_size;
|
||||
const uint16_t *flash_size_ptr = (const uint16_t *)0x1FFF7A22UL;
|
||||
|
||||
flash_size = (uint32_t)*flash_size_ptr;
|
||||
flash_size *= 1024;
|
||||
|
||||
return flash_size;
|
||||
}
|
||||
|
||||
int flash_writer_write_to_memory(void *dest, const void *src, uint32_t size)
|
||||
{
|
||||
uint32_t full_word_cnt;
|
||||
uint32_t byte_cnt;
|
||||
uint32_t idx;
|
||||
const uint32_t *word_src_ptr;
|
||||
uint32_t *word_dest_ptr;
|
||||
const char *char_src_ptr;
|
||||
char *char_dest_ptr;
|
||||
uint32_t pre_byte_count;
|
||||
|
||||
flash_writer_enable_access();
|
||||
while (flash_op_busy());
|
||||
|
||||
/* Number of full words to program */
|
||||
full_word_cnt = size / 4u;
|
||||
byte_cnt = size % 4;
|
||||
|
||||
word_dest_ptr = dest;
|
||||
word_src_ptr = src;
|
||||
|
||||
/* Do the first bytes, in case the destination is not word aligned */
|
||||
pre_byte_count = (4 - ((uint32_t)dest % 4u)) % 4;
|
||||
if (pre_byte_count) {
|
||||
FLASH->CR = 0u;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
char_src_ptr = src;
|
||||
char_dest_ptr = dest;
|
||||
|
||||
/* Write bytes to memory until we hit the next word aligned address */
|
||||
for (idx = 0; idx < pre_byte_count; idx++) {
|
||||
*(char_dest_ptr++) = *(char_src_ptr++);
|
||||
}
|
||||
|
||||
/* Correct the word addresses set above */
|
||||
full_word_cnt = (size - pre_byte_count) / 4u;
|
||||
byte_cnt = (size - pre_byte_count) % 4;
|
||||
word_dest_ptr = (uint32_t *)char_dest_ptr;
|
||||
word_src_ptr = (uint32_t *)char_src_ptr;
|
||||
|
||||
while(flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
/* Do the full word flash write */
|
||||
if (full_word_cnt) {
|
||||
FLASH->CR = FLASH_CR_PSIZE_1;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
for (idx = 0; idx < full_word_cnt; idx++) {
|
||||
*word_dest_ptr = *word_src_ptr;
|
||||
word_dest_ptr++;
|
||||
word_src_ptr++;
|
||||
}
|
||||
|
||||
while (flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
/* write remaining bytes */
|
||||
if (byte_cnt) {
|
||||
char_src_ptr = (char *)word_src_ptr;
|
||||
char_dest_ptr = (char *)word_dest_ptr;
|
||||
|
||||
FLASH->CR = 0u;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
for (idx = 0; idx < byte_cnt; idx++) {
|
||||
*char_dest_ptr = *char_src_ptr;
|
||||
char_dest_ptr++;
|
||||
char_src_ptr++;
|
||||
}
|
||||
|
||||
while (flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
lock_flash_cr();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t flash_writer_get_base_address(void)
|
||||
{
|
||||
return FLASH_BASE;
|
||||
}
|
16
stm-firmware/updater/ram-code/flash-writer.h
Normal file
16
stm-firmware/updater/ram-code/flash-writer.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _FLASH_WRITER_H_
|
||||
#define _FLASH_WRITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void flash_writer_enable_access(void);
|
||||
|
||||
void flash_writer_perform_mass_erase(void);
|
||||
|
||||
uint32_t flash_writer_get_flash_size(void);
|
||||
|
||||
uint32_t flash_writer_get_base_address(void);
|
||||
|
||||
int flash_writer_write_to_memory(void *dest, const void *src, uint32_t size);
|
||||
|
||||
#endif /* _FLASH_WRITER_H_ */
|
@@ -1,72 +1,74 @@
|
||||
#include "hex-parser.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
static int convert_hex_char_to_value(char c, uint32_t *out)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t value = 0;
|
||||
int ret = 0;
|
||||
uint32_t value = 0;
|
||||
|
||||
if (!out)
|
||||
return -1002;
|
||||
if (!out)
|
||||
return -1002;
|
||||
|
||||
switch (c) {
|
||||
case '0' ... '9':
|
||||
value = (uint32_t)c - (uint32_t)'0';
|
||||
break;
|
||||
case 'a' ... 'f':
|
||||
/* Convert to upper */
|
||||
c -= 0x20;
|
||||
/* FALLTHRU */
|
||||
case 'A' ... 'F':
|
||||
value = (uint32_t)c - (uint32_t)'A' + 10UL;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
switch (c) {
|
||||
case '0' ... '9':
|
||||
value = (uint32_t)c - (uint32_t)'0';
|
||||
break;
|
||||
case 'a' ... 'f':
|
||||
/* Convert to upper */
|
||||
c -= 0x20;
|
||||
/* FALLTHRU */
|
||||
case 'A' ... 'F':
|
||||
value = (uint32_t)c - (uint32_t)'A' + 10UL;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
*out = value;
|
||||
if (ret == 0)
|
||||
*out = value;
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int convert_big_endian_hex_string_to_val(const char *string, size_t len, uint32_t *out)
|
||||
{
|
||||
int ret_val = -1;
|
||||
uint32_t converted_value = 0UL;
|
||||
uint32_t digit;
|
||||
int res;
|
||||
unsigned int i;
|
||||
int ret_val = -1;
|
||||
uint32_t converted_value = 0UL;
|
||||
uint32_t digit;
|
||||
int res;
|
||||
unsigned int i;
|
||||
|
||||
/* Return error in case of an input error */
|
||||
if (!string || !len)
|
||||
goto exit;
|
||||
/* Return error in case of an input error */
|
||||
if (!string || !len)
|
||||
goto exit;
|
||||
|
||||
if (!out)
|
||||
return -1003;
|
||||
if (!out)
|
||||
return -1003;
|
||||
|
||||
/* we don't support strings larger than 8 chars */
|
||||
if (len > 8)
|
||||
goto exit;
|
||||
/* we don't support strings larger than 8 chars */
|
||||
if (len > 8)
|
||||
goto exit;
|
||||
|
||||
|
||||
for (i = 0; i < len && string[i] != '\0'; i++) {
|
||||
/* Convert current character to number */
|
||||
res = convert_hex_char_to_value(string[i], &digit);
|
||||
if (res) {
|
||||
/* Not a hex number */
|
||||
ret_val = -2;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < len && string[i] != '\0'; i++) {
|
||||
/* Convert current character to number */
|
||||
res = convert_hex_char_to_value(string[i], &digit);
|
||||
if (res) {
|
||||
/* Not a hex number */
|
||||
ret_val = -2;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
converted_value *= 0x10;
|
||||
converted_value += digit;
|
||||
}
|
||||
converted_value *= 0x10;
|
||||
converted_value += digit;
|
||||
}
|
||||
|
||||
*out = converted_value;
|
||||
*out = converted_value;
|
||||
ret_val = 0;
|
||||
|
||||
exit:
|
||||
return ret_val;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_name)
|
||||
@@ -85,7 +87,179 @@ enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_
|
||||
return HEX_PARSER_OK;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len);
|
||||
static int read_line_from_file(FIL *file, char *data, int size)
|
||||
{
|
||||
char *ret_ptr;
|
||||
int length;
|
||||
|
||||
ret_ptr = f_gets(data, size, file);
|
||||
if (!ret_ptr)
|
||||
return -1;
|
||||
|
||||
/* To be sure */
|
||||
data[size - 1] = 0;
|
||||
length = strlen(ret_ptr);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static int hex_record_check_checksum(const char *buff, int hex_byte_count)
|
||||
{
|
||||
int i;
|
||||
int res;
|
||||
uint32_t checksum = 0;
|
||||
uint32_t tmp;
|
||||
|
||||
if (!buff || !hex_byte_count)
|
||||
return -1000;
|
||||
|
||||
for (i = 0; i < hex_byte_count; i++) {
|
||||
res = convert_big_endian_hex_string_to_val(&buff[2 * i], 2, &tmp);
|
||||
if (res)
|
||||
return -1;
|
||||
checksum += tmp;
|
||||
}
|
||||
|
||||
if (checksum & 0xFF) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len,
|
||||
size_t *lenout)
|
||||
{
|
||||
static char workbuff[512];
|
||||
int count;
|
||||
int i;
|
||||
enum hex_parser_ret retval = HEX_PARSER_DATA_OK;
|
||||
uint32_t hex_addr;
|
||||
uint32_t byte_count;
|
||||
uint32_t record_type;
|
||||
uint32_t tmp;
|
||||
|
||||
if (!parser || !lenout || !data_len || !data || !address)
|
||||
return HEX_PARSER_ERROR;
|
||||
|
||||
/* Read a line from the file */
|
||||
count = read_line_from_file(&parser->file, workbuff, sizeof(workbuff));
|
||||
if (!count) {
|
||||
/* Check for error in case nothing is read */
|
||||
if (f_error(&parser->file)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
} else if (f_eof(&parser->file)) {
|
||||
retval = HEX_PARSER_FILE_END;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip out invalid characters at the end */
|
||||
for (i = count - 1; i >= 0; i--) {
|
||||
if (workbuff[i] == '\r' || workbuff[i] == '\n' ||
|
||||
workbuff[i] == '\t' || workbuff[i] == ' ')
|
||||
{
|
||||
workbuff[i] = 0;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* We read a valid line, check for valid marker */
|
||||
if (workbuff[0] != ':') {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Line has to be longer than 11 chars in total */
|
||||
if (count < 11) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the data count */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[1], 2, &byte_count)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the address */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[3], 4, &hex_addr)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the record type */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[7], 2, &record_type)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (byte_count * 2 + 9 + 2 != (unsigned int)count) {
|
||||
/* Line not the expected length */
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Check the checksum. We have bytecount + 5 bytes in a record */
|
||||
if (hex_record_check_checksum(&workbuff[1], byte_count + 5)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Check record type */
|
||||
switch (record_type) {
|
||||
case 0x00: /* Data */
|
||||
if (byte_count > data_len) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*lenout = 0;
|
||||
*address = hex_addr + parser->current_address_offset;
|
||||
|
||||
for (i = 0; i < (int)byte_count; i++) {
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[9 + 2*i], 2, &tmp)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*data = (char)(tmp & 0xFF);
|
||||
data++;
|
||||
(*lenout)++;
|
||||
}
|
||||
retval = HEX_PARSER_DATA_OK;
|
||||
break;
|
||||
case 0x01: /* End of file */
|
||||
retval = HEX_PARSER_EOF_RECORD;
|
||||
break;
|
||||
case 0x04: /* extended linear address */
|
||||
if (byte_count != 2) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Parse the upper 16 bit of the address */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[9], 4, &tmp)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
parser->current_address_offset = tmp << 16;
|
||||
retval = HEX_PARSER_OK;
|
||||
break;
|
||||
case 0x05:
|
||||
retval = HEX_PARSER_OK;
|
||||
break;
|
||||
default:
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_close(struct hex_parser *parser) {
|
||||
if (!parser)
|
||||
|
@@ -6,10 +6,11 @@
|
||||
#include <stddef.h>
|
||||
|
||||
enum hex_parser_ret {
|
||||
HEX_PARSER_OK,
|
||||
HEX_PARSER_OK = 0,
|
||||
HEX_PARSER_DATA_OK,
|
||||
HEX_PARSER_ERROR,
|
||||
HEX_PARSER_FILE_END,
|
||||
HEX_PARSER_EOF_RECORD,
|
||||
};
|
||||
|
||||
struct hex_parser {
|
||||
@@ -19,7 +20,8 @@ struct hex_parser {
|
||||
|
||||
enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_name);
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len);
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len,
|
||||
size_t *len_out);
|
||||
|
||||
enum hex_parser_ret hex_parser_close(struct hex_parser *parser);
|
||||
|
||||
|
53
stm-firmware/updater/ram-code/itoa.c
Normal file
53
stm-firmware/updater/ram-code/itoa.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "itoa.h"
|
||||
|
||||
/*
|
||||
* The heapless_itoa function is copied from the shellmatta project.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief itoa like function to convert int to an ascii string
|
||||
* @warning you have to provide a large enough buffer
|
||||
* @param[in] value
|
||||
* @param[in,out] buffer
|
||||
* @param[in] base
|
||||
* @return number of bytes in string
|
||||
*/
|
||||
uint32_t heapless_itoa(int32_t value, char *buffer, uint32_t base)
|
||||
{
|
||||
char tempBuffer[34u];
|
||||
uint32_t i;
|
||||
uint32_t bufferIdx = 0u;
|
||||
int8_t digitValue;
|
||||
|
||||
/** -# check the base for plausibility */
|
||||
if((base >= 2) && (base <= 16))
|
||||
{
|
||||
/** -# check for sign */
|
||||
if(value < 0)
|
||||
{
|
||||
value = value * (-1);
|
||||
buffer[0u] = '-';
|
||||
bufferIdx += 1u;
|
||||
}
|
||||
|
||||
/** -# loop through all digits in reverse order */
|
||||
i = 0u;
|
||||
do
|
||||
{
|
||||
digitValue = (int8_t) (value % base);
|
||||
tempBuffer[i] = (digitValue < 10) ? ('0' + digitValue) : ('A' + (digitValue - 10));
|
||||
value /= base;
|
||||
i ++;
|
||||
}while(value > 0);
|
||||
|
||||
/** -# store the string in the correct order onto the buffer */
|
||||
while(i > 0u)
|
||||
{
|
||||
buffer[bufferIdx] = tempBuffer[i - 1u];
|
||||
i --;
|
||||
bufferIdx ++;
|
||||
}
|
||||
}
|
||||
return bufferIdx;
|
||||
}
|
||||
|
8
stm-firmware/updater/ram-code/itoa.h
Normal file
8
stm-firmware/updater/ram-code/itoa.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef _ITOA_H_
|
||||
#define _ITOA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t heapless_itoa(int32_t value, char *buffer, uint32_t base);
|
||||
|
||||
#endif /* _ITOA_H_ */
|
@@ -3,7 +3,19 @@
|
||||
#include <cmsis/core_cm4.h>
|
||||
#include "hex-parser.h"
|
||||
#include <fatfs/ff.h>
|
||||
|
||||
/* This is used to get the defines for the external watchdog */
|
||||
#include <reflow-controller/safety/safety-config.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/stm32-gpio-macros.h>
|
||||
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include "flash-writer.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "uart.h"
|
||||
#include "itoa.h"
|
||||
|
||||
static volatile unsigned int wait_tick;
|
||||
|
||||
@@ -12,6 +24,14 @@ static void watchdog_ack(void)
|
||||
IWDG->KR = 0xAAAA;
|
||||
}
|
||||
|
||||
static void external_watchdog_disable(void)
|
||||
{
|
||||
RCC->AHB1ENR |= SAFETY_EXT_WATCHDOG_RCC_MASK;
|
||||
__DSB();
|
||||
/* Set Pin to input. This disables the external watchdog. */
|
||||
SAFETY_EXT_WATCHDOG_PORT->MODER &= MODER_DELETE(SAFETY_EXT_WATCHDOG_PIN);
|
||||
}
|
||||
|
||||
void sdio_wait_ms(unsigned int ms)
|
||||
{
|
||||
wait_tick = 0;
|
||||
@@ -23,24 +43,247 @@ static FATFS _fs;
|
||||
|
||||
static void __attribute__((noreturn)) ram_code_exit(bool updated)
|
||||
{
|
||||
(void)updated;
|
||||
struct safety_memory_boot_status boot_status;
|
||||
safety_memory_get_boot_status(&boot_status);
|
||||
boot_status.code_updated = updated ? 0xFFFFFFFFUL : 0x0UL;
|
||||
boot_status.reboot_to_bootloader = 0x0UL;
|
||||
safety_memory_set_boot_status(&boot_status);
|
||||
|
||||
uart_send_string("Rebooting in 1s...\r\n");
|
||||
sdio_wait_ms(1000);
|
||||
|
||||
NVIC_SystemReset();
|
||||
while(1);
|
||||
}
|
||||
|
||||
static int check_hex_file(const char *fname, uint32_t *update_size)
|
||||
{
|
||||
enum hex_parser_ret hex_ret;
|
||||
struct hex_parser parser;
|
||||
uint32_t addr;
|
||||
char data[128];
|
||||
size_t dlen;
|
||||
int retval = -1;
|
||||
uint32_t flash_base;
|
||||
uint32_t flash_top;
|
||||
uint32_t total_size = 0UL;
|
||||
|
||||
flash_base = flash_writer_get_base_address();
|
||||
flash_top = flash_base + flash_writer_get_flash_size();
|
||||
|
||||
hex_ret = hex_parser_open(&parser, fname);
|
||||
if (hex_ret != HEX_PARSER_OK) {
|
||||
retval = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
do {
|
||||
hex_ret = hex_parser_parse(&parser, &addr, data, sizeof(data), &dlen);
|
||||
if (hex_ret == HEX_PARSER_DATA_OK) {
|
||||
if (addr < flash_base || addr+dlen >= flash_top) {
|
||||
retval = -2;
|
||||
goto ret_close_parser;
|
||||
}
|
||||
total_size += dlen;
|
||||
}
|
||||
} while (hex_ret == HEX_PARSER_DATA_OK || hex_ret == HEX_PARSER_OK);
|
||||
|
||||
if (hex_ret == HEX_PARSER_EOF_RECORD) {
|
||||
retval = 0;
|
||||
if (update_size)
|
||||
*update_size = total_size;
|
||||
}
|
||||
|
||||
ret_close_parser:
|
||||
hex_parser_close(&parser);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int write_flash_from_buffer(const char *buffer, uint32_t len, uint32_t addr)
|
||||
{
|
||||
int res;
|
||||
uint32_t i;
|
||||
const char *verify_ptr = (const char *)addr;
|
||||
|
||||
res = flash_writer_write_to_memory((void *)addr, buffer, len);
|
||||
if (res) {
|
||||
uart_send_string("Error writing to flash!\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Verify the write */
|
||||
for (i = 0; i < len; i++, verify_ptr++) {
|
||||
if (*verify_ptr != buffer[i]) {
|
||||
uart_send_string("Error verifying written data!\r\n");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_flash_from_file(const char *fname)
|
||||
{
|
||||
enum hex_parser_ret hex_ret;
|
||||
struct hex_parser parser;
|
||||
static char write_buffer[4096];
|
||||
uint32_t wbuffer_base_addr = 0;
|
||||
uint32_t wbuffer_fill_level = 0;
|
||||
uint32_t addr;
|
||||
static char tmp_buff[256];
|
||||
size_t dlen;
|
||||
int retval = 0;
|
||||
int res;
|
||||
|
||||
hex_ret = hex_parser_open(&parser, fname);
|
||||
if (hex_ret != HEX_PARSER_OK) {
|
||||
uart_send_string("Error reading hex file.\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
hex_ret = hex_parser_parse(&parser, &addr, tmp_buff, sizeof(tmp_buff), &dlen);
|
||||
if (hex_ret == HEX_PARSER_DATA_OK) {
|
||||
/* Check if tmp would fit in wbuffer */
|
||||
if (dlen + wbuffer_fill_level > sizeof(write_buffer)) {
|
||||
/* Write out the buffer and clean it if it doens't fit */
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
wbuffer_fill_level = 0;
|
||||
wbuffer_base_addr = 0;
|
||||
}
|
||||
|
||||
/* Check if parsed data can be linearily appended to buffer */
|
||||
if (wbuffer_fill_level && wbuffer_base_addr + wbuffer_fill_level != addr) {
|
||||
/* Write out the buffer and clean it if it cannot be appended */
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
wbuffer_fill_level = 0;
|
||||
wbuffer_base_addr = 0;
|
||||
}
|
||||
|
||||
/* Fill in the data into the buffer */
|
||||
if (wbuffer_fill_level == 0) {
|
||||
wbuffer_base_addr = addr;
|
||||
}
|
||||
memcpy(&write_buffer[wbuffer_fill_level], tmp_buff, dlen);
|
||||
wbuffer_fill_level += dlen;
|
||||
}
|
||||
} while (hex_ret == HEX_PARSER_DATA_OK || hex_ret == HEX_PARSER_OK);
|
||||
|
||||
if (hex_ret == HEX_PARSER_EOF_RECORD) {
|
||||
if (wbuffer_fill_level > 0) {
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
} else {
|
||||
retval = -3;
|
||||
}
|
||||
|
||||
exit_parser_close:
|
||||
hex_parser_close(&parser);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int ram_code_main(void)
|
||||
{
|
||||
FRESULT fres;
|
||||
int res;
|
||||
enum safety_memory_state safety_mem_state;
|
||||
static char filename[256];
|
||||
static char tmp_buff[256];
|
||||
uint32_t count;
|
||||
uint32_t update_size;
|
||||
int retries = 3;
|
||||
|
||||
|
||||
SysTick_Config(168000UL);
|
||||
external_watchdog_disable();
|
||||
__enable_irq();
|
||||
|
||||
fres = f_mount(fs, "0:/", 1);
|
||||
if (fres != FR_OK) {
|
||||
|
||||
/* Init the uart module
|
||||
* Pins don't need configuration. They're already setup by the main program
|
||||
*/
|
||||
uart_init();
|
||||
|
||||
/* Clear display and set cursor to home position */
|
||||
uart_send_string("\e[2J\e[H");
|
||||
uart_send_string("Updater started.\r\n");
|
||||
|
||||
res = safety_memory_init(&safety_mem_state);
|
||||
if (res || safety_mem_state != SAFETY_MEMORY_INIT_VALID_MEMORY) {
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
fres = f_mount(fs, "0:/", 1);
|
||||
if (fres != FR_OK) {
|
||||
uart_send_string("Could not mount SD card\r\n");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
res = safety_memory_get_update_filename(filename, NULL);
|
||||
if (res)
|
||||
ram_code_exit(false);
|
||||
|
||||
uart_send_string("Checking hex file ");
|
||||
uart_send_string(filename);
|
||||
uart_send_string("\r\n");
|
||||
if (check_hex_file(filename, &update_size)) {
|
||||
uart_send_string("Error in hex file\r\n");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
uart_send_string("File ");
|
||||
uart_send_string(filename);
|
||||
uart_send_string(" checked successfully.\r\n");
|
||||
count = heapless_itoa(update_size, tmp_buff, 10);
|
||||
if (count > 0) {
|
||||
tmp_buff[count] = 0;
|
||||
uart_send_string("Update size: ");
|
||||
uart_send_string(tmp_buff);
|
||||
uart_send_string(" bytes\r\n");
|
||||
}
|
||||
|
||||
uart_send_string("Starting updater...\r\n");
|
||||
|
||||
/* disable the ART caches */
|
||||
FLASH->ACR &= ~FLASH_ACR_DCEN;
|
||||
FLASH->ACR &= ~FLASH_ACR_ICEN;
|
||||
FLASH->ACR |= FLASH_ACR_DCRST | FLASH_ACR_ICRST;
|
||||
|
||||
do {
|
||||
uart_send_string("Erasing chip...");
|
||||
flash_writer_perform_mass_erase();
|
||||
uart_send_string(" done\r\n");
|
||||
|
||||
uart_send_string("Programming flash...\r\n");
|
||||
res = update_flash_from_file(filename);
|
||||
|
||||
if (res) {
|
||||
uart_send_string("Programming NOT successful.\r\n");
|
||||
if (retries > 0) {
|
||||
uart_send_string("Will retry...\r\n");
|
||||
}
|
||||
} else {
|
||||
uart_send_string("Programming completed successfully!\r\n");
|
||||
ram_code_exit(true);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (retries > 0);
|
||||
|
||||
while(1) {
|
||||
__WFI();
|
||||
}
|
||||
|
@@ -260,28 +260,28 @@ extern unsigned int __ld_vector_start;
|
||||
extern unsigned int __ld_sbss;
|
||||
extern unsigned int __ld_ebss;
|
||||
|
||||
|
||||
#ifdef CPACR
|
||||
#undef CPACR
|
||||
#endif
|
||||
|
||||
#define CPACR (*((volatile uint32_t *)0xE000ED88))
|
||||
|
||||
void Reset_Handler(void)
|
||||
void Reset_Handler()
|
||||
{
|
||||
|
||||
|
||||
/* The first thing we do here, is to initialize the FPU
|
||||
* When this code is compiled optimized with hardfpu abi,
|
||||
* GCC tends to generate FPU instructions for data copying
|
||||
*/
|
||||
CPACR |= (0xF << 20);
|
||||
/* Reset the stack pointer to top of stack. SP is not required to be inside the clobber list! */
|
||||
__asm__ __volatile__ ("mov sp, %0\n\t" :: "r"(&__ld_top_of_stack) :);
|
||||
|
||||
|
||||
/* Fill bss with zero */
|
||||
__fill_zero(&__ld_sbss, &__ld_ebss);
|
||||
/* Fill Heap with zero */
|
||||
|
||||
/* Reset the stack pointer to top of stack. SP is not required to be inside the clobber list! */
|
||||
__asm__ __volatile__ ("mov sp, %0\n\t" :: "r"(&__ld_top_of_stack) :);
|
||||
|
||||
|
||||
|
||||
ram_code_main();
|
||||
|
||||
|
29
stm-firmware/updater/ram-code/uart.c
Normal file
29
stm-firmware/updater/ram-code/uart.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "uart.h"
|
||||
#include <reflow-controller/periph-config/shell-uart-config.h>
|
||||
#include <stm32/stm32f4xx.h>
|
||||
#include <string.h>
|
||||
|
||||
void uart_init(void)
|
||||
{
|
||||
SHELL_UART_RCC_REG |= SHELL_UART_RCC_MASK;
|
||||
SHELL_UART_PERIPH->BRR = SHELL_UART_BRR_REG_VALUE;
|
||||
SHELL_UART_PERIPH->CR2 = 0;
|
||||
SHELL_UART_PERIPH->CR3 = 0;
|
||||
SHELL_UART_PERIPH->CR1 = USART_CR1_TE | USART_CR1_UE;
|
||||
}
|
||||
|
||||
void uart_send_char(char c)
|
||||
{
|
||||
while (!(SHELL_UART_PERIPH->SR & USART_SR_TXE));
|
||||
SHELL_UART_PERIPH->DR = c;
|
||||
}
|
||||
|
||||
void uart_send_string(const char *str)
|
||||
{
|
||||
int len, i;
|
||||
|
||||
len = strlen(str);
|
||||
for (i = 0; i < len; i++) {
|
||||
uart_send_char(str[i]);
|
||||
}
|
||||
}
|
10
stm-firmware/updater/ram-code/uart.h
Normal file
10
stm-firmware/updater/ram-code/uart.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _UART_H_
|
||||
#define _UART_H_
|
||||
|
||||
void uart_init(void);
|
||||
|
||||
void uart_send_char(char c);
|
||||
|
||||
void uart_send_string(const char *str);
|
||||
|
||||
#endif /* _UART_H_ */
|
@@ -19,14 +19,15 @@
|
||||
*/
|
||||
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include <reflow-controller/safety/watchdog.h>
|
||||
#include "ram-code/updater-ram-code.bin.h"
|
||||
#include <generated/updater-ram-code.bin.h>
|
||||
#include <stm32/stm32f4xx.h>
|
||||
#include <cmsis/core_cm4.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void __attribute__((noreturn)) start_updater(void)
|
||||
void __attribute__((noreturn)) start_updater_ram_code(void)
|
||||
{
|
||||
const char *updater_src = binary_blob;
|
||||
char *dest_ptr = (char *)UPDATER_RAM_CODE_BASE_ADDRESS;
|
||||
@@ -57,3 +58,17 @@ void __attribute__((noreturn)) start_updater(void)
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) updater_update_from_file(const char *filename)
|
||||
{
|
||||
struct safety_memory_boot_status status;
|
||||
|
||||
safety_memory_get_boot_status(&status);
|
||||
status.reboot_to_bootloader = 0xFFFFFFFFUL;
|
||||
safety_memory_set_boot_status(&status);
|
||||
|
||||
safety_memory_set_update_filename(filename);
|
||||
|
||||
NVIC_SystemReset();
|
||||
while (1);
|
||||
}
|
||||
|
Reference in New Issue
Block a user