Я видел, что эта тема обсуждалась во многих других вопросах, но я не могу найти ответ для своего конкретного случая.
Я работаю с микроконтроллером 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.
Другие предложенные решения, которые я нашел в переполнении стека, зависели от варианта использования.
Любое предложение?
Я бы предложил создать два конкретных указателя для работы. Вы можете создавать их во время инициализации или статически, чтобы вам не приходилось создавать их каждый раз.
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;
или что-то подобное.
Таким образом, похоже, что есть способ заставить GCC съесть тип-наказание без жалоб. Точно так же, как упоминал Рик в реальном времени. Мне также удалось подавить предупреждение
void* p = &_handle->Instance->DR;
(uint8_t*) p = val;
Я сделал шаг назад и пересмотрел то, что пытался сделать, чтобы окончательно решить просто отключить строгий псевдоним с FNo-строгий ступенчатости
Зачем? Насколько я понимаю, строгий псевдоним — это оптимизация, а не функциональное требование. Мое программное обеспечение предназначено для точного наложения типов, поэтому строгое наложение имен — это просто оптимизация, которую я не могу себе позволить. Или, по крайней мере, я считаю, что лучше отключить его, а не пытаться обмануть компилятор, полагая, что я не выполняю наказание за тип, пока я на самом деле это делаю.
я использую 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
Я надеюсь, что это будет полезно для вас.