如何在STM32F4上使用硬件NSS(SPI)?

8
我尝试使用NSS硬件信号和HAL库,但是我找不到任何函数来使NSS引脚具有低电平或高电平。我也尝试在HAL文档中寻找答案,但那里也没有任何信息。互联网上的所有示例都只包含软件NSS。
一个人应该如何使用硬件NSS?

如果您使用硬件NSS,则引脚状态由外设管理。您不需要在软件中显式驱动它。要这样做,只需使用SPI_InitTypeDef.NSS配置SPI_NSS_HARD_INPUT(如果您的设备是从设备)或SPI_NSS_HARD_OUTPUT(如果您的设备是主设备)。 - smertrios
我还没有找到一个SPI从机可以安全地允许硬件驱动NSS。通常只有通过软件GPIO来驱动该引脚才能满足CS的要求。 - Turbo J
它在SPI主模式下就像经典的CS一样不起作用。从参考手册中可以看到:“当主设备开始通信时,NSS信号被拉低,并保持低电平直到SPI被禁用。” - LennyB
我遇到了同样的问题;使用STM32F417作为SPI主机;当配置为HARD_OUTPUT时,输出始终为低电平(即选择从机)。在SOFT模式下工作正常,但这不是我想要的。HAL和硬件都没有报告任何错误。 - akohlsmith
5个回答

7

我在某处读到,只要SPI主机启用,NSS就会被拉低,并在禁用SPI主机时再次拉高。我尝试使用来自ST的STM32L476和轮询SPI1的HAL库(Cube/STM32CubeMX)。在传输之前进行初始化和之后进行去初始化并没有设置NSS引脚,但它花费了很长时间:

初始化结构:

hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

传输序列:

HAL_SPI_Init( &hspi1 );
HAL_SPI_TransmitReceive( &hspi1, btx, brx, l, 5 ); // timeout 5msec;
while( hspi1.State == HAL_SPI_STATE_BUSY );  // wait for xmission complete
HAL_SPI_DeInit( &hspi1 );

所以我决定手动设置引脚,使用GPIO(在初始化中使用SPI_NSS_SOFT):
HAL_GPIO_WritePin( NSS1_GPIO_Port, NSS1_Pin, GPIO_PIN_RESET ); // NSS1 low
HAL_SPI_TransmitReceive( &hspi1, btx, brx, l, 5 ); // timeout 5msec;
while( hspi1.State == HAL_SPI_STATE_BUSY );  // wait xmission complete
HAL_GPIO_WritePin( NSS1_GPIO_Port, NSS1_Pin, GPIO_PIN_SET ); // NSS1 high

我使用了阻塞传输(无DMA或中断),因为速度足够快,没有其他任务在等待。在20 MHz下,DMA设置需要不可接受的时间才能发送仅有的24个字节。这可能是一种可接受的替代方案。
就我所看到的STM32L4xx手册第38.4.12/13章节,自动NSS在每个字节/字传输后高电平,因此对于保持NSS低电平进行长时间传输来说并不适用。

1
看起来NSS信号被设计为带有上拉电阻的开漏输出;我认为当外设控制它时,该引脚会变成高阻态而不是高电平。将该引脚配置为备用功能、开漏输出和内部上拉似乎会使该引脚在外设启用时变低,在未启用时变高。外部上拉电阻也可以使用。 - Will

2
你可以将NSS引脚用作标准GPIO,并使用中断例程驱动它。你需要通过软件来完成这个部分。首先把NSS拉低,然后发送你的帧(HAL_SPI_Transmit)。
在从设备接收完整个数据帧之后,使用HAL_SPI_RxCpltCallback函数,在其中设置中断并将NSS引脚拉高。
不要忘记将GPIO引脚连接到从设备的NSS引脚上。

2

请查看您的STM32芯片参考手册。我不知道它们是否都相同,但根据我的(STM32WB55xx)和我能够找到的一个公共网页链接的手册(STM32F0)...

请参阅SPI功能描述中的NSS引脚管理(我提供的STM32F0文档的第28.5.5节),其中描述了三种模式:

  • 软件NSS管理(SPIx_CR1寄存器SSM位=1)。内部从设备选择信息由SPIx_CR1寄存器中的SSI位内部驱动。外部NSS引脚可供应用程序使用。

  • 硬件NSS管理(SSM位=0)。这有两种可能的配置,取决于SPIx_CR1寄存器中的SSOE位:

    • NSS输出使能(SSOE = 1)。仅在MCU为主时使用。在主模式下启用SPI(SPE = 1)后,NSS信号会被拉低,并保持低电平,直到禁用SPI(SPE = 0)。如果激活了NSS脉冲模式,则在连续通信之间可以生成脉冲。使用此NSS设置,SPI无法在多主配置中工作
    • NSS输出禁止(SSOE = 0)。如果MCU是主机,则允许多主机功能。如果NSS被拉低,则SPI进入主模式故障状态,并自动重新配置为从模式。在从模式下,NSS引脚作为标准的“芯片选择”输入,当NSS线处于低电平时选择从设备。

查看我的SoC的头文件,软模式为SSM=1(软件NSS管理)。硬输出模式为SSM=0和SSOE=1(NSS输出使能)。硬输入模式为全零(NSS输出禁用)。

如果您使用硬输出模式,您还可以查看NSS脉冲模式(STM32F0文档中的第28.5.12节)。它描述了系统如何在大多数时间内保持NSS低电平,在数据帧之间将其高电平脉冲。如果您的设备使用NSS / CS来同步数据帧,则可能会有用。或者,如果您的设备通过中止当前操作来响应NSS变为高电平,则可能不是很有用,因为该文本似乎表明它会在每个传输的字之间脉冲NSS,而不是在缓冲区之间。

不幸的是,这似乎不是最灵活的实现。根据您的应用程序,您可能会发现保持软模式并通过GPIO切换NSS引脚更容易。


2
“NSS”引脚可能需要上拉电阻,如果HiZ(未记录)。
(注:link1和link2是链接,无法直接翻译)

内部可用吗?也就是说,这是硬件配置的问题吗? - Peter Mortensen
很遗憾,内部不可用。需要一个物理上拉电阻器。 - Cris

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接