А это общая тенденция к умиранию форумов - Казус, Изиэлектроникс, Электроникс. Это вообще нынче такая тенденция, что молодняк не желает учиться и делать что-то материальное, они желают развозить еду и выдавать товары на пунктах выдачи Озона. Раньше в юности я еще увлекался 3D-моделированием. Сейчас эта тема вообще полностью умерла. Раньше я думал, что новое поколение будет намного умнее меня. А оказалось, что я в то время знал больше, чем молодняк сейчас при наличии этих ваших интернетов.
Заголовок сообщения: Re: WinAvr в вопросах и ответах
Добавлено: Пн окт 21, 2024 20:13:25
Открыл глаза
Зарегистрирован: Пн май 04, 2015 12:30:18 Сообщений: 68
Рейтинг сообщения:0
Доброго времени суток! Большое спасибо всем ответившим. Извиняюсь за долгое отсутствие. Дело в том, что это любительская конструкция "выходного дня". И все это время я пытался найти баги в программе опираясь на прочитанное в этой теме. Кое что получилось. Всемогущий volatile - частично помог. Я пересмотрел всю программу и сделал все глобальные переменные используемые в прерываниях volatile. В результате стал нормально работать энкодер (раньше "заедал"). Заработала функция определения мощности. Но, при компиляции Optimize debugging experience (-Og) - глючит. Постоянно определяет мощность ТЭН2. При компиляции Optimize for size (-Os), Optimize most (-O3), Optimize more (-O2) - нормально. При Optimize (-O1) - глюки с выводом float (ну, и бог с ним, это ерунда). Далее я решил перенести функцию определения мощности в библиотеку. И.... все! Не работает... header file: Спойлер#ifndef INCFILE_H_ #define INCFILE_H_
volatile uint8_t lcd_data[20][4]; //массив для вывода на дисплей. Используется в lcd2004 volatile uint8_t uart_data[26]; //массив для входящих данных UART. Используется в UART volatile float power_percent[3]; //проценты от мощности
volatile uint8_t data_count; //счетчик для массива uart_data[26]. Используется в UART volatile uint8_t response_time; //счетчик времени ожидания ответа от PZEM-004t. Используется в PZEM-004t
#endif /* INCFILE_H_ */ Текст функции: Спойлерfloat Power_Measurement (void) { float min_power = 1000000000; //минимальная мощность uint8_t cycle_timer = 0; uint8_t cycle_timer0 = 0; request_pzem = 0; enable_sync; do { if (request_pzem) { if (PZEM_response()) { request_pzem = 0; power_percent[0] = (float)PZEM_Voltage()/10; //измеренное напряжение power_percent[1] = (float)PZEM_Current()/1000; //измеренный ток power_percent[2] = (float)PZEM_Power()/10; //измеренная мощность if (PZEM_Current() != 0) { temperatura = power_percent[0]*power_percent[1]; //вычисленная мощность power_percent[1] = temperatura/100; //ошибка power_percent[0] = temperatura-power_percent[1]; //min (вычисленная мощность - ошибка) power_percent[1] += temperatura; //max (вычисленная мощность + ошибка) if ((power_percent[2]<power_percent[1]) && (power_percent[2]>power_percent[0])) { if (min_power > power_percent[2]) min_power = power_percent[2]; cycle_timer++; } } else cycle_timer0++; } } if (cycle_timer0 == 10) { cycle_timer = 10; min_power = 0; } } while (cycle_timer < 10); disable_sync; return min_power; } Для того, что бы не плодить лишние переменные, я использую массив power_percent[3], кторый использульзуется у меня при работе основной программы. А пременную - volatile float temperatura (температура радиатора определенную в main.c и так, же используемую основной программой), я заменяю на float calc_power, которую определяю в функции Power_Measurement.
BDDW, еще обратите внимание, сколько ОЗУ у вас занято. Иногда бывает, что стек наползает на область данных и начинаются интересные глюки )
Я то же на это грешу. Как видно добавление нового float - дает глюки. А как можно на пальцах прикинуть этот стек? Используется ATmega168. Вот данные компилятора: Спойлер// Optimize (-O1) // Program Memory Usage : 8468 bytes 51,7 % Full // Data Memory Usage : 163 bytes 15,9 % Full // Warning: Memory Usage estimation may not be accurate if there are sections other than .text sections in ELF file // EEPROM Memory Usage : 310 bytes 60,5 % Full
// Optimize more (-O2) // Program Memory Usage : 8766 bytes 53,5 % Full // Data Memory Usage : 163 bytes 15,9 % Full // Warning: Memory Usage estimation may not be accurate if there are sections other than .text sections in ELF file // EEPROM Memory Usage : 310 bytes 60,5 % Full
// Optimize most (-O3) // Program Memory Usage : 10758 bytes 65,7 % Full // Data Memory Usage : 163 bytes 15,9 % Full // Warning: Memory Usage estimation may not be accurate if there are sections other than .text sections in ELF file // EEPROM Memory Usage : 310 bytes 60,5 % Full
// Optimize for size (-Os) - мелкие глюки с выходом из режима функции // Program Memory Usage : 8428 bytes 51,4 % Full // Data Memory Usage : 163 bytes 15,9 % Full // Warning: Memory Usage estimation may not be accurate if there are sections other than .text sections in ELF file // EEPROM Memory Usage : 310 bytes 60,5 % Full
// Optimize debugging experience (-Og) - глючит. Постоянно определяет мощность ТЭН2 // Program Memory Usage : 8592 bytes 52,4 % Full // Data Memory Usage : 163 bytes 15,9 % Full // Warning: Memory Usage estimation may not be accurate if there are sections other than .text sections in ELF file // EEPROM Memory Usage : 310 bytes 60,5 % Full Как видно, не сильно занята память. P.S. Программа довольно таки большая, по этому я ее тут не выкладываю. Если кто то захочет с ней разобраться - то выложу
1. Выкладывать не надо: если вы пытаетесь исправить проблемы бездумной расстановкой volatile, то в вашем коде никто ковыряться не захочет. 2. Ваше упоминание volatile для всех переменных, используемых в прерываниях, вы сопроводили кусочком кода, где таковыми помечены массивы вывода на ЖКИ... Это не то, чтобы плохо, это категорически неприемлемо - из прерываний выводить на ЖКИ! Если, конечно, я прав (код ваш смотрел 3 секунды) 3. Расход стека проще всего определить при помощи протеуса. Если там запустить свою программу на некоторое время, а потом поставить на паузу и открыть окно просмотра RAM, можно элементарно понять, сколько жрет стек. Стек будет в виде мусора в конце памяти, а от начала будут рабочие переменные. Если между ними будет достаточно много нулевых ячеек, стек не переполняется и не налезает на переменные. А вот если этого промежутка не будет, сливай воду...
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
2. Ваше упоминание volatile для всех переменных, используемых в прерываниях, вы сопроводили кусочком кода, где таковыми помечены массивы вывода на ЖКИ... Это не то, чтобы плохо, это категорически неприемлемо - из прерываний выводить на ЖКИ! Если, конечно, я прав (код ваш смотрел 3 секунды)
Нет. Я этого не делаю. Я из прерывания могу изменять содержание того или иного поля массива вывода на ЖКИ. А вывожу в основном цикле.
3. Расход стека проще всего определить при помощи протеуса. Если там запустить свою программу на некоторое время, а потом поставить на паузу и открыть окно просмотра RAM, можно элементарно понять, сколько жрет стек. Стек будет в виде мусора в конце памяти, а от начала будут рабочие переменные. Если между ними будет достаточно много нулевых ячеек, стек не переполняется и не налезает на переменные. А вот если этого промежутка не будет, сливай воду...
Я, к сожалению, не пользуюсь протеусом. А как нибудь по другому можно?
Если вы не отладили, то очевидно что бы не посоветовать то вы этим не пользуетесь... В протеусе удобно отладить программу или сразу всю или хотя бы сложные куски.
"AVRStudio" пробовали?
Если подозрения на стек, возьмите похожий проц помощнее и проверьте на нем.
Если вы не отладили, то очевидно что бы не посоветовать то вы этим не пользуетесь... В протеусе удобно отладить программу или сразу всю или хотя бы сложные куски.
Я знаю про протеус. И как то пытался его ставить. Но у меня ничего не получилось.
Для отладки каких ни будь кусков программы - пользуюсь. А вот как всю программу отладить??? Как имитировать внешние прерывания и другие входящие сигналы? В протеусе - понятно. А тут как?
Если подозрения на стек, возьмите похожий проц помощнее и проверьте на нем.
Да. Можно поставить ATmega328. Но, хотелось бы разобраться с тем, что так разносит стек аж на 75% памяти. У меня переменными занято всего 163 байта из 1024.
Но, хотелось бы разобраться с тем, что так разносит стек аж на 75% памяти.
Откуда узнали?
BDDW писал(а):
Я знаю про протеус. И как то пытался его ставить. Но у меня ничего не получилось.
С процессором тоже не получилось, но пробуете, а протеус бросили.
BDDW писал(а):
Можно поставить ATmega328. Но, хотелось бы разобраться с тем,
Так это только для того, чтобы проверить, а не просто заменить на более мощный.
BDDW писал(а):
А вот как всю программу отладить??? Как имитировать внешние прерывания и другие входящие сигналы?
Все просто или вручную установив нужный бит в нужном регистре или подключить модуль протеуса, а отладку делать в студио(но работает в старой версии студии).
Но, хотелось бы разобраться с тем, что так разносит стек аж на 75% памяти.
Откуда узнали?
Это просто мои предположения. Компилятор пишет: Data Memory Usage : 163 bytes 15,9 % Full. Значит остальное может занимать стек. Про то, что стек может наползать на данные, у меня закрадывалась мысль, а ответ Just_Fluffy полностью утвердил меня в моих предположениях. P.S. Я кажется нашел грабли!!! Стек тут не причем. Но, об этом немного позже. Будет несколько вопросов.
Я знаю про протеус. И как то пытался его ставить. Но у меня ничего не получилось.
С процессором тоже не получилось, но пробуете, а протеус бросили.
Там проблема, по моему, была в дистрибутиве. Просто не нашел рабочую версию. А желания "хочу, аж кушать не могу" - не было. Все получалось и без него, методами отладки в AVR Studio.
Пожалуйста, не пинайте меня сильно, но я не знаю как это сделать. Много искал, читал. В этой теме говорилось неоднократно (по моему). Где то писали, что в Microchip Studio 7.0 и последних AVR Studio она подключается автоматически. Вы говорите про подключение про libm.a? Спойлер Это оно?
Предложение: удалить float. Напряжение: в миллиВольтах, ток: в миллиамперах, мощность: в миллиВаттах, переменные: в uint32_t/unsigned long int/, а при печати просто поставьте точку там, где хотите. Таким образом, используемой памяти будет мало, будет быстро и т.д. Просто в будущем используйте float там, где необходимо.
Но с использованием float я почти уверен, что проблема не в его. Дайте либо весь код программы, либо более крупный фрагмент вашей программы /если что-то из нее "скрываете" для публичного обсуждения/. А так непонятно, в чем причина.
давайте архивом весь проект, полюбопытствую, что там такое вы наделали. только дополнительно описание задачи какое-то, что ли. если есть, конечно.
Большое спасибо за желание помочь, но мне кажется, что я нашел причину. Дело в том, что несколько лет назад я написал такую функцию для вывода float на LCD: Спойлерchar lcd_data[4][20]; //массив для вывода на дисплей LCD 2004 void LcdFloat(float ch, uint8_t _len, uint8_t accur, uint8_t PosCur_X, uint8_t PosCur_Y) { char bufer[_len]; dtostrf(ch, _len, accur, bufer); for(uint8_t x=0; x<_len; x++) lcd_data[PosCur_Y][PosCur_X+x]=bufer[x]; } где: ch - выводимое число _len - кол-во выводимых знаков accur - кол-во знаков после запятой PosCur_X - позиция в строке PosCur_Y - номер строки И все, работало без замечаний. Недавно пересмотрел эту функцию и не понял, нафига я организую массив, а потом его еще и переписываю? И решил переделать напрямую так: dtostrf(temperatura, 5, 1, lcd_data[2]+1); Но, дело в том, что я отвожу 5 символов для вывода температуры - 3 под целые (в том числе и для отрицательных значений), одно под запятую и одно для десятых. А на самом деле dtostrf заполняет 6 символов. Последний символ - 0 (0x0). И тут видно, что при использовании выше приведенной функции под массив bufer я отвожу (в данном случае) 5 байт, а заполняется 6 байт. Можно ли, и как можно избавится от последнего нуля?
Предложение: удалить float. Напряжение: в миллиВольтах, ток: в миллиамперах, мощность: в миллиВаттах, переменные: в uint32_t/unsigned long int/, а при печати просто поставьте точку там, где хотите. Таким образом, используемой памяти будет мало, будет быстро и т.д. Просто в будущем используйте float там, где необходимо.
Я делаю регулятор мощности. Имея 3 ТЭНа по 1 кВт с помощью ШИМ модулятора я могу добиться что бы отдаваемая мощность была, скажем 1200 Вт. И поддерживалась автоматически при изменении напряжения в сети. Информацию о напряжении в сети, потребляемом токе и мощности я беру с модуля PZEM-004t. Все измеряемые им величины я получаю в uint32_t по шине UART. Так, что теоретически это возможно. А вот практически? Ведь мне туда надо будет прикрутить еще ПИД регулятор.
Но с использованием float я почти уверен, что проблема не в его. Дайте либо весь код программы, либо более крупный фрагмент вашей программы /если что-то из нее "скрываете" для публичного обсуждения/. А так непонятно, в чем причина.
Тут мне скрывать нечего. Единственное, пинайте за "быдлокодерство".
Вложения:
Комментарий к файлу: Для ATmega168 13.10.2024.zip [30.22 KiB]
Скачиваний: 44
ну, вот смотрю я на код... скажем, видел я и похуже. у вас хотя бы комментарии по существу, более-менее просто разбираться. но... как я и предполагал, смешались в кучу кони, люди...
буду писать по мере нахождения "нюансиков".
1. Вот у вас есть функция PZEM_request, которая в свою очередь использует в цикле Uart_Transmit, которая, даже судя по названию работает с "медленным" USART (и по факту, это так)... и что, спросите вы? а то, что PZEM_request у вас вызывается из обработчика прерывания INT0!!! вот и скажите мне, чем это отличается от ранее предвиденного мною?
Цитата:
это категорически неприемлемо - из прерываний выводить на ЖКИ!
да, вместо медленного ЖКИ вы выводите в еще более медленный USART...
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Значит остальное может занимать стек. Про то, что стек может наползать на данные, у меня закрадывалась мысль, а ответ Just_Fluffy полностью утвердил меня в моих предположениях.
У вас обоих неправильное предположение.
BDDW писал(а):
Пожалуйста, не пинайте меня сильно, но я не знаю как это сделать.
То был вопрос, а не совет подключить.
Последний раз редактировалось codenamehawk Сб окт 26, 2024 22:10:58, всего редактировалось 1 раз.
три функции, которые делают абсолютно одинаковые действия! разница лишь в том, с какими ячейками массивов они работают! но ведь тут очевидно напрашивается ОДНА функция, которая получает в параметре номер первой рабочей ячейки, а уж остальное делает относительно этого адреса... поправьте меня, если это не так:
и что-то сильно-сильно мне подсказывает, что в этой функции вы решили провести низкоуровневую оптимизацию вычислений... как-то странно выглядят эти сдвиги и маски. почему-то мне кажется, что все можно было сделать гораздо проще
Добавлено after 52 seconds: 3. там дальше по коду еще много похожих "одинаковых" функций.
Добавлено after 11 minutes 49 seconds: в общем, моё мнение однозначно: нужен глубокий рефакторинг кода. чтобы и собственное понимание задачи и пути её решения выкристаллизовалось лучше.
у вас в коде намешаны низкоуровневые операции типа обращения к портам с высокоуровневыми действиями типа расчетов полученных извне значений. то же самое касается и "деления" кода на функции: складывается впечатление, что это делалось не путем разработки алгоритма, а путем вычленения кусков "гладкого кода" в отдельные кусочки, чтобы просто хоть как-то можно было уследить за ходом процессов... но так писать не стоит.
не знаю, что именно делает ваша программа, но, имхо, функция main должна выглядеть примерно так (кода нет, одни комменты к несуществующим функциям
Код:
intmain(void){ // инициализация всей периферии // обращение к внешней аппаратуре и инициализация её // подготовка данных (считывание из EEPROM? поиск там каких-то адресов и т.п.) // вывод на ЖКИ приветствия (или меню, заставки и т.п.) // запуск прерываний (это не обязательно, может, только некоторых из них) while(1){ // измерения // обработка измерений // вывод результатов // опрос событий EVENT (пришли данные по UART? нажаты кнопки? повернут энкодер? и т.п.) switch(EVENT){ case EV_USART: // обработка данных из USART break; case EV_KBD: // обработка нажатий кнопок break; // и так далее, все события } } }
и функции у вас должны работать с одними и теми же сущностями: если функция вычисляет, то она в порты не лезет, а если лезет в порты, то не вычисляет. как-то так.
Добавлено after 8 minutes 13 seconds: и последнее.
не бойтесь сервиса, который предоставляет вам Си: ввод-вывод стандартными средствами сделан там вполне прилично, и, уверяю вас, сильно упростит вам жизнь.
последним способом я пользуюсь уже много лет, и ни разу не пожалел. согласитесь, гораздо проще написать
Цитата:
printf("U=%d\nI=%d\n", U, I);
и тем самым отправить в USART две строки с значениями напряжения и тока, чем городить то же самое с помощью кучи массивов, циклов и т.п. неочевидных преобразований... 2 килобайта FLASH ради такого - не великая цена. ну и еще 2К, если никак без float в этом случае не обойтись... нервы дороже.
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 21
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения