45 Commits

Author SHA1 Message Date
a7c91cee0e GUI: About menu: Add '[DEBUG]' text if code is build in debug mode 2020-06-15 22:00:47 +02:00
acbde24c2f Makefile: Add program-debug option for flashing debug elf 2020-06-15 21:57:36 +02:00
47d8df052a Makefile: Make a separate memory mapfile for each build configuration (release or debug) 2020-06-15 21:48:37 +02:00
3705cc09d1 SDIO driver: Fix minor issues in SDIO driver 2020-06-15 21:31:49 +02:00
ecd8d2537d Append -release to release output elf 2020-06-15 21:24:27 +02:00
56439a3b13 Makefile: Add differnet targets for debug and release builds 2020-06-15 21:22:05 +02:00
137e846cf2 Move temperature file specification and implementation to own repository and add submodule 2020-06-15 18:39:12 +02:00
78417e0c8c Progress in error handling 2020-06-14 23:36:49 +02:00
62a3e06baa Fixx style problems and design errors in main.c 2020-06-14 23:22:35 +02:00
485b887b54 fix too long lines 2020-06-14 22:56:34 +02:00
fe75b93ec7 Fix logical or operator 2020-06-14 20:26:32 +02:00
ab157bfb5a Fix empty line 2020-06-14 19:15:02 +02:00
f0bf10d91d use correct LED for error blinking 2020-06-14 19:13:50 +02:00
cbd28f9a12 Add safety management in PID handler and main loop 2020-06-14 19:10:09 +02:00
a33154b2d0 Move shadow buffer of LCD to CCMRAM 2020-06-14 18:02:45 +02:00
828b47f3be Fix error in power handling of LCD FSM 2020-06-14 17:52:27 +02:00
7b426c93c9 About GUI menu: Improve rotary handling 2020-06-14 16:26:32 +02:00
20fd7b41e6 Edit about menu: Add new entry for serial number and compile info 2020-06-14 16:19:42 +02:00
f60545f664 make convenience pointer to global structs const 2020-06-14 14:50:27 +02:00
679d4534cb Add formatted prinbt function for LCD menu and use systick vonversion in uptime shell command 2020-06-14 14:45:58 +02:00
372be53471 make uptime in LCD UI use systick function for converting uptime 2020-06-14 14:16:12 +02:00
0cdc7448e4 Systick: Add function to calculate uptime in days, hours, minutes, and seconds from global tick 2020-06-14 13:35:47 +02:00
43b4fd1e77 Changes for menu
* Make Systick a 100us Timer. Millisecond ticks are still untouched.
* LCD now has a 100us resolution tick
* LCD uses 500us delay for waitstate
* Make 'About' menu verbose:
	* Add 3 page menu
	* 1st page: Generic info
	* 2nd page: Version info
	* 3rd page: Uptime in seconds
2020-06-14 13:25:47 +02:00
d178910594 Fix last change to make controller WFI in LCD_FMS_NOP state 2020-06-14 01:34:42 +02:00
6f4363e021 Change return value of reflow_menu_handle 2020-06-14 01:31:44 +02:00
0fca4c6c20 LCD Timing changes 2020-06-14 01:04:21 +02:00
7595e6ced8 Fix smaller code mistake in LCD FSM 2020-06-13 23:37:04 +02:00
2547c134f2 Add PID controller to oven driver module 2020-06-13 23:23:59 +02:00
a6dc4f9b46 Add about command to menu and restructure code. Delete preliminary code from mainloop. Better code will follow 2020-06-13 22:47:45 +02:00
e627cb67a5 fix smaller bugs in Menu code and implement first test of main menu with one functional sunbmenu for the safety parameters 2020-06-12 01:35:37 +02:00
d6e489bb61 Add defines for special LCD characters 2020-06-11 23:58:51 +02:00
3b2d8c14c3 Change division to multiplication in floating point operation 2020-06-09 23:01:04 +02:00
9f0d81cc76 Merge branch 'dev' into ui 2020-06-09 22:59:20 +02:00
b9c1968dc4 Merge branch 'master' into dev 2020-06-09 22:58:56 +02:00
7553cfa310 Add qtproejct target to Makefile which generates a usable qtcreator project folder 2020-06-09 22:58:17 +02:00
af04da6eca update to new shellmatta 2020-06-09 22:55:53 +02:00
949d16cd03 Add display buffer to ccm ram 2020-06-09 22:53:13 +02:00
917497e7e4 implement display update function for lcd menu 2020-06-09 22:50:20 +02:00
7db5f02067 implemnt scrollable menu field. Not yet tested 2020-06-09 22:43:00 +02:00
3c3715effa Merge branch 'dev' into ui 2020-06-09 21:56:02 +02:00
a016681d08 Merge branch 'dev' of git.shimatta.de:mhu/reflow-oven-control-sw into dev 2020-06-09 21:52:17 +02:00
5fc4220ecf Add qtproejct target to Makefile which generates a usable qtcreator project folder 2020-06-09 21:51:33 +02:00
70730fd0f0 Add qtproejct target to Makefile which generates a usable qtcreator project folder 2020-06-09 21:50:37 +02:00
c63986e271 Add further testing code for rotary encoder and lcd 2020-06-09 19:03:33 +02:00
9615fdb39d Further menu implementations 2020-06-04 21:53:00 +02:00
28 changed files with 1002 additions and 668 deletions

4
.gitmodules vendored
View File

@@ -5,3 +5,7 @@
path = c-style-checker path = c-style-checker
url = https://git.shimatta.de/mhu/c-style-checker.git url = https://git.shimatta.de/mhu/c-style-checker.git
branch = master branch = master
[submodule "reflow-controller-temp-profile-lang"]
path = reflow-controller-temp-profile-lang
url = https://git.shimatta.de/mhu/reflow-controller-temp-profile-lang.git
branch = master

View File

@@ -5,15 +5,15 @@
#Add Files and Folders below######################################################### #Add Files and Folders below#########################################################
CFILES = main.c syscalls.c setup/system_stm32f4xx.c systick.c CFILES = main.c syscalls.c setup/system_stm32f4xx.c systick.c
ASFILES = boot/startup_stm32f4xx.S ASFILES = boot/startup_stm32f4xx.S
INCLUDEPATH = -Icmsis -Iinclude INCLUDEPATH = -Iinclude
OBJDIR = obj OBJDIR_BASE = obj
target = reflow-controller TARGET_BASE = reflow-controller
LIBRARYPATH = -L. -Lmathlib LIBRARYPATH = -L. -Lmathlib
LIBRARIES = -larm_cortexM4lf_math -lm LIBRARIES = -larm_cortexM4lf_math -lm
DEFINES = -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4 -DHSE_VALUE=8000000UL DEFINES = -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4 -DHSE_VALUE=8000000UL
mapfile = memory-mapping MAPFILE_BASE = memory-mapping
export GIT_VER = $(shell git describe --always --dirty --tags) export GIT_VER = $(shell git describe --always --dirty --tags)
DEFINES += -DGIT_VER=$(GIT_VER) DEFINES += -DGIT_VER=$(GIT_VER)
@@ -41,28 +41,59 @@ CFILES += calibration.c
CFILES += temp-converter.c CFILES += temp-converter.c
CFILES += rotary-encoder.c button.c CFILES += rotary-encoder.c button.c
CFILES += stack-check.c CFILES += stack-check.c
CFILES += ui/lcd.c ui/menu.c lcd-menu.c CFILES += ui/lcd.c ui/menu.c reflow-menu.c
#CFILES += onewire-temp-sensors.c #CFILES += onewire-temp-sensors.c
CFILES += fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c fatfs/shimatta_sdio_driver/shimatta_sdio.c CFILES += fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c fatfs/shimatta_sdio_driver/shimatta_sdio.c
CFILES += pid-controller.c oven-driver.c CFILES += pid-controller.c oven-driver.c
CFILES += settings/settings.c settings/settings-sd-card.c CFILES += settings/settings.c settings/settings-sd-card.c
CFILES += safety-adc.c CFILES += safety-adc.c
DEFINES += -DDEBUGBUILD DEBUG_DEFINES = -DDEBUGBUILD
RELEASE_DEFINES =
################################################################################### ###################################################################################
CC=arm-none-eabi-gcc ifeq ($(CROSS_COMPILE),)
OBJCOPY=arm-none-eabi-objcopy CROSS_COMPILE=arm-none-eabi-
OBJDUMP=arm-none-eabi-objdump endif
SIZE=arm-none-eabi-size
LFLAGS = -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork CC=$(CROSS_COMPILE)gcc
OBJCOPY=$(CROSS_COMPILE)objcopy
OBJDUMP=$(CROSS_COMPILE)objdump
SIZE=$(CROSS_COMPILE)size
CFLAGS_RELEASE = -O3 -g
CFLAGS_DEBUG = -O0 -g
LFLAGS_RELEASE = -Wl,--gc-sections
LFLAGS_DEBUG =
CFLAGS =
LFLAGS =
ifneq ($(DEBUGBUILD),true)
DEFINES += $(RELEASE_DEFINES)
CFLAGS += $(CFLAGS_RELEASE)
LFLAGS += $(LFLAGS_RELEASE)
target = $(TARGET_BASE)-release
OBJDIR = $(OBJDIR_BASE)/release
MAPFILE = $(MAPFILE_BASE)-release
else
DEFINES += $(DEBUG_DEFINES)
target = $(TARGET_BASE)-debug
CFLAGS += $(CFLAGS_DEBUG)
LFLAGS += $(LFLAGS_DEBUG)
OBJDIR = $(OBJDIR_BASE)/debug
MAPFILE = $(MAPFILE_BASE)-debug
endif
LFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
LFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles LFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles
LFLAGS += -Tstm32f407vet6_flash.ld -Wl,-Map=$(mapfile).map LFLAGS += -Tstm32f407vet6_flash.ld -Wl,-Map=$(MAPFILE).map
CFLAGS = -c -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork CFLAGS += -c -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles -O0 -g CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles
CFLAGS += -Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter -Wimplicit-fallthrough=3 -Wsign-compare CFLAGS += -Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter -Wimplicit-fallthrough=3 -Wsign-compare
#################################################################################### ####################################################################################
OBJ = $(CFILES:%.c=$(OBJDIR)/%.c.o) OBJ = $(CFILES:%.c=$(OBJDIR)/%.c.o)
@@ -70,6 +101,14 @@ ASOBJ += $(ASFILES:%.S=$(OBJDIR)/%.S.o)
default: $(target).elf default: $(target).elf
all: debug release
release:
$(QUIET)$(MAKE) DEBUGBUILD=false
debug:
$(QUIET)$(MAKE) DEBUGBUILD=true
%.bin: %.elf %.bin: %.elf
$(QUIET)$(OBJCOPY) -O binary $^ $@ $(QUIET)$(OBJCOPY) -O binary $^ $@
%.hex: %.elf %.hex: %.elf
@@ -95,7 +134,10 @@ $(ASOBJ):
$(QUIET)$(CC) $(CFLAGS) -MMD -MT $@ $(INCLUDEPATH) $(DEFINES) -o $@ $(@:$(OBJDIR)/%.S.o=%.S) $(QUIET)$(CC) $(CFLAGS) -MMD -MT $@ $(INCLUDEPATH) $(DEFINES) -o $@ $(@:$(OBJDIR)/%.S.o=%.S)
.PHONY: qtproject clean mrproper objcopy disassemble program .PHONY: qtproject-legacy qtproject qtproject-debug clean mrproper objcopy disassemble program program-debug
program-debug:
$(QUIET)$(MAKE) DEBUGBUILD=true program
program: $(target).elf program: $(target).elf
./program-device.sh $< ./program-device.sh $<
@@ -106,14 +148,23 @@ disassemble: $(target).elf
objcopy: $(target).bin $(target).hex objcopy: $(target).bin $(target).hex
mrproper: clean mrproper: clean
$(QUIET)rm -f $(target).pro @echo "Purging project files..."
$(QUIET)rm -f $(target).pro $(target).creator $(target).files $(target).cflags $(target).cxxflags $(target).includes $(target).config
clean: clean:
@echo "Cleaning up derived files..." @echo -n "Cleaning up derived files for "
ifneq ($(DEBUGBUILD),true)
@echo "RELEASE build"
else
@echo "DEBUG build"
endif
$(QUIET)rm -f $(target).elf $(target).bin $(target).hex $(OBJ) $(ASOBJ) $(mapfile).map $(CFILES:%.c=$(OBJDIR)/%.c.d) $(ASFILES:%.S=$(OBJDIR)/%.S.d) $(QUIET)rm -f $(target).elf $(target).bin $(target).hex $(OBJ) $(ASOBJ) $(mapfile).map $(CFILES:%.c=$(OBJDIR)/%.c.d) $(ASFILES:%.S=$(OBJDIR)/%.S.d)
$(QUIET)rm -rf $(OBJDIR)/* $(QUIET)rm -rf $(OBJDIR)/*
ifneq ($(DEBUGBUILD),true)
$(QUIET)$(MAKE) DEBUGBUILD=true clean
endif
qtproject: qtproject-legacy:
echo -e "TEMPLATE = app\nCONFIG -= console app_bundle qt" > $(target).pro echo -e "TEMPLATE = app\nCONFIG -= console app_bundle qt" > $(target).pro
echo -e "SOURCES += $(CFILES) $(ASFILES)" >> $(target).pro echo -e "SOURCES += $(CFILES) $(ASFILES)" >> $(target).pro
echo -ne "INCLUDEPATH += " >> $(target).pro echo -ne "INCLUDEPATH += " >> $(target).pro
@@ -123,4 +174,26 @@ qtproject:
echo -ne "\nDEFINES += " >> $(target).pro echo -ne "\nDEFINES += " >> $(target).pro
echo "$(DEFINES)" | sed "s/-D//g" >> $(target).pro echo "$(DEFINES)" | sed "s/-D//g" >> $(target).pro
qtproject-debug:
@echo "Generating debug build project"
$(QUIET)$(MAKE) DEBUGBUILD=true qtproject
qtproject:
$(QUIET)rm -f $(target).files $(target).cflags $(target).config $(target).creator $(target).includes $(target).creator.user
@echo "Generating source file list"
$(QUIET)echo "$(CFILES)" | tr ' ' '\n' > $(target).files
@echo -n "Appending found header files from folders "
@echo `echo $(INCLUDEPATH) | sed "s/-I//g"`
$(QUIET)for dir in `echo $(INCLUDEPATH) | sed "s/-I//g"`; do \
find `echo "$${dir}"` -name "*.h" >> $(target).files; \
done
@echo "Generating $(target).cflags"
$(QUIET)echo "" > $(target).cflags
@echo "Generating $(target).includes"
$(QUIET)echo $(INCLUDEPATH) | sed "s/-I/,/g" | tr , '\n' | sed "/^$$/d" > $(target).includes;
@echo "Generating $(target).config"
$(QUIET)echo $(DEFINES) | sed "s/-D/,#define /g" | tr , '\n' | sed "/^$$/d" > $(target).config
@echo "Generating $(target).creator"
$(QUIET)echo "[GENERAL]" > $(target).creator
-include $(CFILES:%.c=$(OBJDIR)/%.c.d) $(ASFILES:%.S=$(OBJDIR)/%.S.d) -include $(CFILES:%.c=$(OBJDIR)/%.c.d) $(ASFILES:%.S=$(OBJDIR)/%.S.d)

View File

@@ -238,6 +238,8 @@ static int sdio_check_status_register_cmd13(uint16_t rca, uint32_t *status)
uint32_t response; uint32_t response;
int res; int res;
*status = 0UL;
do { do {
sdio_send_cmd(13, (rca<<16)&0xFFFF0000, SHORT_ANS); sdio_send_cmd(13, (rca<<16)&0xFFFF0000, SHORT_ANS);
if (!(res = sdio_get_response(13, SHORT_ANS, &response))) { if (!(res = sdio_get_response(13, SHORT_ANS, &response))) {
@@ -465,10 +467,14 @@ static int sdio_send_select_card_cmd7(uint16_t rca) {
} while(--timeout > 0); } while(--timeout > 0);
/* Check, if card in in TRANS state */ /* Check, if card in in TRANS state */
if (sdio_check_status_register_cmd13(rca, &(status.value))) if (sdio_check_status_register_cmd13(rca, &status.value)) {
res = -1; res = -1;
goto ret_val;
}
if (status.statusstruct.CURRENT_STATE != CURRENT_STATE_TRAN) if (status.statusstruct.CURRENT_STATE != CURRENT_STATE_TRAN)
res = -2; res = -2;
ret_val:
return res; return res;
} }
@@ -705,9 +711,10 @@ DRESULT sdio_disk_write(const BYTE *buff, DWORD sector, UINT count)
while (count) { while (count) {
do { do {
sdio_check_status_register_cmd13(card_info.rca, &status.value); ret = sdio_check_status_register_cmd13(card_info.rca, &status.value);
} while (status.statusstruct.CURRENT_STATE == CURRENT_STATE_PRG || } while (status.statusstruct.CURRENT_STATE == CURRENT_STATE_PRG ||
status.statusstruct.CURRENT_STATE == CURRENT_STATE_RCV); status.statusstruct.CURRENT_STATE == CURRENT_STATE_RCV ||
!ret);
if (status.statusstruct.CURRENT_STATE == CURRENT_STATE_STBY) { if (status.statusstruct.CURRENT_STATE == CURRENT_STATE_STBY) {
if (sdio_send_select_card_cmd7(card_info.rca)) if (sdio_send_select_card_cmd7(card_info.rca))

View File

@@ -22,6 +22,35 @@
#define __OVEN_DRIVER_H__ #define __OVEN_DRIVER_H__
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <reflow-controller/pid-controller.h>
enum oven_pid_error_report {
OVEN_PID_NO_ERROR = 0,
OVEN_PID_ERR_PT1000_ADC_WATCHDOG = (1<<0),
OVEN_PID_ERR_PT1000_ADC_OFF = (1<<1),
OVEN_PID_ERR_PT1000_OTHER = (1<<2),
OVEN_PID_ERR_VREF_TOL = (1<<3),
OVEN_PID_ERR_OVERTEMP = (1<<4),
};
struct oven_pid_errors {
bool generic_error;
bool pt1000_adc_watchdog;
bool pt1000_adc_off;
bool pt1000_other;
bool vref_tol;
bool controller_overtemp;
};
struct oven_pid_status {
bool active;
bool error_set;
struct oven_pid_errors error_flags;
float target_temp;
float current_temp;
uint64_t timestamp_last_run;
};
void oven_driver_init(void); void oven_driver_init(void);
@@ -29,4 +58,16 @@ void oven_driver_set_power(uint8_t power);
void oven_driver_disable(void); void oven_driver_disable(void);
void oven_pid_ack_errors(void);
void oven_pid_init(struct pid_controller *controller_to_copy);
void oven_pid_handle(float target_temp);
void oven_pid_stop();
void oven_pid_report_error(enum oven_pid_error_report report);
const struct oven_pid_status *oven_pid_get_status(void);
#endif /* __OVEN_DRIVER_H__ */ #endif /* __OVEN_DRIVER_H__ */

View File

@@ -45,4 +45,6 @@ float pid_sample(struct pid_controller *pid, float deviation);
float pid_get_control_output(const struct pid_controller *pid); float pid_get_control_output(const struct pid_controller *pid);
int pid_copy(struct pid_controller *dest, const struct pid_controller *src);
#endif /* __PID_CONTROLLER_H__ */ #endif /* __PID_CONTROLLER_H__ */

View File

@@ -18,7 +18,15 @@
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __LCD_MENU_H__ #ifndef __REFLOW_MENU_H__
#define __LCD_MENU_H__ #define __REFLOW_MENU_H__
#endif /* __LCD_MENU_H__ */ /**
* @brief Handle the reflow controller's LCD Menu
* @return 0 if no delay is requested, 1 if delay is requested
*/
int reflow_menu_handle(void);
void reflow_menu_init(void);
#endif /* __REFLOW_MENU_H__ */

View File

@@ -39,4 +39,6 @@ int32_t rotary_encoder_get_change_val(void);
void rotary_encoder_stop(void); void rotary_encoder_stop(void);
void rotary_encoder_zero(void);
#endif /* __ROTARY_ENCODER_H__ */ #endif /* __ROTARY_ENCODER_H__ */

View File

@@ -24,11 +24,10 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#define SAFETY_ADC_FRAC_BITS (8) #define SAFETY_ADC_VREF_MVOLT (2500.0f)
#define SAFETY_ADC_VREF_VOLT (2.5) #define SAFETY_ADC_VREF_TOL_MVOLT (100.0f)
#define SAFETY_ADC_VREF_TOL (0.25) #define SAFETY_ADC_TEMP_LOW_LIM (0.0f)
#define SAFETY_ADC_VREF_INT () #define SAFETY_ADC_TEMP_HIGH_LIM (65.0f)
enum safety_adc_meas_channel {SAFETY_ADC_MEAS_VREF, SAFETY_ADC_MEAS_TEMP}; enum safety_adc_meas_channel {SAFETY_ADC_MEAS_VREF, SAFETY_ADC_MEAS_TEMP};
enum safety_adc_check_result { enum safety_adc_check_result {
@@ -40,6 +39,12 @@ enum safety_adc_check_result {
SAFETY_ADC_INTERNAL_ERROR = (1U<<4), SAFETY_ADC_INTERNAL_ERROR = (1U<<4),
}; };
extern enum safety_adc_check_result global_safety_adc_status;
enum safety_adc_check_result safety_adc_get_errors();
void safety_adc_clear_errors(void);
void safety_adc_init(); void safety_adc_init();
void safety_adc_deinit(); void safety_adc_deinit();

View File

@@ -32,9 +32,9 @@
* @brief Reload value for the systick timer. * @brief Reload value for the systick timer.
* *
* This value has to be configured to set the systick to a one milliscond tick interval * This value has to be configured to set the systick to a one milliscond tick interval
* The default value is 168000, which results in a 1ms tick for 168 MHz CPU speed * The default value is 16800, which results in a 100us tick for 168 MHz CPU speed
*/ */
#define SYSTICK_RELOAD (168000UL) #define SYSTICK_RELOAD (16800UL)
/** /**
* @brief Variable used by the systick_wait_ms function * @brief Variable used by the systick_wait_ms function
@@ -49,6 +49,11 @@ extern volatile uint32_t wait_tick_ms;
*/ */
extern volatile uint64_t global_tick_ms; extern volatile uint64_t global_tick_ms;
/**
* @brief Wait counter for the display. This must not be used anywhere else
*/
extern volatile uint32_t lcd_tick_100us;
/** /**
* @brief Setup the Systick timer to generate a 1 ms tick * @brief Setup the Systick timer to generate a 1 ms tick
*/ */
@@ -66,6 +71,8 @@ void systick_wait_ms(uint32_t ms);
uint64_t systick_get_global_tick(); uint64_t systick_get_global_tick();
void systick_get_uptime_from_tick(uint32_t *days, uint32_t *hours, uint32_t *minutes, uint32_t *seconds);
bool systick_ticks_have_passed(uint64_t start_timestamp, uint64_t ticks); bool systick_ticks_have_passed(uint64_t start_timestamp, uint64_t ticks);
#endif /* __SYSTICK_H__ */ #endif /* __SYSTICK_H__ */

View File

@@ -36,6 +36,10 @@
#define LCD_CHAR_WIDTH 16 #define LCD_CHAR_WIDTH 16
#define LCD_ROW_COUNT 4 #define LCD_ROW_COUNT 4
#define LCD_SHIMATTA_STRING "\xBC\xCF\xAF\xC0"
#define LCD_DEGREE_SYMBOL_STRING "\xDF"
#define LCD_DEGREE_SYMBOL_CHAR '\xDF'
enum lcd_fsm_ret {LCD_FSM_NOP, LCD_FSM_CALL_AGAIN, LCD_FSM_WAIT_CALL}; enum lcd_fsm_ret {LCD_FSM_NOP, LCD_FSM_CALL_AGAIN, LCD_FSM_WAIT_CALL};
void lcd_init(void); void lcd_init(void);

View File

@@ -47,21 +47,41 @@ struct lcd_menu {
}; };
struct menu_list { struct menu_list {
const char **entry_names; void (*update_display)(uint8_t row, const char *data);
const char * const * entry_names;
uint32_t entry_count;
uint32_t currently_selected; uint32_t currently_selected;
const menu_func_t *submenu_list; const menu_func_t *submenu_list;
}; };
void menu_handle(struct lcd_menu *menu, uint16_t rotary_encoder_delta, enum button_state push_button); void menu_handle(struct lcd_menu *menu, int16_t rotary_encoder_delta, enum button_state push_button);
void menu_init(struct lcd_menu *menu, menu_func_t root_node, void (*display_update)(uint8_t row, const char *data)); void menu_init(struct lcd_menu *menu, menu_func_t root_node, void (*display_update)(uint8_t row, const char *data));
void menu_ack_rotary_delta(struct lcd_menu *menu);
void menu_display_clear(struct lcd_menu *menu);
void menu_entry_dropback(struct lcd_menu *menu, menu_func_t parent_func); void menu_entry_dropback(struct lcd_menu *menu, menu_func_t parent_func);
void menu_entry_enter(struct lcd_menu *menu, menu_func_t parent_func, bool handle_immediately); void menu_entry_enter(struct lcd_menu *menu, menu_func_t entry, bool handle_immediately);
void menu_override_lcd_output(struct lcd_menu *menu, uint8_t row_num, const char *text); void menu_lcd_output(struct lcd_menu *menu, uint8_t row_num, const char *text);
void menu_list_display(struct menu_list *list, uint8_t top_row, uint8_t bottom_row); void menu_lcd_outputf(struct lcd_menu *menu, uint8_t row_num, const char *format, ...);
void menu_list_display(struct menu_list *list, uint32_t top_row, uint32_t bottom_row);
int16_t menu_get_rotary_delta(const struct lcd_menu *menu);
enum button_state menu_get_button_state(const struct lcd_menu *menu);
void menu_list_compute_count(struct menu_list *list);
void menu_list_scroll_down(struct menu_list *list);
void menu_list_scroll_up(struct menu_list *list);
void menu_list_enter_selected_entry(struct menu_list *list, struct lcd_menu *menu);
#endif /* __MENU_H__ */ #endif /* __MENU_H__ */

View File

@@ -1,21 +0,0 @@
/* 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.
*
* 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/lcd-menu.h>

View File

@@ -34,23 +34,20 @@
#include <reflow-controller/systick.h> #include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h> #include <reflow-controller/adc-meas.h>
#include <reflow-controller/shell.h> #include <reflow-controller/shell.h>
#include <reflow-controller/ui/lcd.h>
#include <reflow-controller/digio.h> #include <reflow-controller/digio.h>
#include "fatfs/shimatta_sdio_driver/shimatta_sdio.h" #include "fatfs/shimatta_sdio_driver/shimatta_sdio.h"
#include <reflow-controller/temp-converter.h>
#include <reflow-controller/rotary-encoder.h>
#include <reflow-controller/pid-controller.h>
#include <stm-periph/stm32-gpio-macros.h> #include <stm-periph/stm32-gpio-macros.h>
#include <stm-periph/clock-enable-manager.h> #include <stm-periph/clock-enable-manager.h>
#include <stm-periph/uart.h> #include <stm-periph/uart.h>
#include <reflow-controller/shell-uart-config.h> #include <reflow-controller/shell-uart-config.h>
#include <helper-macros/helper-macros.h>
#include <reflow-controller/button.h>
#include <reflow-controller/oven-driver.h> #include <reflow-controller/oven-driver.h>
#include <reflow-controller/safety-adc.h> #include <reflow-controller/safety-adc.h>
#include <fatfs/ff.h> #include <fatfs/ff.h>
#include <reflow-controller/reflow-menu.h>
static void setup_nvic_priorities() bool global_error_state;
static void setup_nvic_priorities(void)
{ {
/* No sub priorities */ /* No sub priorities */
NVIC_SetPriorityGrouping(2); NVIC_SetPriorityGrouping(2);
@@ -61,18 +58,10 @@ static void setup_nvic_priorities()
NVIC_SetPriority(DMA2_Stream7_IRQn, 3); NVIC_SetPriority(DMA2_Stream7_IRQn, 3);
} }
/* Process parameters are defined static globally to be watched in debugger from any context */
static float pt1000_value;
static volatile int pt1000_value_status;
static uint32_t rot;
static float target_temperature;
static struct pid_controller pid;
static volatile enum button_state button;
FATFS fs; FATFS fs;
FATFS *fs_ptr = &fs; FATFS * const fs_ptr = &fs;
static inline void uart_gpio_config() 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 * In case the application is build in debug mode, use the TX/RX Pins on the debug header
@@ -99,7 +88,7 @@ static shellmatta_retCode_t write_shell_callback(const char *data, uint32_t len)
return SHELLMATTA_OK; return SHELLMATTA_OK;
} }
static inline void setup_sell_uart(struct stm_uart *uart) static inline void setup_shell_uart(struct stm_uart *uart)
{ {
uart->rx = 1; uart->rx = 1;
uart->tx = 1; uart->tx = 1;
@@ -132,30 +121,16 @@ static bool mount_sd_card_if_avail(bool mounted)
if (!sdio_check_inserted() && !mounted) { if (!sdio_check_inserted() && !mounted) {
res = f_mount(fs_ptr, "0:/", 1); res = f_mount(fs_ptr, "0:/", 1);
if (res == FR_OK) { if (res == FR_OK)
return true; return true;
} else { else
return false; return false;
}
} }
return mounted; return mounted;
} }
static inline int32_t handle_pid_controller(struct pid_controller *pid, float target_temperature, static void setup_unused_pins(void)
float current_temperature)
{
int32_t pid_out;
pid_out = (int32_t)pid_sample(pid, target_temperature - current_temperature);
/* Blink green LED */
led_set(1, !led_get(1));
return pid_out;
}
static void setup_unused_pins()
{ {
int i; int i;
@@ -165,7 +140,7 @@ static void setup_unused_pins()
GPIOE->PUPDR |= PULLDOWN(i); GPIOE->PUPDR |= PULLDOWN(i);
} }
static inline void setup_system() static inline void setup_system(void)
{ {
setup_nvic_priorities(); setup_nvic_priorities();
systick_setup(); systick_setup();
@@ -175,13 +150,11 @@ static inline void setup_system()
digio_setup_default_all(); digio_setup_default_all();
led_setup(); led_setup();
loudspeaker_setup(); loudspeaker_setup();
rotary_encoder_setup(); reflow_menu_init();
button_init();
lcd_init();
safety_adc_init(); safety_adc_init();
uart_gpio_config(); uart_gpio_config();
setup_sell_uart(&shell_uart); setup_shell_uart(&shell_uart);
setup_unused_pins(); setup_unused_pins();
} }
@@ -192,103 +165,101 @@ static void handle_shell_uart_input(shellmatta_handle_t shell_handle)
const char *uart_input; const char *uart_input;
size_t uart_input_len; size_t uart_input_len;
/* Handle uart input for shell */ /* Handle UART input for shell */
uart_receive_status = uart_receive_data_with_dma(&shell_uart, &uart_input, &uart_input_len); uart_receive_status = uart_receive_data_with_dma(&shell_uart, &uart_input, &uart_input_len);
if (uart_receive_status >= 0) if (uart_receive_status >= 0)
shell_handle_input(shell_handle, uart_input, uart_input_len); shell_handle_input(shell_handle, uart_input, uart_input_len);
} }
static void zero_ccm_ram(void)
{
/* These extern variables are placed in the linker script */
extern char _sccmram;
extern char _eccmram;
uint32_t len;
uint32_t i;
uint32_t *ptr = (uint32_t *)&_sccmram;
len = (uint32_t)&_eccmram - (uint32_t)&_sccmram;
for (i = 0; i < len; i++)
ptr[i] = 0UL;
}
int main() /**
* @brief This function sets the appropriate error flags in the oven PID controller
* depending on the Safety ADC measurements.
* The PID controller's error flags have to be cleared via the GUI by either starting a new RUN or explicitly
* ack'ing these errors.
*/
static void propagate_safety_adc_error_to_oven_pid(void)
{
enum safety_adc_check_result safety_adc_result;
safety_adc_result = safety_adc_get_errors();
if (safety_adc_result & SAFETY_ADC_CHECK_TEMP_LOW ||
safety_adc_result & SAFETY_ADC_CHECK_TEMP_HIGH)
oven_pid_report_error(OVEN_PID_ERR_OVERTEMP);
if (safety_adc_result & SAFETY_ADC_CHECK_VREF_LOW ||
safety_adc_result & SAFETY_ADC_CHECK_VREF_HIGH)
oven_pid_report_error(OVEN_PID_ERR_VREF_TOL);
if (safety_adc_result & SAFETY_ADC_INTERNAL_ERROR)
oven_pid_report_error(0);
}
int main(void)
{ {
bool sd_card_mounted = false; bool sd_card_mounted = false;
shellmatta_handle_t shell_handle; shellmatta_handle_t shell_handle;
int menu_wait_request;
uint64_t quarter_sec_timestamp = 0ULL;
const struct oven_pid_status *pid_status;
enum adc_pt1000_error pt1000_status;
uint64_t pid_timestamp = 0ULL; zero_ccm_ram();
bool pid_controller_active = false;
int32_t pid_controller_output;
uint64_t display_timestamp = 0ULL;
char disp[4][21] = {0};
enum lcd_fsm_ret lcd_ret = LCD_FSM_NOP;
int temp_status;
float current_temp;
target_temperature = 25.0f;
setup_system(); setup_system();
global_error_state = false;
shell_handle = shell_init(write_shell_callback); shell_handle = shell_init(write_shell_callback);
shell_print_motd(shell_handle); shell_print_motd(shell_handle);
pid_init(&pid, 0.1, 0.1, 4.0, 0.0, 100.0, 40.0, 0.25);
pid_zero(&pid);
while (1) { while (1) {
sd_card_mounted = mount_sd_card_if_avail(sd_card_mounted); sd_card_mounted = mount_sd_card_if_avail(sd_card_mounted);
snprintf(&disp[0][0], 17, "SD %smounted", sd_card_mounted ? "" : "un"); pid_status = oven_pid_get_status();
pt1000_value_status = adc_pt1000_get_current_resistance(&pt1000_value); if (systick_ticks_have_passed(quarter_sec_timestamp, 250)) {
quarter_sec_timestamp = systick_get_global_tick();
if (systick_ticks_have_passed(pid_timestamp, 250)) {
(void)handle_safety_adc(); (void)handle_safety_adc();
propagate_safety_adc_error_to_oven_pid();
pid_timestamp = systick_get_global_tick(); if (global_error_state)
temp_status = temp_converter_convert_resistance_to_temp(pt1000_value,
&current_temp);
if (pt1000_value_status >= 0 && pid_controller_active)
pid_controller_output = handle_pid_controller(&pid, target_temperature, current_temp);
/* Blink red led in case of temp error */
if (pt1000_value_status < 0)
led_set(0, !led_get(0)); led_set(0, !led_get(0));
else else
led_set(0, 0); led_set(0, 0);
snprintf(&disp[3][0], 17, "Temp: %s%.1f C", (temp_status == 0 ? "" : temp_status < 0 ? "<" : ">")
, current_temp);
} }
/* Handle error in case PID controller should be active, but temperature measurement failed */ pt1000_status = adc_pt1000_check_error();
if (pid_controller_active && pt1000_value_status < 0) { global_error_state = pid_status->error_set || !!safety_adc_get_errors() || !!pt1000_status;
/* Disable the oven controller */
menu_wait_request = reflow_menu_handle();
/* Deactivate oven output in case of error! */
if (!pid_status->active || global_error_state)
oven_driver_set_power(0U); oven_driver_set_power(0U);
/* Activate loundspeaker permanently */
loudspeaker_set(100);
} else if (pid_controller_active) {
/* In case temperature measurement is okay and controlelr is working, write output power */
oven_driver_set_power(pid_controller_output < 0 ? 0U : (uint8_t)pid_controller_output);
}
button = button_read_event();
rot = rotary_encoder_get_abs_val();
oven_driver_set_power(rot > 100U ? 100U : rot);
/* TODO: handle gui */
snprintf(&disp[1][0], 17, "Rotary: %u", (unsigned int)rot);
snprintf(&disp[2][0], 17, "Button: %s", (button == BUTTON_SHORT ? "SHORT" : (button == BUTTON_LONG ? "LONG" : "")));
handle_shell_uart_input(shell_handle); handle_shell_uart_input(shell_handle);
if (systick_ticks_have_passed(display_timestamp, 2) || lcd_ret == LCD_FSM_CALL_AGAIN) { if (menu_wait_request)
lcd_ret = lcd_fsm_write_buffer(disp);
display_timestamp = systick_get_global_tick();
}
if (lcd_ret == LCD_FSM_CALL_AGAIN) {
/* Nothing */
} else {
__WFI(); __WFI();
}
} }
return 0;
} }
void sdio_wait_ms(uint32_t ms) void sdio_wait_ms(uint32_t ms)
@@ -296,12 +267,12 @@ void sdio_wait_ms(uint32_t ms)
systick_wait_ms(ms); systick_wait_ms(ms);
} }
void DMA2_Stream7_IRQHandler() void DMA2_Stream7_IRQHandler(void)
{ {
uint32_t hisr = DMA2->HISR; uint32_t hisr = DMA2->HISR;
DMA2->HIFCR = hisr; DMA2->HIFCR = hisr;
if (hisr & DMA_HISR_TCIF7) { if (hisr & DMA_HISR_TCIF7)
uart_tx_dma_complete_int_callback(&shell_uart); uart_tx_dma_complete_int_callback(&shell_uart);
}
} }

View File

@@ -21,6 +21,19 @@
#include <reflow-controller/oven-driver.h> #include <reflow-controller/oven-driver.h>
#include <reflow-controller/periph-config/oven-driver-hwcfg.h> #include <reflow-controller/periph-config/oven-driver-hwcfg.h>
#include <stm-periph/clock-enable-manager.h> #include <stm-periph/clock-enable-manager.h>
#include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/temp-converter.h>
static struct pid_controller oven_pid;
static struct oven_pid_status oven_pid_current_status = {
.active = false,
.error_set = false,
.target_temp = 0.0f,
.current_temp = 0.0f,
.timestamp_last_run = 0ULL
};
void oven_driver_init() void oven_driver_init()
{ {
@@ -57,3 +70,94 @@ void oven_driver_disable()
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_PORT_RCC_MASK)); rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_PORT_RCC_MASK));
rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_TIM_RCC_MASK)); rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_TIM_RCC_MASK));
} }
void oven_pid_ack_errors(void)
{
oven_pid_current_status.error_set = false;
oven_pid_current_status.error_flags.vref_tol = false;
oven_pid_current_status.error_flags.pt1000_other = false;
oven_pid_current_status.error_flags.generic_error = false;
oven_pid_current_status.error_flags.pt1000_adc_off = false;
oven_pid_current_status.error_flags.controller_overtemp = false;
oven_pid_current_status.error_flags.pt1000_adc_watchdog = false;
}
void oven_pid_init(struct pid_controller *controller_to_copy)
{
pid_copy(&oven_pid, controller_to_copy);
oven_pid.output_sat_min = 0.0f;
oven_pid.output_sat_max = 100.0f;
oven_pid_current_status.timestamp_last_run = 0ULL;
oven_pid_current_status.active = true;
oven_pid_ack_errors();
}
void oven_pid_handle(float target_temp)
{
float pid_out;
float current_temp;
int resistance_status;
enum adc_pt1000_error pt1000_error;
if (oven_pid_current_status.active && !oven_pid_current_status.error_set) {
if (systick_ticks_have_passed(oven_pid_current_status.timestamp_last_run,
(uint64_t)(oven_pid.sample_period * 1000))) {
resistance_status = adc_pt1000_get_current_resistance(&current_temp);
if (resistance_status < 0) {
oven_driver_set_power(0);
pt1000_error = adc_pt1000_check_error();
if (pt1000_error & ADC_PT1000_WATCHDOG_ERROR)
oven_pid_report_error(OVEN_PID_ERR_PT1000_ADC_WATCHDOG);
if (pt1000_error & ADC_PT1000_INACTIVE)
oven_pid_report_error(OVEN_PID_ERR_PT1000_ADC_OFF);
if (pt1000_error & ADC_PT1000_OVERFLOW)
oven_pid_report_error(OVEN_PID_ERR_PT1000_OTHER);
return;
}
(void)temp_converter_convert_resistance_to_temp(current_temp, &current_temp);
pid_out = pid_sample(&oven_pid, target_temp - current_temp);
oven_driver_set_power((uint8_t)pid_out);
oven_pid_current_status.timestamp_last_run = systick_get_global_tick();
oven_pid_current_status.target_temp = target_temp;
oven_pid_current_status.current_temp = current_temp;
}
}
}
void oven_pid_report_error(enum oven_pid_error_report report)
{
struct oven_pid_errors *e = &oven_pid_current_status.error_flags;
oven_pid_current_status.active = false;
oven_pid_current_status.error_set = true;
if (report == 0) {
e->generic_error = true;
}
if (report & OVEN_PID_ERR_OVERTEMP)
e->controller_overtemp = true;
if (report & OVEN_PID_ERR_VREF_TOL)
e->controller_overtemp = true;
if (report & OVEN_PID_ERR_PT1000_OTHER)
e->pt1000_other = true;
if (report & OVEN_PID_ERR_PT1000_ADC_OFF)
e->pt1000_adc_off = true;
if (report & OVEN_PID_ERR_PT1000_ADC_WATCHDOG)
e->pt1000_adc_watchdog = true;
}
const struct oven_pid_status *oven_pid_get_status()
{
return &oven_pid_current_status;
}
void oven_pid_stop()
{
oven_pid_current_status.active = false;
oven_pid_current_status.target_temp = 0.0f;
oven_pid_current_status.current_temp = 0.0f;
}

View File

@@ -19,6 +19,7 @@
*/ */
#include <reflow-controller/pid-controller.h> #include <reflow-controller/pid-controller.h>
#include <string.h>
void pid_init(struct pid_controller *pid, float k_deriv, float k_int, float k_p, float output_sat_min, float output_sat_max, float integral_max, float sample_period) void pid_init(struct pid_controller *pid, float k_deriv, float k_int, float k_p, float output_sat_min, float output_sat_max, float integral_max, float sample_period)
{ {
@@ -29,7 +30,7 @@ void pid_init(struct pid_controller *pid, float k_deriv, float k_int, float k_p,
pid->k_p = k_p; pid->k_p = k_p;
pid->k_int = k_int; pid->k_int = k_int;
pid->k_deriv = k_deriv; pid->k_deriv = k_deriv;
pid->k_int_t = pid->k_int * pid->sample_period / 2.0f; pid->k_int_t = pid->k_int * pid->sample_period * 0.5f;
pid->k_deriv_t = pid->k_deriv * 2.0f / pid->sample_period; pid->k_deriv_t = pid->k_deriv * 2.0f / pid->sample_period;
pid->output_sat_max = output_sat_max; pid->output_sat_max = output_sat_max;
pid->output_sat_min = output_sat_min; pid->output_sat_min = output_sat_min;
@@ -38,6 +39,16 @@ void pid_init(struct pid_controller *pid, float k_deriv, float k_int, float k_p,
pid_zero(pid); pid_zero(pid);
} }
int pid_copy(struct pid_controller *dest, const struct pid_controller *src)
{
if (!dest || !src)
return -1;
memcpy(dest, src, sizeof(struct pid_controller));
return 0;
}
void pid_zero(struct pid_controller *pid) void pid_zero(struct pid_controller *pid)
{ {
pid->control_output = 0.0f; pid->control_output = 0.0f;

285
stm-firmware/reflow-menu.c Normal file
View File

@@ -0,0 +1,285 @@
/* 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.
*
* 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/reflow-menu.h>
#include <reflow-controller/ui/menu.h>
#include <reflow-controller/ui/lcd.h>
#include <reflow-controller/rotary-encoder.h>
#include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/safety-adc.h>
#include <reflow-controller/temp-converter.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/unique-id.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
static char __attribute__((section(".ccmram"))) display_buffer[4][21] = {0};
static struct lcd_menu reflow_menu;
static struct lcd_menu * const reflow_menu_ptr = &reflow_menu;
static void update_display_buffer(uint8_t row, const char *data)
{
int i;
if (row > 4)
return;
if (!data)
return;
for (i = 0; data[i] && i < LCD_CHAR_WIDTH; i++) {
display_buffer[row][i] = data[i];
}
display_buffer[row][i] = 0;
}
static void reflow_menu_monitor(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
{
static void *my_parent;
static uint64_t my_timestamp = 0;
char line[17];
float tmp;
int res;
const char *prefix;
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
my_parent = parent;
menu_display_clear(menu);
}
if (systick_ticks_have_passed(my_timestamp, 250)) {
my_timestamp = systick_get_global_tick();
adc_pt1000_get_current_resistance(&tmp);
snprintf(line, sizeof(line), "Res: %.1f", tmp);
menu->update_display(0, line);
res = temp_converter_convert_resistance_to_temp(tmp, &tmp);
switch (res) {
case -1:
prefix = "<";
break;
case 1:
prefix = ">";
break;
default:
prefix = "";
break;
}
snprintf(line, sizeof(line), "Temp: %s%.1f " LCD_DEGREE_SYMBOL_STRING "C", prefix, tmp);
menu->update_display(1, line);
tmp = safety_adc_get_temp();
snprintf(line, sizeof(line), "Tj: %.1f " LCD_DEGREE_SYMBOL_STRING "C", tmp);
menu->update_display(2, line);
tmp = safety_adc_get_vref();
snprintf(line, sizeof(line), "Vref: %.1f mV", tmp);
menu->update_display(3, line);
}
if (menu->inputs.push_button == BUTTON_SHORT_RELEASED || menu->inputs.push_button == BUTTON_LONG) {
menu_entry_dropback(menu, my_parent);
}
}
static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
{
static void *my_parent;
static bool button_ready;
static int page = 0;
static uint32_t uptime_secs;
uint32_t new_uptime_secs;
uint32_t uptime_mins;
uint32_t uptime_hours;
uint32_t uptime_days;
int16_t rot_delta;
uint32_t ser1, ser2, ser3;
enum button_state push_button;
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
uptime_secs = 0ULL;
page = 0;
my_parent = parent;
button_ready = false;
menu_display_clear(menu);
menu_ack_rotary_delta(menu);
}
rot_delta = menu_get_rotary_delta(menu);
if (rot_delta >= 4) {
menu_ack_rotary_delta(menu);
if (page < 4) {
page++;
menu_display_clear(menu);
}
} else if (rot_delta <= -4) {
menu_ack_rotary_delta(menu);
if (page > 0) {
page--;
menu_display_clear(menu);
}
}
switch (page) {
case 0:
menu_lcd_output(menu, 0, LCD_SHIMATTA_STRING " Shimatta");
menu_lcd_output(menu, 1, "Oven Controller");
menu_lcd_output(menu, 2, "(c) Mario H\xF5ttel");
menu_lcd_output(menu, 3, "Page 1/5");
break;
case 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]);
}
#ifdef DEBUGBUILD
menu_lcd_output(menu, 3, "Page 2/5 [DEBUG]");
#else
menu_lcd_output(menu, 3, "Page 2/5");
#endif
break;
case 2:
menu_lcd_output(menu, 0, "Compile Info");
menu_lcd_output(menu, 1, __DATE__);
menu_lcd_output(menu, 2, __TIME__);
menu_lcd_output(menu, 3, "Page 3/5");
break;
case 3:
unique_id_get(&ser1, &ser2, &ser3);
menu_lcd_outputf(menu, 0, "Serial: %08X", ser1);
menu_lcd_outputf(menu, 1, " %08X", ser2);
menu_lcd_outputf(menu, 2, " %08X", ser3);
menu_lcd_output(menu, 3, "Page 4/5");
break;
case 4:
systick_get_uptime_from_tick(&uptime_days, &uptime_hours, &uptime_mins, &new_uptime_secs);
if (new_uptime_secs != uptime_secs) {
uptime_secs = new_uptime_secs;
menu_lcd_output(menu, 0, "Uptime:");
menu_lcd_outputf(menu, 1, "%lu day%s %02lu:%02lu:%02lu",
uptime_days, (uptime_days == 1 ? "" : "s"), uptime_hours, uptime_mins, uptime_secs);
menu_lcd_output(menu, 3, "Page 5/5");
}
break;
default:
page = 0;
break;
}
push_button = menu_get_button_state(menu);
if (push_button == BUTTON_IDLE)
button_ready = true;
if (button_ready &&
(push_button == BUTTON_SHORT_RELEASED || push_button == BUTTON_LONG)) {
menu_entry_dropback(menu, my_parent);
}
}
static void reflow_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
{
(void)parent;
static struct menu_list list;
static bool button_valid;
static const char * const root_entry_names[] = {
"About",
"Monitoring",
NULL
};
static const menu_func_t root_entry_funcs[] = {
reflow_menu_about,
reflow_menu_monitor
};
enum button_state push_button;
int16_t rot_delta;
if (entry_type != MENU_ENTRY_CONTINUE) {
menu_display_clear(menu);
update_display_buffer(0, "Main Menu");
menu_ack_rotary_delta(menu);
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
button_valid = false;
list.entry_names = root_entry_names;
list.submenu_list = root_entry_funcs;
list.update_display = menu->update_display;
list.currently_selected = 0;
menu_list_compute_count(&list);
}
}
push_button = menu_get_button_state(menu);
rot_delta = menu_get_rotary_delta(menu);
if (push_button == BUTTON_IDLE) {
button_valid = true;
} else if (button_valid && push_button == BUTTON_SHORT_RELEASED) {
/* Enter currently selected menu_entry */
menu_list_enter_selected_entry(&list, menu);
}
if (rot_delta >= 4) {
menu_list_scroll_down(&list);
menu_ack_rotary_delta(menu);
} else if (rot_delta <= -4) {
menu_list_scroll_up(&list);
menu_ack_rotary_delta(menu);
}
menu_list_display(&list, 1, 3);
}
int reflow_menu_handle()
{
int32_t rot_delta;
enum button_state button;
static enum lcd_fsm_ret lcd_ret = LCD_FSM_NOP;
rot_delta = rotary_encoder_get_change_val();
button = button_read_event();
menu_handle(reflow_menu_ptr, (int16_t)rot_delta, button);
if (lcd_ret == LCD_FSM_CALL_AGAIN || lcd_tick_100us >= 5) {
lcd_ret = lcd_fsm_write_buffer(display_buffer);
lcd_tick_100us = 0UL;
}
if (lcd_ret == LCD_FSM_CALL_AGAIN)
return 0;
else
return 1;
}
void reflow_menu_init()
{
rotary_encoder_setup();
button_init();
lcd_init();
menu_init(reflow_menu_ptr, reflow_menu_root_entry, update_display_buffer);
}

View File

@@ -83,3 +83,8 @@ void rotary_encoder_stop(void)
rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(ROTARY_ENCODER_TIMER_RCC_MASK)); rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(ROTARY_ENCODER_TIMER_RCC_MASK));
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(ROTARY_ENCODER_RCC_MASK)); rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(ROTARY_ENCODER_RCC_MASK));
} }
void rotary_encoder_zero(void)
{
ROTARY_ENCODER_TIMER->CNT = 0UL;
}

View File

@@ -20,13 +20,27 @@
#include <reflow-controller/safety-adc.h> #include <reflow-controller/safety-adc.h>
#include <reflow-controller/periph-config/safety-adc-hwcfg.h> #include <reflow-controller/periph-config/safety-adc-hwcfg.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/clock-enable-manager.h> #include <stm-periph/clock-enable-manager.h>
enum safety_adc_check_result global_safety_adc_status;
enum safety_adc_check_result safety_adc_get_errors()
{
return global_safety_adc_status;
}
void safety_adc_clear_errors(void)
{
global_safety_adc_status = SAFETY_ADC_CHECK_OK;
}
void safety_adc_init() void safety_adc_init()
{ {
rcc_manager_enable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(SAFETY_ADC_ADC_RCC_MASK)); rcc_manager_enable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(SAFETY_ADC_ADC_RCC_MASK));
safety_adc_clear_errors();
/* Enable temperature and VREFINT measurement */ /* Enable temperature and VREFINT measurement */
ADC->CCR |= ADC_CCR_TSVREFE; ADC->CCR |= ADC_CCR_TSVREFE;
@@ -50,18 +64,32 @@ enum safety_adc_check_result safety_adc_check_results(uint16_t vref_result, uint
float *vref_calculated, float *temp_calculated) float *vref_calculated, float *temp_calculated)
{ {
enum safety_adc_check_result res = SAFETY_ADC_CHECK_OK; enum safety_adc_check_result res = SAFETY_ADC_CHECK_OK;
float vref;
float temp;
vref = (SAFETY_ADC_INT_REF_MV * 4095.0f) / (float)vref_result;
if (vref_calculated) { if (vref_calculated) {
*vref_calculated = (SAFETY_ADC_INT_REF_MV * 4095.0f) / (float)vref_result; *vref_calculated = vref;
} }
temp = (((float)temp_result / 4095.0f * 2500.0f -
SAFETY_ADC_TEMP_NOM_MV) / SAFETY_ADC_TEMP_MV_SLOPE) + SAFETY_ADC_TEMP_NOM;
if (temp_calculated) { if (temp_calculated) {
*temp_calculated = (((float)temp_result / 4095.0f * 2500.0f - *temp_calculated = temp;
SAFETY_ADC_TEMP_NOM_MV) / SAFETY_ADC_TEMP_MV_SLOPE) + SAFETY_ADC_TEMP_NOM;
} }
/* TODO: Implement safety ADC checking */ if (ABS(vref - SAFETY_ADC_VREF_MVOLT) > SAFETY_ADC_VREF_TOL_MVOLT) {
if (vref > SAFETY_ADC_VREF_MVOLT)
res |= SAFETY_ADC_CHECK_VREF_HIGH;
else
res |= SAFETY_ADC_CHECK_VREF_LOW;
}
if (temp < SAFETY_ADC_TEMP_LOW_LIM)
res |= SAFETY_ADC_CHECK_TEMP_LOW;
else if (temp < SAFETY_ADC_CHECK_TEMP_HIGH)
res |= SAFETY_ADC_CHECK_TEMP_HIGH;
return res; return res;
} }
@@ -103,16 +131,17 @@ void safety_adc_trigger_meas(enum safety_adc_meas_channel measurement)
SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_SWSTART; SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_SWSTART;
} }
static volatile uint16_t safety_vref_meas_raw; static uint16_t safety_vref_meas_raw;
static volatile bool safety_vref_valid = false; static bool safety_vref_valid = false;
static volatile uint16_t safety_temp_meas_raw; static uint16_t safety_temp_meas_raw;
static volatile bool safety_temp_valid = false; static bool safety_temp_valid = false;
static float safety_vref; static float safety_vref;
static float safety_temp; static float safety_temp;
enum safety_adc_check_result handle_safety_adc() enum safety_adc_check_result handle_safety_adc()
{ {
static enum safety_adc_meas_channel safety_meas_channel = SAFETY_ADC_MEAS_VREF; static enum safety_adc_meas_channel safety_meas_channel = SAFETY_ADC_MEAS_VREF;
enum safety_adc_check_result check_result;
uint16_t result; uint16_t result;
int poll_status; int poll_status;
@@ -139,10 +168,13 @@ enum safety_adc_check_result handle_safety_adc()
} }
if (safety_temp_valid && safety_vref_valid) { if (safety_temp_valid && safety_vref_valid) {
return safety_adc_check_results(safety_vref_meas_raw, safety_temp_meas_raw, &safety_vref, &safety_temp); check_result = safety_adc_check_results(safety_vref_meas_raw, safety_temp_meas_raw, &safety_vref, &safety_temp);
global_safety_adc_status |= check_result;
} else { } else {
return SAFETY_ADC_CHECK_OK; check_result = SAFETY_ADC_CHECK_OK;
} }
return check_result;
} }
float safety_adc_get_temp() float safety_adc_get_temp()

View File

@@ -189,8 +189,18 @@ static shellmatta_retCode_t shell_cmd_uptime(const shellmatta_handle_t handle,
{ {
(void)arguments; (void)arguments;
(void)length; (void)length;
uint32_t days;
uint32_t hours;
uint32_t mins;
uint32_t secs;
shellmatta_printf(handle, "Uptime: %llu secs", global_tick_ms/1000); systick_get_uptime_from_tick(&days, &hours, &mins, &secs);
shellmatta_printf(handle, "Uptime: %u day%s %02u:%02u:%02u",
days, (days == 1 ? "" : "s"),
hours,
mins,
secs);
return SHELLMATTA_OK; return SHELLMATTA_OK;
} }
@@ -239,11 +249,10 @@ static shellmatta_retCode_t shell_cmd_rot(const shellmatta_handle_t handle,
(void)length; (void)length;
uint32_t rot_val; uint32_t rot_val;
int32_t delta;
rot_val = rotary_encoder_get_abs_val(); rot_val = rotary_encoder_get_abs_val();
delta = rotary_encoder_get_change_val(); //delta = rotary_encoder_get_change_val();
shellmatta_printf(handle, "Rotary encoder value: %u, delta: %d\r\n", rot_val, delta); shellmatta_printf(handle, "Rotary encoder value: %u\r\n", rot_val);
return SHELLMATTA_OK; return SHELLMATTA_OK;
} }
@@ -352,6 +361,20 @@ static shellmatta_retCode_t shell_cmd_safety_adc(const shellmatta_handle_t handl
shellmatta_printf(handle, "VREF:\t%8.2f\tmV\r\n", safety_adc_get_vref()); shellmatta_printf(handle, "VREF:\t%8.2f\tmV\r\n", safety_adc_get_vref());
shellmatta_printf(handle, "TEMP:\t%8.2f\tdeg. Celsius\r\n", safety_adc_get_temp()); shellmatta_printf(handle, "TEMP:\t%8.2f\tdeg. Celsius\r\n", safety_adc_get_temp());
shellmatta_printf(handle, "Errors:\t%X", safety_adc_get_errors());
return SHELLMATTA_OK;
}
static shellmatta_retCode_t shell_cmd_safety_adc_clear_error(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
(void)length;
(void)arguments;
(void)handle;
safety_adc_clear_errors();
return SHELLMATTA_OK; return SHELLMATTA_OK;
} }
@@ -365,7 +388,7 @@ static shellmatta_retCode_t shell_cmd_safety_adc(const shellmatta_handle_t handl
// struct shellmatta_cmd *next; /**< pointer to next command or NULL */ // struct shellmatta_cmd *next; /**< pointer to next command or NULL */
//} shellmatta_cmd_t; //} shellmatta_cmd_t;
static shellmatta_cmd_t cmd[14] = { static shellmatta_cmd_t cmd[15] = {
{ {
.cmd = "version", .cmd = "version",
.cmdAlias = "ver", .cmdAlias = "ver",
@@ -476,6 +499,14 @@ static shellmatta_cmd_t cmd[14] = {
.helpText = "", .helpText = "",
.usageText = "", .usageText = "",
.cmdFct = shell_cmd_safety_adc, .cmdFct = shell_cmd_safety_adc,
.next = &cmd[14],
},
{
.cmd = "safety-adc-clear-error",
.cmdAlias = NULL,
.helpText = "",
.usageText = "",
.cmdFct = shell_cmd_safety_adc_clear_error,
.next = NULL, .next = NULL,
}, },

View File

@@ -112,13 +112,8 @@ SECTIONS
_siccmram = LOADADDR(.ccmram); _siccmram = LOADADDR(.ccmram);
/* CCM-RAM section /* CCM-RAM section */
* .ccmram (NOLOAD):
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.ccmram :
{ {
. = ALIGN(4); . = ALIGN(4);
_sccmram = .; /* create a global symbol at ccmram start */ _sccmram = .; /* create a global symbol at ccmram start */
@@ -126,12 +121,12 @@ SECTIONS
*(.ccmram*) *(.ccmram*)
. = ALIGN(4); . = ALIGN(4);
_eccmram = .; /* create a global symbol at ccmram end */ _eccmram = .; /* create a global symbol at ccmram end */
} >CCM AT> FLASH } >CCM
_sidata = LOADADDR(.data); _sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */ /* Initialized data sections goes into RAM, load LMA copy after code */
.data : .data :
{ {
. = ALIGN(4); . = ALIGN(4);
_sdata = .; /* create a global symbol at data start */ _sdata = .; /* create a global symbol at data start */

View File

@@ -26,12 +26,13 @@
#include <stm32/stm32f4xx.h> #include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h> #include <cmsis/core_cm4.h>
volatile uint32_t wait_tick_ms; volatile uint32_t wait_tick_ms = 0UL;
volatile uint64_t global_tick_ms; volatile uint64_t global_tick_ms = 0ULL;
volatile uint32_t lcd_tick_100us = 0UL;
void systick_setup(void) void systick_setup(void)
{ {
/* Setup Systick for 1ms tick @ 168 MHz Clock Speed */ /* Setup Systick for 100us tick @ 168 MHz Clock Speed */
SysTick_Config(SYSTICK_RELOAD); SysTick_Config(SYSTICK_RELOAD);
} }
@@ -52,6 +53,35 @@ uint64_t systick_get_global_tick()
return temp; return temp;
} }
void systick_get_uptime_from_tick(uint32_t *days, uint32_t *hours, uint32_t *minutes, uint32_t *seconds)
{
uint64_t tick_secs;
uint32_t secs;
uint32_t mins;
uint32_t hs;
uint32_t ds;
tick_secs = systick_get_global_tick() / 1000;
secs = tick_secs % 60;
tick_secs /= 60;
mins = tick_secs % 60;
tick_secs /= 60;
hs = tick_secs % 60;
tick_secs /= 24;
ds = tick_secs;
if (days)
*days = ds;
if (hours)
*hours = hs;
if (minutes)
*minutes = mins;
if (seconds)
*seconds = secs;
}
bool __attribute__((optimize("O3"))) systick_ticks_have_passed(uint64_t start_timestamp, uint64_t ticks) bool __attribute__((optimize("O3"))) systick_ticks_have_passed(uint64_t start_timestamp, uint64_t ticks)
{ {
uint64_t end_timestamp = start_timestamp + ticks; uint64_t end_timestamp = start_timestamp + ticks;
@@ -81,7 +111,14 @@ bool __attribute__((optimize("O3"))) systick_ticks_have_passed(uint64_t start_ti
*/ */
void __attribute__((optimize("O3"))) SysTick_Handler() void __attribute__((optimize("O3"))) SysTick_Handler()
{ {
/* Increase tick counters */ static uint32_t pre_tick = 0UL;
wait_tick_ms++;
global_tick_ms++; pre_tick++;
if (pre_tick == 10) {
pre_tick = 0;
/* Increase tick counters */
wait_tick_ms++;
global_tick_ms++;
}
lcd_tick_100us++;
} }

View File

@@ -149,7 +149,7 @@ static void lcd_command(uint8_t data)
// Set DD RAM Address --------- 0b1xxxxxxx (Display Data RAM) // Set DD RAM Address --------- 0b1xxxxxxx (Display Data RAM)
#define LCD_SET_DDADR 0x80 #define LCD_SET_DDADR 0x80
static char shadow_display[4][21]; static char __attribute__((section(".ccmram"))) shadow_display[4][21];
void lcd_clear(void) void lcd_clear(void)
{ {
@@ -305,12 +305,22 @@ static void lcd_fsm_enable(bool en)
__ASM("nop"); __ASM("nop");
__ASM("nop"); __ASM("nop");
__ASM("nop"); __ASM("nop");
__ASM("nop");
__ASM("nop");
__ASM("nop");
if (en) if (en)
LCD_DPORT->ODR |= LCD_E_MASK; LCD_DPORT->ODR |= LCD_E_MASK;
else else
LCD_DPORT->ODR &= ~LCD_E_MASK; LCD_DPORT->ODR &= ~LCD_E_MASK;
__ASM("nop");
__ASM("nop");
__ASM("nop");
__ASM("nop");
__ASM("nop");
__ASM("nop");
__ASM("nop");
__ASM("nop");
__ASM("nop");
__ASM("nop"); __ASM("nop");
__ASM("nop"); __ASM("nop");
__ASM("nop"); __ASM("nop");
@@ -393,10 +403,10 @@ enum lcd_fsm_ret lcd_fsm_write_buffer(const char (*display_buffer)[21])
state_cnt++; state_cnt++;
break; break;
case 4: case 4:
if (!systick_ticks_have_passed(timestamp, 5)) { if (!systick_ticks_have_passed(timestamp, 4)) {
ret = LCD_FSM_WAIT_CALL; ret = LCD_FSM_WAIT_CALL;
} else { } else {
ret = LCD_FSM_WAIT_CALL; ret = LCD_FSM_CALL_AGAIN;
state_cnt++; state_cnt++;
} }
break; break;
@@ -408,7 +418,7 @@ enum lcd_fsm_ret lcd_fsm_write_buffer(const char (*display_buffer)[21])
break; break;
case 6: case 6:
lcd_fsm_enable(false); lcd_fsm_enable(false);
ret = LCD_FSM_CALL_AGAIN; ret = LCD_FSM_WAIT_CALL;
state_cnt++; state_cnt++;
break; break;
case 7: case 7:

View File

@@ -20,8 +20,10 @@
#include <reflow-controller/ui/menu.h> #include <reflow-controller/ui/menu.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h>
#include <stdarg.h>
void menu_handle(struct lcd_menu *menu, uint16_t rotary_encoder_delta, enum button_state push_button) void menu_handle(struct lcd_menu *menu, int16_t rotary_encoder_delta, enum button_state push_button)
{ {
menu_func_t tmp; menu_func_t tmp;
@@ -29,7 +31,7 @@ void menu_handle(struct lcd_menu *menu, uint16_t rotary_encoder_delta, enum butt
return; return;
menu->inputs.push_button = push_button; menu->inputs.push_button = push_button;
menu->inputs.rotary_encoder_delta = rotary_encoder_delta; menu->inputs.rotary_encoder_delta += rotary_encoder_delta;
if (menu->active_entry == NULL) if (menu->active_entry == NULL)
menu->active_entry = menu->root_entry; menu->active_entry = menu->root_entry;
@@ -38,9 +40,9 @@ void menu_handle(struct lcd_menu *menu, uint16_t rotary_encoder_delta, enum butt
if (menu->active_entry_type == MENU_ENTRY_FIRST_ENTER) { if (menu->active_entry_type == MENU_ENTRY_FIRST_ENTER) {
menu->active_entry(menu, menu->active_entry_type, menu->init_parent); menu->active_entry(menu, menu->active_entry_type, menu->init_parent);
} else { } else {
menu->active_entry(menu, menu->active_entry_type, NULL); menu->active_entry(menu, menu->active_entry_type, NULL);
} }
if (menu->active_entry_type != MENU_ENTRY_CONTINUE && tmp == menu->active_entry) { if (menu->active_entry_type != MENU_ENTRY_CONTINUE && tmp == menu->active_entry) {
@@ -74,13 +76,20 @@ void menu_entry_dropback(struct lcd_menu *menu, menu_func_t parent_func)
menu->active_entry_type = MENU_ENTRY_DROPBACK; menu->active_entry_type = MENU_ENTRY_DROPBACK;
} }
void menu_entry_enter(struct lcd_menu *menu, menu_func_t parent_func, bool handle_immediately) void menu_entry_enter(struct lcd_menu *menu, menu_func_t entry, bool handle_immediately)
{ {
if (!menu)
return;
menu->init_parent = menu->active_entry; menu->init_parent = menu->active_entry;
menu->active_entry_type = MENU_ENTRY_FIRST_ENTER; menu->active_entry_type = MENU_ENTRY_FIRST_ENTER;
menu->active_entry = entry;
if (handle_immediately)
menu_handle(menu, menu->inputs.rotary_encoder_delta, menu->inputs.push_button);
} }
void menu_override_lcd_output(struct lcd_menu *menu, uint8_t row_num, const char *text) void menu_lcd_output(struct lcd_menu *menu, uint8_t row_num, const char *text)
{ {
if (!menu || !menu->update_display) if (!menu || !menu->update_display)
return; return;
@@ -88,7 +97,159 @@ void menu_override_lcd_output(struct lcd_menu *menu, uint8_t row_num, const char
menu->update_display(row_num, text); menu->update_display(row_num, text);
} }
void menu_list_display(struct menu_list *list, uint8_t top_row, uint8_t bottom_row) void menu_lcd_outputf(struct lcd_menu *menu, uint8_t row_num, const char *format, ...)
{ {
char buff[64];
va_list valist;
va_start(valist, format);
vsnprintf(buff, sizeof(buff), format, valist);
buff[sizeof(buff) - 1] = '\0';
menu_lcd_output(menu, row_num, buff);
va_end(valist);
}
void menu_list_display(struct menu_list *list, uint32_t top_row, uint32_t bottom_row)
{
uint8_t row_count;
uint32_t mid_row;
uint32_t count_above_mid;
uint32_t count_below_mid;
uint32_t start_index;
uint32_t current_row;
uint32_t current_idx;
char workbuff[64];
if (!list || !list->update_display)
return;
if (bottom_row < top_row)
return;
if (list->entry_count == 0) {
for (current_row = top_row; current_row <= bottom_row; current_row++) {
list->update_display((uint8_t)current_row, "");
}
return;
}
/* Calculate list parameters */
row_count = bottom_row - top_row + 1;
mid_row = (top_row + bottom_row) / 2;
count_above_mid = mid_row - top_row;
count_below_mid = bottom_row - mid_row;
/* Check if there are more elements above the and below the currently selected one that can be displayed. in this case position
* active entry in center
*/
if (list->currently_selected > count_above_mid && (list->entry_count - list->currently_selected - 1) > count_below_mid) {
start_index = list->currently_selected - count_above_mid;
} else if (list->currently_selected < count_above_mid) {
start_index = 0;
} else if ((list->entry_count - list->currently_selected - 1) <= count_below_mid) {
if (list->entry_count < row_count)
start_index = 0;
else
start_index = list->entry_count - row_count;
} else {
start_index = 0;
}
for (current_row = top_row, current_idx = start_index; current_row <= bottom_row; current_row++, current_idx++) {
if (current_idx >= list->entry_count)
break;
snprintf(workbuff, sizeof(workbuff), "%c%s", (current_idx == list->currently_selected ? '>' : ' '),
list->entry_names[current_idx]);
workbuff[sizeof(workbuff)-1] = 0;
list->update_display((uint8_t)current_row, workbuff);
}
} }
void menu_list_compute_count(struct menu_list *list)
{
uint32_t count = 0;
if (!list)
return;
for (count = 0; list->entry_names[count] != NULL; count++);
list->entry_count = count;
}
void menu_list_scroll_down(struct menu_list *list)
{
if (!list)
return;
if (list->currently_selected < list->entry_count - 1) {
list->currently_selected++;
}
}
void menu_list_enter_selected_entry(struct menu_list *list, struct lcd_menu *menu)
{
menu_func_t entry;
if (!list)
return;
if (!list->submenu_list)
return;
entry = list->submenu_list[list->currently_selected];
if (!entry)
return;
menu_entry_enter(menu, entry, false);
}
void menu_list_scroll_up(struct menu_list *list)
{
if (!list)
return;
if (list->currently_selected > 0)
list->currently_selected--;
}
void menu_ack_rotary_delta(struct lcd_menu *menu)
{
if (!menu)
return;
menu->inputs.rotary_encoder_delta = 0;
}
int16_t menu_get_rotary_delta(const struct lcd_menu *menu)
{
int16_t ret = 0;
if (menu)
ret = menu->inputs.rotary_encoder_delta;
return ret;
}
enum button_state menu_get_button_state(const struct lcd_menu *menu)
{
enum button_state ret = BUTTON_IDLE;
if (menu)
ret = menu->inputs.push_button;
return ret;
}
void menu_display_clear(struct lcd_menu *menu)
{
uint8_t i;
if (!menu || !menu->update_display)
return;
for (i = 0; i < 4; i++)
menu->update_display(i, "");
}

View File

@@ -1,279 +0,0 @@
# Created by https://www.gitignore.io/api/latex
# Edit at https://www.gitignore.io/?templates=latex
### LaTeX ###
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
specification.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Build tool directories for auxiliary files
# latexrun
latex.out/
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.pre
*.snm
*.vrb
# changes
*.soc
# comment
*.cut
# cprotect
*.cpt
# elsarticle (documentclass of Elsevier journals)
*.spl
# endnotes
*.ent
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
# *.ist
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.gtex
# htlatex
*.4ct
*.4tc
*.idv
*.lg
*.trc
*.xref
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Comment the next line if you want to keep your tikz graphics files
*.tikz
*-tikzDictionary
# listings
*.lol
# luatexja-ruby
*.ltjruby
# makeidx
*.idx
*.ilg
*.ind
# minitoc
*.maf
*.mlf
*.mlt
*.mtc[0-9]*
*.slf[0-9]*
*.slt[0-9]*
*.stc[0-9]*
# minted
_minted*
*.pyg
# morewrites
*.mw
# nomencl
*.nlg
*.nlo
*.nls
# pax
*.pax
# pdfpcnotes
*.pdfpc
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# tcolorbox
*.listing
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# todonotes
*.tdo
# vhistory
*.hst
*.ver
# easy-todo
*.lod
# xcolor
*.xcp
# xmpincl
*.xmpi
# xindy
*.xdy
# xypic precompiled matrices
*.xyc
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# LyX
*.lyx~
# Kile
*.backup
# KBibTeX
*~[0-9]*
# auto folder when using emacs and auctex
./auto/*
*.el
# expex forward references with \gathertags
*-tags.tex
# standalone packages
*.sta
### LaTeX Patch ###
# glossaries
*.glstex
# Version File
*.ver
*.commit
*.branch
# End of https://www.gitignore.io/api/latex

View File

@@ -1,10 +0,0 @@
target=specification
.PHONY: $(target).pdf all clean
all: $(target).pdf
$(target).pdf: $(target).tex
latexmk -pdf -pdflatex="pdflatex -interaction=nostopmode -shell-escape" -use-make $^
clean:
latexmk -CA

View File

@@ -1,172 +0,0 @@
\documentclass[12pt,a4paper,oneside,notitlepage, numbers=noenddot,openany]{scrreprt}
\usepackage[a4paper]{geometry}
\geometry{verbose,tmargin=2.5cm,bmargin=4.5cm,lmargin=2.5cm,rmargin=2.5cm}
\setlength{\parindent}{0cm}
\usepackage{array}
\usepackage{textcomp}
\usepackage{float}
\usepackage{graphicx}
\usepackage{caption}
%\usepackage{subcaption}
%\usepackage{textgreek}
\usepackage{setspace}
\usepackage{nomencl}
\usepackage{listing}
\usepackage{tabularx}
\PassOptionsToPackage{hyphens}{url}
\usepackage[%
pdftitle={Temperature Profile File Specification},%
pdfauthor={Mario Huettel},%
pdfsubject={},%
pdfcreator={pdflatex, LaTeX with KOMA-Script},%
pdfpagemode=UseOutlines, % Beim Oeffnen Inhaltsverzeichnis anzeigen
pdfdisplaydoctitle=true, % Dokumenttitel statt Dateiname anzeigen.
pdflang=de, % Sprache des Dokuments.
%plainpages=false,
]{hyperref}
\immediate\write18{git describe --always --long --dirty > \jobname.ver}
\immediate\write18{git rev-parse --verify HEAD > \jobname.commit}
\immediate\write18{git rev-parse --abbrev-ref HEAD > \jobname.branch}
\usepackage[automark,headsepline,plainheadsepline,footsepline,plainfootsepline,autooneside=true]{scrlayer-scrpage}
\clearpairofpagestyles
\hypersetup{%
colorlinks=true, % Aktivieren von farbigen Links im Dokument
linkcolor=blue, % Farbe festlegen
citecolor=green,
%filecolor,
%menucolor=black,
%urlcolor=cyan,
bookmarksnumbered=true%, % Überschriftsnummerierung im PDF Inhalt anzeigen.
%hidelinks=false
}
\usepackage{CJKutf8}
\newenvironment{Japanese}{%
\CJKfamily{min}%
\CJKtilde
\CJKnospace}{}
\usepackage{booktabs}
\usepackage{color}
%\usepackage{cite}
\usepackage{blindtext}
\usepackage[utf8]{inputenc}
\usepackage{multicol}
\usepackage{lastpage}
\usepackage[american]{babel}
\usepackage{amssymb}
\usepackage{datetime}
\usepackage[withpage,printonlyused]{acronym}
\usepackage{amsmath}
\usepackage{mathtools}
\usepackage{forloop}
\usepackage{csvsimple}
\usepackage{xspace}
%\setcounter{secnumdepth}{3}
%\setcounter{tocdepth}{3}
%\usepackage{apacite}
%\usepackage{natbib}
%\usepackage[babel,german=quotes]{csquotes}
\usepackage{xspace}
\usepackage{nth}
%\usepackage[backend=biber, style=apa, natbib, bibencoding=utf8]{biblatex}
%
%
%
%\addbibresource{lit.bib}
%\DeclareLanguageMapping{ngerman}{ngerman-apa}
%
\usepackage{enumitem}
\newcounter{reqcount}
\newlist{requirements}{description}{1}
\setlist[requirements,1]{%
before={%
\renewcommand*\thereqcount{\arabic{reqcount}}},
font={\bfseries\stepcounter{reqcount}REQ-\thereqcount:}
}
\newcommand{\newreq}{\bfseries\stepcounter{reqcount}REQ-\thereqcount:~}
\usepackage{xargs} % Use more than one optional parameter in a new commands
\usepackage[colorinlistoftodos,prependcaption,textsize=tiny]{todonotes}
\newcommandx{\unsure}[2][1=]{\todo[noline,linecolor=red,backgroundcolor=red!25,bordercolor=red,#1]{#2}}
\newcommandx{\miscite}[1][1=]{\todo[noline,linecolor=black,backgroundcolor=black!25,bordercolor=black]{Missing citation! #1}}
\newcommandx{\change}[2][1=]{\todo[noline,linecolor=blue,backgroundcolor=blue!25,bordercolor=blue,#1]{#2}}
\newcommandx{\info}[2][1=]{\todo[noline,linecolor=OliveGreen,backgroundcolor=OliveGreen!25,bordercolor=OliveGreen,#1]{#2}}
\newcommandx{\improvement}[2][1=]{\todo[noline,linecolor=Plum,backgroundcolor=Plum!25,bordercolor=Plum,#1]{#2}}
\newcommandx{\thiswillnotshow}[2][1=]{\todo[disable,#1]{#2}}
%Figure, table and listing enumeration style
\captionsetup{labelfont=bf}
\usepackage{chngcntr}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\clubpenalty10000
\widowpenalty10000
\displaywidowpenalty10000
%\counterwithout{figure}{part}
\ihead[Spec]{Spec}
\chead[\input{\jobname.ver}]{\input{\jobname.ver}}
\ohead{\headmark}
\ofoot[\pagemark]{\pagemark}
\cfoot[\url{https://git.shimatta.de/mhu/reflow-oven-control-sw}]{\url{https://git.shimatta.de/mhu/reflow-oven-control-sw}}
\setheadsepline{.5pt}
\setfootsepline{.5pt}
\BeforeStartingTOC{\thispagestyle{scrheadings}}
\pagestyle{scrheadings}
\thispagestyle{scrheadings}
\raggedbottom
\begin{document}
\pagenumbering{roman}
\begin{titlepage}
\begin{center}
\begin{figure}[H]
\centering
\resizebox{5cm}{!}{
\includegraphics{img/shimatta-logo}}
\end{figure}
\vspace{10em}
\begin{Huge}
\sffamily\textbf{Temperature Profile File Specification}
\end{Huge}
\vspace{1cm}
\textbf{Version: \input{\jobname.ver}}
\vspace{3em}
\small\texttt{\input{\jobname.commit}/ \input{\jobname.branch}}
\end{center}
\end{titlepage}
\tableofcontents
\newpage
\thispagestyle{scrheadings}
\pagenumbering{arabic}
\chapter{Test}
\end{document}