mirror of
https://github.com/eddyem/stm32samples.git
synced 2025-12-06 02:35:23 +03:00
Add menu, TODO: add hold key events to quickly change value in menu setters
This commit is contained in:
parent
e6dc55764b
commit
13cc2a7e70
@ -45,14 +45,14 @@
|
||||
|
||||
#include "i2c.h"
|
||||
#include "BMP280.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "usb.h"
|
||||
#ifdef EBUG
|
||||
#include "strfunc.h"
|
||||
#include "usb.h"
|
||||
extern volatile uint32_t Tms;
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<<i;
|
||||
}
|
||||
// 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(evt == EVT_PRESS && lastevent[i] != EVT_PRESS) evtmask |= 1<<i;
|
||||
lastevent[i] = evt;
|
||||
}
|
||||
if(!evtmask) return 0;
|
||||
if(dispstate == DISP_WINDOW) refresh_window(evtmask);
|
||||
else refresh_menu(evtmask);
|
||||
return evtmask;
|
||||
}
|
||||
|
||||
void indication_process(){
|
||||
if(!LEDsON) return;
|
||||
leds_proc();
|
||||
btns_proc();
|
||||
switch(dispstate){
|
||||
case DISP_MAINWIN:
|
||||
if(Tms - lastTmeas > 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -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
|
||||
|
||||
167
F3:F303/NitrogenFlooding/menu.c
Normal file
167
F3:F303/NitrogenFlooding/menu.c
Normal 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);}
|
||||
40
F3:F303/NitrogenFlooding/menu.h
Normal file
40
F3:F303/NitrogenFlooding/menu.h
Normal 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);
|
||||
Binary file not shown.
@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 483 B After Width: | Height: | Size: 497 B |
@ -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;
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
#include "strfunc.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stm32f3.h>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "253"
|
||||
#define BUILD_DATE "2023-05-12"
|
||||
#define BUILD_NUMBER "280"
|
||||
#define BUILD_DATE "2023-05-14"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user