STM32_SPI通信_与传感器芯片通信的SPI优化之路

先讲个题外话,仅用于笔记,可跳过,问题:系统断电复位,方位轴ICM-26090芯片的数据常出现无法读取传感器数据或读取到的传感器数据错误异常的情况。乱试找到了一个解决办法:在初始化拉高片选信号线的时候,HAL_Delay(500); 这个解决办法都很片面,应该并不是本质的办法是。下面的操作我猜测才是从本质上解决问题的,即通过芯片的电源管理,先把加速度计和陀螺disable,然后开始配置好,再enable。(传感器芯片上没有讲这样用,有点坑)

	SPI_E_WRITE(PWR_MGMT_2_ADDR_W,0x3F);	
	// Disable 3-axis accelerometer and 3-axis gyro before configuration
	HAL_Delay(100);	// This Delay is important 
	
	SPI_E_WRITE(CONFIG_ADDR_W,0x00);
	SPI_E_WRITE(GYRO_CONFIG_ADDR_W,0x00);
	SPI_E_WRITE(ACCEL_CONFIG_ADDR_W,0x00);
	SPI_E_WRITE(ACCEL_CONFIG_2_ADDR_W,0x37);
	SPI_E_WRITE(LP_MODE_CONFIG_ADDR_W,0x70);
	SPI_E_WRITE(FIFO_EN_ADDR_W,0xF8);
	SPI_E_WRITE(ODR_DELAY_EN_ADDR_W,0x00);
	SPI_E_WRITE(INT_ENABLE_ADDR_W,0x00);
	SPI_E_WRITE(FIFO_WM_TH_ADDR_W,0x00);	

	SPI_E_WRITE(PWR_MGMT_1_ADDR_W,0x00);
	
	SPI_E_WRITE(PWR_MGMT_2_ADDR_W,0x00);	
	// Enable 3-axis accelerometer and 3-axis gyro after configuration

上面谈到了如何才能正常的读取芯片的数据,下面步入正题,STM32芯片的SPI通信效率的优化。

优化尝试1、提升通信时钟频率

目前最有效的方法是提升SPI Baud Rate(通信时钟频率),但是提升也是极限的,与STM32通信的传感器芯片(从器件),它本身有一个时钟频率的极限,你不能超过从器件能够承受的时钟极限。

 预分频器原本是16,改成4后,波特率(时钟)提升到原来的4倍,同样的代码,运行时间从189us,降到了126.6us,运行效率提升了30%。

优化尝试2、单字节访问改为多字节访问。

读取传感器芯片某一个寄存器的数据,需要发送一个包含(最高位1+低七位为寄存器地址)的一个字节的数据,然后再接收一个字节数据。 如果尝试连续发送要读的地址,连续接收呢?

这种做法,时间上是优化的(45us),效果贼明显,然(收到的数据全是0)并卵。虽然没用,但是我们发现的现象给了我们启示,单字节的通信方式效率比多字节的通信方式效率低很多,说明什么? 说明HAL库的SPI通信封装存在很多冗余的东西。 不然我单字节通信何多字节通信效率不应该相差这么多倍。

因为SPI要操作的芯片是,它收到一个指令 读指令+地址,中间需要一个间隔时间(芯片自身的运算),然后把你要读的地址的寄存器数据发给你。 你如果连续发送很多命令,那么芯片反应不过来。 则导致这样的做法是不成功的(这是我当时当下的理解,请续看后文)。

优化尝试3、SPI+DMA,并不是YYDS

我一直想看看能不能用DMA去操作SPI。 毕竟在前面的学习串口通信的过程中发现,DMA真的太香了。网上相关资料比较少,现在想来,资料少也是有一定道理的。为此我还请教了一些有经验的前辈,发现原来STM32和DSP一样,也是有example这种东西的,只能去边学边用。参考:STM32 HAL库手册获取和查阅方法以及查看官方例程_何事误红尘的博客-CSDN博客_hal库函数中文手册。我尝试发送用HAL_SPI_Transmit_DMA发送, 接收还是用HAL_SPI_Receive接收,结果运行时间飙到207啦(不过话说207us 已经大于 200us(5k), 怎么定时器还在正常工作呢? ),我以为我在朝着正确的方向前进,结果我在反向优化?

	HAL_SPI_Transmit_DMA(&hspi1,&addr,1);
//HAL_SPI_Transmit(&hspi1,&addr,1,3);
	HAL_SPI_Receive(&hspi1,Rxdata,1,3);

接下来尝试把DMA接收试一下。

	Errornum = HAL_SPI_Transmit_DMA(&hspi1,&Tx,1);
	while((hspi1.Instance->SR & SPI_SR_RXNE)!=RESET);
	while((hspi1.Instance->SR & SPI_SR_BSY)!=RESET);
	Errornum = HAL_SPI_Receive_DMA(&hspi1,&Rx,1);

针对传感器芯片SPI通信的DMA解决方案真的很难搞,弄完了才发现,效率真的是感人!! 207us 变到 310us,我正在朝着反向优化的方向,渐行渐远。

优化尝试4、使用HAL_SPI_TransmitReceive优化

【流水账警告】

虽然是流水账,但是每一步都对优化有启发。之前一直尝试 HAL_SPI_TransmitReceive函数没成功,尝试HAL_SPI_TransmitReceive_DMA也没成功。数据是收到了的,但是不对。全是00。应该是时间没对齐。

但从 HAL_SPI_Receive 函数的源码里受到了启发

HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
#if (USE_SPI_CRC != 0U)
  __IO uint32_t tmpreg = 0U;
#endif /* USE_SPI_CRC */
  uint32_t tickstart;
  HAL_StatusTypeDef errorcode = HAL_OK;

  if ((hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES))
  {
    hspi->State = HAL_SPI_STATE_BUSY_RX;
    /* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */
    return HAL_SPI_TransmitReceive(hspi, pData, pData, Size, Timeout);
  }
   ...
   // 也就是说 HAL_SPI_Receive本质上调用就是函数HAL_SPI_TransmitReceive(hspi, pData, pData, Size, Timeout);

下面这段话也是我后面才理解到的: HAL_SPI_TransmitReceive它本身没有问题,只是我对它的理解有问题。我以为我用它就能传感器芯片发一个(读命令+寄存器地址)的字节,收到一个对于地址的寄存器数据字节。 但是后面才发现,和传感器芯片通信的流程是,发送一个(读命令+寄存器地址)的字节,接收一个内容为0x00字节, 发送任意字节(一般就为0x00)给传感器,接收到真正想要读取的寄存器的字节。 简单说,就是发两个字节,第一个有用第二个没用,接收两个字节,第一个没用,第二个有用。

从HAL_SPI_Receive 函数源码可以推断,用HAL_SPI_TransmitReceive 替换HAL_SPI_Receive,效率肯定更高。我确实对DMA还没有死心,用HAL_SPI_TransmitReceive_DMA 替换 HAL_SPI_Receive_DMA 试试

	spi_cs_A_Enable;
	Tx = 0xF5;
	Rx = 0x00;
	HAL_SPI_Transmit_DMA(&hspi1,(uint8_t*)&Tx,1);
	HAL_SPI_TransmitReceive_DMA(&hspi1,(uint8_t*)&Tx,(uint8_t*)&Rx,1);
	//HAL_SPI_TransmitReceive(&hspi1,(uint8_t*)&Tx,(uint8_t*)&Rx,1,3);
	spi_cs_A_Disable;	

效率也感人,310us 变到 280us。

相信前面的结果大家也看到了, SPI用DMA的效率 没有 直接用轮询高。

但是刚才的现象我们发现,直接 HAL_SPI_TransmitReceive 当成HAL_SPI_Receive 来用是可以提升效率的。

所以,我们把代码进一步优化

void SPI_A_READ(unsigned char addr, unsigned char * Rxdata){
		spi_cs_A_Enable;
		HAL_SPI_Transmit(&hspi1,&addr,1,3);
		HAL_SPI_TransmitReceive(&hspi1,&addr,Rxdata,1,3);
		spi_cs_A_Disable;
}

从原来的116.9us (删除了点点冗余代码把126.6us变到了116.9us) 降到了 108us, 虽然没有什么提升,但是聊胜于无。

进一步发散思路,用两个TransmitReceive可不可以呢? 答案是肯定的,不过效率是更低了,125.8us

void SPI_A_READ(unsigned char addr, unsigned char * Rxdata){
		spi_cs_A_Enable;
		HAL_SPI_TransmitReceive(&hspi1,&addr,Rxdata,1,3);
		HAL_SPI_TransmitReceive(&hspi1,&addr,Rxdata,1,3);
		spi_cs_A_Disable;
}

▲ 从下面的尝试开始, 优化思路终于步入正向优化的轨迹了。

那一次读取数据,可能用两个来实现,那我可不可以,就用一次,而操作两个数据呢

void SPI_A_READ(unsigned char addr, unsigned char * Rxdata){
		unsigned char addr2[2] = {addr, addr};
		unsigned char Rxdata2[2] = {0x00,0x00};
		
		spi_cs_A_Enable;
		HAL_SPI_TransmitReceive(&hspi1,addr2,Rxdata2,2,3);
		*Rxdata = Rxdata2[1];
		spi_cs_A_Disable;
}

答案是肯定的, 目前的时间变成了102.65us,虽然效率提升不大,但方向是对的啦。

试试DMA呢?(我也不知道我当时怎么对DMA还不死心)

void SPI_A_READ(unsigned char addr, unsigned char * Rxdata){
		unsigned char addr2[2] = {addr, addr};
		unsigned char Rxdata2[2] = {0x00,0x00};
		
		spi_cs_A_Enable;
		HAL_SPI_TransmitReceive_DMA(&hspi1,addr2,Rxdata2,2);
		*Rxdata = Rxdata2[1];
		spi_cs_A_Disable;
}

实验证明, 这样做,不能读到芯片的数据。 但时间却是降到了大概1半,168.9us。 如果是其它的SPI通信对象,也许DMA效率未必低,特别是一次性收发大量数据的时候。不过针对我用的这一款传感器芯片的SPI通信,使用DMA的方式,在我这儿我已经彻底死心了。

上面做的操作都是一个字节的读取操作。 有没有可能我连续读两个字节呢? 如果能连续读两个字节,效率有所优化的话,那么把所有需要读取的数据连续读出,效率会更优。 抱着这样的思路,让我们再试一把。

Ten years later

试完了, 连续两个字节是不行滴,拉闸了呀,兄弟。

Ten years later

不放弃,继续优化!

对于传感器芯片个人的理解是,你发一个读命令+地址的字节过去(同时收到一个没有用的数据),芯片收到你的读命令+地址,它把该地址下的寄存器的值复制到移位寄存器上。 你这时候,你随便再发一个没用的数据过去, 然后你将收到你想要读取的芯片上面对应地址的数据。你的目标是读芯片上某个地址的某个数据, 但本质上,这个流程中,你发送了两个字节,收到了两个字节。我的本意是, 我能不能连续的去读芯片上两个地址上的数据呢?我需要发送4个字节,收到4个字节。刚刚测试过了,这样子操作不行,第一个地址的数据读取是正确的,第二个就不对了。 尝试下不同的地址、更低的时钟。都不行。

不过,在我的不懈努力之下,发现,在每两个字节发送中间,添加一个片选的重启,是可以的,黄天不负有心人

 /* Transmit and Receive data in 8 Bit mode */
  else
  {
    if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
    {
      *((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
      hspi->pTxBuffPtr += sizeof(uint8_t);
      hspi->TxXferCount--;
    }
    while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U))
    {
      /* Check TXE flag */
      if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U))
      {
				if(hspi->TxXferCount==2){				// heqiunong add code
					spi_cs_A_Disable;					// heqiunong add code
					spi_cs_A_Enable;					// heqiunong add code
				}

把HAL_SPI_TransmitReceive的定义打开,然后添加了如下代码(heqiunong add code),其中

#define spi_cs_A_Enable		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2, GPIO_PIN_RESET)
#define spi_cs_A_Disable	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2, GPIO_PIN_SET)

那让我们看看效果如何。

哈哈哈, 运行时间再次从102.65us 降到了 86.7us。 相比于在开始的189us,现在已经走了很远了。

不过,我还是不满意, 因为它还有下降的空间。

优化尝试5、再次尝试连续读取方式

我们整个程序是读取芯片上的12个字节,6个数据。上一步已尝试连续读取两个字节是可行的,那下一步,就是连续读取12个字节(传感器芯片所有要读的数据是12字节)。

关键修改的点是

if(hspi->TxXferCount==2  || hspi->TxXferCount==4 || hspi->TxXferCount==6 || hspi->TxXferCount==8 || hspi->TxXferCount==10){			// heqiunong add code
					spi_cs_A_Disable;					// heqiunong add code
					spi_cs_A_Enable;					// heqiunong add code
}
// 每发送两个字节(相当于读1个字节),就重启一次片选
void SPI_A_READ_12BYTE(unsigned char * addrArray, unsigned char * Rxdata){	
		unsigned char Rxtemp[24] = {0x00};
		spi_cs_A_Enable;
		HAL_SPI_TransmitReceive_HQN_A(&hspi1,addrArray,Rxtemp,24,100);
    	// 这个是自己改的HAL_SPI_TransmitReceive,里面加了那个片选重启操作
		Rxdata[0] = Rxtemp[1];
		Rxdata[1] = Rxtemp[3];
		Rxdata[2] = Rxtemp[5];
		Rxdata[3] = Rxtemp[7];
		Rxdata[4] = Rxtemp[9];
		Rxdata[5] = Rxtemp[11];
		Rxdata[6] = Rxtemp[13];
		Rxdata[7] = Rxtemp[15];
		Rxdata[8] = Rxtemp[17];
		Rxdata[9] = Rxtemp[19];
		Rxdata[10] = Rxtemp[21];
		Rxdata[11] = Rxtemp[23];		
		spi_cs_A_Disable;
}
	unsigned char addrarray[24] = {\
        GYRO_XOUT_H_ADDR_R, 0x00, GYRO_XOUT_L_ADDR_R, 0x00, \
		GYRO_YOUT_H_ADDR_R, 0x00, GYRO_YOUT_L_ADDR_R, 0x00, \
		GYRO_ZOUT_H_ADDR_R, 0x00, GYRO_ZOUT_L_ADDR_R, 0x00, \
		ACCEL_XOUT_H_ADDR_R, 0x00, ACCEL_XOUT_L_ADDR_R, 0x00,\
		ACCEL_YOUT_H_ADDR_R, 0x00, ACCEL_YOUT_L_ADDR_R, 0x00,\
		ACCEL_ZOUT_H_ADDR_R, 0x00, ACCEL_ZOUT_L_ADDR_R, 0x00 };
	unsigned char data[12]={0x00};
        SPI_A_READ_12BYTE(addrarray,  data);
		fAGyroX = ((float)((int16_t)(data[0] << 8) + data[1])) / 131;	// °/s
		fAGyroY = ((float)((int16_t)(data[2] << 8) + data[3])) / 131;	// °/s
		fAGyroZ = ((float)((int16_t)(data[4] << 8) + data[5])) / 131;	// °/s
		fAaccX = ((float)((int16_t)(data[6] << 8) + data[7])) / 2048;	// g
		fAaccY = ((float)((int16_t)(data[8] << 8) + data[9])) / 2048;	// g
		fAaccZ = ((float)((int16_t)(data[10] << 8) + data[11])) / 2048;	// g

效率确实是提升了, 又从86.7us 降到了 62.1us, 但是Z轴陀螺的数据没有读对,读到的全是0,其它的数据都是正确的。 这着实有些许的恶心了。 检测代码,发现是木有问题的。

Ten years later!

终于发现问题了,我虽然是连续12个字节,但是我是发的24个字节。每两个字节就重启片选的操作,我只操作了一半,还有一半没有操作。这个停顿应该有11下。 修改之后,就没有问题了。不过运行时间从62.1us提升到了75us。 总结来讲,86.7us -> 75us 11.7us的效率提升还是可以的。

代码优化到现在,已经从189us优化到75us了, 已经不错啦,效率提升60%。

			if(  hspi->TxXferCount==2  \
				|| hspi->TxXferCount==4  \
				|| hspi->TxXferCount==6  \
				|| hspi->TxXferCount==8  \
				|| hspi->TxXferCount==10 \
				|| hspi->TxXferCount==12 \
				|| hspi->TxXferCount==14 \
				|| hspi->TxXferCount==16 \
				|| hspi->TxXferCount==18 \
				|| hspi->TxXferCount==20 \
				|| hspi->TxXferCount==22 \
				){									// heqiunong add code					
				spi_cs_A_Disable;					// heqiunong add code
				spi_cs_A_Enable;					// heqiunong add code
			}

优化尝试6、大胆修改HAL函数,删除冗余代码

毫无疑问,代码肯定还能再优化的,但目前不知道从何下手。 说不知道从何下手,一下手,又下降了10us

因为我们是8Bit的SPI通信,所以里面关于16bit的判断就可以去掉了。 对于超时的错误判断我也去掉了。

HAL_StatusTypeDef HAL_SPI_TransmitReceive_HQN_A(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,
                                          uint32_t Timeout)
{
  uint16_t             initial_TxXferCount;
  uint32_t             tmp_mode;
  HAL_SPI_StateTypeDef tmp_state;
  uint32_t             tickstart;
#if (USE_SPI_CRC != 0U)
  __IO uint32_t tmpreg = 0U;
#endif /* USE_SPI_CRC */

  /* Variable used to alternate Rx and Tx during transfer */
  uint32_t             txallowed = 1U;
  HAL_StatusTypeDef    errorcode = HAL_OK;

  /* Check Direction parameter */
  assert_param(IS_SPI_DIRECTION_2LINES(hspi->Init.Direction));

  /* Process Locked */
  __HAL_LOCK(hspi);

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

  /* Init temporary variables */
  tmp_state           = hspi->State;
  tmp_mode            = hspi->Init.Mode;
  initial_TxXferCount = Size;

  if (!((tmp_state == HAL_SPI_STATE_READY) || \
        ((tmp_mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES) && (tmp_state == HAL_SPI_STATE_BUSY_RX))))
  {
    errorcode = HAL_BUSY;
    goto error;
  }

  if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U))
  {
    errorcode = HAL_ERROR;
    goto error;
  }

  /* Don't overwrite in case of HAL_SPI_STATE_BUSY_RX */
  if (hspi->State != HAL_SPI_STATE_BUSY_RX)
  {
    hspi->State = HAL_SPI_STATE_BUSY_TX_RX;
  }

  /* Set the transaction information */
  hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
  hspi->pRxBuffPtr  = (uint8_t *)pRxData;
  hspi->RxXferCount = Size;
  hspi->RxXferSize  = Size;
  hspi->pTxBuffPtr  = (uint8_t *)pTxData;
  hspi->TxXferCount = Size;
  hspi->TxXferSize  = Size;

  /*Init field not used in handle to zero */
  hspi->RxISR       = NULL;
  hspi->TxISR       = NULL;

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

    // 这里删除了一部分冗余的代码

    while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U))
    {
			if(  hspi->TxXferCount==2  \
				|| hspi->TxXferCount==4  \
				|| hspi->TxXferCount==6  \
				|| hspi->TxXferCount==8  \
				|| hspi->TxXferCount==10 \
				|| hspi->TxXferCount==12 \
				|| hspi->TxXferCount==14 \
				|| hspi->TxXferCount==16 \
				|| hspi->TxXferCount==18 \
				|| hspi->TxXferCount==20 \
				|| hspi->TxXferCount==22 \
				){												// heqiunong add code					
				spi_cs_A_Disable;					// heqiunong add code
				spi_cs_A_Enable;					// heqiunong add code
			}
      /* Check TXE flag */
      if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U))
      {
        *(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr);
        hspi->pTxBuffPtr++;
        hspi->TxXferCount--;
        /* Next Data is a reception (Rx). Tx not allowed */
        txallowed = 0U;

#if (USE_SPI_CRC != 0U)
        /* Enable CRC Transmission */
        if ((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE))
        {
          SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
        }
#endif /* USE_SPI_CRC */
      }

      /* Wait until RXNE flag is reset */
      if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U))
      {
        (*(uint8_t *)hspi->pRxBuffPtr) = hspi->Instance->DR;
        hspi->pRxBuffPtr++;
        hspi->RxXferCount--;
        /* Next Data is a Transmission (Tx). Tx is allowed */
        txallowed = 1U;
      }
      
       // 这里删除了一部分冗余的代码, 就是超时的
			
    }
  

  /* 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);
  }

error :
  hspi->State = HAL_SPI_STATE_READY;
  __HAL_UNLOCK(hspi);
  return errorcode;
}

至此, 189us 已优化到 65.2。 优化率:65.6%

我知道SPI通信还能优化, 比如全部换成寄存器级操作。 但,我的优化,暂时就到此为止吧,如果各位大佬有好的思路,欢迎您为我提供。

  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
FPGA(现场可编程门阵列)和STM32是两种不同的芯片。FPGA是一种可编程逻辑器件,它可以按照用户需求重新配置其逻辑电路,适用于复杂的数字电路设计。而STM32是一种微控制器,它包含了一个处理器核心以及丰富的外设,适用于嵌入式系统设计。 SPI(串行外设接口)是一种常用的串行通信协议,可以用于连接多个设备,实现设备之间的数据传输。FPGA和STM32都支持SPI通信,可以通过相应的硬件接口和软件配置实现。 在使用FPGA实现SPI通信时,我们可以通过编写Verilog或VHDL代码来定义FPGA中的SPI接口,配置FPGA的引脚和时钟,并实现发送和接收数据的逻辑。FPGA可以使用其可编程逻辑电路来处理SPI通信协议的各个部分,例如时序、数据格式和校验等。通过适当的配置和连接,我们可以将FPGA与其他SPI设备(如传感器、存储器或其他嵌入式设备)进行通信。 而在STM32中实现SPI通信,我们可以使用STM32的内置SPI外设来实现。首先,我们需要配置SPI外设的相关寄存器,包括时钟速率、数据格式和模式等。然后,使用STM32的GPIO外设来配置相关引脚,使其与SPI外设连接。最后,通过编写适当的软件代码,实现SPI数据的发送和接收。这些代码可以使用STM32的相关库函数或直接操作寄存器来实现。通过配置和连接,我们可以将STM32与其他SPI设备进行通信,实现数据的传输和交互。 无论是通过FPGA还是STM32实现SPI通信,我们都需要仔细了解SPI协议的要求和特性,充分利用相应的硬件资源和软件工具,以确保通信的准确性和可靠性。同时,还要根据具体的应用需求和系统设计,选择合适的芯片和外设,进行相应的配置和编程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江湖上都叫我秋博

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值