uVision Keil. Помогите разобраться с компиляторами.

Кто любит RISC в жизни, заходим, не стесняемся.
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

Доброго всем дня.
Пишу код под микроконтроллеры 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. Помогите разобраться с компиляторами.

Сообщение 240265 »

Зачем темы плодить ? Есть же https://radiokot.ru/forum/viewtopic.php ... 01bb313f44 . АДМИН куда смотришь ?
IVL ex UA6PJ
Реклама
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение VladislavS »

Satarych, если решил С++ заняться, то выкидывай Keil, его туда толком не завезли. Переходи на что-нибудь на основе GCC, там C++17 отлично работает. Посмотри VisualGDB, например.
Аватара пользователя
Мурик
Друг Кота
Сообщения: 3383
Зарегистрирован: Пн окт 11, 2010 19:00:08

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Мурик »

Satarych писал(а):Какие вообще существуют компиляторы для ARM?
GCC например. В отличие от компилятора кейла, полностью бесплатный и без ограничений.
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
Oxford
Опытный кот
Сообщения: 819
Зарегистрирован: Вт окт 23, 2012 13:17:25
Откуда: Прокопьевск
Контактная информация:

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Oxford »

В KEIL можно подключать GCC кому это нужно.
Инженер R@D

Telegram чат: https://t.me/radiowolf или в поиске приложения @radiowolf. Личка:@cncoxford
Реклама
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

[uquote="240265",url="/forum/viewtopic.php?p=3556097#p3556097"]Зачем темы плодить ? Есть же https://radiokot.ru/forum/viewtopic.php ... 01bb313f44 . АДМИН куда смотришь ?[/uquote] Я не частый гость на форумах, поэтому не особо знаю как тут устроена кухня. Учитывая, что за сутки админ не высказал никаких претензий, видимо, всё в порядке. К тому же, тема уже за пару сообщений вышла за рамки своего названия.
VladislavS писал(а):Satarych, если решил С++ заняться, то выкидывай Keil, его туда толком не завезли. Переходи на что-нибудь на основе GCC, там C++17 отлично работает. Посмотри VisualGDB, например.
Вообще на Keil'е сидел только потому, что им пользовался человек, который меня с STM'ками знакомил. Так больше исторически сложилось) Попробовал VS+VisualGDB, вполне неплохо, пока всё нравится. Есть только одно но, у меня другой товарищ на Линуксе сидит и пользуется Эклипсом. Я его тоже попробую, но почитав отзывы, не проникся. Раз уж решился менять IDE, то хотелось бы, чтоб она на Линуксе заводилась, чтоб в одной среде работать. Может кто подскажет ещё мультиплатформенные варианты, кроме Эклипса?


Отвечая на свой второй вопрос:
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++.
Как по мне, добавлять заголовочники в проект это уже совсем костыли, но вдруг кому пригодится.
Реклама
Аватара пользователя
Мурик
Друг Кота
Сообщения: 3383
Зарегистрирован: Пн окт 11, 2010 19:00:08

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Мурик »

Satarych писал(а):Раз уж решился менять IDE, то хотелось бы, чтоб она на Линуксе заводилась
Под линукс не так много IDE и почти все они на эклипсе. Посмотрите еще бесплатную IDE EmBitz. Она для винды, но под вайном в линуксе скорее всего будет работать, исключая разве что отладку, потому что нужен доступ к USB, и под вайном не факт что заработает.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение VladislavS »

[uquote="Satarych",url="/forum/viewtopic.php?p=3556920#p3556920"]Может кто подскажет ещё мультиплатформенные варианты, кроме Эклипса?[/uquote]Ну тогда SES от Segger.
Аватара пользователя
oleg110592
Друг Кота
Сообщения: 3832
Зарегистрирован: Сб сен 10, 2011 17:46:25

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение oleg110592 »

Keil-ы всякие в Linux под вайном работают - без инсталляции, просто скопированные с виндовс ПК. Для компиляции можно использовать make. Для редактирования и компиляции - продвинутые редакторы типа sublime text/visual studio code. Отладка под вайном пока не работает. Отладку, если нужна редко, можно сделать в виртуальной машине или на ноутбуке с виндовс.
Изображение
з.ы. под линух есть собрат EmBitz - Codeblocks - арм плагин там был вроде.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение VladislavS »

oleg110592, это называется сексом стоя в гамаке.
Аватара пользователя
oleg110592
Друг Кота
Сообщения: 3832
Зарегистрирован: Сб сен 10, 2011 17:46:25

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение oleg110592 »

для автора: в тему использования Линукс для разработки, свежая статья
Настройка VSCODE под разработку для ARM на примере отладочной платы...
Можно посмотреть стек вызовов, а также просматривать и модифицировать регистры и память, ставить и удалять брейкпойнты, вычислять выражения и всё то же самое, что обычно при отладке можно.
https://habr.com/ru/post/437760/?utm_so ... ign=437760
з.ы. в кубе можно создавать makefile проект, после создания удаляем внутри папку с HAL, вычищаем от HAL файл main.c, немного правим сам make файл и получаем проект с самым свежим CMSIS. Открываем получившуюся папку в VS Code - создаем код, используя фишки современных IDE, и отлаживаем...
Аватара пользователя
dosikus
Друг Кота
Сообщения: 3604
Зарегистрирован: Пн июл 28, 2008 22:12:01

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение dosikus »

oleg110592, весьма сомнительное рукоблудство, Ставь лучше SES , все работает сразу искарапки ...
Аватара пользователя
oleg110592
Друг Кота
Сообщения: 3832
Зарегистрирован: Сб сен 10, 2011 17:46:25

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение oleg110592 »

ставил уже давно SES - отличная штука - у меня еще много прошивок stm8 ст-линком, придется еще один дебагер/программатор - итак на столе жмут кабелей с дебагерами в хаб - пылесосить не удобно. На работе на ПК лучше на всяк не ставить (FREE for any non-commercial use).
p/s/ SES вроде на основе кроссплатформенного Crossworks, а он вроде ст-линк поддерживает, за 100уе можно купить, пургену не видать.
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

Всем спасибо за советы. Остановился пока на связке 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 - компилятор пихает её в оперативку. Я явно чего-то не понимаю. Может нужно как-то явно указывать в какую область памяти я хочу определить переменную?
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение VladislavS »

[uquote="Satarych",url="/forum/viewtopic.php?p=3559681#p3559681"]Практической цели вопрос не несёт, просто любопытно.[/uquote]Удовлетворить своё любопытство можешь прочитав map-файл.

[uquote="Satarych",url="/forum/viewtopic.php?p=3559681#p3559681"]Может нужно как-то явно указывать в какую область памяти я хочу определить переменную?[/uquote]Для начало надо определиться, константа это или переменная. В принципе, под простую константу может и не выделяться память, если она где-то в простой формуле или пересылке используется - компилятор просто подставит её в код где надо.
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

[uquote="VladislavS",url="/forum/viewtopic.php?p=3559703#p3559703"]Удовлетворить своё любопытство можешь прочитав map-файл.[/uquote] Открыл блокнотом, посмотрел, но не могу похвастаться пониманием его содержимого)) О каких-то вещах догадываюсь, но это нельзя назвать пониманием.

[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;
}
Создаём 2 глобальных объекта (ножки, на которых висят светодиоды) и используем их в main
Спойлер

Код: Выделить всё

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();
	}
}
Всё работает, НО оба объекта в оперативке, причём компилятор суёт их в область ".bss". Тоесть при старте контроллера переменные объекта содержат нули, вызываются конструкторы объектов и переменные заполняют нужными значениями. Хотя конструктор пустой и все значения переменных доступны компилятору в списке инициализации. Почему компилятор не может сразу расположить этот объект в той части Flash памяти, из которой он будет их копировать в конструкторе?

На дурака решил провести следующий эксперимент:
Спойлер

Код: Выделить всё

// Описываем структуру, идентичную нашему классу по содержанию переменных
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();
	}
}
Понятно, что это дичайший костыль и нормальные люди так делать не будут, но в данном случае led1 уже находится во Flash и оно работает.
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Reflector »

[uquote="Satarych",url="/forum/viewtopic.php?p=3562348#p3562348"]Почему компилятор не может сразу расположить этот объект в той части Flash памяти, из которой он будет их копировать в конструкторе?[/uquote]
Я тебе уже в старой теме писал, что класс с приватными полями или конструкторами не является 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();
Добавлено after 16 minutes 52 seconds:
[uquote="Satarych",url="/forum/viewtopic.php?p=3562348#p3562348"]

Код: Выделить всё

// Функция изменяет состояние ножки на противоположное
void Stm32Gpio::tgl() const
{
	m_gpio->ODR ^= m_pinMask;
}
[/uquote]
Распространенная ошибка. В регистр читается значение ODR, потом вызывается прерывание или переключается задача и там что-то пишется в тот же порт, по возвращению в ODR сохраняется старое значение с измененным битом...
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

Начну с простого
[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]Распространенная ошибка. В регистр читается значение 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 компиляторе, либо он так делать не умеет, либо просто я не знаю как его об этом попросить.

Насчёт шаблонов. Я пробовал их использовать, но, видимо, до конца в них не разобрался.
Спойлер

Код: Выделить всё

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;
}
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 параметры.
Аватара пользователя
Мурик
Друг Кота
Сообщения: 3383
Зарегистрирован: Пн окт 11, 2010 19:00:08

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Мурик »

Satarych писал(а):нормальные люди так делать не будут
Почему? Массив структур имеет смысл. Лучше чем множество дефайнов и код короче и понятней.
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

[uquote="Мурик",url="/forum/viewtopic.php?p=3562631#p3562631"]
Satarych писал(а):нормальные люди так делать не будут
Почему? Массив структур имеет смысл. Лучше чем множество дефайнов и код короче и понятней.[/uquote]Я тут больше имел ввиду преобразование структуры в класс.
Во первых, в реальности мне в эту структуру нужно ещё как-то добавить __vptr.
Во вторых, как сказал Reflector, компилятор может приватные и публичные поля располагать в том порядке, какой ему больше понравится. Можно и не угадать)) Опасно так нагло преобразовывать типы.
Ответить

Вернуться в «ARM»