STM32 SPI 慢速计算

Posted

技术标签:

【中文标题】STM32 SPI 慢速计算【英文标题】:STM32 SPI Slow Compute 【发布时间】:2019-10-19 18:50:39 【问题描述】:

我正在使用 STM32F4 及其 SPI 与本教程中的 74HC595 通信。区别在于初学者我使用非 DMA 版本为简单起见。我用STMCubeMX配置SPI和GPIO

问题是:我没有获得闩锁 PIN,我将其设置为 PA8,以便在传输过程中足够快地切换。

我正在使用的代码:

        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);

我尝试过的事情:

    将引脚 PA8 的最大输出速度设置为非常快

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

    在此处对 SPI 使用 DMA,这实际上使其变慢了。

如何让它更快地切换?我应该在 SPI 完成时创建和中断并在那里设置锁存器吗?

【问题讨论】:

那是哪个控制器,时钟频率是多少?有什么要求,之前/之后可接受的延迟? @berendi 在他的回答中写道,如果您使用 HAL,请准备好这些功能会变慢。 【参考方案1】:

如何让它更快地切换?

如果可能,请使用硬件 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 或您实际使用的任何一个。 是的,我想通了 :) 虽然找不到 SPI_SR_SPE 标志,但谷歌也一无所获。它有什么作用? @Julian 抱歉,打错了。代码现已修复。 嗯,不适用于此代码:SPI1->SR |= SPI_CR1_SPE; // 这只需要一次 GPIOA->BSRR = 1 DR = 0b00000010;而((SPI1->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE); GPIOA->BSRR = 1 @Julian 仔细看第一行【参考方案2】:

所以经过一些输入后,我想出了一个解决方案,我重新定义了 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);

【讨论】:

这是一些严重的臃肿软件......对于单工通信,您需要做的就是:设置 SPI,包括波特率和时钟,一次。设置 /SS 引脚。循环开始。写入数据寄存器。等待状态标志。循环结束。清除 /SS 引脚。 我现在已将此问答添加为书签,因为它准确地显示了 HAL 的低效程度。感谢您的范围拍摄:) 不客气,如果我让你的代码运行,我会做第三个 :) 修复了另一个错字(缺少&amp; 是的,那行得通,你可以偷最后一张截图来回答:)

以上是关于STM32 SPI 慢速计算的主要内容,如果未能解决你的问题,请参考以下文章

使用STM32cubeProgrammer进行外部FLASH慢速验证

STM32F103 SPI 通信问题!

请教STM32的SPI通信中的CRC校验问题 SPI

请教STM32F103与DSP采用SPI通信的问题!

linux驱动调用spi标准函数spi_sync发送速率慢的问题

stm32的spi如何抗干扰