#天空星硬件SPI驱动GD25Q64

news2025/1/17 6:05:18

一、使用方式及接线:

硬件SPI,软件片选CS(PA4)

STM32F407(主机)

GD25Q322(从机)

说明

PA4(SPI1_NSS)

CS(NSS)

片选线

PA5(SPI1_SCK)

CLK

时钟线

PA6(SPI_MISO)

DO(IO1)(MISO)

主机输入从机输出线

PA7(SPI_MOSI)

DI(IO0)(MOSI)

主机输出从机输入线

二、操作:

写入一串数据(嘉立创)

GD25Q32E-Rev1.3 (szlcsc.com)icon-default.png?t=N7T8https://atta.szlcsc.com/upload/public/pdf/source/20240402/EEA34CC25AEF519FAE78D6FF38362CF4.pdf

三、GD25Q32简介

1.简介

GD25Q32是一种常见的串行闪存器件,它采用SPI(Serial Peripheral Interface)接口协议,具有高速读写和擦除功能,可用于存储和读取数据。GD25Q32芯片容量为32 Mbit(4 MB),其中名称后的数字代表不同的容量选项。不同的型号和容量选项可以满足不同应用的需求,通常被用于嵌入式设备、存储设备、路由器等高性能电子设备中。

GD25Q32闪存芯片的内存分配是按照扇区(Sector)和块(Block)进行的,每个扇区的大小为4KB,每个块包含16个扇区,即一个块的大小为64KB

2.技术手册

 

3.主要参数

芯片容量每颗芯片块数量每个块大小每块扇区数量每个扇区大小
32Mbit(4MB)6464KB164KB

3.芯片相关指令

3.1读ID

//读取芯片ID          
//读取设备ID
uint16_t GD25Q32_readID(void)
{
    uint16_t  temp = 0;     
    //将CS端拉低为低电平     
    W25QXX_CS_ON(0);        
    //发送指令90h    
    spi_read_write_byte(0x90);//发送读取ID命令      
    //发送地址  000000H    
    spi_read_write_byte(0x00);             
    spi_read_write_byte(0x00);             
    spi_read_write_byte(0x00); 
        
    //接收数据
    //接收制造商ID
    temp |= spi_read_write_byte(0xFF)<<8;  
    //接收设备ID
    temp |= spi_read_write_byte(0xFF);        
    //恢复CS端为高电平
    W25QXX_CS_ON(1);      
    //返回ID                  
    return temp;
}

3.2写使能

//发送写使能
void GD25Q32_write_enable(void)   
{
    //拉低CS端为低电平
    W25QXX_CS_ON(0);                          
    //发送指令06h
    spi_read_write_byte(0x06);                  
    //拉高CS端为高电平
    W25QXX_CS_ON(1);
}                                          

 3.3忙碌状态

在GD25Q322的数据手册中,有3个状态寄存器,可以判断当前GD25Q32是否正在传输、写入、读取数据等,我们每一次要对GD25Q32进行操作时,需要先判断GD25Q32是否在忙。如果在忙的状态,我们去操作GD25Q32,很可能会导致数据丢失,并且操作失败。而判断是否忙,是通过状态寄存器1的S0为进行判断,状态寄存器1的地址为0X05。

读取状态寄存器的时序图如下:

  1. 拉低CS端为低电平;

  2. 发送指令05h(0000_0101);

  3. 接收状态寄存器值;

  4. 恢复CS端为高电平;

/**********************************************************
 * 函 数 名 称:GD25Q32_wait_busy
 * 函 数 功 能:检测线路是否繁忙
 * 传 入 参 数:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
**********************************************************/
void GD25Q32_wait_busy(void)   
{   
    unsigned char byte = 0;
    do
     { 
        //拉低CS端为低电平
        W25QXX_CS_ON(0); 
        //发送指令05h                           
        spi_read_write_byte(0x05);                
        //接收状态寄存器值
        byte = spi_read_write_byte(0Xff);       
        //恢复CS端为高电平
        W25QXX_CS_ON(1);      
     //判断BUSY位是否为1 如果为1说明在忙,重新读写BUSY位直到为0   
     }while( ( byte & 0x01 ) == 1 );  
}

 3.4扇区擦除

发送写使能->CS拉低->擦除扇区命令(20H)->三字节(24位)地址->CS拉高

/**********************************************************
 * 函 数 名 称:GD25Q32_erase_sector
 * 函 数 功 能:擦除一个扇区
 * 传 入 参 数:addr=擦除的扇区号
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:addr=擦除的扇区号,范围=0~15
**********************************************************/
void GD25Q32_erase_sector(uint32_t addr)   
{
        //计算扇区号,一个扇区4KB=4096
        addr *= 4096;
        GD25Q32_write_enable();  //写使能   
        GD25Q32_wait_busy();     //判断忙,如果忙则一直等待
        //拉低CS端为低电平
        W25QXX_CS_ON(0);  
        //发送指令20h                                     
        spi_read_write_byte(0x20);
        //发送24位扇区地址的高8位                
        spi_read_write_byte((uint8_t)((addr)>>16));      
        //发送24位扇区地址的中8位    
        spi_read_write_byte((uint8_t)((addr)>>8));   
        //发送24位扇区地址的低8位    
        spi_read_write_byte((uint8_t)addr);
        //恢复CS端为高电平  
        W25QXX_CS_ON(1);                  
        //等待擦除完成                                                  
        GD25Q32_wait_busy();   
}

3.5 写入数据

CS拉低--》写入数据命令(0X02)--》3个字节地址(24bit)--》写入至少一个字节--》CS拉高

/**********************************************************
 * 函 数 名 称:GD25Q32_write
 * 函 数 功 能:写数据到GD25Q32进行保存
 * 传 入 参 数:buffer=写入的数据内容        addr=写入地址        numbyte=写入数据的长度
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
**********************************************************/
void GD25Q32_write(uint8_t* buffer, uint32_t addr, uint16_t numbyte)
{    
    unsigned int i = 0;
    //擦除扇区数据
    GD25Q32_erase_sector(addr/4096);
    //写使能 
    GD25Q32_write_enable();  
    //忙检测  
    GD25Q32_wait_busy();    
    //写入数据
    //拉低CS端为低电平
    W25QXX_CS_ON(0);         
    //发送指令02h                              
    spi_read_write_byte(0x02);                 
    //发送写入的24位地址中的高8位   
    spi_read_write_byte((uint8_t)((addr)>>16));  
    //发送写入的24位地址中的中8位
    spi_read_write_byte((uint8_t)((addr)>>8));   
    //发送写入的24位地址中的低8位
    spi_read_write_byte((uint8_t)addr);   
    //根据写入的字节长度连续写入数据buffer
    for(i=0;i<numbyte;i++)
    {
        spi_read_write_byte(buffer[i]);  
    }
    //恢复CS端为高电平
    W25QXX_CS_ON(1);
    //忙检测 
    GD25Q32_wait_busy();      
}

3.6读取数据

CS拉低-》发送读取命令0X03-》发送24位地址

/**********************************************************
 * 函 数 名 称:GD25Q32_read
 * 函 数 功 能:读取GD25Q32的数据
 * 传 入 参 数:buffer=读出数据的保存地址  read_addr=读取地址   read_length=读去长度
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
**********************************************************/
void GD25Q32_read(uint8_t* buffer,uint32_t read_addr,uint16_t read_length)   
{ 
        uint16_t i;                   
        //拉低CS端为低电平
        W25QXX_CS_ON(0);    
        //发送指令03h        
        spi_read_write_byte(0x03);  
        //发送24位读取数据地址的高8位                         
        spi_read_write_byte((uint8_t)((read_addr)>>16));     
        //发送24位读取数据地址的中8位      
        spi_read_write_byte((uint8_t)((read_addr)>>8));   
        //发送24位读取数据地址的低8位
        spi_read_write_byte((uint8_t)read_addr);   
        //根据读取长度读取出地址保存到buffer中
        for(i=0;i<read_length;i++)
        { 
            buffer[i]= spi_read_write_byte(0XFF);  
        }
        //恢复CS端为高电平
        W25QXX_CS_ON(1);                                    
}

四、SPI介绍 

1.上面所述的FLASH芯片的SPI模式

2.SPI概述 

 

在SPI协议中,主设备是通信的发起方和控制方,而从设备则是被动接收和响应主设备的命令和数据。主设备通过时钟信号来同步数据传输,同时使用多个双向数据线来实现数据的传输和接收。

SPI协议是一种全双工通信方式,意味着主设备和从设备可以同时发送和接收数据。它还使用一种选择信号(通常称为片选或使能信号),用于选择与主设备进行通信的特定从设备。

3.SPI模式

SPI协议定义了多种传输模式,也称为SPI模式或时序模式,用于控制数据在时钟信号下的传输顺序和数据采样方式。SPI的传输模式主要由两个参数决定:时钟极性 (CKPL) 和相位 (CKPH)。 时钟极性 (CKPL):时钟极性定义了时钟信号在空闲状态时的电平。

CKPL = 0:时钟信号在空闲状态时为低电平。

CKPL = 1:时钟信号在空闲状态时为高电平。

时钟相位 (CKPH):相位定义了数据采样和更新发生在时钟信号的哪个边沿上。

CKPH = 0:数据采样发生在时钟的第一个边沿,数据更新发生在第二个边沿。

CKPH = 1:数据采样发生在时钟的第二个边沿,数据更新发生在第一个边沿。

以下是常见的SPI模式:

  1. 模式0(CKPL=0,CKPH=0):

- 时钟极性(Clock Polarity)为0,表示时钟空闲状态为低电平。

- 时钟相位(Clock Phase)为0,表示数据在时钟信号的第一个边沿(时钟上升沿)进行采样和稳定。

  1. 模式1(CKPL=0,CKPH=1):

- 时钟极性为0,时钟空闲状态为低电平。

- 时钟相位为1,数据在时钟信号的第二个边沿(时钟下降沿)进行采样和稳定。

  1. 模式2(CKPL=1,CKPH=0):

- 时钟极性为1,时钟空闲状态为高电平。

- 时钟相位为0,数据在时钟信号的第一个边沿(时钟下降沿)进行采样和稳定。

  1. 模式3(CKPL=1,CKPH=1):

- 时钟极性为1,时钟空闲状态为高电平。

- 时钟相位为1,数据在时钟信号的第二个边沿(时钟上升沿)进行采样和稳定。

 GD25Q32手册中指出模式0和模式3都是支持的,如下配置即为模式三

 五、编程

1.先读取ID,我贴的GD25Q32ESIG芯片ID为C815

/*
 * 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
 * 开发板官网:www.lckfb.com
 * 技术支持常驻论坛,任何技术问题欢迎随时交流学习
 * 立创论坛:club.szlcsc.com
 * 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
 * 不靠卖板赚钱,以培养中国工程师为己任
 */
#include "board.h"
#include "bsp_uart.h"
#include "stdio.h"
#include "sys.h"

#define CS_HIGH GPIO_SetBits(GPIOA,GPIO_Pin_4)
#define CS_LOW GPIO_ResetBits(GPIOA,GPIO_Pin_4)
/*
 *初始化SPI的GPIO
 *PA4--SPI1_NSS
 *PA5--SPI1_SCK
 *PA6--SPI1_MISO
 *PA7--SPI1_MOSI
 */
void SpiGpio_Init(void)
{
//1.1复用GPIO
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1);
//1.2初始化GPIO PA4 PA5 PA6 PA7
	GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

	/*配置MISO*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	/*配置MOSI*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	/*配置CS*/	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
//1.3拉高CS
  CS_HIGH;
}

/*
 *初始化SPI1
 */
void Spi_Init(void)
{
	//2.1初始化gpio
   SpiGpio_Init();
	//2.2配置Spi
	SPI_InitTypeDef SPI_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;  //84MHZ/4=21MHZ
	SPI_InitStruct.SPI_CPHA=SPI_CPHA_2Edge; //第二个边沿有效
	SPI_InitStruct.SPI_CPOL=SPI_CPOL_High;  //高电平有效
	SPI_InitStruct.SPI_CRCPolynomial= 7;     
	SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b; //单位数据大小1个字节
	SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex; //全双工
	SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;  //高位先行
	SPI_InitStruct.SPI_Mode=SPI_Mode_Master; //主机
	SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;   //软件选择CS
	SPI_Init(SPI1,&SPI_InitStruct);
	SPI_Cmd(SPI1,ENABLE);
}

/*
 *spi发送一个字节
 */
uint8_t spi_read_write_byte(uint8_t dat)
{
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);  //等待发送完毕
	SPI_I2S_SendData(SPI1,dat);
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET); //等待接收到数据
	return SPI_I2S_ReceiveData(SPI1);
}

/*
 *获取芯片ID和设备ID
 */
uint16_t GD25Q32_readID(void)
{
uint16_t id=0;     //要做移位操作必须初始化!!!
CS_LOW;
spi_read_write_byte(0x90);  //读芯片ID指令
spi_read_write_byte(0x00);
spi_read_write_byte(0x00);
spi_read_write_byte(0x00);	
id |= spi_read_write_byte(0xff)<<8;
id |= spi_read_write_byte(0xff);
CS_HIGH;
return id;
}

int main(void)
{

        board_init();
        uart1_init(115200);
	    Spi_Init();

        while(1)
        {       
			    printf("man_id is %X",GD25Q32_readID());
                delay_ms(1000);
        }
}

 2.spi编写为便于移植

spi.c


#include "spi.h"

/*
 *初始化SPI的GPIO
 *PA4--SPI1_NSS
 *PA5--SPI1_SCK
 *PA6--SPI1_MISO
 *PA7--SPI1_MOSI
 */
void SpiGpio_Init(void)
{
//1.1复用GPIO
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1);
//1.2初始化GPIO PA4 PA5 PA6 PA7
	GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

	/*配置MISO*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	/*配置MOSI*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	/*配置CS*/	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
//1.3拉高CS
  CS_HIGH;
}

/*
 *初始化SPI1
 */
void Spi_Init(void)
{
	//2.1初始化gpio
   SpiGpio_Init();
	//2.2配置Spi
	SPI_InitTypeDef SPI_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;  //84MHZ/4=21MHZ
	SPI_InitStruct.SPI_CPHA=SPI_CPHA_2Edge; //第二个边沿有效
	SPI_InitStruct.SPI_CPOL=SPI_CPOL_High;  //空闲状态为高电平
	SPI_InitStruct.SPI_CRCPolynomial= 7;     
	SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b; //单位数据大小1个字节
	SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex; //全双工
	SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;  //高位先行
	SPI_InitStruct.SPI_Mode=SPI_Mode_Master; //主机
	SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;   //软件选择CS
	SPI_Init(SPI1,&SPI_InitStruct);
	SPI_Cmd(SPI1,ENABLE);
}

/*
 *spi发送一个字节
 */
uint8_t spi_read_write_byte(uint8_t dat)
{
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);  //等待发送完毕
	SPI_I2S_SendData(SPI1,dat);
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET); //等待接收到数据
	return SPI_I2S_ReceiveData(SPI1);
}

spi.h 

#ifndef _SPI_H
#define _SPI_H
#include "board.h"

#define CS_HIGH GPIO_SetBits(GPIOA,GPIO_Pin_4)
#define CS_LOW GPIO_ResetBits(GPIOA,GPIO_Pin_4)

void SpiGpio_Init(void);
void Spi_Init(void);
uint8_t spi_read_write_byte(uint8_t dat);

#endif

 3.gd25q32

gd25q32.c

#include "gd25q32.h"
#include "spi.h"

/*
 *获取芯片ID和设备ID
 */
uint16_t GD25Q32_readID(void)
{
uint16_t id=0;     //要做移位操作必须初始化!!!
CS_LOW;
spi_read_write_byte(0x90);  //读芯片ID指令
spi_read_write_byte(0x00);
spi_read_write_byte(0x00);
spi_read_write_byte(0x00);	
id |= spi_read_write_byte(0xff)<<8;
id |= spi_read_write_byte(0xff);
CS_HIGH;
return id;
}

//发送写使能
void GD25Q32_write_enable(void)   
{
    //拉低CS端为低电平
    CS_LOW;                          
    //发送指令06h
    spi_read_write_byte(0x06);                  
    //拉高CS端为高电平
    CS_HIGH;
}

/**********************************************************
 * 函 数 名 称:GD25Q32_wait_busy
 * 函 数 功 能:检测线路是否繁忙
 * 传 入 参 数:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
**********************************************************/
void GD25Q32_wait_busy(void)   
{   
    unsigned char byte = 0;
    do
     { 
        //拉低CS端为低电平
        CS_LOW; 
        //发送指令05h                           
        spi_read_write_byte(0x05);                
        //接收状态寄存器值
        byte = spi_read_write_byte(0Xff);       
        //恢复CS端为高电平
        CS_HIGH;      
     //判断BUSY位是否为1 如果为1说明在忙,重新读写BUSY位直到为0   
     }while( ( byte & 0x01 ) == 1 );  
}

/**********************************************************
 * 函 数 名 称:GD25Q32_erase_sector
 * 函 数 功 能:擦除一个扇区
 * 传 入 参 数:addr=擦除的扇区号
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:addr=擦除的扇区号,范围=0~15
**********************************************************/
void GD25Q32_erase_sector(uint32_t addr)   
{
        //计算扇区号,一个扇区4KB=4096
        addr *= 4096;
        GD25Q32_write_enable();  //写使能   
        GD25Q32_wait_busy();     //判断忙,如果忙则一直等待
        //拉低CS端为低电平
        CS_LOW;  
        //发送指令20h                                     
        spi_read_write_byte(0x20);
        //发送24位扇区地址的高8位                
        spi_read_write_byte((uint8_t)((addr)>>16));      
        //发送24位扇区地址的中8位    
        spi_read_write_byte((uint8_t)((addr)>>8));   
        //发送24位扇区地址的低8位    
        spi_read_write_byte((uint8_t)addr);
        //恢复CS端为高电平  
        CS_HIGH;                  
        //等待擦除完成                                                  
        GD25Q32_wait_busy();   
}

/**********************************************************
 * 函 数 名 称:GD25Q32_write
 * 函 数 功 能:写数据到GD25Q32进行保存
 * 传 入 参 数:buffer=写入的数据内容        addr=写入地址        numbyte=写入数据的长度
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
**********************************************************/
void GD25Q32_write(uint8_t* buffer, uint32_t addr, uint16_t numbyte)
{    
    unsigned int i = 0;
    //擦除扇区数据
    GD25Q32_erase_sector(addr/4096);
    //写使能 
    GD25Q32_write_enable();  
    //忙检测  
    GD25Q32_wait_busy();    
    //写入数据
    //拉低CS端为低电平
    CS_LOW;         
    //发送指令02h                              
    spi_read_write_byte(0x02);                 
    //发送写入的24位地址中的高8位   
    spi_read_write_byte((uint8_t)((addr)>>16));  
    //发送写入的24位地址中的中8位
    spi_read_write_byte((uint8_t)((addr)>>8));   
    //发送写入的24位地址中的低8位
    spi_read_write_byte((uint8_t)addr);   
    //根据写入的字节长度连续写入数据buffer
    for(i=0;i<numbyte;i++)
    {
        spi_read_write_byte(buffer[i]);  
    }
    //恢复CS端为高电平
    CS_HIGH;
    //忙检测 
    GD25Q32_wait_busy();      
}

/**********************************************************
 * 函 数 名 称:GD25Q32_read
 * 函 数 功 能:读取GD25Q32的数据
 * 传 入 参 数:buffer=读出数据的保存地址  read_addr=读取地址   read_length=读去长度
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
**********************************************************/
void GD25Q32_read(uint8_t* buffer,uint32_t read_addr,uint16_t read_length)   
{ 
        uint16_t i;                   
        //拉低CS端为低电平
        CS_LOW;    
        //发送指令03h        
        spi_read_write_byte(0x03);  
        //发送24位读取数据地址的高8位                         
        spi_read_write_byte((uint8_t)((read_addr)>>16));     
        //发送24位读取数据地址的中8位      
        spi_read_write_byte((uint8_t)((read_addr)>>8));   
        //发送24位读取数据地址的低8位
        spi_read_write_byte((uint8_t)read_addr);   
        //根据读取长度读取出地址保存到buffer中
        for(i=0;i<read_length;i++)
        { 
            buffer[i]= spi_read_write_byte(0XFF);  
        }
        //恢复CS端为高电平
        CS_HIGH;                                    
}

gd25q32.h

#ifndef _GD25Q32_H
#define _GD25Q32_H
#include "gd25q32.h"
#include "board.h"

uint16_t GD25Q32_readID(void);
void GD25Q32_write_enable(void);
void GD25Q32_wait_busy(void);
void GD25Q32_erase_sector(uint32_t addr);
void GD25Q32_write(uint8_t* buffer, uint32_t addr, uint16_t numbyte);
void GD25Q32_read(uint8_t* buffer,uint32_t read_addr,uint16_t read_length);

#endif

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1628273.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

以场景驱动CMDB数据治理经验分享

数据治理是 CMDB 项目实施中难度最大、成本最高的环节&#xff0c;是一个长期治理的过程&#xff0c;而行业很少提出 CMDB 数据治理的技术实现方案。CMDB 数据治理不仅需要解决配置管理工程性的技术问题&#xff0c;还要基于运维组织的特点&#xff0c;建立适应性的配置运营能力…

【Unity动画系统】动画状态基本属性与相关API、IK简单概述

动画状态基本属性与相关API Tag&#xff1a;判断是否当前播放着相对应Tag的动画&#xff0c;如果是&#xff0c;那么玩家的输入就是无效的。 using UnityEngine.InputSystem;public AnimatorStateInfo stateInfo;void State(){//stateInfo animator.GetCurrentAnimatorStateIn…

Kafka 3.x.x 入门到精通(04)——对标尚硅谷Kafka教程

Kafka 3.x.x 入门到精通&#xff08;04&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.5 存储消息2.5.1 存储组件2.5.2 数据存储2.5.2.1 ACKS校验2.5.2.2 内部主题校验2.5.2.3 ACKS应答及副本数量关系校验2.5.2.4 日志文…

EOCR-i3MS-WRDUW电机保护器 韩国三和 进口施耐德

韩国三和&#xff0c;EOCR&#xff0c;SAMWHA&#xff0c;Schneider&#xff0c;施耐德&#xff0c;电机保护器&#xff0c;电动机保护器&#xff0c;电子式电动机保护继电器&#xff0c;电子式过电流继电器&#xff0c;电子式欠电流继电器&#xff0c;电子式电压继电器&#x…

Windows10系统安装IIS的步骤

在Windows 10上安装IIS的步骤如下&#xff1a;12 打开控制面板&#xff0c;选择“程序”或“程序和功能”。点击“启用或关闭Windows功能”。在列表中找到“Internet Information Services”&#xff0c;勾选该选项。根据需要勾选IIS的具体组件&#xff0c;如万维网服务、IIS可…

11 c++版本的贪吃蛇

前言 呵呵 这大概是 大学里面的 c 贪吃蛇了吧 有一些 面向对象的理解, 但是不多 最近 因为想要 在单片机上面移植一下 贪吃蛇, 所以 重新拿出了一下 这份代码 然后 将它更新为 c 版本, 还是 用了一些时间 这里 具体的实现 就不赘述, 仅仅是 发一下代码 以及 具体的使用…

Linux实验一:Linux环境及编程工具

目录 一、实验目的二、实验内容三、参考代码四、实验步骤步骤1. 编辑源代码test1.c步骤2. 编译源代码test1.c步骤3. 调试test1步骤4. 重新编译运行test1.c 五、实验结果六、实验总结 一、实验目的 1、掌握Linux C开发过程中的基本概念&#xff1b; 2、掌握如vim&#xff0c;GC…

go语言实现简单登陆返回token样例

目录 1、代码实现样例&#xff1a; 2、postman调用&#xff0c;获取登陆后的token&#xff1a; 1、代码实现样例&#xff1a; package mainimport ("net/http""time""github.com/dgrijalva/jwt-go""github.com/gin-gonic/gin" )var …

深度学习系列66:试穿模型IDM-VTON上手

1. 模型概述 如图&#xff0c;总体流程为&#xff1a; 输入为&#xff1a;衣服的编码xg&#xff1b;人物noise的编码xt&#xff1b;人物身上衣物的mask和人体pose分割(densepose)&#xff1b;衣服部分经过两部分网络&#xff1a;1&#xff09;高级语义网络IP-Adapter&#xff…

谷歌搜索留痕怎么做?

使用GLB外推技术&#xff0c;一个能让你的网站或者你的产品在谷歌上排名的神器 GLB外推&#xff0c;是利用先进的SEO技术&#xff0c;特别是光算科技的谷歌搜索留痕霸屏外推&#xff0c;来帮助你的产品或服务在谷歌上获得更高的曝光度&#xff0c;这项技术能让你的业务关键词在…

阿里云企业邮箱API的使用方法?调用限制?

阿里云企业邮箱API性能如何优化&#xff1f;配置邮箱API的优势&#xff1f; 阿里云企业邮箱以其稳定、高效和安全的特点&#xff0c;受到了众多企业的青睐。而阿里云企业邮箱API的开放&#xff0c;更是为企业提供了更加灵活、便捷的管理和操作方式。下面&#xff0c;我AokSend…

安装VMware后的相关配置

一、创建完虚拟机后 看看虚拟机设置里面的DVD&#xff1b;有没有自动检测到 二、打开虚拟机后 一直点击继续3、完成后进行重新下载VM——tools 来进行跨机子的复制粘贴&#xff0c;和屏幕大小的自适应注意:如果安装不了tools是灰色的 点开虚拟机设置——两个光盘都选用物理驱…

如何通过安全数据传输平台,保护核心数据的安全传输?

在数字化的浪潮中&#xff0c;企业的数据安全传输显得尤为关键。随着网络攻击手段的日益复杂&#xff0c;传统的数据传输方式已不再安全&#xff0c;这就需要我们重视并采取有效的措施&#xff0c;通过安全数据传输平台来保护核心数据。 传统的数据传输面临的主要问题包括&…

matlab批量读取csv文件

matlab如何批量读取csv文件 在Matlab中&#xff0c;有多种方法可以批量读取CSV文件。下面是几种常用的实现方法&#xff1a; 方法一&#xff1a;使用dir函数获取文件列表 folder 文件夹路径; files dir(fullfile(folder, *.csv)); numFiles length(files);for i 1:numFi…

通往AGI路上,DPU将如何构建生成式AI时代的坚实算力基石?

4月19日&#xff0c;在以“重构世界 奔赴未来”为主题的2024中国生成式AI大会上&#xff0c;中科驭数作为DPU新型算力基础设施代表&#xff0c;受邀出席了中国智算中心创新论坛&#xff0c;发表了题为《以网络为中心的AI算力底座构建之路》主题演讲&#xff0c;勾勒出在通往AGI…

PS入门|仿制图章工具咋用?

前言 最近讲着讲着&#xff0c;小白也不记得PS的内容讲到了哪。 咱们用过选择工具、钢笔工具、画笔工具、选框工具、魔棒工具&#xff0c;还使用过内容识别功能、蒙版功能等。 小伙伴们有没有发现&#xff0c;PS其实也没那么难&#xff1f;通过之前几次的练习&#xff0c;发…

牛客NC143 矩阵乘法【中等 矩阵 C++/Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/bf358c3ac73e491585943bac94e309b0 思路 矩阵算法在图像处理、神经网络、模式识别等领域有着广泛的用途。在矩阵乘法中&#xff0c;A矩阵和B矩阵可以做乘法运算必须满足A矩阵的列的数量等于B矩阵的行的数量。运算…

ROS1快速入门学习笔记 - 05发布者Publisher编程的实现

目录 一、话题模型&#xff08;发布/订阅&#xff09; 二、实现步骤 1. 创建一个功能包 2. C代码的实现 3. 配置发布者代码编译规则 4. 编译并运行发布者 5. Python代码的实现 一、话题模型&#xff08;发布/订阅&#xff09; 二、实现步骤 1. 创建一个功能包 $ cd~/ca…

NAT网络地址转换实验(华为)

思科设备参考&#xff1a;NAT网络地址转换实验&#xff08;思科&#xff09; 一&#xff0c;技术简介 NAT&#xff08;Network Address Translation&#xff09;&#xff0c;即网络地址转换技术&#xff0c;是一种在现代计算机网络中广泛应用的技术&#xff0c;主要用于有效管…

C++中把Lambda 表达式作为参数传递给模板函数。

例子&#xff1a; template<class fun> void mytest(fun f) {_string s1 "abc";_string s2 "abc";if (f(s1, s2)){std::cout << "相等。\n";}}int main() {mytest([](const _string s1, const _string& s2) { return s1 s2; …