STM32 SPI и строгие предупреждения о псевдонимах

Я видел, что эта тема обсуждалась во многих других вопросах, но я не могу найти ответ для своего конкретного случая.

Я работаю с микроконтроллером STM32F0. Вершина FIFO приема / передачи SPI доступна посредством доступа к памяти. Этот конкретный микроконтроллер позволяет мне читать / писать 8 бит или 16 бит с вершины FIFO. Точнее говоря, когда выполняется команда LDRB / STRB, 8 битов извлекаются / выталкиваются из / в FIFO, а когда выполняется инструкция LDRH / STRH, 16 битов выталкиваются / выталкиваются из / в FIFO.

Уровень аппаратной абстракции, предоставляемый STMicroelectronic, предлагает этот синтаксис для чтения SPI FIFO.

return *(volatile uint8_t*)&_handle->Instance->DR; // Pop 1 byte
return *(volatile uint16_t*)&_handle->Instance->DR; // Pop 2 byte

*(volatile uint8_t*)&_handle->Instance->DR = val; // Push 1 byte
*(volatile uint16_t*)&_handle->Instance->DR = val; // Push 2 bytes

куда DR это uint32_t* указывая на вершину SPI FIFO

Я построил свое программное обеспечение, используя этот синтаксис, и он работает нормально. Единственная проблема заключается в том, что g ++ выдает много предупреждений о типах. Точнее:

Inc / drivers / SPI.h: 70: 50: предупреждение: разыменование указателя типа-наказание нарушит правила строгого псевдонима [-Wstrict-aliasing] return *(volatile uint16_t*)&_handle->Instance->DR;

После некоторых чтений кажется, что использование union не является хорошей идеей в C ++. Я все равно попробовал, но у меня возникли проблемы. На самом деле доступ к памяти через указатель в объединении приводит к сбою моего микроконтроллера, так же как и к выравниванию доступа к памяти.

static_cast и reinterpret_cast выдают то же предупреждение, что и в стиле C

Я не могу использовать memcpy с void* поскольку моя конечная цель — заставить компилятор использовать инструкции LDRB / STRB и LDRH / STRH.

Другие предложенные решения, которые я нашел в переполнении стека, зависели от варианта использования.

Любое предложение?

4

Решение

Я бы предложил создать два конкретных указателя для работы. Вы можете создавать их во время инициализации или статически, чтобы вам не приходилось создавать их каждый раз.

static uint8_t * const DR_Byte = (uint8_t * const)&_handle->Instance->DR;
static uint16_t * const DR_Word = (uint16_t * const)&_handle->Instance->DR;

тогда просто прочитайте:

uint8_t read_byte = *DR_Byte;
uint16_t read_word = *DR_Word;

и напишите:

*DR_Byte = byte_to_write;
*DR_Word = word_to_write;

или что-то подобное.

2

Другие решения

Таким образом, похоже, что есть способ заставить GCC съесть тип-наказание без жалоб. Точно так же, как упоминал Рик в реальном времени. Мне также удалось подавить предупреждение

void* p = &_handle->Instance->DR;
(uint8_t*) p = val;

Я сделал шаг назад и пересмотрел то, что пытался сделать, чтобы окончательно решить просто отключить строгий псевдоним с FNo-строгий ступенчатости

Зачем? Насколько я понимаю, строгий псевдоним — это оптимизация, а не функциональное требование. Мое программное обеспечение предназначено для точного наложения типов, поэтому строгое наложение имен — это просто оптимизация, которую я не могу себе позволить. Или, по крайней мере, я считаю, что лучше отключить его, а не пытаться обмануть компилятор, полагая, что я не выполняю наказание за тип, пока я на самом деле это делаю.

1

я использую LL API по STM вместо HAL. Часть /STM32F0xx_LL_Driver/inc/stm32f0xx_ll_spi.h Файл ниже:

/**
* @brief  Read 8-Bits in the data register
* @rmtoll DR           DR            LL_SPI_ReceiveData8
* @param  SPIx SPI Instance
* @retval RxData Value between Min_Data=0x00 and Max_Data=0xFF
*/
__STATIC_INLINE uint8_t LL_SPI_ReceiveData8(SPI_TypeDef *SPIx)
{
return (uint8_t)(READ_REG(SPIx->DR));
}

/**
* @brief  Read 16-Bits in the data register
* @rmtoll DR           DR            LL_SPI_ReceiveData16
* @param  SPIx SPI Instance
* @retval RxData Value between Min_Data=0x00 and Max_Data=0xFFFF
*/
__STATIC_INLINE uint16_t LL_SPI_ReceiveData16(SPI_TypeDef *SPIx)
{
return (uint16_t)(READ_REG(SPIx->DR));
}

/**
* @brief  Write 8-Bits in the data register
* @rmtoll DR           DR            LL_SPI_TransmitData8
* @param  SPIx SPI Instance
* @param  TxData Value between Min_Data=0x00 and Max_Data=0xFF
* @retval None
*/
__STATIC_INLINE void LL_SPI_TransmitData8(SPI_TypeDef *SPIx, uint8_t TxData)
{
*((__IO uint8_t *)&SPIx->DR) = TxData;
}

/**
* @brief  Write 16-Bits in the data register
* @rmtoll DR           DR            LL_SPI_TransmitData16
* @param  SPIx SPI Instance
* @param  TxData Value between Min_Data=0x00 and Max_Data=0xFFFF
* @retval None
*/
__STATIC_INLINE void LL_SPI_TransmitData16(SPI_TypeDef *SPIx, uint16_t TxData)
{
*((__IO uint16_t *)&SPIx->DR) = TxData;
}

Там READ_REG это макросы из /STM32F0xx_LL_Driver/inc/stm32f0xx.h файл и определяется как:

#define READ_REG(REG)         ((REG))

А как насчет вашей проблемы, когда вы получаете доступ к spi-данным, регистрируясь этим _handle->Instance->DR конструкция вы уже разыменование указатель Instance и там DR является volatile uint32_t, Так что вам просто нужно привести, и это должно работать:

return (uint8_t)_handle->Instance->DR;
return (uint16_t)_handle->Instance->DR;

И, наконец, о невыровненном доступе: я не знаю, как это можно гарантировать, но это нужно сделать для работы с микроконтроллерами ARM. Мой автоматически сгенерированный скрипт компоновщика содержит инструкции . = ALIGN(4); в каждом разделе:

.rodata :
{
. = ALIGN(4);
*(.rodata)         /* .rodata sections (constants, strings, etc.) */
*(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH

Я надеюсь, что это будет полезно для вас.

1
По вопросам рекламы [email protected]