Простое меню
- Войдите на сайт для отправки комментариев
Всем привет! Продолжаю знакомиться с Ардуино и языком Си на примере опроса датчика DS18B20 и симуляции в Proteus. С числами и протоколом 1-wire разобрался, всё норм. В завершении этой темы, как и планировал, решил сделать простое меню, что-бы, так сказать, "углубить" и "расширить"))) Всё получилось, всё работает, но получилось довольно-таки громоздко.
"Простое меню" оказалось не очень-то простым для меня. Программа чтения из датчика с индикацией занимала 7% памяти устройства и 4% динамической памяти. С добавлением меню (плюс немного коррекция и контроль пороговых значений) уже 15% и 7% соответственно. Т.е по объёму прога выросла в два раза(!) Это нормально для такого простого меню?
Если есть те, кому не лень глянуть на мои "старания", то прошу конструктивной критики. Заранее извиняюсь за возможные перерывы в ответах
Возможности меню скромные:
1.Коррекция значений температуры в пределах -0.9С...+0.9С
2.Возможность установки верхнего и нижнего порогов неотрицательной темп-ры, для работы устройства в качестве термостата
Управление - одной кнопкой:
1. Долгое удержание, более 6 сек - вход в меню
2. Короткое нажатие - переключение позиции / фиксация значений
3. Долгое нажатие, более 3 сек - смена значений меню
Чётное - единиц, нечётное - десятых .
// программа для чтения из датчика ds18b20 // значений температуры от -55С до +125С // с разрешением 0.0625 // с возможностью использования в качестве термостата // для поддержания темп-ры // в пределах от 0.0С до 99.9С, // коррекцией +- 0.9С, выводе данных текущей темп-ры // (до десятых вкл.)каждые 8сек, и данных // меню на 8-ми разрядный 7-сегментный индикатор с общим // катодом и инвертирующим буфером (транзистором) // УПРОЩЁННАЯ ВЕРСИЯ - ТОЛЬКО МЕНЮ + ИНДИКАЦИЯ с выводом // данных меню в терминал // Плата Arduino Pro Mini // Внутренний генератор 8 Мгц // щщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщ const byte KN = 10; //пин кнопки меню. const byte DATASEG[] = {18, 19, 2, 3, 4, 5, 6, 7}; // пины для вывода // данных на 7 сегментов и точку const byte RAZR[] = {A0, A1, A2, 13, 12, 11, 9, A3}; // пины для // выбора разряда индикатора const byte WIRE = 8; // пин шины 1-wire const byte RELE = 18; // управляющий выход на реле // (временно занят (см. DATASEG[0]) // для подключения терминала к пинам 0, 1 RX,TX) // если терминал не нужен, данные можно подавать // сразу в порт: PORTD = CYFRY[a[i]]; const byte CIFRY[] = { 0b00111111,//0 0b00000110,//1 0b01011011,//2 0b01001111,//3 0b01100110,//4 0b01101101,//5 0b01111101,//6 0b00000111,//7 0b01111111, //8 0b01101111, //9 0b01000000, // "-"(10) 0b00000000, // маска(11) 0b00111001, // "С" (12)если пишем 0b01110110, // "Х" (13) "СОХР" 0b01110011, // "Р" (14) 0b01011100, // "o" (15) 0b01010100, // "n" (16) 0b01110001, // "F" (17) 0b01100011, //символ градуса(18) 0b01110111, // "A"(19) 0b01011110, // "d"(20) 0b01101101, // "S"(21) если пишем 0b00111110, // "U"(22) "SAVE" 0b01111001, // "E"(23) 0b00110111, // "П" (24) 0b00111000 // "L" (25) }; // цифры и символы на индикатор byte a[] = {0, 0, 0, 0, 0, 0, 0, 0}; // байты для разрядов индикатора // расположение разрядов не индикаторе: // a[7] a[0] a[1] a[2] a[3] a[4] a[5] a[6} /* РАСКОММЕНТИРОВАТЬЭТУ ЧАСТЬ ДЛЯ ПОЛНОЙ ВЕРСИИ ПРОГРАММЫ byte temp_tx[] = {0xCC, 0x44, 0xCC, 0xBE}; // последовательность // команд к датчику: // пропуск команды поиск ROM, конвертировать температуру , // пропуск команды поиск ROM, чтение памяти. byte temp_rx[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // массив для записи // результата чтения из датчика byte temp_0 = 0; // байт для промежуточного хранения данных при чтении byte low_byte = 0; // младший байт значения температуры byte high_byte = 0; // старший байт значения температуры byte w = 0; // переменная для определения очерёдности команд к датчику */ //--- ПЕРЕМЕННЫЕ ДЛЯ МЕНЮ ----------- byte b[14] = {11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // байты для // временного хранения значений меню. // пользовательские данные хранятся в двоично-десятичном виде // b[0] - знак коррекции(10-минус или 11-маска) //b[1]-b[2] - байты для хранения значений коррекции //b[3]-b[5] - нижнее пороговое значение темп-ры //b[6]-b[8] - верхнее пороговое значение темп-ры //b[9]-b[13] - значения счётчиков меню byte Nmenu = 0; // байт для хранения номера позиции(пункта) меню byte c_corr1 = 0; // счётчик длительности чётного и нечётного //долгого нажатия кнопки 1 поз. byte c_corr2 = 0; // счётчик дл-ти чётного долгого нажатия кнопки 2 поз. byte c_corr3 = 0; // счётчик дл-ти нечётного долгого нажатия кнопки 2 поз. byte c_corr4 = 0; // счётчик дл-ти чётного долгого нажатия кнопки 3 поз. byte c_corr5 = 0; // счётчик дл-ти нечётного долгого нажатия кнопки 3 поз. byte s = 0; // байт счёта коротких нажатий кнопки меню byte tdreb = 0; // байт циклов антидребезга при отпускании кнопки в меню byte Nkn; // байт числа долгих нажатий кнопки меню byte a_temp[] = {0, 0, 0}; // байты временного хранения для мигания // разрядов в меню int mig = 0; //счётчик циклов для мигания в меню byte mig1 = 0; //счётчик количества миганий в меню byte rdeepr_cycl; // счётчик интервала чтения из eeprom byte tik_go1 = 0; // счётчик антидребезга при входе в меню bool KNflgS = false; // флаг короткого нажатия кн. bool KNflgNEW = false; // флаг нового нажатия кн. bool KNflgL = false; // флаг долгого нажатия кн. bool MENu = false; // флаг разрешающий вход в меню bool long_kn = false; //флаг чётного/нечётного долгого нажатия bool MIGfl = false; //флаг мигания в меню bool go = true; // флаг разрешения начального входа в меню bool out = false; // флаг выхода из меню bool save = false; // флаг сохранения данных меню bool null_0 = false; // флаг обнуления данных меню unsigned int tik = 0; // счётчик циклов для антидребезга и // определения длительности нажатия кнопки меню unsigned int tik_go = 0; //счётчик циклов для началного входа в меню //------------ unsigned int synchr = 0;// общий счётчик синхронизации событий программы byte k = 0; // счётчик для schynhr bool tochka = true; // флаг разрешения индикации точки bool flg = false; // флаг обновления schynhr bool hot = false; // флаг понижения/повышения темп-ры unsigned long last0_millis = 0; // переменная для таймера динамической // индикации, опроса кнопки и счётчика synchr unsigned long KNschet = 0; // переменная для таймера долгого нажатия кнопки //------------- // объявляем функции: void menu(); // меню коррекции темп-ры и ввода значения термостата void wr_eepr(); // запись в eeprom void read_eepr(); // чтение из eeprom void vhod(); // вход в меню по долгому нажатию кнопки(более 6сек) //--------------------- void setup() { pinMode(KN, INPUT); // пин кнопки на вход // pinMode(RELE, OUTPUT); // // digitalWrite(RELE, LOW);//настройка пина для управления реле // high(); // настройка шины 1-wire /временно отключено/ for (int i = 0; i < 8; i++) { pinMode(DATASEG[i], OUTPUT); } for (int i = 0; i < 8; i++) { pinMode(RAZR[i], OUTPUT); // настройка выходов динамической индикации } delay(300); // задержка для нормального запуска датчика //============================== Serial.begin(115200); read_eepr(); // читаем данные меню из eeprom } void loop() { //====================================================== // интервал 4мс для обновления // динамической индикации, счётчика synchr, и // опроса кнопки if ((millis() - last0_millis) >= 4) { last0_millis = millis(); //---- каждые 4мс выполняем цикл loop() --- //------- опрос кнопки --------- if ((digitalRead(KN) == true) || MENu) {//если нажата кнопка или уже в меню vhod(); // первый вход в меню. если уже вошли - пропускаем(внутри функции) menu(); // входим в меню } // если кнопка отпущена и сброшен флаг меню - // - в меню не входим //------- исключаем ложный вход в меню ----------- if ((digitalRead(KN) == false) && !MENu) {// tik_go1++; // антидребезг при отпускании кнопки if (tik_go1 >= 2) { //одинокая помеха - ещё не повод для сброса счётчика входа tik_go = 0; // фиксация отпускания кнопки и сброс счётчика входа // при недостаточно долгих нажатиях для входа в меню tik_go1 = 0; } } //============ // организация мигания разрядов, //доступных для изменения в меню, 1-я часть, перед иникацией if ((s == 1 || s == 3 || s == 5) && (!KNflgL) && MIGfl) { //если находимся в позиции меню,где используется мигание mig++; // и не происходит долг. нажатие , запускаем счётчик циклов if (mig < 80) { // если не прошло 80 циклов(80х4=320мс), // (это время ,на которое гасим индикацию ) сохраняем a_temp[0] = a[1]; // текущие значения индикации a_temp[1] = a[2]; // для замены их на маску (11) a_temp[2] = a[3]; // // if (s == 1) { // в первой позиции меню всегда мигает //только разряд десятых(а[3]) a[3] = 11; // маскируем разряд } if ((s == 3) || (s == 5)) { // во второй и третьей позиции меню if (( Nkn % 2 ) == 0) { // нечётное завершённое долгое нажатие a[2] = 11; // мигают единицы и десятки a[1] = 11; } else { a[3] = 11; // чётное нажатие, мигают десятые } } } } //щщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщ // счётчик synchr и динамическая индикация digitalWrite(RAZR[k], LOW); // гасим разряд для динамической индикации PORTD = PORTD & 0b01111111; // гасим точку k++; // Здесь спасибо, счётчик synchr с кодом Alexey_Rem помогал if (k > 7) { // каждые 8 циклов k = 0; // 8х4=32ms счётчик синхронизации всех synchr++; // +32ms частей программы flg = true; //флаг обновения synchr // разрешающий выпонение некоторых блоков программы } // вывод данных на индикатор: digitalWrite(RAZR[k], HIGH); // выбор разряда int chis = CIFRY[a[k]]; //сохраняем число для индикатора для тек.разряда for (int j = 0; j < 8; j++) { // выводим 8 сегментов исходя из текущего if ((chis & (1 << j)) == 0) { // числа для разряда digitalWrite(DATASEG[j], LOW); // } else { digitalWrite(DATASEG[j], HIGH); } } if ((k == 2) && (tochka == true))// если текущий разряд 2-ой и нет запрета PORTD = PORTD | 0b10000000; // зажигаем точку //щщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщщ // мигание в меню 2-я часть, после индикации if ((s == 1 || s == 3 || s == 5) && (!KNflgL) && MIGfl) { // if (mig < 80) { // если прошли циклы // маскировки разрядов a[1] = a_temp[0]; // возвращаем значение разрядов a[2] = a_temp[1]; // после мигания в меню a[3] = a_temp[2]; } } if (mig > 400) { //400х4=1600мс сбрасываем счётчик циклов одного мигания mig1++; // и считаем количество миганий mig = 0; } if (!KNflgL) { // если кнопка не удерживается if (mig1 > 5) { // число миганий в одной позиции long_kn = !long_kn; // смена доступных для изменения разр-ов Nkn += 1; // смена позиции мигания mig1 = 0; //обнуляем счётчик } } //======================================================== switch (synchr) { case 218: // 218х32 = 6976мс //----- ВЫХОД ИЗ МЕНЮ по флагу out ---------- if (out) { // если поднят флаг выхода из меню MENu = false; // выходим из меню go = true; // разрешаем новый вход в меню out = false; // сбрасываем флаг выхода s = 0; // сбрасываем счётчик позиций меню if (save) { //по флагу, сохраняем wr_eepr(); // новые или восстанавливаем } else { // старые данные меню из read_eepr();// eeprom } if (null_0) { // по флагу for (byte r = 0; r < 14; r++) { // обнуляем данные b[r] = 0; // меню c_corr1 = 0; c_corr2 = 0; c_corr3 = 0; // обнуляем счётчики меню c_corr4 = 0; c_corr5 = 0; wr_eepr(); null_0 = false; } } tochka = false; } //-------------------------------- // если не установлен флаг пребывания в меню // выполняется основная часть программы // распределение времени для // общения с датчиком 1раз в 8сек, преобразование чисел, подсчёт crc if (flg && (!MENu)) { // если не в меню // и поднят флаг обновления shyncr // здесь передача команд датчику flg = false; break; } case 250: // 250х32 = 8000 ожидание преобразования и выполнение // второй части : чтение ,счёт ,crc_8 if (flg && (!MENu)) { // если не в меню synchr = 0; // здесь получение данных из датчика // преобразование чисел // проверка crc // контроль пороговых значений темп-ры // в упр. версии просто выводим a[7] = 11; // -0.7C a[0] = 11; a[1] = 10; a[2] = 0; a[3] = 7; a[4] = 11; a[5] = 18; a[6] = 12; // выводим пользовательские данные меню в терминал Serial.println(b[0]); Serial.println(b[1]); Serial.println(b[2]); Serial.println(b[3]); Serial.println(b[4]); Serial.println(b[5]); Serial.println(b[6]); Serial.println(b[7]); Serial.println(b[8]); rdeepr_cycl++; if (rdeepr_cycl > 150) { // обновляем данные из eeprom read_eepr(); // каждые 150 измерений rdeepr_cycl = 0; // число с "потолка", просто "бережём" } // eeprom))) tochka = true; flg = false; break; } } } } //====== ВХОД В МЕНЮ =========== void vhod() { // if ((!MENu) && go) { //если ещё не в меню, но поднят флаг tik_go++; // разрешающий вход, начинаем счёт if (( tik_go >= 1500) && (digitalRead(KN))) { //1500х4=6000 после удержании tik_go = 0; // кнопки более 6ceк сброс флага, разрешающего вход go = false; KNflgNEW = true; // фиксируем факт нового нажатия кнопки // ( для того, чтобы можно было фиксировать факт отпускания кнопки ))) MENu = true; // и будем входить в меню (после отпускания кнопки) a[7] = 11; //до отпускания кнопки,когда уже возможен вход - выводим нули a[0] = 11; // можно заморочится и написать "UHOD" a[1] = 0; // entry не получается))) a[2] = 0; a[3] = 0; a[4] = 11; a[5] = 11; a[6] = 11; } } } // ============ МЕНЮ =========== void menu() { if (MENu && !go) { // если разрешён вход в меню // и если не происходит первый вход в меню // разбираемся с антидребезгом, длительностью и // числом нажатия кнопки // ==== фиксация долгих нажатий кнопки === if ( digitalRead(KN) && ( !KNflgL)) { // если кнопка нажата // но ещё не зафиксировано tik++; // долгое нажатие if (tik >= 2) // ждём подтверждение антидребезга KNflgNEW = true; // и фиксируем факт нового нажатия кнопки if (tik >= 750) { //750x4ms=3сек если удержание более 3 сек KNflgS = false; // фиксируем долгое нажатие KNflgL = true; long_kn = !long_kn; // фиксируем новое долгоое нажатие // (чётное или нечётное) tik = 0; } } //=== фиксация коротких нажатий кнопки === if (((digitalRead(KN)) == LOW) && (!KNflgS) && KNflgNEW && (!KNflgL)) { //если было зафиксировано новое нажатие,затем кнопка отпущена, // но ещё не было зафиксировано ни долгое ни короткое нажатие tdreb++; // ждём подтверждения антидребезга if (tdreb > 2) { // и считаем, что кнопка отпущена KNflgS = true; // и фиксируем короткое нажатие KNflgNEW = false; s++;// счётчик пунктов меню // переходим в следующий пункт(позицию) меню if (s > 9) s = 1; KNflgL = false; // tdreb = 0; // tik = 0; // обнуляем счётчики } } //======================== //== ОТРАБОТКА КОРОТКИХ НАЖАТИЙ ======== if (KNflgS) { // если произошло новое switch (s) { // короткое нажатие кнопки case 1: // Nmenu = 1; //1-я позиция меню MIGfl = true; // уст. флаг мигания Nkn = 0; //сброс кол-ва долг.нажатий long_kn = true; //первое долгое нажатие - нечётное a[7] = Nmenu; //отображение тек.позиция меню a[0] = 11; // маска if (b[0] == 0) { a[1] = 11; //маскируем, если надо,лишний разряд } else { // a[1] = b[0]; //выводим значения для 1 поз. } a[2] = b[1]; //из массива b[], считанные a[3] = b[2]; // из eeprom a[4] = 11; // маска a[5] = 19; //"A" a[6] = 20; //"d" tochka = true; out = false; // запрещаем выход и вход go = false; // в меню null_0 = false; // запрещаем обнуление данных меню mig1 = 0; // сбрасываем счётчик миганий break; case 2: // фиксируем значения MIGfl = false; // сбр. флаг мигания break; case 3: // 3-е коротк. нажатие Nmenu = 2; // 2-я позиция меню Nkn = 0; MIGfl = true; long_kn = true; a[7] = Nmenu; //отображение тек.позиция меню a[1] = b[3]; // выводим значения для 2 поз. a[2] = b[4]; // из массива b, считанного a[3] = b[5]; // из eeprom a[4] = 11; // маска a[5] = 15; // символы "on" a[6] = 16; tochka = true; mig1 = 0; // сбрасываем счётчик миганий break; case 4: //фиксируем значения MIGfl = false; // break; case 5: // 5-е коротк. нажатие Nmenu = 3; //3-я позиция меню Nkn = 0; // MIGfl = true; // long_kn = true; // a[7] = Nmenu; //отображение тек.позиция меню a[1] = b[6]; //выводим значения для 3 поз. a[2] = b[7]; // из массива b, считанного a[3] = b[8]; // из eeprom a[4] = 11; // маска a[5] = 15; // символы "of"(без 2-ой f) a[6] = 17; tochka = true; mig1 = 0; // сбрасываем счётчик миганий break; case 6: //фиксируем значения MIGfl = false; // break; case 7: // 7-е коротк. нажатие Nmenu = 4; // 4-я поз. меню out = true; tik_go = 0; a[7] = Nmenu; // отображение тек.позиция меню a[0] = 11; // выводим символы a[1] = 16; // "noSAVE" a[2] = 15; // a[3] = 21; // a[4] = 19; // a[5] = 22; a[6] = 23; PORTD = PORTD & 0b01111111; // гасим точку tochka = false; save = false; synchr = 0; // через 8с выходим из меню KNflgL = false; // не реагируем на долгие нажатия break; case 8: // 8-е коротк. нажатие Nmenu = 5; // 5-я позиция меню out = true; // флаг выхода из меню tik_go = 0; a[7] = Nmenu; // отображение тек.позиция меню a[0] = 11; // выводим символы "СОХР" a[1] = 21; //12; или "SAVE" a[2] = 19; //0; a[3] = 22; //13; a[4] = 23; //14; a[5] = 11; a[6] = 11; PORTD = PORTD & 0b01111111; // гасим точку tochka = false; synchr = 0; // через 8с выходим из меню save = true; KNflgL = false; // не реагируем на долгие нажатия break; case 9: // 9-е коротк. нажатие Nmenu = 6; //6-я позиция меню out = true; // tik_go = 0; a[7] = Nmenu; // a[0] = 11; // "nULL" a[1] = 16; // обнуление a[2] = 22; // a[3] = 25; a[4] = 25; a[5] = 11; a[6] = 11; PORTD = PORTD & 0b01111111; // гасим точку tochka = false; synchr = 0; // через 8с выходим из меню null_0 = true; // разрешаем обнуление данных меню save = false; KNflgL = false; // не реагируем на долгие нажатия break; } KNflgS = false; // сброс флага короткого нажатия кнопки KNflgNEW = false; // сброс флага нового нажатия кнопки } //== ОТРАБОТКА ДОЛГИХ НАЖАТИЙ ======== if (KNflgL) { // если было зафиксировано долгое нажатие кнопки, if (digitalRead(KN)) { // при продолжении удержания кнопки a[7] = Nmenu; // отображение тек.позиции меню if ((millis() - KNschet) > 375) { //375х4=1500 примерно // каждые 1.5 секунды KNschet = millis(); // обновляем счётчики меню при удержании кнопки // =================================== if (long_kn == true) { //установка десятых, чётное долг.нажатие == // =================================== if (s == 1) { // первая позиция меню,чётное долгое c_corr1++; // нажатие a[1] = b[0]; // присваиваем сохранённые значения первой позиции a[2] = b[1]; // a[3] = b[2]; // установка десятых коррекции // в первой позиции меню if (c_corr1 < 10) { // положительные числа a[3] = c_corr1; a[1] = 11; } if (c_corr1 > 9) { a[3] = c_corr1 - 10; if (c_corr1 == 10) { // если а[3]=0 a[1] = 11; // то минус не выводим } else { a[1] = 10; // отрицательные числа } // выводим "-" } if (c_corr1 == 20) { c_corr1 = 0; a[3] = 0; a[1] = 11; } b[0] = a[1]; // сохраняем знак b[1] = a[2]; b[2] = a[3]; //сохраняем (в операт. памяти) // значения десятых коррекции } if (s == 3) { // вторая позиция меню, чётное c_corr3++; // долгое нажатие a[1] = b[3]; // присваиваем сохранённые значения второй позиции a[2] = b[4]; // a[3] = b[5]; if (c_corr3 < 10) // установка десятых во второй позиции меню a[3] = c_corr3; if (c_corr3 == 10) { c_corr3 = 0; a[3] = 0; } b[5] = a[3]; } if (s == 5) { // третья позиция меню, чётное c_corr4++; // долгое нажатие a[1] = b[6]; // присваиваем сохранённые значения третьей позиции a[2] = b[7]; // a[3] = b[8]; if (c_corr4 < 10) // установка десятых в третьей позиции меню a[3] = c_corr4; if (c_corr4 == 10) { c_corr4 = 0; a[3] = 0; } b[8] = a[3]; // сохр. значение в оперативной памяти } } // ==================================================== else { //установка едениц и десятков, нечётное долгое нажатие кнопки // =================================================== if (s == 1) { // нечётное долгоe нажатие в первой позиции меню c_corr1++; // продолжает действие чётного нажатия a[1] = b[0]; // присваиваем сохранённые значения первой позиции a[2] = b[1]; // a[3] = b[2]; if (c_corr1 < 10) { // положительные числа a[3] = c_corr1; a[1] = 11; // } if (c_corr1 > 9) { a[3] = c_corr1 - 10; if (c_corr1 == 10) { a[1] = 11; } else { a[1] = 10; // отрицательные числа } } if (c_corr1 == 20) { c_corr1 = 0; a[3] = 0; a[1] = 11; } b[0] = a[1]; // сохраняем знак b[1] = a[2]; b[2] = a[3]; //сохраняем значения десятых коррекции } if (s == 3) { // вторая позиция меню, нечётное // долгое нажатие a[1] = b[3]; // присваиваем сохранённые значения второй позиции a[2] = b[4]; a[3] = b[5]; c_corr2++; if (c_corr2 < 10) { a[2] = c_corr2; a[1] = 0; } if (c_corr2 > 9) { a[1] = c_corr2 / 10; a[2] = c_corr2 % 10; } if (c_corr2 > 99) { a[1] = 0; a[2] = 0; c_corr2 = 0; } b[3] = a[1]; b[4] = a[2]; b[5] = a[3]; // сохраняем нижнее пороговое значение температуры } if (s == 5) { // третья позиция меню,нечётное // долгое нажатие a[1] = b[6]; // присваиваем сохранённые значения третьей позиции a[2] = b[7]; a[3] = b[8]; c_corr5++; if (c_corr5 < 10) { a[2] = c_corr5; a[1] = 0; } if (c_corr5 > 9) { a[1] = c_corr5 / 10; a[2] = c_corr5 % 10; } if (c_corr5 > 99) { a[1] = 0; a[2] = 0; c_corr5 = 0; } b[6] = a[1]; b[7] = a[2]; b[8] = a[3]; // сохраняем верхнее пороговое значение температуры } } } } else { // ЕСЛИ КНОПКА БОЛЬШЕ НЕ УДЕРЖИВАЕТСЯ KNflgNEW = false; // сброс флага нового нажатия кнопки KNflgL = false; // сброс флага долгого нажатия Nkn++; // считаем количество завершённых долгогих нажатий mig1 = 0; // сброс счётчика количества миганий } } } } //================= EEPROM ================= void wr_eepr() { EEARH &= 0b11111110; // записываем 0 в старший разряд адреса b[9] = c_corr1; b[10] = c_corr2; // сохраняем счётчики меню b[11] = c_corr3; b[12] = c_corr4; b[13] = c_corr5; byte eeadr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; //адреса eeprom for (byte i = 0; i < 14; i++) { // для каждого байта while (EECR & (1 << EEPE)); // ждём готовности к записи EEARL = eeadr[i]; //подготавливаем адрес и данные EEDR = b[i]; EECR |= (1 << EEMPE);// уст. в лог. единицу бит EEMPE // начинаем запись установкой бита EEPE EECR |= (1 << EEPE); } } void read_eepr() { EEARH &= 0b11111110; // записываем 0 в старший разряд адреса byte eeadr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; // адреса eeprom for (byte i = 0; i < 14; i++) { // для каждого байта while (EECR & (1 << EEPE)); // ждём готовности EEARL = eeadr[i]; // задаём адрес EECR |= (1 << EERE); // устанавливаем бит EERE // начинаем чтение b[i] = EEDR; // читаем байт } c_corr1 = b[9]; c_corr2 = b[10]; // возвращаем c_corr3 = b[11]; // счётчики меню c_corr4 = b[12]; c_corr5 = b[13]; }
Доступные для изменения значения выделяются миганием
4. Выход из меню - 4, 5, 6 позиции "noSAVE", "SAVE", "nULL"- без изменений, с сохранением изменений и обнуление всех значений меню соответственно. Выход из этих позиций происходит автоматически,по истечении 8 секунд
Данные меню сохраняются в EEPROM в двоично - десятичном виде, один десятичный разряд или знак минуса - один байт.
С антидребезгом сильно не заморачивался, т.к. всё-таки это Proteus. Сделал то, что первое пришло в голову. Подозреваю, что это может потянуть вообще на отдельную тему.
Прилагаю упрощённый код, где оставил только меню и индикацию постоянного значения измеряемой темп-ры -0.7С.Итак на страницу не влазит))). Временно добавил вывод данных eeprom в терминал. Оказывается, в Протеусе это сделать не сложно.
И спасибо lilik-у, за то, что в одной из своих тем показал, как можно выложить гифку на форуме
"Простое меню" оказалось не очень-то простым для меня. Программа чтения из датчика с индикацией занимала 7% памяти устройства и 4% динамической памяти. С добавлением меню (плюс немного коррекция и контроль пороговых значений) уже 15% и 7% соответственно. Т.е по объёму прога выросла в два раза(!) Это нормально для такого простого меню?
это даже очень мало...
Нормально, когда основной код программы (работа с железом и логика) занимает 5%, а меню и плюшки - оставшиеся 95%. И ничего удивительного в этом нет, работа с железом много кода и памяти не требует, а например работа с экраном? :)
Код не смотрел
Нормально, когда основной код программы (работа с железом и логика) занимает 5%, а меню и плюшки - оставшиеся 95%. И ничего удивительного в этом нет
Спасибо за ответ, не знал этого. Моё первое меню
Логику нужно разделить. Например, обработка кнопки - одна отдельная функция или класс, в который зашиты все связанные с этим переменные и логика. Вызов в loop просто возвращает одно из четырех значений - нет нажатия, короткое, длинное, совсем длинное.
Эти массивы типа a и b - через месяц невозможно будет даже самому разобраться, зачем нужны и как работает.
Логику нужно разделить. Например, обработка кнопки - одна отдельная функция или класс, в который зашиты все связанные с этим переменные и логика. Вызов в loop просто возвращает одно из четырех значений - нет нажатия, короткое, длинное, совсем длинное.
Эти массивы типа a и b - через месяц невозможно будет даже самому разобраться, зачем нужны и как работает.
Спасибо, подумаю об этом. Видел пару примеров меню, но они на С++, а я пока в этом "ни в зуб ногой",
ещё только Си пробую.
Решил делать, согласно своему пониманию
Как по мне, если критика приветствуется, длинные долгие нажатия разной длины лучше бы заменить. Я как то делал несколько простых устройств с библиотекой kakmyc_btn.h (есть на форуме, ещё раз ему спасибо). Там использовался энкодер со встроенной кнопкой. Мне было это достаточно и для простого меню и для изменений в нем. Использовал долгое удержание, одинарные/двойные и тройные нажатия, ну и энкодер для изменения значений.
Как по мне, если критика приветствуется, длинные долгие нажатия разной длины лучше бы заменить. Я как то делал несколько простых устройств с библиотекой kakmyc_btn.h (есть на форуме, ещё раз ему спасибо). Там использовался энкодер со встроенной кнопкой. Мне было это достаточно и для простого меню и для изменений в нем. Использовал долгое удержание, одинарные/двойные и тройные нажатия, ну и эндодермах для изменения значений.
Критика именно приветствуется! Долгое(очень) нажатие при входе работает один раз. Я его ввёл специально, чтоб исключить случайный вход по короткому(сначала было так)
Об остальном, спасибо, буду думать
Логику нужно разделить.
Сейчас логика такая:
в loop каждые 4мс происходит индикация, первичный опрос кнопки и запущен общий счётчик synchr
Если меню активно(по нажатию кнопки), то в loop поступают из меню данные об индикации а[] и счётчиков и флагов для мигания. Так же там, по флагу, и выход из меню(вот его легко можно сделать отдельной функцией)
Если меню не активно, по счётчику synchr, происходит опрос датчика , crc, и.т.д. и в loop передаются данные об индикации
Т.е. кнопка и так в отдельной функции(menu), и с loop связана минимально.
Как разделить ещё логику на том , что есть, пока не представляю, разве писать с чистого листа, по новой?
Ну, надо сказать, что меню - это элемент пользовательского интерфейса, а пользовательский интерфейс - всегда головная боль разработчика и масса пожираемых ресурсов. Кстати, именно по критерию ресурсоемкости мне пришлось оптимизировать меню, описанное здесь: http://arduino.ru/forum/proekty/menyu-dlya-dvukhstrochnogo-displeya Там же есть ссылка на видео.
В общем, расход памяти в байтах оказался меньше, чем количество пунктов меню (пунктов более 180, а расход памяти - 120), и это при динамической структуре меню, т.е. меню можно перестраивать прямо в процессе выполнения программы.
А по поводу управления, я придерживаюсь точки зрения, что оптимальным вариантом являются энкодер и пара кнопок. Кнопку на самом энкодере лучше либо вообще не использовать, либо использовать для ускорения пролистывания при широком диапазоне изменения. Например, если необходимо выбрать целое число в диапазоне 1000, то разумно шаг энкодера переключать с 1 при отпущенной кнопке до 30 при нажатой.
оптимальным вариантом являются энкодер и пара кнопок. Кнопку на самом энкодере лучше либо вообще не использовать, либо использовать для ускорения пролистывания при широком диапазоне изменения. Например, если необходимо выбрать целое число в диапазоне 1000, то разумно шаг энкодера переключать с 1 при отпущенной кнопке до 30 при нажатой.
Спасибо, буду знать. Вот и BOOM то же говорит. Файлы скачал, мне на долго теперь хватит разбираться.
С энкодером , если что, попробую следующее меню. Сейчас хочу понять, если что не так, и "допилить" это,
если возможно. Т.е. в принципе-то работает, но смутил объём , что даже на 5 страниц не влезает)))
Логику нужно разделить. Например, обработка кнопки - одна отдельная функция или класс, в который зашиты все связанные с этим переменные и логика. Вызов в loop просто возвращает одно из четырех значений - нет нажатия, короткое, длинное, совсем длинное.
Логику , наконец, разделил )). Всё развёл по функциям, оставил в loop только счётчик synchr и вызов функций. Пришлось добавить один флаг. Но это всё только для удобства чтения, по сути, те же яйца, только в профиль. Даже добавилось 48байт кода и 1 байт оперативной памяти. Как я ни старался, существенно уменьшить объём программы уже не получается, по крайней мере на моём уровне "развития")).
На этом пока остановлюсь, буду учиться дальше, разбирать , что дал andriano, пока много ещё непонятно.
P.S. Меню с одной кнопкой, всё же считаю иногда будет к месту. Если оно маленькое, не надо часто
лазить , выставил, что надо и забыл. Да и нога МК нужна лишь одна.
Ты не поверишь, наерно, но если сделать "лесенку" из резисторов и кнопок и подключить всё это на аналоговый вход, то кнопок можно поставить от 2 до 16, пользоваться будет не в пример удобнее, а вход будет занят тоже только один. Я себе последнее время схему из этого шилда ставлю куда не попадя, работает, с-ка.
Ты не поверишь, наерно, но если сделать "лесенку" из резисторов и кнопок и подключить всё это на аналоговый вход, то кнопок можно поставить от 2 до 16....
Только работает вся эта "лестничная шелупень" неустойчиво (нестабильно). Пробовал и отказался. Навсегда.
Только работает вся эта "лестничная шелупень" неустойчиво (нестабильно). Пробовал и отказался. Навсегда.
Ви, таки, п'госто не умеете её готовить. :)
Правда, справедливости ради, одновременные нажатия ловить на такой клавиатуре - тот еще геморрой, но я их нигде и не использую, чего и вам желаю.
А пгосто бывает, что схему ужЕ поменять невозможно, поэтому и приходится геморойничать.
Ты не поверишь, наерно, но если сделать "лесенку" из резисторов и кнопок и подключить всё это на аналоговый вход, то кнопок можно поставить от 2 до 16, пользоваться будет не в пример удобнее, а вход будет занят тоже только один. Я себе последнее время схему из этого шилда ставлю куда не попадя, работает, с-ка.
Поверю и почти согласен! Это типовая схема многих мониторов и др. аудио/видео. Но , во первых, я делал больше ради обучения, а, во вторых, всё-таки кнопок надо больше))
Ну, надо сказать, что меню - это элемент пользовательского интерфейса, а пользовательский интерфейс - всегда головная боль разработчика и масса пожираемых ресурсов.
Да, в первую очередь нужно думать о пользователе. А у нас об этом, зачастую, забывают. Правда, Дим-димыч?
больше чем скока?
Если в твоём проекте надо больше 16 кнопок, проще тогда взять клаву PS\2, под нее и биб-ки есть.
Это да.) Бывало https://www.youtube.com/watch?v=BgdzBgIMACc
Да, в первую очередь нужно думать о пользователе. А у нас об этом, зачастую, забывают. Правда, Дим-димыч?
Это да, есть такое... Просто , имел ввиду например, если надо поддерживать постоянную температуру в каком-нибудь помещении с помощью ТЭНа, раз в год выставить, ну там ещё раз подкорректировать и забыть. Зачем тогда 6 кнопок?
А так да, если надо постоянно пользоваться - неудобно.
больше чем скока?
Извиняюсь что нечётко выразился. Имел ввиду , что для "лесенки" всё-таки нужна больше чем одна
кнопка.
Извиняюсь что нечётко выразился. Имел ввиду , что для "лесенки" всё-таки нужна больше чем одна
кнопка.
Мне всегда достаточно было 5. Как в 1602 Keypad Shield.
В моем последнем проекте галетник на 8 положений. Это все равно что 8 кнопок, которые нельзя нажать одновременно. Никаких даже намеков на сбои и прочие "неустойчивости (нестабильности)". Нужно только зашунтировать аналоговый вход небольшой емкостью.
Очень часто встречается навигация по меню тремя кнопками. Вверх/вниз/ок
В моем последнем проекте галетник на 8 положений. Это все равно что 8 кнопок, которые нельзя нажать одновременно. Никаких даже намеков на сбои и прочие "неустойчивости (нестабильности)". Нужно только зашунтировать аналоговый вход небольшой емкостью.
8 это немного.
Я тут снимал как то с высечного станка от trumpf сигнал с регулятора скорости . Там 24 положения было.
Правда выходов тоже всего 8, замыкались в разной комбинации. Такое себе развлечение сидеть снимать диаграмму, а потом описывать ее кодом.
Должна быть 4-я - отмена.
Должна быть 4-я - отмена.
Нет не должно
Ок->сдвиг курсора вправо зацикленный.
+/- смена значений или пунктов меню
Да.
Надо было мне процитировать сообщение №12, чтобы было понятно о чем речь.
Но, вообще говоря, я отчетливо различал на потенциометре 128 градаций при полном исключении "мерцания". Так что десятки кнопок IMHO должны различаться уверенно.