Compare commits
61 Commits
v0.2-test
...
7e3d2d1d0b
Author | SHA1 | Date | |
---|---|---|---|
7e3d2d1d0b | |||
ef2cd6acfe | |||
d9bd434dc6 | |||
4ed2203b35 | |||
0b1ce8b614 | |||
bc5e4c14df | |||
43abca4c51 | |||
00c796c58f | |||
4d0f963585 | |||
708fdea058 | |||
ebb1383957 | |||
3345004213 | |||
afadd539c8 | |||
71315b7c92 | |||
410a5d4dd1 | |||
528db7a581 | |||
384e127085 | |||
fe0bde5c32 | |||
2beaccbe32 | |||
b6760ff426 | |||
1b2dac21f2 | |||
97f154d3b9 | |||
ee5dda4a33 | |||
afb8e93b13 | |||
9bd0dd194b | |||
6322c3728b | |||
174bf4220e | |||
566436201e | |||
61e3b58992 | |||
01b445a0fb | |||
5437a323c3 | |||
28e42d3306 | |||
08606689b4 | |||
5776feee85 | |||
6273c68821 | |||
3381840bba | |||
cf35ba735f | |||
3f31acfada | |||
77251cc1bc | |||
f2972903d5 | |||
31b17dfd8d | |||
9f1a791be2 | |||
54416a6350 | |||
9c94428144 | |||
81155887de | |||
8309cef5ec | |||
1a76a69b6d | |||
e50e3f0ace | |||
5fb1612773 | |||
72735915ee | |||
08ec458e8f | |||
d962110823 | |||
bfdc3d3246 | |||
dca839ce2e | |||
eea0826c7b | |||
6e5627fde2 | |||
0f239dc39d | |||
08eee66d30 | |||
7c9d296e34 | |||
533656ca28 | |||
d146b10569 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,3 +10,6 @@
|
||||
path = stm-firmware/base64-lib
|
||||
url = https://git.shimatta.de/mhu/base64-lib.git
|
||||
branch = master
|
||||
[submodule "stm-firmware/linklist-lib"]
|
||||
path = stm-firmware/linklist-lib
|
||||
url = https://git.shimatta.de/mhu/linklist-lib.git
|
||||
|
15
doc/source/reflow-profiles/reflow-245.tpr
Normal file
15
doc/source/reflow-profiles/reflow-245.tpr
Normal file
@@ -0,0 +1,15 @@
|
||||
# This is a comment
|
||||
pid_conf 10 0.3 5 60 2.5 0.5
|
||||
temp_set 50
|
||||
wait_temp 45
|
||||
temp_set 45
|
||||
temp_ramp 150 120
|
||||
temp_ramp 190 90
|
||||
pid_conf 9 0.5 6 60 2.5 0.5
|
||||
temp_set 250
|
||||
wait_temp 245
|
||||
temp_off
|
||||
beep 1
|
||||
wait_time 1
|
||||
beep 0
|
||||
|
@@ -27,11 +27,24 @@ if(NOT WIN32)
|
||||
set(BoldWhite "${Esc}[1;37m")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND bash -c "echo -n $(git describe --always --tags --dirty)"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE)
|
||||
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)
|
||||
@@ -45,19 +58,23 @@ add_subdirectory(updater/ram-code)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
message("${BoldGreen}Version: ${GIT_DESCRIBE}${ColorReset}")
|
||||
|
||||
IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DDEBUGBUILD)
|
||||
add_compile_options(-O0 -g)
|
||||
add_definitions(-DDEBUGBUILD)
|
||||
add_compile_options(-O0 -g)
|
||||
ELSE()
|
||||
add_definitions(-DDEBUGBUILD)
|
||||
add_compile_options(-O3 -g)
|
||||
add_link_options(-Wl,--gc-sections)
|
||||
add_compile_options(-O3 -g)
|
||||
add_link_options(-Wl,--gc-sections)
|
||||
ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
add_subdirectory(base64-lib)
|
||||
|
||||
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)
|
||||
@@ -81,6 +98,12 @@ 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)
|
||||
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})
|
||||
|
126
stm-firmware/boot/startup-tests.c
Normal file
126
stm-firmware/boot/startup-tests.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "startup-tests.h"
|
||||
|
||||
uint32_t startup_test_perform_ccm_ram_check(void)
|
||||
{
|
||||
const void *ccmram_base = (void *)0x10000000UL;
|
||||
const uint32_t ccmram_size = 64U * 1024UL;
|
||||
volatile uint32_t *word_ptr;
|
||||
uint32_t target_val;
|
||||
uint32_t idx;
|
||||
uint32_t ret = 0UL;
|
||||
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 1 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 2 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0xFF */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0xFFFFFFFFUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = 0xFFFFFFFFUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0x00 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0x0UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = 0x0UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exit_ret_address:
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t startup_test_perform_system_ram_check()
|
||||
{
|
||||
const void *ram_base = (void *)0x20000000UL;
|
||||
const uint32_t ram_size = 128U * 1024UL;
|
||||
volatile uint32_t *word_ptr;
|
||||
uint32_t target_val;
|
||||
uint32_t idx;
|
||||
uint32_t ret = 0UL;
|
||||
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 1 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 2 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0xFF */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0xFFFFFFFFUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = 0xFFFFFFFFUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0x00 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0x0UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = 0x0UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
exit_ret_address:
|
||||
return ret;
|
||||
}
|
25
stm-firmware/boot/startup-tests.h
Normal file
25
stm-firmware/boot/startup-tests.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef _STARTUP_TESTS_H_
|
||||
#define _STARTUP_TESTS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Do a RAM check of the CCM RAM.
|
||||
*
|
||||
* Loop over the whole CCM memory area and check it.
|
||||
*
|
||||
* @return 0 if successful. Else the defect address is returned.
|
||||
* @warning This will completely corrupt this memory!
|
||||
* You have to ensure to set it to sane values afterwards!
|
||||
*/
|
||||
uint32_t startup_test_perform_ccm_ram_check(void);
|
||||
|
||||
/**
|
||||
* @brief Do a RAM check of the stnadard SRAM regions
|
||||
* @return 0 if successful. If an error is found, the faulty address is returned
|
||||
* @warning This completely destroys all content in the memory!
|
||||
* @warning Ensure that the stack pointer is moved to a different memory reagion (CCM RAM)!
|
||||
*/
|
||||
uint32_t startup_test_perform_system_ram_check(void);
|
||||
|
||||
#endif /* _STARTUP_TESTS_H_ */
|
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "startup-tests.h"
|
||||
|
||||
/* C++ library init */
|
||||
# if defined(__cplusplus)
|
||||
@@ -288,7 +289,7 @@ extern unsigned int __ld_eheap;
|
||||
|
||||
#define CPACR (*((volatile uint32_t *)0xE000ED88))
|
||||
|
||||
void Reset_Handler(void) {
|
||||
void __attribute__((noreturn)) Reset_Handler(void) {
|
||||
/* Stack is already initialized by hardware */
|
||||
|
||||
/* The first thing we do here, is to initialize the FPU
|
||||
@@ -297,6 +298,9 @@ void Reset_Handler(void) {
|
||||
*/
|
||||
CPACR |= (0xF << 20);
|
||||
|
||||
/**
|
||||
* Prepare RAM etc for the System Init function. This ensures, the RAM tests execute at max speed.
|
||||
*/
|
||||
|
||||
/* Copy .data section */
|
||||
__init_section(&__ld_load_data, &__ld_sdata, &__ld_edata);
|
||||
@@ -311,6 +315,41 @@ void Reset_Handler(void) {
|
||||
|
||||
/* Set clocks, waitstates, ART operation etc. */
|
||||
SystemInit();
|
||||
|
||||
if (startup_test_perform_ccm_ram_check()) {
|
||||
/* Hang forever in case of an error. We cannot handle this (yet?) */
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* Move the Stack pointer to CCMRAM
|
||||
* This allows us to perform a RAM test on the main RAM.
|
||||
* Note: sp is not required to be inside the clobber list!
|
||||
*/
|
||||
__asm__ __volatile__ ("mov sp, %0\n\t" :: "r"(0x10000000UL + (64U * 1024UL)) :);
|
||||
|
||||
if (startup_test_perform_system_ram_check()) {
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* Move the stack pointer back. Note: This only works if this function does not use the stack for variables.
|
||||
* Otherwise everything will be broken.
|
||||
*/
|
||||
__asm__ __volatile__ ("mov sp, %0\n\t" :: "r"(&__ld_top_of_stack) :);
|
||||
|
||||
/**
|
||||
* RAM tests destroyed our values. So we have to copy them again...
|
||||
*/
|
||||
|
||||
/* Copy .data section */
|
||||
__init_section(&__ld_load_data, &__ld_sdata, &__ld_edata);
|
||||
/* Fill bss with zero */
|
||||
__fill_zero(&__ld_sbss, &__ld_ebss);
|
||||
/* Fill Heap with zero */
|
||||
__fill_zero(&__ld_sheap, &__ld_eheap);
|
||||
/* Fill static CCM memory with zeroes */
|
||||
__fill_zero(&__ld_sbss_ccm, &__ld_ebss_ccm);
|
||||
/* Init CCM RAM data section */
|
||||
__init_section(&__ld_load_ccm_data, &__ld_sdata_ccm, &__ld_edata_ccm);
|
||||
|
||||
/* C++ init function */
|
||||
#if defined(__cplusplus)
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#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;
|
||||
@@ -40,7 +41,8 @@ static const struct pl_command_list_map cmd_list_map[_PL_NUM_CMDS] = {
|
||||
{PL_WAIT_FOR_TIME, "wait_time", 1u},
|
||||
{PL_SET_RAMP, "temp_ramp", 2u},
|
||||
{PL_LOUDSPEAKER_SET, "beep", 1u},
|
||||
{PL_OFF, "temp_off", 0u}
|
||||
{PL_OFF, "temp_off", 0u},
|
||||
{PL_CLEAR_FLAGS, "clear_flags", 0u},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -106,7 +108,7 @@ 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;
|
||||
const struct pl_command_list_map *map = NULL;
|
||||
char *endptr;
|
||||
struct pl_command c;
|
||||
|
||||
@@ -146,7 +148,7 @@ static int parse_line(char *line, struct pl_command *cmd)
|
||||
token_idx++;
|
||||
}
|
||||
|
||||
if (token_idx - 1 < map->expected_param_count) {
|
||||
if (!map || (token_idx - 1 < map->expected_param_count)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
@@ -155,9 +157,20 @@ static int parse_line(char *line, struct pl_command *cmd)
|
||||
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,
|
||||
struct pl_command *cmd_list,
|
||||
uint32_t cmd_list_length,
|
||||
SlList **command_list,
|
||||
uint32_t max_len,
|
||||
uint32_t *cmds_parsed)
|
||||
{
|
||||
FIL script_file;
|
||||
@@ -165,9 +178,10 @@ enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
int res;
|
||||
enum pl_ret_val ret = PL_RET_SUCCESS;
|
||||
char workbuff[256];
|
||||
struct pl_command temp_command;
|
||||
uint32_t cmd_idx;
|
||||
|
||||
if (!filename || !cmd_list || !cmd_list_length || !cmds_parsed)
|
||||
if (!filename || !command_list || !max_len || !cmds_parsed)
|
||||
return PL_RET_PARAM_ERR;
|
||||
|
||||
|
||||
@@ -189,19 +203,22 @@ enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
}
|
||||
|
||||
/* Check if list already full */
|
||||
if (cmd_idx >= cmd_list_length) {
|
||||
if (cmd_idx >= max_len) {
|
||||
ret = PL_RET_LIST_FULL;
|
||||
goto exit_close;
|
||||
}
|
||||
|
||||
/* Parse the line */
|
||||
res = parse_line(workbuff, &cmd_list[cmd_idx]);
|
||||
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;
|
||||
*cmds_parsed = cmd_idx;
|
||||
|
||||
/* Append the temp_command to the list */
|
||||
*command_list = copy_and_append_command_to_list(*command_list, &temp_command);
|
||||
}
|
||||
|
||||
|
||||
@@ -212,3 +229,15 @@ exit_close:
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void delete_pl_command(void *cmd)
|
||||
{
|
||||
if (cmd)
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
void temp_profile_free_command_list(SlList **list)
|
||||
{
|
||||
sl_list_free_full(*list, delete_pl_command);
|
||||
*list = NULL;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.20
|
||||
# Doxyfile 1.9.1
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -323,7 +323,10 @@ OPTIMIZE_OUTPUT_SLICE = NO
|
||||
# Note: For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
|
||||
# the files are not read by doxygen.
|
||||
# the files are not read by doxygen. When specifying no_extension you should add
|
||||
# * to the FILE_PATTERNS.
|
||||
#
|
||||
# Note see also the list of default file extension mappings.
|
||||
|
||||
EXTENSION_MAPPING =
|
||||
|
||||
@@ -533,6 +536,13 @@ EXTRACT_LOCAL_METHODS = NO
|
||||
|
||||
EXTRACT_ANON_NSPACES = NO
|
||||
|
||||
# If this flag is set to YES, the name of an unnamed parameter in a declaration
|
||||
# will be determined by the corresponding definition. By default unnamed
|
||||
# parameters remain unnamed in the output.
|
||||
# The default value is: YES.
|
||||
|
||||
RESOLVE_UNNAMED_PARAMS = YES
|
||||
|
||||
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
|
||||
# undocumented members inside documented classes or files. If set to NO these
|
||||
# members will be included in the various overviews, but no documentation
|
||||
@@ -570,11 +580,18 @@ HIDE_IN_BODY_DOCS = NO
|
||||
|
||||
INTERNAL_DOCS = NO
|
||||
|
||||
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
|
||||
# names in lower-case letters. If set to YES, upper-case letters are also
|
||||
# allowed. This is useful if you have classes or files whose names only differ
|
||||
# in case and if your file system supports case sensitive file names. Windows
|
||||
# (including Cygwin) and Mac users are advised to set this option to NO.
|
||||
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
|
||||
# able to match the capabilities of the underlying filesystem. In case the
|
||||
# filesystem is case sensitive (i.e. it supports files in the same directory
|
||||
# whose names only differ in casing), the option must be set to YES to properly
|
||||
# deal with such files in case they appear in the input. For filesystems that
|
||||
# are not case sensitive the option should be be set to NO to properly deal with
|
||||
# output files written for symbols that only differ in casing, such as for two
|
||||
# classes, one named CLASS and the other named Class, and to also support
|
||||
# references to files without having to specify the exact matching casing. On
|
||||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||
# YES.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = NO
|
||||
@@ -813,7 +830,10 @@ WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = NO
|
||||
|
||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||
# a warning is encountered.
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
@@ -849,8 +869,8 @@ INPUT = ../
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
|
||||
# possible encodings.
|
||||
# documentation (see:
|
||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
@@ -863,13 +883,15 @@ INPUT_ENCODING = UTF-8
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# read by doxygen.
|
||||
#
|
||||
# Note the list of default checked file patterns might differ from the list of
|
||||
# default file extension mappings.
|
||||
#
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
||||
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
|
||||
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
|
||||
# *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
*.cc \
|
||||
@@ -934,8 +956,12 @@ RECURSIVE = YES
|
||||
|
||||
EXCLUDE = ../include/stm32 \
|
||||
../include/arm_math.h \
|
||||
../include/cmsis \
|
||||
../shellmatta/test
|
||||
../include/cmsis/ \
|
||||
../shellmatta/test/ \
|
||||
../linklist-lib/test \
|
||||
../base64-lib/test \
|
||||
../shellmatta/doc/main.dox \
|
||||
../updater/ram-code
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -1143,13 +1169,6 @@ VERBATIM_HEADERS = YES
|
||||
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
|
||||
# which the alphabetical index list will be split.
|
||||
# Minimum value: 1, maximum value: 20, default value: 5.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
|
||||
# In case all classes in a project start with a common prefix, all classes will
|
||||
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||
# can be used to specify a prefix (or a list of prefixes) that should be ignored
|
||||
@@ -1320,10 +1339,11 @@ HTML_INDEX_NUM_ENTRIES = 100
|
||||
|
||||
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
|
||||
# generated that can be used as input for Apple's Xcode 3 integrated development
|
||||
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
|
||||
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
||||
# Makefile in the HTML output directory. Running make will produce the docset in
|
||||
# that directory and running make install will install the docset in
|
||||
# environment (see:
|
||||
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
|
||||
# create a documentation set, doxygen will generate a Makefile in the HTML
|
||||
# output directory. Running make will produce the docset in that directory and
|
||||
# running make install will install the docset in
|
||||
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
|
||||
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
|
||||
# genXcode/_index.html for more information.
|
||||
@@ -1365,8 +1385,8 @@ DOCSET_PUBLISHER_NAME = Publisher
|
||||
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
|
||||
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
|
||||
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
|
||||
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
|
||||
# Windows.
|
||||
# (see:
|
||||
# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
|
||||
#
|
||||
# The HTML Help Workshop contains a compiler that can convert all HTML output
|
||||
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
|
||||
@@ -1441,7 +1461,8 @@ QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
|
||||
# (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
|
||||
# The default value is: org.doxygen.Project.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@@ -1449,8 +1470,8 @@ QHP_NAMESPACE = org.doxygen.Project
|
||||
|
||||
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
|
||||
# Help Project output. For more information please see Qt Help Project / Virtual
|
||||
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
|
||||
# folders).
|
||||
# Folders (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
|
||||
# The default value is: doc.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@@ -1458,16 +1479,16 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
|
||||
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
|
||||
# filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# Filters (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# Filters (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
@@ -1479,9 +1500,9 @@ QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
|
||||
# The QHG_LOCATION tag can be used to specify the location of Qt's
|
||||
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
|
||||
# generated .qhp file.
|
||||
# The QHG_LOCATION tag can be used to specify the location (absolute path
|
||||
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
|
||||
# run qhelpgenerator on the generated .qhp file.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHG_LOCATION =
|
||||
@@ -1608,7 +1629,7 @@ USE_MATHJAX = YES
|
||||
|
||||
# When MathJax is enabled you can set the default output format to be used for
|
||||
# the MathJax output. See the MathJax site (see:
|
||||
# http://docs.mathjax.org/en/latest/output.html) for more details.
|
||||
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
|
||||
# Possible values are: HTML-CSS (which is slower, but has the best
|
||||
# compatibility), NativeMML (i.e. MathML) and SVG.
|
||||
# The default value is: HTML-CSS.
|
||||
@@ -1638,7 +1659,8 @@ MATHJAX_EXTENSIONS =
|
||||
|
||||
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
|
||||
# of code that will be used on startup of the MathJax code. See the MathJax site
|
||||
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
|
||||
# (see:
|
||||
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
|
||||
# example see the documentation.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
@@ -1685,7 +1707,8 @@ SERVER_BASED_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: https://xapian.org/).
|
||||
# Xapian (see:
|
||||
# https://xapian.org/).
|
||||
#
|
||||
# See the section "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
@@ -1698,8 +1721,9 @@ EXTERNAL_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: https://xapian.org/). See the section "External Indexing and
|
||||
# Searching" for details.
|
||||
# Xapian (see:
|
||||
# https://xapian.org/). See the section "External Indexing and Searching" for
|
||||
# details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
SEARCHENGINE_URL =
|
||||
@@ -2206,7 +2230,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED = __attribute__(x)= \
|
||||
IN_SECTION(x)=
|
||||
IN_SECTION(x)=
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
@@ -2383,10 +2407,32 @@ UML_LOOK = NO
|
||||
# but if the number exceeds 15, the total amount of fields shown is limited to
|
||||
# 10.
|
||||
# Minimum value: 0, maximum value: 100, default value: 10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
# This tag requires that the tag UML_LOOK is set to YES.
|
||||
|
||||
UML_LIMIT_NUM_FIELDS = 10
|
||||
|
||||
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
|
||||
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
|
||||
# tag is set to YES, doxygen will add type and arguments for attributes and
|
||||
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
|
||||
# will not generate fields with class member information in the UML graphs. The
|
||||
# class diagrams will look similar to the default class diagrams but using UML
|
||||
# notation for the relationships.
|
||||
# Possible values are: NO, YES and NONE.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag UML_LOOK is set to YES.
|
||||
|
||||
DOT_UML_DETAILS = NO
|
||||
|
||||
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
|
||||
# to display on a single line. If the actual line length exceeds this threshold
|
||||
# significantly it will wrapped across multiple lines. Some heuristics are apply
|
||||
# to avoid ugly line breaks.
|
||||
# Minimum value: 0, maximum value: 1000, default value: 17.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_WRAP_THRESHOLD = 17
|
||||
|
||||
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
|
||||
# collaboration graphs will show the relations between templates and their
|
||||
# instances.
|
||||
@@ -2576,9 +2622,11 @@ DOT_MULTI_TARGETS = NO
|
||||
|
||||
GENERATE_LEGEND = YES
|
||||
|
||||
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
|
||||
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
|
||||
# files that are used to generate the various graphs.
|
||||
#
|
||||
# Note: This setting is not only used for dot files but also for msc and
|
||||
# plantuml temporary files.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2019 */
|
||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
@@ -12,14 +12,8 @@
|
||||
#include "shimatta_sdio_driver/shimatta_sdio.h"
|
||||
|
||||
/* Definitions of physical drive number for each drive */
|
||||
#define DEV_SD 0 /* Example: Map MMC/SD card to physical drive 0*/
|
||||
/*
|
||||
DSTATUS SDIO_status();
|
||||
DSTATUS SDIO_initialize();
|
||||
DRESULT SDIO_disk_read(BYTE *buff, DWORD sector, UINT count);
|
||||
DRESULT SDIO_disk_write(const BYTE *buff, DWORD sector, UINT count);
|
||||
DRESULT SDIO_disk_ioctl(BYTE cmd, void* buff);
|
||||
*/
|
||||
#define DEV_SD 0
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
@@ -116,4 +110,3 @@ DRESULT disk_ioctl (
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem Module R0.14 /
|
||||
/ FatFs - Generic FAT Filesystem Module R0.14a /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2019, ChaN, all right reserved.
|
||||
/ Copyright (C) 2020, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#if FF_DEFINED != 86606 /* Revision ID */
|
||||
#if FF_DEFINED != 80196 /* Revision ID */
|
||||
#error Wrong include file (ff.h).
|
||||
#endif
|
||||
|
||||
@@ -1134,13 +1134,12 @@ static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
|
||||
if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */
|
||||
/* Create FSInfo structure */
|
||||
mem_set(fs->win, 0, sizeof fs->win);
|
||||
st_word(fs->win + BS_55AA, 0xAA55);
|
||||
st_dword(fs->win + FSI_LeadSig, 0x41615252);
|
||||
st_dword(fs->win + FSI_StrucSig, 0x61417272);
|
||||
st_dword(fs->win + FSI_Free_Count, fs->free_clst);
|
||||
st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);
|
||||
/* Write it into the FSInfo sector */
|
||||
fs->winsect = fs->volbase + 1;
|
||||
st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */
|
||||
st_dword(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */
|
||||
st_dword(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */
|
||||
st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */
|
||||
st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */
|
||||
fs->winsect = fs->volbase + 1; /* Write it into the FSInfo sector (Next to VBR) */
|
||||
disk_write(fs->pdrv, fs->win, fs->winsect, 1);
|
||||
fs->fsi_flag = 0;
|
||||
}
|
||||
@@ -1235,7 +1234,8 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFF
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* go to default */
|
||||
val = 1; /* Internal error */
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
val = 1; /* Internal error */
|
||||
@@ -1266,7 +1266,7 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
|
||||
|
||||
if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */
|
||||
switch (fs->fs_type) {
|
||||
case FS_FAT12 :
|
||||
case FS_FAT12:
|
||||
bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */
|
||||
res = move_window(fs, fs->fatbase + (bc / SS(fs)));
|
||||
if (res != FR_OK) break;
|
||||
@@ -1280,16 +1280,16 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
|
||||
fs->wflag = 1;
|
||||
break;
|
||||
|
||||
case FS_FAT16 :
|
||||
case FS_FAT16:
|
||||
res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
|
||||
if (res != FR_OK) break;
|
||||
st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */
|
||||
fs->wflag = 1;
|
||||
break;
|
||||
|
||||
case FS_FAT32 :
|
||||
case FS_FAT32:
|
||||
#if FF_FS_EXFAT
|
||||
case FS_EXFAT :
|
||||
case FS_EXFAT:
|
||||
#endif
|
||||
res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
|
||||
if (res != FR_OK) break;
|
||||
@@ -1821,7 +1821,7 @@ static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DEN
|
||||
|
||||
static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
|
||||
DIR* dp, /* Pointer to the directory object */
|
||||
UINT nent /* Number of contiguous entries to allocate */
|
||||
UINT n_ent /* Number of contiguous entries to allocate */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
@@ -1836,16 +1836,16 @@ static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
|
||||
res = move_window(fs, dp->sect);
|
||||
if (res != FR_OK) break;
|
||||
#if FF_FS_EXFAT
|
||||
if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {
|
||||
if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */
|
||||
#else
|
||||
if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {
|
||||
if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */
|
||||
#endif
|
||||
if (++n == nent) break; /* A block of contiguous free entries is found */
|
||||
if (++n == n_ent) break; /* Is a block of contiguous free entries found? */
|
||||
} else {
|
||||
n = 0; /* Not a blank entry. Restart to search */
|
||||
n = 0; /* Not a free entry, restart to search */
|
||||
}
|
||||
res = dir_next(dp, 1);
|
||||
} while (res == FR_OK); /* Next entry with table stretch enabled */
|
||||
res = dir_next(dp, 1); /* Next entry with table stretch enabled */
|
||||
} while (res == FR_OK);
|
||||
}
|
||||
|
||||
if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */
|
||||
@@ -2527,19 +2527,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too
|
||||
FRESULT res;
|
||||
FATFS *fs = dp->obj.fs;
|
||||
#if FF_USE_LFN /* LFN configuration */
|
||||
UINT n, nlen, nent;
|
||||
UINT n, len, n_ent;
|
||||
BYTE sn[12], sum;
|
||||
|
||||
|
||||
if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */
|
||||
for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */
|
||||
for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
|
||||
res = dir_alloc(dp, nent); /* Allocate directory entries */
|
||||
n_ent = (len + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
|
||||
res = dir_alloc(dp, n_ent); /* Allocate directory entries */
|
||||
if (res != FR_OK) return res;
|
||||
dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */
|
||||
dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the allocated entry block offset */
|
||||
|
||||
if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */
|
||||
dp->obj.stat &= ~4;
|
||||
@@ -2580,19 +2580,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too
|
||||
}
|
||||
|
||||
/* Create an SFN with/without LFNs. */
|
||||
nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */
|
||||
res = dir_alloc(dp, nent); /* Allocate entries */
|
||||
if (res == FR_OK && --nent) { /* Set LFN entry if needed */
|
||||
res = dir_sdi(dp, dp->dptr - nent * SZDIRE);
|
||||
n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */
|
||||
res = dir_alloc(dp, n_ent); /* Allocate entries */
|
||||
if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */
|
||||
res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE);
|
||||
if (res == FR_OK) {
|
||||
sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */
|
||||
do { /* Store LFN entries in bottom first */
|
||||
res = move_window(fs, dp->sect);
|
||||
if (res != FR_OK) break;
|
||||
put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum);
|
||||
put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum);
|
||||
fs->wflag = 1;
|
||||
res = dir_next(dp, 0); /* Next entry */
|
||||
} while (res == FR_OK && --nent);
|
||||
} while (res == FR_OK && --n_ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2778,7 +2778,10 @@ static void get_fileinfo (
|
||||
/* Pattern matching */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static DWORD get_achar ( /* Get a character and advances ptr */
|
||||
#define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */
|
||||
|
||||
|
||||
static DWORD get_achar ( /* Get a character and advance ptr */
|
||||
const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
|
||||
)
|
||||
{
|
||||
@@ -2809,41 +2812,43 @@ static DWORD get_achar ( /* Get a character and advances ptr */
|
||||
}
|
||||
|
||||
|
||||
static int pattern_matching ( /* 0:not matched, 1:matched */
|
||||
static int pattern_match ( /* 0:mismatched, 1:matched */
|
||||
const TCHAR* pat, /* Matching pattern */
|
||||
const TCHAR* nam, /* String to be tested */
|
||||
int skip, /* Number of pre-skip chars (number of ?s) */
|
||||
int inf /* Infinite search (* specified) */
|
||||
UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */
|
||||
UINT recur /* Recursion count */
|
||||
)
|
||||
{
|
||||
const TCHAR *pp, *np;
|
||||
DWORD pc, nc;
|
||||
int nm, nx;
|
||||
const TCHAR *pptr, *nptr;
|
||||
DWORD pchr, nchr;
|
||||
UINT sk;
|
||||
|
||||
|
||||
while (skip--) { /* Pre-skip name chars */
|
||||
while ((skip & 0xFF) != 0) { /* Pre-skip name chars */
|
||||
if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */
|
||||
skip--;
|
||||
}
|
||||
if (*pat == 0 && inf) return 1; /* (short circuit) */
|
||||
if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */
|
||||
|
||||
do {
|
||||
pp = pat; np = nam; /* Top of pattern and name to match */
|
||||
pptr = pat; nptr = nam; /* Top of pattern and name to match */
|
||||
for (;;) {
|
||||
if (*pp == '?' || *pp == '*') { /* Wildcard? */
|
||||
nm = nx = 0;
|
||||
do { /* Analyze the wildcard block */
|
||||
if (*pp++ == '?') nm++; else nx = 1;
|
||||
} while (*pp == '?' || *pp == '*');
|
||||
if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
|
||||
nc = *np; break; /* Branch mismatched */
|
||||
if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */
|
||||
if (recur == 0) return 0; /* Too many wildcard terms? */
|
||||
sk = 0;
|
||||
do { /* Analyze the wildcard term */
|
||||
if (*pptr++ == '?') sk++; else sk |= 0x100;
|
||||
} while (*pptr == '?' || *pptr == '*');
|
||||
if (pattern_match(pptr, nptr, sk, recur - 1)) return 1; /* Test new branch (recursive call) */
|
||||
nchr = *nptr; break; /* Branch mismatched */
|
||||
}
|
||||
pc = get_achar(&pp); /* Get a pattern char */
|
||||
nc = get_achar(&np); /* Get a name char */
|
||||
if (pc != nc) break; /* Branch mismatched? */
|
||||
if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */
|
||||
pchr = get_achar(&pptr); /* Get a pattern char */
|
||||
nchr = get_achar(&nptr); /* Get a name char */
|
||||
if (pchr != nchr) break; /* Branch mismatched? */
|
||||
if (pchr == 0) return 1; /* Branch matched? (matched at end of both strings) */
|
||||
}
|
||||
get_achar(&nam); /* nam++ */
|
||||
} while (inf && nc); /* Retry until end of name if infinite search is specified */
|
||||
} while (skip && nchr); /* Retry until end of name if infinite search is specified */
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3290,23 +3295,37 @@ static DWORD make_rand (
|
||||
|
||||
/* Check what the sector is */
|
||||
|
||||
static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Valid BS but not FAT, 3:Invalid BS, 4:Disk error */
|
||||
static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */
|
||||
FATFS* fs, /* Filesystem object */
|
||||
LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */
|
||||
)
|
||||
{
|
||||
WORD w, sign;
|
||||
BYTE b;
|
||||
|
||||
|
||||
fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */
|
||||
if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */
|
||||
|
||||
if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot signature (always here regardless of the sector size) */
|
||||
|
||||
if (FF_FS_EXFAT && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */
|
||||
|
||||
if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */
|
||||
if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */
|
||||
if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */
|
||||
sign = ld_word(fs->win + BS_55AA);
|
||||
#if FF_FS_EXFAT
|
||||
if (sign == 0xAA55 && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */
|
||||
#endif
|
||||
b = fs->win[BS_JmpBoot];
|
||||
if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */
|
||||
if (sign == 0xAA55 && !mem_cmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) return 0; /* It is an FAT32 VBR */
|
||||
/* FAT volumes formatted with early MS-DOS lack boot signature and FAT string, so that we need to identify the FAT VBR without them. */
|
||||
w = ld_word(fs->win + BPB_BytsPerSec);
|
||||
if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS) { /* Properness of sector size */
|
||||
b = fs->win[BPB_SecPerClus];
|
||||
if (b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size */
|
||||
&& (fs->win[BPB_NumFATs] == 1 || fs->win[BPB_NumFATs] == 2) /* Properness of number of FATs */
|
||||
&& ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root entry count */
|
||||
&& ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size */
|
||||
return 0; /* Sector can be presumed an FAT VBR */
|
||||
}
|
||||
}
|
||||
}
|
||||
return 2; /* Valid BS but not FAT */
|
||||
return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or invalid BS) */
|
||||
}
|
||||
|
||||
|
||||
@@ -3698,7 +3717,7 @@ FRESULT f_open (
|
||||
DIR dj;
|
||||
FATFS *fs;
|
||||
#if !FF_FS_READONLY
|
||||
DWORD cl, bcs, clst;
|
||||
DWORD cl, bcs, clst, tm;
|
||||
LBA_t sc;
|
||||
FSIZE_t ofs;
|
||||
#endif
|
||||
@@ -3765,8 +3784,10 @@ FRESULT f_open (
|
||||
#endif
|
||||
{
|
||||
/* Set directory entry initial state */
|
||||
tm = GET_FATTIME(); /* Set created time */
|
||||
st_dword(dj.dir + DIR_CrtTime, tm);
|
||||
st_dword(dj.dir + DIR_ModTime, tm);
|
||||
cl = ld_clust(fs, dj.dir); /* Get current cluster chain */
|
||||
st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */
|
||||
dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */
|
||||
st_clust(fs, dj.dir, 0); /* Reset file allocation info */
|
||||
st_dword(dj.dir + DIR_FileSize, 0);
|
||||
@@ -4705,9 +4726,9 @@ FRESULT f_findnext (
|
||||
for (;;) {
|
||||
res = f_readdir(dp, fno); /* Get a directory item */
|
||||
if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */
|
||||
if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */
|
||||
if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) break; /* Test for the file name */
|
||||
#if FF_USE_LFN && FF_USE_FIND == 2
|
||||
if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */
|
||||
if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) break; /* Test for alternative name if exist */
|
||||
#endif
|
||||
}
|
||||
return res;
|
||||
@@ -5376,10 +5397,12 @@ FRESULT f_getlabel (
|
||||
if (res == FR_OK) {
|
||||
switch (fs->fs_type) {
|
||||
case FS_EXFAT:
|
||||
di = BPB_VolIDEx; break;
|
||||
di = BPB_VolIDEx;
|
||||
break;
|
||||
|
||||
case FS_FAT32:
|
||||
di = BS_VolID32; break;
|
||||
di = BS_VolID32;
|
||||
break;
|
||||
|
||||
default:
|
||||
di = BS_VolID;
|
||||
@@ -5677,7 +5700,7 @@ FRESULT f_forward (
|
||||
|
||||
#if !FF_FS_READONLY && FF_USE_MKFS
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Create an FAT/exFAT volume */
|
||||
/* Create FAT/exFAT volume */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */
|
||||
@@ -5685,12 +5708,12 @@ FRESULT f_forward (
|
||||
#define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */
|
||||
|
||||
|
||||
/* Create partitions on the physical drive */
|
||||
/* Create partitions on the physical drive in format of MBR or GPT */
|
||||
|
||||
static FRESULT create_partition (
|
||||
BYTE drv, /* Physical drive number */
|
||||
const LBA_t plst[], /* Partition list */
|
||||
UINT sys, /* System ID (for only MBR, temp setting) and bit8:GPT */
|
||||
BYTE sys, /* System ID (for only MBR, temp setting) */
|
||||
BYTE* buf /* Working buffer for a sector */
|
||||
)
|
||||
{
|
||||
@@ -5801,7 +5824,7 @@ static FRESULT create_partition (
|
||||
|
||||
st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */
|
||||
st_dword(pte + PTE_SizLba, n_lba32); /* Number of sectors */
|
||||
pte[PTE_System] = (BYTE)sys; /* System type */
|
||||
pte[PTE_System] = sys; /* System type */
|
||||
|
||||
cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start cylinder */
|
||||
hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */
|
||||
@@ -5966,10 +5989,9 @@ FRESULT f_mkfs (
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
if (fsty == FS_EXFAT) { /* Create an exFAT volume */
|
||||
DWORD szb_bit, szb_case, sum, nb, cl, tbl[3];
|
||||
DWORD szb_bit, szb_case, sum, nbit, clu, clen[3];
|
||||
WCHAR ch, si;
|
||||
UINT j, st;
|
||||
BYTE b;
|
||||
|
||||
if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume for exFAT? */
|
||||
#if FF_USE_TRIM
|
||||
@@ -5990,12 +6012,12 @@ FRESULT f_mkfs (
|
||||
if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */
|
||||
if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */
|
||||
|
||||
szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
|
||||
tbl[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */
|
||||
szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
|
||||
clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */
|
||||
|
||||
/* Create a compressed up-case table */
|
||||
sect = b_data + sz_au * tbl[0]; /* Table start sector */
|
||||
sum = 0; /* Table checksum to be stored in the 82 entry */
|
||||
sect = b_data + sz_au * clen[0]; /* Table start sector */
|
||||
sum = 0; /* Table checksum to be stored in the 82 entry */
|
||||
st = 0; si = 0; i = 0; j = 0; szb_case = 0;
|
||||
do {
|
||||
switch (st) {
|
||||
@@ -6006,10 +6028,10 @@ FRESULT f_mkfs (
|
||||
}
|
||||
for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */
|
||||
if (j >= 128) {
|
||||
ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */
|
||||
ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 chars */
|
||||
}
|
||||
st = 1; /* Do not compress short run */
|
||||
/* go to next case */
|
||||
/* FALLTHROUGH */
|
||||
case 1:
|
||||
ch = si++; /* Fill the short run */
|
||||
if (--j == 0) st = 0;
|
||||
@@ -6028,16 +6050,15 @@ FRESULT f_mkfs (
|
||||
sect += n; i = 0;
|
||||
}
|
||||
} while (si);
|
||||
tbl[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */
|
||||
tbl[2] = 1; /* Number of root dir clusters */
|
||||
clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */
|
||||
clen[2] = 1; /* Number of root dir clusters */
|
||||
|
||||
/* Initialize the allocation bitmap */
|
||||
sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */
|
||||
nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */
|
||||
sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of bitmap sectors */
|
||||
nbit = clen[0] + clen[1] + clen[2]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */
|
||||
do {
|
||||
mem_set(buf, 0, sz_buf * ss);
|
||||
for (i = 0; nb >= 8 && i < sz_buf * ss; buf[i++] = 0xFF, nb -= 8) ;
|
||||
for (b = 1; nb != 0 && i < sz_buf * ss; buf[i] |= b, b <<= 1, nb--) ;
|
||||
mem_set(buf, 0, sz_buf * ss); /* Initialize bitmap buffer */
|
||||
for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
sect += n; nsect -= n;
|
||||
@@ -6045,20 +6066,20 @@ FRESULT f_mkfs (
|
||||
|
||||
/* Initialize the FAT */
|
||||
sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */
|
||||
j = nb = cl = 0;
|
||||
j = nbit = clu = 0;
|
||||
do {
|
||||
mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write index */
|
||||
if (cl == 0) { /* Set FAT [0] and FAT[1] */
|
||||
st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
|
||||
st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
|
||||
mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write offset */
|
||||
if (clu == 0) { /* Initialize FAT [0] and FAT[1] */
|
||||
st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++;
|
||||
st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++;
|
||||
}
|
||||
do { /* Create chains of bitmap, up-case and root dir */
|
||||
while (nb != 0 && i < sz_buf * ss) { /* Create a chain */
|
||||
st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
|
||||
i += 4; cl++; nb--;
|
||||
while (nbit != 0 && i < sz_buf * ss) { /* Create a chain */
|
||||
st_dword(buf + i, (nbit > 1) ? clu + 1 : 0xFFFFFFFF);
|
||||
i += 4; clu++; nbit--;
|
||||
}
|
||||
if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */
|
||||
} while (nb != 0 && i < sz_buf * ss);
|
||||
if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get next chain length */
|
||||
} while (nbit != 0 && i < sz_buf * ss);
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
sect += n; nsect -= n;
|
||||
@@ -6072,13 +6093,13 @@ FRESULT f_mkfs (
|
||||
st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */
|
||||
buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */
|
||||
st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */
|
||||
st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */
|
||||
st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* cluster */
|
||||
st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */
|
||||
sect = b_data + sz_au * (tbl[0] + tbl[1]); nsect = sz_au; /* Start of the root directory and number of sectors */
|
||||
sect = b_data + sz_au * (clen[0] + clen[1]); nsect = sz_au; /* Start of the root directory and number of sectors */
|
||||
do { /* Fill root directory sectors */
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect;
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
mem_set(buf, 0, ss);
|
||||
mem_set(buf, 0, ss); /* Rest of entries are filled with zero */
|
||||
sect += n; nsect -= n;
|
||||
} while (nsect);
|
||||
|
||||
@@ -6094,7 +6115,7 @@ FRESULT f_mkfs (
|
||||
st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
|
||||
st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */
|
||||
st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */
|
||||
st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */
|
||||
st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */
|
||||
st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */
|
||||
st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */
|
||||
for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */
|
||||
@@ -6520,7 +6541,7 @@ static void putc_bfd (putbuff* pb, TCHAR c)
|
||||
WCHAR hs, wc;
|
||||
#if FF_LFN_UNICODE == 2
|
||||
DWORD dc;
|
||||
TCHAR *tp;
|
||||
const TCHAR *tp;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -6562,7 +6583,7 @@ static void putc_bfd (putbuff* pb, TCHAR c)
|
||||
return;
|
||||
}
|
||||
}
|
||||
tp = (TCHAR*)pb->bs;
|
||||
tp = (const TCHAR*)pb->bs;
|
||||
dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
|
||||
if (dc == 0xFFFFFFFF) return; /* Wrong code? */
|
||||
wc = (WCHAR)dc;
|
||||
@@ -6650,7 +6671,7 @@ static int putc_flush (putbuff* pb)
|
||||
if ( pb->idx >= 0 /* Flush buffered characters to the file */
|
||||
&& f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
|
||||
&& (UINT)pb->idx == nw) return pb->nchr;
|
||||
return EOF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -6754,7 +6775,7 @@ int f_printf (
|
||||
d = c;
|
||||
if (IsLower(d)) d -= 0x20;
|
||||
switch (d) { /* Atgument type is... */
|
||||
case 'S' : /* String */
|
||||
case 'S': /* String */
|
||||
p = va_arg(arp, TCHAR*);
|
||||
for (j = 0; p[j]; j++) ;
|
||||
if (!(f & 2)) { /* Right padded */
|
||||
@@ -6764,21 +6785,26 @@ int f_printf (
|
||||
while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */
|
||||
continue;
|
||||
|
||||
case 'C' : /* Character */
|
||||
putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
|
||||
case 'C': /* Character */
|
||||
putc_bfd(&pb, (TCHAR)va_arg(arp, int));
|
||||
continue;
|
||||
|
||||
case 'B' : /* Unsigned binary */
|
||||
r = 2; break;
|
||||
case 'B': /* Unsigned binary */
|
||||
r = 2;
|
||||
break;
|
||||
|
||||
case 'O' : /* Unsigned octal */
|
||||
r = 8; break;
|
||||
case 'O': /* Unsigned octal */
|
||||
r = 8;
|
||||
break;
|
||||
|
||||
case 'D' : /* Signed decimal */
|
||||
case 'U' : /* Unsigned decimal */
|
||||
r = 10; break;
|
||||
case 'D': /* Signed decimal */
|
||||
case 'U': /* Unsigned decimal */
|
||||
r = 10;
|
||||
break;
|
||||
|
||||
case 'X' : /* Unsigned hexdecimal */
|
||||
r = 16; break;
|
||||
case 'X': /* Unsigned hexdecimal */
|
||||
r = 16;
|
||||
break;
|
||||
|
||||
default: /* Unknown type (pass-through) */
|
||||
putc_bfd(&pb, c); continue;
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.14 /
|
||||
/ FatFs - Generic FAT Filesystem module R0.14a /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2019, ChaN, all right reserved.
|
||||
/ Copyright (C) 2020, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 86606 /* Revision ID */
|
||||
#define FF_DEFINED 80196 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -345,10 +345,6 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86606 /* Revision ID */
|
||||
#define FFCONF_DEF 80196 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
@@ -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
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#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,
|
||||
@@ -31,6 +32,7 @@ enum pl_command_type {
|
||||
PL_WAIT_FOR_TIME,
|
||||
PL_LOUDSPEAKER_SET,
|
||||
PL_OFF,
|
||||
PL_CLEAR_FLAGS,
|
||||
_PL_NUM_CMDS,
|
||||
};
|
||||
|
||||
@@ -49,9 +51,33 @@ struct pl_command {
|
||||
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,
|
||||
struct pl_command *cmd_list,
|
||||
uint32_t cmd_list_length,
|
||||
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__ */
|
||||
|
@@ -67,7 +67,7 @@ int safety_adc_poll_result(void);
|
||||
* After that, it is safe to use the output values until a new conversion is triggered using #safety_adc_trigger_meas
|
||||
*
|
||||
* @warning This function return a constant buffer, that is directly written on by the DMA! Check #safety_adc_poll_result to prevent race conditions.
|
||||
* @return Array of raw ADC readings with lenght of #SAFETY_ADC_NUM_OF_CHANNELS
|
||||
* @return Array of raw ADC readings with length of #SAFETY_ADC_NUM_OF_CHANNELS
|
||||
*/
|
||||
const uint16_t *safety_adc_get_values(void);
|
||||
|
||||
|
@@ -144,6 +144,9 @@ enum analog_value_monitor {
|
||||
*/
|
||||
#define SAFETY_CONTROLLER_ADC_DELAY_MS 250
|
||||
|
||||
/**
|
||||
* @brief Default persistence of safety flags. These values are loaded into the safety tables on startup
|
||||
*/
|
||||
#define SAFETY_CONFIG_DEFAULT_PERSIST ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_MEAS_ADC_OFF, false), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, false), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, false), \
|
||||
@@ -163,7 +166,9 @@ enum analog_value_monitor {
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_SAFETY_TAB_CORRUPT, true), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_AMON_SUPPLY_VOLT, false), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_OVERTEMP, false), \
|
||||
|
||||
/**
|
||||
* @brief Default config weights of safety flags. These values are loaded into the safety tables on startup
|
||||
*/
|
||||
#define SAFETY_CONFIG_DEFAULT_WEIGHTS ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_OFF, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
|
||||
@@ -175,9 +180,7 @@ enum analog_value_monitor {
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_STACK, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SAFETY_ADC, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SYSTICK, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
|
||||
/* Watchdog timeout is not handled periodically, but only on startup.
|
||||
* Therefore, it is not listed here */\
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_WTCHDG_FIRED, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_WTCHDG_FIRED, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_UNCAL, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_DEBUG, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_TIMING_MAIN_LOOP, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
|
||||
|
@@ -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__ */
|
||||
|
||||
/** @} */
|
||||
|
@@ -24,6 +24,13 @@
|
||||
#include <stdbool.h>
|
||||
#include <reflow-controller/settings/settings.h>
|
||||
|
||||
/**
|
||||
* @brief Save the calibration to the SD card
|
||||
* @param sens_deviation Sensitivity deviation from nomila sensitivity (1)
|
||||
* @param offset Offset
|
||||
* @param active Calibration is active. If false, the calibration will be deleted from SD card
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int sd_card_settings_save_calibration(float sens_deviation, float offset, bool active);
|
||||
|
||||
/**
|
||||
@@ -34,6 +41,13 @@ int sd_card_settings_save_calibration(float sens_deviation, float offset, bool a
|
||||
*/
|
||||
int sd_card_settings_try_load_calibration(float *sens_deviation, float *offset);
|
||||
|
||||
/**
|
||||
* @brief Load the PID parameters from @ref SETTINGS_PID_PARAMETER_FILE
|
||||
* @param settings The PID settings
|
||||
* @return Load result.
|
||||
* @note This function is currently not used for the temperature profiles. They implement the PID parameters directy
|
||||
* inside the profile
|
||||
*/
|
||||
enum settings_load_result sd_card_settings_load_pid_oven_parameters(struct oven_pid_settings *settings);
|
||||
|
||||
#endif /* __SETTINGS_SETTINGS_SD_CARD_H__ */
|
||||
|
@@ -24,20 +24,26 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief Settings for the PID controller that are stored in the config file
|
||||
*/
|
||||
struct oven_pid_settings {
|
||||
float kd;
|
||||
float kp;
|
||||
float ki;
|
||||
float kd_tau;
|
||||
float t_sample;
|
||||
float max_integral;
|
||||
float kd; /**< @brief Derivate term */
|
||||
float kp; /**< @brief Proportional term */
|
||||
float ki; /**< @brief Integral term */
|
||||
float kd_tau; /**< @brief Time constant of the derivate term's low pass filter */
|
||||
float t_sample; /**< @brief Sampling time in seconds. @warning The loading function expects the file to hold a ms value */
|
||||
float max_integral; /**< @brief Maximum value of the intgral term */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Results of a setting loading
|
||||
*/
|
||||
enum settings_load_result {
|
||||
SETT_LOAD_SUCCESS = 0,
|
||||
SETT_LOAD_FILE_NOT_FOUND,
|
||||
SETT_LOAD_ERR,
|
||||
SETT_LOAD_DISK_ERR
|
||||
SETT_LOAD_SUCCESS = 0, /**< @brief Setting loaded successfully */
|
||||
SETT_LOAD_FILE_NOT_FOUND, /**< @brief File not found. This is only used by Settings on the SD card */
|
||||
SETT_LOAD_ERR, /**< @brief Generic loading error */
|
||||
SETT_LOAD_DISK_ERR, /**< @brief Disk access failure during loading */
|
||||
};
|
||||
|
||||
#define SETTINGS_PID_PARAMETER_FILE "pid.conf"
|
||||
@@ -50,14 +56,49 @@ enum settings_load_result {
|
||||
*/
|
||||
int settings_save_calibration(float sens_deviation, float offset, bool active);
|
||||
|
||||
/**
|
||||
* @brief Load the calibration
|
||||
*
|
||||
* If an EEPROM is present, it is first tried to be retrieved from EEPROM.
|
||||
* If there is no EEPROM or there is no valid data inside, it is tried to be loaded from SD card.
|
||||
*
|
||||
* @param sens_dev Sensiotivity deviation
|
||||
* @param offset Offset
|
||||
* @return 0 if successful and calibration valid
|
||||
*/
|
||||
int settings_load_calibration(float *sens_dev, float *offset);
|
||||
|
||||
/**
|
||||
* @brief Load PID overn parameters from file on SD card. This function is not implemented for EEPROM.
|
||||
* @param settings settings
|
||||
* @return Load result
|
||||
*/
|
||||
enum settings_load_result settings_load_pid_oven_parameters(struct oven_pid_settings *settings);
|
||||
|
||||
/**
|
||||
* @brief read the configured overtemperature limit
|
||||
* @param[out] over_temp_limit Overtemperature limit in degrees Celsius
|
||||
* @return Load result
|
||||
*/
|
||||
enum settings_load_result settings_load_overtemp_limit(float *over_temp_limit);
|
||||
|
||||
/**
|
||||
* @brief Save the overtemperature limit
|
||||
* @param over_temp_limit Limit in degrees Celsius
|
||||
* @param active Overtemperature limit active. If false: The config is delted and the controller uses its default limit
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int settings_save_overtemp_limit(float over_temp_limit, bool active);
|
||||
|
||||
/**
|
||||
* @brief Setup the settings module
|
||||
*
|
||||
* This function has to be called before performing any settings operations.
|
||||
* It checks if an EEPTROM is connected and sets the appropriate settings storage in this case.
|
||||
*
|
||||
* EEPROM storage will only be available for HW versions > 1.3. Some functions require an EEPROM because the counterpart
|
||||
* on the SD card is not defined. These functions will fail without an EEPROM.
|
||||
*/
|
||||
void settings_setup(void);
|
||||
|
||||
#endif /* __SETTINGS_SETTINGS_H__ */
|
||||
|
@@ -24,19 +24,59 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the SPI for the eeprom.
|
||||
* @return 0 if succesful
|
||||
*/
|
||||
int spi_eeprom_init();
|
||||
|
||||
/**
|
||||
* @brief Uninitialize the SPI EEPROM
|
||||
*/
|
||||
void spi_eeprom_deinit();
|
||||
|
||||
/**
|
||||
* @brief Read from SPI EEPROM
|
||||
* @param addr address to read from
|
||||
* @param rx_buff buffer to write data to
|
||||
* @param count Amount of bytes to read
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int spi_eeprom_read(uint32_t addr, uint8_t *rx_buff, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Check if the EEPROM is currently performing a write and therefore cannot serve other requests
|
||||
* @return true: Write in Progress, false: No write active
|
||||
*/
|
||||
bool spi_eeprom_write_in_progress(void);
|
||||
|
||||
/**
|
||||
* @brief Write data to the EEPROM
|
||||
* @param addr Address to write to
|
||||
* @param data Data to write
|
||||
* @param count Amount of bytes to write
|
||||
* @return 0 if successful
|
||||
* @note The page handling of the EEPROM is done internally. When using this function there is no need to worry about
|
||||
* the 16 byte page boundaries of the EEPROM
|
||||
*/
|
||||
int spi_eeprom_write(uint32_t addr, const uint8_t *data, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Read the status register of the EEPROM
|
||||
* @return status register
|
||||
*/
|
||||
uint8_t spi_eeprom_read_status_reg(void);
|
||||
|
||||
/**
|
||||
* @brief Check if an EEPROM is connected to the SPI.
|
||||
*
|
||||
* This is done by trying to set the write enable latch in the status register and reading it back.
|
||||
* After it has been set, it is immediately reset.
|
||||
*
|
||||
* If it can't be read back, no EEPROM is connected
|
||||
*
|
||||
* @return true: EEPROM is connected, false: No (compatible) EEPROM found
|
||||
*/
|
||||
bool spi_eeprom_check_connected(void);
|
||||
|
||||
#endif /* __SETTINGS_SPI_EEPROM_H__ */
|
||||
|
@@ -69,10 +69,28 @@ void systick_setup(void);
|
||||
*/
|
||||
void systick_wait_ms(uint32_t ms);
|
||||
|
||||
/**
|
||||
* @brief Get the gloabl millisecond tick. This can be used to implement all sorts of time based procedures / waits.
|
||||
* @warning Use this with care in interrupts. It may lead to race conditions. It is generally safe for use in standard program flow though.
|
||||
* @return Global millisecond tick.
|
||||
*/
|
||||
uint64_t systick_get_global_tick();
|
||||
|
||||
/**
|
||||
* @brief Calculate the uptime from the current millisecond tick.
|
||||
* @param[out] days Days. May be NULL.
|
||||
* @param[out] hours Hours. May be NULL.
|
||||
* @param[out] minutes Minutes. May be NULL.
|
||||
* @param[out] seconds Seconds. May be NULL.
|
||||
*/
|
||||
void systick_get_uptime_from_tick(uint32_t *days, uint32_t *hours, uint32_t *minutes, uint32_t *seconds);
|
||||
|
||||
/**
|
||||
* @brief Check if \p ticks (milliseconds) have passed since \p start_timestamp
|
||||
* @param start_timestamp Start timestamp
|
||||
* @param ticks tick count
|
||||
* @return true: Time has passed
|
||||
*/
|
||||
bool systick_ticks_have_passed(uint64_t start_timestamp, uint64_t ticks);
|
||||
|
||||
#endif /* __SYSTICK_H__ */
|
||||
|
@@ -24,7 +24,7 @@
|
||||
#include <stdint.h>
|
||||
#include <reflow-controller/config-parser/temp-profile-parser.h>
|
||||
|
||||
#define MAX_PROFILE_LENGTH 50
|
||||
#define MAX_PROFILE_LENGTH 64
|
||||
|
||||
enum tpe_status {
|
||||
TPE_OFF,
|
||||
|
@@ -21,6 +21,8 @@
|
||||
#ifndef _GUI_H_
|
||||
#define _GUI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Handle the reflow controller's LCD Menu
|
||||
* @return 0 if no delay is requested, 1 if delay is requested
|
||||
@@ -29,4 +31,8 @@ int gui_handle(void);
|
||||
|
||||
void gui_init(void);
|
||||
|
||||
void gui_root_menu_message_set(const char *heading, const char *text);
|
||||
|
||||
void gui_lcd_write_direct_blocking(uint8_t line, const char *text);
|
||||
|
||||
#endif /* _GUI_H_ */
|
||||
|
@@ -21,6 +21,8 @@
|
||||
#ifndef __LCD_H__
|
||||
#define __LCD_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LCD_DPORT (GPIOD)
|
||||
#define LCD_RCC_MASK RCC_AHB1ENR_GPIODEN
|
||||
#define LCD_DATA_BIT_OFFSET (8)
|
||||
@@ -48,6 +50,8 @@ void lcd_init(void);
|
||||
|
||||
void lcd_string(const char *data);
|
||||
|
||||
void lcd_setcursor(uint8_t x, uint8_t y);
|
||||
|
||||
void lcd_home(void);
|
||||
|
||||
enum lcd_fsm_ret lcd_fsm_write_buffer(const char (*display_buffer)[21]);
|
||||
|
@@ -25,7 +25,14 @@
|
||||
|
||||
/**
|
||||
* @brief Start the RAM Code of the updater. This function will never return!
|
||||
*
|
||||
* This function is called at startup when the controller detects, that an update should
|
||||
* be performed.
|
||||
*
|
||||
* @note You prabably want to call @ref start_updater function to update.
|
||||
*/
|
||||
void __attribute__((noreturn)) start_updater(void);
|
||||
void __attribute__((noreturn)) start_updater_ram_code(void);
|
||||
|
||||
void __attribute__((noreturn)) updater_update_from_file(const char *filename);
|
||||
|
||||
#endif /* __UPDATER_UPDATER_H__ */
|
||||
|
1
stm-firmware/linklist-lib
Submodule
1
stm-firmware/linklist-lib
Submodule
Submodule stm-firmware/linklist-lib added at 18b3ab377a
@@ -37,7 +37,7 @@
|
||||
#include <stm-periph/stm32-gpio-macros.h>
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <stm-periph/uart.h>
|
||||
#include <reflow-controller/shell-uart-config.h>
|
||||
#include <reflow-controller/periph-config/shell-uart-config.h>
|
||||
#include <reflow-controller/oven-driver.h>
|
||||
#include <fatfs/ff.h>
|
||||
#include <reflow-controller/ui/gui.h>
|
||||
@@ -155,6 +155,12 @@ static inline void handle_boot_status(void)
|
||||
res = safety_memory_get_boot_status(&status);
|
||||
if (res != 0)
|
||||
panic_mode();
|
||||
|
||||
if (status.reset_from_panic) {
|
||||
/* We've seen a panic */
|
||||
gui_root_menu_message_set("!! PANIC !!", "Check error me- mory!");
|
||||
}
|
||||
|
||||
if (status.reboot_to_bootloader) {
|
||||
status.reboot_to_bootloader = 0UL;
|
||||
safety_memory_set_boot_status(&status);
|
||||
@@ -162,7 +168,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]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +236,7 @@ int main(void)
|
||||
uint64_t quarter_sec_timestamp = 0ULL;
|
||||
static uint64_t IN_SECTION(.ccm.bss) main_loop_iter_count;
|
||||
|
||||
/* Setup all the peripherals and external componets like LCD, EEPROM etc. and the safety controller */
|
||||
setup_system();
|
||||
|
||||
/* Try load the calibration. This will only succeed if there's an EEPROM */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -42,6 +42,7 @@
|
||||
#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"
|
||||
@@ -52,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)
|
||||
@@ -182,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 {
|
||||
@@ -344,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");
|
||||
@@ -631,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;
|
||||
}
|
||||
@@ -876,7 +899,7 @@ static shellmatta_cmd_t cmd[21] = {
|
||||
{
|
||||
.cmd = "save-calibration",
|
||||
.cmdAlias = "save-cal",
|
||||
.helpText = "",
|
||||
.helpText = "Permanently save the calibration to EEPROM",
|
||||
.usageText = "",
|
||||
.cmdFct = shell_cmd_save_cal,
|
||||
.next = &cmd[13],
|
||||
@@ -924,8 +947,8 @@ static shellmatta_cmd_t cmd[21] = {
|
||||
{
|
||||
.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],
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -106,7 +106,8 @@ bool __attribute__((optimize("O3"))) systick_ticks_have_passed(uint64_t start_ti
|
||||
/**
|
||||
* @brief Interrupt Handler for SysTick
|
||||
*
|
||||
* This handler is called every millisecond
|
||||
* This handler is called every 100 us.
|
||||
* It is important to keep this function simple as it is called that often and may prevent program flow.
|
||||
*
|
||||
* @warning For calling cyclic functions use separate timers/flags and don't spoil this function
|
||||
*/
|
||||
|
@@ -26,6 +26,8 @@
|
||||
#include <reflow-controller/adc-meas.h>
|
||||
#include <reflow-controller/digio.h>
|
||||
|
||||
#include <reflow-controller/safety/safety-controller.h>
|
||||
|
||||
static struct tpe_current_state IN_SECTION(.ccm.data) state = {
|
||||
.status = TPE_OFF,
|
||||
.start_timestamp = 0,
|
||||
@@ -34,7 +36,7 @@ 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 struct pl_command IN_SECTION(.ccm.bss) cmd_list[MAX_PROFILE_LENGTH];
|
||||
static SlList *command_list = NULL;
|
||||
|
||||
static void tpe_abort(void)
|
||||
{
|
||||
@@ -59,11 +61,19 @@ enum pl_ret_val temp_profile_executer_start(const char *filename)
|
||||
state.profile_steps = 0;
|
||||
cmd_continue = false;
|
||||
|
||||
res = temp_profile_parse_from_file(filename, cmd_list, MAX_PROFILE_LENGTH, &parsed_count);
|
||||
/* 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;
|
||||
@@ -153,6 +163,22 @@ static bool cmd_ramp(struct pl_command *cmd, bool cmd_continue)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cmd_ack_flags(void)
|
||||
{
|
||||
uint32_t flag_cnt;
|
||||
uint32_t i;
|
||||
enum safety_flag flag_enum;
|
||||
bool status;
|
||||
|
||||
flag_cnt = safety_controller_get_flag_count();
|
||||
for (i = 0; i < flag_cnt; i++) {
|
||||
safety_controller_get_flag_by_index(i, &status, &flag_enum);
|
||||
if (status)
|
||||
(void)safety_controller_ack_flag(flag_enum);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int temp_profile_executer_handle(void)
|
||||
{
|
||||
struct pl_command *current_cmd;
|
||||
@@ -177,7 +203,7 @@ int temp_profile_executer_handle(void)
|
||||
if (!systick_ticks_have_passed(last_tick, 100))
|
||||
return 0;
|
||||
|
||||
current_cmd = &cmd_list[state.step];
|
||||
current_cmd = (struct pl_command *)sl_list_nth(command_list, state.step)->data;
|
||||
next_step = state.step;
|
||||
|
||||
switch (current_cmd->cmd) {
|
||||
@@ -214,6 +240,10 @@ int temp_profile_executer_handle(void)
|
||||
case PL_SET_RAMP:
|
||||
advance = cmd_ramp(current_cmd, cmd_continue);
|
||||
break;
|
||||
case PL_CLEAR_FLAGS:
|
||||
cmd_ack_flags();
|
||||
advance = true;
|
||||
break;
|
||||
default:
|
||||
tpe_abort();
|
||||
advance = true;
|
||||
@@ -250,6 +280,10 @@ int temp_profile_executer_stop(void)
|
||||
oven_pid_stop();
|
||||
}
|
||||
|
||||
/* Free the command list */
|
||||
if (command_list)
|
||||
temp_profile_free_command_list(&command_list);
|
||||
|
||||
loudspeaker_set(0);
|
||||
|
||||
return 0;
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <reflow-controller/safety/safety-controller.h>
|
||||
#include <reflow-controller/settings/settings.h>
|
||||
#include <reflow-controller/temp-converter.h>
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/unique-id.h>
|
||||
#include <stddef.h>
|
||||
@@ -38,6 +39,8 @@
|
||||
#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;
|
||||
@@ -374,43 +377,38 @@ static void gui_menu_constant_temperature_driver(struct lcd_menu *menu, enum men
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief load_temperature_file_list_from_sdcard
|
||||
* @return -1 File error, 0 successful
|
||||
*/
|
||||
static int load_temperature_file_list_from_sdcard(char (*list)[17], uint32_t len)
|
||||
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)
|
||||
{
|
||||
uint32_t i, j;
|
||||
DIR directory;
|
||||
FILINFO finfo;
|
||||
FRESULT fres;
|
||||
|
||||
if (!list)
|
||||
return -1001;
|
||||
|
||||
/* Zero out the list */
|
||||
for (i = 0; i < len; i++) {
|
||||
for (j = 0; j < sizeof(*list); j++) {
|
||||
list[i][j] = 0;
|
||||
}
|
||||
}
|
||||
SlList *list = NULL;
|
||||
char *name;
|
||||
|
||||
/* find the frist file */
|
||||
fres = f_findfirst(&directory, &finfo, "/", "*.tpr");
|
||||
i = 0;
|
||||
fres = f_findfirst(&directory, &finfo, "/", file_pattern);
|
||||
while (fres == FR_OK && finfo.fname[0]) {
|
||||
strncpy(list[i], finfo.fname, sizeof(*list));
|
||||
name = malloc(strlen(finfo.fname) + 1);
|
||||
strcpy(name, finfo.fname);
|
||||
list = sl_list_append(list, name);
|
||||
fres = f_findnext(&directory, &finfo);
|
||||
i++;
|
||||
if (i >= len)
|
||||
break;
|
||||
}
|
||||
|
||||
if (fres != FR_OK) {
|
||||
return -1;
|
||||
sl_list_free_full(list, delete_file_list_entry);
|
||||
list = NULL;
|
||||
if (error)
|
||||
*error = -1;
|
||||
}
|
||||
|
||||
return (int)i;
|
||||
return list;
|
||||
}
|
||||
|
||||
static void gui_menu_temp_profile_execute(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void* parent)
|
||||
@@ -428,23 +426,32 @@ static void gui_menu_temp_profile_execute(struct lcd_menu *menu, enum menu_entry
|
||||
last_tick = 0ULL;
|
||||
}
|
||||
|
||||
if (systick_ticks_have_passed(last_tick, 300)) {
|
||||
if (systick_ticks_have_passed(last_tick, 250)) {
|
||||
state = temp_profile_executer_status();
|
||||
if (state->status == TPE_RUNNING)
|
||||
menu_lcd_outputf(menu, 0, "Profile running");
|
||||
else if (state->status == TPE_OFF) {
|
||||
menu_lcd_outputf(menu, 0, "Profile finished");
|
||||
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, "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%.0f", (res < 0 ? "<" : (res > 0 ? ">" : "")), temperature);
|
||||
if (oven_pid_get_status() == OVEN_PID_RUNNING) {
|
||||
menu_lcd_outputf(menu, 3, "Target: %.0f", state->setpoint);
|
||||
} else {
|
||||
menu_lcd_outputf(menu, 3, "Temp Off");
|
||||
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();
|
||||
@@ -461,29 +468,29 @@ static void gui_menu_temp_profile_execute(struct lcd_menu *menu, enum menu_entry
|
||||
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 char profile_list[10][17];
|
||||
static bool file_error = false;
|
||||
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;
|
||||
|
||||
int res;
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
|
||||
menu_display_clear(menu);
|
||||
my_parent = parent;
|
||||
res = load_temperature_file_list_from_sdcard(profile_list, 10);
|
||||
file_error = false;
|
||||
file_error = 0;
|
||||
loaded = 0;
|
||||
if (res < 0) {
|
||||
file_error = true;
|
||||
|
||||
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 = (uint32_t)res;
|
||||
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);
|
||||
@@ -501,14 +508,20 @@ static void gui_menu_temp_profile_select(struct lcd_menu *menu, enum menu_entry_
|
||||
if (file_error) {
|
||||
menu_lcd_outputf(menu, 0, "Disk Error");
|
||||
menu_lcd_outputf(menu, 1, "SD inserted?");
|
||||
if (button == BUTTON_SHORT_RELEASED)
|
||||
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)
|
||||
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");
|
||||
@@ -528,17 +541,22 @@ static void gui_menu_temp_profile_select(struct lcd_menu *menu, enum menu_entry_
|
||||
break;
|
||||
}
|
||||
|
||||
if (button == BUTTON_SHORT_RELEASED)
|
||||
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", &profile_list[currently_selected][0]);
|
||||
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(&profile_list[currently_selected][0]);
|
||||
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;
|
||||
}
|
||||
@@ -590,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;
|
||||
@@ -601,6 +734,7 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
|
||||
"Monitoring",
|
||||
"Error Flags",
|
||||
"About",
|
||||
"Update",
|
||||
NULL
|
||||
};
|
||||
static const menu_func_t root_entry_funcs[] = {
|
||||
@@ -609,6 +743,7 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
|
||||
gui_menu_monitor,
|
||||
gui_menu_err_flags,
|
||||
gui_menu_about,
|
||||
gui_update_firmware,
|
||||
};
|
||||
enum button_state push_button;
|
||||
int16_t rot_delta;
|
||||
@@ -630,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()
|
||||
@@ -677,5 +819,37 @@ void gui_init()
|
||||
button_init();
|
||||
lcd_init();
|
||||
|
||||
if (overlay_heading)
|
||||
free(overlay_heading);
|
||||
if (overlay_text)
|
||||
free(overlay_text);
|
||||
|
||||
overlay_heading = NULL;
|
||||
overlay_text = NULL;
|
||||
|
||||
menu_init(reflow_menu_ptr, gui_menu_root_entry, update_display_buffer);
|
||||
}
|
||||
|
||||
void gui_root_menu_message_set(const char *heading, const char *text)
|
||||
{
|
||||
if (heading) {
|
||||
overlay_heading = (char *)malloc(strlen(heading) + 1);
|
||||
strcpy(overlay_heading, heading);
|
||||
}
|
||||
|
||||
if (text) {
|
||||
overlay_text = (char *)malloc(strlen(text) + 1);
|
||||
strcpy(overlay_text, text);
|
||||
}
|
||||
}
|
||||
|
||||
void gui_lcd_write_direct_blocking(uint8_t line, const char *text)
|
||||
{
|
||||
if (!text)
|
||||
return;
|
||||
if (line > 3)
|
||||
return;
|
||||
|
||||
lcd_setcursor(0, line);
|
||||
lcd_string(text);
|
||||
}
|
||||
|
@@ -39,7 +39,8 @@ void menu_handle(struct lcd_menu *menu, int16_t rotary_encoder_delta, enum butto
|
||||
|
||||
tmp = menu->active_entry;
|
||||
|
||||
if (menu->active_entry_type == MENU_ENTRY_FIRST_ENTER && push_button != BUTTON_IDLE) {
|
||||
if ((menu->active_entry_type == MENU_ENTRY_FIRST_ENTER || menu->active_entry_type == MENU_ENTRY_DROPBACK)
|
||||
&& push_button != BUTTON_IDLE) {
|
||||
menu->inputs.button_ready = false;
|
||||
}
|
||||
|
||||
|
@@ -13,13 +13,15 @@ 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})
|
||||
add_executable(${ELFFILE} ${SRCS} ${FATFS_SRCS} ${SDIO_SRCS} ${STM_PERIPH_SRCS} ${SAFETY_MEMORY_SRCS})
|
||||
|
||||
target_include_directories(${ELFFILE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
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 -DBASE64_LOOKUP_TABLE_SECTION=\".ccm.bss\" -DSHELLMATTA_HELP_ALIAS=\"?\" -DGIT_VER=${GIT_DESCRIBE} -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4)
|
||||
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")
|
||||
|
134
stm-firmware/updater/ram-code/flash-writer.c
Normal file
134
stm-firmware/updater/ram-code/flash-writer.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "flash-writer.h"
|
||||
#include <stm32/stm32f4xx.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static bool flash_op_busy(void)
|
||||
{
|
||||
return !!(FLASH->SR & FLASH_SR_BSY);
|
||||
}
|
||||
|
||||
static void lock_flash_cr(void)
|
||||
{
|
||||
FLASH->CR |= FLASH_CR_LOCK;
|
||||
}
|
||||
|
||||
void flash_writer_enable_access(void)
|
||||
{
|
||||
const uint32_t key1 = 0x45670123UL;
|
||||
const uint32_t key2 = 0xCDEF89ABUL;
|
||||
|
||||
FLASH->KEYR = key1;
|
||||
FLASH->KEYR = key2;
|
||||
}
|
||||
|
||||
void flash_writer_perform_mass_erase(void)
|
||||
{
|
||||
flash_writer_enable_access();
|
||||
while (flash_op_busy());
|
||||
|
||||
FLASH->CR = DMA_SxCR_PSIZE_1;
|
||||
FLASH->CR |= FLASH_CR_MER;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
|
||||
while(flash_op_busy());
|
||||
lock_flash_cr();
|
||||
}
|
||||
|
||||
uint32_t flash_writer_get_flash_size(void)
|
||||
{
|
||||
uint32_t flash_size;
|
||||
const uint16_t *flash_size_ptr = (const uint16_t *)0x1FFF7A22UL;
|
||||
|
||||
flash_size = (uint32_t)*flash_size_ptr;
|
||||
flash_size *= 1024;
|
||||
|
||||
return flash_size;
|
||||
}
|
||||
|
||||
int flash_writer_write_to_memory(void *dest, const void *src, uint32_t size)
|
||||
{
|
||||
uint32_t full_word_cnt;
|
||||
uint32_t byte_cnt;
|
||||
uint32_t idx;
|
||||
const uint32_t *word_src_ptr;
|
||||
uint32_t *word_dest_ptr;
|
||||
const char *char_src_ptr;
|
||||
char *char_dest_ptr;
|
||||
uint32_t pre_byte_count;
|
||||
|
||||
flash_writer_enable_access();
|
||||
while (flash_op_busy());
|
||||
|
||||
/* Number of full words to program */
|
||||
full_word_cnt = size / 4u;
|
||||
byte_cnt = size % 4;
|
||||
|
||||
word_dest_ptr = dest;
|
||||
word_src_ptr = src;
|
||||
|
||||
/* Do the first bytes, in case the destination is not word aligned */
|
||||
pre_byte_count = (4 - ((uint32_t)dest % 4u)) % 4;
|
||||
if (pre_byte_count) {
|
||||
FLASH->CR = 0u;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
char_src_ptr = src;
|
||||
char_dest_ptr = dest;
|
||||
|
||||
/* Write bytes to memory until we hit the next word aligned address */
|
||||
for (idx = 0; idx < pre_byte_count; idx++) {
|
||||
*(char_dest_ptr++) = *(char_src_ptr++);
|
||||
}
|
||||
|
||||
/* Correct the word addresses set above */
|
||||
full_word_cnt = (size - pre_byte_count) / 4u;
|
||||
byte_cnt = (size - pre_byte_count) % 4;
|
||||
word_dest_ptr = (uint32_t *)char_dest_ptr;
|
||||
word_src_ptr = (uint32_t *)char_src_ptr;
|
||||
|
||||
while(flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
/* Do the full word flash write */
|
||||
if (full_word_cnt) {
|
||||
FLASH->CR = FLASH_CR_PSIZE_1;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
for (idx = 0; idx < full_word_cnt; idx++) {
|
||||
*word_dest_ptr = *word_src_ptr;
|
||||
word_dest_ptr++;
|
||||
word_src_ptr++;
|
||||
}
|
||||
|
||||
while (flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
/* write remaining bytes */
|
||||
if (byte_cnt) {
|
||||
char_src_ptr = (char *)word_src_ptr;
|
||||
char_dest_ptr = (char *)word_dest_ptr;
|
||||
|
||||
FLASH->CR = 0u;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
for (idx = 0; idx < byte_cnt; idx++) {
|
||||
*char_dest_ptr = *char_src_ptr;
|
||||
char_dest_ptr++;
|
||||
char_src_ptr++;
|
||||
}
|
||||
|
||||
while (flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
lock_flash_cr();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t flash_writer_get_base_address(void)
|
||||
{
|
||||
return FLASH_BASE;
|
||||
}
|
16
stm-firmware/updater/ram-code/flash-writer.h
Normal file
16
stm-firmware/updater/ram-code/flash-writer.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _FLASH_WRITER_H_
|
||||
#define _FLASH_WRITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void flash_writer_enable_access(void);
|
||||
|
||||
void flash_writer_perform_mass_erase(void);
|
||||
|
||||
uint32_t flash_writer_get_flash_size(void);
|
||||
|
||||
uint32_t flash_writer_get_base_address(void);
|
||||
|
||||
int flash_writer_write_to_memory(void *dest, const void *src, uint32_t size);
|
||||
|
||||
#endif /* _FLASH_WRITER_H_ */
|
@@ -1,72 +1,74 @@
|
||||
#include "hex-parser.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
static int convert_hex_char_to_value(char c, uint32_t *out)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t value = 0;
|
||||
int ret = 0;
|
||||
uint32_t value = 0;
|
||||
|
||||
if (!out)
|
||||
return -1002;
|
||||
if (!out)
|
||||
return -1002;
|
||||
|
||||
switch (c) {
|
||||
case '0' ... '9':
|
||||
value = (uint32_t)c - (uint32_t)'0';
|
||||
break;
|
||||
case 'a' ... 'f':
|
||||
/* Convert to upper */
|
||||
c -= 0x20;
|
||||
/* FALLTHRU */
|
||||
case 'A' ... 'F':
|
||||
value = (uint32_t)c - (uint32_t)'A' + 10UL;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
switch (c) {
|
||||
case '0' ... '9':
|
||||
value = (uint32_t)c - (uint32_t)'0';
|
||||
break;
|
||||
case 'a' ... 'f':
|
||||
/* Convert to upper */
|
||||
c -= 0x20;
|
||||
/* FALLTHRU */
|
||||
case 'A' ... 'F':
|
||||
value = (uint32_t)c - (uint32_t)'A' + 10UL;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
*out = value;
|
||||
if (ret == 0)
|
||||
*out = value;
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int convert_big_endian_hex_string_to_val(const char *string, size_t len, uint32_t *out)
|
||||
{
|
||||
int ret_val = -1;
|
||||
uint32_t converted_value = 0UL;
|
||||
uint32_t digit;
|
||||
int res;
|
||||
unsigned int i;
|
||||
int ret_val = -1;
|
||||
uint32_t converted_value = 0UL;
|
||||
uint32_t digit;
|
||||
int res;
|
||||
unsigned int i;
|
||||
|
||||
/* Return error in case of an input error */
|
||||
if (!string || !len)
|
||||
goto exit;
|
||||
/* Return error in case of an input error */
|
||||
if (!string || !len)
|
||||
goto exit;
|
||||
|
||||
if (!out)
|
||||
return -1003;
|
||||
if (!out)
|
||||
return -1003;
|
||||
|
||||
/* we don't support strings larger than 8 chars */
|
||||
if (len > 8)
|
||||
goto exit;
|
||||
/* we don't support strings larger than 8 chars */
|
||||
if (len > 8)
|
||||
goto exit;
|
||||
|
||||
|
||||
for (i = 0; i < len && string[i] != '\0'; i++) {
|
||||
/* Convert current character to number */
|
||||
res = convert_hex_char_to_value(string[i], &digit);
|
||||
if (res) {
|
||||
/* Not a hex number */
|
||||
ret_val = -2;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < len && string[i] != '\0'; i++) {
|
||||
/* Convert current character to number */
|
||||
res = convert_hex_char_to_value(string[i], &digit);
|
||||
if (res) {
|
||||
/* Not a hex number */
|
||||
ret_val = -2;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
converted_value *= 0x10;
|
||||
converted_value += digit;
|
||||
}
|
||||
converted_value *= 0x10;
|
||||
converted_value += digit;
|
||||
}
|
||||
|
||||
*out = converted_value;
|
||||
*out = converted_value;
|
||||
ret_val = 0;
|
||||
|
||||
exit:
|
||||
return ret_val;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_name)
|
||||
@@ -85,7 +87,179 @@ enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_
|
||||
return HEX_PARSER_OK;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len);
|
||||
static int read_line_from_file(FIL *file, char *data, int size)
|
||||
{
|
||||
char *ret_ptr;
|
||||
int length;
|
||||
|
||||
ret_ptr = f_gets(data, size, file);
|
||||
if (!ret_ptr)
|
||||
return -1;
|
||||
|
||||
/* To be sure */
|
||||
data[size - 1] = 0;
|
||||
length = strlen(ret_ptr);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static int hex_record_check_checksum(const char *buff, int hex_byte_count)
|
||||
{
|
||||
int i;
|
||||
int res;
|
||||
uint32_t checksum = 0;
|
||||
uint32_t tmp;
|
||||
|
||||
if (!buff || !hex_byte_count)
|
||||
return -1000;
|
||||
|
||||
for (i = 0; i < hex_byte_count; i++) {
|
||||
res = convert_big_endian_hex_string_to_val(&buff[2 * i], 2, &tmp);
|
||||
if (res)
|
||||
return -1;
|
||||
checksum += tmp;
|
||||
}
|
||||
|
||||
if (checksum & 0xFF) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len,
|
||||
size_t *lenout)
|
||||
{
|
||||
static char workbuff[512];
|
||||
int count;
|
||||
int i;
|
||||
enum hex_parser_ret retval = HEX_PARSER_DATA_OK;
|
||||
uint32_t hex_addr;
|
||||
uint32_t byte_count;
|
||||
uint32_t record_type;
|
||||
uint32_t tmp;
|
||||
|
||||
if (!parser || !lenout || !data_len || !data || !address)
|
||||
return HEX_PARSER_ERROR;
|
||||
|
||||
/* Read a line from the file */
|
||||
count = read_line_from_file(&parser->file, workbuff, sizeof(workbuff));
|
||||
if (!count) {
|
||||
/* Check for error in case nothing is read */
|
||||
if (f_error(&parser->file)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
} else if (f_eof(&parser->file)) {
|
||||
retval = HEX_PARSER_FILE_END;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip out invalid characters at the end */
|
||||
for (i = count - 1; i >= 0; i--) {
|
||||
if (workbuff[i] == '\r' || workbuff[i] == '\n' ||
|
||||
workbuff[i] == '\t' || workbuff[i] == ' ')
|
||||
{
|
||||
workbuff[i] = 0;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* We read a valid line, check for valid marker */
|
||||
if (workbuff[0] != ':') {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Line has to be longer than 11 chars in total */
|
||||
if (count < 11) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the data count */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[1], 2, &byte_count)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the address */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[3], 4, &hex_addr)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the record type */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[7], 2, &record_type)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (byte_count * 2 + 9 + 2 != (unsigned int)count) {
|
||||
/* Line not the expected length */
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Check the checksum. We have bytecount + 5 bytes in a record */
|
||||
if (hex_record_check_checksum(&workbuff[1], byte_count + 5)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Check record type */
|
||||
switch (record_type) {
|
||||
case 0x00: /* Data */
|
||||
if (byte_count > data_len) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*lenout = 0;
|
||||
*address = hex_addr + parser->current_address_offset;
|
||||
|
||||
for (i = 0; i < (int)byte_count; i++) {
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[9 + 2*i], 2, &tmp)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*data = (char)(tmp & 0xFF);
|
||||
data++;
|
||||
(*lenout)++;
|
||||
}
|
||||
retval = HEX_PARSER_DATA_OK;
|
||||
break;
|
||||
case 0x01: /* End of file */
|
||||
retval = HEX_PARSER_EOF_RECORD;
|
||||
break;
|
||||
case 0x04: /* extended linear address */
|
||||
if (byte_count != 2) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Parse the upper 16 bit of the address */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[9], 4, &tmp)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
parser->current_address_offset = tmp << 16;
|
||||
retval = HEX_PARSER_OK;
|
||||
break;
|
||||
case 0x05:
|
||||
retval = HEX_PARSER_OK;
|
||||
break;
|
||||
default:
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_close(struct hex_parser *parser) {
|
||||
if (!parser)
|
||||
|
@@ -6,10 +6,11 @@
|
||||
#include <stddef.h>
|
||||
|
||||
enum hex_parser_ret {
|
||||
HEX_PARSER_OK,
|
||||
HEX_PARSER_OK = 0,
|
||||
HEX_PARSER_DATA_OK,
|
||||
HEX_PARSER_ERROR,
|
||||
HEX_PARSER_FILE_END,
|
||||
HEX_PARSER_EOF_RECORD,
|
||||
};
|
||||
|
||||
struct hex_parser {
|
||||
@@ -19,7 +20,8 @@ struct hex_parser {
|
||||
|
||||
enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_name);
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len);
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len,
|
||||
size_t *len_out);
|
||||
|
||||
enum hex_parser_ret hex_parser_close(struct hex_parser *parser);
|
||||
|
||||
|
53
stm-firmware/updater/ram-code/itoa.c
Normal file
53
stm-firmware/updater/ram-code/itoa.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "itoa.h"
|
||||
|
||||
/*
|
||||
* The heapless_itoa function is copied from the shellmatta project.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief itoa like function to convert int to an ascii string
|
||||
* @warning you have to provide a large enough buffer
|
||||
* @param[in] value
|
||||
* @param[in,out] buffer
|
||||
* @param[in] base
|
||||
* @return number of bytes in string
|
||||
*/
|
||||
uint32_t heapless_itoa(int32_t value, char *buffer, uint32_t base)
|
||||
{
|
||||
char tempBuffer[34u];
|
||||
uint32_t i;
|
||||
uint32_t bufferIdx = 0u;
|
||||
int8_t digitValue;
|
||||
|
||||
/** -# check the base for plausibility */
|
||||
if((base >= 2) && (base <= 16))
|
||||
{
|
||||
/** -# check for sign */
|
||||
if(value < 0)
|
||||
{
|
||||
value = value * (-1);
|
||||
buffer[0u] = '-';
|
||||
bufferIdx += 1u;
|
||||
}
|
||||
|
||||
/** -# loop through all digits in reverse order */
|
||||
i = 0u;
|
||||
do
|
||||
{
|
||||
digitValue = (int8_t) (value % base);
|
||||
tempBuffer[i] = (digitValue < 10) ? ('0' + digitValue) : ('A' + (digitValue - 10));
|
||||
value /= base;
|
||||
i ++;
|
||||
}while(value > 0);
|
||||
|
||||
/** -# store the string in the correct order onto the buffer */
|
||||
while(i > 0u)
|
||||
{
|
||||
buffer[bufferIdx] = tempBuffer[i - 1u];
|
||||
i --;
|
||||
bufferIdx ++;
|
||||
}
|
||||
}
|
||||
return bufferIdx;
|
||||
}
|
||||
|
8
stm-firmware/updater/ram-code/itoa.h
Normal file
8
stm-firmware/updater/ram-code/itoa.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef _ITOA_H_
|
||||
#define _ITOA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t heapless_itoa(int32_t value, char *buffer, uint32_t base);
|
||||
|
||||
#endif /* _ITOA_H_ */
|
@@ -3,7 +3,19 @@
|
||||
#include <cmsis/core_cm4.h>
|
||||
#include "hex-parser.h"
|
||||
#include <fatfs/ff.h>
|
||||
|
||||
/* This is used to get the defines for the external watchdog */
|
||||
#include <reflow-controller/safety/safety-config.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/stm32-gpio-macros.h>
|
||||
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include "flash-writer.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "uart.h"
|
||||
#include "itoa.h"
|
||||
|
||||
static volatile unsigned int wait_tick;
|
||||
|
||||
@@ -12,6 +24,14 @@ static void watchdog_ack(void)
|
||||
IWDG->KR = 0xAAAA;
|
||||
}
|
||||
|
||||
static void external_watchdog_disable(void)
|
||||
{
|
||||
RCC->AHB1ENR |= SAFETY_EXT_WATCHDOG_RCC_MASK;
|
||||
__DSB();
|
||||
/* Set Pin to input. This disables the external watchdog. */
|
||||
SAFETY_EXT_WATCHDOG_PORT->MODER &= MODER_DELETE(SAFETY_EXT_WATCHDOG_PIN);
|
||||
}
|
||||
|
||||
void sdio_wait_ms(unsigned int ms)
|
||||
{
|
||||
wait_tick = 0;
|
||||
@@ -23,24 +43,247 @@ static FATFS _fs;
|
||||
|
||||
static void __attribute__((noreturn)) ram_code_exit(bool updated)
|
||||
{
|
||||
(void)updated;
|
||||
struct safety_memory_boot_status boot_status;
|
||||
safety_memory_get_boot_status(&boot_status);
|
||||
boot_status.code_updated = updated ? 0xFFFFFFFFUL : 0x0UL;
|
||||
boot_status.reboot_to_bootloader = 0x0UL;
|
||||
safety_memory_set_boot_status(&boot_status);
|
||||
|
||||
uart_send_string("Rebooting in 1s...\r\n");
|
||||
sdio_wait_ms(1000);
|
||||
|
||||
NVIC_SystemReset();
|
||||
while(1);
|
||||
}
|
||||
|
||||
static int check_hex_file(const char *fname, uint32_t *update_size)
|
||||
{
|
||||
enum hex_parser_ret hex_ret;
|
||||
struct hex_parser parser;
|
||||
uint32_t addr;
|
||||
char data[128];
|
||||
size_t dlen;
|
||||
int retval = -1;
|
||||
uint32_t flash_base;
|
||||
uint32_t flash_top;
|
||||
uint32_t total_size = 0UL;
|
||||
|
||||
flash_base = flash_writer_get_base_address();
|
||||
flash_top = flash_base + flash_writer_get_flash_size();
|
||||
|
||||
hex_ret = hex_parser_open(&parser, fname);
|
||||
if (hex_ret != HEX_PARSER_OK) {
|
||||
retval = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
do {
|
||||
hex_ret = hex_parser_parse(&parser, &addr, data, sizeof(data), &dlen);
|
||||
if (hex_ret == HEX_PARSER_DATA_OK) {
|
||||
if (addr < flash_base || addr+dlen >= flash_top) {
|
||||
retval = -2;
|
||||
goto ret_close_parser;
|
||||
}
|
||||
total_size += dlen;
|
||||
}
|
||||
} while (hex_ret == HEX_PARSER_DATA_OK || hex_ret == HEX_PARSER_OK);
|
||||
|
||||
if (hex_ret == HEX_PARSER_EOF_RECORD) {
|
||||
retval = 0;
|
||||
if (update_size)
|
||||
*update_size = total_size;
|
||||
}
|
||||
|
||||
ret_close_parser:
|
||||
hex_parser_close(&parser);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int write_flash_from_buffer(const char *buffer, uint32_t len, uint32_t addr)
|
||||
{
|
||||
int res;
|
||||
uint32_t i;
|
||||
const char *verify_ptr = (const char *)addr;
|
||||
|
||||
res = flash_writer_write_to_memory((void *)addr, buffer, len);
|
||||
if (res) {
|
||||
uart_send_string("Error writing to flash!\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Verify the write */
|
||||
for (i = 0; i < len; i++, verify_ptr++) {
|
||||
if (*verify_ptr != buffer[i]) {
|
||||
uart_send_string("Error verifying written data!\r\n");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_flash_from_file(const char *fname)
|
||||
{
|
||||
enum hex_parser_ret hex_ret;
|
||||
struct hex_parser parser;
|
||||
static char write_buffer[4096];
|
||||
uint32_t wbuffer_base_addr = 0;
|
||||
uint32_t wbuffer_fill_level = 0;
|
||||
uint32_t addr;
|
||||
static char tmp_buff[256];
|
||||
size_t dlen;
|
||||
int retval = 0;
|
||||
int res;
|
||||
|
||||
hex_ret = hex_parser_open(&parser, fname);
|
||||
if (hex_ret != HEX_PARSER_OK) {
|
||||
uart_send_string("Error reading hex file.\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
hex_ret = hex_parser_parse(&parser, &addr, tmp_buff, sizeof(tmp_buff), &dlen);
|
||||
if (hex_ret == HEX_PARSER_DATA_OK) {
|
||||
/* Check if tmp would fit in wbuffer */
|
||||
if (dlen + wbuffer_fill_level > sizeof(write_buffer)) {
|
||||
/* Write out the buffer and clean it if it doens't fit */
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
wbuffer_fill_level = 0;
|
||||
wbuffer_base_addr = 0;
|
||||
}
|
||||
|
||||
/* Check if parsed data can be linearily appended to buffer */
|
||||
if (wbuffer_fill_level && wbuffer_base_addr + wbuffer_fill_level != addr) {
|
||||
/* Write out the buffer and clean it if it cannot be appended */
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
wbuffer_fill_level = 0;
|
||||
wbuffer_base_addr = 0;
|
||||
}
|
||||
|
||||
/* Fill in the data into the buffer */
|
||||
if (wbuffer_fill_level == 0) {
|
||||
wbuffer_base_addr = addr;
|
||||
}
|
||||
memcpy(&write_buffer[wbuffer_fill_level], tmp_buff, dlen);
|
||||
wbuffer_fill_level += dlen;
|
||||
}
|
||||
} while (hex_ret == HEX_PARSER_DATA_OK || hex_ret == HEX_PARSER_OK);
|
||||
|
||||
if (hex_ret == HEX_PARSER_EOF_RECORD) {
|
||||
if (wbuffer_fill_level > 0) {
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
} else {
|
||||
retval = -3;
|
||||
}
|
||||
|
||||
exit_parser_close:
|
||||
hex_parser_close(&parser);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int ram_code_main(void)
|
||||
{
|
||||
FRESULT fres;
|
||||
int res;
|
||||
enum safety_memory_state safety_mem_state;
|
||||
static char filename[256];
|
||||
static char tmp_buff[256];
|
||||
uint32_t count;
|
||||
uint32_t update_size;
|
||||
int retries = 3;
|
||||
|
||||
|
||||
SysTick_Config(168000UL);
|
||||
external_watchdog_disable();
|
||||
__enable_irq();
|
||||
|
||||
fres = f_mount(fs, "0:/", 1);
|
||||
if (fres != FR_OK) {
|
||||
|
||||
/* Init the uart module
|
||||
* Pins don't need configuration. They're already setup by the main program
|
||||
*/
|
||||
uart_init();
|
||||
|
||||
/* Clear display and set cursor to home position */
|
||||
uart_send_string("\e[2J\e[H");
|
||||
uart_send_string("Updater started.\r\n");
|
||||
|
||||
res = safety_memory_init(&safety_mem_state);
|
||||
if (res || safety_mem_state != SAFETY_MEMORY_INIT_VALID_MEMORY) {
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
fres = f_mount(fs, "0:/", 1);
|
||||
if (fres != FR_OK) {
|
||||
uart_send_string("Could not mount SD card\r\n");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
res = safety_memory_get_update_filename(filename, NULL);
|
||||
if (res)
|
||||
ram_code_exit(false);
|
||||
|
||||
uart_send_string("Checking hex file ");
|
||||
uart_send_string(filename);
|
||||
uart_send_string("\r\n");
|
||||
if (check_hex_file(filename, &update_size)) {
|
||||
uart_send_string("Error in hex file\r\n");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
uart_send_string("File ");
|
||||
uart_send_string(filename);
|
||||
uart_send_string(" checked successfully.\r\n");
|
||||
count = heapless_itoa(update_size, tmp_buff, 10);
|
||||
if (count > 0) {
|
||||
tmp_buff[count] = 0;
|
||||
uart_send_string("Update size: ");
|
||||
uart_send_string(tmp_buff);
|
||||
uart_send_string(" bytes\r\n");
|
||||
}
|
||||
|
||||
uart_send_string("Starting updater...\r\n");
|
||||
|
||||
/* disable the ART caches */
|
||||
FLASH->ACR &= ~FLASH_ACR_DCEN;
|
||||
FLASH->ACR &= ~FLASH_ACR_ICEN;
|
||||
FLASH->ACR |= FLASH_ACR_DCRST | FLASH_ACR_ICRST;
|
||||
|
||||
do {
|
||||
uart_send_string("Erasing chip...");
|
||||
flash_writer_perform_mass_erase();
|
||||
uart_send_string(" done\r\n");
|
||||
|
||||
uart_send_string("Programming flash...\r\n");
|
||||
res = update_flash_from_file(filename);
|
||||
|
||||
if (res) {
|
||||
uart_send_string("Programming NOT successful.\r\n");
|
||||
if (retries > 0) {
|
||||
uart_send_string("Will retry...\r\n");
|
||||
}
|
||||
} else {
|
||||
uart_send_string("Programming completed successfully!\r\n");
|
||||
ram_code_exit(true);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (retries > 0);
|
||||
|
||||
while(1) {
|
||||
__WFI();
|
||||
}
|
||||
|
@@ -260,29 +260,26 @@ 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();
|
||||
|
||||
/* Catch return from main() */
|
||||
|
29
stm-firmware/updater/ram-code/uart.c
Normal file
29
stm-firmware/updater/ram-code/uart.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "uart.h"
|
||||
#include <reflow-controller/periph-config/shell-uart-config.h>
|
||||
#include <stm32/stm32f4xx.h>
|
||||
#include <string.h>
|
||||
|
||||
void uart_init(void)
|
||||
{
|
||||
SHELL_UART_RCC_REG |= SHELL_UART_RCC_MASK;
|
||||
SHELL_UART_PERIPH->BRR = SHELL_UART_BRR_REG_VALUE;
|
||||
SHELL_UART_PERIPH->CR2 = 0;
|
||||
SHELL_UART_PERIPH->CR3 = 0;
|
||||
SHELL_UART_PERIPH->CR1 = USART_CR1_TE | USART_CR1_UE;
|
||||
}
|
||||
|
||||
void uart_send_char(char c)
|
||||
{
|
||||
while (!(SHELL_UART_PERIPH->SR & USART_SR_TXE));
|
||||
SHELL_UART_PERIPH->DR = c;
|
||||
}
|
||||
|
||||
void uart_send_string(const char *str)
|
||||
{
|
||||
int len, i;
|
||||
|
||||
len = strlen(str);
|
||||
for (i = 0; i < len; i++) {
|
||||
uart_send_char(str[i]);
|
||||
}
|
||||
}
|
10
stm-firmware/updater/ram-code/uart.h
Normal file
10
stm-firmware/updater/ram-code/uart.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _UART_H_
|
||||
#define _UART_H_
|
||||
|
||||
void uart_init(void);
|
||||
|
||||
void uart_send_char(char c);
|
||||
|
||||
void uart_send_string(const char *str);
|
||||
|
||||
#endif /* _UART_H_ */
|
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include <reflow-controller/safety/watchdog.h>
|
||||
#include <generated/updater-ram-code.bin.h>
|
||||
#include <stm32/stm32f4xx.h>
|
||||
@@ -26,7 +27,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void __attribute__((noreturn)) start_updater(void)
|
||||
void __attribute__((noreturn)) start_updater_ram_code(void)
|
||||
{
|
||||
const char *updater_src = binary_blob;
|
||||
char *dest_ptr = (char *)UPDATER_RAM_CODE_BASE_ADDRESS;
|
||||
@@ -57,3 +58,17 @@ void __attribute__((noreturn)) start_updater(void)
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) updater_update_from_file(const char *filename)
|
||||
{
|
||||
struct safety_memory_boot_status status;
|
||||
|
||||
safety_memory_get_boot_status(&status);
|
||||
status.reboot_to_bootloader = 0xFFFFFFFFUL;
|
||||
safety_memory_set_boot_status(&status);
|
||||
|
||||
safety_memory_set_update_filename(filename);
|
||||
|
||||
NVIC_SystemReset();
|
||||
while (1);
|
||||
}
|
||||
|
Reference in New Issue
Block a user