Простое меню
- Войдите на сайт для отправки комментариев
Всем привет! Продолжаю знакомиться с Ардуино и языком Си на примере опроса датчика 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. Меню с одной кнопкой, всё же считаю иногда будет к месту. Если оно маленькое, не надо часто
лазить , выставил, что надо и забыл. Да и нога МК нужна лишь одна.
void loop() { // интервал 4мс для обновления // динамической индикации, счётчика synchr, и // опроса кнопки if ((millis() - last0_millis) >= 4) { last0_millis = millis(); read_button(); // опрос кнопки menu(); // вход в меню(если активно) one_mig1(); // организация мигания в меню 1 часть mask_ind(); // гасим разряд и точку k++; // Здесь спасибо, счётчик synchr с кодом Alexey_Rem помогал if (k > 7) { // каждые 8 циклов k = 0; // 8х4=32ms счётчик синхронизации всех synchr++; // +32ms частей программы flg = true; //флаг обновения synchr // разрешающий выпонение некоторых блоков программы } new_ind(); //обновление индикации one_mig2(); // организация мигания 2 часть switch (synchr) { // распределение времени между // отдельными частями программы case 218: // 218х32 = 6976мс if (out) { // если поднят флаг выхода из меню vihod(); } //-------------------------------- // если не установлен флаг пребывания в меню // выполняется основная часть программы // распределение времени для // общения с датчиком 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; } } } }Ты не поверишь, наерно, но если сделать "лесенку" из резисторов и кнопок и подключить всё это на аналоговый вход, то кнопок можно поставить от 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 должны различаться уверенно.