STM32F7实现SPI读写,读取W25Q16型号

SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。
CubeMX配置:
在这里插入图片描述

SPI模式

有只发送、只接收、半双工和全双工模式;
根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;

片选信号

因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;

帧格式

这里选择Motorola帧格式;
选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。
博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
这是手册里关于TI模式的说明部分和波形图:
在这里插入图片描述
在这里插入图片描述
也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。

数据长度

4-16bit可选,根据实际,选择8bit;

数据传输模式

第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。

SPI速率

SPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。

时钟极性和时钟相位

根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。

CRC校验

暂时还没研究到这里……

NSS脉冲

参考SPI的NSS 脉冲模式的作用,这里用不到;

NSS信号类型

因为前面禁用了硬件片选,所以这里只能设置位软件模式。

除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。
CubeMX生成的代码如下:

SPI_HandleTypeDef SPI3_Handler;  //SPI2句柄

void SPI3_Init(void)
{
    SPI3_Handler.Instance = SPI3;
    SPI3_Handler.Init.Mode = SPI_MODE_MASTER;
    SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
    SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
    SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;
    SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;
    SPI3_Handler.Init.NSS = SPI_NSS_SOFT;
    SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
    SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
    SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
    SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    SPI3_Handler.Init.CRCPolynomial = 7;
    SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
    HAL_SPI_Init(&SPI3_Handler);

    __HAL_SPI_ENABLE(&SPI3_Handler);               
}

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi->Instance==SPI3)
  {

    __HAL_RCC_SPI3_CLK_ENABLE();
  
    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }
}


static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);

  /*Configure GPIO pin : PA4 */
  GPIO_InitStruct.Pin = GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:

void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)
{
    assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    __HAL_SPI_DISABLE(&SPI3_Handler);            //关闭SPI
    SPI3_Handler.Instance->CR1 &= 0XFFC7;        //位3-5清零,用来设置波特率
    SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
    __HAL_SPI_ENABLE(&SPI3_Handler);             //使能SPI

}

因为全双工的读写是同时的,封装一个读写函数:

//SPI2 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI3_ReadWriteByte(u8 TxData)
{
    u8 Rxdata;
    HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);
    return Rxdata;                      //返回收到的数据
}

根据W25QXX的数据手册,写一个读ID的函数:

//读取芯片ID
//返回值如下:				   
//0XEF13,表示芯片型号为W25Q80  
//0XEF14,表示芯片型号为W25Q16    
//0XEF15,表示芯片型号为W25Q32  
//0XEF16,表示芯片型号为W25Q64 
//0XEF17,表示芯片型号为W25Q128 	  
u16 W25QXX_ReadID(void)
{
	u16 Temp = 0;	  
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);	    
	SPI3_ReadWriteByte(0x90);//发送读取ID命令	    
	SPI3_ReadWriteByte(0x00); 	    
	SPI3_ReadWriteByte(0x00); 	    
	SPI3_ReadWriteByte(0x00); 	 			   
	Temp|=SPI3_ReadWriteByte(0xFF)<<8;  
	Temp|=SPI3_ReadWriteByte(0xFF);	 
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);  
	return Temp;
}   

最后写一个在主函数中调用的接口函数。

void W25QXX_Init(void)
{
	MX_GPIO_Init();
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
	SPI3_Init();
	SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
	uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
	printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
}

下载运行,读取到了芯片ID。
在这里插入图片描述

发布了353 篇原创文章 · 获赞 81 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/qq_27508477/article/details/105702541
今日推荐