Reflow Controller GUI: Move gui to gui.c file in UI subfolder
This commit is contained in:
		
							
								
								
									
										384
									
								
								stm-firmware/ui/gui.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								stm-firmware/ui/gui.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,384 @@
 | 
			
		||||
/* 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/ui/gui.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/safety-controller.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 IN_SECTION(.ccm.bss) display_buffer[4][21] = {0};
 | 
			
		||||
static struct lcd_menu IN_SECTION(.ccm.bss) reflow_menu;
 | 
			
		||||
#define 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 gui_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 " LCD_OHM_SYMBOL_STRING, 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);
 | 
			
		||||
 | 
			
		||||
		(void)safety_controller_get_analog_mon_value(ERR_AMON_UC_TEMP, &tmp);
 | 
			
		||||
		snprintf(line, sizeof(line), "Tj: %.1f " LCD_DEGREE_SYMBOL_STRING "C", tmp);
 | 
			
		||||
		menu->update_display(2, line);
 | 
			
		||||
 | 
			
		||||
		(void)safety_controller_get_analog_mon_value(ERR_AMON_VREF, &tmp);
 | 
			
		||||
		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 gui_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
 | 
			
		||||
{
 | 
			
		||||
	static void *my_parent;
 | 
			
		||||
	static int page = 0;
 | 
			
		||||
	static int last_page = -1;
 | 
			
		||||
	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;
 | 
			
		||||
	bool button_ready;
 | 
			
		||||
 | 
			
		||||
	if (entry_type == MENU_ENTRY_FIRST_ENTER) {
 | 
			
		||||
		uptime_secs = 0ULL;
 | 
			
		||||
		page = 0;
 | 
			
		||||
		last_page = -1;
 | 
			
		||||
		my_parent = parent;
 | 
			
		||||
		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:
 | 
			
		||||
		if (last_page == 0)
 | 
			
		||||
			break;
 | 
			
		||||
		last_page = 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:
 | 
			
		||||
		if (last_page == 1)
 | 
			
		||||
			break;
 | 
			
		||||
		last_page = 1;
 | 
			
		||||
		menu_lcd_output(menu, 0, "Version Number:");
 | 
			
		||||
		menu_lcd_outputf(menu, 1, "%.*s", LCD_CHAR_WIDTH, xstr(GIT_VER));
 | 
			
		||||
		if (strlen(xstr(GIT_VER)) > LCD_CHAR_WIDTH) {
 | 
			
		||||
			menu_lcd_outputf(menu, 2, "%s", &xstr(GIT_VER)[LCD_CHAR_WIDTH]);
 | 
			
		||||
		}
 | 
			
		||||
#ifdef DEBUGBUILD
 | 
			
		||||
		menu_lcd_output(menu, 3, "Page 2/5 [DEBUG]");
 | 
			
		||||
#else
 | 
			
		||||
		menu_lcd_output(menu, 3, "Page 2/5");
 | 
			
		||||
#endif
 | 
			
		||||
		break;
 | 
			
		||||
	case 2:
 | 
			
		||||
		if (last_page == 2)
 | 
			
		||||
			break;
 | 
			
		||||
		last_page = 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:
 | 
			
		||||
		if (last_page == 3)
 | 
			
		||||
			break;
 | 
			
		||||
		last_page = 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:
 | 
			
		||||
		last_page = 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;
 | 
			
		||||
		last_page = -1;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	push_button = menu_get_button_state(menu);
 | 
			
		||||
	button_ready = menu_get_button_ready_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 gui_menu_err_flags(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
 | 
			
		||||
{
 | 
			
		||||
	static void *my_parent = NULL;
 | 
			
		||||
	static uint8_t offset;
 | 
			
		||||
	static uint64_t timestamp;
 | 
			
		||||
	static bool end_of_list_reached = true;
 | 
			
		||||
	bool state;
 | 
			
		||||
	enum button_state push_button;
 | 
			
		||||
	int16_t rot;
 | 
			
		||||
	uint32_t i;
 | 
			
		||||
	char name[64];
 | 
			
		||||
	int32_t line_counter;
 | 
			
		||||
	bool skip_err_flag_prefix;
 | 
			
		||||
	bool try_ack = false;
 | 
			
		||||
	enum safety_flag flag;
 | 
			
		||||
	bool button_ready;
 | 
			
		||||
	const char *err_flag_prefix = "ERR_FLAG_";
 | 
			
		||||
 | 
			
		||||
	push_button = menu_get_button_state(menu);
 | 
			
		||||
	rot = menu_get_rotary_delta(menu);
 | 
			
		||||
 | 
			
		||||
	if (entry_type != MENU_ENTRY_CONTINUE) {
 | 
			
		||||
		if (entry_type == MENU_ENTRY_FIRST_ENTER) {
 | 
			
		||||
			my_parent = parent;
 | 
			
		||||
			offset = 0;
 | 
			
		||||
			end_of_list_reached = true;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if (push_button == BUTTON_IDLE && rot == 0) {
 | 
			
		||||
			if (!systick_ticks_have_passed(timestamp, 150))
 | 
			
		||||
				return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	button_ready = menu_get_button_ready_state(menu);
 | 
			
		||||
	if (push_button == BUTTON_SHORT_RELEASED && button_ready) {
 | 
			
		||||
		menu_entry_dropback(menu, my_parent);
 | 
			
		||||
	} if (push_button == BUTTON_LONG && button_ready) {
 | 
			
		||||
		try_ack = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rot >= 4 || rot <= -4) {
 | 
			
		||||
		menu_ack_rotary_delta(menu);
 | 
			
		||||
		if (rot > 0) {
 | 
			
		||||
			if (!end_of_list_reached)
 | 
			
		||||
				offset++;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (offset > 0)
 | 
			
		||||
				offset--;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	menu_display_clear(menu);
 | 
			
		||||
 | 
			
		||||
	line_counter = -offset;
 | 
			
		||||
	for (i = 0; i < safety_controller_get_flag_count(); i++) {
 | 
			
		||||
		(void)safety_controller_get_flag_by_index(i, &state, &flag);
 | 
			
		||||
		if (try_ack)
 | 
			
		||||
			safety_controller_ack_flag(flag);
 | 
			
		||||
		if (state) {
 | 
			
		||||
			if (line_counter >= 0 && line_counter < 4) {
 | 
			
		||||
				safety_controller_get_flag_name_by_index(i, name, sizeof(name));
 | 
			
		||||
				if (!strncmp(name, err_flag_prefix, 9)) {
 | 
			
		||||
					skip_err_flag_prefix = true;
 | 
			
		||||
				} else {
 | 
			
		||||
					skip_err_flag_prefix = false;
 | 
			
		||||
				}
 | 
			
		||||
				menu_lcd_outputf(menu, line_counter, "%s",
 | 
			
		||||
						 (skip_err_flag_prefix ? &name[9] : name));
 | 
			
		||||
			}
 | 
			
		||||
			line_counter++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	end_of_list_reached = (line_counter > 4 ? false : true);
 | 
			
		||||
 | 
			
		||||
	timestamp = systick_get_global_tick();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
 | 
			
		||||
{
 | 
			
		||||
	(void)parent;
 | 
			
		||||
	static struct menu_list list;
 | 
			
		||||
	bool menu_changed = false;
 | 
			
		||||
	static const char * const root_entry_names[] = {
 | 
			
		||||
		"About",
 | 
			
		||||
		"Monitoring",
 | 
			
		||||
		"Error Flags",
 | 
			
		||||
		NULL
 | 
			
		||||
	};
 | 
			
		||||
	static const menu_func_t root_entry_funcs[] = {
 | 
			
		||||
		gui_menu_about,
 | 
			
		||||
		gui_menu_monitor,
 | 
			
		||||
		gui_menu_err_flags,
 | 
			
		||||
	};
 | 
			
		||||
	enum button_state push_button;
 | 
			
		||||
	int16_t rot_delta;
 | 
			
		||||
 | 
			
		||||
	if (entry_type != MENU_ENTRY_CONTINUE) {
 | 
			
		||||
		menu_changed = true;
 | 
			
		||||
		menu_display_clear(menu);
 | 
			
		||||
		update_display_buffer(0, "Main Menu");
 | 
			
		||||
		menu_ack_rotary_delta(menu);
 | 
			
		||||
		if (entry_type == MENU_ENTRY_FIRST_ENTER) {
 | 
			
		||||
			list.entry_names = root_entry_names;
 | 
			
		||||
			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 (menu_get_button_ready_state(menu) && push_button == BUTTON_SHORT_RELEASED) {
 | 
			
		||||
		/* Enter currently selected menu_entry */
 | 
			
		||||
		menu_list_enter_selected_entry(&list, menu);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rot_delta >= 4) {
 | 
			
		||||
		menu_list_scroll_down(&list);
 | 
			
		||||
		menu_ack_rotary_delta(menu);
 | 
			
		||||
		menu_changed = true;
 | 
			
		||||
	} else if (rot_delta <= -4) {
 | 
			
		||||
		menu_list_scroll_up(&list);
 | 
			
		||||
		menu_ack_rotary_delta(menu);
 | 
			
		||||
		menu_changed = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (menu_changed)
 | 
			
		||||
		menu_list_display(&list, 1, 3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gui_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 gui_init()
 | 
			
		||||
{
 | 
			
		||||
	rotary_encoder_setup();
 | 
			
		||||
	button_init();
 | 
			
		||||
	lcd_init();
 | 
			
		||||
 | 
			
		||||
	menu_init(reflow_menu_ptr, gui_menu_root_entry, update_display_buffer);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user