А это общая тенденция к умиранию форумов - Казус, Изиэлектроникс, Электроникс. Это вообще нынче такая тенденция, что молодняк не желает учиться и делать что-то материальное, они желают развозить еду и выдавать товары на пунктах выдачи Озона. Раньше в юности я еще увлекался 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]
Скачиваний: 137
ну, вот смотрю я на код... скажем, видел я и похуже. у вас хотя бы комментарии по существу, более-менее просто разбираться. но... как я и предполагал, смешались в кучу кони, люди...
буду писать по мере нахождения "нюансиков".
1. Вот у вас есть функция PZEM_request, которая в свою очередь использует в цикле Uart_Transmit, которая, даже судя по названию работает с "медленным" USART (и по факту, это так)... и что, спросите вы? а то, что PZEM_request у вас вызывается из обработчика прерывания INT0!!! вот и скажите мне, чем это отличается от ранее предвиденного мною?
Цитата:
это категорически неприемлемо - из прерываний выводить на ЖКИ!
да, вместо медленного ЖКИ вы выводите в еще более медленный USART...
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Значит остальное может занимать стек. Про то, что стек может наползать на данные, у меня закрадывалась мысль, а ответ Just_Fluffy полностью утвердил меня в моих предположениях.
У вас обоих неправильное предположение.
BDDW писал(а):
Пожалуйста, не пинайте меня сильно, но я не знаю как это сделать.
То был вопрос, а не совет подключить.
Последний раз редактировалось codenamehawk Сб окт 26, 2024 22:10:58, всего редактировалось 1 раз.
три функции, которые делают абсолютно одинаковые действия! разница лишь в том, с какими ячейками массивов они работают! но ведь тут очевидно напрашивается ОДНА функция, которая получает в параметре номер первой рабочей ячейки, а уж остальное делает относительно этого адреса... поправьте меня, если это не так:
Код:
uint32_t calculate(uint8_t index){ uint32_t data = (uart_data[index+2]<<8 | uart_data[index + 3]) & 0xFFFF; data = ((data << 8) | uart_data[index]) & 0xFFFFFF; return data << 8 | uart_data[index+1]; }
и что-то сильно-сильно мне подсказывает, что в этой функции вы решили провести низкоуровневую оптимизацию вычислений... как-то странно выглядят эти сдвиги и маски. почему-то мне кажется, что все можно было сделать гораздо проще
Добавлено after 52 seconds: 3. там дальше по коду еще много похожих "одинаковых" функций.
Добавлено after 11 minutes 49 seconds: в общем, моё мнение однозначно: нужен глубокий рефакторинг кода. чтобы и собственное понимание задачи и пути её решения выкристаллизовалось лучше.
у вас в коде намешаны низкоуровневые операции типа обращения к портам с высокоуровневыми действиями типа расчетов полученных извне значений. то же самое касается и "деления" кода на функции: складывается впечатление, что это делалось не путем разработки алгоритма, а путем вычленения кусков "гладкого кода" в отдельные кусочки, чтобы просто хоть как-то можно было уследить за ходом процессов... но так писать не стоит.
не знаю, что именно делает ваша программа, но, имхо, функция main должна выглядеть примерно так (кода нет, одни комменты к несуществующим функциям
Код:
int main(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 в этом случае не обойтись... нервы дороже.
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения