Add menu, TODO: add hold key events to quickly change value in menu setters

This commit is contained in:
Edward Emelianov 2023-05-14 01:44:53 +03:00
parent e6dc55764b
commit 13cc2a7e70
18 changed files with 442 additions and 132 deletions

View File

@ -45,14 +45,14 @@
#include "i2c.h" #include "i2c.h"
#include "BMP280.h" #include "BMP280.h"
#include "usb.h"
#include <math.h>
#ifdef EBUG #ifdef EBUG
#include "strfunc.h" #include "strfunc.h"
#include "usb.h" extern volatile uint32_t Tms;
#endif #endif
#include <math.h>
#define BMP280_I2C_ADDRESS_MASK (0x76) #define BMP280_I2C_ADDRESS_MASK (0x76)
#define BMP280_I2C_ADDRESS_0 (0x76) #define BMP280_I2C_ADDRESS_0 (0x76)
#define BMP280_I2C_ADDRESS_1 (0x77) #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 data[8];
uint8_t datasz = 8; // amount of bytes to read uint8_t datasz = 8; // amount of bytes to read
if(params.ID != BME280_CHIP_ID){ if(params.ID != BME280_CHIP_ID){
DBG("Not BME!\n");
if(H) *H = 0; if(H) *H = 0;
H = NULL; H = NULL;
datasz = 6; datasz = 6;

View File

@ -17,9 +17,6 @@
*/ */
#include "adc.h" #include "adc.h"
#ifdef EBUG
#include "proto.h"
#endif
/** /**
* @brief ADCx_array - arrays for ADC channels with median filtering: * @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]) ; PIX_SORT(p[4], p[2]) ;
#undef PIX_SORT #undef PIX_SORT
#undef PIX_SWAP #undef PIX_SWAP
#ifdef EBUG
DBG("val: "); printu(p[4]); newline();
#endif
return p[4]; return p[4];
} }
@ -137,9 +131,6 @@ uint16_t getADCval(int nch){
float getADCvoltage(int nch){ float getADCvoltage(int nch){
float v = getADCval(nch) * 3.3; float v = getADCval(nch) * 3.3;
v /= 4096.f; // 12bit ADC v /= 4096.f; // 12bit ADC
#ifdef EBUG
DBG("v="); printf(v); newline();
#endif
return v; return v;
} }
@ -151,8 +142,5 @@ float getMCUtemp(){
temperature *= (110.f - 30.f); temperature *= (110.f - 30.f);
temperature /= (float)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR); temperature /= (float)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR);
temperature += 30.f; temperature += 30.f;
#ifdef EBUG
DBG("t="); printf(temperature); newline();
#endif
return(temperature); return(temperature);
} }

View File

@ -25,7 +25,8 @@
#define NUMBER_OF_ADC2_CHANNELS (1) #define NUMBER_OF_ADC2_CHANNELS (1)
// total number of channels - for array // total number of channels - for array
#define NUMBER_OF_ADC_CHANNELS ((NUMBER_OF_ADC1_CHANNELS+NUMBER_OF_ADC2_CHANNELS)) #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 // ADC1 in1..10 - inner and outern Tsens; ADC2 in1 - ext ADC
// channels of ADC in array // channels of ADC in array

View File

@ -37,4 +37,3 @@ extern uint32_t lastUnsleep; // last keys activity time
void process_keys(); void process_keys();
keyevent keystate(uint8_t k, uint32_t *T); keyevent keystate(uint8_t k, uint32_t *T);
keyevent keyevt(uint8_t k); keyevent keyevt(uint8_t k);

View File

@ -135,7 +135,7 @@ TRUE_INLINE void iwdg_setup(){
void hw_setup(){ void hw_setup(){
RCC->AHBENR |= RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN; RCC->AHBENR |= RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN;
gpio_setup(); gpio_setup();
i2c_setup(LOW_SPEED); i2c_setup(HIGH_SPEED);
pwm_setup(); pwm_setup();
#ifndef EBUG #ifndef EBUG
iwdg_setup(); iwdg_setup();

View File

@ -83,6 +83,19 @@
#define BTN_ACTIVITY_TIMEOUT (300000) #define BTN_ACTIVITY_TIMEOUT (300000)
// refresh interval for BME280 and other data - 2.5s // refresh interval for BME280 and other data - 2.5s
#define SENSORS_DATA_TIMEOUT (2500) #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 // buzzer, ADC voltage
#define BUZZER_port GPIOB #define BUZZER_port GPIOB

View File

@ -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 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; 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; *data++ = I2C1->RXDR;
} }
return 1; return 1;
} }
uint8_t read_i2c(uint8_t addr, uint8_t *data, uint8_t nbytes){ uint8_t read_i2c(uint8_t addr, uint8_t *data, uint8_t nbytes){
if(isI2Cbusy()) return 0; if(isI2Cbusy()) return 0;

View File

@ -18,9 +18,11 @@
#include "BMP280.h" #include "BMP280.h"
#include "buttons.h" #include "buttons.h"
#include "fonts.h"
#include "hardware.h" #include "hardware.h"
#include "ili9341.h" #include "ili9341.h"
#include "incication.h" #include "indication.h"
#include "menu.h"
#include "screen.h" #include "screen.h"
#include "strfunc.h" #include "strfunc.h"
#include "usb.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 ledH[LEDS_AMOUNT] = {199, 0, 0, 0};
static uint32_t ledL[LEDS_AMOUNT] = {799, 1, 1, 1}; 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 // led blinking
TRUE_INLINE void leds_proc(){ TRUE_INLINE void leds_proc(){
uint32_t v = getPWM(2); uint32_t v = getPWM(2);
@ -62,61 +81,43 @@ TRUE_INLINE void leds_proc(){
} }
} }
// Display state: main window or menu static void refresh_mainwin(uint8_t evtmask){ // ask all parameters and refresh main window with new values
typedef enum{ if(evtmask & BTN_ESC_MASK){ // force refresh
DISP_MAINWIN, Sens_measured_time = Tms - SENSORS_DATA_TIMEOUT*2; // force refresh
DISP_MENU lastTupd = Tms;
} display_state; return; // just start measurements
}
static display_state dispstate = DISP_MAINWIN; if(evtmask & BTN_SEL_MASK){ // switch to menu
static uint32_t lastTmeas = 0; // last measurement time init_menu(&mainmenu);
return;
static void cls(){ // set default colors (bg=0, fg=0xffff) and clear screen }
setBGcolor(0); if(evtmask) return; // left/right buttons - do nothing
setFGcolor(0xffff);
ClearScreen();
}
static void refresh_mainwin(){ // ask all parameters and refresh main window with new values
DBG("REFRESH main window");
cls(); 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 SetFontScale(1); // small menu items labels
setBGcolor(COLOR_BLACK); setFGcolor(COLOR_LIGHTGREEN); setBGcolor(COLOR_BLACK); setFGcolor(COLOR_LIGHTGREEN);
PutStringAt(4, 16, "Temperature Pressure Humidity Dew point"); PutStringAt(4, 16, "Temperature Pressure Humidity Dew point");
int y = 37; int y = 37;
uint16_t fgcolor; uint16_t fgcolor;
if(s == BMP280_RDY && BMP280_getdata(&T, &P, &H)){ // show data if(Tms - Sens_measured_time < 2*SENSORS_DATA_TIMEOUT){ // data was recently refreshed
if(T < T_MIN || T > T_MAX) fgcolor = COLOR_RED; if(Temperature < T_MIN || Temperature > T_MAX) fgcolor = COLOR_RED;
else if(T < 0) fgcolor = COLOR_BLUE; else if(Temperature < 0) fgcolor = COLOR_BLUE;
else fgcolor = COLOR_GREEN; else fgcolor = COLOR_GREEN;
setFGcolor(fgcolor); PutStringAt(32, y, float2str(T, 2)); setFGcolor(fgcolor); PutStringAt(32, y, float2str(Temperature, 2));
if(P > P_MAX) fgcolor = COLOR_RED; if(Pressure > P_MAX) fgcolor = COLOR_RED;
else fgcolor = COLOR_YELLOW; else fgcolor = COLOR_YELLOW;
setFGcolor(fgcolor); PutStringAt(112, y, float2str(P, 1)); setFGcolor(fgcolor); PutStringAt(104, y, float2str(Pressure, 1));
if(H > H_MAX) fgcolor = COLOR_RED; if(Humidity > H_MAX) fgcolor = COLOR_RED;
else fgcolor = COLOR_CHOCOLATE; else fgcolor = COLOR_CHOCOLATE;
setFGcolor(fgcolor); PutStringAt(192, y, float2str(H, 1)); setFGcolor(fgcolor); PutStringAt(184, y, float2str(Humidity, 1));
float dew = Tdew(T, H); if(Temperature - Dewpoint < DEW_MIN) fgcolor = COLOR_RED;
if(T - dew < DEW_MIN) fgcolor = COLOR_RED;
else fgcolor = COLOR_LIGHTBLUE; else fgcolor = COLOR_LIGHTBLUE;
setFGcolor(fgcolor); PutStringAt(248, y, float2str(dew, 1)); setFGcolor(fgcolor); PutStringAt(248, y, float2str(Dewpoint, 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
}else{ // show "errr" }else{ // show "errr"
setBGcolor(COLOR_RED); setFGcolor(COLOR_CYAN); setBGcolor(COLOR_RED); setFGcolor(COLOR_CYAN);
CenterStringAt(y, "No signal"); CenterStringAt(y, "No signal");
} }
// display all other data // display all other data
SetFontScale(3); SetFontScale(3); setBGcolor(COLOR_BLACK);
// TODO: show current level // TODO: show current level
setFGcolor(COLOR_RED); CenterStringAt(130, "Level: NULL"); setFGcolor(COLOR_RED); CenterStringAt(130, "Level: NULL");
if(getPWM(2) || getPWM(3)){ if(getPWM(2) || getPWM(3)){
@ -124,12 +125,70 @@ static void refresh_mainwin(){ // ask all parameters and refresh main window wit
CenterStringAt(220, "Processing"); CenterStringAt(220, "Processing");
} }
UpdateScreen(0, SCRNH-1); 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"); 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(); 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 * 2 - down
* 3 - select/menu * 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) 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){ for(int i = 0; i < BTNSNO; ++i){
keyevent evt = keystate(i, NULL); // T may be used for doubleclick detection keyevent evt = keystate(i, NULL); // T may be used for doubleclick detection
if(evt == EVT_PRESS || evt == EVT_HOLD) evtmask |= 1<<i; if(evt == EVT_PRESS && lastevent[i] != EVT_PRESS) evtmask |= 1<<i;
} lastevent[i] = evt;
// now check all buttons
if(evtmask & 1<<0){ // escape to main window or force refresh
if(dispstate == DISP_MENU){
dispstate = DISP_MAINWIN;
}
lastTmeas = Tms - SENSORS_DATA_TIMEOUT*2; // force refresh
}
if(dispstate == DISP_MENU){ // buttons 'up'/'down' works only in menu mode
if(evtmask & 1<<1){ // up
;
}
if(evtmask & 1<<2){ // down
;
}
}
if(evtmask & 1<<3){ // select/menu
if(dispstate == DISP_MAINWIN){ // switch to menu mode
dispstate = DISP_MENU;
refresh_menu();
}else{ // select
;
}
} }
if(!evtmask) return 0;
if(dispstate == DISP_WINDOW) refresh_window(evtmask);
else refresh_menu(evtmask);
return evtmask;
} }
void indication_process(){ void indication_process(){
if(!LEDsON) return; if(!LEDsON) return;
leds_proc(); leds_proc();
btns_proc(); if(ScrnState != SCREEN_RELAX) return; // dont process buttons when screen in updating state
switch(dispstate){ uint8_t e = btns_proc();
case DISP_MAINWIN: if(dispstate == DISP_WINDOW){ // send refresh by timeout event
if(Tms - lastTmeas > SENSORS_DATA_TIMEOUT){ if(e) lastTupd = Tms;
refresh_mainwin(); else if(Tms - lastTupd > WINDOW_REFRESH_TIMEOUT){
lastTmeas = Tms; lastTupd = Tms;
} refresh_window(0);
break; }
case DISP_MENU: // do nothing
;
break;
} }
} }

View File

@ -18,6 +18,8 @@
#pragma once #pragma once
#include "menu.h"
// temporary defines - should be stored in settings // temporary defines - should be stored in settings
// temperature (degC) limits // temperature (degC) limits
#define T_MIN (-20.f) #define T_MIN (-20.f)
@ -30,3 +32,5 @@
#define DEW_MIN (3.f) #define DEW_MIN (3.f)
void indication_process(); void indication_process();
void init_window(window_handler h);
void init_menu(menu *m);

View File

@ -24,7 +24,7 @@
#include "hardware.h" #include "hardware.h"
#include "i2c.h" #include "i2c.h"
#include "ili9341.h" #include "ili9341.h"
#include "incication.h" #include "indication.h"
#include "proto.h" #include "proto.h"
#include "screen.h" #include "screen.h"
#include "strfunc.h" #include "strfunc.h"
@ -33,11 +33,43 @@
#define MAXSTRLEN RBINSZ #define MAXSTRLEN RBINSZ
volatile uint32_t Tms = 0; volatile uint32_t Tms = 0;
void sys_tick_handler(void){ void sys_tick_handler(void){
++Tms; ++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` // SPI setup done in `screen.h`
int main(void){ int main(void){
@ -55,10 +87,15 @@ int main(void){
//CAN_setup(the_conf.CANspeed); //CAN_setup(the_conf.CANspeed);
adc_setup(); adc_setup();
BMP280_setup(0); BMP280_setup(0);
BMP280_start();
USBPU_ON(); USBPU_ON();
// CAN_message *can_mesg; // CAN_message *can_mesg;
while(1){ while(1){
IWDG->KR = IWDG_REFRESH; IWDG->KR = IWDG_REFRESH;
if(Tms - Sens_measured_time > SENSORS_DATA_TIMEOUT){ // get BME
getsensors();
Sens_measured_time = Tms;
}
/*CAN_proc(); /*CAN_proc();
if(CAN_get_status() == CAN_FIFO_OVERRUN){ if(CAN_get_status() == CAN_FIFO_OVERRUN){
USB_sendstr("CAN bus fifo overrun occured!\n"); USB_sendstr("CAN bus fifo overrun occured!\n");
@ -105,7 +142,9 @@ int main(void){
LEDS_OFF(); LEDS_OFF();
ili9341_off(); ili9341_off();
}else{ // check operation buttons for menu etc }else{ // check operation buttons for menu etc
IWDG->KR = IWDG_REFRESH;
indication_process(); indication_process();
IWDG->KR = IWDG_REFRESH;
} }
}else{ }else{
if(Tms - lastUnsleep < BTN_ACTIVITY_TIMEOUT/2){ // recent activity - turn on indication if(Tms - lastUnsleep < BTN_ACTIVITY_TIMEOUT/2){ // recent activity - turn on indication

View File

@ -0,0 +1,167 @@
/*
* This file is part of the nitrogen project.
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the nitrogen project.
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
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);

View File

@ -24,9 +24,11 @@ i2c.c
i2c.h i2c.h
ili9341.c ili9341.c
ili9341.h ili9341.h
incication.h
indication.c indication.c
indication.h
main.c main.c
menu.c
menu.h
pdnuart.c pdnuart.c
pdnuart.h pdnuart.h
proto.c proto.c

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 497 B

View File

@ -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){ static int sstate(const char _U_ *cmd, int _U_ parno, const char _U_ *c, int32_t _U_ i){
const char *s = "unknown"; const char *s = "unknown";
switch(getScreenState()){ switch(ScrnState){
case SCREEN_INIT: case SCREEN_INIT:
s = "init"; s = "init";
break; break;

View File

@ -28,6 +28,8 @@
#include "strfunc.h" #include "strfunc.h"
#endif #endif
#include <string.h>
#define SWAPINT(a,b) do{register int r = a; a = b; b = r;}while(0) #define SWAPINT(a,b) do{register int r = a; a = b; b = r;}while(0)
// sprites 8x8 bytes with 8bit foreground and 8bit background // 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 // next data portion size (in bytes!), total amount of bytes in update buffer
static int portionsz = 0, updbuffsz; static int portionsz = 0, updbuffsz;
// font scale // 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 static uint16_t fgColor = 0xff, bgColor = 0; // foreground and background colors
void setBGcolor(uint16_t c){bgColor = c;} void setBGcolor(uint16_t c){bgColor = c;}
void setFGcolor(uint16_t c){fgColor = c;} void setFGcolor(uint16_t c){fgColor = c;}
static screen_state ScrnState = SCREEN_INIT; screen_state ScrnState = SCREEN_INIT;
screen_state getScreenState(){return ScrnState;}
/** /**
* @brief UpdateScreen - request to screen updating from lines y0 to y1 (including) * @brief UpdateScreen - request to screen updating from lines y0 to y1 (including)
* @param y0, y1 - first and last line of field to update * @param y0, y1 - first and last line of field to update
*/ */
void UpdateScreen(int y0, int y1){ void UpdateScreen(int y0, int y1){
if(y0 > y1) SWAPINT(y0, y1); //if(y0 > y1) SWAPINT(y0, y1);
if(y0 < 0) y0 = 0; if(y0 < 0) y0 = 0;
if(y1 > SCRNH) y1 = SCRNH - 1; if(y1 > SCRNH || y1 < 0) y1 = SCRNH - 1;
//USB_sendstr("y0="); USB_sendstr(i2str(y0)); newline();
//USB_sendstr("y1="); USB_sendstr(i2str(y1)); newline();
uy0 = y0; uy1 = y1; uy0 = y0; uy1 = y1;
updidx = -1; updidx = -1;
updbuffsz = SCRNW * (1 + y1 - y0); updbuffsz = SCRNW * (1 + y1 - y0);
@ -93,20 +93,23 @@ void ClearScreen(){
UpdateScreen(0, SCRNH-1); 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 * @brief DrawPix - set or clear pixel
* @param X, Y - pixel coordinates (could be outside of screen) * @param X, Y - pixel coordinates (could be outside of screen)
* @param pix - 1 - foreground, 0 - background * @param pix - 1 - foreground, 0 - background
*/ */
void DrawPix(int X, int Y, uint8_t pix){ void DrawPix(int X, int Y, uint8_t pix){
if(X < 0 || X > SCRNW-1 || Y < 0 || Y > SCRNH-1) return; // outside of screen DRAWPIX(X, Y, pix);
// 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;
} }
/** /**
@ -135,9 +138,15 @@ void invertSpriteColor(int xmin, int xmax, int ymin, int ymax){
uint8_t SetFontScale(uint8_t scale){ uint8_t SetFontScale(uint8_t scale){
if(scale > 0 && scale <= FONTSCALEMAX) fontscale = scale; if(scale > 0 && scale <= FONTSCALEMAX) fontscale = scale;
fontheight = curfont->height * fontscale;
fontbase = curfont->baseline * fontscale;
return 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? // 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!) * @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); const uint8_t *curchar = font_char(Char);
if(!curchar) return 0; if(!curchar) return 0;
// now change Y coordinate to left upper corner of font // 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 // height and width of letter in pixels
uint8_t h = curfont->height, w = *curchar++; // now curchar is pointer to bits array 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 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){ for(uint8_t col = 0; col < w; ++col){
register uint8_t pix = curchar[row*lw + (col/8)] & (1 << (7 - (col%8))); register uint8_t pix = curchar[row*lw + (col/8)] & (1 << (7 - (col%8)));
int xx = X + fontscale * col; int xx = X + fontscale * col;
for(int y = 0; y < fontscale; ++y) for(int x = 0; x < fontscale; ++x) for(int y = 0; y < fontscale; ++y) for(int x = 0; x < fontscale; ++x){
DrawPix(xx + x, Y1 + y, pix); int nxtx = xx+x, nxty = Y1+y;
DRAWPIX(nxtx, nxty, pix);
}
} }
} }
return w * fontscale; return w * fontscale;
@ -193,6 +204,7 @@ int CenterStringAt(int Y, const char *str){
// full string length in pixels // full string length in pixels
int strpixlen(const char *str){ int strpixlen(const char *str){
if(!str) return 0;
int l = 0; int l = 0;
while(*str){ while(*str){
const uint8_t *c = font_char(*str++); const uint8_t *c = font_char(*str++);
@ -251,6 +263,8 @@ void process_screen(){
case SCREEN_INIT: // try to init SPI and screen case SCREEN_INIT: // try to init SPI and screen
DBG("SCREEN_INIT"); DBG("SCREEN_INIT");
spi_setup(); spi_setup();
choose_font(FONT14);
SetFontScale(1);
if(ili9341_init()) ScrnState = SCREEN_RELAX; if(ili9341_init()) ScrnState = SCREEN_RELAX;
else{ else{
Tscr_last = Tms; Tscr_last = Tms;
@ -262,7 +276,7 @@ void process_screen(){
break; break;
case SCREEN_RELAX: // check need of updating case SCREEN_RELAX: // check need of updating
if(updidx > -1) return; if(updidx > -1) return;
DBG("Need to update"); //DBG("Need to update");
if(!ili9341_setcol(0, SCRNW-1)) return; if(!ili9341_setcol(0, SCRNW-1)) return;
if(!ili9341_setrow(uy0, uy1)) return; if(!ili9341_setrow(uy0, uy1)) return;
if(!ili9341_writecmd(ILI9341_RAMWR)) return; if(!ili9341_writecmd(ILI9341_RAMWR)) return;

View File

@ -19,6 +19,7 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <stm32f3.h>
typedef enum{ // screen states typedef enum{ // screen states
SCREEN_INIT // init stage SCREEN_INIT // init stage
@ -88,7 +89,9 @@ typedef enum{ // screen states
#define COLOR_YELLOWGREEN 0x9E66 #define COLOR_YELLOWGREEN 0x9E66
screen_state getScreenState(); extern screen_state ScrnState;
extern int fontheight, fontbase;
void ClearScreen(); void ClearScreen();
void UpdateScreen(int y0, int y1); void UpdateScreen(int y0, int y1);
void setBGcolor(uint16_t c); void setBGcolor(uint16_t c);
@ -103,3 +106,8 @@ int strpixlen(const char *str);
uint8_t *getScreenBuf(); uint8_t *getScreenBuf();
void process_screen(); void process_screen();
TRUE_INLINE void cls(){ // set default colors (bg=0, fg=0xffff) and clear screen
setBGcolor(0);
setFGcolor(0xffff);
ClearScreen();
}

View File

@ -1,2 +1,2 @@
#define BUILD_NUMBER "253" #define BUILD_NUMBER "280"
#define BUILD_DATE "2023-05-12" #define BUILD_DATE "2023-05-14"