while (1) { KEYB_ScanKeyboard(); buf = KEYB_GetKey();
if (buf) { if (b < 4 && buf != '*'&& buf != '#') { // Проверяем, есть ли еще место в массиве и выполняем ограничения на ввод if (b == 0 && buf != '1') { LCD_Goto(0, 0); LCD_SendString("Oshibka"); _delay_ms(2000); LCD_Clear();
b = 0; k = 0; for (i = 0; i < 4; i++) { buttons[i] = 0; } continue; // Пропускаем ввод, если первый адрес массива не равен единице } if (b == 1 && (buf != '0' && buf != '1')) { LCD_Clear(); LCD_Goto(0, 0); LCD_SendString("Oshibka"); _delay_ms(2000); LCD_Clear();
b = 0; k = 0; for (i = 0; i < 4; i++) { buttons[i] = 0; } continue; // Пропускаем ввод, если второй адрес массива не равен нулю или единице }
ну хотя бы send(); end(); fin(); выкинуть из каждого case. И какой смысл передавать одну и ту же PAUSE в selectKey? но без алгоритма такую портянку оптимизировать - только за деньги. Но думаю, что cont явно просится передаться в selectKey без всяких switch-case...
...но без алгоритма такую портянку оптимизировать - только за деньги. ..
В этом коде при наборе комбинации от 1001 до 1100 происходит последовательное включение светодиодов в определенном порядке. С начало идёт включение по функции send(); затем набранная комбинация после чего команды из функции end(); после чего вывод на ЖК дисплей через функцию fin(); хотя думаю end(); и fin(); можно было объединить в одну.
эта комбинация как формируется? могу я изменить правило формирования, например, не 1001...1100, а 1002...1202, то есть, те же сто, только все чётные? или эти числа где-то ещё используются - отображаются?
Прикрепил схему. Функция send(); поочерёдно включает комбинацию светодиодов RES-PRI-TOT-CALL-0-OK-1. Затем поочередно включает последовательность набранной комбинации с клавиатуры. Оно может быть от 1001, 1002, 1003 и тд. до 1100. К примеру, комбинация 1012 будет выглядеть так:
Начальная единица уже прописана внутри функции send(); и в коде проверяется на её присутствие, если начальный символ другой то код выдаст ошибку и остановиться.
1100 конечная максимальная комбинация, далее нельзя. Затем срабатывает комбинация из функции end(); При этом поочерёдно включаются и выключаются светодиоды PRI и RES.
Добавлено after 12 minutes 44 seconds: но если я правильно понял алгоритм, то весь этот громадный switch сокращается до перебора буфера и отправки каждой цифры:
Код:
send(); for (i = 0; i < 4; i++) { switch(buttons[i] - '0') { case 0: selectKey(3, 5, PAUSE); break; case 1: selectKey(3, 5, PAUSE); break; case 2: selectKey(3, 5, PAUSE); break; case 3: selectKey(3, 5, PAUSE); break; case 4: selectKey(3, 5, PAUSE); break; case 5: selectKey(3, 5, PAUSE); break; case 6: selectKey(3, 5, PAUSE); break; case 7: selectKey(3, 5, PAUSE); break; case 8: selectKey(3, 5, PAUSE); break; case 9: selectKey(3, 5, PAUSE); break; } end(); fin();
только надо поставитm правильные строки-столбцы. ну и можно выкинуть PAUSE А можно снова воспользоваться массивами. тогда сократится до
Код:
send(); for (i = 0; i < 4; i++) { selectKey(arr1[buttons[i] - '0'], arr2[buttons[i] - '0'], PAUSE); } end(); fin();
Вы перебрали все 100 вариантов для комбинаций буфера я же перебрал 10 вариантов для каждой ячейки в буфере. если буфер станет 100 ячеек, в моём варианте достаточно изменить число итераций в цикле. а в Вашем? дописать ещё десяток тыщ строк...
Бы попробовал заменить "длинный" switch(count) на напр. 6 массивов возможных состояний. Написанно так будет "короче" как код (будет произведена только замена с данными (один раз в осн. цикле), заданными индексом массива).
(Не гарантирую, что будет занимать меньше ресурса (памяти). Должно быть проверено).
по мне так самая очевидная оптимизация switch(cont) это просто использовать массив из 2-3-4 байтных константных слов, упаковать можно множеством способов, хоть побитно, хоть используя 2й индекс , после получения указателя - разбирать их и грузить куда нужно. исходный это какойто кошмар с которым никакой O2 не справится, это и по размеру кода в разы и по нагрузке на ядро вдвое неэффективно. на научном языке такое называется классический ГК
Добавлено after 18 minutes 16 seconds: забавно veso74 почти тожесамое написал, не даром говорят у программистов мысли совпадают
1. В оригинальной функции selectKey два раза используется функция _delay_ms, причем при первом вызове всегда передается константа PAUSE, а при втором вызове - аргумент delay. С учетом того, что далее по коду в функцию selectKey в качестве аргумента delay всегда передается константа PAUSE, то это выглядит крайне странно. Если вам нужно выдерживать 2 разных по длительности интервала, то используйте и 2 разных константы (или макросы, как у вас). Иначе это выглядит как опечатка, и оба вызова _delay_ms должны быть с аргументом delay. Как заметил Martian, можно просто убрать аргумент delay и сразу прописать PAUSE, т.к. как я понял этот интервал зависит от аппаратной реализации вашей схемы, и не меняется в runtime.
2. При вводе числового кода, у вас есть проверка на то, что первые 2 цифры должны быть "10" или "11". Проверка разделена на 2 условия, и в обоих условиях продублирован практически один и тот же код. Можно объединить в одну проверку: Спойлер
Код:
// Пропускаем ввод, если первый адрес массива не равен единице char isKeycodeInvalid_First = (b == 0 && buf != '1'); // Пропускаем ввод, если второй адрес массива не равен нулю или единице char isKeycodeInvalid_Second = (b == 1 && (buf != '0' && buf != '1')); if (isKeycodeInvalid_First || isKeycodeInvalid_Second) { if (isKeycodeInvalid_Second) LCD_Clear(); LCD_Goto(0, 0); LCD_SendString("Oshibka"); _delay_ms(2000); LCD_Clear(); b = 0; k = 0; for (i = 0; i < 4; i++) buttons[i] = 0; continue; }
Кроме того, часть алгоритма (очистка переменных b, k и buttons, очистка LCD) также продублирована в методе fin. Это тоже можно оптимизировать, либо а) вынести общую часть алгоритма очитки в отдельную функцию, и вызывать эту функцию из функции fin и из условия проверки первых двух цифр, либо б) сделать одну общую функцию "завершения ввода комбинации", в которую через аргументы передавать причину очистки (строки "Код отправлен!", "Ошибка", "Очистка" через аргумент типа const char*).
3. Как уже отметили veso74 и AlexS4, можно использовать массив значений для функции selectKey. Я бы использовал массив структур, т.к. не уловил закономерности (зависимость вводимого кода 1001-1100 от порядка и номеров установки Row и Col при вызове selectKey): Спойлер
// работаем с оригинальной глобальной переменной 'cont', в которой уже посчитано введённое значение { int index = cont - (1001); // т.к. массивы индексируются с нуля const TSelectedKey* key = SelectedKeys[index].Key; // далее из оригинала: send(); selectKey(key[0].Row, key[0].Col, PAUSE); selectKey(key[1].Row, key[1].Col, PAUSE); selectKey(key[2].Row, key[2].Col, PAUSE); end(); fin(); }
Однако тут есть важное замечание: для работы с массивами вам необходимо организовать проверку на выход за границы этого самого массива (при использовании "динамического" индекса)! Например, в оригинальном коде не было проверки на вводимые числа 1101-1199, есть только отдельные проверки на *0**-*1** и **00-**99, что не эквивалентно проверке *001-*100. При этом в оригинальном коде использовался switch-case, и все неописанные комбинации не выполнялись (не изменялось состояние портов). Т.е. при нажатии 'звездочки' при неправильном числовом коде ничего не происходило: ни пины не дергались, ни дисплей не реагировал, но можно было это стереть через 'решетку'. При использовании же массивов всегда будет что-то выполняться, и если входные данные некорректные, то программа просто поломается (лечится перезагрузкой МК).
P.S. Пишите понятные имена объектов. Функции вы нормально назвали (по имени ясно, что она делает), а вот переменные обделили (b, k, ...)
Да не надо там такой громадный массив. Там приблизительно такая логика: есть некий буфер, размером 4. Его цифровое десятичное содержимое (или часть) надо последовательно высветить на экране. Вот и всё. Так как каждое значение имеет лишь десять вариантов, то просо необходим цикл с числом итераций не больше размера этого буфера, где либо проверяется на совпадение одному из 10 состояний, либо адресуется к массиву[10]
Используя Ваши подсказки и советы, за что Вам премного благодарен, немного переписал код и теперь он состоит только из 651 строк против начальных ~2900.: Спойлер
Код:
#define F_CPU 32000000UL // Определение тактовой частоты для функций задержки #include <inttypes.h> // Библиотека для работы с типами int #include <avr/io.h> // Библиотека для ввода-вывода #include <avr/interrupt.h> // Библиотека для работы с прерываниями #include <avr/sleep.h> // Библиотека для управления режимами сна #include <util/delay.h> // Библиотека для работы с задержками #include "keyboard.h" // Библиотека для работы с клавиатурой #include "lcd_lib.h" // Библиотека для работы с LCD #define PAUSE 150 // Определение времени задержки
int k = 0; int b = 0; int i = 0; int c = 0; int cont = 0;
// Определение столбцов и строк int col[5] = {0b00000000, 0b00000001, 0b00000010, 0b00000100, 0b00001000}; int row[6] = {0b00011111, 0b00011110, 0b00011101, 0b00011011, 0b00010111, 0b00001111};
unsigned char buf = 0; unsigned char buttons[4];
// Функция выбора ключа void selectKey(int r, int c) { PORTD = col[r]; // Выбор столбца PORTC = row[c]; // Выбор строки _delay_ms(PAUSE); // Ждем PORTC = row[0]; // Выбираем первую строку PORTD = col[0]; // Выбираем первый столбец _delay_ms(PAUSE); // Ждем }
// Функция отправки void send(void) { selectKey(1, 5); // RES selectKey(1, 4); // PRI selectKey(1, 1); // TOT selectKey(1, 2); // CAL
int i; for ( i = 0; i < 4; i++) { selectKey(3, 5); // 0 } selectKey(4, 5); // OK selectKey(2, 2); // 1 }
// Функция завершения void end(void) { selectKey(1, 4); // PRI selectKey(1, 5); // RES _delay_ms(2); // задержка LCD_Clear(); // очищаем экран LCD_Goto(0, 0); // выбираем позицию на экране LCD_SendString("Kod otpravlen! "); // выводим сообщение _delay_ms(1000); // задержка LCD_Clear(); // очищаем экран b = 0; k = 0; for (i = 0; i < 4; i++) // обнуляем массив { buttons[i] = 0; } cont = 0; // обнуляем вход }
int main(void) { // Установить все выводы порта D и C как выводы DDRD=0xFF; DDRC=0xFF;
// Инициализировать LCD, установить курсор в начало и отобразить сообщение. LCD_Init(); LCD_Goto(0, 0); LCD_SendString("Vvedite "); LCD_Goto(4, 1); LCD_SendString("Koefitsent: "); _delay_ms(2000); // задержка LCD_Clear(); // очищаем экран KEYB_Init(); // инициализируем клавиатуру
//начинаем основной цикл while (1) { // сканируем клавиатуру и получаем значение введенной клавиши KEYB_ScanKeyboard(); buf = KEYB_GetKey();
// если была нажата клавиша if (buf) { // условие ввода символов и исключения некоторых клавиш if (b < 4 && buf != '*'&& buf != '#') { // проверяем правильность кода char isKeycodeInvalid_First = (b == 0 && buf != '1'); char isKeycodeInvalid_Second = (b == 1 && (buf != '0' && buf != '1')); if (isKeycodeInvalid_First || isKeycodeInvalid_Second) { if (isKeycodeInvalid_Second) LCD_Clear(); LCD_Goto(0, 0); LCD_SendString("Oshibka"); _delay_ms(2000); LCD_Clear(); b = 0; k = 0; for (i = 0; i < 4; i++) buttons[i] = 0; continue; } buttons[b++] = buf; LCD_Goto(k++, 1); LCD_WriteData(buf); } // по нажатию '*' выполняем обработку и отправку введенных данных if (buf == '*') { for (c = 0; c < 4; c++) { if (buttons[c] >= '0' && buttons[c] <= '9') { int digit = buttons[c] - '0'; cont = cont * 10 + digit; } } send(); switch(cont) // здесь указана привязка введенных значений к функциям (это нужно прокомментировать подробнее) { case 1000: selectKey(3, 5); selectKey(3, 5); selectKey(3, 5); break; // и так далее... для каждого действия } end(); } // Клавиша '#' очищает все введенные значения if (b > 0&&buf == '#') { LCD_Clear(); LCD_Goto(0, 4); LCD_SendString("Ochistka"); _delay_ms(2000); LCD_Clear();
b = 0; k = 0; for (i = 0; i < 4; i++) { buttons[i] = 0; } } } } return 0; }
while (1) { KEYB_ScanKeyboard(); buf = KEYB_GetKey();
if (buf) { if (b < 4 && buf != '*'&& buf != '#') { // Проверяем, есть ли еще место в массиве и выполняем ограничения на ввод if ((b == 0 && buf != '1') || (b == 1 && (buf != '0' && buf != '1'))) { LCD_Goto(0, 0); LCD_SendString("Oshibka"); _delay_ms(2000); clear(); continue; // Пропускаем ввод, если первый адрес массива не равен единице и если если второй адрес массива не равен нулю или единице } buttons[b++] = buf; LCD_Goto(k++, 1); LCD_WriteData(buf); } if ((buf == '*') && (buttons[0] = '1')) { send(); for (i = 1; i < 4; i++) { switch(buttons[i]) { case '0': selectKey(3, 5); break; case '1': selectKey(2, 2); break; case '2': selectKey(3, 2); break; case '3': selectKey(4, 2); break; case '4': selectKey(2, 3); break; case '5': selectKey(3, 3); break; case '6': selectKey(4, 3); break; case '7': selectKey(2, 4); break; case '8': selectKey(3, 4); break; case '9': selectKey(4, 4); break; } end(); } if (b > 0&&buf == '#') { LCD_Clear(); LCD_Goto(0, 4); LCD_SendString("Ochistka"); _delay_ms(2000); LCD_Clear(); clear(); } } } }
Добавлено after 5 minutes 26 seconds: и думаю, что можно ещё оптимизировать. Особенно в плане проверки этого:
Код:
if (buf) { if (b < 4 && buf != '*'&& buf != '#') { // Проверяем, есть ли еще место в массиве и выполняем ограничения на ввод if ((b == 0 && buf != '1') || (b == 1 && (buf != '0' && buf != '1')))
но мне неохота разбираться, верно ли сиё алгоритму. Но чувствуется некое противоречие
ну, это и есть 1% - я ж досконально не знаю алгоритма. Но он всё же отрабатывает, значит, осталось откорректировать в каком порядке и сколько должно браться, и с каким элементом в буфере сравнивается '1'
то есть, всего лишь это:
Код:
if ((buf == '*') && (buttons[0] = '1')) { send(); for (i = 1; i < 4; i++)
и условие заполнения буфера тоже проверить:
Код:
if (b < 4 && buf != '*'&& buf != '#') { // Проверяем, есть ли еще место в массиве и выполняем ограничения на ввод if ((b == 0 && buf != '1') || (b == 1 && (buf != '0' && buf != '1'))) {
Добавлено after 24 minutes 47 seconds: Поскольку в некоторых функциях уже нет нужды (send и end), можно их исключить, и оптимизировать вывод на дисплей. Итого 134 строк.
А зачем? Вам осталось совсем немного, код получился простой, довести до ума совершенно не сложно. Да и не люблю алгоритмы на видео, я люблю их в виде блок-схем или хотя бы подробного текстового описания....
Прикрепил видео работы устройства. СпойлерЗдесь функция send(); отрабатывает до однократного мигания светодиода под лейблом "1" после того как отработает "#", затем идут три символа из конструкции switch, после чего работает функция end(); Так же прикрепил немного оптимизированный код.
Последний раз редактировалось grachevvlad2023 Ср ноя 08, 2023 11:06:36, всего редактировалось 2 раз(а).
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 49
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения