You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
971 lines
21 KiB
971 lines
21 KiB
9 years ago
|
/* menu.c */
|
||
|
|
||
|
//#include <stdio.h>
|
||
|
|
||
|
#include <stddef.h>
|
||
|
#include <string.h>
|
||
|
#include <avr/io.h>
|
||
|
#include <avr/pgmspace.h>
|
||
|
#include <util/atomic.h>
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "menu.h"
|
||
|
#include "display.h"
|
||
|
#include "timer.h"
|
||
|
#include "therm.h"
|
||
|
#include "config.h"
|
||
|
#include "beep.h"
|
||
|
#include "fields.h"
|
||
|
#include "profile.h"
|
||
|
#include "control.h"
|
||
|
|
||
|
uint8_t menu_current;
|
||
|
uint8_t menu_row;
|
||
|
uint8_t field_row;
|
||
|
uint8_t menu_next;
|
||
|
uint8_t menu_redraw;
|
||
|
bool edit_dirty;
|
||
|
|
||
|
#define REDRAW_NONE 0
|
||
|
#define REDRAW_FIELDS 1
|
||
|
#define REDRAW_ALL 2
|
||
|
|
||
|
uint8_t run_status;
|
||
|
|
||
|
bool menu_editing;
|
||
|
uint8_t menu_editing_field;
|
||
|
|
||
|
menu_t *menu_current_p;
|
||
|
|
||
|
const char str_b[] PROGMEM = "B";
|
||
|
const char str_e[] PROGMEM = "E";
|
||
|
const char str_j[] PROGMEM = "J";
|
||
|
const char str_k[] PROGMEM = "K";
|
||
|
const char str_n[] PROGMEM = "N";
|
||
|
const char str_r[] PROGMEM = "R";
|
||
|
const char str_s[] PROGMEM = "S";
|
||
|
const char str_t[] PROGMEM = "T";
|
||
|
|
||
|
PGM_P const enum_thermocouple_types[] PROGMEM = {
|
||
|
str_b,
|
||
|
str_e,
|
||
|
str_j,
|
||
|
str_k,
|
||
|
str_n,
|
||
|
str_r,
|
||
|
str_s,
|
||
|
str_t
|
||
|
};
|
||
|
|
||
|
const char str_negative[] PROGMEM = "-";
|
||
|
const char str_positive[] PROGMEM = "+";
|
||
|
|
||
|
PGM_P const enum_posneg[] PROGMEM = {
|
||
|
str_negative,
|
||
|
str_positive
|
||
|
};
|
||
|
|
||
|
const char str_off[] PROGMEM = "Off";
|
||
|
const char str_on[] PROGMEM = "On";
|
||
|
|
||
|
PGM_P const enum_boolean[] PROGMEM = {
|
||
|
str_off,
|
||
|
str_on
|
||
|
};
|
||
|
|
||
|
const char str_50hz[] PROGMEM = "50Hz";
|
||
|
const char str_60hz[] PROGMEM = "60Hz";
|
||
|
|
||
|
PGM_P const enum_frequency[] PROGMEM = {
|
||
|
str_50hz,
|
||
|
str_60hz
|
||
|
};
|
||
|
|
||
|
const char str_degc[] PROGMEM = "\047C";
|
||
|
const char str_degf[] PROGMEM = "\047F";
|
||
|
/* str_k already defined */
|
||
|
const char str_degde[] PROGMEM = "\047De";
|
||
|
const char str_degn[] PROGMEM = "\047N";
|
||
|
const char str_degr[] PROGMEM = "\047R";
|
||
|
const char str_degre[] PROGMEM = "\047Re";
|
||
|
const char str_degro[] PROGMEM = "\047Ro";
|
||
|
const char str_mev[] PROGMEM = "meV";
|
||
|
|
||
|
#define TEMPERATURE_CELSIUS 0
|
||
|
#define TEMPERATURE_FAHRENHEIT 1
|
||
|
#define TEMPERATURE_KELVIN 2
|
||
|
#define TEMPERATURE_DELISLE 3
|
||
|
#define TEMPERATURE_NEWTON 4
|
||
|
#define TEMPERATURE_RANKINE 5
|
||
|
#define TEMPERATURE_REAUMUR 6
|
||
|
#define TEMPERATURE_ROMER 7
|
||
|
#define TEMPERATURE_MEV 8
|
||
|
|
||
|
PGM_P const enum_units[] PROGMEM = {
|
||
|
str_degc,
|
||
|
str_degf,
|
||
|
str_k,
|
||
|
str_degde,
|
||
|
str_degn,
|
||
|
str_degr,
|
||
|
str_degre,
|
||
|
str_degro,
|
||
|
str_mev
|
||
|
};
|
||
|
|
||
|
const char str_seconds[] PROGMEM = "s";
|
||
|
const char str_minutes[] PROGMEM = "m";
|
||
|
const char str_hours[] PROGMEM = "h";
|
||
|
|
||
|
PGM_P const enum_time_units[] PROGMEM = {
|
||
|
str_seconds,
|
||
|
str_minutes,
|
||
|
str_hours
|
||
|
};
|
||
|
|
||
|
const char str_0[] PROGMEM = "0";
|
||
|
const char str_1[] PROGMEM = "1";
|
||
|
const char str_2[] PROGMEM = "2";
|
||
|
const char str_3[] PROGMEM = "3";
|
||
|
const char str_4[] PROGMEM = "4";
|
||
|
const char str_5[] PROGMEM = "5";
|
||
|
const char str_6[] PROGMEM = "6";
|
||
|
const char str_7[] PROGMEM = "7";
|
||
|
const char str_8[] PROGMEM = "8";
|
||
|
const char str_9[] PROGMEM = "9";
|
||
|
|
||
|
PGM_P const enum_digits[] PROGMEM = {
|
||
|
str_0,
|
||
|
str_1,
|
||
|
str_2,
|
||
|
str_3,
|
||
|
str_4,
|
||
|
str_5,
|
||
|
str_6,
|
||
|
str_7,
|
||
|
str_8,
|
||
|
str_9,
|
||
|
};
|
||
|
|
||
|
const char str_start[] PROGMEM = "Start?";
|
||
|
const char str_running[] PROGMEM = "Run";
|
||
|
const char str_finished[] PROGMEM = "Done";
|
||
|
//const char str_error[] PROGMEM = "Error";
|
||
|
|
||
|
PGM_P const enum_status[] PROGMEM = {
|
||
|
str_start,
|
||
|
str_running,
|
||
|
str_finished
|
||
|
// str_error
|
||
|
};
|
||
|
|
||
|
#define RUN_STATUS_READY 0
|
||
|
#define RUN_STATUS_RUNNING 1
|
||
|
#define RUN_STATUS_FINISHED 2
|
||
|
#define RUN_STATUS_ERROR 3
|
||
|
|
||
|
const char str_empty[] PROGMEM = "";
|
||
|
const char str_fault[] PROGMEM = "FAULT";
|
||
|
//const char str_open[] PROGMEM = "Connect thermocouple";
|
||
|
//const char str_chiprange[] PROGMEM = "Cold junction range";
|
||
|
//const char str_temprange[] PROGMEM = "Thermocouple range";
|
||
|
|
||
|
PGM_P const enum_faultstatus[] PROGMEM = {
|
||
|
str_empty,
|
||
|
str_fault
|
||
|
// str_open,
|
||
|
// str_chiprange,
|
||
|
// str_temprange
|
||
|
};
|
||
|
|
||
|
const char str_run_profile[] PROGMEM = "Run profile";
|
||
|
const char str_edit_profile[] PROGMEM = "Edit profile";
|
||
|
const char str_configure[] PROGMEM = "Configure";
|
||
|
const char str_faultstatus[] PROGMEM = "\x0a";
|
||
|
const char str_oven[] PROGMEM = "Oven \x08\x02\x02\x02\x02.\x02\x04\x04\x04";
|
||
|
const char str_ambient[] PROGMEM = "Ambient \x08\x02\x02\x02\x02.\x02\x04\x04\x04";
|
||
|
|
||
|
PGM_P const menu_main[] PROGMEM = {
|
||
|
str_run_profile,
|
||
|
str_edit_profile,
|
||
|
str_configure,
|
||
|
NULL,
|
||
|
str_faultstatus,
|
||
|
NULL,
|
||
|
str_oven,
|
||
|
str_ambient
|
||
|
};
|
||
|
|
||
|
const char str_placeholder[] PROGMEM = "Placeholder";
|
||
|
const char str_selecting_profile[] PROGMEM = "Selecting profile";
|
||
|
const char str_str[] PROGMEM = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01";
|
||
|
const char str_editstr[] PROGMEM = "\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11";
|
||
|
|
||
|
PGM_P const menu_select_profile[] PROGMEM = {
|
||
|
str_str,
|
||
|
str_str,
|
||
|
str_str,
|
||
|
str_str,
|
||
|
str_str,
|
||
|
str_str,
|
||
|
str_str,
|
||
|
str_str
|
||
|
};
|
||
|
|
||
|
const char str_editing_profile[] PROGMEM = "Editing profile";
|
||
|
const char str_name_field[] PROGMEM = "> Name field <";
|
||
|
const char str_start_temperature[] PROGMEM = "Start temp \x18\x12\x12\x12\x12\x04<<";
|
||
|
const char str_edit_field[] PROGMEM = "\x02\x02.\x02\x02/ \x12\x12\x12\x12\x13 \x18\x12\x12\x12\x12\x04<<";
|
||
|
|
||
|
PGM_P const menu_edit_profile[] PROGMEM = {
|
||
|
str_editstr,
|
||
|
str_start_temperature,
|
||
|
str_edit_field,
|
||
|
str_edit_field,
|
||
|
str_edit_field,
|
||
|
str_edit_field,
|
||
|
str_edit_field,
|
||
|
str_edit_field,
|
||
|
};
|
||
|
|
||
|
const char str_contrast[] PROGMEM = "Contrast \x12\x12\x12";
|
||
|
const char str_beep[] PROGMEM = "Beep \x15>>";
|
||
|
const char str_thermocouple[] PROGMEM = "Thermocouple \x16";
|
||
|
const char str_frequency[] PROGMEM = "Frequency \x17>>>";
|
||
|
const char str_units[] PROGMEM = "Units \x14>>";
|
||
|
const char str_p[] PROGMEM = "P \x18\x12.\x12\x12";
|
||
|
const char str_i[] PROGMEM = "I \x18\x12.\x12\x12";
|
||
|
const char str_d[] PROGMEM = "D \x18\x12.\x12\x12";
|
||
|
|
||
|
PGM_P const menu_configuration[] PROGMEM = {
|
||
|
str_contrast,
|
||
|
str_beep,
|
||
|
str_thermocouple,
|
||
|
str_frequency,
|
||
|
str_units,
|
||
|
str_p,
|
||
|
str_i,
|
||
|
str_d
|
||
|
};
|
||
|
|
||
|
const char str_running_profile[] PROGMEM = "\x09\x09\x09\x09\x09\x09\x12\x12\x12\x12\x13\x08\x02\x02\x02\x02.\x02\x04<<";
|
||
|
|
||
|
PGM_P const menu_run_profile[] PROGMEM = {
|
||
|
str_running_profile,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static void main_menu_fields(uint8_t row);
|
||
|
static void configuration_fields(uint8_t row);
|
||
|
static void save_configuration_fields(uint8_t row);
|
||
|
static void select_profile_fields(uint8_t row);
|
||
|
static void edit_profile_fields(uint8_t row);
|
||
|
static void save_profile_fields(uint8_t row);
|
||
|
static void update_run_status(uint8_t row);
|
||
|
|
||
|
menu_t menus[] = {
|
||
|
{ menu_main, 0, 2, main_menu_fields, NULL },
|
||
|
{ menu_select_profile, 0, 7, select_profile_fields, NULL },
|
||
|
{ menu_configuration, 0, 7, configuration_fields, save_configuration_fields },
|
||
|
{ menu_edit_profile, 0, 7, edit_profile_fields, save_profile_fields },
|
||
|
{ menu_run_profile, 7, 7, update_run_status, NULL }
|
||
|
};
|
||
|
|
||
|
#define TEMPERATURE_UNIT 0x1000L
|
||
|
|
||
|
int8_t temp_n[] = {1, 9, 1, -3, 33, 9, 4, 21, 10};
|
||
|
int8_t temp_d[] = {1, 5, 1, 2, 100, 5, 5, 40, 116};
|
||
|
int32_t temp_off[] = {0, 32*TEMPERATURE_UNIT, 1118822, -614400, 0, 2013880, 0, 30720, 96450};
|
||
|
|
||
|
int32_t temperature_from_kelvin(temp_t temp)
|
||
|
{
|
||
|
uint8_t units = config.units;
|
||
|
int32_t temp32 = temp;
|
||
|
int32_t source = ((temp32-2732)<<12) / 10;
|
||
|
int32_t result;
|
||
|
result = source * temp_n[units] / temp_d[units] + temp_off[units];
|
||
|
// printf("temp = %d, source = %d (%x), result = %d (%x)\n", temp, source, source, result, result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
temp_t temperature_to_kelvin(int32_t source)
|
||
|
{
|
||
|
uint8_t units = config.units;
|
||
|
int32_t result = (source - temp_off[units]) * temp_d[units] / temp_n[units];
|
||
|
|
||
|
return therm_reduce(result);
|
||
|
}
|
||
|
|
||
|
static void update_run_status(uint8_t row)
|
||
|
{
|
||
|
temp_t temp = therm_temp();
|
||
|
write_field_enum(0, run_status);
|
||
|
write_field_uint16(1, control_now());
|
||
|
write_field_enum(2, 0);
|
||
|
write_field_temperature(3, TRUE, temp);
|
||
|
}
|
||
|
|
||
|
static void main_menu_fields(uint8_t row)
|
||
|
{
|
||
|
temp_t temp;
|
||
|
|
||
|
switch (row) {
|
||
|
case 4:
|
||
|
write_field_enum(0, therm_fault()?1:0);
|
||
|
return;
|
||
|
case 6:
|
||
|
temp = therm_temp();
|
||
|
break;
|
||
|
case 7:
|
||
|
temp = therm_coldtemp();
|
||
|
break;
|
||
|
default:
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
write_field_temperature(0, TRUE, temp);
|
||
|
}
|
||
|
|
||
|
static void configuration_fields(uint8_t row)
|
||
|
{
|
||
|
if (row == 0) {
|
||
|
write_field_uint8(0, config.contrast);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (row > 4) {
|
||
|
int16_t value16;
|
||
|
int32_t val;
|
||
|
switch (row) {
|
||
|
case 5:
|
||
|
value16 = config.p;
|
||
|
break;
|
||
|
case 6:
|
||
|
value16 = config.i;
|
||
|
break;
|
||
|
case 7:
|
||
|
value16 = config.d;
|
||
|
break;
|
||
|
default:
|
||
|
return;
|
||
|
}
|
||
|
val = (int32_t)value16 << 5;
|
||
|
write_field_fracint(0, TRUE, TRUE, val);
|
||
|
} else {
|
||
|
uint8_t value8;
|
||
|
switch (row) {
|
||
|
case 1:
|
||
|
value8 = config.beep;
|
||
|
break;
|
||
|
case 2:
|
||
|
value8 = config.thermocouple_type;
|
||
|
break;
|
||
|
case 3:
|
||
|
value8 = config.frequency;
|
||
|
break;
|
||
|
case 4:
|
||
|
value8 = config.units;
|
||
|
break;
|
||
|
}
|
||
|
write_field_enum(0, value8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void save_configuration_fields(uint8_t row)
|
||
|
{
|
||
|
if (row == 0) {
|
||
|
config.contrast = read_field_uint8(0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (row > 4) {
|
||
|
int16_t value16 = read_field_fracint(0, TRUE) >> 5;
|
||
|
switch (row) {
|
||
|
case 5:
|
||
|
config.p = value16;
|
||
|
break;
|
||
|
case 6:
|
||
|
config.i = value16;
|
||
|
break;
|
||
|
case 7:
|
||
|
config.d = value16;
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
uint8_t value8 = read_field_enum(0);
|
||
|
switch (row) {
|
||
|
case 1:
|
||
|
config.beep = value8;
|
||
|
break;
|
||
|
case 2:
|
||
|
config.thermocouple_type = value8;
|
||
|
break;
|
||
|
case 3:
|
||
|
config.frequency = value8;
|
||
|
break;
|
||
|
case 4:
|
||
|
config.units = value8;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void select_profile_fields(uint8_t row)
|
||
|
{
|
||
|
profile_select(row);
|
||
|
memcpy(field_values, &profile_p->name, PROFILE_NAME_LENGTH);
|
||
|
}
|
||
|
|
||
|
static void save_profile_fields(uint8_t row)
|
||
|
{
|
||
|
int32_t temp;
|
||
|
temp_t k_temp;
|
||
|
uint8_t n;
|
||
|
|
||
|
if (row == 0) {
|
||
|
memcpy(profile_p->name, field_values, PROFILE_NAME_LENGTH);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
n = find_field_number(find_editable_field(0, FALSE));
|
||
|
|
||
|
temp = read_field_fracint(n+((row == 1)?0:2), FALSE);
|
||
|
|
||
|
k_temp = temperature_to_kelvin(temp);
|
||
|
set_profile_temperature(k_temp);
|
||
|
|
||
|
if (row == 1)
|
||
|
return;
|
||
|
|
||
|
set_profile_time(row-2, read_field_uint16(n), read_field_enum(n+1));
|
||
|
}
|
||
|
|
||
|
static void edit_profile_fields(uint8_t row)
|
||
|
{
|
||
|
int32_t unit_temp, prev_temp, step;
|
||
|
temp_t temp;
|
||
|
uint16_t time;
|
||
|
uint8_t time_units;
|
||
|
if (row == 0) {
|
||
|
memcpy(field_values, profile_p->name, PROFILE_NAME_LENGTH);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
temp = get_profile_temperature(row);
|
||
|
write_field_temperature(((row == 1)?0:4), FALSE, temp);
|
||
|
if (row == 1)
|
||
|
return;
|
||
|
|
||
|
unit_temp = temperature_from_kelvin(temp);
|
||
|
prev_temp = temperature_from_kelvin(get_profile_temperature(row-1));
|
||
|
|
||
|
time = profile_get_time(row-2);
|
||
|
time_units = profile_get_time_units(row-2);
|
||
|
|
||
|
if (time == 0)
|
||
|
step = 0;
|
||
|
else
|
||
|
step = (unit_temp - prev_temp) / time;
|
||
|
|
||
|
write_field_fracint(0, FALSE, TRUE, step);
|
||
|
write_field_uint16(2, time);
|
||
|
write_field_enum(3, time_units);
|
||
|
}
|
||
|
|
||
|
#define MENU_MAIN 0
|
||
|
#define MENU_SELECT_PROFILE 1
|
||
|
#define MENU_CONFIGURATION 2
|
||
|
#define MENU_EDIT_PROFILE 3
|
||
|
#define MENU_RUN_PROFILE 4
|
||
|
|
||
|
char menu_getchar(uint8_t col)
|
||
|
{
|
||
|
PGM_P ptr = get_string(&menu_current_p->text[field_row]);
|
||
|
|
||
|
if (!ptr)
|
||
|
return '\0';
|
||
|
|
||
|
if (col > 20)
|
||
|
return '\0';
|
||
|
|
||
|
return pgm_read_byte(ptr + col);
|
||
|
}
|
||
|
|
||
|
field_t fields[] = {
|
||
|
{NULL /* ASCII */, 127-32, 1},
|
||
|
{enum_digits, ARRAY_SIZE(enum_digits), 1},
|
||
|
{enum_time_units, ARRAY_SIZE(enum_time_units), 1},
|
||
|
{enum_units, ARRAY_SIZE(enum_units), 3},
|
||
|
{enum_boolean, ARRAY_SIZE(enum_boolean), 3},
|
||
|
{enum_thermocouple_types, ARRAY_SIZE(enum_thermocouple_types), 1},
|
||
|
{enum_frequency, ARRAY_SIZE(enum_frequency), 4},
|
||
|
{enum_posneg, ARRAY_SIZE(enum_posneg), 1},
|
||
|
{enum_status, ARRAY_SIZE(enum_status), 6},
|
||
|
{enum_faultstatus, ARRAY_SIZE(enum_faultstatus), 21}
|
||
|
};
|
||
|
|
||
|
static void display_field(char c, uint8_t col)
|
||
|
{
|
||
|
uint8_t i;
|
||
|
|
||
|
if (field_is_text(c)) {
|
||
|
if ((field_values[col] >= 32) && (field_values[col] < 32+field_display_entries(c)))
|
||
|
display_putchar(field_values[col]);
|
||
|
else
|
||
|
display_putchar('?');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (field_values[col] >= field_display_entries(c)) {
|
||
|
for (i = 0; i < field_length(c); i++)
|
||
|
display_putchar('?');
|
||
|
} else {
|
||
|
PGM_P str = field_text(c, field_values[col]);
|
||
|
i = 0;
|
||
|
if (field_length(c) > 1) {
|
||
|
i = field_length(c) - strlen_P(str);
|
||
|
if (menu_getchar(col+1) == '>')
|
||
|
for (; i; i--)
|
||
|
display_putchar(' ');
|
||
|
}
|
||
|
display_putstr_P(str);
|
||
|
for (; i; i--)
|
||
|
display_putchar(' ');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define menu_draw_current_row() menu_draw_row(menu_row)
|
||
|
|
||
|
static void menu_set_fields(uint8_t row)
|
||
|
{
|
||
|
menu_t *menu = menu_current_p;
|
||
|
field_row = row;
|
||
|
if ((!menu_editing) && menu->get_field)
|
||
|
menu->get_field(row);
|
||
|
}
|
||
|
|
||
|
static void menu_draw_row(uint8_t row)
|
||
|
{
|
||
|
uint8_t col;
|
||
|
display_settextpos(0, row);
|
||
|
display_setinverse((row == menu_row) && !menu_editing);
|
||
|
if (get_string(&menu_current_p->text[row])) {
|
||
|
char c;
|
||
|
menu_set_fields(row);
|
||
|
col = 0;
|
||
|
while ((c = menu_getchar(col)) != '\0') {
|
||
|
if (menu_editing)
|
||
|
display_setinverse(col == menu_editing_field);
|
||
|
if (is_field(c)) {
|
||
|
display_field(c, col);
|
||
|
col += field_length(c);
|
||
|
} else {
|
||
|
display_putchar(c);
|
||
|
col++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
display_clearline();
|
||
|
}
|
||
|
|
||
|
#if 1
|
||
|
static void display_menu(bool all)
|
||
|
{
|
||
|
uint8_t row;
|
||
|
|
||
|
for (row = 0; row < 8; row++) {
|
||
|
field_row = row;
|
||
|
if (all || (find_field(0) != 255))
|
||
|
menu_draw_row(row);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void set_menu(uint8_t new_menu)
|
||
|
{
|
||
|
if (new_menu == menu_current)
|
||
|
return;
|
||
|
menu_current = new_menu;
|
||
|
menu_current_p = &menus[new_menu];
|
||
|
menu_redraw = TRUE;
|
||
|
// memcpy_P(&menu_row, &menu_current_p->first_line, sizeof(menu_row));
|
||
|
menu_row = 0;
|
||
|
menu_editing = FALSE;
|
||
|
}
|
||
|
|
||
|
void menu_init(void)
|
||
|
{
|
||
|
/* Make sure set_menu does its work */
|
||
|
menu_current = MENU_MAIN+1;
|
||
|
set_menu(MENU_MAIN);
|
||
|
display_init();
|
||
|
}
|
||
|
|
||
|
#define BUTTON_UP 1
|
||
|
#define BUTTON_DOWN 2
|
||
|
#define BUTTON_LEFT 4
|
||
|
#define BUTTON_RIGHT 3
|
||
|
|
||
|
static void menu_end_edit(void)
|
||
|
{
|
||
|
menu_t *menu = menu_current_p;
|
||
|
menu_editing = FALSE;
|
||
|
if (menu->put_field)
|
||
|
menu->put_field(menu_row);
|
||
|
menu_redraw = TRUE;
|
||
|
}
|
||
|
|
||
|
static void menu_edit_new_field(uint8_t field, bool left)
|
||
|
{
|
||
|
field = find_editable_field(field, left);
|
||
|
menu_editing_field = field;
|
||
|
menu_editing = TRUE;
|
||
|
edit_dirty = FALSE;
|
||
|
if (field == 255)
|
||
|
menu_end_edit();
|
||
|
menu_draw_row(menu_row);
|
||
|
}
|
||
|
|
||
|
static void increment_value(int8_t inc)
|
||
|
{
|
||
|
uint8_t val = field_values[menu_editing_field];
|
||
|
uint8_t c = menu_getchar(menu_editing_field);
|
||
|
|
||
|
uint8_t min = 0;
|
||
|
uint8_t max = field_display_entries(c) - 1;
|
||
|
|
||
|
if (field_is_text(c)) {
|
||
|
min += 32;
|
||
|
max += 32;
|
||
|
}
|
||
|
|
||
|
if (inc < 0 && val == min)
|
||
|
val = max;
|
||
|
else if (inc > 0 && val == max)
|
||
|
val = min;
|
||
|
else
|
||
|
val += inc;
|
||
|
|
||
|
field_values[menu_editing_field] = val;
|
||
|
edit_dirty = TRUE;
|
||
|
}
|
||
|
|
||
|
static void menu_navigate_up(void)
|
||
|
{
|
||
|
if (menu_row == menu_current_p->first_line)
|
||
|
return;
|
||
|
|
||
|
menu_row--;
|
||
|
menu_draw_row(menu_row+1);
|
||
|
menu_draw_row(menu_row);
|
||
|
}
|
||
|
|
||
|
static void menu_navigate_down(void)
|
||
|
{
|
||
|
if (menu_row == menu_current_p->last_line)
|
||
|
return;
|
||
|
|
||
|
menu_row++;
|
||
|
menu_draw_row(menu_row-1);
|
||
|
menu_draw_row(menu_row);
|
||
|
}
|
||
|
|
||
|
static void menu_navigate_into(void)
|
||
|
{
|
||
|
uint8_t menu = menu_current;
|
||
|
switch (menu) {
|
||
|
case MENU_MAIN:
|
||
|
switch (menu_row) {
|
||
|
case 0:
|
||
|
menu = MENU_SELECT_PROFILE;
|
||
|
menu_next = MENU_RUN_PROFILE;
|
||
|
break;
|
||
|
case 1:
|
||
|
menu = MENU_SELECT_PROFILE;
|
||
|
menu_next = MENU_EDIT_PROFILE;
|
||
|
break;
|
||
|
case 2:
|
||
|
menu = MENU_CONFIGURATION;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MENU_SELECT_PROFILE:
|
||
|
menu = menu_next;
|
||
|
profile_select(menu_row);
|
||
|
break;
|
||
|
|
||
|
case MENU_RUN_PROFILE:
|
||
|
control_start();
|
||
|
run_status = RUN_STATUS_RUNNING;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
menu_edit_new_field(21, TRUE);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
set_menu(menu);
|
||
|
}
|
||
|
|
||
|
static void menu_navigate_back(void)
|
||
|
{
|
||
|
uint8_t menu = menu_current;
|
||
|
switch (menu) {
|
||
|
case MENU_EDIT_PROFILE:
|
||
|
profile_save();
|
||
|
case MENU_RUN_PROFILE:
|
||
|
menu = MENU_SELECT_PROFILE;
|
||
|
break;
|
||
|
default:
|
||
|
menu = MENU_MAIN;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
set_menu(menu);
|
||
|
}
|
||
|
|
||
|
static void menu_process_leftright(bool left)
|
||
|
{
|
||
|
int8_t increment = left?(-1):1;
|
||
|
|
||
|
if (menu_editing)
|
||
|
menu_edit_new_field(menu_editing_field+increment, left);
|
||
|
else {
|
||
|
if (left)
|
||
|
menu_navigate_back();
|
||
|
else
|
||
|
menu_navigate_into();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void menu_process_updown(bool up)
|
||
|
{
|
||
|
int8_t increment = up?1:(-1);
|
||
|
|
||
|
if (menu_editing) {
|
||
|
increment_value(increment);
|
||
|
menu_draw_row(menu_row);
|
||
|
} else {
|
||
|
if (up)
|
||
|
menu_navigate_up();
|
||
|
else
|
||
|
menu_navigate_down();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define GRAPH_POINTS 128
|
||
|
//#define GRAPH_POINTS 128
|
||
|
//#define GRAPH_POINTS 12
|
||
|
#define GRAPH_START_X 0
|
||
|
|
||
|
#define GRAPH_HEIGHT (GRAPH_PAGES * 8)
|
||
|
#define GRAPH_PAGES 7
|
||
|
#define GRAPH_START_Y 1
|
||
|
|
||
|
//uint8_t graph_desired[GRAPH_POINTS+1];
|
||
|
//uint8_t graph_actual[GRAPH_POINTS+1];
|
||
|
uint8_t graph_actual[4];
|
||
|
|
||
|
static void minmax_mark(uint8_t *min_p, uint8_t *max_p, uint8_t mid)
|
||
|
{
|
||
|
uint8_t min = *min_p;
|
||
|
uint8_t max = *max_p;
|
||
|
|
||
|
if (mid == 255) {
|
||
|
*min_p = 255;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (min == 255)
|
||
|
min = mid;
|
||
|
if (max == 255)
|
||
|
max = mid;
|
||
|
if (max < min) {
|
||
|
uint8_t tmp = min;
|
||
|
min = max;
|
||
|
max = tmp;
|
||
|
}
|
||
|
*min_p = mid - (mid-min)/2;
|
||
|
*max_p = (max-mid)/2 + mid;
|
||
|
}
|
||
|
|
||
|
temp_t graph_temp_min, graph_temp_range;
|
||
|
uint32_t time_per_unit;
|
||
|
uint8_t graph_cursor;
|
||
|
|
||
|
static uint8_t graph_value(temp_t temp);
|
||
|
|
||
|
static uint8_t graph_at(uint8_t col)
|
||
|
{
|
||
|
return graph_value(temperature_at_time(col*time_per_unit));
|
||
|
}
|
||
|
|
||
|
static void graphics_draw_column(uint8_t col)
|
||
|
{
|
||
|
uint8_t data;
|
||
|
uint8_t pr_mid, pr_min, pr_max;
|
||
|
uint8_t act_mid, act_min, act_max;
|
||
|
uint8_t i;
|
||
|
uint8_t page;
|
||
|
uint8_t act_col;
|
||
|
|
||
|
if (col >= GRAPH_POINTS)
|
||
|
return;
|
||
|
|
||
|
if (col == graph_cursor)
|
||
|
act_col = 1;
|
||
|
else
|
||
|
act_col = 0;
|
||
|
|
||
|
pr_mid = graph_at(col);
|
||
|
pr_min = graph_at(col-1);
|
||
|
pr_max = graph_at(col+1);
|
||
|
|
||
|
minmax_mark(&pr_min, &pr_max, pr_mid);
|
||
|
|
||
|
act_min = graph_actual[act_col];
|
||
|
act_mid = graph_actual[act_col+1];
|
||
|
act_max = graph_actual[act_col+2];
|
||
|
|
||
|
minmax_mark(&act_min, &act_max, act_mid);
|
||
|
|
||
|
if (col == 0) {
|
||
|
pr_min = 0;
|
||
|
pr_max = GRAPH_HEIGHT-1;
|
||
|
}
|
||
|
|
||
|
page = GRAPH_PAGES-1;
|
||
|
for (i = 0; i < GRAPH_HEIGHT; i++) {
|
||
|
data <<= 1;
|
||
|
if ((i >= pr_min && i <= pr_max) ||
|
||
|
(i >= act_min && i <= act_max) ||
|
||
|
(i == 0)) {
|
||
|
data |= 1;
|
||
|
}
|
||
|
if ((i & 0x7) == 7) {
|
||
|
if (graph_cursor == col)
|
||
|
data |= 0x55;
|
||
|
display_setposition(col, GRAPH_START_Y + page);
|
||
|
display_data(1, &data);
|
||
|
page--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void graphics_draw_screen(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* initialise profile */
|
||
|
|
||
|
for (i = 0; i < GRAPH_POINTS; i++)
|
||
|
graphics_draw_column(i);
|
||
|
}
|
||
|
|
||
|
static uint8_t __attribute__ ((noinline)) graph_value(temp_t temp)
|
||
|
{
|
||
|
int32_t v;
|
||
|
|
||
|
if (temp == INVALID_TEMPERATURE)
|
||
|
v = 255;
|
||
|
else
|
||
|
v = ((int32_t)temp - graph_temp_min) * GRAPH_HEIGHT / graph_temp_range;
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
static void graphics_init(void)
|
||
|
{
|
||
|
uint8_t i;
|
||
|
uint32_t total_time;
|
||
|
temp_t temp_min, temp_max;
|
||
|
uint8_t *graph_actual_p;
|
||
|
|
||
|
run_status = RUN_STATUS_READY;
|
||
|
|
||
|
temp_min = temp_max = get_profile_temperature(1);
|
||
|
|
||
|
total_time = 0;
|
||
|
|
||
|
for (i = 0; i < 6; i++) {
|
||
|
temp_t temp = get_profile_temperature(i+2);
|
||
|
uint32_t time = profile_time(i);
|
||
|
if (time == 0)
|
||
|
continue;
|
||
|
total_time += time;
|
||
|
if (temp > temp_max)
|
||
|
temp_max = temp;
|
||
|
if (temp < temp_min)
|
||
|
temp_min = temp;
|
||
|
}
|
||
|
|
||
|
time_per_unit = (total_time + GRAPH_POINTS - 1) / GRAPH_POINTS;
|
||
|
|
||
|
temp_min -= 10 * 10;
|
||
|
temp_max += 10 * 10;
|
||
|
graph_temp_range = temp_max - temp_min;
|
||
|
graph_temp_min = temp_min;
|
||
|
|
||
|
graph_cursor = 255;
|
||
|
|
||
|
graph_actual_p = &graph_actual[0];
|
||
|
|
||
|
for (i = 0; i < 4; i++)
|
||
|
*(graph_actual_p++) = 255;
|
||
|
}
|
||
|
|
||
|
static void graphics_poll(void)
|
||
|
{
|
||
|
if (menu_redraw) {
|
||
|
graphics_init();
|
||
|
graphics_draw_screen();
|
||
|
}
|
||
|
if (run_status == RUN_STATUS_RUNNING) {
|
||
|
uint8_t col = control_now() / time_per_unit;
|
||
|
if (col <= GRAPH_POINTS) {
|
||
|
if (col != graph_cursor) {
|
||
|
graph_actual[0] = graph_actual[1];
|
||
|
graph_actual[1] = graph_actual[2];
|
||
|
}
|
||
|
graph_actual[2] = graph_value(therm_temp());
|
||
|
} else {
|
||
|
run_status = RUN_STATUS_FINISHED;
|
||
|
}
|
||
|
graph_cursor = col;
|
||
|
graphics_draw_column(col-1);
|
||
|
graphics_draw_column(col);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void menu_poll(void)
|
||
|
{
|
||
|
uint8_t button_pressed;
|
||
|
|
||
|
ATOMIC_BLOCK(ATOMIC_FORCEON) {
|
||
|
button_pressed = button;
|
||
|
button = 0;
|
||
|
}
|
||
|
|
||
|
if (button_pressed == BUTTON_LEFT || button_pressed == BUTTON_RIGHT)
|
||
|
menu_process_leftright(button_pressed == BUTTON_LEFT);
|
||
|
if (button_pressed == BUTTON_UP || button_pressed == BUTTON_DOWN)
|
||
|
menu_process_updown(button_pressed == BUTTON_UP);
|
||
|
|
||
|
if (menu_redraw || (menu_current == MENU_RUN_PROFILE) || (menu_current == MENU_MAIN)) {
|
||
|
display_menu(menu_redraw);
|
||
|
menu_set_fields(menu_row);
|
||
|
}
|
||
|
|
||
|
if (menu_current == MENU_RUN_PROFILE) {
|
||
|
graphics_poll();
|
||
|
}
|
||
|
menu_redraw = FALSE;
|
||
|
}
|