我正在尝试通过SPI协议与连接到STM32L1(在NUCLEO-L152RE板上)的BlueNRG芯片(在X-NUCLEO-IDB04A1扩展板上)进行通信。
根据BlueNRG手册,我可以发送一个5字节的空SPI数据包:(0x0B, 0, 0, 0, 0)
获取读/写缓冲区大小以及设备状态。如果设备处于睡眠状态并正在唤醒,则状态应为0x02
(就绪)或0x00
或0xFF
。
这是我得到的沟通:
Send (0x0B, 0, 0, 0, 0)
Receive (0x00, 0x00) // why 2 bytes?
Send (0x0B, 0, 0, 0, 0) // assuming device is waking up, re-trying
Receive (0x06, 0x00) // what is code 6?
我的测试应用程序是使用 Zinc 在 Rust 中编写的。MCU 运行默认时钟(MSI 为 2048 MHz)。以下是负责 SPI 初始化的代码:
// PB.3 = CLCK
let _spi_clock = pin::Pin::new(pin::PortB, 3,
pin::AltFunction(pin::AfSpi1_Spi2, pin::OutPushPull, pin::Medium),
pin::PullDown);
// PA.6 = MISO
let _spi_in = pin::Pin::new(pin::PortA, 6,
pin::AltFunction(pin::AfSpi1_Spi2, pin::OutPushPull, pin::Medium),
pin::PullNone);
// PA.7 = MOSI
let _spi_out = pin::Pin::new(pin::PortA, 7,
pin::AltFunction(pin::AfSpi1_Spi2, pin::OutPushPull, pin::Medium),
pin::PullNone);
// PA.1 = CS
let spi_csn = pin::Pin::new(pin::PortA, 1,
pin::GpioOut(pin::OutPushPull, pin::Medium),
pin::PullUp);
spi_csn.set_high();
let spi = spi::Spi::new(spi::Spi1, spi::DirFullDuplex, spi::RoleMaster,
spi::Data8b, spi::DataMsbFirst, 1); // baud pre-scaler = 2
let bnrg_reset = pin::Pin::new(pin::PortA, 8,
pin::GpioOut(pin::OutPushPull, pin::VeryLow),
pin::PullUp);
bnrg_reset.set_low();
// do something
bnrg_reset.set_high();
SPI内部结构由锌母版略有修改。发送/接收代码的组织方式如下:
loop {
spi_csn.set_low();
// send a dummy read request
spi.write(SpiRead as u8); // = 0x0B
spi.write(0);
spi.write(0);
spi.write(0);
spi.write(0);
let status = spi.read();
// debug print status
while spi.has_more_data() {
let data = spi.read();
// debug print data
}
spi_csn.set_high();
}
问题是 - 如何解释或治疗这些BlueNRG反应?我无法找到它使用的低级协议的任何正式描述(这里不是谈论HCI/ACI,而是谈论0x02
= ready以外的状态代码)。很可能我只是错误地初始化了硬件,甚至在这里错过了一些明显的东西。将不胜感激任何指导。
SPI 驱动程序似乎从根本上出了问题。它在存储传入字节的硬件或软件中是否有缓冲区,或者它是如何工作的?通常,您不使用SPI发送或接收,而是以全双工收发。SPI 驱动程序本身无法判断传入的数据是有效还是垃圾,因此不存在"发送 2 个字节时只收到 5 个字节"这样的事情。发送 5 个字节时,您始终会收到 5 个字节。
本应用中的MCU将是SPI主机,外部芯片将是从机。对于发送的每个字节,您还将收到一个字节。该字节是否有意义取决于外部芯片的工作方式。不幸的是,没有SPI标准,因此外部芯片可以说明各种延迟要求,例如"在SCK和MOSI上线之前,必须将从选择拉低x个时间单位"。您必须详细阅读外部芯片的手册。
然后,当所有这些混乱得到解决时,当SPI无法正常工作时,当然会有常见的嫌疑人:由于时钟极性或时钟相位设置不正确而导致时钟偏斜。
请参考ST UM1865,第5章:SPI接口。它适用于BlueNRG-MS(不是BlueNRG),但HCI/ACI接口是相同的。
如您所见,第一个字节是SPI READY指示,0x02表示从SPI接口已准备就绪。如果它是0x02以外的任何值,则主站必须忽略以下 4 个字节并中止 SPI 事务。
如果 BlueNRG(或 -MS)SPI 已就绪 (0x02),则以下 4 个字节提供 2 个缓冲区写入和读取大小:
- 第 2 个字节包含写入缓冲区大小(最大值为 127)
- 第 4 个字节包含读取缓冲区大小
在您的示例中,{0xb, 0, 0, 0, 0} 是 SPI 标头,仅用于检查 BlueNRG 的读取缓冲区大小。同样,{0xa, 0, 0, 0, 0} 用于检查写入缓冲区的大小。
如果返回的大小不是 0,就像您的示例 {0x6, 0} 一样,我认为它应该是 {0x6, 0, 0, 0},这意味着 BlueNRG 已准备好读取 6 (0 <<8 | 0x6) 字节的数据。然后,您应尽快从 BlueNRG 读取 6 个字节。
请参考 C:\Program Files (x86)\STMicroelectronics\BlueNRG DK 2.0.2\Projects\Drivers\BSP\STM32L1xx_BlueNRG\SDK_EVAL_Spi_Driver.c 函数 BlueNRG_SPI_Read_All()。