STM32 SPI 慢速计算
STM32 SPI Slow Compute
我正在使用 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 完成时创建和中断并将锁存器设置在那里吗?
How do i get that to toggle faster?
如果可能,使用硬件NSS pin
一些 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;
所以在输入一些信息后,我想出了一个解决方案,我重新定义了 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);
我正在使用 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 完成时创建和中断并将锁存器设置在那里吗?
How do i get that to toggle faster?
如果可能,使用硬件NSS pin
一些 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;
所以在输入一些信息后,我想出了一个解决方案,我重新定义了 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);