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) pin management。
请注意,全双工 SPI 连接不能只发送或接收,它总是同时进行。如果从机需要一个命令字节,并以 4 字节数据应答,即 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,交错写入和读取,使发送器始终保持忙碌。
如果速度很关键,请不要使用泛化函数,或者确保在使用时可以内联它们。使用启用了链接时优化的编译器,并优化速度(很明显)。