uVision Keil. Помогите разобраться с компиляторами.
uVision Keil. Помогите разобраться с компиляторами.
Доброго всем дня.
Пишу код под микроконтроллеры 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>".
Причём, если не обращать внимания на ошибки, всё отлично компилируется, шьётся в контроллер и работает.
Чем объясняется такое странное поведение и как с этим бороться?
Пишу код под микроконтроллеры 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>".
Причём, если не обращать внимания на ошибки, всё отлично компилируется, шьётся в контроллер и работает.
Чем объясняется такое странное поведение и как с этим бороться?
- Реклама
- 240265
- Электрический кот
- Сообщения: 1029
- Зарегистрирован: Сб мар 09, 2013 11:29:22
- Откуда: 40RUS, Жуков
Re: uVision Keil. Помогите разобраться с компиляторами.
Зачем темы плодить ? Есть же https://radiokot.ru/forum/viewtopic.php ... 01bb313f44 . АДМИН куда смотришь ?
IVL ex UA6PJ
- VladislavS
- Собутыльник Кота
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
Re: uVision Keil. Помогите разобраться с компиляторами.
Satarych, если решил С++ заняться, то выкидывай Keil, его туда толком не завезли. Переходи на что-нибудь на основе GCC, там C++17 отлично работает. Посмотри VisualGDB, например.
Re: uVision Keil. Помогите разобраться с компиляторами.
GCC например. В отличие от компилятора кейла, полностью бесплатный и без ограничений.Satarych писал(а):Какие вообще существуют компиляторы для ARM?
- Oxford
- Опытный кот
- Сообщения: 819
- Зарегистрирован: Вт окт 23, 2012 13:17:25
- Откуда: Прокопьевск
- Контактная информация:
Re: uVision Keil. Помогите разобраться с компиляторами.
В KEIL можно подключать GCC кому это нужно.
Инженер R@D
Telegram чат: https://t.me/radiowolf или в поиске приложения @radiowolf. Личка:@cncoxford
Telegram чат: https://t.me/radiowolf или в поиске приложения @radiowolf. Личка:@cncoxford
- Реклама
Re: uVision Keil. Помогите разобраться с компиляторами.
[uquote="240265",url="/forum/viewtopic.php?p=3556097#p3556097"]Зачем темы плодить ? Есть же https://radiokot.ru/forum/viewtopic.php ... 01bb313f44 . АДМИН куда смотришь ?[/uquote] Я не частый гость на форумах, поэтому не особо знаю как тут устроена кухня. Учитывая, что за сутки админ не высказал никаких претензий, видимо, всё в порядке. К тому же, тема уже за пару сообщений вышла за рамки своего названия.
Отвечая на свой второй вопрос:
http://www.keil.com/support/docs/3973.htm
В самом низу они пишут
Вообще на Keil'е сидел только потому, что им пользовался человек, который меня с STM'ками знакомил. Так больше исторически сложилось) Попробовал VS+VisualGDB, вполне неплохо, пока всё нравится. Есть только одно но, у меня другой товарищ на Линуксе сидит и пользуется Эклипсом. Я его тоже попробую, но почитав отзывы, не проникся. Раз уж решился менять IDE, то хотелось бы, чтоб она на Линуксе заводилась, чтоб в одной среде работать. Может кто подскажет ещё мультиплатформенные варианты, кроме Эклипса?VladislavS писал(а):Satarych, если решил С++ заняться, то выкидывай Keil, его туда толком не завезли. Переходи на что-нибудь на основе GCC, там C++17 отлично работает. Посмотри VisualGDB, например.
Отвечая на свой второй вопрос:
http://www.keil.com/support/docs/3973.htm
В самом низу они пишут
Как по мне, добавлять заголовочники в проект это уже совсем костыли, но вдруг кому пригодится.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++.
Re: uVision Keil. Помогите разобраться с компиляторами.
Под линукс не так много IDE и почти все они на эклипсе. Посмотрите еще бесплатную IDE EmBitz. Она для винды, но под вайном в линуксе скорее всего будет работать, исключая разве что отладку, потому что нужен доступ к USB, и под вайном не факт что заработает.Satarych писал(а):Раз уж решился менять IDE, то хотелось бы, чтоб она на Линуксе заводилась
- VladislavS
- Собутыльник Кота
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
Re: uVision Keil. Помогите разобраться с компиляторами.
[uquote="Satarych",url="/forum/viewtopic.php?p=3556920#p3556920"]Может кто подскажет ещё мультиплатформенные варианты, кроме Эклипса?[/uquote]Ну тогда SES от Segger.
- oleg110592
- Друг Кота
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
Re: uVision Keil. Помогите разобраться с компиляторами.
Keil-ы всякие в Linux под вайном работают - без инсталляции, просто скопированные с виндовс ПК. Для компиляции можно использовать make. Для редактирования и компиляции - продвинутые редакторы типа sublime text/visual studio code. Отладка под вайном пока не работает. Отладку, если нужна редко, можно сделать в виртуальной машине или на ноутбуке с виндовс.

з.ы. под линух есть собрат EmBitz - Codeblocks - арм плагин там был вроде.
з.ы. под линух есть собрат EmBitz - Codeblocks - арм плагин там был вроде.
- VladislavS
- Собутыльник Кота
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
Re: uVision Keil. Помогите разобраться с компиляторами.
oleg110592, это называется сексом стоя в гамаке.
- oleg110592
- Друг Кота
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
Re: uVision Keil. Помогите разобраться с компиляторами.
для автора: в тему использования Линукс для разработки, свежая статья
Настройка VSCODE под разработку для ARM на примере отладочной платы...
з.ы. в кубе можно создавать makefile проект, после создания удаляем внутри папку с HAL, вычищаем от HAL файл main.c, немного правим сам make файл и получаем проект с самым свежим CMSIS. Открываем получившуюся папку в VS Code - создаем код, используя фишки современных IDE, и отлаживаем...
Настройка VSCODE под разработку для ARM на примере отладочной платы...
https://habr.com/ru/post/437760/?utm_so ... ign=437760Можно посмотреть стек вызовов, а также просматривать и модифицировать регистры и память, ставить и удалять брейкпойнты, вычислять выражения и всё то же самое, что обычно при отладке можно.
з.ы. в кубе можно создавать makefile проект, после создания удаляем внутри папку с HAL, вычищаем от HAL файл main.c, немного правим сам make файл и получаем проект с самым свежим CMSIS. Открываем получившуюся папку в VS Code - создаем код, используя фишки современных IDE, и отлаживаем...
Re: uVision Keil. Помогите разобраться с компиляторами.
oleg110592, весьма сомнительное рукоблудство, Ставь лучше SES , все работает сразу искарапки ...
- oleg110592
- Друг Кота
- Сообщения: 3832
- Зарегистрирован: Сб сен 10, 2011 17:46:25
Re: uVision Keil. Помогите разобраться с компиляторами.
ставил уже давно SES - отличная штука - у меня еще много прошивок stm8 ст-линком, придется еще один дебагер/программатор - итак на столе жмут кабелей с дебагерами в хаб - пылесосить не удобно. На работе на ПК лучше на всяк не ставить (FREE for any non-commercial use).
p/s/ SES вроде на основе кроссплатформенного Crossworks, а он вроде ст-линк поддерживает, за 100уе можно купить, пургену не видать.
p/s/ SES вроде на основе кроссплатформенного Crossworks, а он вроде ст-линк поддерживает, за 100уе можно купить, пургену не видать.
Re: uVision Keil. Помогите разобраться с компиляторами.
Всем спасибо за советы. Остановился пока на связке 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 и сделал пустой конструктор со списком инициализации.
[uquote="andryblack",url="/forum/viewtopic.php?p=3497586#p3497586"]Конструктор не пустой, следовательно его нужно вызвать, следовательно экземпляр нельзя разместить в rom, он размещается в ram и генерируется вызов конструктора на этапе инициализации. Замените присвоение в конструкторе на список инициализации.[/uquote] И этот вариант меня полностью удовлетворил, компилятор Keil'a (ArmClang) константные экземпляры классов помещал во Flash. В этот раз я это определил однозначно, посмотрев в отладчике адреса объектов, у константного - 0x0800039C, у не константного - 0x20000000.
Однако компилятору GCC этого не достаточно. Он и константные, и неконстантные экземпляры классов пихает в оперативку.
Более того, если я просто объявлю глобальную константу, в отладчике её адрес недоступен (пишет Attempt to take address of register or constant). Окей, возможно её порезала оптимизация, добавляю ей атрибут volatile - компилятор пихает её в оперативку. Я явно чего-то не понимаю. Может нужно как-то явно указывать в какую область памяти я хочу определить переменную?
Появились новые вопросы:
1. При отключённой оптимизации, GCC компилятор генерирует огромное количество лишнего кода (около 70kB Flash и 2,5kB SRAM) из стандартной библиотеки, когда я начинаю использовать наследование. Кажется, что он вообще всю стандартную библиотеку включил в прошивку. Зачем он это делает? Функции strlen, malloc, calloc и др. нигде не используются даже скрыто (или используются, но я чего-то не понимаю?), ведь при включённой оптимизации он от них избавился. Копмилятор Keil'а так себя не вёл. Практической цели вопрос не несёт, просто любопытно.
2. Пол года назад я уже задавал этот вопрос в теме https://radiokot.ru/forum/viewtopic.php?f=59&t=156603
Коротко он формулируется так: "Как заставить компилятор располагать константные экземпляры классов во Flash, а не в оперативке?"
На тот момент я не до конца понимал смысл ответов и ушёл неспешно читать литературу на тему, заполняя дыры в знаниях.
В итоге воспользовался советом andryblack и сделал пустой конструктор со списком инициализации.
[uquote="andryblack",url="/forum/viewtopic.php?p=3497586#p3497586"]Конструктор не пустой, следовательно его нужно вызвать, следовательно экземпляр нельзя разместить в rom, он размещается в ram и генерируется вызов конструктора на этапе инициализации. Замените присвоение в конструкторе на список инициализации.[/uquote] И этот вариант меня полностью удовлетворил, компилятор Keil'a (ArmClang) константные экземпляры классов помещал во Flash. В этот раз я это определил однозначно, посмотрев в отладчике адреса объектов, у константного - 0x0800039C, у не константного - 0x20000000.
Однако компилятору GCC этого не достаточно. Он и константные, и неконстантные экземпляры классов пихает в оперативку.
Более того, если я просто объявлю глобальную константу, в отладчике её адрес недоступен (пишет Attempt to take address of register or constant). Окей, возможно её порезала оптимизация, добавляю ей атрибут volatile - компилятор пихает её в оперативку. Я явно чего-то не понимаю. Может нужно как-то явно указывать в какую область памяти я хочу определить переменную?
- VladislavS
- Собутыльник Кота
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
Re: uVision Keil. Помогите разобраться с компиляторами.
[uquote="Satarych",url="/forum/viewtopic.php?p=3559681#p3559681"]Практической цели вопрос не несёт, просто любопытно.[/uquote]Удовлетворить своё любопытство можешь прочитав map-файл.
[uquote="Satarych",url="/forum/viewtopic.php?p=3559681#p3559681"]Может нужно как-то явно указывать в какую область памяти я хочу определить переменную?[/uquote]Для начало надо определиться, константа это или переменная. В принципе, под простую константу может и не выделяться память, если она где-то в простой формуле или пересылке используется - компилятор просто подставит её в код где надо.
[uquote="Satarych",url="/forum/viewtopic.php?p=3559681#p3559681"]Может нужно как-то явно указывать в какую область памяти я хочу определить переменную?[/uquote]Для начало надо определиться, константа это или переменная. В принципе, под простую константу может и не выделяться память, если она где-то в простой формуле или пересылке используется - компилятор просто подставит её в код где надо.
Re: uVision Keil. Помогите разобраться с компиляторами.
[uquote="VladislavS",url="/forum/viewtopic.php?p=3559703#p3559703"]Удовлетворить своё любопытство можешь прочитав map-файл.[/uquote] Открыл блокнотом, посмотрел, но не могу похвастаться пониманием его содержимого)) О каких-то вещах догадываюсь, но это нельзя назвать пониманием.
[uquote="VladislavS",url="/forum/viewtopic.php?p=3559703#p3559703"]Для начало надо определиться, константа это или переменная. В принципе, под простую константу может и не выделяться память, если она где-то в простой формуле или пересылке используется - компилятор просто подставит её в код где надо.[/uquote] Могу немного косячить с терминами, ибо не программист по образованию, а так, самоучка. В моём понимании переменная - область памяти, в которой хранится число. Соответственно, константа - переменная, значение которой запрещено менять. Следующим код должен пояснить, что я имел ввиду:Создаём 2 глобальных объекта (ножки, на которых висят светодиоды) и используем их в main
Всё работает, НО оба объекта в оперативке, причём компилятор суёт их в область ".bss". Тоесть при старте контроллера переменные объекта содержат нули, вызываются конструкторы объектов и переменные заполняют нужными значениями. Хотя конструктор пустой и все значения переменных доступны компилятору в списке инициализации. Почему компилятор не может сразу расположить этот объект в той части Flash памяти, из которой он будет их копировать в конструкторе?
На дурака решил провести следующий эксперимент:Понятно, что это дичайший костыль и нормальные люди так делать не будут, но в данном случае led1 уже находится во Flash и оно работает.
[uquote="VladislavS",url="/forum/viewtopic.php?p=3559703#p3559703"]Для начало надо определиться, константа это или переменная. В принципе, под простую константу может и не выделяться память, если она где-то в простой формуле или пересылке используется - компилятор просто подставит её в код где надо.[/uquote] Могу немного косячить с терминами, ибо не программист по образованию, а так, самоучка. В моём понимании переменная - область памяти, в которой хранится число. Соответственно, константа - переменная, значение которой запрещено менять. Следующим код должен пояснить, что я имел ввиду:
Спойлер
Код: Выделить всё
// Возможные режимы работы Gpio в Stm32f10x
enum class Stm32GpioMode
{
// Inputs
Analog_Inp = 0x0, // 0b0000
Floating_Inp = 0x4, // 0b0100
Pull_Dn_Inp = 0x08, // 0b01000
Pull_Up_Inp = 0x18, // 0b11000
// Output 10MHz
GP_PP_10MHzOut = 0x1, // 0b0001
GP_OD_10MHzOut = 0x5, // 0b0101
AF_PP_10MHzOut = 0x9, // 0b1001
AF_OD_10MHzOut = 0xD, // 0b1101
// Outputs 2MHz
GP_PP_2MHzOut = 0x2, // 0b0010
GP_OD_2MHzOut = 0x6, // 0b0110
AF_PP_2MHzOut = 0xA, // 0b1010
AF_OD_2MHzOut = 0xE, // 0b1110
// Outputs 50MHz
GP_PP_50MHzOut = 0x3, // 0b0011
GP_OD_50MHzOut = 0x7, // 0b0111
AF_PP_50MHzOut = 0xB, // 0b1011
AF_OD_50MHzOut = 0xF, // 0b1111
// Исходное состояние выхода
log0_InitOut = 0x00, // 0b00000
log1_InitOut = 0x10 // 0b10000
};
class Stm32Gpio
{
public:
Stm32Gpio() = delete;
Stm32Gpio(GPIO_TypeDef *const gpio, const uint16_t pinMask);
void init(Stm32GpioMode gpioMode) const; // Функция настройки (инициализации) пинов GPIO
void set() const; // Функция включения ножки на лог "1"
void clr() const; // Функция выключения ножки на лог "0"
void tgl() const; // Функция изменяет состояние ножки на противоположное
bool get() const; // Функция возвращает текущее состояние ножек
private :
GPIO_TypeDef *const m_gpio;
const uint16_t m_pinMask;
};
// Конструктор
Stm32Gpio::Stm32Gpio(GPIO_TypeDef *const gpio, const uint16_t pinMask)
: m_gpio (gpio), m_pinMask (pinMask) {}
// Функция настройки (инициализации) ножек порта GPIO
void Stm32Gpio::init(Stm32GpioMode gpioMode) const
{
// Включаем тактирование порта
//using namespace stm32Rcc_Ns;
//coreRcc.enableHardwareClock (m_gpio);
// !NOTE! Временная затычка, пока не починю верхние 2 строки
if (m_gpio == GPIOA) RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
if (m_gpio == GPIOB) RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
if (m_gpio == GPIOC) RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
if (m_gpio == GPIOD) RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
if (m_gpio == GPIOE) RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
//if (m_gpio == GPIOF) RCC->APB2ENR |= RCC_APB2ENR_IOPFEN;
//if (m_gpio == GPIOG) RCC->APB2ENR |= RCC_APB2ENR_IOPGEN;
// 5-й бит аргумента gpioMode содержит исходное состояние ножек
// Если там лог "1" - инициализируем на лог "1"
if((uint32_t)gpioMode & 0x10)
m_gpio->BSRR = m_pinMask;
// Иначе, если лог "0" - инициализируем на лог "0"
else
m_gpio->BRR = m_pinMask;
// Стираем 5-й бит в аргументе pinMode
gpioMode = (Stm32GpioMode)((uint32_t)gpioMode & 0xF);
// Перебираем первые 8 ножек
for(uint8_t pinNum = 0 ; pinNum < 8 ; ++pinNum)
{
// Если нужно настроить данную ножку
if(m_pinMask & (0x1 << pinNum))
{
// Настройки каждой ножки занимают по 4 бита в регистре
// Преобразуем номер ножки в необходимое смещение в регистре
// Зачищаем редактируемую часть регистра
m_gpio->CRL &= ~(0xF << (pinNum * 4));
// Выставляем нужный режим работы ножки
m_gpio->CRL |= ((uint32_t)gpioMode << (pinNum * 4));
}
}
// Перебираем последние 8 ножек
for(uint8_t pinNum = 0 ; pinNum < 8 ; ++pinNum)
{
// Если нужно настроить данную ножку
if(m_pinMask & (0x1 << (pinNum + 8)))
{
// Настройки каждой ножки занимают по 4 бита в регистре
// Преобразуем номер ножки в необходимое смещение в регистре
// Зачищаем редактируемую часть регистра
m_gpio->CRH &= ~(0xF << (pinNum * 4));
// Выставляем нужный режим работы пина
m_gpio->CRH |= ((uint32_t)gpioMode << (pinNum * 4));
}
}
}
// Функция включения ножки на лог "1"
void Stm32Gpio::set() const
{
m_gpio->BSRR = m_pinMask;
}
// Функция выключения ножки на лог "0"
void Stm32Gpio::clr() const
{
m_gpio->BRR = m_pinMask;
}
// Функция изменяет состояние ножки на противоположное
void Stm32Gpio::tgl() const
{
m_gpio->ODR ^= m_pinMask;
}
// Функция возвращает текущее состояние ножек
bool Stm32Gpio::get() const
{
return m_gpio->IDR & m_pinMask;
}Спойлер
Код: Выделить всё
const Stm32Gpio led0 (GPIOA, 1<<8);
const Stm32Gpio led1 (GPIOD, 1<<2);
int main()
{
led0.init (Stm32GpioMode::GP_PP_2MHzOut);
led1.init (Stm32GpioMode::GP_PP_2MHzOut);
for (;;)
{
led0.clr();
led1.set();
Delay();
led0.set();
led1.clr();
Delay();
}
}На дурака решил провести следующий эксперимент:
Спойлер
Код: Выделить всё
// Описываем структуру, идентичную нашему классу по содержанию переменных
struct GpioStruct
{
GPIO_TypeDef *m_gpio;
uint16_t m_pinMask;
};
// Создаём её глобальный константный экземпляр
const GpioStruct led1 = {GPIOD, 1<<2};
// Довольно варварски преобразуем тип и подсовываем вместо исходного класса
int main()
{
led0.init(Stm32GpioMode::GP_PP_2MHzOut);
((const Stm32Gpio*)&led1)->init(Stm32GpioMode::GP_PP_2MHzOut);
for (;;)
{
led0.clr();
((const Stm32Gpio*)&led1)->set();
Delay();
led0.set();
((const Stm32Gpio*)&led1)->clr();
Delay();
}
}
Re: uVision Keil. Помогите разобраться с компиляторами.
[uquote="Satarych",url="/forum/viewtopic.php?p=3562348#p3562348"]Почему компилятор не может сразу расположить этот объект в той части Flash памяти, из которой он будет их копировать в конструкторе?[/uquote]
Я тебе уже в старой теме писал, что класс с приватными полями или конструкторами не является POD и такие объекты нельзя копировать к конструкторе. Чтоб было понятнее, в структуре фиксированный порядок полей, но если в классе есть публичные и приватные поля, то внутри них порядок фиксированный, но сами эти группы могут идти в том порядке, какой компилятор сочтет более подходящим. Это во-первых, а во-вторых, и я тоже об этом уже писал, если хочешь добиться максимальной эффективности, то нужно
использовать шаблоны, тогда будут одни константы которые, естественно, попадут во флеш, если не напрямую в регистры.
Добавлено after 16 minutes 52 seconds:
[uquote="Satarych",url="/forum/viewtopic.php?p=3562348#p3562348"][/uquote]
Распространенная ошибка. В регистр читается значение ODR, потом вызывается прерывание или переключается задача и там что-то пишется в тот же порт, по возвращению в ODR сохраняется старое значение с измененным битом...
Я тебе уже в старой теме писал, что класс с приватными полями или конструкторами не является POD и такие объекты нельзя копировать к конструкторе. Чтоб было понятнее, в структуре фиксированный порядок полей, но если в классе есть публичные и приватные поля, то внутри них порядок фиксированный, но сами эти группы могут идти в том порядке, какой компилятор сочтет более подходящим. Это во-первых, а во-вторых, и я тоже об этом уже писал, если хочешь добиться максимальной эффективности, то нужно
использовать шаблоны, тогда будут одни константы которые, естественно, попадут во флеш, если не напрямую в регистры.
Спойлер
Код: Выделить всё
template<uint32_t gpioBase, uint32_t pin, uint32_t af>
class PinT
{
public:
static constexpr uint32_t pinMask = 1 << pin;
static _always_inline_ auto base() { return reinterpret_cast<GPIO_TypeDef*>(gpioBase); }
PinT() {}
PinT(PinMode mode) { ... }
static void set() { base()->BSRR = pinMask; }
static void clear() { base()->BSRR = 0x10000 << pin; }
static void toggle() { base()->BSRR = (0x10000 << pin) | (~base()->ODR & pinMask); }
static void write(bool data) { base()->BSRR = (0x10000 | data) << pin; }
static bool read() { return base()->IDR & pinMask; }
};
template<uint32_t pin, uint32_t af = 0>
using PinA = PinT<GPIOA_BASE, pin, af>;
template<uint32_t pin, uint32_t af = 0>
using PinB = PinT<GPIOB_BASE, pin, af>;
PinA<5> led1;
using led2 = PinB<3>;
led1.set();
led2::toggle();
[uquote="Satarych",url="/forum/viewtopic.php?p=3562348#p3562348"]
Код: Выделить всё
// Функция изменяет состояние ножки на противоположное
void Stm32Gpio::tgl() const
{
m_gpio->ODR ^= m_pinMask;
}Распространенная ошибка. В регистр читается значение ODR, потом вызывается прерывание или переключается задача и там что-то пишется в тот же порт, по возвращению в ODR сохраняется старое значение с измененным битом...
Re: uVision Keil. Помогите разобраться с компиляторами.
Начну с простого
[uquote="Reflector",url="/forum/viewtopic.php?p=3562363#p3562363"][uquote="Satarych",url="/forum/viewtopic.php?p=3562348#p3562348"][/uquote]Распространенная ошибка. В регистр читается значение ODR, потом вызывается прерывание или переключается задача и там что-то пишется в тот же порт, по возвращению в ODR сохраняется старое значение с измененным битом...[/uquote] Да, согласен. Мигрировало из интернетных примеров, когда только осваивал STM'ки. По факту никогда не натыкался на подобное только потому, что обычно использовал отдельные set() и clr(). Посему и не замечал, но да, поправить нужно.
[uquote="Reflector",url="/forum/viewtopic.php?p=3562363#p3562363"]Я тебе уже в старой теме писал, что класс с приватными полями или конструкторами не является POD и такие объекты нельзя копировать к конструкторе. Чтоб было понятнее, в структуре фиксированный порядок полей, но если в классе есть публичные и приватные поля, то внутри них порядок фиксированный, но сами эти группы могут идти в том порядке, какой компилятор сочтет более подходящим. Это во-первых, а во-вторых, и я тоже об этом уже писал, если хочешь добиться максимальной эффективности, то нужно использовать шаблоны, тогда будут одни константы которые, естественно, попадут во флеш, если не напрямую в регистры.[/uquote] Не спорю насчёт эффективности, в этом отношении вариант с шаблонами определённо лучше. Минус пара тактов на чтение из переменных, а то и вообще минус вызов функции, если оптимизатор её встроит, это всё понятно, но вопрос не в этом. Для реализации этого варианта не обязательно сводить класс к POD. В частности, я хочу использовать виртуальные функции (зачем - отдельная тема), а это добавляет в класс указатель на таблицу виртуальных функций __vptr, тоесть класс с виртуальными функциями автоматически не будет являться POD. Итого - каждый объект за зря будет занимать по 4 байта в оперативке.
Как я писал выше, воспользовался советом andryblack
[uquote="andryblack",url="/forum/viewtopic.php?p=3497586#p3497586"]Конструктор не пустой, следовательно его нужно вызвать, следовательно экземпляр нельзя разместить в rom, он размещается в ram и генерируется вызов конструктора на этапе инициализации. Замените присвоение в конструкторе на список инициализации.[/uquote]и это решило проблему в Keil'е, его компилятор сразу размещал константные экземпляры класса во Flash, используя значения, предоставленные в списке инициализации. Если бы не этот факт, забил бы. А т.к. компилятор Keil'а умеет так делать, значит технических препятствий нет. Значит ключ к решению проблемы кроется в gcc компиляторе, либо он так делать не умеет, либо просто я не знаю как его об этом попросить.
Насчёт шаблонов. Я пробовал их использовать, но, видимо, до конца в них не разобрался.Keil это всё компилировал, но контроллер по факту вываливался в Hard Fault. Gcc выдавал ошибку следующего содержания, когда я пытался создать объекты
Благодаря вашему примеру, кажется понял что именно было не так. Видимо, пользовательский тип не может быть non-type параметром. Нужно мне побольше почитать про non-type параметры.
[uquote="Reflector",url="/forum/viewtopic.php?p=3562363#p3562363"][uquote="Satarych",url="/forum/viewtopic.php?p=3562348#p3562348"]
Код: Выделить всё
// Функция изменяет состояние ножки на противоположное
void Stm32Gpio::tgl() const
{
m_gpio->ODR ^= m_pinMask;
}[uquote="Reflector",url="/forum/viewtopic.php?p=3562363#p3562363"]Я тебе уже в старой теме писал, что класс с приватными полями или конструкторами не является POD и такие объекты нельзя копировать к конструкторе. Чтоб было понятнее, в структуре фиксированный порядок полей, но если в классе есть публичные и приватные поля, то внутри них порядок фиксированный, но сами эти группы могут идти в том порядке, какой компилятор сочтет более подходящим. Это во-первых, а во-вторых, и я тоже об этом уже писал, если хочешь добиться максимальной эффективности, то нужно использовать шаблоны, тогда будут одни константы которые, естественно, попадут во флеш, если не напрямую в регистры.[/uquote] Не спорю насчёт эффективности, в этом отношении вариант с шаблонами определённо лучше. Минус пара тактов на чтение из переменных, а то и вообще минус вызов функции, если оптимизатор её встроит, это всё понятно, но вопрос не в этом. Для реализации этого варианта не обязательно сводить класс к POD. В частности, я хочу использовать виртуальные функции (зачем - отдельная тема), а это добавляет в класс указатель на таблицу виртуальных функций __vptr, тоесть класс с виртуальными функциями автоматически не будет являться POD. Итого - каждый объект за зря будет занимать по 4 байта в оперативке.
Как я писал выше, воспользовался советом andryblack
[uquote="andryblack",url="/forum/viewtopic.php?p=3497586#p3497586"]Конструктор не пустой, следовательно его нужно вызвать, следовательно экземпляр нельзя разместить в rom, он размещается в ram и генерируется вызов конструктора на этапе инициализации. Замените присвоение в конструкторе на список инициализации.[/uquote]и это решило проблему в 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; // Функция возвращает текущее состояние ножек
};
// Функция настройки (инициализации) ножек порта GPIO
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
void Stm32Gpio <m_gpio, m_pinMask>::init(Stm32GpioMode gpioMode) const
{
// Включаем тактирование порта
//using namespace stm32Rcc_Ns;
//coreRcc.enableHardwareClock (m_gpio);
// !NOTE! Временная затычка, пока не починю верхние 2 строки
if (m_gpio == GPIOA) RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
if (m_gpio == GPIOB) RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
if (m_gpio == GPIOC) RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
if (m_gpio == GPIOD) RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
if (m_gpio == GPIOE) RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
//if (m_gpio == GPIOF) RCC->APB2ENR |= RCC_APB2ENR_IOPFEN;
//if (m_gpio == GPIOG) RCC->APB2ENR |= RCC_APB2ENR_IOPGEN;
// 5-й бит аргумента gpioMode содержит исходное состояние ножек
// Если там лог "1" - инициализируем на лог "1"
if((uint32_t)gpioMode & 0x10)
m_gpio->BSRR = m_pinMask;
// Иначе, если лог "0" - инициализируем на лог "0"
else
m_gpio->BRR = m_pinMask;
// Стираем 5-й бит в аргументе pinMode
gpioMode = (Stm32GpioMode)((uint32_t)gpioMode & 0xF);
// Перебираем первые 8 ножек
for(uint8_t pinNum = 0 ; pinNum < 8 ; ++pinNum)
{
// Если нужно настроить данную ножку
if(m_pinMask & (0x1 << pinNum))
{
// Настройки каждой ножки занимают по 4 бита в регистре
// Преобразуем номер ножки в необходимое смещение в регистре
// Зачищаем редактируемую часть регистра
m_gpio->CRL &= ~(0xF << (pinNum * 4));
// Выставляем нужный режим работы ножки
m_gpio->CRL |= ((uint32_t)gpioMode << (pinNum * 4));
}
}
// Перебираем последние 8 ножек
for(uint8_t pinNum = 0 ; pinNum < 8 ; ++pinNum)
{
// Если нужно настроить данную ножку
if(m_pinMask & (0x1 << (pinNum + 8)))
{
// Настройки каждой ножки занимают по 4 бита в регистре
// Преобразуем номер ножки в необходимое смещение в регистре
// Зачищаем редактируемую часть регистра
m_gpio->CRH &= ~(0xF << (pinNum * 4));
// Выставляем нужный режим работы пина
m_gpio->CRH |= ((uint32_t)gpioMode << (pinNum * 4));
}
}
}
// Функция включения ножки на лог "1"
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
void Stm32Gpio <m_gpio, m_pinMask>::set() const
{
m_gpio->BSRR = m_pinMask;
}
// Функция выключения ножки на лог "0"
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
void Stm32Gpio <m_gpio, m_pinMask>::clr() const
{
m_gpio->BRR = m_pinMask;
}
// Функция изменяет состояние ножки на противоположное
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
void Stm32Gpio <m_gpio, m_pinMask>::tgl() const
{
m_gpio->ODR ^= m_pinMask;
}
// Функция возвращает текущее состояние ножек
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
bool Stm32Gpio <m_gpio, m_pinMask>::Stm32Gpio::get() const
{
return m_gpio->IDR & m_pinMask;
}Код: Выделить всё
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
Re: uVision Keil. Помогите разобраться с компиляторами.
Почему? Массив структур имеет смысл. Лучше чем множество дефайнов и код короче и понятней.Satarych писал(а):нормальные люди так делать не будут
Re: uVision Keil. Помогите разобраться с компиляторами.
[uquote="Мурик",url="/forum/viewtopic.php?p=3562631#p3562631"]
Во первых, в реальности мне в эту структуру нужно ещё как-то добавить __vptr.
Во вторых, как сказал Reflector, компилятор может приватные и публичные поля располагать в том порядке, какой ему больше понравится. Можно и не угадать)) Опасно так нагло преобразовывать типы.
Почему? Массив структур имеет смысл. Лучше чем множество дефайнов и код короче и понятней.[/uquote]Я тут больше имел ввиду преобразование структуры в класс.Satarych писал(а):нормальные люди так делать не будут
Во первых, в реальности мне в эту структуру нужно ещё как-то добавить __vptr.
Во вторых, как сказал Reflector, компилятор может приватные и публичные поля располагать в том порядке, какой ему больше понравится. Можно и не угадать)) Опасно так нагло преобразовывать типы.


