From 13cc2a7e70cf72bbbfc4faeac32a3d6b9c050a71 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Sun, 14 May 2023 01:44:53 +0300 Subject: [PATCH] Add menu, TODO: add `hold key` events to quickly change value in menu setters --- F3:F303/NitrogenFlooding/BMP280.c | 9 +- F3:F303/NitrogenFlooding/adc.c | 12 -- F3:F303/NitrogenFlooding/adc.h | 3 +- F3:F303/NitrogenFlooding/buttons.h | 1 - F3:F303/NitrogenFlooding/hardware.c | 2 +- F3:F303/NitrogenFlooding/hardware.h | 13 ++ F3:F303/NitrogenFlooding/i2c.c | 8 +- F3:F303/NitrogenFlooding/indication.c | 200 +++++++++++------- .../{incication.h => indication.h} | 4 + F3:F303/NitrogenFlooding/main.c | 43 +++- F3:F303/NitrogenFlooding/menu.c | 167 +++++++++++++++ F3:F303/NitrogenFlooding/menu.h | 40 ++++ F3:F303/NitrogenFlooding/nitrogen.bin | Bin 30776 -> 32324 bytes F3:F303/NitrogenFlooding/nitrogen.files | 4 +- F3:F303/NitrogenFlooding/proto.c | 2 +- F3:F303/NitrogenFlooding/screen.c | 52 +++-- F3:F303/NitrogenFlooding/screen.h | 10 +- F3:F303/NitrogenFlooding/version.inc | 4 +- 18 files changed, 442 insertions(+), 132 deletions(-) rename F3:F303/NitrogenFlooding/{incication.h => indication.h} (93%) create mode 100644 F3:F303/NitrogenFlooding/menu.c create mode 100644 F3:F303/NitrogenFlooding/menu.h diff --git a/F3:F303/NitrogenFlooding/BMP280.c b/F3:F303/NitrogenFlooding/BMP280.c index 7cf963d..8981e74 100644 --- a/F3:F303/NitrogenFlooding/BMP280.c +++ b/F3:F303/NitrogenFlooding/BMP280.c @@ -45,14 +45,14 @@ #include "i2c.h" #include "BMP280.h" - -#include - +#include "usb.h" #ifdef EBUG #include "strfunc.h" -#include "usb.h" +extern volatile uint32_t Tms; #endif +#include + #define BMP280_I2C_ADDRESS_MASK (0x76) #define BMP280_I2C_ADDRESS_0 (0x76) #define BMP280_I2C_ADDRESS_1 (0x77) @@ -338,7 +338,6 @@ int BMP280_getdata(float *T, float *P, float *H){ uint8_t data[8]; uint8_t datasz = 8; // amount of bytes to read if(params.ID != BME280_CHIP_ID){ - DBG("Not BME!\n"); if(H) *H = 0; H = NULL; datasz = 6; diff --git a/F3:F303/NitrogenFlooding/adc.c b/F3:F303/NitrogenFlooding/adc.c index 1f84b63..84c2968 100644 --- a/F3:F303/NitrogenFlooding/adc.c +++ b/F3:F303/NitrogenFlooding/adc.c @@ -17,9 +17,6 @@ */ #include "adc.h" -#ifdef EBUG -#include "proto.h" -#endif /** * @brief ADCx_array - arrays for ADC channels with median filtering: @@ -127,9 +124,6 @@ uint16_t getADCval(int nch){ PIX_SORT(p[4], p[2]) ; #undef PIX_SORT #undef PIX_SWAP -#ifdef EBUG - DBG("val: "); printu(p[4]); newline(); -#endif return p[4]; } @@ -137,9 +131,6 @@ uint16_t getADCval(int nch){ float getADCvoltage(int nch){ float v = getADCval(nch) * 3.3; v /= 4096.f; // 12bit ADC -#ifdef EBUG - DBG("v="); printf(v); newline(); -#endif return v; } @@ -151,8 +142,5 @@ float getMCUtemp(){ temperature *= (110.f - 30.f); temperature /= (float)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR); temperature += 30.f; -#ifdef EBUG - DBG("t="); printf(temperature); newline(); -#endif return(temperature); } diff --git a/F3:F303/NitrogenFlooding/adc.h b/F3:F303/NitrogenFlooding/adc.h index a08e5c9..317ec39 100644 --- a/F3:F303/NitrogenFlooding/adc.h +++ b/F3:F303/NitrogenFlooding/adc.h @@ -25,7 +25,8 @@ #define NUMBER_OF_ADC2_CHANNELS (1) // total number of channels - for array #define NUMBER_OF_ADC_CHANNELS ((NUMBER_OF_ADC1_CHANNELS+NUMBER_OF_ADC2_CHANNELS)) - +// number of AIN channels +#define NUMBER_OF_AIN_CHANNELS (10) // ADC1 in1..10 - inner and outern Tsens; ADC2 in1 - ext ADC // channels of ADC in array diff --git a/F3:F303/NitrogenFlooding/buttons.h b/F3:F303/NitrogenFlooding/buttons.h index f981e30..5ccf20b 100644 --- a/F3:F303/NitrogenFlooding/buttons.h +++ b/F3:F303/NitrogenFlooding/buttons.h @@ -37,4 +37,3 @@ extern uint32_t lastUnsleep; // last keys activity time void process_keys(); keyevent keystate(uint8_t k, uint32_t *T); keyevent keyevt(uint8_t k); - diff --git a/F3:F303/NitrogenFlooding/hardware.c b/F3:F303/NitrogenFlooding/hardware.c index 1ba7ef6..0767602 100644 --- a/F3:F303/NitrogenFlooding/hardware.c +++ b/F3:F303/NitrogenFlooding/hardware.c @@ -135,7 +135,7 @@ TRUE_INLINE void iwdg_setup(){ void hw_setup(){ RCC->AHBENR |= RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN; gpio_setup(); - i2c_setup(LOW_SPEED); + i2c_setup(HIGH_SPEED); pwm_setup(); #ifndef EBUG iwdg_setup(); diff --git a/F3:F303/NitrogenFlooding/hardware.h b/F3:F303/NitrogenFlooding/hardware.h index fe872fc..fee3b5e 100644 --- a/F3:F303/NitrogenFlooding/hardware.h +++ b/F3:F303/NitrogenFlooding/hardware.h @@ -83,6 +83,19 @@ #define BTN_ACTIVITY_TIMEOUT (300000) // refresh interval for BME280 and other data - 2.5s #define SENSORS_DATA_TIMEOUT (2500) +// refresh interval for screen windows +#define WINDOW_REFRESH_TIMEOUT (1000) + +// buttons masks bit0 - button0 etc +#define BTN_ESC_MASK (1<<0) +#define BTN_LEFT_MASK (1<<1) +#define BTN_RIGHT_MASK (1<<2) +#define BTN_SEL_MASK (1<<3) + +// global sensors' data +extern float Temperature, Pressure, Humidity, Dewpoint; +extern uint32_t Sens_measured_time; +extern uint16_t ADCvals[]; // buzzer, ADC voltage #define BUZZER_port GPIOB diff --git a/F3:F303/NitrogenFlooding/i2c.c b/F3:F303/NitrogenFlooding/i2c.c index 881577a..e6a5367 100644 --- a/F3:F303/NitrogenFlooding/i2c.c +++ b/F3:F303/NitrogenFlooding/i2c.c @@ -153,12 +153,6 @@ static uint8_t write_i2cs(uint8_t addr, uint8_t *data, uint8_t nbytes, uint8_t s } I2C1->TXDR = data[i]; // send data } - cntr = Tms; - // wait for data gone - while(I2C1->ISR & I2C_ISR_BUSY){ - IWDG->KR = IWDG_REFRESH; - if(Tms - cntr > I2C_TIMEOUT){break;} - } return 1; } @@ -217,7 +211,7 @@ static uint8_t read_i2cb(uint8_t addr, uint8_t *data, uint8_t nbytes, uint8_t bu *data++ = I2C1->RXDR; } return 1; - } +} uint8_t read_i2c(uint8_t addr, uint8_t *data, uint8_t nbytes){ if(isI2Cbusy()) return 0; diff --git a/F3:F303/NitrogenFlooding/indication.c b/F3:F303/NitrogenFlooding/indication.c index e6bacf0..c61d0d3 100644 --- a/F3:F303/NitrogenFlooding/indication.c +++ b/F3:F303/NitrogenFlooding/indication.c @@ -18,9 +18,11 @@ #include "BMP280.h" #include "buttons.h" +#include "fonts.h" #include "hardware.h" #include "ili9341.h" -#include "incication.h" +#include "indication.h" +#include "menu.h" #include "screen.h" #include "strfunc.h" #include "usb.h" @@ -38,6 +40,23 @@ static uint32_t ledT[LEDS_AMOUNT] = {0}; static uint32_t ledH[LEDS_AMOUNT] = {199, 0, 0, 0}; static uint32_t ledL[LEDS_AMOUNT] = {799, 1, 1, 1}; +static void refresh_mainwin(uint8_t evtmask); +static void refresh_menu(uint8_t evtmask); + +// current menu +static menu *curmenu = &mainmenu; +// window handler +window_handler swhandler = refresh_mainwin; + +// Display state: window or menu +typedef enum{ + DISP_WINDOW, // show window + DISP_MENU // show menu +} display_state; + +static display_state dispstate = DISP_WINDOW; +static uint32_t lastTupd = 0; // last main window update time + // led blinking TRUE_INLINE void leds_proc(){ uint32_t v = getPWM(2); @@ -62,61 +81,43 @@ TRUE_INLINE void leds_proc(){ } } -// Display state: main window or menu -typedef enum{ - DISP_MAINWIN, - DISP_MENU -} display_state; - -static display_state dispstate = DISP_MAINWIN; -static uint32_t lastTmeas = 0; // last measurement time - -static void cls(){ // set default colors (bg=0, fg=0xffff) and clear screen - setBGcolor(0); - setFGcolor(0xffff); - ClearScreen(); -} - -static void refresh_mainwin(){ // ask all parameters and refresh main window with new values - DBG("REFRESH main window"); +static void refresh_mainwin(uint8_t evtmask){ // ask all parameters and refresh main window with new values + if(evtmask & BTN_ESC_MASK){ // force refresh + Sens_measured_time = Tms - SENSORS_DATA_TIMEOUT*2; // force refresh + lastTupd = Tms; + return; // just start measurements + } + if(evtmask & BTN_SEL_MASK){ // switch to menu + init_menu(&mainmenu); + return; + } + if(evtmask) return; // left/right buttons - do nothing cls(); - float T, P, H; - BMP280_status s = BMP280_get_status(); - if(s == BMP280_NOTINIT || s == BMP280_ERR) BMP280_init(); SetFontScale(1); // small menu items labels setBGcolor(COLOR_BLACK); setFGcolor(COLOR_LIGHTGREEN); PutStringAt(4, 16, "Temperature Pressure Humidity Dew point"); int y = 37; uint16_t fgcolor; - if(s == BMP280_RDY && BMP280_getdata(&T, &P, &H)){ // show data - if(T < T_MIN || T > T_MAX) fgcolor = COLOR_RED; - else if(T < 0) fgcolor = COLOR_BLUE; + if(Tms - Sens_measured_time < 2*SENSORS_DATA_TIMEOUT){ // data was recently refreshed + if(Temperature < T_MIN || Temperature > T_MAX) fgcolor = COLOR_RED; + else if(Temperature < 0) fgcolor = COLOR_BLUE; else fgcolor = COLOR_GREEN; - setFGcolor(fgcolor); PutStringAt(32, y, float2str(T, 2)); - if(P > P_MAX) fgcolor = COLOR_RED; + setFGcolor(fgcolor); PutStringAt(32, y, float2str(Temperature, 2)); + if(Pressure > P_MAX) fgcolor = COLOR_RED; else fgcolor = COLOR_YELLOW; - setFGcolor(fgcolor); PutStringAt(112, y, float2str(P, 1)); - if(H > H_MAX) fgcolor = COLOR_RED; + setFGcolor(fgcolor); PutStringAt(104, y, float2str(Pressure, 1)); + if(Humidity > H_MAX) fgcolor = COLOR_RED; else fgcolor = COLOR_CHOCOLATE; - setFGcolor(fgcolor); PutStringAt(192, y, float2str(H, 1)); - float dew = Tdew(T, H); - if(T - dew < DEW_MIN) fgcolor = COLOR_RED; + setFGcolor(fgcolor); PutStringAt(184, y, float2str(Humidity, 1)); + if(Temperature - Dewpoint < DEW_MIN) fgcolor = COLOR_RED; else fgcolor = COLOR_LIGHTBLUE; - setFGcolor(fgcolor); PutStringAt(248, y, float2str(dew, 1)); -#ifdef EBUG - USB_sendstr("T="); USB_sendstr(float2str(T, 2)); USB_sendstr("\nP="); - USB_sendstr(float2str(P, 1)); - P *= 0.00750062f; USB_sendstr("\nPmm="); USB_sendstr(float2str(P, 1)); - USB_sendstr("\nH="); USB_sendstr(float2str(H, 1)); - USB_sendstr("\nTdew="); USB_sendstr(float2str(dew, 1)); - newline(); -#endif + setFGcolor(fgcolor); PutStringAt(248, y, float2str(Dewpoint, 1)); }else{ // show "errr" setBGcolor(COLOR_RED); setFGcolor(COLOR_CYAN); CenterStringAt(y, "No signal"); } // display all other data - SetFontScale(3); + SetFontScale(3); setBGcolor(COLOR_BLACK); // TODO: show current level setFGcolor(COLOR_RED); CenterStringAt(130, "Level: NULL"); if(getPWM(2) || getPWM(3)){ @@ -124,12 +125,70 @@ static void refresh_mainwin(){ // ask all parameters and refresh main window wit CenterStringAt(220, "Processing"); } UpdateScreen(0, SCRNH-1); - if(!BMP280_start()) BMP280_init(); // start new measurement } -static void refresh_menu(){ // refresh menu with changed selection +#define refresh_window(e) swhandler(e) + +void init_window(window_handler h){ + dispstate = DISP_WINDOW; + swhandler = h ? h : refresh_mainwin; + refresh_window(0); +} + +void init_menu(menu *m){ + dispstate = DISP_MENU; + curmenu = m; + refresh_menu(0); +} + +static void refresh_menu(uint8_t evtmask){ // refresh menu with changed selection DBG("REFRESH menu"); + if(!curmenu){ + init_window(refresh_mainwin); + return; + } + if(evtmask & BTN_ESC_MASK){ // escape to level upper or to main window + if(curmenu) curmenu = curmenu->parent; + if(!curmenu){ + init_window(refresh_mainwin); + return; + } + } else if(evtmask & BTN_SEL_MASK){ + menuitem *selitem = &curmenu->items[curmenu->selected]; + menu *sub = selitem->submenu; + void (*action)() = selitem->action; + if(action) action(curmenu); + if(sub){ // change to submenu + curmenu = sub; + } else return; + } else if(evtmask & BTN_LEFT_MASK){ // up + if(curmenu->selected) --curmenu->selected; + } else if(evtmask & BTN_RIGHT_MASK){ // down + if(curmenu->selected < curmenu->nitems - 1) ++curmenu->selected; + } cls(); + // check number of first menu item to display + int firstitem = 0, nitemsonscreen = SCRNH / fontheight, half = nitemsonscreen/2; + if(curmenu->nitems > nitemsonscreen){ // menu is too large + if(curmenu->nitems - curmenu->selected <= half) firstitem = curmenu->nitems - nitemsonscreen; + else firstitem = curmenu->selected - half; + } else nitemsonscreen = curmenu->nitems; + if(firstitem < 0) firstitem = 0; + SetFontScale(3); + int Y = fontheight - fontbase + 1; + for(int i = 0; i < nitemsonscreen; ++i, Y += fontheight){ + int idx = firstitem + i; + if(idx >= curmenu->nitems) break; + if(idx == curmenu->selected){ // invert fg/bg + setFGcolor(COLOR_BLACK); + setBGcolor(COLOR_DARKGREEN); + }else{ + setFGcolor(COLOR_DARKGREEN); + setBGcolor(COLOR_BLACK); + } + CenterStringAt(Y, curmenu->items[idx].text); + } + UpdateScreen(0, SCRNH-1); } /* @@ -139,50 +198,33 @@ static void refresh_menu(){ // refresh menu with changed selection * 2 - down * 3 - select/menu */ -TRUE_INLINE void btns_proc(){ +TRUE_INLINE uint8_t btns_proc(){ + static uint32_t lastT = 0; uint8_t evtmask = 0; // bitmask for active buttons (==1) + static keyevent lastevent[BTNSNO] = {0}; + if(lastUnsleep == lastT) return 0; // no buttons activity + lastT = lastUnsleep; for(int i = 0; i < BTNSNO; ++i){ keyevent evt = keystate(i, NULL); // T may be used for doubleclick detection - if(evt == EVT_PRESS || evt == EVT_HOLD) evtmask |= 1< SENSORS_DATA_TIMEOUT){ - refresh_mainwin(); - lastTmeas = Tms; - } - break; - case DISP_MENU: // do nothing - ; - break; + if(ScrnState != SCREEN_RELAX) return; // dont process buttons when screen in updating state + uint8_t e = btns_proc(); + if(dispstate == DISP_WINDOW){ // send refresh by timeout event + if(e) lastTupd = Tms; + else if(Tms - lastTupd > WINDOW_REFRESH_TIMEOUT){ + lastTupd = Tms; + refresh_window(0); + } } } diff --git a/F3:F303/NitrogenFlooding/incication.h b/F3:F303/NitrogenFlooding/indication.h similarity index 93% rename from F3:F303/NitrogenFlooding/incication.h rename to F3:F303/NitrogenFlooding/indication.h index afbf7a2..61bf5fa 100644 --- a/F3:F303/NitrogenFlooding/incication.h +++ b/F3:F303/NitrogenFlooding/indication.h @@ -18,6 +18,8 @@ #pragma once +#include "menu.h" + // temporary defines - should be stored in settings // temperature (degC) limits #define T_MIN (-20.f) @@ -30,3 +32,5 @@ #define DEW_MIN (3.f) void indication_process(); +void init_window(window_handler h); +void init_menu(menu *m); diff --git a/F3:F303/NitrogenFlooding/main.c b/F3:F303/NitrogenFlooding/main.c index 0dd74f7..c220c86 100644 --- a/F3:F303/NitrogenFlooding/main.c +++ b/F3:F303/NitrogenFlooding/main.c @@ -24,7 +24,7 @@ #include "hardware.h" #include "i2c.h" #include "ili9341.h" -#include "incication.h" +#include "indication.h" #include "proto.h" #include "screen.h" #include "strfunc.h" @@ -33,11 +33,43 @@ #define MAXSTRLEN RBINSZ volatile uint32_t Tms = 0; - void sys_tick_handler(void){ ++Tms; } +// global BME data and last measured time +float Temperature, Pressure, Humidity, Dewpoint; +uint32_t Sens_measured_time = 100000; +uint16_t ADCvals[NUMBER_OF_ADC_CHANNELS]; // raw ADC values + +// get BME and sensors data for different calculations +static void getsensors(){ + // turn ON ADC voltage + ADCON(1); + BMP280_status s = BMP280_get_status(); + if(s == BMP280_NOTINIT || s == BMP280_ERR) BMP280_init(); + else if(s == BMP280_RDY && BMP280_getdata(&Temperature, &Pressure, &Humidity)){ + Dewpoint = Tdew(Temperature, Humidity); +#if 0 + USB_sendstr("T="); USB_sendstr(float2str(Temperature, 2)); USB_sendstr("\nP="); + USB_sendstr(float2str(Pressure, 1)); + USB_sendstr("\nPmm="); USB_sendstr(float2str(Pressure * 0.00750062f, 1)); + USB_sendstr("\nH="); USB_sendstr(float2str(Humidity, 1)); + USB_sendstr("\nTdew="); USB_sendstr(float2str(Dewpoint, 1)); + newline(); +#endif + } + // here we should collect and process ADC data - get level etc + for(uint32_t i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i){ + ADCvals[i] = getADCval(i); +#if 0 + USB_sendstr("ADC"); USB_sendstr(u2str(i)); USB_putbyte('='); + USB_sendstr(u2str(ADCvals[i])); newline(); +#endif + } + ADCON(0); // and turn off ADC + if(!BMP280_start()){ BMP280_init(); BMP280_start(); } +} // SPI setup done in `screen.h` int main(void){ @@ -55,10 +87,15 @@ int main(void){ //CAN_setup(the_conf.CANspeed); adc_setup(); BMP280_setup(0); + BMP280_start(); USBPU_ON(); // CAN_message *can_mesg; while(1){ IWDG->KR = IWDG_REFRESH; + if(Tms - Sens_measured_time > SENSORS_DATA_TIMEOUT){ // get BME + getsensors(); + Sens_measured_time = Tms; + } /*CAN_proc(); if(CAN_get_status() == CAN_FIFO_OVERRUN){ USB_sendstr("CAN bus fifo overrun occured!\n"); @@ -105,7 +142,9 @@ int main(void){ LEDS_OFF(); ili9341_off(); }else{ // check operation buttons for menu etc + IWDG->KR = IWDG_REFRESH; indication_process(); + IWDG->KR = IWDG_REFRESH; } }else{ if(Tms - lastUnsleep < BTN_ACTIVITY_TIMEOUT/2){ // recent activity - turn on indication diff --git a/F3:F303/NitrogenFlooding/menu.c b/F3:F303/NitrogenFlooding/menu.c new file mode 100644 index 0000000..0165415 --- /dev/null +++ b/F3:F303/NitrogenFlooding/menu.c @@ -0,0 +1,167 @@ +/* + * This file is part of the nitrogen project. + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#include "adc.h" +#include "hardware.h" +#include "indication.h" +#include "menu.h" +#include "screen.h" +#include "strfunc.h" +#include "usb.h" + +// integer parameter to change +typedef struct{ + uint32_t min; + uint32_t max; + uint32_t cur; + uint32_t *val; +} u32par; + +// current parameter of uint32_t setter +static u32par u32parameter = {0}; + +static const char *parname = NULL; + +// upper level menu to return from subwindow functions +static menu *uplevel = NULL; + +static void showadc(menu *m); +static void htr1(menu *m); +static void htr2(menu *m); + +static void testx(const char *text){ + USB_sendstr(text); + USB_sendstr(" pressed\n"); +} + +static void test3(menu _U_ *m){testx("main 3");} +static void test4(menu _U_ *m){testx("main 4");} +static void stest1(menu _U_ *m){testx("sub 1");} +static void stest2(menu _U_ *m){testx("sub 2");} +static void stest3(menu _U_ *m){testx("sub 3");} +static void stest4(menu _U_ *m){testx("sub 4");} +static void stest5(menu _U_ *m){testx("sub 5");} + + +static menuitem submenu1items[] = { + {"test 1", NULL, stest1}, + {"test 2", NULL, stest2}, + {"test 3", NULL, stest3}, + {"test 4", NULL, stest4}, + {"test 5", NULL, stest5}, +}; + +static menu submenu1 = { + .parent = &mainmenu, + .nitems = 5, + .selected = 0, + .items = submenu1items +}; + +static menuitem mainmenuitems[] = { + {"ADC raw data", NULL, showadc}, + {"Heater1 power", NULL, htr1}, + {"Heater2 power", NULL, htr2}, + {"Submenu1", &submenu1, NULL}, + {"Test3", NULL, test3}, + {"Test4", NULL, test4}, +}; + +menu mainmenu = { + .parent = NULL, + .nitems = 6, + .selected = 0, + .items = mainmenuitems +}; + +static void refresh_adcwin(uint8_t evtmask){ + if(evtmask & BTN_ESC_MASK || evtmask & BTN_SEL_MASK){ // switch to menu + init_menu(uplevel); + return; + } + if(evtmask) return; + cls(); + SetFontScale(1); + int Y = fontheight + 1, wmax = 0; + setFGcolor(COLOR_CYAN); + for(int i = 0; i < NUMBER_OF_AIN_CHANNELS; ++i, Y+=fontheight+2){ + int X = 20; + X = PutStringAt(X, Y, "ADC input "); + X = PutStringAt(X, Y, u2str(i)); + if(X > wmax) wmax = X; + } + wmax += 20; + setFGcolor(COLOR_WHITE); + Y = fontheight + 1; + for(int i = 0; i < NUMBER_OF_AIN_CHANNELS; ++i, Y+=fontheight+2){ + PutStringAt(wmax, Y, u2str(ADCvals[i])); + } + setFGcolor(COLOR_LIGHTGREEN); + Y += 5; + PutStringAt(20, Y, "ADC ext"); PutStringAt(wmax, Y, u2str(ADCvals[ADC_EXT])); + //setFGcolor(COLOR_LIGHTYELLOW); + //Y += 2 + fontheight; + //PutStringAt(20, Y, "T MCU"); PutStringAt(wmax, Y, float2str(getMCUtemp(), 0)); + UpdateScreen(0, -1); +} + +// show RAW ADC data +static void showadc(menu *m){ + init_window(refresh_adcwin); + uplevel = m; +} + +// window of uint32_t setter +static void refresh_u32setter(uint8_t evtmask){ + int selected = 0; + if(evtmask & BTN_ESC_MASK){ // return to main menu + init_menu(uplevel); + return; + } + cls(); + if(evtmask & BTN_SEL_MASK){ // change value + *u32parameter.val = u32parameter.cur; + selected = 1; + } else if(evtmask & BTN_LEFT_MASK){ // decrement + if(u32parameter.cur > u32parameter.min) --u32parameter.cur; + } else if(evtmask & BTN_RIGHT_MASK){ // increment + if(u32parameter.cur < u32parameter.max) ++u32parameter.cur; + } + setFGcolor(COLOR_CHOCOLATE); + SetFontScale(2); + int Y = fontheight + fontheight/2; + CenterStringAt(Y, parname); + SetFontScale(3); + Y += fontheight + fontheight/2; + if(selected) setFGcolor(COLOR_GREEN); + else setFGcolor(COLOR_BLUE); + CenterStringAt(Y, u2str(u32parameter.cur)); +} + +// init pwm setter +static void showpwm(menu *m, int nccr){ + u32parameter.max = 100; + u32parameter.min = 0; + u32parameter.cur = (nccr == 3) ? TIM3->CCR3 : TIM3->CCR4; + u32parameter.val = (nccr == 3) ? (uint32_t*) &TIM3->CCR3 : (uint32_t*) &TIM3->CCR4; + parname = m->items[m->selected].text; + uplevel = m; + init_window(refresh_u32setter); +} +static void htr1(menu *m){showpwm(m, 3);} +static void htr2(menu *m){showpwm(m, 4);} diff --git a/F3:F303/NitrogenFlooding/menu.h b/F3:F303/NitrogenFlooding/menu.h new file mode 100644 index 0000000..6596fde --- /dev/null +++ b/F3:F303/NitrogenFlooding/menu.h @@ -0,0 +1,40 @@ +/* + * This file is part of the nitrogen project. + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#pragma once + +#include + +struct _menu_; +typedef struct{ + const char *text; // name of menu item + struct _menu_ *submenu; // submenu if selected + void (*action)(struct _menu_ *top); // action when item selected (be carefull when both `submenu` and `action` !=NULL), top - parent menu +} menuitem; + +typedef struct _menu_{ + struct _menu_ *parent; // parent menu (when `ESC` pressed) or NULL if should return to main window + int nitems; // amount of `menuitem` + int selected; // selected item + menuitem *items; // array of `menuitem` - menu itself +} menu; + +extern menu mainmenu; + +// subwindow handler +typedef void (*window_handler)(uint8_t evtmask); diff --git a/F3:F303/NitrogenFlooding/nitrogen.bin b/F3:F303/NitrogenFlooding/nitrogen.bin index 61c4c13bce9f9d41017c06c5217029cae4e3f3ce..c64dc54178e040da7e8a93766ac0685cd0993066 100755 GIT binary patch delta 11532 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 delta 9877 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 + #define SWAPINT(a,b) do{register int r = a; a = b; b = r;}while(0) // sprites 8x8 bytes with 8bit foreground and 8bit background @@ -57,25 +59,23 @@ static int updidx = 0; // ==-1 to initialize update // next data portion size (in bytes!), total amount of bytes in update buffer static int portionsz = 0, updbuffsz; // font scale -static uint8_t fontscale = 1; +uint8_t fontscale = 1; +int fontheight = 0, fontbase = 0; static uint16_t fgColor = 0xff, bgColor = 0; // foreground and background colors void setBGcolor(uint16_t c){bgColor = c;} void setFGcolor(uint16_t c){fgColor = c;} -static screen_state ScrnState = SCREEN_INIT; -screen_state getScreenState(){return ScrnState;} +screen_state ScrnState = SCREEN_INIT; /** * @brief UpdateScreen - request to screen updating from lines y0 to y1 (including) * @param y0, y1 - first and last line of field to update */ void UpdateScreen(int y0, int y1){ - if(y0 > y1) SWAPINT(y0, y1); + //if(y0 > y1) SWAPINT(y0, y1); if(y0 < 0) y0 = 0; - if(y1 > SCRNH) y1 = SCRNH - 1; - //USB_sendstr("y0="); USB_sendstr(i2str(y0)); newline(); - //USB_sendstr("y1="); USB_sendstr(i2str(y1)); newline(); + if(y1 > SCRNH || y1 < 0) y1 = SCRNH - 1; uy0 = y0; uy1 = y1; updidx = -1; updbuffsz = SCRNW * (1 + y1 - y0); @@ -93,20 +93,23 @@ void ClearScreen(){ UpdateScreen(0, SCRNH-1); } +#define DRAWPIX(X, Y, pix) \ + if((X) > -1 && (X) < SCRNW && (Y) > -1 && (Y) < SCRNH){ \ + int16_t spritex = (X)/SPRITEWD, spriteidx = spritex + SCRNSPRITEW * ((Y) / SPRITEHT); \ + uint8_t *ptr = &screenbuf[(Y)*SCRNSPRITEW + spritex]; \ + if(pix) *ptr |= 1 << (7 - ((X)%8)); \ + else *ptr &= ~(1 << (7 - ((X)%8))); \ + foreground[spriteidx] = fgColor; \ + background[spriteidx] = bgColor; \ + } + /** * @brief DrawPix - set or clear pixel * @param X, Y - pixel coordinates (could be outside of screen) * @param pix - 1 - foreground, 0 - background */ void DrawPix(int X, int Y, uint8_t pix){ - if(X < 0 || X > SCRNW-1 || Y < 0 || Y > SCRNH-1) return; // outside of screen - // now calculate coordinate of pixel - int16_t spritex = X/SPRITEWD, spriteidx = spritex + SCRNSPRITEW * (Y / SPRITEHT); - uint8_t *ptr = &screenbuf[Y*SCRNSPRITEW + spritex]; // pointer to byte with 8 pixels - if(pix) *ptr |= 1 << (7 - (X%8)); - else *ptr &= ~(1 << (7 - (X%8))); - foreground[spriteidx] = fgColor; - background[spriteidx] = bgColor; + DRAWPIX(X, Y, pix); } /** @@ -135,9 +138,15 @@ void invertSpriteColor(int xmin, int xmax, int ymin, int ymax){ uint8_t SetFontScale(uint8_t scale){ if(scale > 0 && scale <= FONTSCALEMAX) fontscale = scale; + fontheight = curfont->height * fontscale; + fontbase = curfont->baseline * fontscale; return fontscale; } +// left and right bits clearing masks +//static const uint8_t lmask[8] = {0xff, 0b01111111, 0b00111111, 0b00011111, 0b00001111, 0b00000111, 0b00000011, 0b00000001}; +//static const uint8_t rmask[8] = {0, 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b11111100, 0b11111110}; + // TODO in case of low speed: draw at once full line? /** * @brief DrawCharAt - draws character @ position X,Y (this point is left baseline corner of char!) @@ -150,7 +159,7 @@ uint8_t DrawCharAt(int X, int Y, uint8_t Char){ const uint8_t *curchar = font_char(Char); if(!curchar) return 0; // now change Y coordinate to left upper corner of font - Y += fontscale*(curfont->baseline - curfont->height + 1); + Y += fontbase - fontheight + 1; // height and width of letter in pixels uint8_t h = curfont->height, w = *curchar++; // now curchar is pointer to bits array uint8_t lw = curfont->bytes / h; // width of letter in bytes @@ -159,8 +168,10 @@ uint8_t DrawCharAt(int X, int Y, uint8_t Char){ for(uint8_t col = 0; col < w; ++col){ register uint8_t pix = curchar[row*lw + (col/8)] & (1 << (7 - (col%8))); int xx = X + fontscale * col; - for(int y = 0; y < fontscale; ++y) for(int x = 0; x < fontscale; ++x) - DrawPix(xx + x, Y1 + y, pix); + for(int y = 0; y < fontscale; ++y) for(int x = 0; x < fontscale; ++x){ + int nxtx = xx+x, nxty = Y1+y; + DRAWPIX(nxtx, nxty, pix); + } } } return w * fontscale; @@ -193,6 +204,7 @@ int CenterStringAt(int Y, const char *str){ // full string length in pixels int strpixlen(const char *str){ + if(!str) return 0; int l = 0; while(*str){ const uint8_t *c = font_char(*str++); @@ -251,6 +263,8 @@ void process_screen(){ case SCREEN_INIT: // try to init SPI and screen DBG("SCREEN_INIT"); spi_setup(); + choose_font(FONT14); + SetFontScale(1); if(ili9341_init()) ScrnState = SCREEN_RELAX; else{ Tscr_last = Tms; @@ -262,7 +276,7 @@ void process_screen(){ break; case SCREEN_RELAX: // check need of updating if(updidx > -1) return; - DBG("Need to update"); + //DBG("Need to update"); if(!ili9341_setcol(0, SCRNW-1)) return; if(!ili9341_setrow(uy0, uy1)) return; if(!ili9341_writecmd(ILI9341_RAMWR)) return; diff --git a/F3:F303/NitrogenFlooding/screen.h b/F3:F303/NitrogenFlooding/screen.h index fd2e567..ba1a71f 100644 --- a/F3:F303/NitrogenFlooding/screen.h +++ b/F3:F303/NitrogenFlooding/screen.h @@ -19,6 +19,7 @@ #pragma once #include +#include typedef enum{ // screen states SCREEN_INIT // init stage @@ -88,7 +89,9 @@ typedef enum{ // screen states #define COLOR_YELLOWGREEN 0x9E66 -screen_state getScreenState(); +extern screen_state ScrnState; +extern int fontheight, fontbase; + void ClearScreen(); void UpdateScreen(int y0, int y1); void setBGcolor(uint16_t c); @@ -103,3 +106,8 @@ int strpixlen(const char *str); uint8_t *getScreenBuf(); void process_screen(); +TRUE_INLINE void cls(){ // set default colors (bg=0, fg=0xffff) and clear screen + setBGcolor(0); + setFGcolor(0xffff); + ClearScreen(); +} diff --git a/F3:F303/NitrogenFlooding/version.inc b/F3:F303/NitrogenFlooding/version.inc index f64e00a..fdeef24 100644 --- a/F3:F303/NitrogenFlooding/version.inc +++ b/F3:F303/NitrogenFlooding/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "253" -#define BUILD_DATE "2023-05-12" +#define BUILD_NUMBER "280" +#define BUILD_DATE "2023-05-14"