STM32 SPI 计算速度缓慢

5
我正在使用STM32F4和其SPI与74HC595通信,就像这个教程中所示。区别在于,为了简单起见,我使用的是非DMA版本。我使用STMCubeMX配置了SPI和GPIO。
问题是:我没有快速获取设置为PA8的锁存引脚的切换信号。

enter image description here

我正在使用的代码:
        spiTxBuf[0] = 0b00000010;

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);


        HAL_SPI_Transmit(&hspi1, spiTxBuf, 1, HAL_MAX_DELAY);
//        while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);

        HAL_Delay(1);

尝试的方法:

  1. 将引脚PA8的最大输出速度设置为非常快 enter image description here

  2. 等待SPI完成(见上面的注释行)

  3. 像这里一样使用DMA来进行SPI,但实际上速度更慢。

我该如何使其切换速度更快? 是否应该创建一个中断,当SPI完成时设置门闩?


那是哪个控制器,使用什么时钟频率?有什么要求,在之前/之后的可接受延迟是多少? - followed Monica to Codidact
正如@berendi在他的回答中所说,如果你使用HAL,请准备好那些函数会很慢。 - 0___________
2个回答

8

如何使它切换得更快?

如果可能的话,使用硬件NSS引脚

一些 STM32控制器可以自动切换其NSS引脚,在传输后具有可配置的延迟时间。 如果您的控制器是其中之一,请检查参考手册,将移位寄存器的锁存引脚重新连接到MCU上的SPIx_NSS引脚。

不要使用HAL库

对于任何需要严密时序要求的应用,HAL库过于缓慢和复杂化。不要使用它。

只需按照参考手册实现SPI传输程序即可。

SPI->CR1 |= SPI_CR1_SPE; // this is required only once
GPIOA->BSRR = 1 << (8 + 16);
*(volatile uint8_t *)&SPI->DR = 0b00000010;
while((SPI->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE)
    ;
GPIOA->BSRR = 1 << 8;

用实际的SPI控制器替换SPI,例如SPI1SPI2或您实际使用的任何控制器。 - followed Monica to Codidact
是啊,我已经理解了 :) 但找不到 SPI_SR_SPE 标志,即使在谷歌上也没有搜索结果。它的作用是什么? - Julian
哦,使用此代码不起作用: SPI1->SR |= SPI_CR1_SPE; // 只需要一次 GPIOA->BSRR = 1 << (8 + 16); *(volatile uint8_t *)SPI1->DR = 0b00000010; while((SPI1->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE); GPIOA->BSRR = 1 << 8; - Julian
@Julian,仔细看一下第一行。 - followed Monica to Codidact
搞定了!最后一个问题:我该如何在 for 循环中发送多个字节? - Julian
显示剩余5条评论

5

经过一些输入,我找到了一个解决方案,重新定义HAL函数,基本上抛弃了所有慢的部分:

void HAL_GPIO_WritePin_Fast(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{

    if(PinState != GPIO_PIN_RESET)
    {
        GPIOx->BSRR = GPIO_Pin;
    }
    else
    {
        GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;
    }
}

HAL_StatusTypeDef HAL_SPI_Transmit_fast(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
//    uint32_t tickstart = 0U;
    HAL_StatusTypeDef errorcode = HAL_OK;

    /* Check Direction parameter */

    /* Process Locked */
    __HAL_LOCK(hspi);

    /* Init tickstart for timeout management*/
//    tickstart = HAL_GetTick();

//    if(hspi->State != HAL_SPI_STATE_READY)
//    {
//        errorcode = HAL_BUSY;
//        goto error;
//    }
//
//    if((pData == NULL ) || (Size == 0))
//    {
//        errorcode = HAL_ERROR;
//        goto error;
//    }

    /* Set the transaction information */
    hspi->State       = HAL_SPI_STATE_BUSY_TX;
    hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
    hspi->pTxBuffPtr  = (uint8_t *)pData;
    hspi->TxXferSize  = Size;
    hspi->TxXferCount = Size;

    /*Init field not used in handle to zero */
    hspi->pRxBuffPtr  = (uint8_t *)NULL;
    hspi->RxXferSize  = 0U;
    hspi->RxXferCount = 0U;
    hspi->TxISR       = NULL;
    hspi->RxISR       = NULL;

    /* Configure communication direction : 1Line */
    if(hspi->Init.Direction == SPI_DIRECTION_1LINE)
    {
        SPI_1LINE_TX(hspi);
    }

#if (USE_SPI_CRC != 0U)
    /* Reset CRC Calculation */
  if(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SPI_RESET_CRC(hspi);
  }
#endif /* USE_SPI_CRC */

    /* Check if the SPI is already enabled */
    if((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
    {
        /* Enable SPI peripheral */
        __HAL_SPI_ENABLE(hspi);
    }

    /* Transmit data in 16 Bit mode */
    if(hspi->Init.DataSize == SPI_DATASIZE_16BIT)
    {
        if((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01))
        {
            hspi->Instance->DR = *((uint16_t *)pData);
            pData += sizeof(uint16_t);
            hspi->TxXferCount--;
        }
        /* Transmit data in 16 Bit mode */
        while (hspi->TxXferCount > 0U)
        {
            /* Wait until TXE flag is set to send data */
            if(__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
            {
                hspi->Instance->DR = *((uint16_t *)pData);
                pData += sizeof(uint16_t);
                hspi->TxXferCount--;
            }
            else
            {
//                /* Timeout management */
//                if((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >=  Timeout)))
//                {
//                    errorcode = HAL_TIMEOUT;
//                    goto error;
//                }
            }
        }
    }
        /* Transmit data in 8 Bit mode */
    else
    {
        if((hspi->Init.Mode == SPI_MODE_SLAVE)|| (hspi->TxXferCount == 0x01))
        {
            *((__IO uint8_t*)&hspi->Instance->DR) = (*pData);
            pData += sizeof(uint8_t);
            hspi->TxXferCount--;
        }
        while (hspi->TxXferCount > 0U)
        {
            /* Wait until TXE flag is set to send data */
            if(__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
            {
                *((__IO uint8_t*)&hspi->Instance->DR) = (*pData);
                pData += sizeof(uint8_t);
                hspi->TxXferCount--;
            }
            else
            {
//                /* Timeout management */
//                if((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >=  Timeout)))
//                {
//                    errorcode = HAL_TIMEOUT;
//                    goto error;
//                }
            }
        }
    }





    /* Clear overrun flag in 2 Lines communication mode because received is not read */
    if(hspi->Init.Direction == SPI_DIRECTION_2LINES)
    {
        __HAL_SPI_CLEAR_OVRFLAG(hspi);
    }
#if (USE_SPI_CRC != 0U)
    /* Enable CRC Transmission */
  if(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
     SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
  }
#endif /* USE_SPI_CRC */

    if(hspi->ErrorCode != HAL_SPI_ERROR_NONE)
    {
        errorcode = HAL_ERROR;
    }

    error:
    hspi->State = HAL_SPI_STATE_READY;
    /* Process Unlocked */
    __HAL_UNLOCK(hspi);
    return errorcode;
}

这肯定是一种选择,但可能不是最优雅的 :) 不过它显著提高了时间:

输入图像描述

编辑: berendis的解决方案更快:

输入图像描述

这里是用于多个字节的代码:

spiTxBuf[0] = 0b00000110;
spiTxBuf[1] = 0b00000111;
spiTxBuf[2] = 0b00000111;
spiTxBuf[3] = 0b00000111;
spiTxBuf[4] = 0b00000111;

GPIOA->BSRR = 1 << (8 + 16);
for(int i=0; i<5; i++){
    *(volatile uint8_t *)&SPI1->DR = spiTxBuf[i];
    while ((SPI1->SR & SPI_SR_TXE) == RESET);

}

while((SPI1->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE);
GPIOA->BSRR = 1 << 8;
HAL_Delay(100);

1
这是一些严重的膨胀软件...你所需要做的仅仅是:设置SPI,包括波特率和时钟。设置/SS引脚。循环开始。写入数据寄存器。等待状态标志。循环结束。清除/SS引脚。 - Lundin
2
我现在已经将这个问答加入书签了,因为它 完全 显示出 HAL 的低效。感谢提供示波器截图 :) - followed Monica to Codidact
如果我能让你的代码运行起来,那么我会再写第三个 :) 不用谢。 - Julian
又修复了一个拼写错误(缺少&)。 - followed Monica to Codidact
好的,成功了,你可以在你的回答中使用那个最后的截图 :) - Julian

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