Почему SPI на STM32F0 и STM32G0 отправляет 16 бит вместо 8?
Обновлено Редакция GAW
У STM32F0 и STM32G0 модуль SPI устроен иначе, чем у F1 и F4: в нём есть FIFO на 32 бита, размер кадра задаётся полем DS от 4 до 16 бит, и данные упаковываются. Код, перенесённый с STM32F103, из-за этого отправляет лишние байты или зависает на приёме.
Упаковка данных
По RM0091 (с. 780) и RM0444 (с. 1137), если кадр не длиннее 8 бит, каждая 16-битная запись в SPIx_DR кладёт в FIFO два кадра — сначала младший байт, потом старший. В CMSIS регистр DR объявлен 32-битным, поэтому SPI1->DR = 0x5A компилятор превращает в запись шириной 32 бита. Наш вывод: для модуля это обращение шире байта, оно попадает под упаковку, и наружу уходят 0x5A и 0x00.
| Обращение к DR при DS = 8 бит | Что происходит |
|---|---|
| 16-битная запись в DR | два кадра: b и 0x00 (RM0091) |
SPI1->DR = b (32 бита в CMSIS) | так же, два кадра — наш вывод |
*(__IO uint8_t *)&SPI1->DR = b | один кадр |
| 16-битное чтение при FRXTH = 0 | два принятых кадра |
| 8-битное чтение при FRXTH = 1 | один кадр, RXNE на каждый байт |
Так же, через указатель на байт, обращается к DR и HAL ST для кадров до 8 бит.
Правильная настройка
SPI1->CR2 = (7U << SPI_CR2_DS_Pos) // 8-bit frame
| SPI_CR2_FRXTH; // RXNE at 8 bits
uint8_t spi_xfer(uint8_t b) {
while (!(SPI1->SR & SPI_SR_TXE)) {}
*(__IO uint8_t *)&SPI1->DR = b;
while (!(SPI1->SR & SPI_SR_RXNE)) {}
return *(__IO uint8_t *)&SPI1->DR;
}
Код DS = 0111 — 8 бит; коды 0000–0010 запрещены и превращаются в 8 бит. Без FRXTH флаг RXNE ждёт 16 бит в FIFO, и функция выше зависнет.
Где посмотреть
Код инициализации с FRXTH и байтовым доступом выдаёт калькулятор частоты SPI STM32 для STM32F0 и G0. Особенности семейств — на страницах STM32F0 и STM32G0, а как выглядит кадр на линиях, нарисует калькулятор кадра SPI. Если ведомый отвечает одними 0xFF, причина обычно в подключении — см. «Почему SPI читает только 0xFF».
Похожие вопросы
Почему такой же код работает на STM32F103?
У STM32F1 и F4 нет FIFO и упаковки: размер кадра — 8 или 16 бит по биту DFF, и запись в DR при DFF = 0 отправляет один байт, сколько бы бит ни было в обращении. У F0 и G0 модуль SPI другой — с FIFO на 32 бита и полем DS для кадров от 4 до 16 бит.
Что будет без FRXTH, если читать по одному байту?
По RM0091 при FRXTH = 0 флаг RXNE ставится, когда в приёмном FIFO накопилось 16 бит. Если программа после каждого отправленного байта ждёт RXNE, она зависнет на первом же байте или будет читать с опозданием на кадр. ST требует согласовать порог FRXTH с размером чтения DR.
Можно ли пользоваться упаковкой специально?
Да: если кадры 8-битные, одной 16-битной записью в DR можно отправить два байта, младший первым, а 16-битным чтением при FRXTH = 0 забрать два принятых. Это сокращает число обращений к регистру при больших передачах, но ломает побайтный код — его надо писать с учётом упаковки (наш вывод).
Источники
- STMicroelectronics. RM0091 Rev 10 (STM32F0x1/x2/x8) и RM0444 Rev 6 (STM32G0x1), reference manuals — RM0091 с. 780, RM0444 с. 1137 — Data packing: кадр не длиннее байта, 16-битное обращение к SPIx_DR передаёт два кадра (сначала младший байт); RM0091 с. 816 — FRXTH: 0 — RXNE при заполнении FIFO от 1/2 (16 бит), 1 — от 1/4 (8 бит); с. 777 — порог FRXTH согласуют с размером чтения DR; с. 817 — DS[3:0]: 0111 — 8 бит, 1111 — 16 бит, 0000–0010 превращаются в 8 бит
- STMicroelectronics. STM32CubeF0 и STM32CubeG0 HAL: stm32f0xx_hal_spi.c, stm32g0xx_hal_spi.c — для кадров до 8 бит HAL пишет и читает DR через *(__IO uint8_t *)&hspi->Instance->DR; в CMSIS cmsis-device-f0 (stm32f030x6.h) поле DR структуры SPI_TypeDef объявлено как __IO uint32_t
- STMicroelectronics. RM0360 Rev 5 (STM32F030/F070) — с. 666 — DS[3:0] в SPI_CR2, тот же порядок кодов
Нашли ошибку?
Смотрите также
- Делитель SPI STM32BR[2:0], шина APB, предел SCK по даташиту, режимы и код
- STM32F0Cortex-M0, до 48 МГц: F030/F070, F031–F091, F042/F072, F0x8 — линейки и распиновки
- STM32G0Cortex-M0+, до 64 МГц: G030–G0B0 Value line, G031–G0C1 — линейки и распиновки
- Кадр SPIДиаграмма режимов 0–3, время передачи, предельная частота