Перейти к содержанию

Простое меню LCD2004


Рекомендуемые сообщения

Доброго времени суток. Понадобилось сделать несложное меню.  Меню построено следующим образом - имеется главный пункт и 4 подпункта. Попасть в нужный подпункт можно только через главное меню. Вот примерный вид главного меню.

1.thumb.png.455ad9b91942691f9480b7afab4014e9.png

Блок схема меню

5b59f7258c7c8_.png.b7f1024689033f82b7da51915de73024.png

 

Конструкция кода

typedef struct {
   uint8_t menu;//
   uint8_t submenu;// MAIN_MENU, MENU_MANUAL, MENU_AUTO, MENU_SETUP, MENU_START
}menuItem;

typedef struct Selection
{
  unsigned char id; //Номер меню/подменю
  unsigned char ent_f : 4; //Флаг входа в подменю 
  unsigned char esc_f : 4; //Флаг выхода из подменю
}SL;

enum switchVariants : byte  {  // Определения для переключателя пунктов меню;
  MAIN_MENU, MENU_MANUAL, MENU_AUTO, MENU_SETUP, MENU_START
};
switchVariants switchPointer = MAIN_MENU; // С чего начнем цикл

int main (void) {
	while(1) {
		
	}
}

Посоветуйте как организовать вход и выход из меню? Для этого завел два флага ent и esc. Но как их связать голову ломаю.

Изменено пользователем Alexander1997
Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

56 минут назад, Alexander1997 сказал:

Посоветуйте как организовать вход и выход из меню?

Для этого необходимо видеть как организовано само меню. А Вы показали пустой код, с какими то typedef'ами ...

Ссылка на комментарий
Поделиться на другие сайты

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

 @Alex, честно сказать код собран в среде ардуино,  на основе найденного в сети. Раньше не было проблем с меню, делал ветки меню на switch-case. Код стал разрастаться и стало неудобно. Перешел к изучению структур, понял только основы на примерах.   

Вот код

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include "Cl_do_btn_long.h" // подключаем библиотеку для опроса кнопок
//------------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
//------------------------------------------
// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
//------------------------------------------
//Инициализируем обработчики кнопок управления
Cl_do_btn_long Btn_down(11);
Cl_do_btn_long Btn_up(8);
Cl_do_btn_long Btn_ok(12);
Cl_do_btn_long Btn_prev(9);
Cl_do_btn_long Btn_next(10);
Cl_do_btn_long Btn_none(13);
//------------------------------------------
#define UP 0
#define DOWN 3
#define LEFT 1
#define RIGHT 2

//------------------------------------------
typedef void (*FuncPtr)(void);
//указатель на функции
FuncPtr FPtr;
//Структура описывает текущее состояние меню и подменю
struct Menu_State{
   uint8_t menu;//1,2,3,4
   uint8_t submenu;//1,2,3
}MN;

typedef struct Selection
{
  unsigned char id; //Номер меню/подменю
  unsigned char ent_f : 4; //Флаг входа 4 бита — обычно ID меню в которое надо войти
  unsigned char esc_f : 4; //Флаг выхода 4 бита — обычно ID меню в которое надо вернуться
}SL;

enum switchVariants : byte  {  // Определения для переключателя пунктов меню;
  MAIN_MENU, MENU_MANUAL, MENU_AUTO, MENU_SETUP, MENU_START, EXITSAVE
};
switchVariants switchPointer = MAIN_MENU; // С чего начнем цикл;

const uint8_t MN000[] PROGMEM="REWORK v05\0";
//меню 1
const uint8_t MN100[] PROGMEM="SETTINGS\0";
//подпункт меню
const uint8_t MN101[] PROGMEM="STEPS:\0";
const uint8_t MN102[] PROGMEM="DWELL:\0";
const uint8_t MN103[] PROGMEM="PWR:\0";
const uint8_t MN104[] PROGMEM="RAMP:\0";
const uint8_t MN105[] PROGMEM="TARGET:\0";
//меню 2
const uint8_t MN200[] PROGMEM="<<All ON/OFF>>\0";
//подпункт меню 2
const uint8_t MN201[] PROGMEM="All ON\0";
const uint8_t MN202[] PROGMEM="All OFF\0";
//меню 3
const uint8_t MN300[] PROGMEM="<<Auto scroll>>\0";
//подпункт меню 3
const uint8_t MN301[] PROGMEM="Left\0";
//меню 4
const uint8_t MN400[] PROGMEM="<<Blink All>>\0";
//подпункт меню 4
const uint8_t MN401[] PROGMEM="Blink Fast\0";
//Массивы указателей на строки меню, хранящиеся на флэш
const uint8_t *MENU[] ={
    MN100,  //menu 1 string
    MN200,  //menu 2 string
    MN300,  //menu 3 string
    MN400 //menu 4 string
    };
const uint8_t *SUBMENU[] ={
    MN101, MN102, MN103, MN104, MN105,  //подпункты меню 1
    MN201, MN202,                       //подпункты меню 2
    MN301,                              //подпункты меню 3
    MN401,                              //подпункты меню 4
    };

//Структура меню
//[0] -Number of level 0 menu items
//[1]...[n] number of second level menu items
//Eg. MSTR2[1] shows that menu item 1 has 3 submenus
const uint8_t MSTR2[] PROGMEM ={
  4,  // количество пунктов меню
  5,  //Количество подпунктов в пункте меню 1
  2,  //Количество подпунктов в пункте меню 2
  1,  //Количество подпунктов в пункте меню 3
  1 //Количество подпунктов в пункте меню 4
  }; 
//Прототипы функций
//Инициализация Timer2 
///void init_timer2(void);
//Начальное меню
void menu_Init(void);
//Назначаем порты для кнопок и светодиодов
void ports_Init(void);
//Функции для каждого пункта меню
void func101(void);
void func102(void);
void func103(void);
void func104(void);
void func105(void);
void func201(void);
void func202(void);
void func301(void);
void func401(void);

//#define NULL_FUNC  (void*)0

//Массив указателей на функции во флэш
const FuncPtr FuncPtrTable[] PROGMEM=
{   
  func101, func102, func103, func104, func105,  //functions for submenus of menu 1
  func201, func202,     //functions for submenus of menu 2
  func301,             //functions for submenus of menu 3
  func401              //functions for submenus of menu 4
};
//SubMenu and Function table pointer update
uint8_t MFIndex(uint8_t, uint8_t);
//------------------------------------------
uint8_t pageNum = 0;          // счетчик нажатий кнопки
//------------------------------------------
void setup() {
  lcd.begin(); // initialize the LCD
  lcd.backlight(); // Turn on the blacklight and print a message.
  lcd.home();
  //Welcome demo message
  CopyStringtoLCD(MN000, 0, 0);
  _delay_ms(1000);
  lcd.clear();
  ports_Init();
  //Initial menu and initial function
  menu_Init();
  //init_timer2();
  //sei();
}
//------------------------------------------
void loop() { 
  Btn_down.run(&press_down, &longPress_down);
  Btn_up.run(&press_up, &longPress_up);
  Btn_ok.run(&press_ok, &longPress_ok);
  Btn_prev.run(&press_prev, &longPress_prev);
  Btn_next.run(&press_next, &longPress_next);
  Btn_none.run(&press_cansel, &longPress_cansel);
  //Выполнить функцию, на которую указывает Ptr
  FPtr();
}
//------------------------------------------
void CopyStringtoLCD(const uint8_t *FlashLoc, uint8_t x, uint8_t y)
{
  uint8_t i;
  lcd.setCursor(x,y);
  for(i=0;(uint8_t)pgm_read_byte(&FlashLoc[i]);i++)
  {
    lcd.write((uint8_t)pgm_read_byte(&FlashLoc[i]));
  }
}
//------------------------------------------
void ports_Init() {
  DDRB&=~(0<<UP|0<<DOWN|0<<LEFT|0<<RIGHT);//as input
  PORTB|=1<<UP|1<<DOWN|1<<LEFT|1<<RIGHT;//Pullups
}
//------------------------------------------

void menu_Init(void)
{
  MN.menu=1;
  MN.submenu=1;
  lcd.clear();
  CopyStringtoLCD(MENU[(MN.menu-1)], 0, 0 );
  CopyStringtoLCD(SUBMENU[(MN.submenu-1)], 0, 1 );
  FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[0]);
}
//------------------------------------------
uint8_t MFIndex(uint8_t mn, uint8_t sb)
{
  uint8_t p=0;//points to menu in table of function pointer 
  for(uint8_t i=0; i<(mn-1); i++)
  {
    p=p+pgm_read_byte(&MSTR2[i+1]);
  }
  p=p+sb-1;
  return p;
}
//------------------------------------------
void func101(void)
{

}
void func102(void)
{

}
void func103(void)
{

}
void func104(void)
{

}
void func105(void)
{

}
void func201(void)
{

}
void func202(void)
{

}
void func301(void)
{

}
void func302(void)
{

}
void func401(void)
{

}
void func402(void)
{

}
//------------------------------
// кнопка 1
void press_down() { 
 if (MN.menu==1)
  { 
    MN.menu=pgm_read_byte(&MSTR2[0]);
    MN.submenu=1;
  }
  else
  {
    MN.menu--;
  }
  lcd.clear();
  CopyStringtoLCD(MENU[MN.menu-1], 0, 0 );
  CopyStringtoLCD(SUBMENU[MFIndex(MN.menu, MN.submenu)], 0, 1 );
  FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menu, MN.submenu)]);
}
void longPress_down() { 
  
}
// кнопка 2
void press_up() { 
  if (MN.menu<pgm_read_byte(&MSTR2[0]))
  { 
    MN.menu++;
    MN.submenu=1;
  }
  else
  {
    MN.menu=1;
  }
  lcd.clear();
  //Display menu item
  CopyStringtoLCD(MENU[MN.menu-1], 0, 0 );
  //Display submenu item
  CopyStringtoLCD(SUBMENU[MFIndex(MN.menu, MN.submenu)], 0, 1 );
  //Assign function to function pointer
  FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menu, MN.submenu)]);
}
void longPress_up() { 
  
}
// кнопка 3
void press_ok() { 
  ButtonClick(&pageNum); // Вызывается функция обработки нажатия на кнопку
}
void longPress_ok() { 
  
}
// кнопка 4
void press_prev() { 
  if (MN.submenu==1)
    { 
      MN.submenu=pgm_read_byte(&MSTR2[MN.menu]);
    }
    else
    {
      MN.submenu--;
    }
    lcd.clear();
    CopyStringtoLCD(MENU[MN.menu-1], 0, 0 );
    CopyStringtoLCD(SUBMENU[MFIndex(MN.menu, MN.submenu)], 0, 1 );
    FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menu, MN.submenu)]); 
}
void longPress_prev() { 

}
// кнопка 5
void press_next() { 
  if (MN.submenu<pgm_read_byte(&MSTR2[MN.menu]))
  { 
    MN.submenu++;
  }
  else
  {
    MN.submenu=1;
  }
  lcd.clear();
  CopyStringtoLCD(MENU[MN.menu-1], 0, 0 );
  CopyStringtoLCD(SUBMENU[MFIndex(MN.menu, MN.submenu)], 0, 1 );
  FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menu, MN.submenu)]);   
}

void longPress_next() { 
  
}
// кнопка 6
void press_cansel() { 
 
}
void longPress_cansel() { 
  
}
//------------------------------------------
void ButtonClick(uint8_t *ButtonId) {
  //if (enterMenu) { // Если мы в меню
    if (*ButtonId == 0)  switchPointer = MENU_MANUAL;   // Клик [Menu] Выход из меню
    else if (*ButtonId == 1) switchPointer = MENU_AUTO;  //MenuCurent--;   // Клик [Prev] Позицию ниже
    else if (*ButtonId == 2) switchPointer = MENU_SETUP; //MenuCurent++;   // Клик [Next] Позиция выше
    else if (*ButtonId == 3) switchPointer = MENU_START;
  //} else {
    //switchPointer = MAIN_MENU; // Возврат в главное меню
  //}
}

Он не рабочий, собираюсь доделать.

Мне нужно связать пункты меню в такой схеме

MAIN_MENU -> MENU_MANUAL ->  MAIN_MENU

MAIN_MENU -> MENU_AUTO  ->  MAIN_MENU

MAIN_MENU -> MENU_SETUP  ->  MAIN_MENU

MAIN_MENU -> MENU_START  ->  MAIN_MENU

Создал для этого структуру

typedef struct Selection
{
  unsigned char id; //Номер меню/подменю
  unsigned char ent_f : 4; //Флаг входа 4 бита — обычно ID меню в которое надо войти
  unsigned char esc_f : 4; //Флаг выхода 4 бита — обычно ID меню в которое надо вернуться
}SL;

Примерно так, но дальше знаний не хватает

static SL menu_[]={
{1,  MENU_MANUAL, MAIN_MENU}, //Punkt 1
{2,  MENU_AUTO, MAIN_MENU}, //Punkt 2
{3,  MENU_SETUP, MAIN_MENU}, //Punkt 3
{4,  MENU_START, MAIN_MENU}, //Punkt 4
};

Посоветуйте как лучше поступить?

Изменено пользователем Alexander1997
Ссылка на комментарий
Поделиться на другие сайты

Выбираем схему BMS для заряда литий-железофосфатных (LiFePO4) аккумуляторов

Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Вот как было устроено меню у меня. Разберетесь без функций ввода-вывода или мне все же выложить их ближе к вечеру?

typedef struct{
	unsigned char prev; //предыдущий элемент в том же подменю
	unsigned char next; //следующий
	unsigned char up; //номер пункта родительского меню, в котором содержится данный элемент
	unsigned char down; //номер подменю, в которое можно попасть из этого. Если 7-й бит выставлен, то остальные интерпретируются как номер вызываемой функции
	char caption[15]; //в моем случае ширина экрана 16 символов, плюс "украшения"
}menu_t;
////////////////////////////////////////////////////////////////////////////
//              MENU
//               |                 
//  SETTINGS------------------------------------------EDIT------------------RUN
//   |                                                  |                    |
//  UART------SPI--I/O----ADC                     EDIT_CURRENT-LOAD-SAVE     AUTO-TERMOSTAT
//  |          |    b      |                           |        |    |
//BAUD-LOG     -        FILTER-0C-SCALE-ACCURACY       O        -    -
// i    b                 c    i    i      i
////////////////////////////////////////////////////////////////////////////
// i   input integer
// c   input byte
// b   input boolean
// -   disabled
// O   special procedure
////////////////////////////////////////////////////////////////////////////
PROGMEM menu_t menu_arr[]={
	{0x01,0x01,0x00,0x01,"     MENU     "},	//0x00
	{0x03,0x02,0x00,0x04,"   SETTINGS   "},	//0x01
	{0x01,0x03,0x00,0x0E,"     EDIT     "},	//0x02
	{0x02,0x01,0x00,0x11,"     RUN      "},	//0x03
	{0x07,0x05,0x01,0x08,"     UART     "},	//0x04
	{0x04,0x06,0x01,0x05,"     SPI      "},	//0x05
	{0x05,0x07,0x01,0x85,"     I/O      "},	//0x06
	{0x06,0x04,0x01,0x0A,"     ADC      "},	//0x07
	{0x09,0x09,0x04,0x80,"     BAUD     "},	//0x08
	{0x08,0x08,0x04,0x81,"     LOG      "},	//0x09
	{0x0D,0x0B,0x07,0x82,"    FILTER    "},	//0x0A
	{0x0A,0x0C,0x07,0x83,"     0 C      "},	//0x0B
	{0x0B,0x0D,0x07,0x84,"    SCALE     "},	//0x0C
	{0x0C,0x0A,0x07,0x86,"   ACCURACY   "},	//0x0D
	{0x10,0x0F,0x02,0x00," EDIT CURRENT "},	//0x0E
	{0x0E,0x10,0x02,0x00,"     LOAD     "},	//0x0F
	{0x0F,0x0E,0x02,0x00,"     SAVE     "},	//0x10
	{0x12,0x12,0x03,0x00,"     AUTO     "},	//0x11
	{0x11,0x11,0x03,0x00,"  TERMOSTAT   "}	//0x12
	};
menu_t menu_temp;

unsigned char menu_pos=1;

 

Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Ссылка на комментарий
Поделиться на другие сайты


void menu_redraw(){
  menu_t menu_near;
  lcd_enable(); //на тех же портах что и дисплей у меня висело еще что-то (уж не помню что) поэтому включаем явным образом
//загружаем из ПЗУ содержимое текущего пункта меню
  memcpy_P(&menu_temp,&(menu_arr[menu_pos]),sizeof(menu_temp));
//загружаем из ПЗУ названия пунктов, окружающих текущий: "верхний", он же название подменю
  memcpy_P(&menu_near.caption,&(menu_arr[menu_temp.up].caption),sizeof(menu_near.caption)); 
  lcd_goto(LCD_STR_1); lcd_data('['); lcd_str(menu_near.caption); lcd_data(']');
//предыдущий
  memcpy_P(&menu_near.caption,&(menu_arr[menu_temp.prev].caption),sizeof(menu_near.caption)); 
  lcd_goto(LCD_STR_2); lcd_data(0xD9); lcd_str(menu_near.caption); lcd_data(0xD9);
//текущий
  lcd_goto(LCD_STR_3); lcd_data(0xDB); lcd_str(menu_temp.caption); lcd_data(0xDC);
//следующий
  memcpy_P(&menu_near.caption,&(menu_arr[menu_temp.next].caption),sizeof(menu_near.caption));
  lcd_goto(LCD_STR_4); lcd_data(0xDA); lcd_str(menu_near.caption); lcd_data(0xDA);
//символы '[', ']', 0xD9, 0xDA, 0xDB, 0xDC это "рамка" вокруг пунктов чтобы однозначно выделить кто из них текущий
}
void menu(){
  while(1){
    kbd_enable(); //если не ошибаюсь, именно клавиатура и "сожительствует" с дисплеем
    kbd_update();
    //анализируем клавиатуру только если ее состояние изменилось (были нажаты новые клавиши)
    if(kbd_t!=kbd_last){
      switch(kbd_t){
        case KBD_NEXT:	menu_pos=menu_temp.next; break; //ну тут все очевидно
        case KBD_PREV:	menu_pos=menu_temp.prev; break;
        case KBD_NO:	menu_pos=menu_temp.up; break;
        //самый "развесистый" вариант, поскольку поведение "дочернего" пункта может быть различным
        case KBD_OK:	  //если старший бит выставлен, запускаем ввод выбранного типа данных
                    	if(menu_temp.down & 0x80)menu_input(menu_temp.down & 0b01111111); else
                          //тут какие-то костыли, сделанные именно для моей задачи. На самом деле хорошо было бы реализовать как-то по-другому. Поэтому и комментировать не буду
                    	if(menu_pos==0x0E)select_program();else
                    	if(menu_pos==0x11){work_flags = work_flags|WF_RUN|WF_PROG; time_to_curtime(0); return;}else
                    	if(menu_pos==0x12){work_flags = (work_flags&~WF_PROG)|WF_RUN; time_to_curtime(0); return;}else
                          //самый простой вариант. Если "внизу" скрывается еще одно подменю - переходим в него
                    	menu_pos=menu_temp.down; break;
      }
      menu_redraw(); //какая-то клавиша была нажата, значит перерисовываем меню
    }
  }
}

Как-то так

Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Ссылка на комментарий
Поделиться на другие сайты

  • 2 года спустя...

Присоединяйтесь к обсуждению

Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...