Доброго всем дня. Пишу код под микроконтроллеры STM32 в среде uVision Keil и захотелось плюшек, доступных в стандарте C++11. Немного погуглив, наткнулся на следующую страницу, в которой для поддержки C++11 рекомендуется использовать компилятор V6.8 или новее (armclang). http://www.keil.com/support/docs/3696.htm Вернее в версии 5.05 (armcc) C++11 поддерживается, но динамический анализ синтаксиса будет работать не корректно, что не очень удобно.
До сего момента всегда использовал включенный по умолчанию 5.06 (armcc) и даже не задумывался о том, что компиляторов в Keil'е несколько. Отсюда вытекает мой первый комплект вопросов: Какие вообще существуют компиляторы для ARM? Я так понимаю, ядро одно, значит и компиляторы одни и теже в пределах одного семейства. Какие у них преимущества/недостатки? Почему Keil по умолчанию использует компилятор, который они сами называют устаревшим (по крайней мере для серии STM32F10x)?
Второй вопрос относится к самому Keil'у. У меня стоит версия 5.25.2.0 Попробовал новый компилятор, всё отлично работало до тех пор, пока вся программа находилась в одном файле "main.cpp". Создал заголовочный файл "MyLib.h", перетащил туда часть кода, однако динамический анализ синтаксиса начал ругаться на всё, что относится к синтаксису C++ (ключевые слова class, namespace и т.д.), а также на новый синтаксис подключения заголовочных файлов типа "#include <cstdint>" Если переименовать заголовочный файл в "MyLib.hpp", на ключевые слова C++ ругаться перестаёт, но продолжает ругаться на "#include <cstdint>". Причём, если не обращать внимания на ошибки, всё отлично компилируется, шьётся в контроллер и работает. Чем объясняется такое странное поведение и как с этим бороться?
Satarych, если решил С++ заняться, то выкидывай Keil, его туда толком не завезли. Переходи на что-нибудь на основе GCC, там C++17 отлично работает. Посмотри VisualGDB, например.
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
Я не частый гость на форумах, поэтому не особо знаю как тут устроена кухня. Учитывая, что за сутки админ не высказал никаких претензий, видимо, всё в порядке. К тому же, тема уже за пару сообщений вышла за рамки своего названия.
VladislavS писал(а):
Satarych, если решил С++ заняться, то выкидывай Keil, его туда толком не завезли. Переходи на что-нибудь на основе GCC, там C++17 отлично работает. Посмотри VisualGDB, например.
Вообще на Keil'е сидел только потому, что им пользовался человек, который меня с STM'ками знакомил. Так больше исторически сложилось) Попробовал VS+VisualGDB, вполне неплохо, пока всё нравится. Есть только одно но, у меня другой товарищ на Линуксе сидит и пользуется Эклипсом. Я его тоже попробую, но почитав отзывы, не проникся. Раз уж решился менять IDE, то хотелось бы, чтоб она на Линуксе заводилась, чтоб в одной среде работать. Может кто подскажет ещё мультиплатформенные варианты, кроме Эклипса?
If user wants C++ code to be evaluated inside a header file, there are two ways. If a cpp file includes the header file, then it will be evaluated as C++. Alternatively, go to the Project dialog and right click on a source group folder. Add the header file as an existing file. Then in the Project dialog, right click on the file. Go to Options for File... => Properties tab => File Type field. Set the type to C++ Source file. User may receive a fatal error message, if DSC is turned on and the file type is not set to C++.
Как по мне, добавлять заголовочники в проект это уже совсем костыли, но вдруг кому пригодится.
Раз уж решился менять IDE, то хотелось бы, чтоб она на Линуксе заводилась
Под линукс не так много IDE и почти все они на эклипсе. Посмотрите еще бесплатную IDE EmBitz. Она для винды, но под вайном в линуксе скорее всего будет работать, исключая разве что отладку, потому что нужен доступ к USB, и под вайном не факт что заработает.
Keil-ы всякие в Linux под вайном работают - без инсталляции, просто скопированные с виндовс ПК. Для компиляции можно использовать make. Для редактирования и компиляции - продвинутые редакторы типа sublime text/visual studio code. Отладка под вайном пока не работает. Отладку, если нужна редко, можно сделать в виртуальной машине или на ноутбуке с виндовс. з.ы. под линух есть собрат EmBitz - Codeblocks - арм плагин там был вроде.
для автора: в тему использования Линукс для разработки, свежая статья Настройка VSCODE под разработку для ARM на примере отладочной платы...
Цитата:
Можно посмотреть стек вызовов, а также просматривать и модифицировать регистры и память, ставить и удалять брейкпойнты, вычислять выражения и всё то же самое, что обычно при отладке можно.
https://habr.com/ru/post/437760/?utm_so ... ign=437760 з.ы. в кубе можно создавать makefile проект, после создания удаляем внутри папку с HAL, вычищаем от HAL файл main.c, немного правим сам make файл и получаем проект с самым свежим CMSIS. Открываем получившуюся папку в VS Code - создаем код, используя фишки современных IDE, и отлаживаем...
ставил уже давно SES - отличная штука - у меня еще много прошивок stm8 ст-линком, придется еще один дебагер/программатор - итак на столе жмут кабелей с дебагерами в хаб - пылесосить не удобно. На работе на ПК лучше на всяк не ставить (FREE for any non-commercial use). p/s/ SES вроде на основе кроссплатформенного Crossworks, а он вроде ст-линк поддерживает, за 100уе можно купить, пургену не видать.
Всем спасибо за советы. Остановился пока на связке VS+VisualGDB. Появились новые вопросы:
1. При отключённой оптимизации, GCC компилятор генерирует огромное количество лишнего кода (около 70kB Flash и 2,5kB SRAM) из стандартной библиотеки, когда я начинаю использовать наследование. Кажется, что он вообще всю стандартную библиотеку включил в прошивку. Зачем он это делает? Функции strlen, malloc, calloc и др. нигде не используются даже скрыто (или используются, но я чего-то не понимаю?), ведь при включённой оптимизации он от них избавился. Копмилятор Keil'а так себя не вёл. Практической цели вопрос не несёт, просто любопытно.
2. Пол года назад я уже задавал этот вопрос в теме https://radiokot.ru/forum/viewtopic.php?f=59&t=156603 Коротко он формулируется так: "Как заставить компилятор располагать константные экземпляры классов во Flash, а не в оперативке?" На тот момент я не до конца понимал смысл ответов и ушёл неспешно читать литературу на тему, заполняя дыры в знаниях. В итоге воспользовался советом andryblack и сделал пустой конструктор со списком инициализации.
Конструктор не пустой, следовательно его нужно вызвать, следовательно экземпляр нельзя разместить в rom, он размещается в ram и генерируется вызов конструктора на этапе инициализации. Замените присвоение в конструкторе на список инициализации.
И этот вариант меня полностью удовлетворил, компилятор Keil'a (ArmClang) константные экземпляры классов помещал во Flash. В этот раз я это определил однозначно, посмотрев в отладчике адреса объектов, у константного - 0x0800039C, у не константного - 0x20000000.
Однако компилятору GCC этого не достаточно. Он и константные, и неконстантные экземпляры классов пихает в оперативку. Более того, если я просто объявлю глобальную константу, в отладчике её адрес недоступен (пишет Attempt to take address of register or constant). Окей, возможно её порезала оптимизация, добавляю ей атрибут volatile - компилятор пихает её в оперативку. Я явно чего-то не понимаю. Может нужно как-то явно указывать в какую область памяти я хочу определить переменную?
Может нужно как-то явно указывать в какую область памяти я хочу определить переменную?
Для начало надо определиться, константа это или переменная. В принципе, под простую константу может и не выделяться память, если она где-то в простой формуле или пересылке используется - компилятор просто подставит её в код где надо.
Для начало надо определиться, константа это или переменная. В принципе, под простую константу может и не выделяться память, если она где-то в простой формуле или пересылке используется - компилятор просто подставит её в код где надо.
Могу немного косячить с терминами, ибо не программист по образованию, а так, самоучка. В моём понимании переменная - область памяти, в которой хранится число. Соответственно, константа - переменная, значение которой запрещено менять. Следующим код должен пояснить, что я имел ввиду: Спойлер
void init(Stm32GpioMode gpioMode) const; // Функция настройки (инициализации) пинов GPIO void set() const; // Функция включения ножки на лог "1" void clr() const; // Функция выключения ножки на лог "0" void tgl() const; // Функция изменяет состояние ножки на противоположное bool get() const; // Функция возвращает текущее состояние ножек
Всё работает, НО оба объекта в оперативке, причём компилятор суёт их в область ".bss". Тоесть при старте контроллера переменные объекта содержат нули, вызываются конструкторы объектов и переменные заполняют нужными значениями. Хотя конструктор пустой и все значения переменных доступны компилятору в списке инициализации. Почему компилятор не может сразу расположить этот объект в той части Flash памяти, из которой он будет их копировать в конструкторе?
На дурака решил провести следующий эксперимент: Спойлер
Код:
// Описываем структуру, идентичную нашему классу по содержанию переменных struct GpioStruct { GPIO_TypeDef *m_gpio; uint16_t m_pinMask; };
// Довольно варварски преобразуем тип и подсовываем вместо исходного класса int main() { led0.init(Stm32GpioMode::GP_PP_2MHzOut); ((const Stm32Gpio*)&led1)->init(Stm32GpioMode::GP_PP_2MHzOut);
Почему компилятор не может сразу расположить этот объект в той части Flash памяти, из которой он будет их копировать в конструкторе?
Я тебе уже в старой теме писал, что класс с приватными полями или конструкторами не является POD и такие объекты нельзя копировать к конструкторе. Чтоб было понятнее, в структуре фиксированный порядок полей, но если в классе есть публичные и приватные поля, то внутри них порядок фиксированный, но сами эти группы могут идти в том порядке, какой компилятор сочтет более подходящим. Это во-первых, а во-вторых, и я тоже об этом уже писал, если хочешь добиться максимальной эффективности, то нужно использовать шаблоны, тогда будут одни константы которые, естественно, попадут во флеш, если не напрямую в регистры. Спойлер
// Функция изменяет состояние ножки на противоположное void Stm32Gpio::tgl() const { m_gpio->ODR ^= m_pinMask; }
Распространенная ошибка. В регистр читается значение ODR, потом вызывается прерывание или переключается задача и там что-то пишется в тот же порт, по возвращению в ODR сохраняется старое значение с измененным битом...
// Функция изменяет состояние ножки на противоположное void Stm32Gpio::tgl() const { m_gpio->ODR ^= m_pinMask; }
Распространенная ошибка. В регистр читается значение ODR, потом вызывается прерывание или переключается задача и там что-то пишется в тот же порт, по возвращению в ODR сохраняется старое значение с измененным битом...
Да, согласен. Мигрировало из интернетных примеров, когда только осваивал STM'ки. По факту никогда не натыкался на подобное только потому, что обычно использовал отдельные set() и clr(). Посему и не замечал, но да, поправить нужно.
Я тебе уже в старой теме писал, что класс с приватными полями или конструкторами не является POD и такие объекты нельзя копировать к конструкторе. Чтоб было понятнее, в структуре фиксированный порядок полей, но если в классе есть публичные и приватные поля, то внутри них порядок фиксированный, но сами эти группы могут идти в том порядке, какой компилятор сочтет более подходящим. Это во-первых, а во-вторых, и я тоже об этом уже писал, если хочешь добиться максимальной эффективности, то нужно использовать шаблоны, тогда будут одни константы которые, естественно, попадут во флеш, если не напрямую в регистры.
Не спорю насчёт эффективности, в этом отношении вариант с шаблонами определённо лучше. Минус пара тактов на чтение из переменных, а то и вообще минус вызов функции, если оптимизатор её встроит, это всё понятно, но вопрос не в этом. Для реализации этого варианта не обязательно сводить класс к POD. В частности, я хочу использовать виртуальные функции (зачем - отдельная тема), а это добавляет в класс указатель на таблицу виртуальных функций __vptr, тоесть класс с виртуальными функциями автоматически не будет являться POD. Итого - каждый объект за зря будет занимать по 4 байта в оперативке.
Как я писал выше, воспользовался советом andryblack
Конструктор не пустой, следовательно его нужно вызвать, следовательно экземпляр нельзя разместить в rom, он размещается в ram и генерируется вызов конструктора на этапе инициализации. Замените присвоение в конструкторе на список инициализации.
и это решило проблему в Keil'е, его компилятор сразу размещал константные экземпляры класса во Flash, используя значения, предоставленные в списке инициализации. Если бы не этот факт, забил бы. А т.к. компилятор Keil'а умеет так делать, значит технических препятствий нет. Значит ключ к решению проблемы кроется в gcc компиляторе, либо он так делать не умеет, либо просто я не знаю как его об этом попросить.
Насчёт шаблонов. Я пробовал их использовать, но, видимо, до конца в них не разобрался. Спойлер
Код:
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask> class Stm32Gpio { public: void init(Stm32GpioMode gpioMode) const; // Функция настройки (инициализации) пинов GPIO virtual void set() const; // Функция включения ножки на лог "1" virtual void clr() const; // Функция выключения ножки на лог "0" virtual void tgl() const; // Функция изменяет состояние ножки на противоположное virtual bool get() const; // Функция возвращает текущее состояние ножек };
Keil это всё компилировал, но контроллер по факту вываливался в Hard Fault. Gcc выдавал ошибку следующего содержания, когда я пытался создать объекты
Код:
Stm32Gpio <GPIOD, 1<<2> led1; '(GPIO_TypeDef*)((1073741824 + 65536) + 5120)' is not a valid template argument for 'GPIO_TypeDef*' because it is not the address of a variable
Благодаря вашему примеру, кажется понял что именно было не так. Видимо, пользовательский тип не может быть non-type параметром. Нужно мне побольше почитать про non-type параметры.
Почему? Массив структур имеет смысл. Лучше чем множество дефайнов и код короче и понятней.
Я тут больше имел ввиду преобразование структуры в класс. Во первых, в реальности мне в эту структуру нужно ещё как-то добавить __vptr. Во вторых, как сказал Reflector, компилятор может приватные и публичные поля располагать в том порядке, какой ему больше понравится. Можно и не угадать)) Опасно так нагло преобразовывать типы.
Сейчас этот форум просматривают: mab72 и гости: 31
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения