Compare commits

..

93 Commits
jenkins ... dev

Author SHA1 Message Date
5c429ec894 Remove trailing whitespace 2024-03-24 14:13:56 +01:00
62ef147105 Incrment Cmake required version number 2024-03-24 14:11:33 +01:00
c6038969ca Fix style and code issues in bin2carray python script 2023-06-23 22:43:45 +02:00
0c8a0cd562 Fix watchdog tcode to write correct reload value 2023-06-09 00:12:41 +02:00
1300fe88a4 Shorten wtchdog trigger interval to 1.25 seconds nominal. This will ensure the internal watchdog triggers before the external one 2023-06-09 00:04:32 +02:00
df593e2ab2 Merge pull request 'misc-crc-monitor' (#55) from misc-crc-monitor into dev
Reviewed-on: #55
2023-05-23 19:07:21 +02:00
34ad930bd8 Fix mask for misc crc cfg monitor 2023-05-23 19:03:50 +02:00
1c1874abf1 Make safety flag ERR_FLAG_CFG_CRC_MISC trigger panic mode 2023-05-23 18:46:27 +02:00
fd2994f9b9 Add err flag cfg crc misc to flag list 2023-05-23 18:32:03 +02:00
b6befa70a2 Implement misc CRC monitor to supervise clocks and other system settings 2023-05-23 18:30:27 +02:00
dd6a7bef18 Merge pull request 'Pre-Release v0.5' (#54) from dev into master
Reviewed-on: #54
2023-01-03 16:37:53 +01:00
a005afaa13 Merge pull request 'Update FatFS in Firmware and Updater RAM Blob' (#53) from update-fatfs into dev
Reviewed-on: #53
2023-01-03 16:32:21 +01:00
cadd33d5a4 #48: Update FatFS in RAM code of updater 2023-01-03 15:08:57 +01:00
da132028b1 Issue #48: Update FatFS of main firmware to R0.15 2023-01-03 15:02:48 +01:00
2cd4847a57 Merge pull request 'version-rework' (#52) from version-rework into dev
Reviewed-on: #52
2023-01-01 21:23:32 +01:00
e335cb42ac Add comment to cmake build step 2023-01-01 21:16:36 +01:00
34c1c3db4e Rework UI to use newly created version handling 2023-01-01 21:12:19 +01:00
df70238b36 Remove git_ver define from updater 2023-01-01 21:12:00 +01:00
2c2e4c1484 Add compile date and time to version.c 2023-01-01 21:07:10 +01:00
767aa75c25 Make git a required build dependency 2023-01-01 21:04:03 +01:00
745e7db78f Add version c file that includes a generated file during build 2023-01-01 21:02:49 +01:00
9c0cbb107b Add script to generate header file containing version and commit hash 2023-01-01 20:45:57 +01:00
d48ccf1612 Fix #50: Enable core cycle counter 2023-01-01 20:18:39 +01:00
fd12faff75 Merge pull request 'Update develop. Ready for pre-release v0.4' (#49) from dev into master
Reviewed-on: #49
2022-12-31 20:39:51 +01:00
5ad1d574ff Fix #46: Fix update bug of GUI. Works now 2022-12-31 20:38:22 +01:00
f95ad1729e Fix buf on sd mounting handling 2022-12-31 20:29:22 +01:00
b18186423f Fix #46: Add SD status to GUI 2022-12-31 20:22:41 +01:00
ec2d3da4cb Add missing header file required for memset() 2022-12-31 20:22:08 +01:00
5e00441d99 Add separate source file handling the mounting of the SD card. This will give proper access to the GUI to check whether an SD is mounted 2022-12-31 20:18:34 +01:00
e91b33f379 Remove old CRC patcher 2022-12-31 19:56:33 +01:00
b621c66378 Update Flag Weights
* Fix #47: Update flash and data CRCs to trigger panic mode
* Update meas ADC CRC flag to stop PID
* Update safety ADC CRC flag to trigger panic mode
2022-12-31 19:53:45 +01:00
8ff402caaa Merge branch 'dev' of git.shimatta.de:mhu/reflow-oven-control-sw into dev 2022-12-31 19:43:41 +01:00
aade3288eb Update doxygen headers in config parser 2022-12-31 19:43:37 +01:00
049afbd66f Merge pull request 'Use patchelfcrc program for CRC patching.' (#45) from port-to-patchelfcrc into dev
Reviewed-on: #45
2022-12-31 19:42:54 +01:00
240b1ffc8f Update gitignore for use with vscode and clangd 2022-12-31 19:25:55 +01:00
f46044e5fc Remove mthumb-interwork from GCC command line. It is not neededfor CM4 cores 2022-12-31 19:24:27 +01:00
354c26ca67 Change Build step to use pathcelfcrc program instead of python script 2022-10-25 21:23:02 +02:00
6570d217c7 Separate flash CRC checker from safety controller and implement shell command to calculate CRCs 2022-09-22 21:16:41 +02:00
aeffb9df99 Update C file generator script 2022-09-22 21:16:07 +02:00
ad3de6e6b7 Improve HW version detect code. Funtionally equivalent 2022-08-09 00:23:55 +02:00
ab5fd6433e Rename python file to snake case 2022-08-09 00:23:23 +02:00
35542f56cb Add pylintrc and fix style problems in crc patcher 2022-08-09 00:21:57 +02:00
f9b9a3c685 Add compile error for configuration mistake 2022-07-30 16:45:26 +02:00
aaed95cc95 Restructure safety handle function. Now returns worst flag state set. Used to blink LED 2022-07-30 16:04:46 +02:00
6ac108e1b2 make LED blink if error that stop the PID are present 2022-07-30 15:53:04 +02:00
7a36b597be Add comment to function 2022-07-30 15:52:47 +02:00
00b8d1f48e Fix style issues in config-parser.c 2022-07-16 15:14:35 +02:00
8ac1d52240 Fix style issues in spi-eeprom.c/h 2022-07-16 15:09:41 +02:00
3e82cf69b4 Fix style in settings-sd-card.c 2022-07-16 15:07:33 +02:00
62b256fd18 Fix style in settings-sd-card.c 2022-07-16 15:07:01 +02:00
74111826a0 Fix style in settings-eeprom.c 2022-07-16 15:04:09 +02:00
69d7724c8f Fix style problems in settings.c 2022-07-16 14:46:44 +02:00
48f69d0fb5 Fix style issues in watchdog.c 2022-07-16 14:44:55 +02:00
8fd924829e Fix style problems in stack-check.c 2022-07-16 14:43:29 +02:00
ba8072c21d Fix style problems in safety-memory.c 2022-07-16 14:40:54 +02:00
5ddae7efee Fiy style issues 2022-07-16 13:01:38 +02:00
14ea4d22fe Fix style issues in STM peripheral drivers 2022-07-16 12:46:52 +02:00
d5780500f3 Update C-style checker 2022-07-16 12:38:37 +02:00
d4d654e8dd Add additional debug info to version command and add CPU cycle counter to cc instruction's output 2022-07-13 19:47:16 +02:00
8a62ed2ea7 Fix wrong short shell command for setting baudrate 2022-06-09 18:44:39 +02:00
7d43b4130c Add connector info to LCD GUI. So TX and RX of UART can be found without looking at the schematic. 2022-06-09 18:18:30 +02:00
835faf7e4e Merge pull request 'enhancement/39-digio-cmd-profile-language' (#44) from enhancement/39-digio-cmd-profile-language into dev
Reviewed-on: #44
2021-12-26 21:20:15 +01:00
2957a42cef Fix indetation in header file 2021-12-26 20:59:08 +01:00
1e45c8b5d7 Issue #39: Implement DIGIO profile commands 2021-12-26 20:54:04 +01:00
3bb4607798 digio: Make digio check if an port to set is an output port. 2021-12-26 20:40:28 +01:00
a8a622df25 Make temperture profile executer reset the DIGIO settings after profile execution/abort 2021-12-26 20:36:49 +01:00
45f91a7c5a Add commands for DIGIO channels to profile parser and add base construct to the profile interpreter 2021-12-26 20:27:20 +01:00
bf6a222c9d Merge branch 'feature/42-update-fatfs' of mhu/reflow-oven-control-sw into dev 2021-12-16 20:55:34 +01:00
ef534746f8 Update updater to newest fatfs and enable exfat support 2021-12-16 20:47:08 +01:00
8cd47aeed5 Add new FatFS implementation 2021-12-16 20:41:10 +01:00
1291b0fb6a Add FatFS license 2021-12-16 20:28:39 +01:00
b6a2790a59 cleanup code and write a little documentation 2021-11-28 23:40:43 +01:00
7bfa0732db Fix wrong register bit in flash writer. This actually didn't result in an error but it's better to fix it either way. 2021-11-28 23:11:40 +01:00
0dace23021 Bugfix: Fix null pointer access in temp profile parser 2021-11-27 19:24:51 +01:00
c78477296a Bugfix: Fix hardfault when update menu is opened and no hex file is located on disk 2021-11-27 18:47:17 +01:00
96e0931c9f Add a bunch of stuff: Add baudrate reconfig command to shell and move the uart to separate C file 2021-11-27 17:41:11 +01:00
6b9b7d78a0 Add v1.3.1 to supported hardware revisions 2021-10-24 21:59:44 +02:00
22bb227bc7 Add a few comments 2021-10-24 14:50:07 +02:00
78f24f7338 Add LCD output to shell function 'Emulate UI' 2021-10-24 14:47:52 +02:00
e3a552248d Make a wrong size of the flashcrc struct an error becuase it might lead to misaligned CRCs in the structure 2021-10-23 21:26:01 +02:00
288b19c4fc Comment functions 2021-10-23 21:18:32 +02:00
56872086fa Comment code 2021-10-23 21:14:24 +02:00
fe0e8136d4 Refactor temperature profile executer 2021-10-23 21:00:21 +02:00
73606bf7a0 Write documentation for the config parser 2021-10-23 20:54:37 +02:00
78501143cc Fix wrongly typed float literal 2021-10-23 20:54:20 +02:00
bb0ea908cb Merge branch 'brown-out-detection' into dev 2021-10-17 12:52:59 +02:00
61cb92fbe8 Add command for printing option bytes 2021-10-17 12:30:58 +02:00
eb41e5e210 Implement option byte writing and set brown out level 2021-10-16 23:39:23 +02:00
decb484d06 Start implementation of option byte manipulation 2021-10-16 00:44:39 +02:00
4009a2794d Add STM device resvision to MOTD. 2021-10-15 22:06:30 +02:00
d9c145ec81 Fix some coding issues and a possible race condition 2021-10-15 21:16:26 +02:00
6fde4cfd66 Improve doxygen comments in code. 2021-10-11 19:45:13 +02:00
c82ca7d8f0 Merge branch 'jenkins' into dev 2021-10-10 22:41:00 +02:00
86 changed files with 5202 additions and 3146 deletions

18
Jenkinsfile vendored
View File

@ -2,22 +2,9 @@ pipeline {
agent any
stages {
stage('Configure') {
steps {
echo 'Configuring Cmake...'
sh '''
mkdir "build" &&
cd "build" &&
cmake "../stm-firmware"
'''
}
}
stage('Build') {
steps {
sh '''
cd "build" &&
make
'''
echo 'Building..'
}
}
stage('Test') {
@ -26,9 +13,8 @@ pipeline {
}
}
stage('Deploy') {
when { tag "*" }
steps {
echo 'Deploying tag...'
echo 'Deploying....'
}
}
}

@ -1 +1 @@
Subproject commit cb937262aa71ee3d610900d7f14eef28e9f11dd0
Subproject commit aeb2707d80049094337742d0f62cd24dbef1c01d

View File

@ -15,4 +15,4 @@ mechanisms and the behavior. For a detailed code documentation see the doxygen o
safety/index
code/index
hw-version-detect
peripherals

View File

@ -0,0 +1,44 @@
.. _peripherals:
Used Peripheral Modules
=======================
This section lists all the used peripheral modules of the ``STM32F407VxT6``.
Core Peripherals
----------------
- ``SysTick``: Generating a 100us tick for the LCD routine and 1ms base system tick.
- ``NVIC``: Interrupt controller
- ``FPU``: The Flaoting Point Unit is activated and used in this formware.
AHB Peripherals
---------------
- ``DMA2``
- ``Stream0``: DMA transfer of PT1000 measurement ADC to memory
- ``Stream4``: DMA transfer of Safety ADC measurement values
- ``Stream5``: Shell UART RX
- ``Stream7``: Shell UART TX
- ``RNG``: Random number generation for stack corruption / overflow checker.
- ``CRC``: CRC verfication of various data (Safety structures, EEPROM data, Safety RAM)
- ``Backup RAM``: Backup RAM storing errors and bootloader information beyond system resets. The memory is cleared by a power cycle!
ABP1 Peripherals
----------------
- ``IWDG``: Independent Watchdog
- ``TIM2``: PT1000 measurement ADC sample time generation timer. Genewrates the 1 KHz sample trigger to the ADC peripheral via the internal event routing system.
- ``TIM3``: PWM timer for oven relais output.
- ``TIM5``: Input capture for rotary encoder.
- ``TIM7``: Timer for loudspeaker tone generation.
APB2 Peripherals
----------------
- ``SPI1``: SPI for external SPI-EEPROM
- ``SDIO``: SD card interface
- ``USART1``: Shell UART
- ``ADC1``: Safety ADC for monitoring voltages
- ``ADC3``: PT1000 measurement ADC

View File

@ -16,3 +16,10 @@ reflow-controller.includes
*.files
*.user.*
*.user
# VSCODE and CLANGD sepcific excludes
.vscode/
build/
.cache/
compile_commands.json

View File

@ -1,7 +1,7 @@
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_CROSSCOMPILING 1)
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.18)
set(CMAKE_TOOLCHAIN_FILE "arm-none-eabi-gcc.cmake")
@ -38,31 +38,16 @@ if (GIT_FOUND)
)
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}")
message(FATAL_ERROR "Git is required")
endif (GIT_FOUND)
find_program(VIRTUALENV virtualenv)
if (VIRTUALENV)
message("Python virtual environment found")
execute_process(
COMMAND ${VIRTUALENV} venv
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
OUTPUT_QUIET
COMMAND_ERROR_IS_FATAL ANY
)
set(VENV_BIN "${CMAKE_CURRENT_BINARY_DIR}/venv/bin")
execute_process(
COMMAND ./pip install -r "${CMAKE_CURRENT_SOURCE_DIR}/crc-patcher/requirements.txt"
WORKING_DIRECTORY ${VENV_BIN}
OUTPUT_QUIET
COMMAND_ERROR_IS_FATAL ANY
)
message("${BoldGreen}python virtual environment set up!${ColorReset}")
else(VIRTUALENV)
message(FATAL_ERROR "${BoldRed}Python virtual environment not set up: virtualenv: command not found!${ColorReset}")
endif (VIRTUALENV)
find_program(PATCHELFCRC patchelfcrc)
if (PATCHELFCRC)
message("patchelfcrc found: ${PATCHELFCRC}")
else(PATCHELFCRC)
message(FATAL_ERROR "${BoldRed}Patchelfcrc not found. Cannot patch CRC checksum into ELF file: patchelfcrc: command not found! See: https://git.shimatta.de/mhu/patchelfcrc${ColorReset}")
endif (PATCHELFCRC)
set(ELFFILE ${PROJECT_NAME}.elf)
@ -71,11 +56,10 @@ set(MAPFILE ${PROJECT_NAME}.map)
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/stm32f407vet6_flash.ld)
add_compile_options(-Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter)
add_compile_options(-mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles -Wimplicit-fallthrough=3 -Wsign-compare)
add_compile_options(-mlittle-endian -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles -Wimplicit-fallthrough=3 -Wsign-compare)
set(GIT_DESCRIBE "${GIT_DESCRIBE}")
add_definitions(-DBASE64_LOOKUP_TABLE_SECTION=\".ccm.bss\" -DSHELLMATTA_HELP_ALIAS=\"?\" -DGIT_VER=${GIT_DESCRIBE} -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4)
add_definitions(-DBASE64_LOOKUP_TABLE_SECTION=\".ccm.bss\" -DSHELLMATTA_HELP_ALIAS=\"?\" -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4)
add_subdirectory(doxygen)
add_subdirectory(updater/ram-code)
@ -115,24 +99,35 @@ aux_source_directory("shellmatta/src" SHELLMATTA_SRCS)
aux_source_directory("updater" UPDATER_SRCS)
aux_source_directory("temp-profile" PROFILE_SRCS)
set(GEN_VERSION_HEADER_PATH "${CMAKE_CURRENT_BINARY_DIR}/include/generated-version")
add_custom_target(
generate-version-header
COMMAND mkdir -p ${GEN_VERSION_HEADER_PATH} && bash "${CMAKE_CURRENT_SOURCE_DIR}/create_version_header.sh" "${GEN_VERSION_HEADER_PATH}/version.h"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating version number using git"
)
add_executable(${ELFFILE} ${MAIN_SOURCES} ${CFG_PARSER_SRCS} ${UI_SRCS}
${FAT_SRCS} ${SDIO_SRCS} ${BOOT_SRCS} ${SETUP_SRCS}
${STM_PERIPH_SRCS} ${SETTINGS_SRCS} ${SAFETY_SRCS}
${SHELLMATTA_SRCS} ${UPDATER_SRCS} ${PROFILE_SRCS}
)
add_dependencies(${ELFFILE} updater-ram-code-header-blob)
add_dependencies(${ELFFILE} updater-ram-code-header-blob generate-version-header)
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_options(${ELFFILE} PRIVATE -mlittle-endian -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles -T${LINKER_SCRIPT} -Wl,--print-memory-usage)
target_link_libraries(${ELFFILE} base64-lib linklist-lib)
target_include_directories(${ELFFILE} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/updater/ram-code/include/")
target_include_directories(${ELFFILE} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/updater/ram-code/include/" "${CMAKE_CURRENT_BINARY_DIR}/include/")
add_custom_command(
TARGET ${ELFFILE}
POST_BUILD
COMMAND ./python "${CMAKE_CURRENT_SOURCE_DIR}/crc-patcher/crc-patch-elf.py" "${CMAKE_CURRENT_BINARY_DIR}/${ELFFILE}"
WORKING_DIRECTORY ${VENV_BIN}
COMMAND ${PATCHELFCRC} --little-endian --verbose --granularity word --start-magic 0xa8be53f9 --end-magic 0xffa582ff -O .flashcrc -p crc-32-mpeg -S .text -S .data -S .ccmdata -S .vectors "${CMAKE_CURRENT_BINARY_DIR}/${ELFFILE}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running Flash CRC Patcher"
)

View File

@ -324,7 +324,6 @@ void __attribute__((noreturn)) Reset_Handler(void) {
/* Move the stack and the stack pointer to CCMRAM
* This allows us to perform a RAM test on the main RAM.
*/
/* R2 holds the amount of bytes / words on the stack. */
__asm__ __volatile__ (
"mov r2, sp\n" /* Move stack pointer to register 2 */
"sub r2, %[stacktop], r2\n" /* Subtract stackpointer from top of ram => byte usage */

View File

@ -28,10 +28,24 @@
#include <stdio.h>
#include <stdlib.h>
/**
* @brief Config parser magic value used to check sanity of passed structs
*/
#define CONFIG_PARSER_MAGIC 0x464a6e2bUL
/**
* @brief Config parser type casting macro
*/
#define CONFIG_PARSER(p) ((struct config_parser *)(p))
#define config_parser_check_handle(handle) do { if (!(handle) || \
/**
* @brief Check if the supplied pointer is a valid @ref config_parser_handle_t
*
* If the pointer is invalid, the function using this macro will return with
* CONFIG_PARSER_PARAM_ERR
*/
#define config_parser_check_handle(handle) do { \
if (!(handle) || \
((struct config_parser *)(handle))->magic != CONFIG_PARSER_MAGIC) \
return CONFIG_PARSER_PARAM_ERR; \
} while (0)
@ -56,8 +70,17 @@ config_parser_handle_t config_parser_open_file(struct config_parser *config_pars
return (config_parser_handle_t)config_parser;
}
/**
* @brief Token delimiters for the config parser.
*/
static const char * const token_delim = " \t";
/**
* @brief Parse a value in the configuration
* @param entry Entry to parse the value in to
* @param value_start_token char pointer holding the value. Must be null-terminated
* @return 0 if successful
*/
static int parse_value(struct config_parser_entry *entry, char *value_start_token)
{
char *dot;
@ -76,19 +99,18 @@ static int parse_value(struct config_parser_entry *entry, char *value_start_tok
if (value_start_token[0] != '-') {
/* Try parsing as ul */
/* Try parsing as int */
entry->value.uint_val = strtoul(value_start_token, &endptr, 0);
if (endptr == value_start_token) {
if (endptr == value_start_token)
return -1;
}
entry->type = CONFIG_PARSER_TYPE_UINT;
goto exit;
} else {
/* Try parsing as int */
entry->value.int_val = strtod(value_start_token, &endptr);
if (endptr == value_start_token) {
if (endptr == value_start_token)
return -1;
}
entry->type = CONFIG_PARSER_TYPE_INT;
}
@ -96,14 +118,16 @@ exit:
return 0;
}
enum config_parser_ret config_parser_get_line(config_parser_handle_t handle, struct config_parser_entry *entry, bool force_float)
enum config_parser_ret config_parser_get_line(config_parser_handle_t handle, struct config_parser_entry *entry,
bool force_float)
{
struct config_parser *p;
config_parser_check_handle(handle);
p = CONFIG_PARSER(handle);
char *token;
int token_round = 0;
config_parser_check_handle(handle);
p = CONFIG_PARSER(handle);
if (!entry)
return CONFIG_PARSER_PARAM_ERR;
@ -117,7 +141,6 @@ enum config_parser_ret config_parser_get_line(config_parser_handle_t handle, str
if (token[0] == '#') {
if (token_round == 0)
return CONFIG_PARSER_LINE_COMMENT;
else
break;
}
@ -126,9 +149,8 @@ enum config_parser_ret config_parser_get_line(config_parser_handle_t handle, str
entry->name = token;
break;
case 1: /* = Symbol */
if (strcmp(token, "=")) {
if (strcmp(token, "="))
return CONFIG_PARSER_LINE_MALFORM;
}
break;
case 2: /* VALUE */
if (parse_value(entry, token))
@ -158,6 +180,7 @@ enum config_parser_ret config_parser_reset_to_start(config_parser_handle_t handl
{
FRESULT res;
struct config_parser *p;
config_parser_check_handle(handle);
p = CONFIG_PARSER(handle);
@ -180,6 +203,7 @@ enum config_parser_ret config_parser_close_file(config_parser_handle_t handle)
{
struct config_parser *p;
FRESULT res;
config_parser_check_handle(handle);
p = CONFIG_PARSER(handle);

View File

@ -33,44 +33,68 @@
#include <stdbool.h>
#include <fatfs/ff.h>
/**
* @brief Confi parser instance struct
*/
struct config_parser {
uint32_t magic;
bool write;
FIL file;
char *buffer;
size_t buff_size;
uint32_t magic; /**< @brief Magic value. Checked by each function to verify if the passed pointer is valid */
bool write; /**< @brief File opened in write / read mode */
FIL file; /**< @brief FatFS file */
char *buffer; /**< @brief Working buffer */
size_t buff_size; /** @brief Size of the working buffer config_parser::buffer */
};
/**
* @brief handle type for the config parser. Never dereference this pointer directly!
*/
typedef void * config_parser_handle_t;
/**
* @brief Differnet value types a config entry can hold
*/
enum config_parser_value_type {
CONFIG_PARSER_TYPE_UINT = 0,
CONFIG_PARSER_TYPE_INT,
CONFIG_PARSER_TYPE_FLOAT,
};
/**
* @brief Error return code of config parser functions
*/
enum config_parser_ret {
CONFIG_PARSER_OK = 0,
CONFIG_PARSER_PARAM_ERR,
CONFIG_PARSER_GENERIC_ERR,
CONFIG_PARSER_IOERR,
CONFIG_PARSER_LINE_COMMENT,
CONFIG_PARSER_LINE_TOO_LONG,
CONFIG_PARSER_LINE_MALFORM,
CONFIG_PARSER_END_REACHED,
CONFIG_PARSER_WRONG_MODE,
CONFIG_PARSER_OK = 0, /**< @brief Operation succeeded */
CONFIG_PARSER_PARAM_ERR, /**< @brief Function parameter error */
CONFIG_PARSER_GENERIC_ERR, /**< @brief Generic unspecified error */
CONFIG_PARSER_IOERR, /**< @brief I/O Error while file handling */
CONFIG_PARSER_LINE_COMMENT, /**< @brief The parser encountered a line starting with a comment. */
CONFIG_PARSER_LINE_TOO_LONG, /**< @brief The read line is too long for the parser to process. */
CONFIG_PARSER_LINE_MALFORM, /**< @brief Malfoirmed line. Line is neither a commenbt nor a key=value string */
CONFIG_PARSER_END_REACHED, /**< @brief The config parser has reached the end of the file */
CONFIG_PARSER_WRONG_MODE, /**< @brief Read or write requested on config parser instance that is opened in a different mode */
};
/**
* @brief Represents a configuration key-value-pair used by the config parser
*/
struct config_parser_entry {
const char *name;
enum config_parser_value_type type;
const char *name; /**< @brief Pointer to the name of the config entry (key) */
enum config_parser_value_type type; /**< @brief Type of the value held by this struct */
union {
uint32_t uint_val;
int32_t int_val;
float float_val;
} value;
} value; /**< @brief Value of the config entry. For correct processing, config_parser_entry::type has to be taken into account */
};
/**
* @brief Open a config file
* @param config_parser Struict holding the config parser isntance. Will be filled by this function
* @param write Open the config file for writing (true) or reading (false)
* @param file_name File name to open
* @param working_buffer A working buffer used by the config parser to process the file.
* @param buff_size Size of \p working_buffer. Must be large enough to hold a full line of the config file
* @return Config parser error
*/
config_parser_handle_t config_parser_open_file(struct config_parser *config_parser, bool write, const char *file_name,
char *working_buffer, size_t buff_size);
@ -83,13 +107,37 @@ config_parser_handle_t config_parser_open_file(struct config_parser *config_pars
* @return Config parser error
*/
enum config_parser_ret config_parser_get_line(config_parser_handle_t handle, struct config_parser_entry *entry, bool force_float);
/**
* @brief Reset the config parser instance to the beginning of the config file
* @param handle Config parser
* @return Config parser error
*/
enum config_parser_ret config_parser_reset_to_start(config_parser_handle_t handle);
/**
* @brief Write a config entry to a config file
* @param handle Handle to the config parser. This must be opened in write mode.
* @param entry
* @return Config parser error
* @warning This function is currently not implemented and will return with a success!
*/
enum config_parser_ret config_parser_write_entry(config_parser_handle_t handle, const struct config_parser_entry *entry);
/**
* @brief Close a config parser handle
* @param handle Config parser
* @return Config parser error
*/
enum config_parser_ret config_parser_close_file(config_parser_handle_t handle);
/**
* @brief Check if the \p return_val is a abort condition to stop parsing a file.
*
* This function will return true if a disk error occured or the end of file is reached.
*
* @param return_val
* @return
*/
bool config_parser_ret_is_abort_condition(enum config_parser_ret return_val);
#endif /* _CONFIG_PARSER_H_ */

View File

@ -1,113 +0,0 @@
#!/bin/python
"""
This script patches the CRC checksums into an existing ELF file.
For this, it searches the follwoing sections:
1) .text
2) .data
3) .ccmdata
4) .vectors
All sections MUST be a multiple of 4 bytes long because the CRC calculation relies on whole 32 bit words.
The sections are excrated and the CRC is calculated for each section.
In the section .flashcrc, the script expects a single struct with the prototype:
struct flash_crcs {
uint32_t start_magic;
uint32_t crc_section_text;
uint32_t crc_section_data;
uint32_t crc_section_ccm_data;
uint32_t crc_section_vectors;
uint32_t end_magic;
};
It checks, if the start magic and end magic are set to the appropriate values and then patches in the CRC values of the sections.
The magic values checked for are: 0xA8BE53F9 and 0xFFA582FF
"""
from elftools.elf.elffile import ELFFile
from elftools.elf.segments import Segment
import elftools.elf.constants as elf_const
import sys
import crcmod
import crcmod.predefined
import struct
crc_calc = crcmod.predefined.mkCrcFun('crc-32-mpeg')
if len(sys.argv) < 2:
print("Usage:", sys.argv[0], ' <elf file>')
sys.exit(-1)
filename=sys.argv[1]
def section_calculate_crc(section):
data = bytearray(section.data())
be_data = bytearray([0 for k in range(0, len(data))])
# Rearrange data, because the STM controller sees it as 32 bit little endian words
for i in range(0, int(len(data)/4)):
be_data[i*4+0] = data[i*4+3]
be_data[i*4+1] = data[i*4+2]
be_data[i*4+2] = data[i*4+1]
be_data[i*4+3] = data[i*4+0]
return crc_calc(be_data)
with open(filename, 'r+b') as f:
elf = ELFFile(f)
sections = {}
sections['.text'] = elf.get_section_by_name('.text')
sections['.data'] = elf.get_section_by_name('.data')
sections['.ccmdata'] = elf.get_section_by_name('.ccmdata')
sections['.vectors'] = elf.get_section_by_name('.vectors')
for key, sec in sections.items():
if sec is None:
print("Error! Section", key, "not found in ELF file!")
sys.exit(-1)
print('Found section', key, 'Size:',
sec.data_size, 'Type:', sec['sh_type'])
if sec['sh_type'] != 'SHT_PROGBITS':
print('Error! Section must be of type SHT_PROGBITS')
sys.exit(-1)
if (sec.data_size % 4 != 0):
print("Section", key, "has wrong size. Must be a multiple of 4 bytes!")
sys.exit(-1)
text_crc = section_calculate_crc(sections['.text'])
print('CRC of .text section:', hex(text_crc))
data_crc = section_calculate_crc(sections['.data'])
print('CRC of .data section:', hex(data_crc))
ccmdata_crc = section_calculate_crc(sections['.ccmdata'])
print('CRC of .ccmdata section:', hex(ccmdata_crc))
vextors_crc = section_calculate_crc(sections['.vectors'])
print('CRC of .vectors section:', hex(vextors_crc))
# Check the flashcrc section
flashcrc_sec = elf.get_section_by_name('.flashcrc')
if flashcrc_sec is None:
print('Section for flash CRC missing!')
sys.exit(-1)
if flashcrc_sec.data_size != 6*4:
print("Warning!!! .flashcrc section has wrong size:",flashcrc_sec.data_size)
crc_sec_data = bytearray(flashcrc_sec.data())
magic1 = struct.unpack('<I'*1, bytes(crc_sec_data[0:4]))[0]
magic2 = struct.unpack('<I'*1, bytes(crc_sec_data[-4:]))[0]
print("CRC section magic values:", hex(magic1), hex(magic2))
if magic1 != 0xA8BE53F9 or magic2 != 0xFFA582FF:
print("Wrong magics in CRC section. Data misalignment?")
sys.exit(-2)
crc_sec_offset = flashcrc_sec['sh_offset']
print('CRC section ELF file offset:', hex(crc_sec_offset))
crc_sec_data[4:8] = struct.pack('<I',text_crc)
crc_sec_data[8:12] = struct.pack('<I',data_crc)
crc_sec_data[12:16] = struct.pack('<I',ccmdata_crc)
crc_sec_data[16:20] = struct.pack('<I',vextors_crc)
f.seek(crc_sec_offset)
f.write(crc_sec_data)
print('CRCs patched successfully')

View File

@ -1,2 +0,0 @@
crcmod==1.7
pyelftools==0.27

View File

@ -6,7 +6,7 @@ import pathlib
license_header = """/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
@ -37,7 +37,7 @@ cpath = os.path.join(project_dir, sys.argv[1]+'.c')
hfile = sys.argv[1]+'.h'
hpath = os.path.join(module_include_dir, hfile)
h_define = '__'+hfile.replace('.', '_').replace('-', '_').replace('/', '_').upper()+'__'
h_define = '_'+hfile.replace('.', '_').replace('-', '_').replace('/', '_').upper()+'_'
if os.path.exists(cpath) or os.path.exists(hpath):
print("File already exists! Abort!")

View File

@ -0,0 +1,15 @@
#!/bin/bash
if [[ -z $1 ]]; then
# Exit with error in case no output file is specified
exit -1
fi
firmware_version=`git describe --tags --always --dirty`
commit=`git rev-parse HEAD`
echo "#ifndef _VERSION_GENERATED_H_" > $1
echo "#define _VERSION_GENERATED_H_" >> $1
echo "#define GIT_VERSION_STRING \"$firmware_version\"" >> $1
echo "#define GIT_FULL_COMMIT \"$commit\"" >> $1
echo "#endif /* _VERSION_GENERATED_H_ */" >> $1

View File

@ -18,6 +18,11 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup digio
* @{
*/
#include <reflow-controller/digio.h>
#include <stm32/stm32f4xx.h>
#include <stm-periph/rcc-manager.h>
@ -47,17 +52,10 @@ static void digio_setup_pin_int(uint8_t bit_no, uint8_t in_out, uint8_t alt_func
}
void digio_setup_default_all(void)
void digio_init(void)
{
unsigned int i;
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(DIGIO_RCC_MASK));
for (i = 0; i < COUNT_OF(digio_pins); i++) {
digio_setup_pin_int(digio_pins[i], digio_default_io[i], digio_default_altfunc[i]);
if (digio_default_io[i] == 1)
digio_set(i, 0);
}
digio_set_default_values();
}
void digio_setup_pin(uint8_t num, uint8_t in_out, uint8_t alt_func)
@ -69,12 +67,22 @@ void digio_setup_pin(uint8_t num, uint8_t in_out, uint8_t alt_func)
void digio_set(uint8_t num, int val)
{
uint8_t pin;
if (num >= COUNT_OF(digio_pins))
return;
pin = digio_pins[num];
/* Check if port is an output. If not, do noting. */
if ((DIGIO_PORT->MODER & (0x3<<pin)) != OUTPUT(pin)) {
return;
}
if (val)
DIGIO_PORT->ODR |= (1<<digio_pins[num]);
DIGIO_PORT->ODR |= (1<<pin);
else
DIGIO_PORT->ODR &= ~(1<<digio_pins[num]);
DIGIO_PORT->ODR &= ~(1<<pin);
}
int digio_get(uint8_t num)
@ -119,7 +127,12 @@ int led_get(uint8_t num)
return ((LED_PORT->ODR & (1<<led_pins[num])) ? 1 : 0);
}
/**
* @brief Initialize the timer for the beeper to generate the output frequency
*
* TIM7 is used as the frequency generating timer. If @ref LOUDSPEAKER_MULTIFREQ
* is 0, the timer is unused and this function does nothing.
*/
static void loudspeaker_freq_timer_init(void)
{
#if LOUDSPEAKER_MULTIFREQ
@ -143,6 +156,13 @@ void loudspeaker_setup(void)
loudspeaker_set(0U);
}
/**
* @brief Start the beeper
* @param val frequency value of the speaker in 'Timer relaod values'
* @note If @ref LOUDSPEAKER_MULTIFREQ isn't set,
* the speaker output will be set to high and no frequency is generated.
* The value of @p val is ignored in this case
*/
static void loudspeaker_start_beep(uint16_t val)
{
#if LOUDSPEAKER_MULTIFREQ
@ -155,6 +175,9 @@ static void loudspeaker_start_beep(uint16_t val)
#endif
}
/**
* @brief Stop the beeping of the loudspeaker
*/
static void loudspeaker_stop_beep(void)
{
#if LOUDSPEAKER_MULTIFREQ
@ -184,6 +207,12 @@ uint16_t loudspeaker_get(void)
}
#if LOUDSPEAKER_MULTIFREQ
/**
* @brief Timer7 IRQ Handler
*
* This IRQ Handler is used by the loudspeaker to synthesize the output frequency.
* If @ref LOUDSPEAKER_MULTIFREQ is 0, TIM7 and this interrupt will not be used.
*/
void TIM7_IRQHandler(void)
{
TIM7->SR = 0UL;
@ -191,3 +220,16 @@ void TIM7_IRQHandler(void)
LOUDSPEAKER_PORT->ODR ^= (1<<LOUDSPEAKER_PIN);
}
#endif
/** @} */
void digio_set_default_values()
{
unsigned int i;
for (i = 0; i < COUNT_OF(digio_pins); i++) {
digio_setup_pin_int(digio_pins[i], digio_default_io[i], digio_default_altfunc[i]);
if (digio_default_io[i] == 1)
digio_set(i, 0);
}
}

View File

@ -463,7 +463,7 @@ LOOKUP_CACHE_SIZE = 0
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
NUM_PROC_THREADS = 0
#---------------------------------------------------------------------------
# Build related configuration options
@ -968,7 +968,8 @@ EXCLUDE = ../include/stm32 \
../linklist-lib/test \
../base64-lib/test \
../shellmatta/doc/main.dox \
../updater/ram-code
../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

View File

@ -0,0 +1,24 @@
FatFs License
FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files.
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem Module Rx.xx /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 20xx, 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
/ that the following condition is met:
/
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.
/----------------------------------------------------------------------------*/
Therefore FatFs license is one of the BSD-style licenses, but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, do not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses include GNU GPL. When you redistribute the FatFs source code with changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license.

File diff suppressed because it is too large Load Diff

View File

@ -1,170 +1,208 @@
/*------------------------------------------------------------------------*/
/* Sample Code of OS Dependent Functions for FatFs */
/* (C)ChaN, 2018 */
/* A Sample Code of User Provided OS Dependent Functions for FatFs */
/*------------------------------------------------------------------------*/
#include <fatfs/ff.h>
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
#if FF_USE_LFN == 3 /* Use dynamic memory allocation */
/*------------------------------------------------------------------------*/
/* Allocate a memory block */
/* Allocate/Free a Memory Block */
/*------------------------------------------------------------------------*/
#include <stdlib.h> /* with POSIX API */
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
UINT msize /* Number of bytes to allocate */
)
{
return malloc(msize); /* Allocate a new memory block with POSIX API */
return malloc((size_t)msize); /* Allocate a new memory block */
}
/*------------------------------------------------------------------------*/
/* Free a memory block */
/*------------------------------------------------------------------------*/
void ff_memfree (
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
void* mblock /* Pointer to the memory block to free (no effect if null) */
)
{
free(mblock); /* Free the memory block with POSIX API */
free(mblock); /* Free the memory block */
}
#endif
#if FF_FS_REENTRANT /* Mutal exclusion */
/*------------------------------------------------------------------------*/
/* Create a Synchronization Object */
/* Definitions of Mutex */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to create a new
/ synchronization object for the volume, such as semaphore and mutex.
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
*/
//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */
#define OS_TYPE 0 /* 0:Win32, 1:uITRON4.0, 2:uC/OS-II, 3:FreeRTOS, 4:CMSIS-RTOS */
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
BYTE vol, /* Corresponding volume (logical drive number) */
FF_SYNC_t* sobj /* Pointer to return the created sync object */
)
{
/* Win32 */
*sobj = CreateMutex(NULL, FALSE, NULL);
return (int)(*sobj != INVALID_HANDLE_VALUE);
#if OS_TYPE == 0 /* Win32 */
#include <windows.h>
static HANDLE Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
/* uITRON */
// T_CSEM csem = {TA_TPRI,1,1};
// *sobj = acre_sem(&csem);
// return (int)(*sobj > 0);
#elif OS_TYPE == 1 /* uITRON */
#include "itron.h"
#include "kernel.h"
static mtxid Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
/* uC/OS-II */
// OS_ERR err;
// *sobj = OSMutexCreate(0, &err);
// return (int)(err == OS_NO_ERR);
#elif OS_TYPE == 2 /* uc/OS-II */
#include "includes.h"
static OS_EVENT *Mutex[FF_VOLUMES + 1]; /* Table of mutex pinter */
/* FreeRTOS */
// *sobj = xSemaphoreCreateMutex();
// return (int)(*sobj != NULL);
#elif OS_TYPE == 3 /* FreeRTOS */
#include "FreeRTOS.h"
#include "semphr.h"
static SemaphoreHandle_t Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
/* CMSIS-RTOS */
// *sobj = osMutexCreate(&Mutex[vol]);
// return (int)(*sobj != NULL);
}
/*------------------------------------------------------------------------*/
/* Delete a Synchronization Object */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to delete a synchronization
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
/ the f_mount() function fails with FR_INT_ERR.
*/
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
)
{
/* Win32 */
return (int)CloseHandle(sobj);
/* uITRON */
// return (int)(del_sem(sobj) == E_OK);
/* uC/OS-II */
// OS_ERR err;
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
// return (int)(err == OS_NO_ERR);
/* FreeRTOS */
// vSemaphoreDelete(sobj);
// return 1;
/* CMSIS-RTOS */
// return (int)(osMutexDelete(sobj) == osOK);
}
/*------------------------------------------------------------------------*/
/* Request Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on entering file functions to lock the volume.
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
*/
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
FF_SYNC_t sobj /* Sync object to wait */
)
{
/* Win32 */
return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
/* uITRON */
// return (int)(wai_sem(sobj) == E_OK);
/* uC/OS-II */
// OS_ERR err;
// OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
// return (int)(err == OS_NO_ERR);
/* FreeRTOS */
// return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
/* CMSIS-RTOS */
// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
}
/*------------------------------------------------------------------------*/
/* Release Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on leaving file functions to unlock the volume.
*/
void ff_rel_grant (
FF_SYNC_t sobj /* Sync object to be signaled */
)
{
/* Win32 */
ReleaseMutex(sobj);
/* uITRON */
// sig_sem(sobj);
/* uC/OS-II */
// OSMutexPost(sobj);
/* FreeRTOS */
// xSemaphoreGive(sobj);
/* CMSIS-RTOS */
// osMutexRelease(sobj);
}
#elif OS_TYPE == 4 /* CMSIS-RTOS */
#include "cmsis_os.h"
static osMutexId Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
#endif
/*------------------------------------------------------------------------*/
/* Create a Mutex */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount function to create a new mutex
/ or semaphore for the volume. When a 0 is returned, the f_mount function
/ fails with FR_INT_ERR.
*/
int ff_mutex_create ( /* Returns 1:Function succeeded or 0:Could not create the mutex */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
Mutex[vol] = CreateMutex(NULL, FALSE, NULL);
return (int)(Mutex[vol] != INVALID_HANDLE_VALUE);
#elif OS_TYPE == 1 /* uITRON */
T_CMTX cmtx = {TA_TPRI,1};
Mutex[vol] = acre_mtx(&cmtx);
return (int)(Mutex[vol] > 0);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
Mutex[vol] = OSMutexCreate(0, &err);
return (int)(err == OS_NO_ERR);
#elif OS_TYPE == 3 /* FreeRTOS */
Mutex[vol] = xSemaphoreCreateMutex();
return (int)(Mutex[vol] != NULL);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexDef(cmsis_os_mutex);
Mutex[vol] = osMutexCreate(osMutex(cmsis_os_mutex));
return (int)(Mutex[vol] != NULL);
#endif
}
/*------------------------------------------------------------------------*/
/* Delete a Mutex */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount function to delete a mutex or
/ semaphore of the volume created with ff_mutex_create function.
*/
void ff_mutex_delete ( /* Returns 1:Function succeeded or 0:Could not delete due to an error */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
CloseHandle(Mutex[vol]);
#elif OS_TYPE == 1 /* uITRON */
del_mtx(Mutex[vol]);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
OSMutexDel(Mutex[vol], OS_DEL_ALWAYS, &err);
#elif OS_TYPE == 3 /* FreeRTOS */
vSemaphoreDelete(Mutex[vol]);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexDelete(Mutex[vol]);
#endif
}
/*------------------------------------------------------------------------*/
/* Request a Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on enter file functions to lock the volume.
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
*/
int ff_mutex_take ( /* Returns 1:Succeeded or 0:Timeout */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
return (int)(WaitForSingleObject(Mutex[vol], FF_FS_TIMEOUT) == WAIT_OBJECT_0);
#elif OS_TYPE == 1 /* uITRON */
return (int)(tloc_mtx(Mutex[vol], FF_FS_TIMEOUT) == E_OK);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
OSMutexPend(Mutex[vol], FF_FS_TIMEOUT, &err));
return (int)(err == OS_NO_ERR);
#elif OS_TYPE == 3 /* FreeRTOS */
return (int)(xSemaphoreTake(Mutex[vol], FF_FS_TIMEOUT) == pdTRUE);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
return (int)(osMutexWait(Mutex[vol], FF_FS_TIMEOUT) == osOK);
#endif
}
/*------------------------------------------------------------------------*/
/* Release a Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on leave file functions to unlock the volume.
*/
void ff_mutex_give (
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
ReleaseMutex(Mutex[vol]);
#elif OS_TYPE == 1 /* uITRON */
unl_mtx(Mutex[vol]);
#elif OS_TYPE == 2 /* uC/OS-II */
OSMutexPost(Mutex[vol]);
#elif OS_TYPE == 3 /* FreeRTOS */
xSemaphoreGive(Mutex[vol]);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexRelease(Mutex[vol]);
#endif
}
#endif /* FF_FS_REENTRANT */

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------*/
/* Unicode handling functions for FatFs R0.13+ */
/* Unicode Handling Functions for FatFs R0.13 and Later */
/*------------------------------------------------------------------------*/
/* This module will occupy a huge memory in the .rodata section when the */
/* FatFs is configured for LFN with DBCS. If the system has a Unicode */
/* library for the code conversion, this module should be modified to use */
/* it to avoid silly memory consumption. */
/*------------------------------------------------------------------------*/
/* This module will occupy a huge memory in the .const section when the /
/ FatFs is configured for LFN with DBCS. If the system has any Unicode /
/ utilitiy for the code conversion, this module should be modified to use /
/ that function to avoid silly memory consumption. /
/-------------------------------------------------------------------------*/
/*
/ Copyright (C) 2014, ChaN, all right reserved.
/ Copyright (C) 2022, 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
@ -25,7 +25,7 @@
#include <fatfs/ff.h>
#if FF_USE_LFN /* This module will be blanked if non-LFN configuration */
#if FF_USE_LFN != 0 /* This module will be blanked if in non-LFN configuration */
#define MERGE2(a, b) a ## b
#define CVTBL(tbl, cp) MERGE2(tbl, cp)
@ -15214,8 +15214,8 @@ static const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */
/*------------------------------------------------------------------------*/
/* OEM <==> Unicode conversions for static code page configuration */
/* SBCS fixed code page */
/* OEM <==> Unicode Conversions for Static Code Page Configuration with */
/* SBCS Fixed Code Page */
/*------------------------------------------------------------------------*/
#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900
@ -15267,8 +15267,8 @@ WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
/*------------------------------------------------------------------------*/
/* OEM <==> Unicode conversions for static code page configuration */
/* DBCS fixed code page */
/* OEM <==> Unicode Conversions for Static Code Page Configuration with */
/* DBCS Fixed Code Page */
/*------------------------------------------------------------------------*/
#if FF_CODE_PAGE >= 900
@ -15346,7 +15346,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
/*------------------------------------------------------------------------*/
/* OEM <==> Unicode conversions for dynamic code page configuration */
/* OEM <==> Unicode Conversions for Dynamic Code Page Configuration */
/*------------------------------------------------------------------------*/
#if FF_CODE_PAGE == 0
@ -15458,7 +15458,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
/*------------------------------------------------------------------------*/
/* Unicode up-case conversion */
/* Unicode Up-case Conversion */
/*------------------------------------------------------------------------*/
DWORD ff_wtoupper ( /* Returns up-converted code point */
@ -15590,4 +15590,4 @@ DWORD ff_wtoupper ( /* Returns up-converted code point */
}
#endif /* #if FF_USE_LFN */
#endif /* #if FF_USE_LFN != 0 */

View File

@ -27,12 +27,20 @@
#include <stm-periph/rcc-manager.h>
#include <stm32/stm32f4xx.h>
#if HW_REV_DETECT_PIN_LOW > HW_REV_DETECT_PIN_HIGH
#error Configuration error for Hardware derection pins. Lowest position must be less than the highest pin position.
#endif
enum hw_revision get_pcb_hardware_version(void)
{
uint8_t current_pin;
uint16_t port_bitmask = 0U;
uint16_t port_bitmask = 0U; /* Use uint16_t because a port can have 16 IOs max. */
const uint16_t highest_bit_mask = (1 << (HW_REV_DETECT_PIN_HIGH - HW_REV_DETECT_PIN_LOW));
static enum hw_revision revision = HW_REV_NOT_DETECTED;
/* If the revision has been previously detected,
* just return it and don't do the whole detection stuff
*/
if (revision != HW_REV_NOT_DETECTED)
return revision;
@ -45,12 +53,15 @@ enum hw_revision get_pcb_hardware_version(void)
HW_REV_DETECT_GPIO->PUPDR |= PULLUP(current_pin);
}
/* Loop again and read in the pin mask */
/* Loop again and read in the pin mask.
* Because we use GND-Shorts on the pins to detect the version, the pins are read inverted.
*/
for (current_pin = HW_REV_DETECT_PIN_LOW; current_pin <= HW_REV_DETECT_PIN_HIGH; current_pin++) {
port_bitmask >>= 1;
port_bitmask |= (HW_REV_DETECT_GPIO->IDR & (1 << current_pin)) ? 0x0 : 0x80;
port_bitmask |= (HW_REV_DETECT_GPIO->IDR & (1 << current_pin)) ? 0x0 : highest_bit_mask;
}
/* Resolve the read in bitmask to a hardware version */
switch (port_bitmask) {
case 0U:
revision = HW_REV_V1_2;
@ -58,6 +69,9 @@ enum hw_revision get_pcb_hardware_version(void)
case 1U:
revision = HW_REV_V1_3;
break;
case 2U:
revision = HW_REV_V1_3_1;
break;
default:
revision = HW_REV_ERROR;
}

View File

@ -1,8 +1,8 @@
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem module R0.14a /
/ FatFs - Generic FAT Filesystem module R0.15 /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 2020, ChaN, all right reserved.
/ Copyright (C) 2022, 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 80196 /* Revision ID */
#define FF_DEFINED 80286 /* Revision ID */
#ifdef __cplusplus
extern "C" {
@ -35,10 +35,14 @@ extern "C" {
/* Integer types used for FatFs API */
#if defined(_WIN32) /* Main development platform */
#if defined(_WIN32) /* Windows VC++ (for development only) */
#define FF_INTDEF 2
#include <windows.h>
typedef unsigned __int64 QWORD;
#include <float.h>
#define isnan(v) _isnan(v)
#define isinf(v) (!_finite(v))
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
#define FF_INTDEF 2
#include <stdint.h>
@ -48,6 +52,7 @@ typedef uint16_t WORD; /* 16-bit unsigned integer */
typedef uint32_t DWORD; /* 32-bit unsigned integer */
typedef uint64_t QWORD; /* 64-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#else /* Earlier than C99 */
#define FF_INTDEF 1
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
@ -58,53 +63,6 @@ typedef WORD WCHAR; /* UTF-16 character type */
#endif
/* Definitions of volume management */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
#endif
/* Type of path name strings on FatFs API */
#ifndef _INC_TCHAR
#define _INC_TCHAR
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
#endif
/* Type of file size and LBA variables */
#if FF_FS_EXFAT
@ -127,14 +85,57 @@ typedef DWORD LBA_t;
/* Type of path name strings on FatFs API (TCHAR) */
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
/* Definitions of volume management */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
#endif
/* Filesystem object structure (FATFS) */
typedef struct {
BYTE fs_type; /* Filesystem type (0:not mounted) */
BYTE pdrv; /* Associated physical drive */
BYTE pdrv; /* Volume hosting physical drive */
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] flag (b0:dirty) */
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
BYTE wflag; /* win[] status (b0:dirty) */
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
WORD id; /* Volume mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */
@ -147,9 +148,6 @@ typedef struct {
#if FF_FS_EXFAT
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
#endif
#if FF_FS_REENTRANT
FF_SYNC_t sobj; /* Identifier of sync object */
#endif
#if !FF_FS_READONLY
DWORD last_clst; /* Last allocated cluster */
DWORD free_clst; /* Number of free clusters */
@ -163,10 +161,10 @@ typedef struct {
#endif
#endif
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Size of an FAT [sectors] */
DWORD fsize; /* Number of sectors per FAT */
LBA_t volbase; /* Volume base sector */
LBA_t fatbase; /* FAT base sector */
LBA_t dirbase; /* Root directory base sector/cluster */
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
LBA_t database; /* Data base sector */
#if FF_FS_EXFAT
LBA_t bitbase; /* Allocation bitmap base sector */
@ -181,7 +179,7 @@ typedef struct {
typedef struct {
FATFS* fs; /* Pointer to the hosting volume of this object */
WORD id; /* Hosting volume mount ID */
WORD id; /* Hosting volume's mount ID */
BYTE attr; /* Object attribute */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
@ -250,7 +248,7 @@ typedef struct {
WORD ftime; /* Modified time */
BYTE fattrib; /* File attribute */
#if FF_USE_LFN
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
#else
TCHAR fname[12 + 1]; /* File name */
@ -298,8 +296,10 @@ typedef enum {
/*--------------------------------------------------------------*/
/* FatFs Module Application Interface */
/*--------------------------------------------------------------*/
/* FatFs module application interface */
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_close (FIL* fp); /* Close an open file object */
@ -336,6 +336,8 @@ int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
/* Some API fucntions are implemented as macro */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr)
@ -349,38 +351,43 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
/*--------------------------------------------------------------*/
/* Additional user defined functions */
/* Additional Functions */
/*--------------------------------------------------------------*/
/* RTC function */
/* RTC function (provided by user) */
#if !FF_FS_READONLY && !FF_FS_NORTC
DWORD get_fattime (void);
DWORD get_fattime (void); /* Get current time */
#endif
/* LFN support functions */
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
/* LFN support functions (defined in ffunicode.c) */
#if FF_USE_LFN >= 1
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
#endif
/* O/S dependent functions (samples available in ffsystem.c) */
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */
#endif
/* Sync functions */
#if FF_FS_REENTRANT
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
#if FF_FS_REENTRANT /* Sync functions */
int ff_mutex_create (int vol); /* Create a sync object */
void ff_mutex_delete (int vol); /* Delete a sync object */
int ff_mutex_take (int vol); /* Lock sync object */
void ff_mutex_give (int vol); /* Unlock sync object */
#endif
/*--------------------------------------------------------------*/
/* Flags and offset address */
/* Flags and Offset Address */
/*--------------------------------------------------------------*/
/* File access mode and open method flags (3rd argument of f_open) */
#define FA_READ 0x01

View File

@ -1,8 +1,8 @@
/*---------------------------------------------------------------------------/
/ FatFs Functional Configurations
/ Configurations of FatFs Module
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 80196 /* Revision ID */
#define FFCONF_DEF 80286 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
@ -25,14 +25,6 @@
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_STRFUNC 1
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define FF_USE_FIND 1
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
@ -64,6 +56,30 @@
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
#define FF_USE_STRFUNC 1
#define FF_PRINT_LLI 0
#define FF_PRINT_FLOAT 0
#define FF_STRF_ENCODE 0
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion.
/
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
/ makes f_printf() support floating point argument. These features want C99 or later.
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/ to be read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
@ -137,19 +153,6 @@
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_STRF_ENCODE 3
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
/ This option selects assumption of character encoding ON THE FILE to be
/ read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
#define FF_FS_RPATH 2
/* This option configures support for relative path.
/
@ -175,7 +178,7 @@
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/ not defined, a user defined volume string table is needed as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
@ -187,14 +190,14 @@
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this function is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
/ function will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/ for variable sector size mode and disk_ioctl() function needs to implement
/ GET_SECTOR_SIZE command. */
@ -228,7 +231,7 @@
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 0
#define FF_FS_EXFAT 1
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
@ -237,10 +240,10 @@
#define FF_FS_NORTC 0
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#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
#define FF_NORTC_YEAR 2022
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
@ -250,7 +253,7 @@
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ option, and f_getfree() function at the first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
@ -272,26 +275,21 @@
/ lock control is independent of re-entrancy. */
/* #include <somertos.h> // O/S definitions */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
#define FF_SYNC_t HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/ to the same volume is under control of this featuer.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
/ function, must be added to the project. Samples are available in ffsystem.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
*/

View File

@ -65,11 +65,19 @@
#define BEEPER_RCC_MASK RCC_AHB1ENR_GPIOBEN
/**
* @brief Enable all clocks and setup pins in default setting
* @brief Enable all clocks and setup pins in default setting.
* @warning This function uses @ref rcc_manager_enable_clock to enable the clocks. Therefore, it must not be called
* multiple times.
* @note Calls @ref digio_set_default_values() internally.
*/
void digio_setup_default_all(void);
void digio_init(void);
/**
* @brief Setup or restore the default DIGIO settings.
*
* This function can be called multiple times.
*/
void digio_set_default_values(void);
/**
* @brief Set up a DIGIO pin.
@ -146,7 +154,7 @@ int led_get(uint8_t num);
#define LOUDSPEAKER_PIN 1
/**
* @brief The loudpseaker requires a frequncy signal instead of a simple on/off signal.
* @brief The loudpseaker requires a frequency signal instead of a simple on/off signal.
*/
#define LOUDSPEAKER_MULTIFREQ 1
@ -162,7 +170,14 @@ void loudspeaker_setup(void);
/**
* @brief Set the loudspeaker value
* @param val Value
*
* Zero turns off the beeper. 1 is a special value
* and will set the @ref LOUDSPEAKER_MULTIFREQ_DEFAULT value.
*
* If @ref LOUDSPEAKER_MULTIFREQ is 0, then no actual frequency is produced.
* Instead any @p val unequal to zero turns the output pin high and 0 will turn it low.
*
* @param val Value.
*/
void loudspeaker_set(uint16_t val);

View File

@ -48,7 +48,6 @@
*/
#define HW_REV_DETECT_PIN_HIGH (15U)
/**
* @brief PCB/Hardware Revision Type
*/
@ -57,6 +56,7 @@ enum hw_revision {
HW_REV_ERROR = 1, /**< @brief The hardware revision could not be detected due to an internal error */
HW_REV_V1_2 = 120, /**< @brief Hardware Revision v1.2 */
HW_REV_V1_3 = 130, /**< @brief Hardware Revision v1.3 */
HW_REV_V1_3_1 = 131, /**< @brief Hardware revision v1.3.1 */
};
/**
@ -70,6 +70,7 @@ enum hw_revision {
* The function returns the HW revision as an enum hw_revision.
* For v1.2 the return value is 120 (HW_REV_V1_2).
* For v1.3 the return value is 130 (HW_REV_V1_3).
* For v1.3.1 the return value is 131 (HW_REV_V1_3_1).
*
* Other return values are not defined yet.
*

View File

@ -18,15 +18,52 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @defgroup main-cycle-counter Main Cycle Counter
* The main cycle counter is incremented after every loop run of the main loop in main.c
* @{
*/
#ifndef __MAIN_CYCLE_COUNTER_H__
#define __MAIN_CYCLE_COUNTER_H__
#include <stdint.h>
void main_cycle_counter_init(void);
/**
* @brief Initialize the main cycle counter and reset to 0. This also enables the core cycle counter
* This function can be called multiple times.
*/
void main_and_core_cycle_counter_init(void);
/**
* @brief Reset the main cycle counter.
* @note This does not reset the core cycle counter
*/
void main_cycle_counter_reset(void);
/**
* @brief Increment the main cycle counter by 1
*/
void main_cycle_counter_inc(void);
/**
* @brief Get the current main cycle counter value
* @return Value
*/
uint64_t main_cycle_counter_get(void);
/**
* @brief Reset the core cycle counter to 0
*/
void core_cycle_counter_reset(void);
/**
* @brief Get the current value of the core cycle counter
*
* @return uint32_t Counter value
*/
uint32_t core_cycle_counter_get(void);
#endif /* __MAIN_CYCLE_COUNTER_H__ */
/** @} */

View File

@ -21,32 +21,93 @@
#ifndef __OVEN_DRIVER_H__
#define __OVEN_DRIVER_H__
/**
* @defgroup oven-driver Oven SSR Driver and PID Controller
* @{
*/
#include <stdint.h>
#include <stdbool.h>
#include <reflow-controller/pid-controller.h>
enum oven_pid_status {OVEN_PID_DEACTIVATED,
OVEN_PID_RUNNING,
OVEN_PID_ABORTED};
/**
* @brief Status of the PID controlling the oven
*/
enum oven_pid_status {
OVEN_PID_DEACTIVATED, /**< @brief The PID of the oven is deactivated. */
OVEN_PID_RUNNING, /**< @brief The PID of the oven is currently active and running. */
OVEN_PID_ABORTED, /**< @brief The PID of the oven has been aborted due to an error and is not running. */
};
/**
* @brief Initialize the oven driver.
*
* This will initialize the Timer for the PWM output to the SSR.
* If the hardware revision is >= v1.3 the SSR safety enable line will also be initialized.
*/
void oven_driver_init(void);
/**
* @brief Set a power level on the oven control output
* @param power Power level between 0 to 100
* @note This will not actually set the output. For this, @ref oven_driver_apply_power_level() has to be called.
* It will be called in the main loop.
*/
void oven_driver_set_power(uint8_t power);
/**
* @brief Disable the oven driver.
*
* This shuts down the oven driver timer and the corresponding clocks.
*/
void oven_driver_disable(void);
/**
* @brief Initialize the PID controller for the oven output
* @param PID controller holding the settings
*/
void oven_pid_init(struct pid_controller *controller_to_copy);
/**
* @brief Handle the PID controller.
* This must be called cyclically. When the sampling period has passed, the function will process the PT1000
* resistance and do a PID cycluilation for this sample.
* @note This function must be called with a frequency greater or equal to the PID's sampling frequency
*/
void oven_pid_handle(void);
/**
* @brief Stop the oven PID controller
*/
void oven_pid_stop(void);
/**
* @brief Abort the oven PID controller. This is the same as oven_pid_stop() but will set the abort flag.
* @note this function is called by the safety controller to disable the PID controller in case of an error.
*/
void oven_pid_abort(void);
/**
* @brief Set the target temperature of the PID controller.
* @param temp
*/
void oven_pid_set_target_temperature(float temp);
/**
* @brief Output the power level currently configured to the SSR.
*
* This function is separated from oven_driver_set_power() because it is called in the main loop after the
* safety controller has run. This ensures, that if the safety controller decides to stop the PID no glitch makes it
* out to the SSR.
*/
void oven_driver_apply_power_level(void);
/**
* @brief Get the current status of the oven's PID controller
* @return
*/
enum oven_pid_status oven_pid_get_status(void);
/** @} */
#endif /* __OVEN_DRIVER_H__ */

View File

@ -25,9 +25,11 @@
#endif
/* UART_DIV is 45.5625 => 115200 @ 84 MHz */
#define SHELL_UART_DIV_FRACTION 9U /* Equals 9/16 = 0.5625 */
#define SHELL_UART_DIV_MANTISSA 45U /* Equals 45 */
#define SHELL_UART_DEFAULT_DIV_FRACTION 9U /* Equals 9/16 = 0.5625 */
#define SHELL_UART_DEFAULT_DIV_MANTISSA 45U /* Equals 45 */
#define SHELL_UART_BRR_REG_VALUE ((SHELL_UART_DIV_MANTISSA<<4) | SHELL_UART_DIV_FRACTION);
#define SHELL_UART_DEFAULT_BRR_REG_VALUE ((SHELL_UART_DEFAULT_DIV_MANTISSA<<4) | SHELL_UART_DEFAULT_DIV_FRACTION);
#define SHELL_UART_PERIPHERAL_CLOCK (84000000UL)
#endif /* __SHELL_UART_CONFIG_H__ */

View File

@ -0,0 +1,37 @@
/* Reflow Oven Controller
*
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SAFETY_FLASH_CRC_STRUCT_H_
#define _SAFETY_FLASH_CRC_STRUCT_H_
#include <stdint.h>
struct flash_crcs {
uint32_t start_magic;
uint32_t crc_section_text;
uint32_t crc_section_data;
uint32_t crc_section_ccm_data;
uint32_t crc_section_vectors;
uint32_t end_magic;
};
extern const struct flash_crcs crcs_in_flash;
#endif /* _SAFETY_FLASH_CRC_STRUCT_H_ */

View File

@ -0,0 +1,56 @@
/* Reflow Oven Controller
*
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SAFETY_FLASH_CRC_H_
#define _SAFETY_FLASH_CRC_H_
#include <stdint.h>
enum flash_crc_section {
FLASH_CRC_VECTOR = 0,
FLASH_CRC_TEXT,
FLASH_CRC_DATA,
FLASH_CRC_CCMDATA,
N_FLASH_CRC,
};
/**
* @brief Perform a CRC check of the flash memory and set appropriate flags
* @return negative if internal error occured. Otherwise (independent from CRC check result) 0.
* @note This function requires the safety controller (and CRC unit) to be set up before calling!
*/
int flash_crc_trigger_check(void);
/**
* @brief Calculate CRC over flash section
* @param sec Section to calculate CRC over
* @param[out] crc_result Calculated CRC
* @return negative if internal error occured. Otherwise (independent from CRC check result) 0.
*/
int flash_crc_calc_section(enum flash_crc_section sec, uint32_t *crc_result);
/**
* @brief Get expected CRC value of a section
* @param sec Section
* @return Expected CRC
*/
uint32_t flash_crc_get_expected_crc(enum flash_crc_section sec);
#endif /* _SAFETY_FLASH_CRC_H_ */

View File

@ -27,6 +27,15 @@
#ifndef __SAFETY_CONFIG_H__
#define __SAFETY_CONFIG_H__
/**
* @brief Weights of error flags.
*/
enum config_weight {
SAFETY_FLAG_CONFIG_WEIGHT_NONE = 0, /**< @brief This flag has no global error consequence, but might be respected by certain software modules. */
SAFETY_FLAG_CONFIG_WEIGHT_PID = 1, /**< @brief This flag will force a stop of the temperature PID controller */
SAFETY_FLAG_CONFIG_WEIGHT_PANIC = 2, /**< @brief This flag will trigger the panic mode */
};
/**
* @brief Enum type representing safety flags.
*
@ -60,6 +69,7 @@ enum safety_flag {
ERR_FLAG_FLASH_CRC_DATA = (1<<20),
ERR_FLAG_CFG_CRC_MEAS_ADC = (1<<21),
ERR_FLAG_CFG_CRC_SAFETY_ADC = (1<<22),
ERR_FLAG_CFG_CRC_MISC = (1<<23),
};
/**
@ -78,6 +88,7 @@ enum timing_monitor {
enum crc_monitor {
ERR_CRC_MON_MEAS_ADC = 0,
ERR_CRC_MON_SAFETY_ADC,
ERR_CRC_MON_MISC,
N_ERR_CRC_MON
};
@ -115,19 +126,21 @@ enum analog_value_monitor {
#define WATCHDOG_HALT_DEBUG (0)
#endif
#define WATCHDOG_PRESCALER 16
/**
* @brief Watchdog clock prescaler value
*/
#define WATCHDOG_PRESCALER (16)
/**
* @brief Watchdog reload value
*/
#define WATCHDOG_RELOAD_VALUE (2500)
/**
* @brief Minimum number of bytes that have to be free on the stack. If this is not the case, an error is detected
*/
#define SAFETY_MIN_STACK_FREE 0x100
#define PID_CONTROLLER_ERR_CAREMASK (ERR_FLAG_STACK | ERR_FLAG_AMON_UC_TEMP | ERR_FLAG_AMON_VREF | \
ERR_FLAG_TIMING_PID | ERR_FLAG_TIMING_MEAS_ADC | ERR_FLAG_MEAS_ADC_OFF | \
ERR_FLAG_MEAS_ADC_OVERFLOW)
#define HALTING_CAREMASK (ERR_FLAG_STACK | ERR_FLAG_AMON_UC_TEMP)
#define SAFETY_ADC_VREF_MVOLT (2500.0f)
#define SAFETY_ADC_VREF_TOL_MVOLT (100.0f)
#define SAFETY_ADC_TEMP_LOW_LIM (0.0f)
@ -165,7 +178,13 @@ enum analog_value_monitor {
#define SAFETY_CRC_MON_SAFETY_ADC_PW 0xA8DF2368
/**
* @brief Default persistence of safety flags. These values are loaded into the safety tables on startup
* @brief Password for resetting ERR_CRC_MON_MISC
*
*/
#define SAFETY_CRC_MON_MISC_PW 0x9A62E96A
/**
* @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), \
@ -190,8 +209,9 @@ enum analog_value_monitor {
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_FLASH_CRC_DATA, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_CFG_CRC_MISC, true),
/**
* @brief Default config weights of safety flags. These values are loaded into the safety tables on startup
* @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), \
@ -212,9 +232,10 @@ enum analog_value_monitor {
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SAFETY_TAB_CORRUPT, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_AMON_SUPPLY_VOLT, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_OVERTEMP, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_FLASH_CRC_CODE, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_FLASH_CRC_DATA, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_FLASH_CRC_CODE, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_FLASH_CRC_DATA, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_MISC, SAFETY_FLAG_CONFIG_WEIGHT_PANIC)
#endif /* __SAFETY_CONFIG_H__ */

View File

@ -70,14 +70,14 @@ struct timing_monitor_info {
* You have to call safety_controller_handle
* If this function fails, it will hang, because errors in the safety controller are not recoverable
*/
void safety_controller_init();
void safety_controller_init(void);
/**
* @brief Handle the safety controller.
* @note This function must be executed periodically in order to prevent the watchdog from resetting the firmware
* @return 0 if successful
* @returns Worst flag weigth that is currently set.
*/
int safety_controller_handle();
enum config_weight safety_controller_handle(void);
/**
* @brief Report one or multiple errors to the safety controller
@ -170,13 +170,13 @@ bool safety_controller_get_flags_by_mask(enum safety_flag mask);
* @brief Get the count of error flags
* @return Error flag count
*/
uint32_t safety_controller_get_flag_count();
uint32_t safety_controller_get_flag_count(void);
/**
* @brief Get the count of analog monitors
* @return Analog monitor count
*/
uint32_t safety_controller_get_analog_monitor_count();
uint32_t safety_controller_get_analog_monitor_count(void);
/**
* @brief Get an error flag's name by its index.
@ -267,13 +267,6 @@ int safety_controller_set_overtemp_limit(float over_temperature);
*/
float safety_controller_get_overtemp_limit(void);
/**
* @brief Perform a CRC check of the flash memory and set appropriate flags
* @return negative if internal error occured. Otherwise (independent from CRC check result) 0.
* @note This function requires the safety controller to be set up before!
*/
int safety_controller_trigger_flash_crc_check(void);
/**
* @brief Recalculate the CRC of a given CRC Monitor. This has to be done once the supervised registers update
* @param mon Monitor to recalculate

View File

@ -24,6 +24,7 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <reflow-controller/safety/safety-config.h>
/** @addtogroup safety-memory
* @{
@ -131,15 +132,6 @@ enum config_override_entry_type {
SAFETY_MEMORY_CONFIG_OVERRIDE_PERSISTENCE = 2,
};
/**
* @brief Weights of error flags.
*/
enum config_weight {
SAFETY_FLAG_CONFIG_WEIGHT_NONE = 0, /**< @brief This flag has no global error consequence, but might be respected by certain software modules. */
SAFETY_FLAG_CONFIG_WEIGHT_PID = 1, /**< @brief This flag will force a stop of the temperature PID controller */
SAFETY_FLAG_CONFIG_WEIGHT_PANIC = 2, /**< @brief This flag will trigger the panic mode */
};
/**
* @brief representation of a config override memory entry
*/

View File

@ -27,11 +27,19 @@
/**
* @brief Setup the watchdog for the safety controller
* @param Prescaler to use for the 32 KHz LSI clock
*
* The watchdog timeout can be calculated with:
* \f[ t = \frac{(\mathrm{RELOAD_VAL} + 1)\cdot \mathrm{PRESCALER}}{32000 } s\f]
*
* Valid prescaler values are: 4, 8, 16, 32, 64, 128, 256.
* @param prescaler Prescaler to use for the 32 KHz LSI clock
* @param reload_value Reload value to reload the timer with when reset. 0 to 0xFFF
* @return 0 if successful
* @return -1 if prescaler is wrong
* @return -2 if a reload value > 0xFFF is selected. 0xFFF will be used in this case
* @note Once the watchdog is enabled, it cannot be turned off!
*/
int watchdog_setup(uint8_t prescaler);
int watchdog_setup(uint16_t prescaler, uint16_t reload_value);
/**
* @brief Reset watchdog counter

View File

@ -0,0 +1,42 @@
/* Reflow Oven Controller
*
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* GDSII-Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SD_H_
#define _SD_H_
#include <stdbool.h>
#include <fatfs/ff.h>
/**
* @brief Mount the SD card if available and not already mounted
* @param fs Filesystem to mount to.
* @return true if mounted, false if an error occured or the SD is not inserted and cannot be mounted
*/
bool mount_sd_card_if_avail(FATFS *fs);
/**
* @brief Return whether an SD card is inserted and mounted
*
* @return true SD inserted and mounted
* @return false No SD inserted or not mounted
*/
bool sd_card_is_mounted(void);
#endif /* _SD_H_ */

View File

@ -28,12 +28,12 @@
* @brief Initialize the SPI for the eeprom.
* @return 0 if succesful
*/
int spi_eeprom_init();
int spi_eeprom_init(void);
/**
* @brief Uninitialize the SPI EEPROM
*/
void spi_eeprom_deinit();
void spi_eeprom_deinit(void);
/**
* @brief Read from SPI EEPROM

View File

@ -25,7 +25,8 @@
* @brief Convert PT1000 resistance to temperature in degrees celsius
* @param resistance PT1000 resistance value
* @param[out] temp_out Temperature output
* @return 0 if ok, -1 if value is below conversion range, 1 if value is above conversion range,-1000 in case of pointer error
* @return 0 if ok, -1 if value is below conversion range, 1 if value is above conversion range,
* -1000 in case of pointer error
*/
int temp_converter_convert_resistance_to_temp(float resistance, float *temp_out);

View File

@ -44,9 +44,9 @@ enum tpe_status {
};
/**
* @brief The current execution state of the temperature profile
* @brief The execution state of the temperature profile
*/
struct tpe_current_state {
struct tpe_exec_state {
enum tpe_status status; /**< @brief Execution status */
float setpoint; /**< @brief Temperature setpoint in degrees Celsius */
uint64_t start_timestamp; /**< @brief The millisicend tick timestamp, the profile execution was started */
@ -78,7 +78,7 @@ int temp_profile_executer_handle(void);
* @warning The returned state structure is static and used internally. You must not modify it.
* @return Execution state
*/
const struct tpe_current_state *temp_profile_executer_status(void);
const struct tpe_exec_state *temp_profile_executer_status(void);
/**
* @brief Stop the temperature profile execution.

View File

@ -19,7 +19,7 @@
*/
/**
* @addtogroup temp-profile
* @defgroup temp-profile Temperature Profile Parser and Executer
* @{
*/
@ -41,6 +41,9 @@ enum pl_command_type {
PL_LOUDSPEAKER_SET, /**< @brief Set the loudspeaker/beeper */
PL_OFF, /**< @brief Disable the temperature output and shutdown the PID controller */
PL_CLEAR_FLAGS, /**< @brief Try clear all flags */
PL_DIGIO_CONF, /**< @brief Configure a DIGIO pin */
PL_DIGIO_SET, /**< @brief Set a DIGIO pin */
PL_DIGIO_WAIT, /**< @brief Wait until a DIGIO pin is set to the specified level */
_PL_NUM_CMDS, /**< @brief Sentinel to determine the total amount of commands */
};
@ -56,7 +59,7 @@ enum pl_ret_val {
};
/**
* @brief Maximum parameter count of a command
* @brief Maximum parameter count of a command.
*/
#define PROFILE_LANG_MAX_NUM_ARGS (8)
@ -89,7 +92,7 @@ enum pl_ret_val temp_profile_parse_from_file(const char *filename,
uint32_t *cmds_parsed);
/**
* @brief Fully free a comamnd list including hte sotred command structures.
* @brief Fully free a comamnd list including hte stored command structures.
*
* \p list's destination is set to NULL to indicate the empty list.
*

View File

@ -22,6 +22,7 @@
#define _GUI_H_
#include <stdint.h>
#include <stddef.h>
/**
* @brief Handle the reflow controller's LCD Menu
@ -29,10 +30,39 @@
*/
int gui_handle(void);
/**
* @brief Initialize the GUI (LCD, button, and rotary encoder)
*/
void gui_init(void);
/**
* @brief Set a overlay message displayed on top of the root menu
* @param heading Heading of the overlay message (1st line)
* @param text Text of the overlay (the two bottom lines, 2 times 16 chars)
*/
void gui_root_menu_message_set(const char *heading, const char *text);
/**
* @brief Directly write to the LCD
*
* This function writes directly to the LCD and doesn't use the handling FSM in the
* background. Therefore, the function will block until all data is written to the LCD.
*
* @param line line to write to. Starts at 0
* @param text Text to write to the line
*/
void gui_lcd_write_direct_blocking(uint8_t line, const char *text);
/**
* @brief Get the vertical size of the display
* @return Count of rows
*/
size_t gui_get_line_count(void);
/**
* @brief Return the const char disp[][21] array contianing all display rows
* @note This directly returns the working buffer pointer. Do not change it!
*/
const char (*gui_get_current_display_content(void))[21];
#endif /* _GUI_H_ */

View File

@ -0,0 +1,51 @@
/* Reflow Oven Controller
*
* Copyright (C) 2021 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SHELL_UART_H__
#include <shellmatta.h>
#include <stddef.h>
#include <stdint.h>
/**
* @brief Configure the UART for the shellmatta shell.
*
* This will configure the UART for use with a DMA ring buffer.
* @param uart
*/
void shell_uart_setup(void);
shellmatta_retCode_t shell_uart_write_callback(const char *data, uint32_t len);
int shell_uart_receive_data_with_dma(const char **data, size_t *len);
/**
* @brief Configure a new connection speed.
* @param new_baud Baudrate. E.g: 115200
* @return Error in permille (1/1000). A return value of 2 means a baudrate error of: 0.002 or 0.2%.
* Return value is negative in case of a hard error.
*/
int32_t shell_uart_reconfig_baud(uint32_t new_baud);
uint32_t shell_uart_get_current_baudrate(void);
#define __SHELL_UART_H__
#endif /* __SHELL_UART_H__ */

View File

@ -0,0 +1,29 @@
/* Reflow Oven Controller
*
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _VERSION_H_
#define _VERSION_H_
extern const char *version_git_version_string;
extern const char *version_git_full_commit_string;
extern const char *version_compile_date;
extern const char *version_compile_time;
#endif /* _VERSION_H_ */

View File

@ -0,0 +1,45 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* GDSII-Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _OPTION_BYTES_H_
#define _OPTION_BYTES_H_
#include <stdint.h>
struct option_bytes {
/* Word 1 */
uint32_t read_protection;// : 8;
uint32_t nrst_standby;// : 1;
uint32_t nrst_stop;// : 1;
uint32_t wdg_sw;// : 1;
uint32_t brown_out_level;// : 2;
/* Word 2 */
uint32_t nwrpi;// : 12;
};
/**
* @brief Read out the option bytes to structs
* @param opts
*/
void stm_option_bytes_read(struct option_bytes *opts);
int stm_option_bytes_program(const struct option_bytes *opts);
#endif /* _OPTION_BYTES_H_ */

View File

@ -33,7 +33,7 @@ enum random_number_error {
void random_number_gen_init(bool int_enable);
void random_number_gen_deinit();
void random_number_gen_deinit(void);
void random_number_gen_reset(bool int_en);

View File

@ -50,6 +50,8 @@ int uart_init(struct stm_uart *uart);
void uart_change_brr(struct stm_uart *uart, uint32_t brr);
uint32_t uart_get_brr(struct stm_uart *uart);
void uart_disable(struct stm_uart *uart);
void uart_send_char(struct stm_uart *uart, char c);

View File

@ -29,6 +29,10 @@
* @param mid mid word of ID
* @param low low word of ID
*/
void unique_id_get(uint32_t *high, uint32_t *mid, uint32_t *low);
void stm_unique_id_get(uint32_t *high, uint32_t *mid, uint32_t *low);
void stm_dev_rev_id_get(uint32_t *device_id, uint32_t *revision_id);
void stm_cpuid_get(uint8_t *implementer, uint8_t *variant, uint16_t *part_no, uint8_t *rev);
#endif /* __UNIQUE_ID_H__ */

View File

@ -18,12 +18,38 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup main-cycle-counter
* @{
*/
#include <reflow-controller/main-cycle-counter.h>
#include <helper-macros/helper-macros.h>
#include <stm32/stm32f4xx.h>
/**
* @brief Variable storing the main cycle counter.
* @note This variable should not be accessed directly.
* Use the main_cycle_counter_get() or main_cycle_counter_inc() functions.
*/
static uint64_t IN_SECTION(.ccm.bss) main_cycle_counter;
void main_cycle_counter_init(void)
void main_and_core_cycle_counter_init(void)
{
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
/* Enable the core cycle counter if available on current processor */
if (!(DWT->CTRL & DWT_CTRL_NOCYCCNT_Msk)) {
/* Cycle counter is available. Enable */
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
/* Reset the counters to 0 */
main_cycle_counter_reset();
core_cycle_counter_reset();
}
void main_cycle_counter_reset(void)
{
main_cycle_counter = 0ULL;
}
@ -37,3 +63,15 @@ uint64_t main_cycle_counter_get(void)
{
return main_cycle_counter;
}
void core_cycle_counter_reset(void)
{
DWT->CYCCNT = 0UL;
}
uint32_t core_cycle_counter_get(void)
{
return DWT->CYCCNT;
}
/** @} */

View File

@ -23,6 +23,7 @@
* @brief Main file for firmware
*/
#include "reflow-controller/safety/safety-config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -31,16 +32,17 @@
#include <setup/system_stm32f4xx.h>
#include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/shell.h>
#include <reflow-controller/digio.h>
#include "fatfs/shimatta_sdio_driver/shimatta_sdio.h"
#include <stm-periph/stm32-gpio-macros.h>
#include <stm-periph/rcc-manager.h>
#include <stm-periph/uart.h>
#include <reflow-controller/periph-config/shell-uart-config.h>
#include <reflow-controller/oven-driver.h>
#include <fatfs/ff.h>
#include <reflow-controller/sd.h>
#include <reflow-controller/ui/gui.h>
#include <reflow-controller/ui/shell.h>
#include <reflow-controller/ui/shell-uart.h>
#include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/settings/settings.h>
#include <reflow-controller/safety/safety-memory.h>
@ -49,6 +51,7 @@
#include <reflow-controller/temp-profile/temp-profile-executer.h>
#include <reflow-controller/settings/spi-eeprom.h>
#include <reflow-controller/main-cycle-counter.h>
#include <stm-periph/option-bytes.h>
static void setup_nvic_priorities(void)
{
@ -67,13 +70,13 @@ static void setup_nvic_priorities(void)
FATFS fs;
#define fs_ptr (&fs)
/**
* @brief Configure UART GPIOs
* In case the application is build in debug mode, use the TX/RX Pins on the debug header
* else the Pins on the DIGIO header are configured in the digio module and this function does nothing.
*/
static inline void uart_gpio_config(void)
{
/*
* In case the application is build in debug mode, use the TX/RX Pins on the debug header
* else the Pins on the DIGIO header are configured in the digio module
*/
#if defined(DEBUGBUILD) || defined(UART_ON_DEBUG_HEADER)
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(SHELL_UART_PORT_RCC_MASK));
SHELL_UART_PORT->MODER &= MODER_DELETE(SHELL_UART_TX_PIN) & MODER_DELETE(SHELL_UART_RX_PIN);
@ -86,68 +89,13 @@ static inline void uart_gpio_config(void)
#endif
}
static char shell_uart_tx_buff[256];
static char shell_uart_rx_buff[48];
struct stm_uart shell_uart;
static shellmatta_retCode_t write_shell_callback(const char *data, uint32_t len)
{
uart_send_array_with_dma(&shell_uart, data, len);
return SHELLMATTA_OK;
}
static inline void setup_shell_uart(struct stm_uart *uart)
{
uart->rx = 1;
uart->tx = 1;
uart->brr_val = SHELL_UART_BRR_REG_VALUE;
uart->rcc_reg = &SHELL_UART_RCC_REG;
uart->rcc_bit_no = BITMASK_TO_BITNO(SHELL_UART_RCC_MASK);
uart->uart_dev = SHELL_UART_PERIPH;
uart->dma_rx_buff = shell_uart_rx_buff;
uart->dma_tx_buff = shell_uart_tx_buff;
uart->rx_buff_count = sizeof(shell_uart_rx_buff);
uart->tx_buff_count = sizeof(shell_uart_tx_buff);
uart->base_dma_num = 2;
uart->dma_rx_stream = SHELL_UART_RECEIVE_DMA_STREAM;
uart->dma_tx_stream = SHELL_UART_SEND_DMA_STREAM;
uart->dma_rx_trigger_channel = SHELL_UART_RX_DMA_TRIGGER;
uart->dma_tx_trigger_channel = SHELL_UART_TX_DMA_TRIGGER;
uart_init(uart);
NVIC_EnableIRQ(DMA2_Stream7_IRQn);
}
static bool mount_sd_card_if_avail(bool mounted)
{
FRESULT res;
static uint8_t IN_SECTION(.ccm.bss) inserted_counter = 0;
if (sdio_check_inserted() && mounted) {
memset(fs_ptr, 0, sizeof(FATFS));
sdio_stop_clk();
inserted_counter = 0;
return false;
}
if (!sdio_check_inserted() && inserted_counter < 255)
inserted_counter++;
if (!sdio_check_inserted() && !mounted && inserted_counter > 4) {
inserted_counter = 0;
res = f_mount(fs_ptr, "0:/", 1);
if (res == FR_OK) {
led_set(1, 1);
return true;
} else {
return false;
}
}
return mounted;
}
/**
* @brief Process the boot status structure in the safety (backup) RAM
* Depending on the flags set there, this function will:
* - Reboot into the ram code for reflashing
* - Display the PANIC message
* - Display if the flash has been successfully updated
*/
static inline void handle_boot_status(void)
{
struct safety_memory_boot_status status;
@ -183,34 +131,104 @@ static inline void handle_boot_status(void)
}
}
/**
* @brief Read out the option bytes of the STM32 and program them to the desired values.
*
* - This function currently forces the brown out level to Level 3.
*/
static void check_and_program_opt_bytes(void)
{
struct option_bytes opts;
int err;
/** - Read option bytes */
stm_option_bytes_read(&opts);
if (opts.brown_out_level != 0) {
/* Set the brown out level to level 3 => highest brown out limit. */
opts.brown_out_level = 0;
/** - Program the option bytes if brown out level was not set correctly */
err = stm_option_bytes_program(&opts);
/** - If programming failes, enter panic mode */
if (err)
panic_mode();
/** - If programming is successful, reset the system to apply new settings */
NVIC_SystemReset();
}
}
/**
* @brief Setup the system.
*
* This function does all basic initializations of the MCU and its peripherals
*/
static inline void setup_system(void)
{
float tmp;
/** - Read the option bytes and if necessary program them to the desired values */
check_and_program_opt_bytes();
/** - Setup the NVIC priorities of the core peripherals using interrupts */
setup_nvic_priorities();
/* Init safety controller and safety memory */
/** - Init safety controller and safety memory */
safety_controller_init();
/** - Setup the systick module generating the 100us tick fort the GUI and
* the 1ms tick for the global systick timestamp
*/
systick_setup();
/** - Initialize the oven output driver outputting the wavepacket control signal for the SSR and */
oven_driver_init();
digio_setup_default_all();
/** - Initialize all DIGIO Pins to their default state and pin functions */
digio_init();
/** - Set-up the LED outputs */
led_setup();
/** - Set-up the loudspeaker / beeper output */
loudspeaker_setup();
/** - Initialize the GUI */
gui_init();
/** - Initialize the pins for the uart interface. */
uart_gpio_config();
/** - Set-up the settings module */
settings_setup();
/* Load the overtemperature limit from eeprom if available. Otherwise the default value will be used */
/** - Load the overtemperature limit from eeprom if available. Otherwise the default value will be used */
if (settings_load_overtemp_limit(&tmp) == SETT_LOAD_SUCCESS)
safety_controller_set_overtemp_limit(tmp);
/** - Handle the boot status struct in the safety memory */
handle_boot_status();
setup_shell_uart(&shell_uart);
/** - Initialize the shell UART */
shell_uart_setup();
/** - Enable the ADC for PT1000 measurement */
adc_pt1000_setup_meas();
/** - Enable the misc CRC config monitor to supervise clock, systick and flash settings */
(void)safety_controller_set_crc_monitor(ERR_CRC_MON_MISC, SAFETY_CRC_MON_MISC_PW);
}
/**
* @brief Handle the input for the shell instance.
*
* This function will check if the RX ring buffer of the UART contains data.
* If so, it will prowvide it to the shellmatta shell.
*
* @param shell_handle Handle to the shellmatta instance
*/
static void handle_shell_uart_input(shellmatta_handle_t shell_handle)
{
int uart_receive_status;
@ -218,11 +236,15 @@ static void handle_shell_uart_input(shellmatta_handle_t shell_handle)
size_t uart_input_len;
/* Handle UART input for shell */
uart_receive_status = uart_receive_data_with_dma(&shell_uart, &uart_input, &uart_input_len);
uart_receive_status = shell_uart_receive_data_with_dma(&uart_input, &uart_input_len);
if (uart_receive_status >= 0)
shell_handle_input(shell_handle, uart_input, uart_input_len);
}
/**
* @brief This is the main function containing the initilizations and the cyclic main loop
* @return Don't care. This function will never return. We're on an embedded device...
*/
int main(void)
{
bool cal_active;
@ -234,26 +256,36 @@ int main(void)
shellmatta_handle_t shell_handle;
int menu_wait_request;
uint64_t quarter_sec_timestamp = 0ULL;
enum config_weight worst_safety_flag = SAFETY_FLAG_CONFIG_WEIGHT_NONE;
/* Setup all the peripherals and external componets like LCD, EEPROM etc. and the safety controller */
/** - 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 */
/** - Try load the calibration. This will only succeed if there's an EEPROM */
status = settings_load_calibration(&sens, &offset);
if (!status)
adc_pt1000_set_resistance_calibration(offset, sens, true);
shell_handle = shell_init(write_shell_callback);
/** - Initialize the shellmatta shell */
shell_handle = shell_init(shell_uart_write_callback);
/** - Print motd to shell */
shell_print_motd(shell_handle);
main_cycle_counter_init();
/** - Set the main cycle counter to 0 and activate the core cycle counter if available */
main_and_core_cycle_counter_init();
/** - Do a loop over the following */
while (1) {
/** - If 250 ms have passed since the last time this step was reached, we try to initialize the
* SD card. If the card has been mounted and there is no current resistance calibration,
* it is tried to load it from SD card.
*/
if (systick_ticks_have_passed(quarter_sec_timestamp, 250)) {
led_set(1, 0);
led_set(1u, 0);
sd_old = sd_card_mounted;
sd_card_mounted = mount_sd_card_if_avail(sd_card_mounted);
sd_card_mounted = mount_sd_card_if_avail(fs_ptr);
if (sd_card_mounted && !sd_old) {
adc_pt1000_get_resistance_calibration(NULL, NULL, &cal_active);
@ -264,46 +296,59 @@ int main(void)
}
}
/* Check if any flags are present, that disable the PID controller. Blink
* LED 0 in this case
*/
if (worst_safety_flag >= SAFETY_FLAG_CONFIG_WEIGHT_PID)
led_set(0u, led_get(0u) ? 0 : 1);
else
led_set(0u, 0);
quarter_sec_timestamp = systick_get_global_tick();
}
/** - Handle the GUI */
menu_wait_request = gui_handle();
/** - Handle the uart input for the shell */
handle_shell_uart_input(shell_handle);
/* Execute current profile step, if a profile is active */
/** - Execute current profile step, if a profile is active */
temp_profile_executer_handle();
safety_controller_handle();
/** - Handle the safety controller. This must be called! Otherwise a watchdog reset will occur */
worst_safety_flag = safety_controller_handle();
/** - If the Oven PID controller is running, we handle its sample function */
if (oven_pid_get_status() == OVEN_PID_RUNNING)
oven_pid_handle();
/** - Apply the power level of the oven driver */
oven_driver_apply_power_level();
/** - Report the main loop timing to the timing monitor to detect a slowed down main loop */
safety_controller_report_timing(ERR_TIMING_MAIN_LOOP);
/** - If the menu requests a directly following loop run, the main loop will continue.
* Otherwise it will wait for the next interrupt
*/
if (menu_wait_request)
__WFI();
else
__NOP();
/** - Increment the main cycle counter */
main_cycle_counter_inc();
}
return 0;
}
/**
* @brief Callback function for the SDIO driver to wait \p ms milliseconds
* @param ms
* @warning This function relies on the systick and must not be used in interrupt context.
*/
void sdio_wait_ms(uint32_t ms)
{
systick_wait_ms(ms);
}
/**
* @brief Handles the TX of UART1 (Shellmatta)
*/
void DMA2_Stream7_IRQHandler(void)
{
uint32_t hisr = DMA2->HISR & (0x3F << 22);
DMA2->HIFCR = hisr;
if (hisr & DMA_HISR_TCIF7)
uart_tx_dma_complete_int_callback(&shell_uart);
}

View File

@ -18,6 +18,11 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup oven-driver
* @{
*/
#include <reflow-controller/oven-driver.h>
#include <reflow-controller/periph-config/oven-driver-hwcfg.h>
#include <stm-periph/rcc-manager.h>
@ -28,13 +33,42 @@
#include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/hw-version-detect.h>
/**
* @brief PID controller instance of the oven driver
*/
static struct pid_controller IN_SECTION(.ccm.bss) oven_pid;
static bool oven_pid_running;
static bool oven_pid_aborted;
/**
* @brief Oven PID is currently running
*/
static bool IN_SECTION(.ccm.bss) oven_pid_running;
/**
* @brief Oven PID has been aborted / abnormally stopped.
*/
static bool IN_SECTION(.ccm.bss) oven_pid_aborted;
/**
* @brief Power level [0..100] of the oven to be applied
*/
static uint8_t IN_SECTION(.ccm.bss) oven_driver_power_level;
/**
* @brief Current target temperature of the oven PID controller in degC
*/
static float IN_SECTION(.ccm.bss) target_temp;
/**
* @brief The millisecond timestamp of the last run of the PID controller
*/
static uint64_t IN_SECTION(.ccm.bss) timestamp_last_run;
/**
* @brief Enable or disable the safety enable line of the oven control relay.
* @param enable
* @note This function is only working for hardware revisions >= v1.3. Below,
* the safety enable is unavailable.
*/
static void ssr_safety_en(bool enable)
{
if (get_pcb_hardware_version() >= HW_REV_V1_3) {
@ -165,3 +199,5 @@ enum oven_pid_status oven_pid_get_status(void)
return ret;
}
/** @} */

View File

@ -24,6 +24,12 @@
#include <reflow-controller/safety/safety-memory.h>
#include <helper-macros/helper-macros.h>
/**
* @brief Handler for hard faults.
*
* This hard fault handler will turn of the oven output and go to panic mode.
* @note Depending on the fault condition some of the things done here could fail.
*/
void HardFault_Handler(void)
{
/* This is a non recoverable fault. Stop the oven */
@ -38,11 +44,26 @@ void HardFault_Handler(void)
}
/* Overwrite default handler. Go to panic mode */
/**
* @brief Default interrupt handler. This will trigger a panic.
* @note This function should never be called during normal operation.
*/
void __int_default_handler(void)
{
panic_mode();
}
/**
* @brief Put the device into panic mode.
*
* This function can be used when a irrecoverable error is encountered.
* The function will:
* - Disable the oven output
* - Set the panic flag in the safety memory
* - Hang and wait for the watchdog to trigger a system reset.
*
* The panic state will be entered after the reset due to the set panic flag in the safety memory
*/
void panic_mode(void)
{
/* This variable is static, because I don't want it to be on the stack */
@ -57,5 +78,6 @@ void panic_mode(void)
}
/* Let the watchdog do the rest */
while (1);
while (1)
;
}

View File

@ -0,0 +1,31 @@
/* Reflow Oven Controller
*
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/safety/flash-crc-struct.h>
#include <helper-macros/helper-macros.h>
const struct flash_crcs IN_SECTION(.flashcrc) crcs_in_flash = {
.start_magic = 0xA8BE53F9UL,
.crc_section_ccm_data = 0UL,
.crc_section_text = 0UL,
.crc_section_data = 0UL,
.crc_section_vectors = 0UL,
.end_magic = 0xFFA582FFUL,
};

View File

@ -0,0 +1,152 @@
/* Reflow Oven Controller
*
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/safety/flash-crc.h>
#include <reflow-controller/safety/flash-crc-struct.h>
#include <reflow-controller/safety/safety-controller.h>
#include <stm-periph/crc-unit.h>
extern const uint32_t __ld_vectors_start;
extern const uint32_t __ld_vectors_end;
extern const uint32_t __ld_text_start;
extern const uint32_t __ld_text_end;
extern const uint32_t __ld_sdata_ccm;
extern const uint32_t __ld_edata_ccm;
extern const uint32_t __ld_load_ccm_data;
extern const uint32_t __ld_sdata;
extern const uint32_t __ld_edata;
extern const uint32_t __ld_load_data;
int flash_crc_trigger_check(void)
{
int ret = -1;
int res;
int any_err = 0;
uint32_t crc;
/* Perform CRC check over vector table */
res = flash_crc_calc_section(FLASH_CRC_VECTOR, &crc);
if (res || crc != flash_crc_get_expected_crc(FLASH_CRC_VECTOR))
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
any_err |= res;
/* Perform CRC check over text section */
res = flash_crc_calc_section(FLASH_CRC_TEXT, &crc);
if (res || crc != flash_crc_get_expected_crc(FLASH_CRC_TEXT))
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
any_err |= res;
/* Perform CRC check over data section */
res = flash_crc_calc_section(FLASH_CRC_DATA, &crc);
if (res || crc != flash_crc_get_expected_crc(FLASH_CRC_DATA))
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
any_err |= res;
/* Perform CRC check over ccm data section */
res = flash_crc_calc_section(FLASH_CRC_CCMDATA, &crc);
if (res || crc != flash_crc_get_expected_crc(FLASH_CRC_CCMDATA))
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
any_err |= res;
if (!any_err)
ret = 0;
return ret;
}
int flash_crc_calc_section(enum flash_crc_section sec, uint32_t *crc_result)
{
uint32_t len;
const uint32_t *startptr;
const uint32_t *endptr;
const uint32_t *load_addr = NULL;
if (!crc_result)
return -1002;
switch (sec) {
case FLASH_CRC_VECTOR:
startptr = &__ld_vectors_start;
endptr = &__ld_vectors_end;
break;
case FLASH_CRC_TEXT:
startptr = &__ld_text_start;
endptr = &__ld_text_end;
break;
case FLASH_CRC_DATA:
startptr = &__ld_sdata;
endptr = &__ld_edata;
load_addr = &__ld_load_data;
break;
case FLASH_CRC_CCMDATA:
startptr = &__ld_sdata_ccm;
endptr = &__ld_edata_ccm;
load_addr = &__ld_load_ccm_data;
break;
default:
return -1001;
}
len = (uint32_t)((void *)endptr - (void *)startptr);
if (!load_addr)
load_addr = startptr;
/* Not a multiple of 32bit words long. Cannot calculate CRC in this case! */
if (len % 4)
return -1;
/* Calculate word count */
len /= 4;
/* Reset CRC and calculate over data range */
crc_unit_reset();
crc_unit_input_array(load_addr, len);
*crc_result = crc_unit_get_crc();
return 0;
}
uint32_t flash_crc_get_expected_crc(enum flash_crc_section sec)
{
uint32_t crc;
switch (sec) {
case FLASH_CRC_VECTOR:
crc = crcs_in_flash.crc_section_vectors;
break;
case FLASH_CRC_TEXT:
crc = crcs_in_flash.crc_section_text;
break;
case FLASH_CRC_DATA:
crc = crcs_in_flash.crc_section_data;
break;
case FLASH_CRC_CCMDATA:
crc = crcs_in_flash.crc_section_ccm_data;
break;
default:
crc = 0xFFFFFFFFul;
break;
}
return crc;
}

View File

@ -31,8 +31,22 @@
#include <reflow-controller/safety/safety-controller.h>
static const uint8_t safety_adc_channels[SAFETY_ADC_NUM_OF_CHANNELS] = {SAFETY_ADC_CHANNELS};
static volatile uint8_t safety_adc_conversion_complete;
static volatile uint8_t safety_adc_triggered;
/**
* @brief Safety ADC conversion complete. Set in interrupt.
*/
static volatile uint8_t IN_SECTION(.ccm.bss) safety_adc_conversion_complete;
/**
* @brief Safety ADC has been started. It will perform all specified conversions and
* set @ref safety_adc_conversion_complete afterwards
*/
static volatile uint8_t IN_SECTION(.ccm.bss) safety_adc_triggered;
/**
* @brief Safety ADC conversion storage. This is filled by DMA.
* @note Do not move this to CCM RAM as the DMA won't be able to access it.
*/
static volatile uint16_t safety_adc_conversions[SAFETY_ADC_NUM_OF_CHANNELS];
void safety_adc_init(void)
@ -46,7 +60,8 @@ void safety_adc_init(void)
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN));
if (hw_rev != HW_REV_V1_2) {
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT_RCC_MASK));
rcc_manager_enable_clock(&RCC->AHB1ENR,
BITMASK_TO_BITNO(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT_RCC_MASK));
SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT->MODER &= MODER_DELETE(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PIN);
SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT->MODER |= ANALOG(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PIN);
}
@ -83,7 +98,8 @@ void safety_adc_init(void)
DMA2_Stream4->PAR = (uint32_t)&SAFETY_ADC_ADC_PERIPHERAL->DR;
DMA2_Stream4->M0AR = (uint32_t)safety_adc_conversions;
DMA2_Stream4->NDTR = SAFETY_ADC_NUM_OF_CHANNELS;
DMA2_Stream4->CR = DMA_SxCR_PL_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_TCIE | DMA_SxCR_EN;
DMA2_Stream4->CR = DMA_SxCR_PL_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_CIRC |
DMA_SxCR_TCIE | DMA_SxCR_EN;
NVIC_EnableIRQ(DMA2_Stream4_IRQn);
/* Enable ADC */
@ -161,7 +177,7 @@ void safety_adc_trigger_meas(void)
safety_adc_triggered = 1;
}
void DMA2_Stream4_IRQHandler()
void DMA2_Stream4_IRQHandler(void)
{
uint32_t hisr;

View File

@ -23,6 +23,7 @@
* @{
*/
#include "stm32/stm32f407xx.h"
#include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/safety/safety-config.h>
#include <reflow-controller/safety/watchdog.h>
@ -43,6 +44,7 @@
#include <stm-periph/rcc-manager.h>
#include <reflow-controller/temp-converter.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/safety/flash-crc.h>
#include <reflow-controller/periph-config/safety-adc-hwcfg.h>
/**
@ -154,15 +156,6 @@ struct overtemp_config {
uint32_t crc;
};
struct flash_crcs {
uint32_t start_magic;
uint32_t crc_section_text;
uint32_t crc_section_data;
uint32_t crc_section_ccm_data;
uint32_t crc_section_vectors;
uint32_t end_magic;
};
struct crc_monitor_register {
const volatile void *reg_addr;
uint32_t mask;
@ -171,9 +164,15 @@ struct crc_monitor_register {
#define CRC_MON_REGISTER_ENTRY(_addr, _mask, _size) {.reg_addr = &(_addr), .mask = (_mask), .size = (_size)}
/**
* @brief Sentinel Element for crc monitor register list
*
*/
#define CRC_MON_REGISTER_SENTINEL {.reg_addr = NULL, .mask = 0, .size = 0}
struct crc_mon {
/**
* @brief Array of registers to monitor. Terminated by NULL sentinel!
* @brief Array of registers to monitor. Terminated by NULL sentinel @ref CRC_MON_REGISTER_SENTINEL
*/
const struct crc_monitor_register *registers;
const enum crc_monitor monitor;
@ -212,6 +211,7 @@ static volatile struct error_flag IN_SECTION(.ccm.data) flags[] = {
ERR_FLAG_ENTRY(ERR_FLAG_FLASH_CRC_DATA),
ERR_FLAG_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC),
ERR_FLAG_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC),
ERR_FLAG_ENTRY(ERR_FLAG_CFG_CRC_MISC),
};
/**
@ -299,7 +299,7 @@ static const struct crc_monitor_register meas_adc_crc_regs[] = {
ADC_SQR2_SQ8 | ADC_SQR2_SQ7, 4),
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SQR3, ADC_SQR3_SQ6 | ADC_SQR3_SQ5 | ADC_SQR3_SQ4 |
ADC_SQR3_SQ3 | ADC_SQR3_SQ2 | ADC_SQR3_SQ1, 4),
{NULL, 0, 0}
CRC_MON_REGISTER_SENTINEL
};
static const struct crc_monitor_register safety_adc_crc_regs[] = {
@ -315,11 +315,27 @@ static const struct crc_monitor_register safety_adc_crc_regs[] = {
ADC_SQR2_SQ8 | ADC_SQR2_SQ7, 4),
CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SQR3, ADC_SQR3_SQ6 | ADC_SQR3_SQ5 | ADC_SQR3_SQ4 |
ADC_SQR3_SQ3 | ADC_SQR3_SQ2 | ADC_SQR3_SQ1, 4),
{NULL, 0, 0}
CRC_MON_REGISTER_ENTRY(RCC->APB2ENR, SAFETY_ADC_ADC_RCC_MASK, 4),
CRC_MON_REGISTER_SENTINEL
};
static struct crc_mon IN_SECTION(.ccm.data) crc_monitors[] =
{
static const struct crc_monitor_register misc_config_crc_regs[] = {
/* Check clock tree settings */
CRC_MON_REGISTER_ENTRY(RCC->CR, RCC_CR_PLLON | RCC_CR_HSEON | RCC_CR_PLLI2SON | RCC_CR_HSION, 4),
CRC_MON_REGISTER_ENTRY(RCC->CSR, RCC_CSR_LSION, 4),
CRC_MON_REGISTER_ENTRY(RCC->CFGR, RCC_CFGR_SWS | RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2, 4),
CRC_MON_REGISTER_ENTRY(RCC->PLLCFGR, RCC_PLLCFGR_PLLM | RCC_PLLCFGR_PLLQ | RCC_PLLCFGR_PLLSRC | RCC_PLLCFGR_PLLP | RCC_PLLCFGR_PLLN | RCC_PLLCFGR_PLLM , 4),
/* Check Flash settings */
CRC_MON_REGISTER_ENTRY(FLASH->ACR, FLASH_ACR_LATENCY | FLASH_ACR_DCEN | FLASH_ACR_ICEN | FLASH_ACR_PRFTEN, 4),
/* Check vector table offset */
CRC_MON_REGISTER_ENTRY(SCB->VTOR, 0xFFFFFFFF, 4),
/* Check system tick configuration */
CRC_MON_REGISTER_ENTRY(SysTick->CTRL, SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk, 4),
CRC_MON_REGISTER_ENTRY(SysTick->LOAD, 0xFFFFFFFF, 4),
CRC_MON_REGISTER_SENTINEL
};
static struct crc_mon IN_SECTION(.ccm.data) crc_monitors[] = {
{
.registers = meas_adc_crc_regs,
.monitor = ERR_CRC_MON_MEAS_ADC,
@ -340,6 +356,16 @@ static struct crc_mon IN_SECTION(.ccm.data) crc_monitors[] =
.last_crc = 0UL,
.active = false,
},
{
.registers = misc_config_crc_regs,
.monitor = ERR_CRC_MON_MISC,
.pw = SAFETY_CRC_MON_MISC_PW,
.flag_to_set = ERR_FLAG_CFG_CRC_MISC,
.expected_crc = 0UL,
.expected_crc_inv = ~0UL,
.last_crc = 0UL,
.active = false,
}
};
/**
@ -360,16 +386,22 @@ static void set_overtemp_config(float over_temperature)
safety_controller_overtemp_config.crc_dummy_seed = 0xA4F5C7E6UL;
safety_controller_overtemp_config.overtemp_deg_celsius = over_temperature;
safety_controller_overtemp_config.overtemp_equiv_resistance = resistance;
crc_unit_input_array((const uint32_t *)&safety_controller_overtemp_config, wordsize_of(struct overtemp_config) - 1);
crc_unit_input_array((const uint32_t *)&safety_controller_overtemp_config,
wordsize_of(struct overtemp_config) - 1);
safety_controller_overtemp_config.crc = crc_unit_get_crc();
}
/**
* @brief Check the overtemperature config structure's CRC
* @return true if check failed. false if CRC check successful
*/
static bool over_temperature_config_check(void)
{
if (safety_controller_overtemp_config.crc_dummy_seed != 0xA4F5C7E6UL)
return true;
crc_unit_reset();
crc_unit_input_array((const uint32_t *)&safety_controller_overtemp_config, wordsize_of(struct overtemp_config) - 1);
crc_unit_input_array((const uint32_t *)&safety_controller_overtemp_config,
wordsize_of(struct overtemp_config) - 1);
if (crc_unit_get_crc() != safety_controller_overtemp_config.crc)
return true;
@ -519,9 +551,8 @@ static int safety_controller_check_crc_monitors(void)
if (crc_monitor_calculate_crc(mon->registers, &crc))
return -1;
if (mon->expected_crc != crc || ~mon->expected_crc_inv != crc) {
if (mon->expected_crc != crc || ~mon->expected_crc_inv != crc)
safety_controller_report_error(mon->flag_to_set);
}
mon->last_crc = crc;
}
@ -608,16 +639,14 @@ static void apply_config_overrides(void)
case SAFETY_MEMORY_CONFIG_OVERRIDE_WEIGHT:
flag_enum = flag_no_to_flag_enum(override.entry.weight_override.flag);
flag = find_error_flag(flag_enum);
if (flag && flag->weight) {
if (flag && flag->weight)
flag->weight->weight = override.entry.weight_override.weight;
}
break;
case SAFETY_MEMORY_CONFIG_OVERRIDE_PERSISTENCE:
flag_enum = flag_no_to_flag_enum(override.entry.persistence_override.flag);
flag = find_error_flag(flag_enum);
if (flag && flag->persistence) {
if (flag && flag->persistence)
flag->persistence->persistence = override.entry.persistence_override.persistence;
}
break;
default:
continue;
@ -647,12 +676,11 @@ static bool error_flag_get_status(const volatile struct error_flag *flag)
if (!flag)
return true;
if (flag->error_state == flag->error_state_inv) {
if (flag->error_state == flag->error_state_inv)
return true;
} else {
else
return flag->error_state;
}
}
/**
* @brief Find a analog value monitor structure by its enum number
@ -693,7 +721,7 @@ static volatile struct timing_mon *find_timing_mon(enum timing_monitor mon)
/**
* @brief Check the active timing monitors and set the appropriate flags in case of an error.
*/
static void safety_controller_process_active_timing_mons()
static void safety_controller_process_active_timing_mons(void)
{
uint32_t i;
volatile struct timing_mon *current_mon;
@ -716,7 +744,8 @@ static void safety_controller_process_active_timing_mons()
* Process the analog and timing monitors and set the relevant flags in case of a monitor outside its limits.
* Furthermore, the PT1000 resistance is checked for overtemperature
*
* The checking of the analog monitors will only be armed after a startup delay of 1000 ms to allow the values to stabilize.
* The checking of the analog monitors will only be armed after a startup delay of 1000 ms to
* allow the values to stabilize.
*/
static void safety_controller_process_monitor_checks(void)
{
@ -731,21 +760,18 @@ static void safety_controller_process_monitor_checks(void)
if (startup_completed) {
analog_mon_count = safety_controller_get_analog_monitor_count();
for (idx = 0; idx < analog_mon_count; idx++) {
for (idx = 0; idx < analog_mon_count; idx++)
if (safety_controller_get_analog_mon_by_index(idx, &amon_info)) {
panic_mode();
}
if (amon_info.status != ANALOG_MONITOR_OK) {
if (amon_info.status != ANALOG_MONITOR_OK)
safety_controller_report_error(amon_info.associated_flag);
}
}
}
adc_pt1000_get_current_resistance(&pt1000_val);
if (pt1000_val > safety_controller_overtemp_config.overtemp_equiv_resistance) {
if (pt1000_val > safety_controller_overtemp_config.overtemp_equiv_resistance)
safety_controller_report_error(ERR_FLAG_OVERTEMP);
}
(void)safety_controller_check_crc_monitors();
@ -820,10 +846,9 @@ void safety_controller_report_timing(enum timing_monitor monitor)
tim = find_timing_mon(monitor);
if (tim) {
if (tim->enabled) {
if (!systick_ticks_have_passed(tim->last, tim->min_delta) && tim->min_delta > 0U) {
if (!systick_ticks_have_passed(tim->last, tim->min_delta) && tim->min_delta > 0U)
safety_controller_report_error(tim->associated_flag);
}
}
tim->calculated_delta = timestamp - tim->last;
tim->last = timestamp;
@ -870,10 +895,9 @@ static int get_safety_flags_from_error_mem(enum safety_flag *flags)
for (idx = 0; idx < count; idx++) {
res = safety_memory_get_error_entry(idx, &entry);
if (entry.type == SAFETY_MEMORY_ERR_ENTRY_FLAG) {
if (entry.type == SAFETY_MEMORY_ERR_ENTRY_FLAG)
return_flags |= flag_no_to_flag_enum(entry.flag_num);
}
}
*flags = return_flags;
return 0;
@ -884,11 +908,12 @@ static int get_safety_flags_from_error_mem(enum safety_flag *flags)
*
* The external harware watchdog has to be periodically reset or it will reset hte controller.
* Because debugging is not possible, when the watchdog is active, it is only activated, if the application is
* compiled in release mode. Any interruption of the main programm will then trigger the internal and/or the external watchdog.
* compiled in release mode. Any interruption of the main programm will then trigger the internal and/or
* the external watchdog.
*
* @note When enabled, execute the @ref external_watchdog_toggle function to reset the external watchdog.
*/
static void safety_controller_init_external_watchdog()
static void safety_controller_init_external_watchdog(void)
{
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(SAFETY_EXT_WATCHDOG_RCC_MASK));
SAFETY_EXT_WATCHDOG_PORT->MODER &= MODER_DELETE(SAFETY_EXT_WATCHDOG_PIN);
@ -899,7 +924,7 @@ static void safety_controller_init_external_watchdog()
__DSB();
}
void safety_controller_init()
void safety_controller_init(void)
{
enum safety_memory_state found_memory_state;
enum safety_flag flags_in_err_mem = ERR_FLAG_NO_FLAG;
@ -916,7 +941,7 @@ void safety_controller_init()
/* This is usually done by the safety memory already. But, since this module also uses the CRC... */
crc_unit_init();
safety_controller_trigger_flash_crc_check();
flash_crc_trigger_check();
stack_check_init_corruption_detect_area();
hw_rev = get_pcb_hardware_version();
@ -948,7 +973,7 @@ void safety_controller_init()
MEAS_ADC_SAFETY_FLAG_KEY);
safety_adc_init();
watchdog_setup(WATCHDOG_PRESCALER);
(void)watchdog_setup(WATCHDOG_PRESCALER, WATCHDOG_RELOAD_VALUE);
if (rcc_manager_get_reset_cause(false) & RCC_RESET_SOURCE_IWDG)
safety_controller_report_error(ERR_FLAG_WTCHDG_FIRED);
@ -967,7 +992,7 @@ void safety_controller_init()
* 1) Checking the remaining free space at the moment between stack pointer and top of heap.
* 2) Checking The CRC of the corruption detect area between heap and stack
*/
static void safety_controller_check_stack()
static void safety_controller_check_stack(void)
{
int32_t free_stack;
@ -975,18 +1000,17 @@ static void safety_controller_check_stack()
if (free_stack < SAFETY_MIN_STACK_FREE)
safety_controller_report_error(ERR_FLAG_STACK);
if (stack_check_corruption_detect_area()) {
if (stack_check_corruption_detect_area())
safety_controller_report_error(ERR_FLAG_STACK);
}
}
/**
* @brief Handle the Safety ADC
*
* This function handles the safety ADC.
* If the safety ADC ius not executing a measurment and the time since the last measurement has
* passed @ref SAFETY_CONTROLLER_ADC_DELAY_MS, the safety ADC is retriggered and will automatically perform a measurement
* on all of its channels.
* If the safety ADC is not executing a measurment and the time since the last measurement has
* passed @ref SAFETY_CONTROLLER_ADC_DELAY_MS, the safety ADC is retriggered and will automatically perform a
* measurement on all of its channels.
* When called again, this function will retrieve the data from the safety ADC and converts it into the
* appropriate analog values for the analog value monitors.
*
@ -995,7 +1019,7 @@ static void safety_controller_check_stack()
*
* The channels, the ssafety ADC will convert is defined in its header file using the define @ref SAFETY_ADC_CHANNELS.
*/
static void safety_controller_handle_safety_adc()
static void safety_controller_handle_safety_adc(void)
{
static uint64_t last_result_timestamp = 0;
const uint16_t *channels;
@ -1066,10 +1090,9 @@ static void safety_controller_handle_memory_checks(void)
/* Check the safety memory */
if (safety_memory_check()) {
(void)safety_memory_reinit(&found_state);
if (found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (found_state != SAFETY_MEMORY_INIT_VALID_MEMORY)
safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT);
}
}
/* If flag weight table is broken, reinit to default and set flag */
if (flag_weight_table_crc_check()) {
@ -1097,7 +1120,7 @@ static void safety_controller_handle_memory_checks(void)
* If the systick stays constant for more than 1000 calls of this function,
* the @ref ERR_FLAG_SYSTICK flag is set.
*/
static void safety_controller_do_systick_checking()
static void safety_controller_do_systick_checking(void)
{
static uint64_t last_systick;
static uint32_t same_systick_cnt = 0UL;
@ -1121,22 +1144,29 @@ static void safety_controller_do_systick_checking()
* is set, the appropriate action defined by the flag weight is executed.
* @note If no flag weigth is present for a given error flag, it is treated as the most critical category
* (@ref SAFETY_FLAG_CONFIG_WEIGHT_PANIC)
*
* @returns Worst config weight set
*/
static void safety_controller_handle_weighted_flags()
static enum config_weight safety_controller_handle_weighted_flags(void)
{
uint32_t flag_index;
volatile struct error_flag *current_flag;
enum config_weight flag_weigth;
enum config_weight worst = SAFETY_FLAG_CONFIG_WEIGHT_NONE;
for (flag_index = 0u; flag_index < COUNT_OF(flags); flag_index++) {
current_flag = &flags[flag_index];
/* Continue if this flag is not set */
if (!error_flag_get_status(current_flag)) {
if (!error_flag_get_status(current_flag))
continue;
}
flag_weigth = get_flag_weight(current_flag);
/* Override the worst flag weigt set, if it is worse than the previous ones */
if (flag_weigth > worst)
worst = flag_weigth;
switch (flag_weigth) {
case SAFETY_FLAG_CONFIG_WEIGHT_NONE:
break;
@ -1152,18 +1182,20 @@ static void safety_controller_handle_weighted_flags()
}
}
return worst;
}
#ifndef DEBUGBUILD
static void external_watchdog_toggle()
static void external_watchdog_toggle(void)
{
SAFETY_EXT_WATCHDOG_PORT->ODR ^= (1<<SAFETY_EXT_WATCHDOG_PIN);
}
#endif
int safety_controller_handle()
enum config_weight safety_controller_handle(void)
{
int ret = 0;
enum config_weight worst_weight_set;
#ifndef DEBUGBUILD
static uint32_t watchdog_counter = 0UL;
#endif
@ -1173,9 +1205,10 @@ int safety_controller_handle()
safety_controller_handle_memory_checks();
safety_controller_do_systick_checking();
safety_controller_process_monitor_checks();
safety_controller_handle_weighted_flags();
worst_weight_set = safety_controller_handle_weighted_flags();
ret |= watchdog_ack(WATCHDOG_MAGIC_KEY);
/* Ignore error here. Will trigger restart anyway */
(void)watchdog_ack(WATCHDOG_MAGIC_KEY);
#ifndef DEBUGBUILD
if (get_pcb_hardware_version() != HW_REV_V1_2) {
@ -1186,7 +1219,8 @@ int safety_controller_handle()
}
}
#endif
return (ret ? -1 : 0);
return worst_weight_set;
}
int safety_controller_enable_timing_mon(enum timing_monitor monitor, bool enable)
@ -1273,9 +1307,8 @@ int safety_controller_ack_flag_with_key(enum safety_flag flag, uint32_t key)
int ret = -1;
volatile struct error_flag *found_flag;
if (!is_power_of_two(flag)) {
if (!is_power_of_two(flag))
return -1001;
}
found_flag = find_error_flag(flag);
if (found_flag) {
@ -1306,17 +1339,17 @@ bool safety_controller_get_flags_by_mask(enum safety_flag mask)
return ret;
}
uint32_t safety_controller_get_flag_count()
uint32_t safety_controller_get_flag_count(void)
{
return COUNT_OF(flags);
}
uint32_t safety_controller_get_analog_monitor_count()
uint32_t safety_controller_get_analog_monitor_count(void)
{
return COUNT_OF(analog_mons);
}
uint32_t safety_controller_get_timing_monitor_count()
uint32_t safety_controller_get_timing_monitor_count(void)
{
return COUNT_OF(timings);
}
@ -1423,9 +1456,8 @@ int safety_controller_get_timing_mon_by_index(uint32_t index, struct timing_moni
if (!info)
return -1002;
if (index >= COUNT_OF(timings)) {
if (index >= COUNT_OF(timings))
return -1001;
}
mon = &timings[index];
@ -1449,99 +1481,6 @@ float safety_controller_get_overtemp_limit(void)
return safety_controller_overtemp_config.overtemp_deg_celsius;
}
extern const uint32_t __ld_vectors_start;
extern const uint32_t __ld_vectors_end;
extern const uint32_t __ld_text_start;
extern const uint32_t __ld_text_end;
extern const uint32_t __ld_sdata_ccm;
extern const uint32_t __ld_edata_ccm;
extern const uint32_t __ld_load_ccm_data;
extern const uint32_t __ld_sdata;
extern const uint32_t __ld_edata;
extern const uint32_t __ld_load_data;
int safety_controller_trigger_flash_crc_check()
{
/* This structs needs to be volatile!!
* This prevents the compiler form optimizing out the reads to the crcs which will be patched in later by
* a separate python script!
*/
static volatile const struct flash_crcs IN_SECTION(.flashcrc) crcs_in_flash =
{
.start_magic = 0xA8BE53F9UL,
.crc_section_ccm_data = 0UL,
.crc_section_text = 0UL,
.crc_section_data = 0UL,
.crc_section_vectors = 0UL,
.end_magic = 0xFFA582FFUL,
};
int ret = -1;
uint32_t len;
uint32_t crc;
/* Perform CRC check over vector table */
len = (uint32_t)((void *)&__ld_vectors_end - (void *)&__ld_vectors_start);
if (len % 4) {
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
} else {
len /= 4;
crc_unit_reset();
crc_unit_input_array(&__ld_vectors_start, len);
crc = crc_unit_get_crc();
if (crc != crcs_in_flash.crc_section_vectors) {
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
}
}
/* Perform CRC check over text section */
len = (uint32_t)((void *)&__ld_text_end - (void *)&__ld_text_start);
if (len % 4) {
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
} else {
len /= 4;
crc_unit_reset();
crc_unit_input_array(&__ld_text_start, len);
crc = crc_unit_get_crc();
if (crc != crcs_in_flash.crc_section_text) {
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
}
}
/* Perform CRC check over data section */
len = (uint32_t)((void *)&__ld_edata - (void *)&__ld_sdata);
if (len % 4) {
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
} else {
len /= 4;
crc_unit_reset();
crc_unit_input_array(&__ld_load_data, len);
crc = crc_unit_get_crc();
if (crc != crcs_in_flash.crc_section_data) {
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
}
}
/* Perform CRC check over ccm data section */
len = (uint32_t)((void *)&__ld_edata_ccm - (void *)&__ld_sdata_ccm);
if (len % 4) {
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
} else {
len /= 4;
crc_unit_reset();
crc_unit_input_array(&__ld_load_ccm_data, len);
crc = crc_unit_get_crc();
if (crc != crcs_in_flash.crc_section_ccm_data) {
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
}
}
ret = 0;
return ret;
}
int safety_controller_set_crc_monitor(enum crc_monitor mon, uint32_t password)
{
uint32_t i;

View File

@ -99,7 +99,8 @@ static enum safety_memory_state safety_memory_get_header(struct safety_memory_he
res = 0;
if (header->boot_status_offset < wordsize_of(struct safety_memory_header))
res++;
if (header->config_overrides_offset < header->boot_status_offset + wordsize_of(struct safety_memory_boot_status))
if (header->config_overrides_offset < header->boot_status_offset +
wordsize_of(struct safety_memory_boot_status))
res++;
if (header->config_overrides_len > SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT)
res++;
@ -107,7 +108,8 @@ static enum safety_memory_state safety_memory_get_header(struct safety_memory_he
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)
if (header->err_memory_end >= backup_ram_get_size_in_words() ||
header->err_memory_end < header->err_memory_offset)
res++;
if (res) {
@ -148,7 +150,7 @@ static void safety_memory_write_new_header(void)
safety_memory_write_and_patch_header(&header);
}
static int safety_memory_check_crc()
static int safety_memory_check_crc(void)
{
struct safety_memory_header header;
enum safety_memory_state state = safety_memory_get_header(&header);
@ -181,7 +183,7 @@ static int safety_memory_check_crc()
return 0;
}
static int safety_memory_gen_crc()
static int safety_memory_gen_crc(void)
{
struct safety_memory_header header;
uint32_t word_addr;
@ -268,9 +270,8 @@ int safety_memory_get_boot_status(struct safety_memory_boot_status *status)
if (!status)
return -1001;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
if (safety_memory_check_crc())
return -2001;
@ -289,9 +290,8 @@ int safety_memory_set_boot_status(const struct safety_memory_boot_status *status
if (!status)
return -1001;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
if (safety_memory_check_crc())
return -2001;
@ -304,7 +304,7 @@ int safety_memory_set_boot_status(const struct safety_memory_boot_status *status
return 0;
}
static int safety_memory_check_error_entries()
static int safety_memory_check_error_entries(void)
{
struct safety_memory_header header;
uint32_t addr;
@ -312,9 +312,8 @@ static int safety_memory_check_error_entries()
int ret = 0;
int res;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
for (addr = header.err_memory_offset; addr < header.err_memory_end; addr++) {
res = backup_ram_get_data(addr, &data, 1UL);
@ -340,9 +339,8 @@ int safety_memory_get_error_entry_count(uint32_t *count)
if (!count)
return -1001;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
*count = header.err_memory_end - header.err_memory_offset;
@ -354,9 +352,8 @@ int safety_memory_check(void)
int res;
res = safety_memory_check_crc();
if (!res) {
if (!res)
res |= safety_memory_check_error_entries();
}
return -!!res;
}
@ -372,9 +369,8 @@ int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry
if (!entry)
return -1001;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
err_mem_count = header.err_memory_end - header.err_memory_offset;
if (idx < err_mem_count && err_mem_count > 0) {
@ -410,9 +406,8 @@ int safety_memory_insert_error_entry(struct error_memory_entry *entry)
input_data = error_memory_entry_to_word(entry);
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
if (entry->type == SAFETY_MEMORY_ERR_ENTRY_NOP) {
/* Append to end */
@ -510,9 +505,8 @@ int safety_memory_insert_config_override(struct config_override *config_override
int res;
int ret = -3;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
if (header.config_overrides_len == 0)
return -1;
@ -550,9 +544,8 @@ int safety_memory_get_config_override_count(uint32_t *count)
*count = 0UL;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
if (header.config_overrides_len == 0)
return 0;
@ -582,18 +575,15 @@ int safety_memory_get_config_override(uint32_t idx, struct config_override *conf
if (!config_override)
return -1002;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -2000;
}
if (idx >= header.config_overrides_len) {
if (idx >= header.config_overrides_len)
return -1001;
}
res = backup_ram_get_data(header.config_overrides_offset + idx, &data, 1UL);
if (res) {
if (res)
return -1;
}
switch (data & 0xFF) {
case 0xA2:
@ -649,8 +639,8 @@ 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;
volatile char *ptr;
/* If filename and outlen are both NULL, we don't do anything */
if (!filename && !outlen)
@ -696,9 +686,9 @@ int safety_memory_set_update_filename(const char *filename)
ram_ptr = backup_ram_get_base_ptr();
ram_ptr += header.firmware_update_filename * 4;
for (i = 0u; i < len; i++) {
for (i = 0u; i < len; i++)
ram_ptr[i] = filename[i];
}
ram_ptr[i] = 0;
ret = safety_memory_gen_crc();

View File

@ -26,7 +26,7 @@
extern char __ld_top_of_stack;
extern char __ld_end_stack;
int32_t stack_check_get_usage()
int32_t stack_check_get_usage(void)
{
uint32_t stack_top;
uint32_t stack_ptr;
@ -37,7 +37,7 @@ int32_t stack_check_get_usage()
return stack_top - stack_ptr;
}
int32_t stack_check_get_free()
int32_t stack_check_get_free(void)
{
uint32_t upper_heap_boundary;
uint32_t stack_ptr;
@ -102,9 +102,6 @@ int stack_check_corruption_detect_area(void)
&__ld_start_stack_corruption_detect_area;
crc_unit_reset();
crc_unit_input_array(&__ld_start_stack_corruption_detect_area, area_size_in_words);
if (crc_unit_get_crc() == 0UL) {
return 0;
} else {
return -1;
}
return crc_unit_get_crc() == 0UL ? 0 : -1;
}

View File

@ -42,45 +42,63 @@
*/
#define STM32_WATCHDOG_REGISTER_ACCESS_KEY 0x5555
int watchdog_setup(uint8_t prescaler)
int watchdog_setup(uint16_t prescaler, uint16_t reload_value)
{
uint32_t prescaler_reg_val;
int ret = 0;
/** - Activate the LSI oscillator */
RCC->CSR |= RCC_CSR_LSION;
__DSB();
/** - Wait for the oscillator to be ready */
while (!(RCC->CSR & RCC_CSR_LSIRDY));
while (!(RCC->CSR & RCC_CSR_LSIRDY))
;
if (prescaler == 4U)
if (prescaler == 4U) {
prescaler_reg_val = 0UL;
else if (prescaler == 8U)
} else if (prescaler == 8U) {
prescaler_reg_val = 1UL;
else if (prescaler == 16U)
} else if (prescaler == 16U) {
prescaler_reg_val = 2UL;
else if (prescaler == 32U)
} else if (prescaler == 32U) {
prescaler_reg_val = 3UL;
else if (prescaler == 64U)
} else if (prescaler == 64U) {
prescaler_reg_val = 4UL;
else if (prescaler == 128U)
} else if (prescaler == 128U) {
prescaler_reg_val = 5UL;
else
} else if (prescaler == 256U) {
prescaler_reg_val = 6UL;
} else {
prescaler_reg_val = 6UL;
ret = -1;
}
/** - (De)activate the watchdog during debug access according to @ref WATCHDOG_HALT_DEBUG */
if (WATCHDOG_HALT_DEBUG)
DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_IWDG_STOP;
else
DBGMCU->APB1FZ &= ~DBGMCU_APB1_FZ_DBG_IWDG_STOP;
/** - Unlock registers */
IWDG->KR = STM32_WATCHDOG_REGISTER_ACCESS_KEY;
/** - Wait until prescaler can be written */
while (IWDG->SR & IWDG_SR_PVU);
while (IWDG->SR & IWDG_SR_PVU)
;
/** - Write prescaler value */
IWDG->PR = prescaler_reg_val;
/* - Wait until reload value can be written */
while (IWDG->SR & IWDG_SR_RVU);
while (IWDG->SR & IWDG_SR_RVU)
;
/** - Set reload value fixed to 0xFFF */
IWDG->RLR = 0xFFFU;
/** - Set reload value */
if (reload_value > 0xFFFu) {
reload_value = 0xFFFFu;
ret = -2;
}
IWDG->RLR = reload_value;
/** - Write enable key */
IWDG->KR = STM32_WATCHDOG_ENABLE_KEY;
@ -88,7 +106,7 @@ int watchdog_setup(uint8_t prescaler)
/** - Do a first reset of the counter. This also locks the config regs */
watchdog_ack(WATCHDOG_MAGIC_KEY);
return 0;
return ret;
}
int watchdog_ack(uint32_t magic)

65
stm-firmware/sd.c Normal file
View File

@ -0,0 +1,65 @@
/* Reflow Oven Controller
*
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* GDSII-Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/sd.h>
#include "fatfs/ff.h"
#include "fatfs/shimatta_sdio_driver/shimatta_sdio.h"
#include <stdint.h>
#include <string.h>
#include <helper-macros/helper-macros.h>
#include <reflow-controller/digio.h>
static bool sd_card_mounted_state = false;
bool mount_sd_card_if_avail(FATFS *fs)
{
FRESULT res;
static uint8_t IN_SECTION(.ccm.bss) inserted_counter = 0;
if (sdio_check_inserted() && sd_card_mounted_state) {
memset(fs, 0, sizeof(FATFS));
sdio_stop_clk();
inserted_counter = 0;
sd_card_mounted_state = false;
goto ret;
}
if (!sdio_check_inserted() && inserted_counter < 255)
inserted_counter++;
if (!sdio_check_inserted() && !sd_card_mounted_state && inserted_counter > 4) {
inserted_counter = 0;
res = f_mount(fs, "0:/", 1);
if (res == FR_OK) {
led_set(1, 1);
sd_card_mounted_state = true;
} else {
sd_card_mounted_state = false;
}
}
ret:
return sd_card_mounted_state;
}
bool sd_card_is_mounted(void)
{
return sd_card_mounted_state;
}

View File

@ -37,6 +37,7 @@ struct eeprom_calibration_settings {
};
#define EEPROM_OVER_TEMP_CONFIG_BASE_ADDR (EEPROM_CALIBRATION_BASE_ADDR + sizeof(struct eeprom_calibration_settings))
struct eeprom_over_temp_config {
float over_temperature;
uint32_t over_temp_crc;
@ -54,15 +55,15 @@ static bool check_eeprom_header(void)
return true;
}
static void settings_eeprom_zero()
static void settings_eeprom_zero(void)
{
settings_eeprom_save_calibration(0.0f, 0.0f, false);
settings_eeprom_save_overtemp_limit(0.0, false);
settings_eeprom_save_overtemp_limit(0.0f, false);
}
bool settings_eeprom_detect_and_prepare(void)
{
bool eeprom_ready = false;;
bool eeprom_ready = false;
int res;
@ -77,7 +78,10 @@ bool settings_eeprom_detect_and_prepare(void)
if (check_eeprom_header() == false) {
/* Try to write a new header and check it again */
spi_eeprom_write(0, expected_header, sizeof(expected_header));
while (spi_eeprom_write_in_progress());
while (spi_eeprom_write_in_progress())
;
if (check_eeprom_header() == false) {
goto ret_deinit_crc;
} else {

View File

@ -38,7 +38,7 @@ static void get_controller_folder_path(char *path, size_t size)
if (!path)
return;
unique_id_get(&high, &mid, &low);
stm_unique_id_get(&high, &mid, &low);
snprintf(path, size, "/%08X-%08X-%08X",
(unsigned int)high, (unsigned int)mid, (unsigned int)low);
@ -72,18 +72,12 @@ static int create_controller_folder(void)
ret = 0;
} else {
filesystem_result = f_mkdir(foldername);
if (filesystem_result == FR_OK) {
ret = 1;
} else {
ret = -1;
}
ret = filesystem_result == FR_OK ? 1 : -1;
}
return ret;
}
int sd_card_settings_save_calibration(float sens_deviation, float offset, bool active)
{
char path[200];

View File

@ -52,7 +52,7 @@ static void eeprom_cs_deactivate(void)
SPI_EEPROM_SPI_PORT->ODR |= (1<<SPI_EEPROM_CS_PIN);
}
int spi_eeprom_init()
int spi_eeprom_init(void)
{
static struct stm_spi_dev spi_dev;
struct stm_spi_settings settings;
@ -61,7 +61,8 @@ int spi_eeprom_init()
SPI_EEPROM_SPI_PORT->MODER &= MODER_DELETE(SPI_EEPROM_CS_PIN) & MODER_DELETE(SPI_EEPROM_MISO_PIN) &
MODER_DELETE(SPI_EEPROM_MOSI_PIN) & MODER_DELETE(SPI_EEPROM_SCK_PIN);
SPI_EEPROM_SPI_PORT->MODER |= ALTFUNC(SPI_EEPROM_MISO_PIN) | ALTFUNC(SPI_EEPROM_SCK_PIN) | ALTFUNC(SPI_EEPROM_MOSI_PIN);
SPI_EEPROM_SPI_PORT->MODER |= ALTFUNC(SPI_EEPROM_MISO_PIN) | ALTFUNC(SPI_EEPROM_SCK_PIN) |
ALTFUNC(SPI_EEPROM_MOSI_PIN);
SPI_EEPROM_SPI_PORT->MODER |= OUTPUT(SPI_EEPROM_CS_PIN);
SETAF(SPI_EEPROM_SPI_PORT, SPI_EEPROM_MISO_PIN, SPI_EEPROM_SPI_ALTFUNC_NO);
@ -85,7 +86,7 @@ int spi_eeprom_init()
return -1;
}
void spi_eeprom_deinit()
void spi_eeprom_deinit(void)
{
spi_deinit(eeprom_spi_handle);
@ -166,7 +167,8 @@ static void spi_eeprom_do_write_page(uint32_t addr, const uint8_t *data, uint8_t
uint8_t cmd[2];
/* Wait for the previous write to finish */
while (spi_eeprom_write_in_progress());
while (spi_eeprom_write_in_progress())
;
/* Set the write enable latch */
spi_eeprom_set_write_enable_latch(true);

View File

@ -46,7 +46,8 @@ void backup_ram_init(bool use_backup_regulator)
PWR->CSR |= PWR_CSR_BRE;
/* Wait until regulator is ready */
while (!(PWR->CSR & PWR_CSR_BRR));
while (!(PWR->CSR & PWR_CSR_BRR))
;
}
/* Enable clock for backup ram interface */

View File

@ -37,11 +37,10 @@ static size_t calculate_ring_buffer_fill_level(size_t buffer_size, size_t get_id
{
size_t fill_level;
if (put_idx >= get_idx) {
if (put_idx >= get_idx)
fill_level = (put_idx - get_idx);
} else {
else
fill_level = buffer_size - get_idx + put_idx;
}
return fill_level;
}
@ -49,7 +48,7 @@ static size_t calculate_ring_buffer_fill_level(size_t buffer_size, size_t get_id
static int dma_ring_buffer_switch_clock_enable(uint8_t base_dma, bool clk_en)
{
int ret_val;
int (*clk_func)(volatile uint32_t *, uint8_t);
int (*clk_func)(volatile uint32_t *reg, uint8_t bit_no);
if (clk_en)
clk_func = rcc_manager_enable_clock;
@ -72,8 +71,9 @@ static int dma_ring_buffer_switch_clock_enable(uint8_t base_dma, bool clk_en)
}
int dma_ring_buffer_periph_to_mem_initialize(struct dma_ring_buffer_to_mem *dma_buffer, uint8_t base_dma_id,
DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count, size_t element_size,
volatile void *data_buffer, void* src_reg, uint8_t dma_trigger_channel)
DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count,
size_t element_size, volatile void *data_buffer,
void *src_reg, uint8_t dma_trigger_channel)
{
int ret_val = 0;
@ -106,7 +106,8 @@ int dma_ring_buffer_periph_to_mem_initialize(struct dma_ring_buffer_to_mem *dma_
return 0;
}
int dma_ring_buffer_periph_to_mem_get_data(struct dma_ring_buffer_to_mem *buff, const volatile void **data_buff, size_t *len)
int dma_ring_buffer_periph_to_mem_get_data(struct dma_ring_buffer_to_mem *buff, const volatile void **data_buff,
size_t *len)
{
int ret_code = 0;
uint32_t ndtr;
@ -167,7 +168,10 @@ int dma_ring_buffer_periph_to_mem_fill_level(struct dma_ring_buffer_to_mem *buff
return 0;
}
int dma_ring_buffer_mem_to_periph_initialize(struct dma_ring_buffer_to_periph *dma_buffer, uint8_t base_dma_id, DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count, size_t element_size, volatile void *data_buffer, uint8_t dma_trigger_channel, void *dest_reg)
int dma_ring_buffer_mem_to_periph_initialize(struct dma_ring_buffer_to_periph *dma_buffer, uint8_t base_dma_id,
DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count,
size_t element_size, volatile void *data_buffer,
uint8_t dma_trigger_channel, void *dest_reg)
{
if (!dma_buffer || !dma_stream || !data_buffer || !dest_reg)
return -1000;
@ -221,7 +225,8 @@ static void queue_or_start_dma_transfer(struct dma_ring_buffer_to_periph *buff)
buff->dma->CR |= DMA_SxCR_EN;
}
int dma_ring_buffer_mem_to_periph_insert_data(struct dma_ring_buffer_to_periph *buff, const void *data_to_insert, size_t count)
int dma_ring_buffer_mem_to_periph_insert_data(struct dma_ring_buffer_to_periph *buff, const void *data_to_insert,
size_t count)
{
int ret = 0;
size_t free_item_count;
@ -234,7 +239,8 @@ int dma_ring_buffer_mem_to_periph_insert_data(struct dma_ring_buffer_to_periph *
return -1000;
/* Check if data fits into buffer minus one element. If not: try full-1 buffer and rest
* Buffer is not allowed to be completely full, because I cannot ddifferentiate a full buffer from a completely empty one
* Buffer is not allowed to be completely full, because I cannot ddifferentiate a full buffer from a
* completely empty one
*/
if (count >= buff->buffer_count) {
ret = dma_ring_buffer_mem_to_periph_insert_data(buff, data_to_insert, buff->buffer_count - 1);
@ -247,7 +253,9 @@ int dma_ring_buffer_mem_to_periph_insert_data(struct dma_ring_buffer_to_periph *
/* Wait for buffer to be able to handle input */
do {
free_item_count = buff->buffer_count - calculate_ring_buffer_fill_level(buff->buffer_count, buff->dma_get_idx_current, buff->sw_put_idx);
free_item_count = buff->buffer_count -
calculate_ring_buffer_fill_level(buff->buffer_count, buff->dma_get_idx_current,
buff->sw_put_idx);
} while (free_item_count < count+1);
/* Fillup buffer (max is buffer end, wrap around afterwards) */

View File

@ -0,0 +1,92 @@
/* Reflow Oven Controller
*
* Copyright (C) 2021 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stm-periph/option-bytes.h>
#include <stm32/stm32f4xx.h>
/**
* @brief First key for unlocking hte option byte write access
*/
#define FLASH_OPTION_KEY1 (0x08192A3BUL)
/**
* @brief Second key for unlocking hte option byte write access
*/
#define FLASH_OPTION_KEY2 (0x4C5D6E7FUL)
void stm_option_bytes_read(struct option_bytes *opts)
{
uint32_t opt_reg;
if (!opts)
return;
opt_reg = FLASH->OPTCR;
opts->brown_out_level = (opt_reg & FLASH_OPTCR_BOR_LEV) >> 2;
opts->nrst_standby = (opt_reg & FLASH_OPTCR_nRST_STDBY) >> 7;
opts->nrst_stop = (opt_reg & FLASH_OPTCR_nRST_STOP) >> 6;
opts->nwrpi = (opt_reg & FLASH_OPTCR_nWRP) >> 16;
opts->read_protection = (opt_reg & FLASH_OPTCR_RDP) >> 8;
opts->wdg_sw = (opt_reg & FLASH_OPTCR_WDG_SW) >> 5;
}
int stm_option_bytes_program(const struct option_bytes *opts)
{
uint32_t reg;
FLASH->OPTKEYR = FLASH_OPTION_KEY1;
FLASH->OPTKEYR = FLASH_OPTION_KEY2;
__DSB();
if (FLASH->OPTCR & FLASH_OPTCR_OPTLOCK) {
/* Unlocking failed */
return -1;
}
reg = FLASH->OPTCR;
reg &= ~FLASH_OPTCR_BOR_LEV;
reg &= ~FLASH_OPTCR_nRST_STDBY;
reg &= ~FLASH_OPTCR_nRST_STOP;
reg &= ~FLASH_OPTCR_nWRP;
reg &= ~FLASH_OPTCR_RDP;
reg &= ~FLASH_OPTCR_WDG_SW;
reg |= (opts->brown_out_level << 2) & FLASH_OPTCR_BOR_LEV;
reg |= (opts->nrst_standby << 7) & FLASH_OPTCR_nRST_STDBY;
reg |= (opts->nrst_stop << 6) & FLASH_OPTCR_nRST_STOP;
reg |= (opts->nwrpi << 16) & FLASH_OPTCR_nWRP;
reg |= (opts->read_protection << 8) & FLASH_OPTCR_RDP;
reg |= (opts->wdg_sw << 5) & FLASH_OPTCR_WDG_SW;
while (FLASH->SR & FLASH_SR_BSY)
;
FLASH->OPTCR = reg;
FLASH->OPTCR |= FLASH_OPTCR_OPTSTRT;
__DSB();
while (FLASH->SR & FLASH_SR_BSY)
;
FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK;
__DSB();
return 0;
}

View File

@ -95,9 +95,8 @@ int rcc_manager_enable_clock(volatile uint32_t *rcc_enable_register, uint8_t bit
int ret_val = 0;
struct rcc_enable_count *entry;
if (!rcc_enable_register || bit_no > 31) {
if (!rcc_enable_register || bit_no > 31)
return -1000;
}
/* Enable the clock in any case, no matter what follows */
*rcc_enable_register |= (1U<<bit_no);
@ -132,9 +131,8 @@ int rcc_manager_disable_clock(volatile uint32_t *rcc_enable_register, uint8_t bi
int ret_val = -1;
struct rcc_enable_count *entry;
if (!rcc_enable_register || bit_no > 31) {
if (!rcc_enable_register || bit_no > 31)
return -1000;
}
entry = search_enable_entry_in_list(rcc_enable_register, bit_no);

View File

@ -30,7 +30,7 @@ void random_number_gen_init(bool int_enable)
random_number_gen_reset(int_enable);
}
void random_number_gen_deinit()
void random_number_gen_deinit(void)
{
RNG->CR = 0;
__DSB();
@ -66,5 +66,5 @@ enum random_number_error random_number_gen_get_number(uint32_t *random_number, b
*random_number = RNG->DR;
/* Return from function with proper status */
return (value_ready ? RNG_ERROR_OK : RNG_ERROR_NOT_READY);
return value_ready ? RNG_ERROR_OK : RNG_ERROR_NOT_READY;
}

View File

@ -65,7 +65,8 @@ static struct stm_spi_dev *spi_handle_to_struct(stm_spi_handle handle)
return dev;
}
stm_spi_handle spi_init(struct stm_spi_dev *spi_dev_struct, SPI_TypeDef *spi_regs, const struct stm_spi_settings *settings)
stm_spi_handle spi_init(struct stm_spi_dev *spi_dev_struct, SPI_TypeDef *spi_regs,
const struct stm_spi_settings *settings)
{
stm_spi_handle ret_handle = NULL;
uint32_t reg_val;
@ -131,10 +132,14 @@ void spi_deinit(stm_spi_handle handle)
static uint8_t transfer_byte(uint8_t byte, struct stm_spi_dev *dev)
{
while (dev->spi_regs->SR & SPI_SR_BSY);
while (dev->spi_regs->SR & SPI_SR_BSY)
;
dev->spi_regs->DR = (uint16_t)byte;
__DSB();
while((dev->spi_regs->SR & SPI_SR_BSY) || !(dev->spi_regs->SR & SPI_SR_TXE));
while ((dev->spi_regs->SR & SPI_SR_BSY) || !(dev->spi_regs->SR & SPI_SR_TXE))
;
return (uint8_t)dev->spi_regs->DR;
}

View File

@ -95,6 +95,14 @@ void uart_change_brr(struct stm_uart *uart, uint32_t brr)
uart->uart_dev->BRR = brr;
}
uint32_t uart_get_brr(struct stm_uart *uart)
{
if (!uart || !uart->uart_dev)
return 0;
return uart->brr_val;
}
void uart_disable(struct stm_uart *uart)
{
if (!uart)
@ -118,7 +126,9 @@ void uart_send_char(struct stm_uart *uart, char c)
if (!uart || !uart->uart_dev)
return;
while(!(uart->uart_dev->SR & USART_SR_TXE));
while (!(uart->uart_dev->SR & USART_SR_TXE))
;
uart->uart_dev->DR = c;
}
@ -167,7 +177,8 @@ char uart_get_char(struct stm_uart *uart)
if (!uart)
return 0;
/* Wait for data to be available */
while (!(uart->uart_dev->SR & USART_SR_RXNE));
while (!(uart->uart_dev->SR & USART_SR_RXNE))
;
return (char)uart->uart_dev->DR;
}

View File

@ -19,12 +19,13 @@
*/
#include <stm-periph/unique-id.h>
#include <stm32/stm32f4xx.h>
#define LOW_WORD_ADDR (0x1FFF7A10UL)
#define MID_WORD_ADDR (LOW_WORD_ADDR+4U)
#define HIGH_WORD_ADDR (LOW_WORD_ADDR+8U)
void unique_id_get(uint32_t *high, uint32_t *mid, uint32_t *low)
void stm_unique_id_get(uint32_t *high, uint32_t *mid, uint32_t *low)
{
if (!high || !mid || !low)
return;
@ -33,3 +34,32 @@ void unique_id_get(uint32_t *high, uint32_t *mid, uint32_t *low)
*mid = *((uint32_t *)MID_WORD_ADDR);
*high = *((uint32_t *)HIGH_WORD_ADDR);
}
void stm_dev_rev_id_get(uint32_t *device_id, uint32_t *revision_id)
{
if (device_id)
*device_id = DBGMCU->IDCODE & DBGMCU_IDCODE_DEV_ID;
if (revision_id)
*revision_id = (DBGMCU->IDCODE & DBGMCU_IDCODE_REV_ID) >> 16;
}
void stm_cpuid_get(uint8_t *implementer, uint8_t *variant, uint16_t *part_no, uint8_t *rev)
{
uint32_t cpuid;
cpuid = SCB->CPUID;
if (implementer)
*implementer = (uint8_t)((cpuid >> 24) & 0xFFU);
if (variant)
*variant = (uint8_t)((cpuid >> 20) & 0x0FU);
if (part_no)
*part_no = (uint16_t)((cpuid >> 4) & 0x0FFFU);
if (rev)
*rev = (uint8_t)(cpuid & 0x0FU);
}

View File

@ -33,7 +33,7 @@
#include <reflow-controller/safety/safety-controller.h>
static struct tpe_current_state IN_SECTION(.ccm.data) state = {
static struct tpe_exec_state IN_SECTION(.ccm.data) current_state = {
.status = TPE_OFF,
.start_timestamp = 0,
};
@ -46,7 +46,7 @@ static SlList *command_list = NULL;
static void tpe_abort(void)
{
temp_profile_executer_stop();
state.status = TPE_ABORT;
current_state.status = TPE_ABORT;
}
enum pl_ret_val temp_profile_executer_start(const char *filename)
@ -54,16 +54,16 @@ enum pl_ret_val temp_profile_executer_start(const char *filename)
uint32_t parsed_count = 0;
enum pl_ret_val res;
state.setpoint = 0.0f;
state.start_timestamp = 0ULL;
state.setpoint = 0.0f;
state.step = 0;
state.profile_steps = 0;
current_state.setpoint = 0.0f;
current_state.start_timestamp = 0ULL;
current_state.setpoint = 0.0f;
current_state.step = 0;
current_state.profile_steps = 0;
oven_pid_stop();
pid_should_run = false;
state.status = TPE_OFF;
state.profile_steps = 0;
current_state.status = TPE_OFF;
current_state.profile_steps = 0;
cmd_continue = false;
/* This should never happen... But who knows */
@ -73,9 +73,9 @@ enum pl_ret_val temp_profile_executer_start(const char *filename)
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();
current_state.profile_steps = parsed_count;
current_state.status = TPE_RUNNING;
current_state.start_timestamp = systick_get_global_tick();
} else {
if (command_list)
temp_profile_free_command_list(&command_list);
@ -132,7 +132,7 @@ static bool cmd_set_temp(struct pl_command *cmd)
{
reactivate_pid_if_suspended();
oven_pid_set_target_temperature(cmd->params[0]);
state.setpoint = cmd->params[0];
current_state.setpoint = cmd->params[0];
return true;
}
@ -146,21 +146,21 @@ static bool cmd_ramp(struct pl_command *cmd, bool cmd_continue)
if (!cmd_continue) {
/* Init of command */
start_temp = state.setpoint;
start_temp = current_state.setpoint;
slope = (cmd->params[0] - start_temp) / cmd->params[1];
reactivate_pid_if_suspended();
oven_pid_set_target_temperature(start_temp);
start_timestamp = systick_get_global_tick();
} else {
secs_passed = ((float)(systick_get_global_tick() - start_timestamp)) / 1000.0f;
if ((state.setpoint <= cmd->params[0] && start_temp < cmd->params[0]) ||
(state.setpoint >= cmd->params[0] && start_temp > cmd->params[0])) {
state.setpoint = start_temp + secs_passed * slope;
if ((current_state.setpoint <= cmd->params[0] && start_temp < cmd->params[0]) ||
(current_state.setpoint >= cmd->params[0] && start_temp > cmd->params[0])) {
current_state.setpoint = start_temp + secs_passed * slope;
} else {
state.setpoint = cmd->params[0];
current_state.setpoint = cmd->params[0];
ret = true;
}
oven_pid_set_target_temperature(state.setpoint);
oven_pid_set_target_temperature(current_state.setpoint);
}
return ret;
@ -185,6 +185,36 @@ static void cmd_ack_flags(void)
}
static void cmd_digio_conf(uint8_t digio_num, uint8_t mode)
{
uint8_t pin_mode;
uint8_t alt_func = 0;
if (mode == 0 || mode == 1) {
pin_mode = mode;
} else if (mode >= 0x80 && mode <= 0x87) {
/* Alternate function */
pin_mode = 2;
alt_func = mode - 0x80;
} else {
return;
}
digio_setup_pin(digio_num, pin_mode, alt_func);
}
bool cmd_digio_wait(uint8_t digio_num, uint8_t digio_state)
{
bool advance = false;
int res;
res = digio_get(digio_num);
if (res < 0 || (uint8_t)res == digio_state)
advance = true;
return advance;
}
int temp_profile_executer_handle(void)
{
struct pl_command *current_cmd;
@ -195,7 +225,7 @@ int temp_profile_executer_handle(void)
/* Return if no profile is currently executed */
if (state.status != TPE_RUNNING)
if (current_state.status != TPE_RUNNING)
return -1;
/* Abort profile execution if oven PID is aborted. This is most likely due to some error flags */
@ -209,8 +239,8 @@ int temp_profile_executer_handle(void)
if (!systick_ticks_have_passed(last_tick, 100))
return 0;
current_cmd = (struct pl_command *)sl_list_nth(command_list, state.step)->data;
next_step = state.step;
current_cmd = (struct pl_command *)sl_list_nth(command_list, current_state.step)->data;
next_step = current_state.step;
switch (current_cmd->cmd) {
case PL_WAIT_FOR_TIME:
@ -250,6 +280,17 @@ int temp_profile_executer_handle(void)
cmd_ack_flags();
advance = true;
break;
case PL_DIGIO_CONF:
advance = true;
cmd_digio_conf((uint8_t)current_cmd->params[0], (uint8_t)current_cmd->params[1]);
break;
case PL_DIGIO_SET:
digio_set((uint8_t)current_cmd->params[0], current_cmd->params[1] ? 1u : 0u);
advance = true;
break;
case PL_DIGIO_WAIT:
advance = cmd_digio_wait((uint8_t)current_cmd->params[0], current_cmd->params[1] ? 1u : 0u);
break;
default:
tpe_abort();
advance = true;
@ -259,9 +300,9 @@ int temp_profile_executer_handle(void)
if (advance)
next_step++;
if (next_step != state.step) {
state.step = next_step;
if (next_step >= state.profile_steps) {
if (next_step != current_state.step) {
current_state.step = next_step;
if (next_step >= current_state.profile_steps) {
(void)temp_profile_executer_stop();
} else {
cmd_continue = false;
@ -274,15 +315,15 @@ int temp_profile_executer_handle(void)
return 0;
}
const struct tpe_current_state *temp_profile_executer_status(void)
const struct tpe_exec_state *temp_profile_executer_status(void)
{
return &state;
return &current_state;
}
int temp_profile_executer_stop(void)
{
if (state.status == TPE_RUNNING) {
state.status = TPE_OFF;
if (current_state.status == TPE_RUNNING) {
current_state.status = TPE_OFF;
oven_pid_stop();
}
@ -290,7 +331,9 @@ int temp_profile_executer_stop(void)
if (command_list)
temp_profile_free_command_list(&command_list);
/* Reset loudspeaker and reset default state of DIGIO channels */
loudspeaker_set(0);
digio_set_default_values();
return 0;
}

View File

@ -48,6 +48,9 @@ static const struct pl_command_list_map cmd_list_map[_PL_NUM_CMDS] = {
{PL_LOUDSPEAKER_SET, "beep", 1u},
{PL_OFF, "temp_off", 0u},
{PL_CLEAR_FLAGS, "clear_flags", 0u},
{PL_DIGIO_CONF, "digio_conf", 2u},
{PL_DIGIO_SET, "digio_set", 2u},
{PL_DIGIO_WAIT, "digio_wait", 2u},
};
/**
@ -108,6 +111,13 @@ static const struct pl_command_list_map *string_to_command(const char *str)
return ret;
}
/**
* @brief Parse a line in the temperature profile to a command.
* @param line Line to parse
* @param[out] cmd Command parsed
* @return negative in case of an error (Invalid command, invalid line);
* 1 in case the line is an empty line.
*/
static int parse_line(char *line, struct pl_command *cmd)
{
uint8_t token_idx = 0;
@ -123,7 +133,10 @@ static int parse_line(char *line, struct pl_command *cmd)
token = strtok(line, delim);
if (!token) {
/* Empty line or command line */
/* Empty line.
* Note: The comments in the lines are already filteed out before calling this function.
* Therefore, the empty line case covers lines containing just a comment
*/
return 1;
}
@ -131,6 +144,10 @@ static int parse_line(char *line, struct pl_command *cmd)
switch (token_idx) {
case 0:
map = string_to_command(token);
if (!map) {
/* No valid command found */
return -1;
}
c.cmd = map->command;
break;
default:
@ -162,12 +179,23 @@ static int parse_line(char *line, struct pl_command *cmd)
return 0;
}
/**
* @brief Append a command to a singly linked list.
*
* The the list item is newly allocated and the command is copied. Therefore, \p cmd can be overwritten after
* a call to this function.
*
* @param list List to add the command to
* @param cmd Command to add to list
* @return The new head of the list. If an error occured */
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));
/* This will go catastrophically wrong, if the heap is full... just saying. */
list = sl_list_append(list, alloced_cmd);
return list;
@ -235,6 +263,10 @@ exit:
return ret;
}
/**
* @brief Free an allocated pl_command structure
* @param cmd command struct. Tolerates NULL.
*/
static void delete_pl_command(void *cmd)
{
if (cmd)

View File

@ -53,6 +53,7 @@ void button_init()
enum button_state button_read_event()
{
uint64_t time_delta;
uint64_t activation_stmp;
enum button_state temp_state;
if (override_state != BUTTON_IDLE) {
@ -66,7 +67,13 @@ enum button_state button_read_event()
int_state = BUTTON_IDLE;
return temp_state;
} else {
time_delta = systick_get_global_tick() - to_active_timestamp;
/* Unfortunately I don't jave a better idea for now to ensure,
* that to_active_timestamp is read atomically
*/
__disable_irq();
activation_stmp = to_active_timestamp;
__enable_irq();
time_delta = systick_get_global_tick() - activation_stmp;
if (time_delta >= BUTTON_LONG_ON_TIME_MS)
return BUTTON_LONG;
else if (time_delta >= BUTTON_SHORT_ON_TIME_MS)

View File

@ -18,10 +18,12 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "reflow-controller/version.h"
#include <reflow-controller/ui/gui.h>
#include <reflow-controller/ui/gui-config.h>
#include <reflow-controller/ui/menu.h>
#include <reflow-controller/ui/lcd.h>
#include <reflow-controller/sd.h>
#include <reflow-controller/ui/rotary-encoder.h>
#include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h>
@ -166,9 +168,9 @@ static void gui_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry ent
break;
last_page = 1;
menu_lcd_output(menu, 0, "Version Number:");
menu_lcd_outputf(menu, 1, "%.*s", LCD_CHAR_WIDTH, xstr(GIT_VER));
if (strlen(xstr(GIT_VER)) > LCD_CHAR_WIDTH) {
menu_lcd_outputf(menu, 2, "%s", &xstr(GIT_VER)[LCD_CHAR_WIDTH]);
menu_lcd_outputf(menu, 1, "%.*s", LCD_CHAR_WIDTH, version_git_version_string);
if (strlen(version_git_version_string) > LCD_CHAR_WIDTH) {
menu_lcd_outputf(menu, 2, "%s", &version_git_version_string[LCD_CHAR_WIDTH]);
}
#ifdef DEBUGBUILD
menu_lcd_output(menu, 3, "Page 2/5 [DEBUG]");
@ -189,7 +191,7 @@ static void gui_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry ent
if (last_page == 3)
break;
last_page = 3;
unique_id_get(&ser1, &ser2, &ser3);
stm_unique_id_get(&ser1, &ser2, &ser3);
menu_lcd_outputf(menu, 0, "Serial: %08X", ser1);
menu_lcd_outputf(menu, 1, " %08X", ser2);
@ -414,7 +416,7 @@ static SlList *load_file_list_from_sdcard(int *error, const char *file_pattern)
static void gui_menu_temp_profile_execute(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void* parent)
{
static void *my_parent;
const struct tpe_current_state *state;
const struct tpe_exec_state *state;
static uint64_t last_tick;
float temperature;
float resistance;
@ -663,6 +665,7 @@ static void gui_update_firmware(struct lcd_menu *menu, enum menu_entry_func_entr
}
}
if (list_length) {
if (entry_type == MENU_ENTRY_FIRST_ENTER ||
previously_selected_file != currently_selected_file) {
fname = sl_list_nth(file_list, currently_selected_file)->data;
@ -673,11 +676,15 @@ static void gui_update_firmware(struct lcd_menu *menu, enum menu_entry_func_entr
if (button == BUTTON_SHORT_RELEASED) {
fname = sl_list_nth(file_list, currently_selected_file)->data;
menu_display_clear(menu);
file_list = NULL;
updater_update_from_file(fname);
/* This code is here for completeness. It will never be reached! */
sl_list_free_full(file_list, delete_file_list_entry);
} else if (button == BUTTON_LONG) {
}
} else {
menu_lcd_output(menu, 0, "No files!");
}
if (button == BUTTON_LONG) {
sl_list_free_full(file_list, delete_file_list_entry);
file_list = NULL;
menu_entry_dropback(menu, my_parent);
@ -686,6 +693,37 @@ static void gui_update_firmware(struct lcd_menu *menu, enum menu_entry_func_entr
}
}
static void gui_connector_info(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);
menu_lcd_output(menu, 0, "2,4: DIGIO[0,1]");
menu_lcd_output(menu, 1, "6: DIGIO2 (TX)");
menu_lcd_output(menu, 2, "8: DIGIO3 (RX)");
menu_lcd_output(menu, 3, "10:3V3 Rest:GND");
}
if (menu_get_button_ready_state(menu)) {
/* Buttons are ready. Read button */
button = menu_get_button_state(menu);
/* Throw away any rotation */
(void)menu_get_rotary_delta(menu);
if (button == BUTTON_SHORT_RELEASED || button == BUTTON_LONG ||
button == BUTTON_LONG_RELEASED) {
/* Exit menu */
menu_entry_dropback(menu, my_parent);
}
}
}
static char *overlay_heading = NULL;
static char *overlay_text = NULL;
@ -735,6 +773,7 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
"Error Flags",
"About",
"Update",
"Connector Info",
NULL
};
static const menu_func_t root_entry_funcs[] = {
@ -744,6 +783,7 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
gui_menu_err_flags,
gui_menu_about,
gui_update_firmware,
gui_connector_info,
};
enum button_state push_button;
int16_t rot_delta;
@ -751,7 +791,6 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
if (entry_type != MENU_ENTRY_CONTINUE) {
menu_changed = true;
menu_display_clear(menu);
update_display_buffer(0, "Main Menu");
menu_ack_rotary_delta(menu);
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
list.entry_names = root_entry_names;
@ -762,6 +801,8 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
}
}
update_display_buffer(0, sd_card_is_mounted() ? "Main Menu [SD]" : "Main Menu [--]");
push_button = menu_get_button_state(menu);
rot_delta = menu_get_rotary_delta(menu);
@ -791,7 +832,7 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
}
}
int gui_handle()
int gui_handle(void)
{
int32_t rot_delta;
enum button_state button;
@ -813,12 +854,18 @@ int gui_handle()
return 1;
}
void gui_init()
void gui_init(void)
{
/** - Setup the rotary encoder input */
rotary_encoder_setup();
/** - Setup the push button */
button_init();
/** - Setup the LCD */
lcd_init();
/** - If an overlay has been previously set, clear it */
if (overlay_heading)
free(overlay_heading);
if (overlay_text)
@ -827,6 +874,7 @@ void gui_init()
overlay_heading = NULL;
overlay_text = NULL;
/** - Init the GUI menu */
menu_init(reflow_menu_ptr, gui_menu_root_entry, update_display_buffer);
}
@ -853,3 +901,13 @@ void gui_lcd_write_direct_blocking(uint8_t line, const char *text)
lcd_setcursor(0, line);
lcd_string(text);
}
size_t gui_get_line_count(void)
{
return (size_t)LCD_ROW_COUNT;
}
const char (*gui_get_current_display_content(void))[21]
{
return display_buffer;
}

View File

@ -0,0 +1,130 @@
/* Reflow Oven Controller
*
* Copyright (C) 2021 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/ui/shell-uart.h>
#include <reflow-controller/periph-config/shell-uart-config.h>
#include <stm-periph/stm32-gpio-macros.h>
#include <stm-periph/uart.h>
#include <helper-macros/helper-macros.h>
/**
* @brief TX buffer for the shell's uart
*/
static char shell_uart_tx_buff[4096];
/**
* @brief RX buffer for the shell's uart
*/
static char shell_uart_rx_buff[128];
/**
* @brief The uart instance handling the shellmatta shell.
*/
struct stm_uart shell_uart;
void shell_uart_setup(void)
{
struct stm_uart *uart = &shell_uart;
uart->rx = 1;
uart->tx = 1;
uart->brr_val = SHELL_UART_DEFAULT_BRR_REG_VALUE;
uart->rcc_reg = &SHELL_UART_RCC_REG;
uart->rcc_bit_no = BITMASK_TO_BITNO(SHELL_UART_RCC_MASK);
uart->uart_dev = SHELL_UART_PERIPH;
uart->dma_rx_buff = shell_uart_rx_buff;
uart->dma_tx_buff = shell_uart_tx_buff;
uart->rx_buff_count = sizeof(shell_uart_rx_buff);
uart->tx_buff_count = sizeof(shell_uart_tx_buff);
uart->base_dma_num = 2;
uart->dma_rx_stream = SHELL_UART_RECEIVE_DMA_STREAM;
uart->dma_tx_stream = SHELL_UART_SEND_DMA_STREAM;
uart->dma_rx_trigger_channel = SHELL_UART_RX_DMA_TRIGGER;
uart->dma_tx_trigger_channel = SHELL_UART_TX_DMA_TRIGGER;
uart_init(uart);
NVIC_EnableIRQ(DMA2_Stream7_IRQn);
}
shellmatta_retCode_t shell_uart_write_callback(const char *data, uint32_t len)
{
uart_send_array_with_dma(&shell_uart, data, len);
return SHELLMATTA_OK;
}
int shell_uart_receive_data_with_dma(const char **data, size_t *len)
{
return uart_receive_data_with_dma(&shell_uart, data, len);
}
int32_t shell_uart_reconfig_baud(uint32_t new_baud)
{
uint32_t brr_val_floor;
uint32_t brr_val_remainder;
uint32_t error_permille;
int64_t actual_baud;
/* Calculate the new BRR register value */
brr_val_floor = SHELL_UART_PERIPHERAL_CLOCK / new_baud;
brr_val_remainder = SHELL_UART_PERIPHERAL_CLOCK % new_baud;
/* Round to the nearest value */
if (brr_val_remainder > (new_baud / 2u)) {
brr_val_floor++;
brr_val_remainder = new_baud - brr_val_remainder;
}
actual_baud = (1000U *(int64_t)SHELL_UART_PERIPHERAL_CLOCK) / brr_val_floor;
error_permille = (ABS(actual_baud - 1000U * (int64_t)new_baud)) / (int64_t)new_baud;
if (error_permille < 20u) {
uart_change_brr(&shell_uart, brr_val_floor);
} else {
return -1;
}
return (int32_t)error_permille;
}
uint32_t shell_uart_get_current_baudrate(void)
{
uint32_t brr;
brr = uart_get_brr(&shell_uart);
if (brr == 0)
return 0;
return (SHELL_UART_PERIPHERAL_CLOCK) / brr;
}
/**
* @brief Handles the TX of UART1 (Shellmatta)
*/
void DMA2_Stream7_IRQHandler(void)
{
uint32_t hisr = DMA2->HISR & (0x3F << 22);
DMA2->HIFCR = hisr;
if (hisr & DMA_HISR_TCIF7)
uart_tx_dma_complete_int_callback(&shell_uart);
}

View File

@ -20,7 +20,7 @@
#include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h>
#include <reflow-controller/shell.h>
#include <reflow-controller/ui/shell.h>
#include <stm-periph/uart.h>
#include <string.h>
#include <reflow-controller/adc-meas.h>
@ -45,11 +45,13 @@
#include <reflow-controller/temp-profile/temp-profile-executer.h>
#include <reflow-controller/updater/updater.h>
#include <reflow-controller/main-cycle-counter.h>
#include <stdio.h>
#include <stm-periph/option-bytes.h>
#include <reflow-controller/ui/gui.h>
#include <reflow-controller/ui/shell-uart.h>
#include <reflow-controller/safety/flash-crc.h>
#include <reflow-controller/version.h>
#ifndef GIT_VER
#define GIT_VER "VERSION NOT SET"
#endif
#include <stdio.h>
static shellmatta_instance_t shell;
static char shell_buffer[512];
@ -64,13 +66,21 @@ static shellmatta_retCode_t shell_cmd_ver(const shellmatta_handle_t handle,
uint32_t low_id;
uint32_t mid_id;
uint32_t high_id;
uint32_t stm_rev_id;
uint32_t stm_dev_id;
uint8_t core_rev;
uint8_t core_implementer;
uint8_t core_variant;
uint16_t core_part_no;
const char *hw_rev_str;
enum hw_revision pcb_rev;
unique_id_get(&high_id, &mid_id, &low_id);
stm_unique_id_get(&high_id, &mid_id, &low_id);
stm_dev_rev_id_get(&stm_dev_id, &stm_rev_id);
shellmatta_printf(handle, "Reflow Oven Controller Firmware " xstr(GIT_VER) "\r\n"
"Compiled: " __DATE__ " at " __TIME__ "\r\n");
shellmatta_printf(handle, "Reflow Oven Controller Firmware %s\r\n"
"Compiled: %s at %s\r\n",
version_git_version_string, version_compile_date, version_compile_time);
shellmatta_printf(handle, "Serial: %08X-%08X-%08X\r\n", high_id, mid_id, low_id);
pcb_rev = get_pcb_hardware_version();
@ -81,11 +91,23 @@ static shellmatta_retCode_t shell_cmd_ver(const shellmatta_handle_t handle,
case HW_REV_V1_3:
hw_rev_str = "Hardware: v1.3";
break;
case HW_REV_V1_3_1:
hw_rev_str = "Hardware: v1.3.1";
break;
default:
hw_rev_str = "Hardware: Unknown Revision. You might have to update the firmware!";
break;
}
shellmatta_printf(handle, "%s", hw_rev_str);
shellmatta_printf(handle, "%s\r\n", hw_rev_str);
shellmatta_printf(handle, "STM Device ID: 0x%04X\r\n", stm_dev_id);
shellmatta_printf(handle, "STM Revision ID: 0x%04X\r\n", stm_rev_id);
stm_cpuid_get(&core_implementer, &core_variant, &core_part_no, &core_rev);
shellmatta_printf(handle, "CPU Implementer: 0x%02X\r\n", (unsigned int)core_implementer);
shellmatta_printf(handle, "CPU Variant: 0x%02X\r\n", (unsigned int)core_variant);
shellmatta_printf(handle, "CPU Part No.: 0x%04X\r\n", (unsigned int)core_part_no);
shellmatta_printf(handle, "CPU Revision: 0x%02X\r\n", (unsigned int)core_rev);
return SHELLMATTA_OK;
}
@ -473,6 +495,11 @@ static shellmatta_retCode_t shell_cmd_ui_emulation(const shellmatta_handle_t han
uint32_t i;
uint32_t len;
char *buff;
uint8_t row;
uint8_t col;
bool disp_content_differs = false;
static char IN_SECTION(.ccm.bss) display_buffer[4][21] = {0};
const char (*current_display)[21];
shellmatta_read(handle, &buff, &len);
@ -501,6 +528,39 @@ static shellmatta_retCode_t shell_cmd_ui_emulation(const shellmatta_handle_t han
case 'R':
button_override_event(BUTTON_LONG_RELEASED);
break;
case '\x03':
/* CTRL-C received. Delete the display buffer so it will be updated the next time
* This function is called.
* Otherwise the diplay might not be printed to the console if nothing has changed
*/
display_buffer[0][0] = 0;
display_buffer[1][0] = 0;
display_buffer[2][0] = 0;
display_buffer[3][0] = 0;
return SHELLMATTA_OK;
break;
}
}
current_display = gui_get_current_display_content();
for (row = 0; row < 4; row++) {
for (col = 0; col < 21; col++) {
if (current_display[row][col] != display_buffer[row][col]) {
display_buffer[row][col] = current_display[row][col];
disp_content_differs = true;
}
}
display_buffer[row][20] = 0;
}
if (disp_content_differs) {
/* Clear the display */
shellmatta_printf(handle, "\e[2J\e[H");
/* Print out the rows of the LCD to console */
for (row = 0; row < 4; row++) {
shellmatta_printf(handle, "%s\r\n", &display_buffer[row][0]);
}
}
@ -717,7 +777,7 @@ shellmatta_retCode_t shell_cmd_overtemp_cfg(const shellmatta_handle_t handle, co
shellmatta_retCode_t shell_cmd_execute(const shellmatta_handle_t handle, const char *args, uint32_t len)
{
enum pl_ret_val res;
const struct tpe_current_state *state;
const struct tpe_exec_state *state;
static bool running = false;
char *data;
uint32_t dlen;
@ -798,6 +858,7 @@ shellmatta_retCode_t shell_cmd_execute(const shellmatta_handle_t handle, const c
shellmatta_retCode_t shell_cmd_cycle_count(const shellmatta_handle_t handle, const char *args, uint32_t len)
{
uint64_t counter;
uint32_t core_cycle_count;
(void)args;
(void)len;
char option;
@ -805,8 +866,10 @@ shellmatta_retCode_t shell_cmd_cycle_count(const shellmatta_handle_t handle, con
uint32_t arg_len;
int opt_stat;
bool clear = false;
bool hex = false;
const shellmatta_opt_long_t options[] = {
{"clear", 'c', SHELLMATTA_OPT_ARG_NONE},
{"hex", 'h', SHELLMATTA_OPT_ARG_NONE},
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE},
};
@ -818,6 +881,8 @@ shellmatta_retCode_t shell_cmd_cycle_count(const shellmatta_handle_t handle, con
case 'c':
clear = true;
break;
case 'h':
hex = true;
default:
break;
}
@ -825,10 +890,18 @@ shellmatta_retCode_t shell_cmd_cycle_count(const shellmatta_handle_t handle, con
counter = main_cycle_counter_get();
shellmatta_printf(handle, "%"PRIu64"\r\n", counter);
if (clear)
main_cycle_counter_init();
core_cycle_count = core_cycle_counter_get();
if (hex) {
shellmatta_printf(handle, "Main loop: 0x%016"PRIX64"\r\n", counter);
shellmatta_printf(handle, "CPU cycles: 0x%08"PRIX32"\r\n", core_cycle_count);
} else {
shellmatta_printf(handle, "Main loop: %"PRIu64"\r\n", counter);
shellmatta_printf(handle, "CPU cycles: %"PRIu32"\r\n", core_cycle_count);
}
if (clear) {
main_cycle_counter_reset();
core_cycle_counter_reset();
}
return SHELLMATTA_OK;
}
@ -877,6 +950,122 @@ shellmatta_retCode_t shell_cmd_filter_alpha(const shellmatta_handle_t handle, co
return SHELLMATTA_OK;
}
shellmatta_retCode_t shell_cmd_print_opt_bytes(const shellmatta_handle_t handle,
const char *args, uint32_t len)
{
(void)args;
(void)len;
struct option_bytes opts;
stm_option_bytes_read(&opts);
shellmatta_printf(handle, "Brown-out Level: 0x%x\r\n", opts.brown_out_level);
shellmatta_printf(handle, "nRST Standby: 0x%x\r\n", opts.nrst_standby);
shellmatta_printf(handle, "nRST Stop: 0x%x\r\n", opts.nrst_stop);
shellmatta_printf(handle, "Write Protection: 0x%x\r\n", opts.nwrpi);
shellmatta_printf(handle, "Read Protection: 0x%x\r\n", opts.read_protection);
shellmatta_printf(handle, "SW Watchdog: 0x%x\r\n", opts.wdg_sw);
return SHELLMATTA_OK;
}
shellmatta_retCode_t shell_cmd_set_baud(const shellmatta_handle_t handle, const char *args, uint32_t len)
{
shellmatta_retCode_t opt_stat;
char option;
char *argument;
uint32_t arg_len;
char *baud_string = NULL;
uint32_t baud;
(void)len;
(void)args;
const shellmatta_opt_long_t options[] = {
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE},
};
while (1) {
opt_stat = shellmatta_opt_long(handle, options, &option, &argument, &arg_len);
if (opt_stat != SHELLMATTA_OK)
break;
switch (option) {
case '\0':
baud_string = argument;
break;
default:
break;
}
}
shellmatta_printf(handle, "Current baudrate: %u\r\n", (unsigned int)shell_uart_get_current_baudrate());
if (!baud_string) {
shellmatta_printf(handle, "Please specify a baudrate\r\n");
return SHELLMATTA_OK;
}
baud = strtoul(baud_string, NULL, 0);
if (baud < 38400) {
shellmatta_printf(handle, "38400 is the minimum recommended baudrate!\r\n");
}
shellmatta_printf(handle, "Setting baud to: %u\r\n", (unsigned int)baud);
if (shell_uart_reconfig_baud(baud) < 0) {
shellmatta_printf(handle,"Setting baudrate not possible. Error greater than 2%%\r\n");
}
return SHELLMATTA_OK;
}
static void shell_print_crc_line(const shellmatta_handle_t handle, const char *name,
uint32_t crc, uint32_t crc_expected)
{
shellmatta_printf(handle, "%-15s0x%08x 0x%08x [%s]\r\n",
name,
crc,
crc_expected,
crc != crc_expected ? "\e[1;31mERR\e[m" : "\e[32mOK\e[m");
}
shellmatta_retCode_t shell_cmd_flash_crc(const shellmatta_handle_t handle, const char *args, uint32_t len)
{
(void)args;
(void)len;
uint32_t crc = 0;
uint32_t crc_expected = 0;
int res;
shellmatta_printf(handle, " Calculated Expected State\r\n\r\n");
res = flash_crc_calc_section(FLASH_CRC_VECTOR, &crc);
crc_expected = flash_crc_get_expected_crc(FLASH_CRC_VECTOR);
if (res)
shellmatta_printf(handle, "Error during calculation!\r\n");
shell_print_crc_line(handle, "Vector CRC:", crc, crc_expected);
res = flash_crc_calc_section(FLASH_CRC_TEXT, &crc);
crc_expected = flash_crc_get_expected_crc(FLASH_CRC_TEXT);
if (res)
shellmatta_printf(handle, "Error during calculation!\r\n");
shell_print_crc_line(handle, "Code CRC:", crc, crc_expected);
res = flash_crc_calc_section(FLASH_CRC_DATA, &crc);
crc_expected = flash_crc_get_expected_crc(FLASH_CRC_DATA);
if (res)
shellmatta_printf(handle, "Error during calculation!\r\n");
shell_print_crc_line(handle, "Data CRC:", crc, crc_expected);
res = flash_crc_calc_section(FLASH_CRC_CCMDATA, &crc);
crc_expected = flash_crc_get_expected_crc(FLASH_CRC_CCMDATA);
if (res)
shellmatta_printf(handle, "Error during calculation!\r\n");
shell_print_crc_line(handle, "CCM Data CRC:", crc, crc_expected);
return SHELLMATTA_OK;
}
//typedef struct shellmatta_cmd
//{
// char *cmd; /**< command name */
@ -886,7 +1075,7 @@ shellmatta_retCode_t shell_cmd_filter_alpha(const shellmatta_handle_t handle, co
// shellmatta_cmdFct_t cmdFct; /**< pointer to the cmd callack function */
// struct shellmatta_cmd *next; /**< pointer to next command or NULL */
//} shellmatta_cmd_t;
static shellmatta_cmd_t cmd[24] = {
static shellmatta_cmd_t cmd[26] = {
{
.cmd = "version",
.cmdAlias = "ver",
@ -1070,8 +1259,33 @@ static shellmatta_cmd_t cmd[24] = {
.helpText = "Sets the filter constant",
.usageText = "filter-alpha <alpha>",
.cmdFct = shell_cmd_filter_alpha,
.next = &cmd[23],
},
{
.cmd = "print-option-bytes",
.cmdAlias = "opt-bytes",
.helpText = "Print the currently set option bytes of the STM32",
.usageText = "",
.cmdFct = shell_cmd_print_opt_bytes,
.next = &cmd[24],
},
{
.cmd = "baudrate",
.cmdAlias = "baud",
.helpText = "Set a new temporary baudrate for the UART",
.usageText = "baudrate <new baud>",
.cmdFct = shell_cmd_set_baud,
.next = &cmd[25],
},
{
.cmd = "flashcrc",
.cmdAlias = "fcrc",
.helpText = "Calculate the Flash CRCs",
.usageText = "flashcrc",
.cmdFct = shell_cmd_flash_crc,
.next = NULL,
}
},
};
shellmatta_handle_t shell_init(shellmatta_write_t write_func)

View File

@ -2,7 +2,7 @@ project(updater-ram-code)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_CROSSCOMPILING 1)
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.18)
set(CMAKE_TOOLCHAIN_FILE "arm-none-eabi-gcc.cmake")
@ -31,7 +31,7 @@ add_executable(${ELFFILE} ${SRCS} ${FATFS_SRCS} ${SDIO_SRCS} ${STM_PERIPH_SRCS}
target_include_directories(${ELFFILE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ../../include ${CMAKE_CURRENT_SOURCE_DIR}/3rd-party)
target_compile_options(${ELFFILE} PRIVATE -Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter)
target_compile_options(${ELFFILE} PRIVATE -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles -Wimplicit-fallthrough=3 -Wsign-compare -Os -g3)
target_compile_definitions(${ELFFILE} PRIVATE -DGIT_VER=${GIT_DESCRIBE} -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4 -DSAFETY_MEMORY_STRIPOUT_DUMP)
target_compile_definitions(${ELFFILE} PRIVATE -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")

View File

@ -1,35 +1,45 @@
#!env python
# Convert a file to a c array
# bin2carray <output file> <input file>
"""
Convert a file to a c array
bin2carray <output file> <input file>
"""
import os
import os.path
import sys
def main():
"""
Main script function
"""
if len(sys.argv) < 3:
sys.exit(-1)
return -1
source_file = sys.argv[2]
dest_file = sys.argv[1]
print("%s --> %s" % (source_file, dest_file))
print(f'{source_file} --> {dest_file}')
with open(source_file, "rb") as src:
with open(source_file, 'rb') as src:
data = src.read()
with open(dest_file, "w") as dest:
header_guard = "__" + os.path.basename(dest_file).replace('.', '_').replace('-', '_') + "_H__"
dest.write("#ifndef %s\n" % (header_guard))
dest.write("#define %s\n" % (header_guard))
dest.write("static const char binary_blob[%d] = {\n" % (len(data)))
for current,idx in zip(data, range(len(data))):
if ((idx+1) % 4 == 0):
dest.write(hex(current)+",\n")
with open(dest_file, 'w', encoding='utf-8') as dest:
header_guard = '_' + os.path.basename(dest_file).replace('.', '_').replace('-', '_') + '_H_'
header_guard = header_guard.upper()
dest.write(f'#ifndef {header_guard}\n')
dest.write(f'#define {header_guard}\n')
dest.write(f'static const char binary_blob[{len(data)}] = {{\n')
for idx, current in enumerate(data, start=1):
if idx % 4 == 0:
dest.write(hex(current)+',\n')
else:
dest.write(hex(current)+",")
dest.write(hex(current)+',')
dest.write("};\n")
dest.write("#endif /* %s */\n" % (header_guard))
dest.write('};\n')
dest.write(f'#endif /* {header_guard} */\n')
sys.exit(0)
return 0
if __name__ == '__main__':
sys.exit(main())

File diff suppressed because it is too large Load Diff

View File

@ -1,170 +1,208 @@
/*------------------------------------------------------------------------*/
/* Sample Code of OS Dependent Functions for FatFs */
/* (C)ChaN, 2018 */
/* A Sample Code of User Provided OS Dependent Functions for FatFs */
/*------------------------------------------------------------------------*/
#include <fatfs/ff.h>
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
#if FF_USE_LFN == 3 /* Use dynamic memory allocation */
/*------------------------------------------------------------------------*/
/* Allocate a memory block */
/* Allocate/Free a Memory Block */
/*------------------------------------------------------------------------*/
#include <stdlib.h> /* with POSIX API */
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
UINT msize /* Number of bytes to allocate */
)
{
return malloc(msize); /* Allocate a new memory block with POSIX API */
return malloc((size_t)msize); /* Allocate a new memory block */
}
/*------------------------------------------------------------------------*/
/* Free a memory block */
/*------------------------------------------------------------------------*/
void ff_memfree (
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
void* mblock /* Pointer to the memory block to free (no effect if null) */
)
{
free(mblock); /* Free the memory block with POSIX API */
free(mblock); /* Free the memory block */
}
#endif
#if FF_FS_REENTRANT /* Mutal exclusion */
/*------------------------------------------------------------------------*/
/* Create a Synchronization Object */
/* Definitions of Mutex */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to create a new
/ synchronization object for the volume, such as semaphore and mutex.
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
*/
//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */
#define OS_TYPE 0 /* 0:Win32, 1:uITRON4.0, 2:uC/OS-II, 3:FreeRTOS, 4:CMSIS-RTOS */
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
BYTE vol, /* Corresponding volume (logical drive number) */
FF_SYNC_t* sobj /* Pointer to return the created sync object */
)
{
/* Win32 */
*sobj = CreateMutex(NULL, FALSE, NULL);
return (int)(*sobj != INVALID_HANDLE_VALUE);
#if OS_TYPE == 0 /* Win32 */
#include <windows.h>
static HANDLE Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
/* uITRON */
// T_CSEM csem = {TA_TPRI,1,1};
// *sobj = acre_sem(&csem);
// return (int)(*sobj > 0);
#elif OS_TYPE == 1 /* uITRON */
#include "itron.h"
#include "kernel.h"
static mtxid Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
/* uC/OS-II */
// OS_ERR err;
// *sobj = OSMutexCreate(0, &err);
// return (int)(err == OS_NO_ERR);
#elif OS_TYPE == 2 /* uc/OS-II */
#include "includes.h"
static OS_EVENT *Mutex[FF_VOLUMES + 1]; /* Table of mutex pinter */
/* FreeRTOS */
// *sobj = xSemaphoreCreateMutex();
// return (int)(*sobj != NULL);
#elif OS_TYPE == 3 /* FreeRTOS */
#include "FreeRTOS.h"
#include "semphr.h"
static SemaphoreHandle_t Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
/* CMSIS-RTOS */
// *sobj = osMutexCreate(&Mutex[vol]);
// return (int)(*sobj != NULL);
}
/*------------------------------------------------------------------------*/
/* Delete a Synchronization Object */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to delete a synchronization
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
/ the f_mount() function fails with FR_INT_ERR.
*/
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
)
{
/* Win32 */
return (int)CloseHandle(sobj);
/* uITRON */
// return (int)(del_sem(sobj) == E_OK);
/* uC/OS-II */
// OS_ERR err;
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
// return (int)(err == OS_NO_ERR);
/* FreeRTOS */
// vSemaphoreDelete(sobj);
// return 1;
/* CMSIS-RTOS */
// return (int)(osMutexDelete(sobj) == osOK);
}
/*------------------------------------------------------------------------*/
/* Request Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on entering file functions to lock the volume.
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
*/
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
FF_SYNC_t sobj /* Sync object to wait */
)
{
/* Win32 */
return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
/* uITRON */
// return (int)(wai_sem(sobj) == E_OK);
/* uC/OS-II */
// OS_ERR err;
// OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
// return (int)(err == OS_NO_ERR);
/* FreeRTOS */
// return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
/* CMSIS-RTOS */
// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
}
/*------------------------------------------------------------------------*/
/* Release Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on leaving file functions to unlock the volume.
*/
void ff_rel_grant (
FF_SYNC_t sobj /* Sync object to be signaled */
)
{
/* Win32 */
ReleaseMutex(sobj);
/* uITRON */
// sig_sem(sobj);
/* uC/OS-II */
// OSMutexPost(sobj);
/* FreeRTOS */
// xSemaphoreGive(sobj);
/* CMSIS-RTOS */
// osMutexRelease(sobj);
}
#elif OS_TYPE == 4 /* CMSIS-RTOS */
#include "cmsis_os.h"
static osMutexId Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
#endif
/*------------------------------------------------------------------------*/
/* Create a Mutex */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount function to create a new mutex
/ or semaphore for the volume. When a 0 is returned, the f_mount function
/ fails with FR_INT_ERR.
*/
int ff_mutex_create ( /* Returns 1:Function succeeded or 0:Could not create the mutex */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
Mutex[vol] = CreateMutex(NULL, FALSE, NULL);
return (int)(Mutex[vol] != INVALID_HANDLE_VALUE);
#elif OS_TYPE == 1 /* uITRON */
T_CMTX cmtx = {TA_TPRI,1};
Mutex[vol] = acre_mtx(&cmtx);
return (int)(Mutex[vol] > 0);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
Mutex[vol] = OSMutexCreate(0, &err);
return (int)(err == OS_NO_ERR);
#elif OS_TYPE == 3 /* FreeRTOS */
Mutex[vol] = xSemaphoreCreateMutex();
return (int)(Mutex[vol] != NULL);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexDef(cmsis_os_mutex);
Mutex[vol] = osMutexCreate(osMutex(cmsis_os_mutex));
return (int)(Mutex[vol] != NULL);
#endif
}
/*------------------------------------------------------------------------*/
/* Delete a Mutex */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount function to delete a mutex or
/ semaphore of the volume created with ff_mutex_create function.
*/
void ff_mutex_delete ( /* Returns 1:Function succeeded or 0:Could not delete due to an error */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
CloseHandle(Mutex[vol]);
#elif OS_TYPE == 1 /* uITRON */
del_mtx(Mutex[vol]);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
OSMutexDel(Mutex[vol], OS_DEL_ALWAYS, &err);
#elif OS_TYPE == 3 /* FreeRTOS */
vSemaphoreDelete(Mutex[vol]);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexDelete(Mutex[vol]);
#endif
}
/*------------------------------------------------------------------------*/
/* Request a Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on enter file functions to lock the volume.
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
*/
int ff_mutex_take ( /* Returns 1:Succeeded or 0:Timeout */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
return (int)(WaitForSingleObject(Mutex[vol], FF_FS_TIMEOUT) == WAIT_OBJECT_0);
#elif OS_TYPE == 1 /* uITRON */
return (int)(tloc_mtx(Mutex[vol], FF_FS_TIMEOUT) == E_OK);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
OSMutexPend(Mutex[vol], FF_FS_TIMEOUT, &err));
return (int)(err == OS_NO_ERR);
#elif OS_TYPE == 3 /* FreeRTOS */
return (int)(xSemaphoreTake(Mutex[vol], FF_FS_TIMEOUT) == pdTRUE);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
return (int)(osMutexWait(Mutex[vol], FF_FS_TIMEOUT) == osOK);
#endif
}
/*------------------------------------------------------------------------*/
/* Release a Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on leave file functions to unlock the volume.
*/
void ff_mutex_give (
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
ReleaseMutex(Mutex[vol]);
#elif OS_TYPE == 1 /* uITRON */
unl_mtx(Mutex[vol]);
#elif OS_TYPE == 2 /* uC/OS-II */
OSMutexPost(Mutex[vol]);
#elif OS_TYPE == 3 /* FreeRTOS */
xSemaphoreGive(Mutex[vol]);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexRelease(Mutex[vol]);
#endif
}
#endif /* FF_FS_REENTRANT */

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------*/
/* Unicode handling functions for FatFs R0.13+ */
/* Unicode Handling Functions for FatFs R0.13 and Later */
/*------------------------------------------------------------------------*/
/* This module will occupy a huge memory in the .rodata section when the */
/* FatFs is configured for LFN with DBCS. If the system has a Unicode */
/* library for the code conversion, this module should be modified to use */
/* it to avoid silly memory consumption. */
/*------------------------------------------------------------------------*/
/* This module will occupy a huge memory in the .const section when the /
/ FatFs is configured for LFN with DBCS. If the system has any Unicode /
/ utilitiy for the code conversion, this module should be modified to use /
/ that function to avoid silly memory consumption. /
/-------------------------------------------------------------------------*/
/*
/ Copyright (C) 2014, ChaN, all right reserved.
/ Copyright (C) 2022, 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
@ -22,10 +22,9 @@
/ by use of this software.
*/
#include <fatfs/ff.h>
#if FF_USE_LFN /* This module will be blanked if non-LFN configuration */
#if FF_USE_LFN != 0 /* This module will be blanked if in non-LFN configuration */
#define MERGE2(a, b) a ## b
#define CVTBL(tbl, cp) MERGE2(tbl, cp)
@ -15214,8 +15213,8 @@ static const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */
/*------------------------------------------------------------------------*/
/* OEM <==> Unicode conversions for static code page configuration */
/* SBCS fixed code page */
/* OEM <==> Unicode Conversions for Static Code Page Configuration with */
/* SBCS Fixed Code Page */
/*------------------------------------------------------------------------*/
#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900
@ -15267,8 +15266,8 @@ WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
/*------------------------------------------------------------------------*/
/* OEM <==> Unicode conversions for static code page configuration */
/* DBCS fixed code page */
/* OEM <==> Unicode Conversions for Static Code Page Configuration with */
/* DBCS Fixed Code Page */
/*------------------------------------------------------------------------*/
#if FF_CODE_PAGE >= 900
@ -15346,7 +15345,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
/*------------------------------------------------------------------------*/
/* OEM <==> Unicode conversions for dynamic code page configuration */
/* OEM <==> Unicode Conversions for Dynamic Code Page Configuration */
/*------------------------------------------------------------------------*/
#if FF_CODE_PAGE == 0
@ -15458,7 +15457,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
/*------------------------------------------------------------------------*/
/* Unicode up-case conversion */
/* Unicode Up-case Conversion */
/*------------------------------------------------------------------------*/
DWORD ff_wtoupper ( /* Returns up-converted code point */
@ -15590,4 +15589,4 @@ DWORD ff_wtoupper ( /* Returns up-converted code point */
}
#endif /* #if FF_USE_LFN */
#endif /* #if FF_USE_LFN != 0 */

View File

@ -26,7 +26,7 @@ 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_PSIZE_1;
FLASH->CR |= FLASH_CR_MER;
FLASH->CR |= FLASH_CR_STRT;

View File

@ -1,8 +1,8 @@
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem module R0.14 /
/ FatFs - Generic FAT Filesystem module R0.15 /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 2019, ChaN, all right reserved.
/ Copyright (C) 2022, 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 80286 /* Revision ID */
#ifdef __cplusplus
extern "C" {
@ -35,10 +35,14 @@ extern "C" {
/* Integer types used for FatFs API */
#if defined(_WIN32) /* Main development platform */
#if defined(_WIN32) /* Windows VC++ (for development only) */
#define FF_INTDEF 2
#include <windows.h>
typedef unsigned __int64 QWORD;
#include <float.h>
#define isnan(v) _isnan(v)
#define isinf(v) (!_finite(v))
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
#define FF_INTDEF 2
#include <stdint.h>
@ -48,6 +52,7 @@ typedef uint16_t WORD; /* 16-bit unsigned integer */
typedef uint32_t DWORD; /* 32-bit unsigned integer */
typedef uint64_t QWORD; /* 64-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#else /* Earlier than C99 */
#define FF_INTDEF 1
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
@ -58,53 +63,6 @@ typedef WORD WCHAR; /* UTF-16 character type */
#endif
/* Definitions of volume management */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
#endif
/* Type of path name strings on FatFs API */
#ifndef _INC_TCHAR
#define _INC_TCHAR
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
#endif
/* Type of file size and LBA variables */
#if FF_FS_EXFAT
@ -127,14 +85,57 @@ typedef DWORD LBA_t;
/* Type of path name strings on FatFs API (TCHAR) */
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
/* Definitions of volume management */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
#endif
/* Filesystem object structure (FATFS) */
typedef struct {
BYTE fs_type; /* Filesystem type (0:not mounted) */
BYTE pdrv; /* Associated physical drive */
BYTE pdrv; /* Volume hosting physical drive */
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] flag (b0:dirty) */
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
BYTE wflag; /* win[] status (b0:dirty) */
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
WORD id; /* Volume mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */
@ -147,9 +148,6 @@ typedef struct {
#if FF_FS_EXFAT
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
#endif
#if FF_FS_REENTRANT
FF_SYNC_t sobj; /* Identifier of sync object */
#endif
#if !FF_FS_READONLY
DWORD last_clst; /* Last allocated cluster */
DWORD free_clst; /* Number of free clusters */
@ -163,10 +161,10 @@ typedef struct {
#endif
#endif
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Size of an FAT [sectors] */
DWORD fsize; /* Number of sectors per FAT */
LBA_t volbase; /* Volume base sector */
LBA_t fatbase; /* FAT base sector */
LBA_t dirbase; /* Root directory base sector/cluster */
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
LBA_t database; /* Data base sector */
#if FF_FS_EXFAT
LBA_t bitbase; /* Allocation bitmap base sector */
@ -181,7 +179,7 @@ typedef struct {
typedef struct {
FATFS* fs; /* Pointer to the hosting volume of this object */
WORD id; /* Hosting volume mount ID */
WORD id; /* Hosting volume's mount ID */
BYTE attr; /* Object attribute */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
@ -250,7 +248,7 @@ typedef struct {
WORD ftime; /* Modified time */
BYTE fattrib; /* File attribute */
#if FF_USE_LFN
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
#else
TCHAR fname[12 + 1]; /* File name */
@ -298,8 +296,10 @@ typedef enum {
/*--------------------------------------------------------------*/
/* FatFs Module Application Interface */
/*--------------------------------------------------------------*/
/* FatFs module application interface */
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_close (FIL* fp); /* Close an open file object */
@ -336,6 +336,8 @@ int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
/* Some API fucntions are implemented as macro */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr)
@ -345,46 +347,47 @@ 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
/*--------------------------------------------------------------*/
/* Additional user defined functions */
/* Additional Functions */
/*--------------------------------------------------------------*/
/* RTC function */
/* RTC function (provided by user) */
#if !FF_FS_READONLY && !FF_FS_NORTC
DWORD get_fattime (void);
DWORD get_fattime (void); /* Get current time */
#endif
/* LFN support functions */
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
/* LFN support functions (defined in ffunicode.c) */
#if FF_USE_LFN >= 1
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
#endif
/* O/S dependent functions (samples available in ffsystem.c) */
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */
#endif
/* Sync functions */
#if FF_FS_REENTRANT
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
#if FF_FS_REENTRANT /* Sync functions */
int ff_mutex_create (int vol); /* Create a sync object */
void ff_mutex_delete (int vol); /* Delete a sync object */
int ff_mutex_take (int vol); /* Lock sync object */
void ff_mutex_give (int vol); /* Unlock sync object */
#endif
/*--------------------------------------------------------------*/
/* Flags and offset address */
/* Flags and Offset Address */
/*--------------------------------------------------------------*/
/* File access mode and open method flags (3rd argument of f_open) */
#define FA_READ 0x01

View File

@ -1,8 +1,8 @@
/*---------------------------------------------------------------------------/
/ FatFs Functional Configurations
/ Configurations of FatFs Module
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 86606 /* Revision ID */
#define FFCONF_DEF 80286 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
@ -25,15 +25,7 @@
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_STRFUNC 1
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define FF_USE_FIND 0
#define FF_USE_FIND 1
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
@ -64,6 +56,30 @@
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
#define FF_USE_STRFUNC 1
#define FF_PRINT_LLI 0
#define FF_PRINT_FLOAT 0
#define FF_STRF_ENCODE 0
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion.
/
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
/ makes f_printf() support floating point argument. These features want C99 or later.
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/ to be read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
@ -137,19 +153,6 @@
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_STRF_ENCODE 3
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
/ This option selects assumption of character encoding ON THE FILE to be
/ read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
#define FF_FS_RPATH 0
/* This option configures support for relative path.
/
@ -175,7 +178,7 @@
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/ not defined, a user defined volume string table is needed as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
@ -187,14 +190,14 @@
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this function is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
/ function will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/ for variable sector size mode and disk_ioctl() function needs to implement
/ GET_SECTOR_SIZE command. */
@ -205,8 +208,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. */
@ -228,7 +231,7 @@
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 0
#define FF_FS_EXFAT 1
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
@ -237,10 +240,10 @@
#define FF_FS_NORTC 0
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2019
/* 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
#define FF_NORTC_YEAR 2022
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
@ -250,7 +253,7 @@
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ option, and f_getfree() function at the first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
@ -272,26 +275,21 @@
/ lock control is independent of re-entrancy. */
/* #include <somertos.h> // O/S definitions */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
#define FF_SYNC_t HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/ to the same volume is under control of this featuer.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
/ function, must be added to the project. Samples are available in ffsystem.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
*/

View File

@ -6,7 +6,7 @@
void uart_init(void)
{
SHELL_UART_RCC_REG |= SHELL_UART_RCC_MASK;
SHELL_UART_PERIPH->BRR = SHELL_UART_BRR_REG_VALUE;
SHELL_UART_PERIPH->BRR = SHELL_UART_DEFAULT_BRR_REG_VALUE;
SHELL_UART_PERIPH->CR2 = 0;
SHELL_UART_PERIPH->CR3 = 0;
SHELL_UART_PERIPH->CR1 = USART_CR1_TE | USART_CR1_UE;

27
stm-firmware/version.c Normal file
View File

@ -0,0 +1,27 @@
/* Reflow Oven Controller
*
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/version.h>
#include <generated-version/version.h>
const char *version_git_version_string = GIT_VERSION_STRING;
const char *version_git_full_commit_string = GIT_FULL_COMMIT;
const char *version_compile_date = __DATE__;
const char *version_compile_time = __TIME__;