Как стать автором
Обновить
2
0

Пользователь

Отправить сообщение

Эти макросы берутся из хидера от STMicroelectronics, конечно это не исключает возможных ошибок, но шансы столкнуться с ошибками в них довольно малы т.к. эти контроллеры очень широко используют. Разве что какой-нибудь диверсант решит переопределить макрос, думаю что заморочки такого плана могут иметь смысл только в устройствах от которых напрямую зависит безопасность людей.
А насчет else if, понятно что тут лишняя проверка в одном из вариантов, но ведь это выражение все равно будет вычислено на этапе компиляции. Честно говоря else if мне не понравился тем что портит выравнивание, увеличивает объем кода и при этом никак не влияет на прошивку, которая окажется в контроллере, так что я оставил просто if :-)

Например, в микроконтроллерах STM есть Bit Banding (атомарная модификация отдельных битов), который полезен для модификации некоторых регистров, но он поддерживается не во всем адресном пространстве, т.е. по хорошему нужно делать валидацию чтобы не дебажить потом часами устройство из-за одной опечатки.
Но валидация во время выполнения впустую тратит процессорное время т.к. в большинстве случаев нужный адрес известен уже на этапе компиляции, а самое главное, валидация занимает место во flash памяти, которой зачастую не так много как хотелось бы.
В данном случае constexpr подходит просто идеально, позволяя абсолютно бесплатно выполнить вычисление нужного адреса и его валидацию еще на этапе компиляции.
PS: На мой взгляд constexpr - фича крайне полезная, в моем случае она позволила убрать кучу проверок и вычислений из рантайма и уменьшить объем прошивки примерно на 20%.

Пример кода
template<uint32 address, uint32 bitMask>
constexpr uint32 GET_BIT_BAND_ADDR()
{
	// Области памяти поддерживающие bit-banding
	constexpr uint32 ONE_MEGABYTE = 0xFFFFC;
	constexpr mem_region sram_area = mem_region(SRAM_BASE, SRAM_BASE + ONE_MEGABYTE);
	constexpr mem_region periph_area = mem_region(PERIPH_BASE, PERIPH_BASE + ONE_MEGABYTE);
	static_assert(sram_area.contains<address>() || periph_area.contains<address>(), "Invalid address");
	
	// Проверяем что установлен только один бит
	static_assert(IsPowOf2<bitMask>(), "Invalid bitMask");

	constexpr auto bitNumber = LSB_Number<bitMask>();
	uint32 bitAddress = 0;
	if (sram_area.contains<address>())
	{
		constexpr auto bitBandBaseAddress = SRAM_BASE + 0x2000000;
		bitAddress = bitBandBaseAddress + (address - SRAM_BASE) * sizeof(uint32) * 8 + bitNumber * sizeof(uint32);
	}
	if (periph_area.contains<address>())
	{
		constexpr auto bitBandBaseAddress = PERIPH_BASE + 0x2000000;
		bitAddress = bitBandBaseAddress + (address - PERIPH_BASE) * sizeof(uint32) * 8 + bitNumber * sizeof(uint32);
	}
	return bitAddress;
}
Да, все верно, я не стал экспериментировать с чтением по 4 байта т.к. других проблем хватало, поэтому что будет в этом случае не знаю, да и в даташите про USB четко написано «Dedicated packet buffer memory SRAM access scheme: 1 x 16bit / word».
Поэтому у меня так же чтение/запись по 2 байта, только инкремент указателя на 2 приходится делать. И при чтении я тоже копирую из PMA во внешний буфер, хотя это делалось больше из соображений передачи данных которые могут превышать размер буфера в PMA.
Мне очень помогла эта статья, правда она на немецком и код у них довольно сильно запутан из-за того что все в кучу намешано, но некоторые моменты с логикой работы USB действительно удалось подсмотреть там.
Несколько месяцев назад успешно завершил написание своего USB CDC велосипеда на регистрах, тоже использовал C++, constexpr и шаблоны, правда у меня не настолько все зашаблонизировано.
Для размещения буферов мне показалось проще определить в линкере новую секцию для PMA, дальше достаточно определить обычную переменную с атрибутом __attribute__((section(".usb_pma"), used)) и она автоматом попадет куда нужно, без дополнительных телодвижений.
Правда окончательный адрес и размер буфера для записи в регистры все равно приходится вычислять на этапе выполнения т.к. память PMA мапится на шине контроллера кусками по 2 байта которые выровнены на границу 4 байта (проще говоря с пропусками, ну во всяком случае для STM32F303VCT6), а в constexpr невозможно делить адреса переменных.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность