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 61c4c13..c64dc54 100755 Binary files a/F3:F303/NitrogenFlooding/nitrogen.bin and b/F3:F303/NitrogenFlooding/nitrogen.bin differ diff --git a/F3:F303/NitrogenFlooding/nitrogen.files b/F3:F303/NitrogenFlooding/nitrogen.files index 5b5717e..79bd216 100644 --- a/F3:F303/NitrogenFlooding/nitrogen.files +++ b/F3:F303/NitrogenFlooding/nitrogen.files @@ -24,9 +24,11 @@ i2c.c i2c.h ili9341.c ili9341.h -incication.h indication.c +indication.h main.c +menu.c +menu.h pdnuart.c pdnuart.h proto.c diff --git a/F3:F303/NitrogenFlooding/proto.c b/F3:F303/NitrogenFlooding/proto.c index 542a505..9b8532d 100644 --- a/F3:F303/NitrogenFlooding/proto.c +++ b/F3:F303/NitrogenFlooding/proto.c @@ -366,7 +366,7 @@ static int sputstr(const char _U_ *cmd, int parno, const char *c, int32_t _U_ i) } static int sstate(const char _U_ *cmd, int _U_ parno, const char _U_ *c, int32_t _U_ i){ const char *s = "unknown"; - switch(getScreenState()){ + switch(ScrnState){ case SCREEN_INIT: s = "init"; break; diff --git a/F3:F303/NitrogenFlooding/screen.c b/F3:F303/NitrogenFlooding/screen.c index 63dcb40..4236c54 100644 --- a/F3:F303/NitrogenFlooding/screen.c +++ b/F3:F303/NitrogenFlooding/screen.c @@ -28,6 +28,8 @@ #include "strfunc.h" #endif +#include + #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"