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.
970 lines
21 KiB
970 lines
21 KiB
/* 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; |
|
}
|
|
|