79 Commits

Author SHA1 Message Date
43abca4c51 Merge branch 'memory-test' into dev 2021-05-22 11:41:45 +02:00
00c796c58f Merge branch 'memory-test' of mhu/reflow-oven-control-sw into dev 2021-05-22 11:41:22 +02:00
4d0f963585 Merge branch 'dev' into memory-test 2021-05-22 11:35:03 +02:00
708fdea058 Add static blocking write function to gui. This allows us to set a updating... status on the display when starting the updater. 2021-05-22 11:34:33 +02:00
ebb1383957 Write memory test for system RAM 2021-05-22 11:25:55 +02:00
3345004213 Merge branch 'dev' into memory-test 2021-05-22 00:23:18 +02:00
afadd539c8 Merge branch '28-update-notification' of mhu/reflow-oven-control-sw into dev 2021-05-22 00:17:01 +02:00
71315b7c92 Issue #28: Implement update successful notification. 2021-05-22 00:14:56 +02:00
410a5d4dd1 Change -Og to -O0 because it made a lot of problems when debugging 2021-05-22 00:13:07 +02:00
528db7a581 Fix error in CCM Ram test 2021-05-22 00:06:51 +02:00
384e127085 Add first draft of memory checking for CCM RAM 2021-05-20 23:55:32 +02:00
fe0bde5c32 Fix wrong string constant 2021-05-20 23:54:42 +02:00
2beaccbe32 Fix typo in comment 2021-05-20 23:54:21 +02:00
b6760ff426 Merge branch 'issue/24-update-could-fail' of mhu/reflow-oven-control-sw into dev 2021-05-16 20:41:56 +02:00
1b2dac21f2 Merge branch 'issue/25-add-update-to-gui' of mhu/reflow-oven-control-sw into dev 2021-05-16 20:40:39 +02:00
97f154d3b9 Add working reflow profile so it isn't lost by accident 2021-05-16 20:38:17 +02:00
ee5dda4a33 Issue #25: Add update menu to GUI 2021-05-16 20:34:09 +02:00
afb8e93b13 Rework function for finding temperature profiles to be able to search for any pattern 2021-05-16 19:57:08 +02:00
9bd0dd194b Issue #24: Fix bug in update code 2021-05-16 19:53:46 +02:00
6322c3728b Use singly linked list to store profile commands. 2021-05-15 21:58:00 +02:00
174bf4220e Remove unused function 2021-05-15 21:57:23 +02:00
566436201e Increase heap size 2021-05-15 21:56:33 +02:00
61e3b58992 Use singly linked list dfor temperature profile file list 2021-05-07 22:09:55 +02:00
01b445a0fb Update linked list library and include it into build 2021-04-10 22:35:30 +02:00
5437a323c3 Add linklist submodule 2021-04-10 22:29:18 +02:00
28e42d3306 Shell: Update: Print error if no update file name is specified 2021-04-10 22:28:30 +02:00
08606689b4 Update fatfs in main application 2021-04-10 20:35:33 +02:00
5776feee85 Merge branch 'dev' of git.shimatta.de:mhu/reflow-oven-control-sw into dev 2021-04-10 20:13:29 +02:00
6273c68821 Shell update command: Take file name as argument 2021-04-10 20:09:52 +02:00
3381840bba Modify git version generation in cmake 2021-04-10 20:09:15 +02:00
cf35ba735f Modify git version generation in cmake 2021-04-10 19:40:49 +02:00
3f31acfada Updater: Fix missing line break in uart output 2021-04-10 15:42:19 +02:00
77251cc1bc increase stack and heap sizes 2021-04-10 14:44:27 +02:00
f2972903d5 Use safer string copying in gui for file list 2021-04-10 14:37:51 +02:00
31b17dfd8d Updater: Clear shell at startup and print size of update 2021-04-10 14:37:05 +02:00
9f1a791be2 Remove custom option checker and use the one of the shellmatta instead 2021-04-10 14:10:17 +02:00
54416a6350 Edit Cmake file to use ENV variable to device if Uart is on debug header or not 2021-04-10 13:45:00 +02:00
9c94428144 Bugfix: button ready state not correctly detected when menu drops back 2021-04-10 13:24:15 +02:00
81155887de Bug: Fix missing variable in printf varargs for gui 2021-04-10 13:11:35 +02:00
8309cef5ec Updater: Fix updating code 2021-04-08 22:03:38 +02:00
1a76a69b6d Switch updater to -Os compilation 2021-04-08 21:50:42 +02:00
e50e3f0ace Add verify step to updater 2021-04-08 21:49:53 +02:00
5fb1612773 make updater reboot after successful update 2021-04-08 21:37:49 +02:00
72735915ee Clear code updated status flag upon startup 2021-04-08 21:35:19 +02:00
08ec458e8f Add update code to updater and use uart for status updates 2021-04-08 21:23:25 +02:00
d962110823 Updater: Use -O0 and write flash writer and fix hex parser 2021-04-07 23:14:45 +02:00
bfdc3d3246 Updater: Store update file name in safety memory before executing updater. Currently name is hardcoded. 2021-04-07 13:26:39 +02:00
dca839ce2e Merge branch 'dev' into updater 2021-04-07 13:19:53 +02:00
eea0826c7b updater: Add function to safety memory for storing the update file name 2021-04-07 13:19:16 +02:00
6e5627fde2 Updater: Add safety memory to updater 2021-04-06 20:55:41 +02:00
0f239dc39d Fix warning in temp profile parser 2021-04-06 20:53:18 +02:00
08eee66d30 Issue #28: Change GUI of profile executer 2021-04-06 19:46:31 +02:00
7c9d296e34 Merge branch 'dev' into feature/28-profile-parser 2021-04-06 19:45:37 +02:00
533656ca28 Add Cmake variable for Uart on debug header in release and use -Og for debugbuild instead of -O0 2021-04-06 19:25:25 +02:00
d146b10569 Issue #28: Inmprove GUI of profile executer 2021-04-06 19:23:45 +02:00
8576bf4231 Merge branch 'dev' into feature/28-profile-parser 2021-04-04 19:35:52 +02:00
6ebd74cb31 Safety Bug: Add meas ADC timing flag to output of the pt1000 read function so it becomes invalid in this case. 2021-04-04 19:35:32 +02:00
46125ba752 Issue #28: Implement first working draft of temperature profile GUI 2021-04-04 19:34:13 +02:00
6c9f90c986 Issue #28: Fix buf in profile executer 2021-04-04 19:33:33 +02:00
74defd5384 Add main loop cycle counter for debugging 2021-04-04 19:32:44 +02:00
5deac33949 Implement basic gui for profile selection. Still doesn't do anything 2021-04-04 17:43:31 +02:00
666353e3b7 Merge branch 'dev' into feature/28-profile-parser 2021-04-04 17:01:51 +02:00
df40ab1be7 Update base64 lib submodule 2021-03-23 22:19:43 +01:00
4c3574c2e2 Make project Cmake ready 2021-03-23 22:15:11 +01:00
289f49204d fisr draft of cmake buidl system. Update generation still missing 2021-03-21 22:38:22 +01:00
4bc98e6baf Merge branch 'dev' into feature/28-profile-parser 2021-03-21 21:08:38 +01:00
e50ce0d541 Add pictures of external watchdog 2021-03-21 21:08:19 +01:00
78b63c853f Issue #28: Start GUI for Temp Profile execution 2021-03-21 21:07:54 +01:00
a25b249d77 Issue #28: Improve script handling 2021-03-21 19:21:36 +01:00
e815442617 Pid Controller: Set integral term lower boundary to 0 2021-03-21 19:18:08 +01:00
60104df30e Issue #28: Write temperture ramp command 2021-03-20 01:02:33 +01:00
5ab911b4b6 Merge branch 'dev' into feature/28-profile-parser 2021-03-19 20:55:00 +01:00
08427cc589 Merge branch 'dev' into feature/28-profile-parser 2021-03-19 20:53:58 +01:00
ba6e0880b4 Merge branch 'dev' into feature/28-profile-parser 2021-03-19 20:52:13 +01:00
6a71416d2a Add void casts to unused parameters 2021-03-19 20:24:49 +01:00
1ecd5edd93 Add temperature profile executer and add shell command 2021-03-19 20:19:37 +01:00
1b4eba1871 Write parser for temp profile language. Not yet tested. 2021-03-19 16:58:14 +01:00
e3e4a6d926 Merge branch 'dev' into feature/28-profile-parser 2021-03-18 22:50:28 +01:00
93ff4959a2 Issue #28: Start Temp profile parser 2021-03-18 22:44:05 +01:00
51 changed files with 2628 additions and 583 deletions

3
.gitmodules vendored
View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View 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

View File

@@ -15,3 +15,4 @@ reflow-controller.includes
*.config
*.files
*.user.*
*.user

109
stm-firmware/CMakeLists.txt Normal file
View 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})

View File

@@ -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)

View File

@@ -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;
}

View 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)

View 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;
}

View 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_ */

View File

@@ -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)

View 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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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);

View File

@@ -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__ */
/** @} */

View File

@@ -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__ */

View File

@@ -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_ */

View File

@@ -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]);

View File

@@ -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__ */

View File

@@ -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__ */

View File

@@ -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;

View File

@@ -1,3 +0,0 @@
*
*.*
!.gitignore

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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,
}
};

View File

@@ -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 */

View 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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View 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"
)

View File

@@ -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)

View File

@@ -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)))

View 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;
}

View 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_ */

View File

@@ -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)

View File

@@ -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);

View 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;
}

View 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_ */

View File

@@ -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();
}

View File

@@ -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();

View 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]);
}
}

View 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_ */

View File

@@ -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);
}