From 92784cbe1a9b8a376449c7ae3e22e8f6da617015 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 14 Aug 2021 15:03:01 +0200 Subject: [PATCH] Restored previous work - cleaned up github stuff + restored cad data + restored graph calculations + restored code base --- .github/ISSUE_TEMPLATE/bug_report.md | 27 - .github/ISSUE_TEMPLATE/feature_request.md | 20 - .gitignore | 1 + cad/BOM.ods | Bin 0 -> 6915 bytes cad/adapter/adapter.scad | 41 + cad/adapter/adapter.stl | 646 ++ cad/encoder/encoder.scad | 34 + cad/encoder/encoder.stl | 12042 ++++++++++++++++++++ cad/main_board/main_board_routing.json | 231 + cad/main_board/main_board_schematic.json | 15 + docs/Graphs.ods | Bin 0 -> 26845 bytes src/main.c | 141 +- src/ui.c | 309 + src/ui.h | 76 + src/utils.c | 30 + src/utils.h | 12 + 16 files changed, 13567 insertions(+), 58 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 cad/BOM.ods create mode 100644 cad/adapter/adapter.scad create mode 100644 cad/adapter/adapter.stl create mode 100644 cad/encoder/encoder.scad create mode 100644 cad/encoder/encoder.stl create mode 100644 cad/main_board/main_board_routing.json create mode 100644 cad/main_board/main_board_schematic.json create mode 100644 docs/Graphs.ods create mode 100644 src/ui.c create mode 100644 src/ui.h create mode 100644 src/utils.c create mode 100644 src/utils.h diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index ff4a22a..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG]" -labels: bug -assignees: Steins7 - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Code -2. Specific Hardware used -3. OS and toolchain -4. Conditions for the bug to appear - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 97b15b8..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: "[FEATURE]" -labels: enhancement -assignees: Steins7 - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index 1042eb2..9831b2d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ bin .gdb_history *tags *.taghl +.~lock* diff --git a/cad/BOM.ods b/cad/BOM.ods new file mode 100644 index 0000000000000000000000000000000000000000..c2590441747e2b4c014f6e1b4a1a69f881015f78 GIT binary patch literal 6915 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 literal 0 HcmV?d00001 diff --git a/src/main.c b/src/main.c index fffa31b..19a6906 100644 --- a/src/main.c +++ b/src/main.c @@ -1,31 +1,94 @@ // standard headers #include -#include // driver includes #include "drivers/rcc.h" #include "drivers/io.h" +#include "drivers/lcd.h" +#include "drivers/adc.h" Clock_t sysclks; #include "drivers/timer.h" -extern uint32_t end; +// project headers +#include "ui.h" //------------------------------------------------------------------------------ /* static variables */; int val = 0; //debug led +int read_flag = 0; +int sensor_id = 0; + +// low_pass filter +#define FILTER_LENGTH 10 +int16_t temp_filter[3][FILTER_LENGTH] = {}; + +// adc channels +uint8_t channels[3][2] = { + {4, 5}, + {0, 1}, + {2, 3} +}; + +vars_t vars = { + {}, + 5, + 2, + {}, + 1, + 1 +}; //------------------------------------------------------------------------------ /* Timer IRQ */ static void timeout_cb(void) { io_write(GPIOC, val, PIN_13); val = !val; + + // set temp read flag + read_flag = 1; +} + +int16_t read_temp(uint8_t id) { + // get ADC values + uint32_t data = 0; + data = adc_read(ADC1, channels[id][0]); + data -= adc_read(ADC1, channels[id][1]); + + int16_t voltage = ((data*4) << 8)/4095 ; + + return ((voltage - 0x73) << 8)/0x9 + vars.facs[id][0]; +} + +void process_temps(void) { + // shift filter queue + int32_t sum = 0; + for(int i=0; i<(FILTER_LENGTH-1); ++i) { + temp_filter[sensor_id][i] = temp_filter[sensor_id][i+1]; + sum += temp_filter[sensor_id][i]; + } + + // calculate temp value + temp_filter[sensor_id][FILTER_LENGTH-1] = read_temp(sensor_id); + + // apply filter + sum += temp_filter[sensor_id][FILTER_LENGTH-1]; + vars.temps[sensor_id] = (sum/FILTER_LENGTH) >> 8; + + // switch to next sensor + sensor_id++; + sensor_id = sensor_id%3; } //------------------------------------------------------------------------------ /* main function */ int main(void) { + //initialize variables + val = 0; //debug led + read_flag = 0; + sensor_id = 0; + // configure clocks (necessary before using timers) rcc_config_clock(CLOCK_CONFIG_PERFORMANCE, &sysclks); @@ -34,19 +97,75 @@ int main(void) { return -1; io_write(GPIOC, 1, PIN_13); - // start timed interruption - timer_tick_init(TIM2, 1000, timeout_cb); - timer_start(TIM2); + // configure GPIO for relay + if(io_configure(GPIOB, PIN_11, IO_MODE_OUTPUT | IO_OUT_PUSH_PULL, 0)) + return -1; + io_write(GPIOB, 1, PIN_11); - uint32_t test = (uint32_t)(&end); - test++; - int* tab = (int*)malloc(10*sizeof(int)); - for(int i=0; i<10; ++i) { - tab[i] = i; + // configure GPIOS for temperature sensors + if(io_configure(GPIOA, PIN_0 | PIN_1 | PIN_2 | PIN_3 | PIN_4 | PIN_5, + IO_MODE_INPUT | IO_IN_ANALOG, 0)) return -1; + if(adc_init(ADC1)) return -1; + + // initilize filter + for(int i=0; i<3; ++i) { + int16_t tmp = read_temp(i); + for(int j=0; j= 6) { + count = 0; + + // compute temps + process_temps(); + + // set fan state + if((vars.temps[vars.silo] - vars.temps[T_EXT]) >= + vars.start_treshold) { + if(!vars.fan) { + io_set(GPIOB, PIN_11); + vars.fan = 1; + } + } else if((vars.temps[vars.silo] - vars.temps[T_EXT]) <= + vars.stop_treshold) { + if(vars.fan) { + io_clear(GPIOB, PIN_11); + vars.fan = 0; + } + } + } + } + + // update every 0.2 seconds + ui_update(); +// timer_wait_ms(TIM1, 200, 0); + } return 0; } diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..f341c6f --- /dev/null +++ b/src/ui.c @@ -0,0 +1,309 @@ +#include "ui.h" + +//------------------------------------------------------------------------------ +/* control variables */ +static int state; +static volatile int button_update; +static int sel, prev_sel; +static TIM_TypeDef* enc; +static TIM_TypeDef* tim; +static vars_t* vars; + +/* data variables */ +static const char* ui_default[] = { + "SILO: Te: ""\xDF""C", + "1: ""\xDF""C 2: ""\xDF""C" +}; + +static const char* ui_menu[] = { + " 1-Retour", + " 2-Silo:", + " 3-Start:", + " 4-Stop:", +// " 5-Calibrer Te", +// " 6-Calibrer T1", +// " 7-Calibrer T2", +// " 8-Test ventil", + " 1-Retour" +}; + +static const char* ui_interact[] = { + " lue: ""\xDF""C", + " reelle: ""\xDF""C" +}; + +//------------------------------------------------------------------------------ +/* internal functions */ +void show_bg(const char** bg) { + lcd_send_cmd(LCD_CLEAR); + lcd_send_cmd(LCD_CUR_HOME); + lcd_print(bg[0]); + lcd_set_cursor(0,1); + lcd_print(bg[1]); +} + +void update_entry(uint8_t prev_entry, uint8_t value) { + // get vlaue in str form + char str[8]; //too long to be sure + num2str(str, value, 10); + + // print value in the correct position + if(sel == prev_entry) lcd_set_cursor(14, 1); + else lcd_set_cursor(14, 0); + lcd_print(str); +} + +void change_value(uint8_t entry, uint8_t* entry_val, int max) { + int local_val = *entry_val; + lcd_set_cursor(14,0); + while(!button_update) { + local_val = ((enc->CNT/2)%max) + 1; + char str[8]; + num2str(str, local_val, 10); + lcd_print(str); + lcd_set_cursor(14,0); + timer_wait_ms(TIM1, 200, 0); + } + *entry_val = local_val; +} + +void interact(uint8_t entry, uint8_t* entry_val) { + while(!button_update) { + lcd_set_cursor(10,0); + lcd_print(" "); + lcd_set_cursor(10,0); + + lcd_set_cursor(10,0); + lcd_print(" "); + } +} + +void lcd_sleep() { + // disable screen functions to economize energy + lcd_send_cmd(LCD_DISP_CTRL | LCD_CTRL_DISP_OFF | LCD_CTRL_CUR_OFF + | LCD_CTRL_BLINK_OFF); + + // disable backlight + io_clear(GPIOB, PIN_9); +} + +void lcd_resume() { + // re-enable screen functions + lcd_send_cmd(LCD_DISP_CTRL | LCD_CTRL_DISP_ON | LCD_CTRL_CUR_OFF + | LCD_CTRL_BLINK_OFF); //TODO + + // re-enable backlight + io_set(GPIOB, PIN_9); +} + +//------------------------------------------------------------------------------ +/* timeout timer cb */ +static void ui_sleep(void) { + state = UI_SLEEP; +} + +/* encoder button gpio cb */ +static void ui_button_cb(void) { + for(int i=0; i<1000000; ++i); //avoid double trigger + timer_start(tim); + button_update = 1; +} + +//------------------------------------------------------------------------------ +/* main functions */ +int ui_init(TIM_TypeDef* encoder, TIM_TypeDef* timeout, vars_t* vars_p) { + // save parameters + enc = encoder; + tim = timeout; + vars = vars_p; + + // configure backlight control pin + if(io_configure(GPIOB, PIN_9, IO_MODE_OUTPUT | IO_OUT_PUSH_PULL, 0)) + return -1; + io_set(GPIOB, PIN_9); + + // configure lcd + lcd_init(TIM1, 16, 2); + lcd_send_cmd(LCD_CUR_HOME); + lcd_print(ui_default[0]); + lcd_set_cursor(0, 1); + lcd_print(ui_default[1]); + lcd_send_cmd(LCD_DISP_CTRL | LCD_CTRL_DISP_ON | LCD_CTRL_CUR_OFF | + LCD_CTRL_BLINK_OFF); + + // configure encoder + io_configure(GPIOB, PIN_6 | PIN_7, IO_MODE_INPUT | IO_IN_FLOATING, 0); + timer_enc_init(enc); //TODO fix weird behaviour around 0 + + io_configure(GPIOB, PIN_8, IO_MODE_INPUT | IO_IN_PULL_UP | + IO_IRQ_EDGE_FALL, ui_button_cb); + + // setup state variables + state = UI_SLEEP; + sel = 0; + prev_sel = -1; + + return 0; +} + +void ui_update(void) { + // manage button press + if(button_update) { + button_update = 0; + + switch(state) { + //------------------------- + case UI_DEFAULT: + // reset encoder and previous values + timer_start(enc); + sel = 0; + prev_sel = -1; + + // go to menu screen + state = UI_MENU; + break; + //------------------------- + case UI_MENU: + // go back to default screen whith return + if(sel == UI_RETURN) { + show_bg(ui_default); + lcd_set_cursor(5,0); + char str[4]; + num2str(str, vars->silo, 10); + lcd_print(str); + + state = UI_DEFAULT; + for(int i=0; i<3; ++i) ui_update_temp(i); + break; + } + + // otherwise interact with option + if(sel == UI_CAL_T_EXT || sel == UI_CAL_T1 + || sel == UI_CAL_T2) { + show_bg(ui_interact); + } + lcd_send_cmd(LCD_DISP_CTRL | LCD_CTRL_DISP_ON | + LCD_CTRL_CUR_ON | LCD_CTRL_BLINK_OFF); + timer_start(enc); + state = UI_INTERACT; + break; + //------------------------- + case UI_INTERACT: + state = UI_MENU; + lcd_send_cmd(LCD_DISP_CTRL | LCD_CTRL_DISP_ON | + LCD_CTRL_CUR_OFF | LCD_CTRL_BLINK_OFF); + break; + //------------------------- + case UI_SLEEP: + // re-enable screen + lcd_resume(); + + // re-enable timeout system + timer_wait_ms(tim, TIMEOUT_LENGHT, ui_sleep); + + // go back to default screen + show_bg(ui_default); + lcd_set_cursor(5,0); + char str[4]; + num2str(str, vars->silo, 10); + lcd_print(str); + + state = UI_DEFAULT; + for(int i=0; i<3; ++i) ui_update_temp(i); + break; + } + } + + // manage dynamic draw + switch(state) { + //--------------------- + case UI_MENU: + // get current encoder value + sel = (enc->CNT/2)%(MENU_LENGHT+1); + + // refresh menu if selected item changed + if(sel != prev_sel) { + prev_sel = sel; + timer_start(tim); //refresh timeout timer + show_bg(ui_menu + sel); + lcd_send_cmd(LCD_CUR_HOME); + lcd_print_c(0x7E); + + // show silo value if needed + if(sel == UI_RETURN || sel == UI_SILO) { + update_entry(UI_RETURN, vars->silo); + } + // show start value if needed + if(sel == UI_SILO || sel == UI_START) { + update_entry(UI_SILO, vars->start_treshold); + } + // show start value if needed + if(sel == UI_START || sel == UI_STOP) { + update_entry(UI_START, vars->stop_treshold); + } + } + break; + //--------------------- + case UI_INTERACT: + switch(sel) { + case UI_SILO: + change_value(UI_SILO, &vars->silo, 2); + break; + case UI_START: + change_value(UI_START, &vars->start_treshold, 9); + break; + case UI_STOP: + change_value(UI_STOP, &vars->stop_treshold, 9); + break; + } + //--------------------- + case UI_DEFAULT: + break; + //--------------------- + case UI_SLEEP: + lcd_sleep(); + } +} + +static uint8_t temp_pos[][2] = { + {11, 0}, + { 2, 1}, + {11, 1}}; + +void ui_update_temp(uint8_t id) { + if(state != UI_DEFAULT) return; + + if(id > 2) return; //protect from overflow + uint8_t* pos = temp_pos[id]; + + // prepare data + char str[16]; //longer, in case of error + + // convert int into str + uint32_t nb = num2str(str, (vars->temps[id]), 10); + + // clear previous text + lcd_set_cursor(pos[0],pos[1]); + lcd_print(" "); + + // prepare lcd for write + switch(nb) { + case 1: + lcd_set_cursor(pos[0]+2,pos[1]); + break; + case 2: + lcd_set_cursor(pos[0]+1,pos[1]); + break; + case 3: + lcd_set_cursor(pos[0],pos[1]); + break; + default: // something went wrong + lcd_set_cursor(pos[0],pos[1]); + lcd_print("Err"); + return; + } + + // write value + lcd_print(str); +} + diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..cdce527 --- /dev/null +++ b/src/ui.h @@ -0,0 +1,76 @@ +#ifndef UI_H +#define UI_H + +// drivers headers +#include "drivers/lcd.h" +#include "drivers/timer.h" + +// standard headers +#include + +// project headers +#include "utils.h" + +//------------------------------------------------------------------------------ + +#define TIMEOUT_LENGHT 20000 + +/** vars_t + * structure that contains all the variables needed by the ui and used or + * created elsewhere + */ +typedef struct { + int16_t temps[3]; + uint8_t start_treshold; + uint8_t stop_treshold; + uint8_t facs[3][2]; + uint8_t silo; + uint8_t fan; +} vars_t; + +// ui enums, created to improve readability +enum temp { + T_EXT = 0, + T1 = 1, + T2 = 2 +}; + +enum ui_state { + UI_DEFAULT, + UI_MENU, + UI_INTERACT, + UI_SLEEP +}; + +enum ui_menu { + UI_RETURN = 0, + UI_SILO = 1, + UI_START = 2, + UI_STOP = 3, + UI_CAL_T_EXT = 4, + UI_CAL_T1 = 5, + UI_CAL_T2 = 6, + UI_TEST = 7 +}; + +#define MENU_LENGHT 3 + +//------------------------------------------------------------------------------ +/** ui_init + * init ui, using a timer for the encoder and another for the timeout delay + */ +int ui_init(TIM_TypeDef* encoder, TIM_TypeDef* timeout, vars_t* vars_p); + +/** ui_update + * update the graphical elements and manages the button press + * should be called quite often for a smooth usage + */ +void ui_update(void); + +/** update_temp + * update on the lcd the given value for the corresponding id + */ +void ui_update_temp(uint8_t id); + +#endif + diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..f501625 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,30 @@ +#include "utils.h" + +uint32_t num2str(char *s, int number, uint8_t base) { + + static char hexChars[] = "0123456789ABCDEF"; + char *p = s; + + // manage sign + uint32_t nb = (number < 0 ? -number : number); + + // get digits + do { + *s++ = hexChars[nb % base]; + } while (nb /= base); + + // finalize string + if(number < 0) *s++ = '-'; + *s='\0'; + + // reverse string + int cnt = s - p; + char tmp; + for(int i=0; i + +uint32_t num2str(char *s, int number, uint8_t base); + +//void print_num(int number, + +#endif +