Форум РадиоКот https://radiokot.ru/forum/ |
|
Использование особенностей GCC для повышения комфорта https://radiokot.ru/forum/viewtopic.php?f=62&t=144535 |
Страница 1 из 2 |
Автор: | ARV [ Сб апр 22, 2017 08:45:00 ] |
Заголовок сообщения: | Использование особенностей GCC для повышения комфорта |
Коллеги! Можете закидать меня объедками, но не могу молчать! Хочу поделиться опытом, который, с моей точки зрения, может быть полезен всем, но по странным необъяснимым причинам как-то не известен широко. Речь пойдет о GCC - бесплатном компиляторе для разных платформ. Для "больших" систем, наверное, мои советы будут лишними, а вот для AVR, мне кажется, очень хороши. Так же менее интересен этот опыт тем, кто любит и активно использует С++, я же, как истинный ретроград, применяю Си, и хочу поделиться тем, как можно (и нужно!) сделать работы с проектами на этом языке более комфортными. 1. Атрибуты В GCC есть возможность задавать атрибуты всем элементам программы - функциям, переменным и т.п. Например, всем известен аттрибут, спрятанный в макросе PROGMEM, который заставляет компилятор помещать константы в память программ. Но есть и другие атрибуты, которые могут заметно помочь в некоторых случаях! задается атрибут при помощи специального макроса __attribute__((А)), где А - это сам атрибут, а остальное - неотъемлемая его часть (обратите внимание на двойные скобки!). Об атрибутах вообще вы можете прочесть в документации на GCC, а далее я остановлюсь на некоторых конкретных случаях их применения. 2. Секции памяти. В GCC есть несколько типов секций памяти, задаваемых при помощи атрибута section("название_секции"). Особенно полезными, с моей точки зрения, следующие (указываю конкретное название_секции): .noinit - все переменные, помещенные в эту секцию, не будут инициализированы "по умолчанию". Как известно, все статические (и глобальные) переменные в Си автоматически инициализируются нулями, есл не указано конкретное значение. Но порой бывает необходимо, чтобы после сброса (например, по WDT) переменная сохранила свое прежнее, досбросовое значение, и эта секция позволяет получить требуемое. Если поместить в этой секции массив, то посчитав CRC этого массива после сброса, можно получить весьма неплохое СЛУЧАЙНОЕ число, по-настоящему случайное, а не псевдослучайное. Чем больше массив, тем лучше случайность. Надеюсь, вы сами понимаете, почему. .initX, где Х - номер секции от 0 до 9. Это секция для кода. Чтобы было понятно: сразу после сброса выполняется код, помещенный в секцию .init0, потом постепенно номер секции увеличивается, и функция main выполняется в секции 10. То есть пометив атрибутом с указанием определенной секции свою функцию, вы заставите ее выполниться ДО НАЧАЛА main. Только будьте осторожны: параметры стека задаются в секции 2, а до этого стек еще не определен. Я рекомендую использовать секции поближе к main, т.е. 6...9. Зачем это надо? О! Собственно, ради этого я и затеял эту писанину! Чуть позже расскажу. .finiX - это "зеркально" обратные секции к только что описанным: в порядке уменьшения своего номера они выполняются ПОСЛЕ ВЫХОДА ИЗ main, то есть сначала код из секции .fini9, потом меньше и меньше, и в 0-й реализуется запрет прерываний и m1: rjmp m1. Почему-то принято считать, что в МК жизни за пределами main нет, и тем более ее нет ПОСЛЕ main, но это, как видите, не так! И, хотя применений этим секциям можно найти меньше, чем инициализирующим, все-таки можно извлечь пользу и из них. 3. Потоковый ввод-вывод. Об этом я уже писал статью, если интересуетесь - почитайте: WinAVR: консольный ввод-вывод Если кратко, то смысл в том, чтобы заставить работать функции printf и scanf так, как это и было задумано в Си - для вывода и ввода. Как-то прижился подход, когда вместо этих функций используют sprintf и sscanf, т.е. работают со строками, а вот получение этих строк приходится "длать ручками". Если же реализовать потоковый ввод-вывод, а это совсем не сложно! - то можно избавиться и от этого. Поверьте, когда вы работаете с консольным выводом (например, через USART), это просто кардинально упрощает жизнь! Кстати, совсем не лишней будет эта возможность при работе с файлами на SD-карте, что тоже многие применяют в своих проектах. Ну, а теперь о том, какая польза может быть извлечена из вышеописанных секций. Буду вести рассказ про себя, а мой опыт каждый может попробовать примерить на себя, чтобы решить, подходит ли, полезен ли он, или нет. Я часто делаю проекты из кучи модулей. И многие из них работают с периферией. И периферию эту надо настраивать. Как принято делать? в каждом таком модуле создают функцию init_xxx(), которую затем вызывают из main. Как делаю я? эту самую функцию init_xxx() я помещаю в секцию .init7 и она вызывается сама! А в main о периферии я вообще не забочусь - все уже подготовлено само! Какие плюсы я получаю? Во-первых, код main становится лаконичным и красивым, в нем именно то, что и должно быть: ГЛАВНАЯ работа. Надо лампочками мигать - они мигают, надо файлы читать - они читаются... а вякая ерунда вроде портов и регистров скрыта в модулях. Как я это делаю? Кто скачивал мои исходники, наверняка видел там файл avr_helper.h - в нем я определил ряд макросов, при помощи которых все это и делается. Вот самые интересные из них: Код: // вспомогательный макрос конкатенации макросов #define _CONCAT_(x,y) x ## y /// макрос конкатенации основной #define CONCAT(y,x) _CONCAT_(y,x) #define DDR(x) CONCAT(DDR,x) #define PORT(x) CONCAT(PORT,x) #define PIN(x) CONCAT(PIN,x) /// макрос для определения функции-инициализатора (вызывается автоматически в секции .inix) #define INIT(x) static void __attribute__((naked, used, section(".init" #x))) CONCAT(_init_, __COUNTER__) (void) /// то же самое для завершающих функций #define DONE(x) static void __attribute__((naked, used, section(".fini" #x))) CONCAT(_fini_, __COUNTER__) (void) /// это если лень писать - самая ранняя инициализация #define AUTOINIT() INIT(2) /// макрос для определения компактной версии функции main() #define MAIN() int __attribute__((OS_main)) main(void) /// неинициализируемые переменные #define NOINIT __attribute__((section(".noinit"))) Как видите, я использую макросы для секций памяти, ибо писать длинные строки муторно. Но - обратите внимание! - как я делаю функции инициализации-завершения: я использую встроенные макросы GCC для генерации уникального имени функции, чтобы при необходимости в листинге их найти можно было. __COUNTER__ это макрос, который автоматически увеличивается на 1 всякий раз, когда используется. Т.е. я могу написать в коде модуля несколько INIT(7) в итоге сгенерируется несколько разноименных функций, все из которых попадут в одну секцию. Теперь в модуле, который работает с портами ввода вывода, я просто пишу нечто подобное: Код: INIT(7){ и на этом мои переживания по поводу инициализации периферии заканчиваются. Да, конечно, в заголовочном файле модуля я определяю следующие макросыDDR(PORT_IO) |= _BV(PIN_IO); } Код: #define PORT_IO D #define PIN_IO 2 Обратите внимание: для задания порта ввода-вывода я использую не привычное PORTD, а только букву D. Знаете зачем? Чтобы так же просто и быстро перенастраивать ВСЕ СВЯЗАННЫЕ с этой буквой регистры при помощи макросов DDR(PORT_IO), PORT(PORT_IO) и PIN(PORT_IO). По-моему, это очень удобно. Кстати, в INIT-секциях удобно считывать их EEPROM значения конфигурационных параметров устройства. А вот в секциях DONE иной раз удобно их сохранять. Правда, для этого надо обеспечить 2 условия: 1. обязательное завершение main, т.е. выход из основного цикла 2. тот самый выход должен быть связан с нажатием кнопки "выключения". Т.е. как минимум в проекте должна быть такая кнопка. Эти условия ограничивают применимость завершающих секций, но если вы делаете проект, управление питанием в котором реализуется через кнопку, заведенную на МК, это так же поможет вам сделать красивый код сохранения настроек, сведя при этом количество записей EEPROM к минимуму. Знатоки С++ наверняка узнали в секциях .init-.fini аналоги конструкторов и деструкторов... ![]() Ну еще пара слов о моих подходах. Главное: при инициализации портов и регистров нужно всегда использовать битовые операции ИЛИ и И для установки битов в регистрах периферии, применение прямой записи крайне нежелательно по одной причине: вы не знаете заранее, какой инициализирующий код из нескольких будет выполнен первым, а какой следующим. Но вы знаете, что после сброса все регистры обнулены (точнее - имеют дефолтное значение). Поэтому в инициализации каждого модуля вы обязаны изменять только те биты в регистрах, которые требуются этому модулю, сохраняя остальные неизменными. Это, конечно, слегка раздувает код "лишними" операциями над всякими DDRx и т.п., но это небольшое зло, как мне кажется, с лихвой компенсируется удобством программирования. И еще одно. Задавая разные номера для макроса INIT, вы можете управлять порядком вызова инициализации. Разумеется, что код в INIT(7) будет выполнен раньше, чем INIT(8). Просто помнить номера инициализаций в нескольких модулях сложновато. Я завел себе правило: если мне надо просто проинициализировать модуль, я использую секцию 7. Если надо гарантировать превентивность инициализации - 6. Если надо гарантировать "запоздалость" - 9. Иных вариантов стараюсь избегать. Вот и все. Буду рад, если кому-то открыл глаза или помог стать лучшим программистом, чем ранее. ![]() Если хотите, у могу еще рассказать о том, как можно делать трассировку кода удобно, но накладно по ресурсам... Это полезно, если МК большой, а аппаратного отладчика нет... |
Автор: | Rtmip [ Сб апр 22, 2017 09:32:30 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
Мне интересно, продолжайте, пожалуйста. |
Автор: | Reflector [ Сб апр 22, 2017 11:15:53 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
ARV писал(а): Я часто делаю проекты из кучи модулей. И многие из них работают с периферией. И периферию эту надо настраивать. Как принято делать? в каждом таком модуле создают функцию init_xxx(), которую затем вызывают из main. Как делаю я? эту самую функцию init_xxx() я помещаю в секцию .init7 и она вызывается сама! А в main о периферии я вообще не забочусь - все уже подготовлено само! Какие плюсы я получаю? Во-первых, код main становится лаконичным и красивым, в нем именно то, что и должно быть: ГЛАВНАЯ работа. Надо лампочками мигать - они мигают, надо файлы читать - они читаются... а вякая ерунда вроде портов и регистров скрыта в модулях. И еще одно. Задавая разные номера для макроса INIT, вы можете управлять порядком вызова инициализации. Разумеется, что код в INIT(7) будет выполнен раньше, чем INIT(8). Просто помнить номера инициализаций в нескольких модулях сложновато. Я завел себе правило: если мне надо просто проинициализировать модуль, я использую секцию 7. Если надо гарантировать превентивность инициализации - 6. Если надо гарантировать "запоздалость" - 9. Иных вариантов стараюсь избегать. Я правильно понимаю, что вместо того чтобы сделать как-то так: Код: void init() { init_xxx(); init_yyy(); init_zzz(9600); } void main() { init(); } Ты делаешь так: Код: // module xxx
INIT(6) { init_xxx(); } // module yyy INIT(7) { init_yyy(); } // module zzz INIT(9) { init_zzz(9600); } void main() { } |
Автор: | ARV [ Сб апр 22, 2017 12:35:41 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
да, именно так и выходит. более того, в модулях вместо Код: void init_xxx(void){ я сразу делаю// some code } INIT(7){ init_xxx(); } Код: INIT(7){ // some code } Добавлено after 40 minutes 53 seconds: Rtmip писал(а): Мне интересно, продолжайте, пожалуйста Ну, раз интересно, вот еще несколько идей, как и обещал, про трассировку.В компиляторе GCC (в той или иной мере и в любом ином) есть средства, назначение которых кажется странным. Например, есть макрос, который имеет значение в виде текстовой строки с именем текущего файла... или с именем текущей функции... И эти возможности можно использовать для организации трассировки программы. Ведь достаточно часто при отладке "через консоль" приходится выводить сведения о состоянии тех или иных переменных, так почему не автоматизировать и упростить этот процесс? Вот что я сделал. Сначала я организовал с помощью вышеописанных возможностей модуль с названием debug.c, при подключении к проекту которого сразу инициализируется файловый вывод в USART на подходящей скорости (обычно я использую 57600 бод). В этом заголовочнике debug.h этого модуля все функции определены при помощи директив условной компиляции Код: #if defined(__DEBUG__) То есть если проект собирается в релизной версии (макрос __DEBUG__ не определен), то все функции заменяются заглушками-пустышками, что гарантирует их исключение из прошивки, т.к. оптимизатор выбросит неиспользуемые функции из кода (если вы еще не знаете, как это сделать - скажу и об этом, хотя это должен знать каждый GCC-шник). Определить директиву __DEBUG__ можно при помощи параметра командной строки -D компилятора, т.е. так: -D__DEBUG__, при этом при компиляции любого файла этот макрос будет "виден".void debuf_func(void); #else #define debug_func() #endif Затем я определил ряд макросов для трассировки одной или нескольких переменных. Привожу пример для одной переменной типа int, по аналогии можно сделать, сколько надо Код: #define log_i(x) printf_P(PSTR("\nLOG> " __FILE__ "\%s:%04d " # x "=%d\n"), __FUNCTION__, __LINE__, x) Теперь если этот макрос вставить в своем файле my_file.c в любом месте, например так (показываю номера строк):Код: 013: void func(void){ то после компиляции и прошивки в микроконтроллер при входе в эту функцию в USART будет выведена следующая строка:014: int test_var = 12; 015: log_i(test_var); 016: // какой-то код 017:} LOG> ..\my_file.c\func:0015 test_var=12 Как видите, этот текст содержит необходимую информацию, чтобы по нему определить все, что необходимо при отладке: - имя файла - имя функции - номер строки - имя выводимой переменной - значение этой переменной Какие возможности при этом использованы? 1. Предопределенный макрос с именем файла __FILE__. Этот макрос содержит "полный путь" к файлу исходного текста модуля, как его видит компилятор, т.е. если там иерархия подпапок, они тоже будут в этом макросе 2. Конкатенация строковых констант. Запись "\nLOG> " __FILE__ слепит из двух строк одну, в моем примере получится такая "\nLOG> ..\my_file.c" 3. Предопределенный макрос __FUNCTION__, содержащий указатель на строку символов с именем функции. Обратите внимание, что это не константа типа строка, а переменная, потому для ее вывода надо указать формат %s 4. Предопределенный макрос __LINE__, равный номеру строки исходного текста, т.к. это число, для его вывода нужен формат %04d 5. Действие директивы # препроцессора, которая превращает один идентификатор правее себя в его строковый эквивалент. То есть передавая в макрос идентификатор переменной мы получим строковую константу с этим идентификатором. Вот так, просто и незатейливо, можно организовать трассировку всего, чего угодно. Разумеется, все эти фичи можно использовать и для других целей. Но за это придется заплатить: - повышенным расходом flash - prinft отъест у вас примерно 1,5 килобайта, плюс все "текстовые" сообщения с именами файлов, функций, номерами строк и т.п., которые благодаря PSTR тоже попадут во flash - повышенным расходом ОЗУ Однако, когда я разбирался с библиотекой Chan-а для SD-карты без аппаратного отладчика, мне пришлось идти на эти жертвы... Потому как протеус с SD-картами далеко не всегда помогает. |
Автор: | Reflector [ Сб апр 22, 2017 12:45:24 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
ARV писал(а): да, именно так и выходит. Так а преимущества то в чем? Хотел разгрузить main, я тебе привел пример где в main одна строка. Но дополнительно я в одном месте вижу какие функции инициализации вызываются, в каком порядке, причем этот порядок легко менять, и могу передавать в них параметры. |
Автор: | ARV [ Сб апр 22, 2017 12:53:22 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
Все преимущества я описал. Например, вы сделали модуль для работы с выводом "в консоль", т.е. USART. Какие параметры вы собрались передавать в функцию инициализации? Скорость? И собираетесь ее менять по ходу исполнения программы? Не лучше ли задать константу скорости в заголовочнике того модуля, и забыть вообще про него? Зачем вам нужен порядок инициализации, если к моменту начала main уже все инициализирвоано - хоть USART, хоть ЖКИ, хоть черт в ступе? У меня вот есть давно сделанный модуль с названием usart_io.c с заголовочником usart_io.h. В заголовочнике прописана скорость 57600, и за последние 4 года я ни разу ее не менял. если мне надо выводить в консоль, я просто кидаю эти файлы в папку проекта, и где захочется, пишу printf - все, усилия кончились на этом. Разве что попадется МК, в котором более 1 USART - тогда приходится имена регистров подправлять, да и то из-за моей же лени - ведь можно при помощи условной компиляции предусмотреть и такой вариант... аналогично я поступил и с ЖКИ. |
Автор: | Reflector [ Сб апр 22, 2017 15:40:57 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
ARV писал(а): Все преимущества я описал. Например, вы сделали модуль для работы с выводом "в консоль", т.е. USART. Какие параметры вы собрались передавать в функцию инициализации? Скорость? И собираетесь ее менять по ходу исполнения программы? Не лучше ли задать константу скорости в заголовочнике того модуля, и забыть вообще про него? Зачем вам нужен порядок инициализации, если к моменту начала main уже все инициализирвоано - хоть USART, хоть ЖКИ, хоть черт в ступе? У меня вот есть давно сделанный модуль с названием usart_io.c с заголовочником usart_io.h. В заголовочнике прописана скорость 57600, и за последние 4 года я ни разу ее не менял. если мне надо выводить в консоль, я просто кидаю эти файлы в папку проекта, и где захочется, пишу printf - все, усилия кончились на этом. Разве что попадется МК, в котором более 1 USART - тогда приходится имена регистров подправлять, да и то из-за моей же лени - ведь можно при помощи условной компиляции предусмотреть и такой вариант... Во-первых, порядок инициализации может иметь значение, наверно именно потому у тебя и можно менять номера секций чтобы гарантировать превентивность или "запоздалость"... Правда там даже чтобы понять в каком порядке происходит инициализация нужно взять ручку и пройтись по всем модулям ![]() Добавлено after 6 minutes 57 seconds: ARV писал(а): Вот так, просто и незатейливо, можно организовать трассировку всего, чего угодно. Разумеется, все эти фичи можно использовать и для других целей. Это как бы повсеместно распространенный подход, обычно такой код пихают в assert. На AVR наверно так реже делают, место экономят ![]() |
Автор: | ARV [ Сб апр 22, 2017 19:05:06 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
Reflector писал(а): Во-первых, порядок инициализации может иметь значение, наверно именно потому у тебя и можно менять номера секций чтобы гарантировать превентивность или "запоздалость"... во-первых, номера секций менять можно не у меня, а у GCC ![]() во-вторых, инициализация и управление по ходу пьесы - это разные вещи. инициализация задает состояние по умолчанию, для которого не требуются параметры. если хочется, по умолчанию можно настроить сразу так, чтобы работало, как хочется. а потом, если хочется, можно перенастраивать. в-третьих, если вы пишите на С++, это еще не доказательство, что вы выбрали самый лучший подход. кстати, у вас в С++ конструкторы статических экземпляров классов GCC сам помещает в секцию .init6 по-моему (могу ошибаться, т.к. не помню наизусть) - это вас не смущает? у меня то же самое, но для Си (если считать модуль эквивалентом экземпляра класса) голову отключать я не советовал, она всегда пригодится. Добавлено after 3 minutes 29 seconds: Reflector писал(а): В идеале любой модуль должен переноситься между проектами без изменений, в чистом С этого добиться сложнее, но по крайней мере можно к этому стремиться переносимость модуля не от Си зависит, а от связи модуля с аппаратной частью. для AVR редко когда можно добиться полной абстракции модуля от аппаратуры, потому и переносимость модулей между проектами не высокая.
|
Автор: | Reflector [ Сб апр 22, 2017 21:56:20 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
ARV писал(а): во-вторых, инициализация и управление по ходу пьесы - это разные вещи. инициализация задает состояние по умолчанию, для которого не требуются параметры. если хочется, по умолчанию можно настроить сразу так, чтобы работало, как хочется. а потом, если хочется, можно перенастраивать. Любая нормальная инициализация может быть с параметрами или без, естественно это касается тех случаев, когда ее по крайней мере можно вызвать ![]() Цитата: в-третьих, если вы пишите на С++, это еще не доказательство, что вы выбрали самый лучший подход. кстати, у вас в С++ конструкторы статических экземпляров классов GCC сам помещает в секцию .init6 по-моему (могу ошибаться, т.к. не помню наизусть) - это вас не смущает? у меня то же самое, но для Си (если считать модуль эквивалентом экземпляра класса) Я пишу для STM32, на C++, классы в большинстве своем полностью статические и шаблонные, у них всегда есть пустой конструктор, метод init и конструктор вызывающий этот init. Допустим было бы как ты хочешь, создаем глобальный экземпляр класса USART, он помещает вызов конструктора в секцию .init6, тот вызывает setBaudRate в который передается 57600. А знаешь что внутри setBaudRate? Там считывается текущая скорость одной из APB шин, на которых и висят USARTы, чтобы правильно проинитить регистр BRR. И какая на тот момент скорость APB? Ты пишешь для AVR, так что я распишу подробнее... Чтобы получить правильную частоту к этому моменту нужно определится с тем используется ли встроенный генератор или кварц, нужно ли PLL, если нужно, то дополнительно задается 3 делителя и наконец есть еще делители для каждой шины, в том числе для нужной нам APB, их тоже нужно задать. В итоге придется в модуль настройки тактирования добавить еще десяток дефайнов и настраивать все через них, причем тут важен порядок, это все должно вызываться до других модулей зависимых от скорости генератора... Естественно я так не делаю и вызываю инициализацию усарта когда уже установлена рабочая частота генератора, перед которой была задана нужная латентность флеша зависимая от этой частоты, опять же не через дефайны в модуле настройки латентности флеша или где они там должны в этом случае находиться... Reflector писал(а): переносимость модуля не от Си зависит, а от связи модуля с аппаратной частью. для AVR редко когда можно добиться полной абстракции модуля от аппаратуры, потому и переносимость модулей между проектами не высокая. Смотри, берем какой-нибудь бюджетный STM32 за 2$, у которого 5 USARTов и дофига флеша. На C++ я могу проинициализировать все пять USART пятью строками, причем в качестве параметра даже пара пинов передается, т.к. они могут быть разными, или размер буферов, если используется буферизация. Правда в последнем случае придется вызвать еще одну функцию на каждый USART в обработчике соответствующего прерывания. Как это можно нормально разрулить на С, да еще и с применением твоей методики я даже не представляю ![]() |
Автор: | ARV [ Вс апр 23, 2017 10:57:12 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
Reflector писал(а): Как это можно нормально разрулить на С, да еще и с применением твоей методики я даже не представляю вам крупно не повезло, вам придется не применять мою методику. чего вы так всполошились - ума не приложу. ![]() кстати, вроде как для вас я писал: ARV писал(а): менее интересен этот опыт тем, кто любит и активно использует С++
|
Автор: | Reflector [ Вс апр 23, 2017 11:14:17 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
ARV писал(а): кстати, вроде как для вас я писал: ARV писал(а): менее интересен этот опыт тем, кто любит и активно использует С++ Нет, это писалось в первом посте для всех, а мне лично ты говорил, что конструкторы в С++ ведут себя подобно твоим самовызывающимся функциям. Но у меня нет ни одного что-то делающего конструктора вызываемого до main, потому что применительно к мк такой подход плох даже для С++, где это реализовать проще. |
Автор: | ARV [ Вс апр 23, 2017 12:31:51 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
Reflector писал(а): применительно к мк такой подход плох просто вам не повезло. я от такого подхода кайфую ![]() |
Автор: | YS [ Сб июн 03, 2017 10:24:41 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
Интересно, интересно. ![]() ![]() Но, честно говоря, подобный подход к инициализации периферии кажется мне несколько перегруженным. Лично я при написании программ стараюсь по возможности исповедовать "принцип наименьшего удивления", чтобы облегчить жизнь тем, кто будет читать мой код. ![]() |
Автор: | ARV [ Пн июн 05, 2017 12:54:05 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
YS писал(а): Когда-то я ради прикола передавал аргументы в main(), используя похожие методы. да, это было именно ради прикола, практической ценности не имеет.YS писал(а): Лично я при написании программ стараюсь по возможности исповедовать "принцип наименьшего удивления" так и я тоже стремлюсь к этому... и при вышеописанном методе руководствовался принципом "если модуль не работает - ищи проблему в модуле". то есть если что-то не заработало - не надо ковыряться по всем файлам, выискивая, где оно инициализировалось, где стартовало, где использовалось - открывай файл модуля и думай... в сущности, мой подход почти всегда именно так и работает. но в некоторых случаях может и усложнить жизнь - признаю. хотя чаще (с моей субъективной точки зрения) именно упрощает. |
Автор: | arkhnchul [ Пн июн 19, 2017 18:59:03 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
у такого повышения комфорта есть некоторая проблема - "особенности компилятора" зачастую существуют только в нем. Тот же IAR, к примеру, не обязан быть в курсе (и он таки не), что там за __attribute__ такие. Можно, конечно, сказать "не, я всегда использовал и буду использовать только один компилятор", но сие в общем не есть хороший подход. |
Автор: | ARV [ Вт июн 20, 2017 07:34:38 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
arkhnchul писал(а): но сие в общем не есть хороший подход знаете, мне кажется, бытующее мнение о том, что легко делать кроссплатформенные (хотя правильнее - кросскомпилятоные) программы на Си - это завуалированный обман.кроссплатформенные программы представляют собой дичайшее нагромождение #ifdef и других препроцессорных штучек, и в итоге все равно без бубна не собираются на разных платформах и компиляторах. как бы разработчик не старался все сделать "просто", при смене платформы или компилятора нужно долго и внимательно читать инструкцию по сборке проекта, править конфиги и т.п. - никакой простоты и автоматичности то есть фактически нет возможности сделать исходник под любой компилятор и платформу. так к чему самообманываься? ну не собирается IAR-ом - да и попутного ему ветра! GCC бесплатно скачивается и за 5 минут устанавливается, после чего все можно собрать и удалить GCC снова, если уж так приспичит. |
Автор: | arkhnchul [ Вт июн 20, 2017 10:40:07 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
мне кажется, бытующее мнение о том, что легко делать кроссплатформенные (хотя правильнее - кросскомпилятоные) программы на Си - это завуалированный обман мнение о том, что их делать легко - неверно. Но возможно. кроссплатформенные программы представляют собой дичайшее нагромождение #ifdef и других препроцессорных штучек, и в итоге все равно без бубна не собираются на разных платформах и компиляторах. как бы разработчик не старался все сделать "просто", при смене платформы или компилятора нужно долго и внимательно читать инструкцию по сборке проекта, править конфиги и т.п. - никакой простоты и автоматичности что-то мне подсказывает, что вы их мало видели ![]() |
Автор: | YS [ Вт июн 20, 2017 10:43:08 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
Ну, я бы не стал так драматизировать. ![]() Потому, честно сказать, мой подход - использовать нестандартные возможности по минимуму и только там, где без них правда никак. Например, при смене компилятора неизбежно придется менять объявления прерываний; но это именно тот случай, когда вариантов особо нет. Что же касается продвинутого использования препроцессора, я как раз не вижу в этом ничего особенно плохого для "боевого" кода. В этом случае и получается истинная кросс-платформенность: перенес исходники, написал в единственном заголовочнике что-то в духе "#define IAR_MODE" (а можно и не писать, многие компиляторы сами определяют уникальные для себя символы) - и все, модуль компилируется. При этом в код можно совсем не заглядывать. Иное дело, когда модификацию кода препроцессором слишком активно используют в проектах, предназначенных для обучения. Самый жуткий пример - демонстрационные проекты для плат Discovery от ST. Я, конечно, понимаю, что так программистам удобнее - не надо делать много версий исходников, но когда стоит цель разобраться с оборудованием это, конечно, сильно мешает. Ну и, конечно, лучше всего без нужды не использовать не только расширения, но и те элементы, для которых в стандарте не прописано четкого поведения, или с которыми по факту обращаются вольно, или которые способны создать машинно-зависимые проблемы (в частности, проблемы с выравниванием). Например, битовые поля или union'ы. |
Автор: | ARV [ Вт июн 20, 2017 11:09:46 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
При всем уважении, YS, останусь при своем мнении. Можно пытаться решить возникающие проблемы, а можно не создавать их. И второй вариант мне ближе. Берется бесплатный компилятор и принимается решение, что никаких вариантов более быть не может. С этого момента у меня нет никаких проблем, даже с совестью по поводу ломаного IARа. Ну а все желающие могут продолжать бороться с придуманными проблемами - кто ж запретит? Мы же не на производстве, где кто-то установил правила и требует их соблюдать! Мы - любители, мы правила сами для себя придумываем. Я для себя придумал и поделился. ![]() |
Автор: | YS [ Вт июн 20, 2017 12:07:42 ] |
Заголовок сообщения: | Re: Использование особенностей GCC для повышения комфорта |
Господи спаси, я не настаиваю на том, чтобы кого-то переубедить. Просто для меня это имеет чисто практическое значение. У нас в лаборатории, например, живет зоопарк компиляторов по причине разнообразия используемых компонентов. Так что хочется или нет, но о кросс-платформенности думать приходится, чтобы при необходимости использовать код коллег и делиться с ними своим кодом. Еще я люблю отлаживать аппаратно-независимые модули на x86, а потом переносить в железо. Вот, например, в текущем проекте мне пришлось писать парсер текстовых команд. Разумеется, отлаживать такое проще на ПК, с обширным тестовым выводом. Кстати, такая практика приучила меня всегда указывать тип констант явно, потому что невинно выглядящий сдвиг типа 1<<8 на x86, x86-64, ARM, MSP430, AVR и STM8 может вести себя по разному. А вот 1UL<<8 работает гораздо более предсказуемо. ![]() Ну и GCC хорош, но не везде. Например, для него так и не сделали кодогенератор для STM8, что меня очень огорчает. Приходится использовать Cosmic, потому что желание использовать SDCC у меня отпало когда я дочитал документацию по нему примерно до половины. Ну а IAR стоит безумных денег, и лицензионная политика у него просто параноидальная. |
Страница 1 из 2 | Часовой пояс: UTC + 3 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |