zl程序教程

您现在的位置是:首页 >  其它

当前栏目

STM32-SPI资料整理

整理 资料 STM32 SPI
2023-09-11 14:21:43 时间

目录

一、SPI的简介

1、SPI物理层

2、协议层

(1)SPI基本通讯时序

(2)起始信号与停止信号

(3)数据有效性

(4)SPI的4种通讯模式 (CPOL - 时钟极性 与 CPHA - 时钟相位)

二、 软件模拟SPI示例

1、软件SPI结构体 以及 SPI模式枚举

2、SPI初始化函数

3、SPI读写函数

三、 STM32-SPI 外设使用示例(HAL库)

四、SPI常规调试手段


一、SPI的简介

1、SPI物理层

SPI 一般使用4条线进行通信:

  • NSS:为片选线
  • MOSI: 主机数据输出、从机数据输入
  • MISO:主机数据输入、从机数据输出
  • SCK:时钟线,由主机提供时钟输出

2、协议层

(1)SPI基本通讯时序

(2)起始信号与停止信号

  • 开始信号:NSS 由 高变低
  • 停止信号:NSS 由 低变高

(3)数据有效性

  • SPI 使用 SCK线进行数据同步,MOSI与MISO在SCK的每个时钟周期传输一位数据、且数据的 输入 与 输出 是同步进行的
  • 进行数据传输时,一般采取MSB先行(先发送高字节,再发低字节)
  • SPI 每次数据传输可以8位或16位为单位,每次传输的单位数不受限制

(4)SPI的4种通讯模式 (CPOL - 时钟极性 与 CPHA - 时钟相位)

  • 时钟极性 CPOL: 表示SCK的空闲状态时候的电平。 (如: CPOL = 0   ----  SCK空闲状态为低电平; CPOL = 1 ---- SCK空闲状态为高电平)
  • 时钟相位 CPHA: 表示数据何时被采样。(如: CPHA = 0 ---- SCK的奇数边沿被采样; CPHA = 1 ---- SCK的偶数边沿被采样)

SPI的四种模式:

 

CPHA = 0时(采样边沿为 奇数边沿的SPI时序图)

CPHA = 1时(采样边沿为 偶数边沿的SPI时序图) 

二、 软件模拟SPI示例

1、软件SPI结构体 以及 SPI模式枚举

typedef enum
{
	MODE_0 = 0, 	/* CPOL = 0, CPHA = 0;  */
	MODE_1,			/* CPOL = 0, CPHA = 1;  */
	MODE_2,			/* CPOL = 1, CPHA = 0;  */
	MODE_3,			/* CPOL = 1, CPHA = 1;  */

}exSPIModeEnum;




typedef struct SoftSPIHandle
{
	GPIO_TypeDef * NSS_Port;
	GPIO_TypeDef * SCK_Port;
	GPIO_TypeDef * MOSI_Port;
	GPIO_TypeDef * MISO_Port;

	uint16_t NSS_Pin;
	uint16_t SCK_Pin;
	uint16_t MOSI_Pin;
	uint16_t MISO_Pin;

	exSPIModeEnum mode;

	void (* SetHigh)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
	void (* SetLow)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
	void (* SetMode)(struct SoftSPIHandle xSoftSPIHandle, exSPIModeEnum eMode);

	uint8_t (* ReadPin)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

	void (* SoftSPIWriteData)(struct SoftSPIHandle xSoftSPIHandle, uint8_t *pData, uint32_t uiLength);
	void (* SoftSPIReadData)(struct SoftSPIHandle xSoftSPIHandle, uint8_t *pData, uint32_t uiLength);

}SoftSPIHandle;

2、SPI初始化函数

void vSoftSPIInit(exSPIModeEnum eMode)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

	/* 管脚初始化 */
	SOFT_SPI_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PB14	------> SPI1_NSS
    PB3     ------> SPI1_SCK
    PB4     ------> SPI1_MISO
    PB5     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = SOFT_SPI_NSS_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(SOFT_SPI_NSS_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SOFT_SPI_SCK_PIN;
    HAL_GPIO_Init(SOFT_SPI_SCK_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SOFT_SPI_MOSI_PIN;
    HAL_GPIO_Init(SOFT_SPI_MOSI_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin  = SOFT_SPI_MISO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    HAL_GPIO_Init(SOFT_SPI_MISO_PORT, &GPIO_InitStruct);

    /* 初始化Soft SPI 句柄 */
    ex_SoftSPIHandle.NSS_Port = ex_SoftSPIHandle.SCK_Port = ex_SoftSPIHandle.MISO_Port = ex_SoftSPIHandle.MOSI_Port = GPIOB;

    ex_SoftSPIHandle.NSS_Pin = SOFT_SPI_NSS_PIN;
    ex_SoftSPIHandle.SCK_Pin = SOFT_SPI_SCK_PIN;
    ex_SoftSPIHandle.MOSI_Pin = SOFT_SPI_MOSI_PIN;
    ex_SoftSPIHandle.MISO_Pin = SOFT_SPI_MISO_PIN;

    /* 注册Soft SPI 函数 */
    ex_SoftSPIHandle.SetHigh = vSetHigh;
    ex_SoftSPIHandle.SetLow = vSetLow;
    ex_SoftSPIHandle.SetMode = vSetMode;
    ex_SoftSPIHandle.ReadPin = HAL_GPIO_ReadPin;

    ex_SoftSPIHandle.SoftSPIWriteData = vSoftSPIWriteData;
    ex_SoftSPIHandle.SoftSPIReadData = vSoftSPIReadData;

    /* 设置对应SPI模式 */
    ex_SoftSPIHandle.mode = eMode;
	if(eMode & MODE_0)
	{
		/* CPOL = 0 ; CPHA = 0 */
	    /* SPI 默认模式0 处于空闲状态 */
	    SOFT_SPI_NSS_High();
	    SOFT_SPI_SCK_Low();
	}
	else if(eMode & MODE_1)
	{
		/* CPOL = 0 ; CPHA = 1 */
	    /* SPI 默认模式1 处于空闲状态 */
	    SOFT_SPI_NSS_High();
	    SOFT_SPI_SCK_Low();
	}
	else if(eMode & MODE_2)
	{
		/* CPOL = 1 ; CPHA = 0 */
	    /* SPI 默认模式2 处于空闲状态 */
	    SOFT_SPI_NSS_High();
	    SOFT_SPI_SCK_High();
	}
	else
	{
		/* CPOL = 1 ; CPHA = 1 */
	    /* SPI 默认模式3 处于空闲状态 */
	    SOFT_SPI_NSS_High();
	    SOFT_SPI_SCK_High();
	}

}

3、SPI读写函数

static void vSoftSPIWriteData(SoftSPIHandle xSoftSPIHandle, uint8_t *pData, uint32_t uiLength)
{
	for(uint32_t i = 0; i < uiLength; i++)
		ucSoftSPIReadWriteByte(xSoftSPIHandle, pData[i]);
}




static void vSoftSPIReadData(SoftSPIHandle xSoftSPIHandle, uint8_t *pData, uint32_t uiLength)
{
	for(uint32_t i = 0; i < uiLength; i++)
		pData[i] = ucSoftSPIReadWriteByte(xSoftSPIHandle, DummyByte);
}



static uint8_t ucSoftSPIReadWriteByte(SoftSPIHandle xSoftSPIHandle, uint8_t ucData)
{
	uint8_t ucRecvData = 0;

	if(xSoftSPIHandle.mode == MODE_0)
	{
		/* CPOL = 0 ; CPHA = 0 */
		/* 数据传输 */
		for(uint8_t i = 0; i < 8; i++)
		{
			/* 发送数据 */
			xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
			vDelayus(2);

			if( ucData & 0x80 )
				xSoftSPIHandle.SetHigh(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
			else
				xSoftSPIHandle.SetLow(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);


			ucData <<= 1;


			xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
			vDelayus(2);

			/* 接收数据 */
			ucRecvData <<= 1;
			if(xSoftSPIHandle.ReadPin(xSoftSPIHandle.MISO_Port, xSoftSPIHandle.MISO_Pin))
				ucRecvData |= 1;
		}

		xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
	}
	else if(xSoftSPIHandle.mode == MODE_1)
	{
		/* CPOL = 0 ; CPHA = 1 */
		/* 数据传输 */
		for(uint8_t i = 0; i < 8; i++)
		{
			/* 发送数据 */
			xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
			vDelayus(2);

			if( ucData & 0x80 )
				xSoftSPIHandle.SetHigh(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
			else
				xSoftSPIHandle.SetLow(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);


			ucData <<= 1;


			xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
			vDelayus(2);

			/* 接收数据 */
			ucRecvData <<= 1;
			if(xSoftSPIHandle.ReadPin(xSoftSPIHandle.MISO_Port, xSoftSPIHandle.MISO_Pin))
				ucRecvData |= 1;
		}

		xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);

	}
	else if(xSoftSPIHandle.mode == MODE_2)
	{
		/* CPOL = 1 ; CPHA = 0 */
		/* 数据传输 */
		for(uint8_t i = 0; i < 8; i++)
		{
			/* 发送数据 */
			xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
			vDelayus(2);

			if( ucData & 0x80 )
				xSoftSPIHandle.SetHigh(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
			else
				xSoftSPIHandle.SetLow(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);


			ucData <<= 1;


			xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
			vDelayus(2);

			/* 接收数据 */
			ucRecvData <<= 1;
			if(xSoftSPIHandle.ReadPin(xSoftSPIHandle.MISO_Port, xSoftSPIHandle.MISO_Pin))
				ucRecvData |= 1;
		}

		xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
	}
	else
	{
		/* CPOL = 1 ; CPHA = 1 */
		/* 数据传输 */
		for(uint8_t i = 0; i < 8; i++)
		{
			/* 发送数据 */
			xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
			vDelayus(2);

			if( ucData & 0x80 )
				xSoftSPIHandle.SetHigh(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
			else
				xSoftSPIHandle.SetLow(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);


			ucData <<= 1;


			xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
			vDelayus(2);

			/* 接收数据 */
			ucRecvData <<= 1;
			if(xSoftSPIHandle.ReadPin(xSoftSPIHandle.MISO_Port, xSoftSPIHandle.MISO_Pin))
				ucRecvData |= 1;
		}

		xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
	}

	return ucRecvData;
}

三、 STM32-SPI 外设使用示例(HAL库)

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspInit 0 */

  /* USER CODE END SPI1_MspInit 0 */
    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PB3     ------> SPI1_SCK
    PB4     ------> SPI1_MISO
    PB5     ------> SPI1_MOSI
    PB14	------> SPI1_NSS
    */
    GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin  = SPI1_NSS_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    HAL_GPIO_Init(SPI1_NSS_Port, &GPIO_InitStruct);


  /* USER CODE BEGIN SPI1_MspInit 1 */

  /* USER CODE END SPI1_MspInit 1 */
  }
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{

  if(spiHandle->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspDeInit 0 */

  /* USER CODE END SPI1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SPI1_CLK_DISABLE();

    /**SPI1 GPIO Configuration
    PB3     ------> SPI1_SCK
    PB4     ------> SPI1_MISO
    PB5     ------> SPI1_MOSI
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);

  /* USER CODE BEGIN SPI1_MspDeInit 1 */

  /* USER CODE END SPI1_MspDeInit 1 */
  }
}



void vSPI1Init(void)
{
	ex_SPI1Handle.Instance = SPI1;
	ex_SPI1Handle.Init.Mode = SPI_MODE_MASTER;
	ex_SPI1Handle.Init.Direction = SPI_DIRECTION_2LINES;
	ex_SPI1Handle.Init.DataSize = SPI_DATASIZE_8BIT;
	ex_SPI1Handle.Init.CLKPolarity = SPI_POLARITY_LOW;
	ex_SPI1Handle.Init.CLKPhase = SPI_PHASE_1EDGE;
	ex_SPI1Handle.Init.NSS = SPI_NSS_SOFT;
	ex_SPI1Handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
	ex_SPI1Handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
	ex_SPI1Handle.Init.TIMode = SPI_TIMODE_DISABLE;
	ex_SPI1Handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	ex_SPI1Handle.Init.CRCPolynomial = 10;

	HAL_SPI_Init(&ex_SPI1Handle);
}



int8_t cSPIWriteData(SPI_HandleTypeDef *ex_SPIhandle, uint8_t *pData, uint16_t usLength)
{
	int8_t status = HAL_ERROR;
	status = HAL_SPI_Transmit(ex_SPIhandle, pData, usLength, 1000);
	return status;
}

int8_t cSPIReadData(SPI_HandleTypeDef *ex_SPIhandle, uint8_t *pData, uint16_t usLength)
{
	int8_t status = HAL_ERROR;
	status = HAL_SPI_Receive(ex_SPIhandle, pData, usLength, 1000);
	return status;

}

四、SPI常规调试手段

1、如:SPI FLASH 可以发送 读取芯片ID的指令查看是否能够正确返回芯片的ID

参考资料为:

【野火】《STM32 HAL库开发实战指南-基于F407》

【正点原子】《STM32F4开发指南(HAL库版)》