[uquote="ARV",url="/forum/viewtopic.php?p=4641221#p4641221"]почитайте мою статью (правда, там движок сайта попорчен и код не красивый) о нисходящем программировании:
https://simple-devices.ru/articles/7-so ... 0-16-40-05
может быть, на какие-то мысли вас натолкнет.[/uquote]
Это подходит в том случае, если есть четкое или какое-нибудь Т.З. А если такового нет?
Используя Ваш пример с покупкой колбасы, моя реальность выглядит так:
Теплый солнечный день. Выходной. Отличное настроение и хочется выйти из дома прогуляться. Но куда и зачем?
И тут вспоминаешь, что на работе кто то говорил про нового мужика на рынке, который торгует отличной колбасой. Именно туда и надо идти.
Ходишь по рынку, смотришь что продают, интересуешься ценами. Доходишь до колбасных рядов и видишь один прилавок отличающийся ассортиментом от других. Правда торгует не мужик, а женщина, но зато продукцией домашнего изготовления.
Пробуешь. Разыгрывается аппетит. Но колбаса - хорошо, а хочется к ужину чего-нибудь "горячего". Она тебе предлагает домашние купаты, которые изготовлены по тому же рецепту что и колбаса, но только сырые. Их можно поджарить как на мангале, так и на сковородке. По этому покупаешь и колбасу и купаты... Ну, надо же сравнить.
И вот тут в голове начинает вырисовываться меню на ужин. По этому покупаешь еще картошку и молоко для пюре.
По пути домой с рынка встречаешь знакомого, который тебе сообщает, что в КБ появилась великолепная водка из Белоруссии. И стоит дешево. Поэтому тут же заходишь в ближайший КБ и еще покупаешь бутылочку.
[uquote="ARV",url="/forum/viewtopic.php?p=4641221#p4641221"]в общем, моё мнение однозначно:
нужен глубокий рефакторинг кода. чтобы и собственное понимание задачи и пути её решения выкристаллизовалось лучше.[/uquote]
Это следствие выше описанных действий. Т.е. когда у тебя еще нет абсолютно никакого представления о том, что будет дальше.
А так, бы (возвращаясь к выше описанному) сразу бы на рынке купил бы еще малосольных огурчиков и маринованных грибов. Но когда на столе все стоит горячее, а рядом запотевшая бутылочка.... уже нет желания бежать на рынок за огурчиками и грибочками.
[uquote="ARV",url="/forum/viewtopic.php?p=4641221#p4641221"]у вас в коде намешаны низкоуровневые операции типа обращения к портам с высокоуровневыми действиями типа расчетов полученных извне значений.
то же самое касается и "деления" кода на функции: складывается впечатление, что это делалось не путем разработки алгоритма, а путем вычленения кусков "гладкого кода" в отдельные кусочки, чтобы просто хоть как-то можно было уследить за ходом процессов... но так писать не стоит.[/uquote]
Думаю, что ярким примером того будет функция измерения температуры - Meas_Temperatura. (она описана в ds18b20.с и использует wire.h)
Спойлер
Код: Выделить всё
float Meas_Temperatura (void)
{
uint16_t DS18b20; //переменная для считывания регистров DS18b20
uint8_t _1Wire = 0;
_1Wire = Wire_init(); //инициализация датчика 1-Wire
Wire_sendComand(0xCC); //Skip ROM пропуск ROM команда всем устройствам на шине
Wire_sendComand(0x44); //все датчики одновременно считали температуру (температурное преобразование)
_1Wire = Wire_init();
Wire_sendComand(0xCC); //Skip ROM пропуск ROM команда всем устройствам на шине
Wire_sendComand(0xBE); //чтение памяти
_1Wire = Wire_ButeRead(); //читаем 1 из 2х байт
DS18b20 = Wire_ButeRead(); //читаем 2 из 2х байт
DS18b20 <<=8;
DS18b20 |=_1Wire;
_1Wire = Wire_init();
return (float)DS18b20/16;
}
И тут мне не понятно, как правильно было бы ее реализовать?
Сделать так, что бы эта функция возвращала uint16_t Raw_Temperatura, которая получается из двух байтов ответа датчика DS18b20. А потом float temperatura = (float)Raw_Temperatura/16;
Так?
[uquote="ARV",url="/forum/viewtopic.php?p=4641221#p4641221"]чем отличаются функции в следующем коде:
Код: Выделить всё
//Вычисление Тока
uint32_t PZEM_Current (void)
//Вычисление мощности
uint32_t PZEM_Power (void)
//Вычисление потребленной энергии
uint32_t PZEM_Energy (void)
три функции, которые делают абсолютно одинаковые действия! разница лишь в том, с какими ячейками массивов они работают! но ведь тут очевидно напрашивается ОДНА функция, которая получает в параметре номер первой рабочей ячейки, а уж остальное делает относительно этого адреса... поправьте меня, если это не так:[/uquote]
Вы абсолютно правы! Переделал эти три и еще три: вычисление частоты PZEM_Frequency, вычисление коэффициента мощности PZEM_PowerFactor и вычисление напряжения PZEM_Voltage.
Я когда отдельно изучал на практике модуль PZEM-004T, долго не мог получить от него "правильный" ответ. Вот и начал писать "по мануалу".
А когда все заработало, не задумался об оптимизации кода.
Большое спасибо! Очень помогло Ваше замечание.
[uquote="ARV",url="/forum/viewtopic.php?p=4641221#p4641221"]не знаю, что именно делает ваша программа, но, имхо, функция main должна выглядеть примерно так (кода нет, одни комменты к несуществующим функциям
Код: Выделить всё
int main(void){
// инициализация всей периферии
// обращение к внешней аппаратуре и инициализация её
// подготовка данных (считывание из EEPROM? поиск там каких-то адресов и т.п.)
// вывод на ЖКИ приветствия (или меню, заставки и т.п.)
// запуск прерываний (это не обязательно, может, только некоторых из них)
while(1){
// измерения
// обработка измерений
// вывод результатов
// опрос событий EVENT (пришли данные по UART? нажаты кнопки? повернут энкодер? и т.п.)
switch(EVENT){
case EV_USART: // обработка данных из USART
break;
case EV_KBD: // обработка нажатий кнопок
break;
// и так далее, все события
}
}
}
и функции у вас должны работать с одними и теми же сущностями: если функция вычисляет, то она в порты не лезет, а если лезет в порты, то не вычисляет. как-то так.[/uquote]
Об этом я много где читал, но до конца не все понятно с прерываниями. Иными словами, если возвращаться к Вашему примеру с колбасой, то будет это выглядеть примерно так:
- утром срабатывает прерывание по таймеру - "проснуться, поставить чайник".
Ставишь чайник - кружишься в общем цикле: достать кружку, подготовить заварку, приготовить бутерброд с колбасой, поесть, одеться и т.д.
- Срабатывает внешнее прерывание - "чайник закипел"
Выключаешь чайник, - кружишься в общем цикле: достать кружку, подготовить заварку заварить чай. приготовить бутерброд с колбасой, поесть одеться и т.д.
- Срабатывает прерывание по таймеру "до автобуса на работу N минут".
Одеваешь ботинки, куртку и на остановку. А далее 8 часов ты не доступен для других прерываний. Точнее пока не выполнишь
Код: Выделить всё
case Perform_Work: // выполнение обязанностей на производстве
break;
другие прерывания становятся в очередь.
Но ведь раньше может сработать внешнее прерывание "нет колбасы" и ты можешь уйти в
Код: Выделить всё
case Shopping_Trip: // поход за покупками
break;
по выходу из которого вставшее в очередь обработки прерывание "до автобуса на работу N минут" - будет уже бесполезно, точнее в
Код: Выделить всё
case Perform_Work: // выполнение обязанностей на производстве
break;
повиснешь до следующего дня стоя на остановке в ожидании автобуса на работу.
Как тут быть? Я не представляю, как сделать так, что бы пришло "самое главное прерывание", которое бы прервало текущую операцию в основном цикле и заставило бы там же (в основном цикле) исполнить
Код: Выделить всё
case Perform_Work: // выполнение обязанностей на производстве
break;
после чего вернулось обратно к выполнению прерванной операции.
Код: Выделить всё
case Shopping_Trip: // поход за покупками
break;
после работы по пути домой.
Мне очень хотелось бы понять, как с точки зрения гуру, следует строить программу прерывания. К примеру возьмем энкодер с кнопкой. Тут возможны следующие варианты:
1) читаем в переменную состояние порта;
2) обрабатываем считанное состояние (инвертирование, сдвиг в право, наложение маски);
3) производим распознование воздействие на энкодер (вращение или нажатие на кнопку) и взводим соответствующую переменную - press_button или rotating_control
4) распознаем действие: кнопка нажата "коротко", "долго" или "двойное нажате". Энкодер вращаем вправо или влево.
Я думаю, что все эти действия следует включать в прерывание. Или можно сделать как то по другому?
[uquote="ARV",url="/forum/viewtopic.php?p=4641221#p4641221"]не бойтесь сервиса, который предоставляет вам Си: ввод-вывод стандартными средствами сделан там вполне прилично, и, уверяю вас, сильно упростит вам жизнь.
https://simple-devices.ru/articles/7-so ... -interface вот так можно красиво работать "стандартными средствами" с ЖКИ
https://simple-devices.ru/articles/7-so ... console-io а вот так с USART
последним способом я пользуюсь уже много лет, и ни разу не пожалел. согласитесь, гораздо проще написать
printf("U=%d\nI=%d\n", U, I);
и тем самым отправить в USART две строки с значениями напряжения и тока, чем городить то же самое с помощью кучи массивов, циклов и т.п. неочевидных преобразований... 2 килобайта FLASH ради такого - не великая цена. ну и еще 2К, если никак без float в этом случае не обойтись... нервы дороже.[/uquote]
Согласен. Но боюсь. На выше приведенном примере я уже "вступил" в dtostrf. Кто бы мог подумать, что ему для вывода числа из N цифр, требуется массив размером N+1.
А так, я изучая LCD изучил его команды и написал свою библиотеку, которой теперь пользуюсь. И по мере необходимости ее изменять и адаптировать под свои задачи.