TL; DR Не используйте HAL, напишите свои функции передачи, используя Справочное руководство.
HAL безнадежно усложняется для срочных задач (среди прочих).Достаточно взглянуть на функцию HAL_SPI_Transmit()
, это более 60 строк кода, пока не дойдет до фактического касания регистра данныхHAL сначала помечает структуру доступа к порту как занятую, даже когда многозадачной ОС не видно, проверяет параметры функции, сохраняет их в структуре hspi
без видимой причины, затем продолжает выяснять, в каком режиме находится SPI и т. Д.Также нет необходимости проверять таймауты в режиме мастера SPI, потому что мастер контролирует все тайминги шины, если он не может получить байт за конечное время, то инициализация порта неверна, точка.
Без HAL все намного проще.Во-первых, выясните, что должно входить в управляющие регистры, установите CR1
и CR2
соответственно.
void SPIx_Init() {
/* full duplex master, 8 bit transfer, default phase and polarity */
SPIx->CR1 = SPI_CR1_MSTR | SPI_CR1_SPE | SPI_CR1_SSM | SPI_CR1_SSI;
/* Disable receive FIFO, it'd complicate things when there is an odd number of bytes to transfer */
SPIx->CR2 = SPI_CR2_FRXTH;
}
Эта инициализация предполагает, что выбор ведомого (NSS
или CS#
) обрабатывается отдельнымGPIO контакты.Если вы хотите, чтобы CS#
управлялось периферийным устройством SPI, найдите в справочном руководстве Управление выводами Slave Select (NSS) .
Обратите внимание, что полнодуплексное соединение SPI не может просто передаватьили получить, он всегда делает оба одновременно.Если ведомый ожидает один командный байт и отвечает четырьмя байтами данных, это 5-байтовая передача, ведомый игнорирует последние 4 байта, мастер должен игнорировать первый.
Очень простая передачафункция будет
void SPIx_Transfer(uint8_t *outp, uint8_t *inp, int count) {
while(count--) {
while(!(SPIx->SR & SPI_SR_TXE))
;
*(volatile uint8_t *)&SPIx->DR = *outp++;
while(!(SPIx->SR & SPI_SR_RXNE))
;
*inp++ = *(volatile uint8_t *)&SPIx->DR;
}
}
При необходимости она может быть дополнительно оптимизирована путем использования SPI fifo, чередования операций записи и чтения, так что передатчик всегда будет занят.
Если скоростькритические, не используйте обобщенные функции, или убедитесь, что они могут быть встроены, когда вы делаете.Используйте компилятор с включенной оптимизацией по времени компоновки и оптимизируйте по скорости (вполне очевидно).