实验平台

硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器
软件:最新版本STM32CubeH7固件库STM32CubeMX v6.10.0,开发板环境MDK v5.35,串口工具putty

SPI

SPI 介绍

  SPI是串行外设接口(Serial PeripheralInterface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议。SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是MISO(数据输入)、MOSI(数据输出)、SCLK(时钟)、CS(片选)。
SPI硬件接口:
MISO :主设备数据输入,从设备数据输出
MOSI :主设备数据输出,从设备数据输入
SCLK :时钟信号,由主设备产生
CS :从设备片选信号,由主设备控制
  主设备是通过片选线选择要与之通信的从设备。每个从设备都有一个片选线,当片选线为低电平时,表示该从设备被选中。(也有一些设备以高电平有效,需要根据其数据手册确定)。主设备通过控制时钟线的电平来同步数据传输。时钟线的上升沿和下降沿用于控制数据的传输和采样。SPI的主从接线方式需要对应,主从机设定后身份固定。
在这里插入图片描述

SPI协议

  根据时钟极性(CPOL)和时钟相位(CPHA)配置的不同,SPI工作模式分为4种。

时钟极性是指SPI通信设备处于空闲状态时(也可以认为这是SPI通信开始时,即SS线为低电平),SCK信号线的电平信号。CPOL=0时,SCK在空闲状态时为低电平,CPOL=1时则相反。
时钟相位是指数据采样的时刻,当CPHA=0时,MOSI或MISO数据线上的信号将会在SCK时钟线的奇数边沿被采样。当CPHA=1时,数据线在SCK的偶数边沿被采样。
在这里插入图片描述
以CPHA=0为例讲解SPI时序。
  首先,由主机把片选信号NSS拉低,意为主机输出。
  在NSS被拉低的时刻,SCK分为两种情况,若我们设置CPOL=0,则SCK时序在这时为低电平,若设置为CPOL=1,则SCK在这个时刻为高电平。
  无论CPOL为0还是1,因为我们配置的时钟相位CPHA=0,在采样时刻的时序中我们可以看到,采样时刻都是在SCK的奇数边沿(注意奇数边沿有时为下降沿,有时为上升沿)。
  因此,MOSI和MISO数据线的有效信号在SCK的奇数边沿保持不变,这个信号将会在SCK奇数边沿时被采集,在非采样时刻,MOSI和MISO的有效信号才发生切换。
  对于CPHA=1的情况也类似,只是数据信号的采样时刻为偶数边沿。

W25Q128介绍

在这里插入图片描述
  W25Q128是一种常见的串行闪存器件,它采用SPI(Serial Peripheral Interface)接口协议,具有高速读写和擦除功能,可用于存储和读取数据。W25Q125芯片容量为128Mbit(16MB),其中名称后的数字代表不同的容量选项。不同的型号和容量选项可以满足不同应用的需求,比如W25Q16、W25Q64、W25Q32等。通常被用于嵌入式设备、存储设备、路由器等高性能电子设备中。 W25Q128闪存芯片的内存分配是按照扇区(Sector)和块(Block)进行的,将16M的容量分为256个块(Block),每个块的大小为64KB,每个块包含16个扇区,每个扇区的大小为4K字节。
  W25Q128 的适用场景非常广泛,几乎涵盖了所有需要可靠存储的嵌入式电子产品:
嵌入式系统:存储微控制器的启动代码、应用程序固件和系统配置文件。
物联网设备:保存传感器数据、设备日志和用于OTA(空中下载)升级的固件包。
消费电子:在智能手表、蓝牙耳机等设备中,用于存放开机动画、提示音等资源文件。
工业控制与汽车电子:存储设备参数、校准数据等,适应恶劣的工作环境。
更多的W25Q128的介绍,请参考W25Q128的DATASHEET
在这里插入图片描述
本章实验对W25Q128操作步骤,初始化后,写使能SPI5,之后进行扇区擦除,等待空闲后即可再次写使能进行页编程,具体如图所示。
在这里插入图片描述

STM32CubeMX生成工程

我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示,我们来看配置SPI部分如下图所示:
在这里插入图片描述

实验代码

1. 主函数

int main(void)
{
    uint16_t device_id=0;     //Flash内部的ID
    uint8_t read_buf=0;      //用于存放读取缓冲地址
    uint8_t write_buf=0;     //用于存放写入缓冲地址
    uint16_t i; 
    
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_SPI5_Init();
    MX_USART6_UART_Init();
    
    uart6.initialize(115200);
    HAL_Delay(5000);//延时5秒,提供使用者打开串口时间
    uart6.printf("\033[1;32;40m");
    device_id = W25QXX_ReadID();//读取Flash内部的ID
    uart6.printf("W25QXX_ReadID:%x\r\n",device_id);
    W25QXX_Erase_Sector(0);//擦除扇区。在闪存存储器中,写入新数据之前,需要先将要写入的扇区擦除,以确保该扇区中之前的数据被清空
        
    uart6.printf("W25QXX_Write_Buffer:0~255\r\n");  
      
    for(i=0;i < 256;i++)
    {
        W25QXX_Page_Program(&write_buf, i, 1); //将数据写入FLASH,参数包括写入数据的缓冲区地址(&write_buf)、写入的地址(i)和写入的数据长度(1)
        write_buf = write_buf + 1; //写入数据的缓冲区地址递增1
    }
    
    uart6.printf("W25QXX_Read_Buffer:");
    
    for(i=0;i < 256;i++)
	{
        if(i%10==0)uart6.printf("\r\n");
        W25QXX_Read(&read_buf, i, 1); //从芯片依次读取数据,参数包括读取数据的缓冲区地址(&read_buf)、读入的地址(i)和读入的数据长度(1)
        uart6.printf("%d\t",read_buf);
    }
    
    uart6.printf("\r\n\r\nFlash Test OK!\r\n");

  while (1)
  {


  }

}

2. SPI初始化函数

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
    if(spiHandle->Instance==SPI5)
    {
        /** 初始化外设时钟*/
        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI5;
        PeriphClkInitStruct.Spi45ClockSelection = RCC_SPI45CLKSOURCE_D2PCLK1;
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
        {
            Error_Handler();
        }

        /*SPI5时钟使能 */
        __HAL_RCC_SPI5_CLK_ENABLE();
        __HAL_RCC_GPIOJ_CLK_ENABLE();
        __HAL_RCC_GPIOH_CLK_ENABLE();
        /**SPI5 GPIO配置
        PJ11     ------> SPI5_MISO
        PJ10     ------> SPI5_MOSI
        PH6     ------> SPI5_SCK
        */
        GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI5;
        HAL_GPIO_Init(GPIOJ, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI5;
        HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

        HAL_NVIC_SetPriority(SPI5_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(SPI5_IRQn);
  }
}


初始化SPIx,设置SPIx工作模式

void MX_SPI5_Init(void)
{
    hspi5.Instance = SPI5;
    hspi5.Init.Mode = SPI_MODE_MASTER;
    hspi5.Init.Direction = SPI_DIRECTION_2LINES;
    hspi5.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi5.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi5.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi5.Init.NSS = SPI_NSS_SOFT;
    hspi5.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
    hspi5.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi5.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi5.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi5.Init.CRCPolynomial = 0x0;
    hspi5.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
    hspi5.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
    hspi5.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
    hspi5.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    hspi5.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    hspi5.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
    hspi5.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    hspi5.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
    hspi5.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
    hspi5.Init.IOSwap = SPI_IO_SWAP_DISABLE;
    if (HAL_SPI_Init(&hspi5) != HAL_OK)
    {
        Error_Handler();
    }

}


实验现象

在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐