Compare commits

...

28 Commits

Author SHA1 Message Date
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
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
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
fc2372f754 Fix possible error in menu implementation 2020-06-04 21:47:31 +02:00
92c0c5cd8c shrink shellmatta history buffer 2020-06-04 21:46:45 +02:00
25bb341fa4 Implement preliminary menu functions 2020-06-04 21:20:59 +02:00
0d44d25ec9 Add reminder to implement checking of safety ADC 2020-06-04 21:20:43 +02:00
6b4029f8c2 Make startup file unix like and add a hardcoded enable for the FPU 2020-06-04 21:20:17 +02:00
76f5a4e9be Fix error in safety ADC 2020-06-01 22:59:27 +02:00
f493b823b3 Add lcd-menu module as specific implementation module for the menu on the LCD 2020-06-01 21:45:36 +02:00
d508402aa8 Add basic definitions for menu entries 2020-06-01 21:42:31 +02:00
6477950eea Merge branch 'dev' into ui 2020-06-01 20:53:43 +02:00
c18acba48b Merge branch 'dev' into ui 2020-05-16 21:08:24 +02:00
6c013d5aa3 Merge branch 'dev' into ui 2020-05-11 21:59:25 +02:00
0b83125ab5 Merge branch 'dev' into ui 2020-05-10 23:13:47 +02:00
5fcb5305f8 Implement dummy for calibration save function 2020-05-09 20:51:30 +02:00
d6d8973800 Fix typo in comment 2020-05-09 20:51:06 +02:00
99ae5f9bc1 Add lcd to menu 2020-05-09 20:50:15 +02:00
23 changed files with 1190 additions and 625 deletions

View File

@ -41,7 +41,7 @@ CFILES += calibration.c
CFILES += temp-converter.c
CFILES += rotary-encoder.c button.c
CFILES += stack-check.c
CFILES += ui/lcd.c ui/menu.c
CFILES += ui/lcd.c ui/menu.c reflow-menu.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 += pid-controller.c oven-driver.c
@ -124,7 +124,7 @@ qtproject-legacy:
find -name "*.h" | tr "\\n" " " >> $(target).pro
echo -ne "\nDEFINES += " >> $(target).pro
echo "$(DEFINES)" | sed "s/-D//g" >> $(target).pro
qtproject:
$(QUIET)rm -f $(target).files $(target).cflags $(target).config $(target).creator $(target).includes $(target).creator.user
echo "Generating source file list"
@ -134,13 +134,13 @@ qtproject:
$(QUIET)for dir in `echo $(INCLUDEPATH) | sed "s/-I//g"`; do \
find `echo "$${dir}"` -name "*.h" >> $(target).files; \
done
@echo "Generating CFLAGS File"
@echo "Generating $(target).cflags"
$(QUIET)echo "" > $(target).cflags
@echo "Generating includes files"
@echo "Generating $(target).includes"
$(QUIET)echo $(INCLUDEPATH) | sed "s/-I/,/g" | tr , '\n' | sed "/^$$/d" > $(target).includes;
@echo "Generating config file"
@echo "Generating $(target).config"
$(QUIET)echo $(DEFINES) | sed "s/-D/,#define /g" | tr , '\n' | sed "/^$$/d" > $(target).config
@echo "Generating creator file"
@echo "Generating $(target).creator"
$(QUIET)echo "[GENERAL]" > $(target).creator
-include $(CFILES:%.c=$(OBJDIR)/%.c.d) $(ASFILES:%.S=$(OBJDIR)/%.S.d)

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,16 @@
#define __OVEN_DRIVER_H__
#include <stdint.h>
#include <stdbool.h>
#include <reflow-controller/pid-controller.h>
struct oven_pid_status {
bool active;
bool aborted;
float target_temp;
float current_temp;
uint64_t timestamp_last_run;
};
void oven_driver_init(void);
@ -29,4 +39,14 @@ void oven_driver_set_power(uint8_t power);
void oven_driver_disable(void);
void oven_pid_init(struct pid_controller *controller_to_copy);
void oven_pid_handle(float target_temp, float current_temp);
void oven_pid_stop();
void oven_pid_report_error(void);
const struct oven_pid_status *oven_pid_get_status(void);
#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);
int pid_copy(struct pid_controller *dest, const struct pid_controller *src);
#endif /* __PID_CONTROLLER_H__ */

View File

@ -0,0 +1,32 @@
/* 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/>.
*/
#ifndef __REFLOW_MENU_H__
#define __REFLOW_MENU_H__
/**
* @brief Handle the reflow controller's LCD Menu
* @return 0 if a delay is requested, 1 if no 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_zero(void);
#endif /* __ROTARY_ENCODER_H__ */

View File

@ -37,6 +37,7 @@ enum safety_adc_check_result {
SAFETY_ADC_CHECK_VREF_HIGH = (1U<<1),
SAFETY_ADC_CHECK_TEMP_LOW = (1U<<2),
SAFETY_ADC_CHECK_TEMP_HIGH = (1U<<3),
SAFETY_ADC_INTERNAL_ERROR = (1U<<4),
};
void safety_adc_init();

View File

@ -22,6 +22,6 @@
#ifndef __SETTINGS_SETTINGS_H__
#define __SETTINGS_SETTINGS_H__
settings_save_calibration();
int settings_save_calibration();
#endif /* __SETTINGS_SETTINGS_H__ */

View File

@ -49,6 +49,11 @@ extern volatile uint32_t wait_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_ms;
/**
* @brief Setup the Systick timer to generate a 1 ms tick
*/

View File

@ -31,6 +31,15 @@
#define LCD_RS_MASK (1U << LCD_RS)
#define LCD_E_MASK (1U << LCD_E)
#define LCD_CHAR_WIDTH 16
#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};
void lcd_init(void);

View File

@ -21,4 +21,61 @@
#ifndef __MENU_H__
#define __MENU_H__
#include <stdint.h>
#include <reflow-controller/button.h>
#include <stdbool.h>
struct lcd_menu;
enum menu_entry_func_entry {MENU_ENTRY_FIRST_ENTER, MENU_ENTRY_CONTINUE, MENU_ENTRY_DROPBACK};
typedef void (*menu_func_t)(struct lcd_menu *menu, enum menu_entry_func_entry entry_type,
void *parent);
struct menu_inputs {
int16_t rotary_encoder_delta;
enum button_state push_button;
};
struct lcd_menu {
menu_func_t active_entry;
menu_func_t root_entry;
enum menu_entry_func_entry active_entry_type;
menu_func_t init_parent;
struct menu_inputs inputs;
void (*update_display)(uint8_t row, const char *data);
};
struct menu_list {
void (*update_display)(uint8_t row, const char *data);
const char * const * entry_names;
uint32_t entry_count;
uint32_t currently_selected;
const menu_func_t *submenu_list;
};
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_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_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_list_display(struct menu_list *list, uint32_t top_row, uint32_t bottom_row);
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__ */

View File

@ -34,21 +34,18 @@
#include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/shell.h>
#include <reflow-controller/ui/lcd.h>
#include <reflow-controller/digio.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/clock-enable-manager.h>
#include <stm-periph/uart.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/safety-adc.h>
#include <fatfs/ff.h>
#include <reflow-controller/reflow-menu.h>
static void setup_nvic_priorities()
{
@ -62,12 +59,7 @@ static void setup_nvic_priorities()
}
/* 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_ptr = &fs;
@ -99,7 +91,7 @@ static shellmatta_retCode_t write_shell_callback(const char *data, uint32_t len)
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->tx = 1;
@ -175,13 +167,11 @@ static inline void setup_system()
digio_setup_default_all();
led_setup();
loudspeaker_setup();
rotary_encoder_setup();
button_init();
lcd_init();
reflow_menu_init();
safety_adc_init();
uart_gpio_config();
setup_sell_uart(&shell_uart);
setup_shell_uart(&shell_uart);
setup_unused_pins();
}
@ -192,101 +182,55 @@ static void handle_shell_uart_input(shellmatta_handle_t shell_handle)
const char *uart_input;
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);
if (uart_receive_status >= 0)
shell_handle_input(shell_handle, uart_input, uart_input_len);
}
extern char _sccmram;
extern char _eccmram;
static void zero_ccm_ram(void)
{
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()
{
bool sd_card_mounted = false;
shellmatta_handle_t shell_handle;
uint64_t pid_timestamp = 0ULL;
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;
int menu_wait_request;
uint64_t quarter_sec_timestamp = 0ULL;
enum safety_adc_check_result safety_adc_status;
target_temperature = 25.0f;
zero_ccm_ram();
setup_system();
shell_handle = shell_init(write_shell_callback);
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) {
sd_card_mounted = mount_sd_card_if_avail(sd_card_mounted);
snprintf(&disp[0][0], 17, "SD %smounted", sd_card_mounted ? "" : "un");
pt1000_value_status = adc_pt1000_get_current_resistance(&pt1000_value);
if (systick_ticks_have_passed(pid_timestamp, 250)) {
(void)handle_safety_adc();
pid_timestamp = systick_get_global_tick();
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));
else
led_set(0, 0);
snprintf(&disp[3][0], 17, "Temp: %s%.1f C", (temp_status == 0 ? "" : temp_status < 0 ? "<" : ">")
, current_temp);
if(systick_ticks_have_passed(quarter_sec_timestamp, 250)) {
safety_adc_status = handle_safety_adc();
quarter_sec_timestamp = systick_get_global_tick();
}
/* Handle error in case PID controller should be active, but temperature measurement failed */
if (pid_controller_active && pt1000_value_status < 0) {
/* Disable the oven controller */
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);
if (systick_ticks_have_passed(display_timestamp, 2) || lcd_ret == LCD_FSM_CALL_AGAIN) {
lcd_ret = lcd_fsm_write_buffer(disp);
display_timestamp = systick_get_global_tick();
}
if (lcd_ret == LCD_FSM_CALL_AGAIN) {
/* Nothing */
} else {
menu_wait_request = reflow_menu_handle();
if (!menu_wait_request)
__WFI();
}
}
}

View File

@ -21,6 +21,17 @@
#include <reflow-controller/oven-driver.h>
#include <reflow-controller/periph-config/oven-driver-hwcfg.h>
#include <stm-periph/clock-enable-manager.h>
#include <reflow-controller/systick.h>
static struct pid_controller oven_pid;
static struct oven_pid_status oven_pid_current_status = {
.active = false,
.aborted = false,
.target_temp = 0.0f,
.current_temp = 0.0f,
.timestamp_last_run = 0ULL
};
void oven_driver_init()
{
@ -57,3 +68,47 @@ void oven_driver_disable()
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));
}
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;
}
void oven_pid_handle(float target_temp, float current_temp)
{
float pid_out;
if (oven_pid_current_status.active) {
if (systick_ticks_have_passed(oven_pid_current_status.timestamp_last_run,
(uint64_t)(oven_pid.sample_period * 1000))) {
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.active = true;
oven_pid_current_status.target_temp = target_temp;
oven_pid_current_status.current_temp = current_temp;
}
}
}
void oven_pid_report_error()
{
oven_pid_current_status.active = false;
oven_pid_current_status.aborted = 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 <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)
{
@ -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_int = k_int;
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->output_sat_max = output_sat_max;
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);
}
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)
{
pid->control_output = 0.0f;

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

@ -0,0 +1,215 @@
/* 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 <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
static char __attribute__((section(".ccmram"))) display_buffer[4][21] = {0};
static struct lcd_menu reflow_menu;
static struct lcd_menu *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;
char buff[20];
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
my_parent = parent;
button_ready = false;
menu_display_clear(menu);
menu->update_display(0, LCD_SHIMATTA_STRING " Reflow");
snprintf(buff, sizeof(buff), "%.*s", LCD_CHAR_WIDTH, xstr(GIT_VER));
menu->update_display(1, buff);
if (strlen(xstr(GIT_VER)) > LCD_CHAR_WIDTH) {
snprintf(buff, sizeof(buff), "%s", &xstr(GIT_VER)[LCD_CHAR_WIDTH]);
menu->update_display(2, buff);
}
menu->update_display(3, __DATE__);
}
if (menu->inputs.push_button == BUTTON_IDLE)
button_ready = true;
if (button_ready &&
(menu->inputs.push_button == BUTTON_SHORT_RELEASED || menu->inputs.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
};
if (entry_type != MENU_ENTRY_CONTINUE) {
menu_display_clear(menu);
update_display_buffer(0, "Main 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);
}
}
if (menu->inputs.push_button == BUTTON_IDLE) {
button_valid = true;
} else if (button_valid && menu->inputs.push_button == BUTTON_SHORT_RELEASED) {
/* Enter currently selected menu_entry */
menu_list_enter_selected_entry(&list, menu);
}
if (menu->inputs.rotary_encoder_delta >= 4) {
menu_list_scroll_down(&list);
menu_ack_rotary_delta(menu);
} else if (menu->inputs.rotary_encoder_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_WAIT_CALL || lcd_tick_ms >= 2) {
lcd_ret = lcd_fsm_write_buffer(display_buffer);
lcd_tick_ms = 0UL;
}
if (lcd_ret == LCD_FSM_CALL_AGAIN)
return 1;
else
return 0;
}
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->AHB1ENR, BITMASK_TO_BITNO(ROTARY_ENCODER_RCC_MASK));
}
void rotary_encoder_zero(void)
{
ROTARY_ENCODER_TIMER->CNT = 0UL;
}

View File

@ -61,6 +61,8 @@ enum safety_adc_check_result safety_adc_check_results(uint16_t vref_result, uint
SAFETY_ADC_TEMP_NOM_MV) / SAFETY_ADC_TEMP_MV_SLOPE) + SAFETY_ADC_TEMP_NOM;
}
/* TODO: Implement safety ADC checking */
return res;
}
@ -132,7 +134,7 @@ enum safety_adc_check_result handle_safety_adc()
break;
default:
safety_meas_channel = SAFETY_ADC_MEAS_VREF;
return -2000;
return SAFETY_ADC_INTERNAL_ERROR;
}
}

View File

@ -19,3 +19,8 @@
*/
#include <reflow-controller/settings/settings.h>
int settings_save_calibration()
{
return 0;
}

View File

@ -44,7 +44,7 @@
extern struct stm_uart shell_uart;
static shellmatta_instance_t shell;
static char shell_buffer[512];
static char history_buffer[1024];
static char history_buffer[600];
static shellmatta_retCode_t shell_cmd_ver(const shellmatta_handle_t handle,
const char *arguments,
@ -59,8 +59,8 @@ static shellmatta_retCode_t shell_cmd_ver(const shellmatta_handle_t handle,
unique_id_get(&high_id, &mid_id, &low_id);
shellmatta_printf(handle, "Reflow Oven Controller Firmware " xstr(GIT_VER) "\r\n"
"Compiled: " __DATE__ " at " __TIME__ "\r\n"
"Serial: %08X-%08X-%08X", high_id, mid_id, low_id);
"Compiled: " __DATE__ " at " __TIME__ "\r\n"
"Serial: %08X-%08X-%08X", high_id, mid_id, low_id);
return SHELLMATTA_OK;
}
@ -239,11 +239,10 @@ static shellmatta_retCode_t shell_cmd_rot(const shellmatta_handle_t handle,
(void)length;
uint32_t rot_val;
int32_t delta;
rot_val = rotary_encoder_get_abs_val();
delta = rotary_encoder_get_change_val();
shellmatta_printf(handle, "Rotary encoder value: %u, delta: %d\r\n", rot_val, delta);
//delta = rotary_encoder_get_change_val();
shellmatta_printf(handle, "Rotary encoder value: %u\r\n", rot_val);
return SHELLMATTA_OK;
}

View File

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

View File

@ -26,8 +26,9 @@
#include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h>
volatile uint32_t wait_tick_ms;
volatile uint64_t global_tick_ms;
volatile uint32_t wait_tick_ms = 0UL;
volatile uint64_t global_tick_ms = 0ULL;
volatile uint32_t lcd_tick_ms = 0UL;
void systick_setup(void)
{
@ -84,4 +85,5 @@ void __attribute__((optimize("O3"))) SysTick_Handler()
/* Increase tick counters */
wait_tick_ms++;
global_tick_ms++;
lcd_tick_ms++;
}

View File

@ -31,8 +31,6 @@
#include <stdbool.h>
#include <string.h>
#define LCD_CHAR_WIDTH 16
static void lcd_port_clear(void)
{
LCD_DPORT->ODR &= ~(LCD_E_MASK);
@ -395,7 +393,7 @@ enum lcd_fsm_ret lcd_fsm_write_buffer(const char (*display_buffer)[21])
state_cnt++;
break;
case 4:
if (!systick_ticks_have_passed(timestamp, 5)) {
if (!systick_ticks_have_passed(timestamp, 4)) {
ret = LCD_FSM_WAIT_CALL;
} else {
ret = LCD_FSM_WAIT_CALL;

View File

@ -19,3 +19,203 @@
*/
#include <reflow-controller/ui/menu.h>
#include <stddef.h>
#include <stdio.h>
void menu_handle(struct lcd_menu *menu, int16_t rotary_encoder_delta, enum button_state push_button)
{
menu_func_t tmp;
if (!menu)
return;
menu->inputs.push_button = push_button;
menu->inputs.rotary_encoder_delta += rotary_encoder_delta;
if (menu->active_entry == NULL)
menu->active_entry = menu->root_entry;
tmp = menu->active_entry;
if (menu->active_entry_type == MENU_ENTRY_FIRST_ENTER) {
menu->active_entry(menu, menu->active_entry_type, menu->init_parent);
} else {
menu->active_entry(menu, menu->active_entry_type, NULL);
}
if (menu->active_entry_type != MENU_ENTRY_CONTINUE && tmp == menu->active_entry) {
menu->active_entry_type = MENU_ENTRY_CONTINUE;
}
}
void menu_init(struct lcd_menu *menu, menu_func_t root_node, void (*display_update)(uint8_t row, const char *data))
{
if (!menu)
return;
menu->root_entry = root_node;
menu->active_entry = root_node;
menu->init_parent = NULL;
menu->inputs.push_button = BUTTON_IDLE;
menu->inputs.rotary_encoder_delta = 0;
menu->active_entry_type = MENU_ENTRY_FIRST_ENTER;
menu->update_display = display_update;
}
void menu_entry_dropback(struct lcd_menu *menu, menu_func_t parent_func)
{
if (!menu)
return;
if (parent_func)
menu->active_entry = parent_func;
else
menu->active_entry = menu->root_entry;
menu->active_entry_type = MENU_ENTRY_DROPBACK;
}
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->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)
{
if (!menu || !menu->update_display)
return;
menu->update_display(row_num, text);
}
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;
}
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, "");
}