我正在 STM32F207 Nucleo 板上测试一个简单的 SPI-Slave。它是没有硬件 NSS 的 2 线仅接收 SPI 从机。这是我的 SPI 初始化:
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_SLAVE;
hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
HAL_SPI_Init(&hspi1);
Slave 在 Interrupt 下工作,有一个非常简单的中断服务例程。一个额外的测试引脚显示我的逻辑分析仪上的中断活动。
void SPI1_IRQHandler(void)
{
HAL_GPIO_WritePin (GPIOE, SPI_IRQ_Pin, GPIO_PIN_SET);
shift_reg = (shift_reg << 8) | SPI1->DR;
++SPI_byte_cnt;
HAL_GPIO_WritePin (GPIOE, SPI_IRQ_Pin, GPIO_PIN_RESET);
}
主机在 4 字节传输结束时发送选通信号,而不是 SPI-NSS 信号。从站有一个额外的中断服务(EXTI-Callback)来响应这个选通信号。同样,测试引脚显示中断活动。
void vmShiftreg_Latch()
{
HAL_GPIO_WritePin (GPIOE, STR_IRQ_Pin, GPIO_PIN_SET);
if (Check_SPI (4)) {
shift_latch = shift_reg;
}
shift_reg = 0;
HAL_GPIO_WritePin (GPIOE, STR_IRQ_Pin, GPIO_PIN_RESET);
}
此中断服务还检查 SPI 状态和传输的字节数:
int Check_SPI (int bytes_needed)
{
int byte_read = SPI_byte_cnt;
SPI_byte_cnt = 0;
if ((SPI1->SR & SPI_SR_OVR ) != 0) {
shift_reg = SPI1->DR;
shift_reg = SPI1->SR;
++ovr_error_cnt;
return 0;
}
if ((SPI1->SR & SPI_SR_BSY) != 0) {
HAL_GPIO_WritePin (GPIOE, BSY_ERR_Pin, GPIO_PIN_SET);
++bsy_error_cnt;
Recover_SPI1();
HAL_GPIO_WritePin (GPIOE, BSY_ERR_Pin, GPIO_PIN_RESET);
return 0;
}
if (byte_read != bytes_needed) {
++shift_error_cnt;
return 0;
}
return 1;
}
现在有时会发生 BSY 标志保持活动状态并且没有任何东西,甚至没有额外的 SPI 时钟,可以重置这个忙碌标志。这有时会在 100000 次正确传输之后或有时在 500 次之后非常随机地发生。 是的,我用示波器一次又一次地检查了 SPI 信号(时钟和数据),但它们很干净,没有任何尖峰。对我来说这似乎很疯狂,即使是数百个额外的 SPI 时钟也无法重置 bsy 信号。
让他回来的唯一解决方案是强制重置 SPI,这是我在这篇post中发现的:
__HAL_RCC_SPI2_FORCE_RESET();
__HAL_RCC_SPI2_RELEASE_RESET();
有人见过这么疯狂的行为吗? 如果我无法找出问题所在,我想我别无选择,只能在每次传输后重置 SPI。
这是我的逻辑分析仪的两张截图。第一个正确传输,第二个显示挂着忙标志。
有更新: 我在从站上使用的额外时钟连接来生成 SPI 时钟无法正常工作。现在我可以看到需要额外的 8 个时钟才能使 BSY 标志变低。换句话说,SPI 确实在内部开始了一个新的帧,即使最后一帧没有额外的时钟。