Alexander1997 Опубликовано 26 июля, 2018 Поделиться Опубликовано 26 июля, 2018 (изменено) Доброго времени суток. Понадобилось сделать несложное меню. Меню построено следующим образом - имеется главный пункт и 4 подпункта. Попасть в нужный подпункт можно только через главное меню. Вот примерный вид главного меню. Блок схема меню Конструкция кода 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. Но как их связать голову ломаю. Изменено 26 июля, 2018 пользователем Alexander1997 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Alex Опубликовано 26 июля, 2018 Поделиться Опубликовано 26 июля, 2018 56 минут назад, Alexander1997 сказал: Посоветуйте как организовать вход и выход из меню? Для этого необходимо видеть как организовано само меню. А Вы показали пустой код, с какими то typedef'ами ... 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
20% скидка на весь каталог электронных компонентов в ТМ Электроникс!Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!Перейти на страницу акции Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849
Alexander1997 Опубликовано 26 июля, 2018 Автор Поделиться Опубликовано 26 июля, 2018 (изменено) @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 }; Посоветуйте как лучше поступить? Изменено 26 июля, 2018 пользователем Alexander1997 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Выбираем схему BMS для заряда литий-железофосфатных (LiFePO4) аккумуляторовОбязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей. Подробнее>>Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161
COKPOWEHEU Опубликовано 27 июля, 2018 Поделиться Опубликовано 27 июля, 2018 Вот как было устроено меню у меня. Разберетесь без функций ввода-вывода или мне все же выложить их ближе к вечеру? 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; 0 Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз. Часть моих наработок. Ссылка на комментарий Поделиться на другие сайты Поделиться
Alexander1997 Опубликовано 27 июля, 2018 Автор Поделиться Опубликовано 27 июля, 2018 COKPOWEHEU, если вас не затруднит выложите функции, заранее благодарю 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
COKPOWEHEU Опубликовано 27 июля, 2018 Поделиться Опубликовано 27 июля, 2018 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(); //какая-то клавиша была нажата, значит перерисовываем меню } } } Как-то так 1 Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз. Часть моих наработок. Ссылка на комментарий Поделиться на другие сайты Поделиться
Alexander1997 Опубликовано 27 июля, 2018 Автор Поделиться Опубликовано 27 июля, 2018 @COKPOWEHEU огромное вам спасибо, глядя на ваш пример разберусь со своим 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
gogaze Опубликовано 21 сентября, 2020 Поделиться Опубликовано 21 сентября, 2020 Ну и как? Получилось разобраться? 0 Мир не без добрых людей! Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения
Присоединяйтесь к обсуждению
Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.