W25Q128芯片的SPI驱动程序

1、四个文件

//SPI底层初始化控制
bsp_spi_hw.h
bsp_spi_hw.c
//W25Qxx NorFlash驱动
bsp_w25qxx.h
bsp_w25qxx.c

2、bsp_spi_hw.h文件

主要是用户配置、根据不同的接线对驱动进行配置,考虑到移植性,只需要修改用户配置这几个宏即可。
#ifndef __BSP_SPI_HW_H_
#define __BSP_SPI_HW_H_
#include "main.h"

/*用户配置*/
#define SPI_SC_GPIOTYPE   GPIOB
#define SPI_SC_PIN        GPIO_PIN_14

#define SPI_CLK_GPIOTYPE  GPIOB
#define SPI_CLK_PIN       GPIO_PIN_3

#define SPI_MISO_GPIOTYPE GPIOB
#define SPI_MISO_PIN      GPIO_PIN_4

#define SPI_MOSI_GPIOTYPE GPIOB
#define SPI_MOSI_PIN      GPIO_PIN_5

#define SPIx SPI1
#define SPI_WAIT_TIME_OUT 0x0FFF

void BSP_SPIx_Init(void);
void BSP_SPIx_WaitReady(void);
void BSP_SPIx_Select(void);
void BSP_SPIx_DisSelect(void);
unsigned char BSP_SPIx_WriteReadByte(unsigned char TxData);
void BSP_SPIx_SetSpeed(unsigned char SPI_BaudRatePrescaler);

#endif

3、bsp_spi_hw.c文件

SPI初始化配置、我是在STM32F4板子进行测试的,使用的hal库,方便移植到其他系列MCU上,毕竟也是这几年ST主推hal库的原因,便于移植。
1、初始化SPI
void BSP_SPIx_Init(void) 

2、 SPI引脚初始化、该函数在HAL库为弱定义、需要用户自己实现函数体、HAL_SPI_Init函数中会调用到
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)

3、设置SPI时钟分频系数(设置完成之后一定再使能下SPI才能生效)
void BSP_SPIx_SetSpeed(unsigned char SPI_BaudRatePrescaler)

4、片选使能、设置CS输出为低电平、
void BSP_SPIx_Select(void)

5、片选失能、设置CS输出为高电平
void BSP_SPIx_DisSelect(void)

6、SPI收发数据函数
SPI通信的两个设备实际上是两个双向的移位寄存器在时钟信号的控制交换数据、发送一个byte数据、同时也会接收到一个byte数据。
unsigned char BSP_SPIx_WriteReadByte(unsigned char TxData)
#include "bsp_spi_hw.h"

SPI_HandleTypeDef SPI1_Handler;   //SPI1句柄
uint8_t TxData = 0xFF;
uint8_t RxData = 0;

//SPI初始化
void BSP_SPIx_Init(void)
{
	//SPI1初始化
	SPI1_Handler.Instance = SPIx;
	SPI1_Handler.Init.Mode = SPI_MODE_MASTER;    //主
	SPI1_Handler.Init.Direction = SPI_DIRECTION_2LINES;  //双线模式
	SPI1_Handler.Init.DataSize = SPI_DATASIZE_8BIT;      //8位数据
	SPI1_Handler.Init.NSS = SPI_NSS_SOFT;    //软件控制SS
	SPI1_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;  //256分频
	SPI1_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;  //发送MSB
	SPI1_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;     //时钟极性0
	SPI1_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;         //时钟相位0
	SPI1_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;  //硬件CRC失能
	SPI1_Handler.Init.TIMode = SPI_TIMODE_DISABLE; //关闭IT模式
	HAL_SPI_Init(&SPI1_Handler);

	//使能SPI1
	__HAL_SPI_ENABLE(&SPI1_Handler);

	//开启SPI传输
	HAL_SPI_TransmitReceive(&SPI1_Handler,&TxData,&RxData,1,SPI_WAIT_TIME_OUT);
}

//GPIO引脚初始化
//PB3 - SPI1_SCK
//PB4 - SPI1_MISO
//PB5 - SPI1_MOSI
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
	if(SPIx == hspi->Instance)
	{
		GPIO_InitTypeDef GPIO_InitStructer;

		//使能GPIO时钟、SPI1时钟
		__HAL_RCC_GPIOB_CLK_ENABLE();
		__HAL_RCC_SPI1_CLK_ENABLE();

		//初始化GPIO
		GPIO_InitStructer.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStructer.Pin = SPI_CLK_PIN|SPI_MISO_PIN|SPI_MOSI_PIN;
		GPIO_InitStructer.Pull = GPIO_PULLUP;
		GPIO_InitStructer.Speed = GPIO_SPEED_FAST;
		GPIO_InitStructer.Alternate = GPIO_AF5_SPI1;
		HAL_GPIO_Init(SPI_CLK_GPIOTYPE,&GPIO_InitStructer);

		//初始化CS
		GPIO_InitStructer.Mode = GPIO_MODE_OUTPUT_PP;
		GPIO_InitStructer.Pin = SPI_SC_PIN;
		GPIO_InitStructer.Speed = GPIO_SPEED_FAST;
		GPIO_InitStructer.Pull = GPIO_PULLUP;
		HAL_GPIO_Init(SPI_SC_GPIOTYPE,&GPIO_InitStructer);

		//CS线初始化拉高
		HAL_GPIO_WritePin(SPI_SC_GPIOTYPE,SPI_SC_PIN,GPIO_PIN_SET);
	}
}

//设置SPI时钟频率
void BSP_SPIx_SetSpeed(unsigned char SPI_BaudRatePrescaler)
{
	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler)); //判断有效性
	SPIx->CR1&=0XFFC7; //位3-5清零,用来设置波特率
	SPIx->CR1|=SPI_BaudRatePrescaler;   //设置SPI1速度 
	__HAL_SPI_ENABLE(&SPI1_Handler);
}

//等待SPI空闲
void BSP_SPIx_WaitReady(void)
{
	RxData = 0;
	HAL_SPI_TransmitReceive(&SPI1_Handler,&TxData,&RxData,1,SPI_WAIT_TIME_OUT);
	if(0xFF == RxData)
	{
		return;
	}
}

//片选使能
void BSP_SPIx_Select(void)
{
	//片选线拉低
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);
}

//片选失能
void BSP_SPIx_DisSelect(void)
{
	//片选线拉高
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);
}

//读写一个字节数据
unsigned char BSP_SPIx_WriteReadByte(unsigned char TxData)
{
	unsigned char RxData = 0;
	HAL_SPI_TransmitReceive(&SPI1_Handler,&TxData,&RxData,1,SPI_WAIT_TIME_OUT);
	return RxData;
}

4、bsp_w25qxx.h

#ifndef __BSP_W25QXX_H_
#define __BSP_W25QXX_H_

#include"main.h"

#define FLASH_SECTOR_SIZE 4096     //扇区大小4KB
#define FLASH_PAGE_SIZE   256      //页大小256byte
#define FLASH_TIME_OUT    0x7FFF

//芯片ID
#define W25Q08  0xEF13
#define W25Q16  0xEF14
#define W25Q32  0xEF15
#define W25Q64  0xEF16
#define W25Q128 0xEF17
#define W25Q256 0xEF18

enum 
{
	S_ERROR,
	S_OK,
};

//状态寄存器ID
typedef enum emNorFlashReg_ID
{
	W25Qxx_STA_REG_1 = 1,
	W25Qxx_STA_REG_2,
	W25Qxx_STA_REG_3,
	W25Qxx_STA_REG_MAX,
}emNorFlashReg_ID;

//芯片类型枚举
typedef enum emNorFlashTypeId
{
	NorFlash_W25Q80 = 0,
	NorFlash_W25Q16,
	NorFlash_W25Q32,
	NorFlash_W25Q64,
	NorFlash_W25Q128,
	NorFlash_W25Q256,
	NorFlash_ID_MAX,
}emNorFlashTypeId;

//输入参数
typedef struct tagNorFlashInPara
{
	unsigned long timeout;
}tagNorFlashInPara;

//输入函数
typedef struct tagNorFlashInFun
{
	void (*SPI_Init)(void);
	void (*SPI_Select)(void);
	void (*SPI_DisSelect)(void);
	void (*SPI_SetSpeed)(unsigned char speed);
	unsigned char (*SPI_WriteReadByte)(unsigned char tx_data);
	
}tagNorFlashInFun;

//输出参数
typedef struct tagNorFlashOutPara
{
	unsigned long totalsize;     //芯片总容量
	unsigned long sectorcount;   //扇区个数
	unsigned long sectorsize;    //扇区大小
	unsigned long blocksize;     //块大小
	unsigned short dev_id;       //芯片型号
	
}tagNorFlashOutPara;

//私有成员
typedef struct tagNorFlashPrv
{
	uint8_t debug_err;
	uint8_t DataBuff[FLASH_SECTOR_SIZE];   //4K的数据缓冲区
}tagNorFlashPrv;

//NorFlash控制块
typedef struct tagNorFlashCB
{
	tagNorFlashInPara  InPara;
	tagNorFlashInFun   InFun;
	tagNorFlashOutPara OutPara;
	tagNorFlashPrv     Prv;
}tagNorFlashCB;



/*-----------------------接口函数----------------------------*/
unsigned short NorFlash_GetVersion(void);
unsigned char  NorFlash_DeInit(tagNorFlashCB *cb);
unsigned char  NorFlash_InitInPara(tagNorFlashCB *cb, unsigned long timeout);
unsigned char  NorFlash_InitInFun(tagNorFlashCB *cb,
							      void *Init(void),
							      void *Select(void),
							      void *DisSelect(void),
							      void *SetSpeed(unsigned char),
							      unsigned char *WriteReadByte(unsigned char *));

unsigned char NorFlash_Init(tagNorFlashCB *cb);
unsigned char NorFlash_SoftReset(tagNorFlashCB *cb);
unsigned short NorFlash_DevId(tagNorFlashCB *cb);
unsigned char NorFlash_Write_Enable(tagNorFlashCB *cb);
unsigned char NorFlash_Write_Disable(tagNorFlashCB *cb);
unsigned char NorFlash_Wait_Busy(tagNorFlashCB *cb);
unsigned char NorFlash_Erase_Sec(tagNorFlashCB *cb, unsigned short sec_id);
unsigned char NorFlash_Erase_Chip(tagNorFlashCB *cb);
unsigned char NorFlash_Read_Regs(tagNorFlashCB *cb, emNorFlashReg_ID reg_id);
unsigned char NorFlash_Wirte_Regs(tagNorFlashCB *cb, emNorFlashReg_ID reg_id,unsigned char reg_val);
unsigned char NorFlash_ReadData(tagNorFlashCB *cb,unsigned char *pbuffer,unsigned long  r_addr,unsigned short r_len);
unsigned char NorFlash_Write_Page(tagNorFlashCB *cb,unsigned char *pbuffer,unsigned long  w_addr,unsigned short w_len);
unsigned char NorFlash_Write_Pages(tagNorFlashCB *cb,unsigned char *pbuffer,unsigned long  w_addr,unsigned short w_len);
unsigned char NorFlash_Wirte(tagNorFlashCB *cb,unsigned char *pbuffer,unsigned long w_addr,unsigned short w_len);
#endif

5、bsp_w25qxx.c

#include "bsp_w25qxx.h"


//模块版本
#define NorFlash_Version  0x100

//指令集
#define W25Qxx_READ_DEV_ID    0x90     //读设备ID
#define W25Qxx_READ_DATA      0x03     //读数据

#define W25Qxx_WRITE_ENABLE   0x06     //写使能
#define W25Qxx_WRITE_DISABLE  0x04     //写失能

#define W25Qxx_ERASE_SEC      0x20     //擦除扇区 4KB
#define W25Qxx_ERASE_BK32     0x52     //擦除块 32KB
#define W25Qxx_ERASE_BK64     0xD8     //擦除块 64KB

#define W25Qxx_PAGE_PROGRAM   0x02     //页编程 页大小为256

#define W25Qxx_Read_StaReg1   0x05     //读状态寄存器1
#define W25Qxx_Read_StaReg2   0x35     //读状态寄存器2
#define W25Qxx_Read_StaReg3   0x15     //读状态寄存器3

#define W25Qxx_Write_StaReg1  0x01     //写状态寄存器1 写状态寄存器中指定的位
#define W25Qxx_Write_StaReg2  0x31     //写状态寄存器2
#define W25Qxx_Write_StaReg3  0x11     //写状态寄存器3
  
#define W25Qxx_Erase_Chip1    0xC7     //全片擦除指令1
#define W25Qxx_Erase_Chip2    0x60     //全片擦除指令2

#define W25Qxx_Reset_Enable   0x66     //芯片复位使能
#define W25Qxx_Reset_Cmd      0x99     //芯片复位指令


//芯片对应的设备ID
const unsigned short NorFlashDevID[NorFlash_ID_MAX] =
{
	0xEF13,   //W25Q80
	0xEF14,   //W25Q16
	0xEF15,   //W25Q32
	0xEF16,   //W25Q64
	0xEF17,   //W25Q128
	0xEF18,   //W25Q256
};

//芯片容量对照表
const unsigned long NorFlashTotalSizeTable[NorFlash_ID_MAX] =
{
	8*1024*1024,    //W25Q80
	2*1024*1024,    //W25Q16
	4*1024*1024,    //W25Q32
	8*1024*1024,    //W25Q64
	16*1024*1024,   //W25Q128
	32*1024*1024,   //W25Q256
};


//获取模块版本
unsigned short NorFlash_GetVersion(void)
{
	return NorFlash_Version;
}

//控制块DeInit
unsigned char  NorFlash_DeInit(tagNorFlashCB *cb)
{
	if(cb)
	{
	#if 0
		cb->InFun.SPI_Init = (void)(*0)(void);
		cb->InFun.SPI_Select = (void)(*0)(void);
		cb->InFun.SPI_DisSelect = (void)(*0)(void);
		cb->InFun.SPI_SetSpeed = (void)(*0)(unsigned char);
		cb->InFun.SPI_WriteReadByte = (unsigned char)(*0)(unsigned char);
	#endif
		cb->InPara.timeout = 0;
		return S_OK;		
	}
	return S_ERROR;
}

//初始化输入参数
unsigned char  NorFlash_InitInPara(tagNorFlashCB *cb, unsigned long timeout)
{
	if(cb)
	{
		cb->InPara.timeout = timeout;
		return S_OK;
	}
	return S_ERROR;
}

//初始化输入函数
unsigned char  NorFlash_InitInFun(tagNorFlashCB *cb,
							      void *Init(void),
							      void *Select(void),
							      void *DisSelect(void),
							      void *SetSpeed(unsigned char),
							      unsigned char *WriteReadByte(unsigned char *))
{
	if(cb)
	{
		cb->InFun.SPI_Init = Init;
		cb->InFun.SPI_Select = Select;
		cb->InFun.SPI_DisSelect = DisSelect;
		cb->InFun.SPI_SetSpeed = SetSpeed;
		cb->InFun.SPI_WriteReadByte = WriteReadByte;
		return S_OK;
	}
	return S_ERROR;
}

//初始化W25Qxx
unsigned char NorFlash_Init(tagNorFlashCB *cb)
{
	unsigned char i;
	if(cb)
	{
		//初始化SPI
		if(cb->InFun.SPI_Init)
		{
			cb->InFun.SPI_Init();
		}
		else
		{
			return S_ERROR;
		}

		//软件复位芯片
		//NorFlash_SoftReset(cb);

		//读取芯片的厂家ID
		cb->OutPara.dev_id = NorFlash_DevId(cb);

		//识别芯片
		for(i = 0; i < NorFlash_ID_MAX; i++)
		{
			if(cb->OutPara.dev_id == NorFlashDevID[i])
			{
				cb->OutPara.totalsize = NorFlashTotalSizeTable[i];
				cb->OutPara.sectorcount = cb->OutPara.totalsize/FLASH_SECTOR_SIZE;
				cb->OutPara.sectorsize = FLASH_SECTOR_SIZE;
				cb->OutPara.blocksize = 16;
			}
		}
		return S_OK;
	}
	return S_ERROR;
}

//芯片软件复位
unsigned char NorFlash_SoftReset(tagNorFlashCB *cb)
{
	if(cb)
	{
		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_WriteReadByte)
		{
			cb->InFun.SPI_WriteReadByte(W25Qxx_Reset_Enable);
			cb->InFun.SPI_WriteReadByte(W25Qxx_Reset_Cmd);
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}

		//应该需要等待复位完成、50us
		return S_OK;
	}
	return S_ERROR;
}

//读取设备id
unsigned short NorFlash_DevId(tagNorFlashCB *cb)
{
	unsigned short dev_id = 0;
	if(cb)
	{
		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_WriteReadByte)
		{
			cb->InFun.SPI_WriteReadByte(W25Qxx_READ_DEV_ID);
			cb->InFun.SPI_WriteReadByte(0x00);
			cb->InFun.SPI_WriteReadByte(0x00);
			cb->InFun.SPI_WriteReadByte(0x00);
			
			dev_id |= cb->InFun.SPI_WriteReadByte(0xFF) << 8;
			dev_id |= cb->InFun.SPI_WriteReadByte(0xFF);
		}

		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}
		return dev_id;
	}
	return S_ERROR;
}

//写使能
unsigned char NorFlash_Write_Enable(tagNorFlashCB *cb)
{
	if(cb)
	{
		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_WriteReadByte)
		{
			cb->InFun.SPI_WriteReadByte(W25Qxx_WRITE_ENABLE);
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}
		return S_OK;
	}
	return S_ERROR;
}

//写禁止
unsigned char NorFlash_Write_Disable(tagNorFlashCB *cb)
{
	if(cb)
	{
		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_WriteReadByte)
		{
			cb->InFun.SPI_WriteReadByte(W25Qxx_WRITE_DISABLE);
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}
		return S_OK;
	}
	return S_ERROR;
}	

//待等芯片空闲
unsigned char NorFlash_Wait_Busy(tagNorFlashCB *cb)
{
	unsigned long timeout = 0;
	if(cb)
	{
		while(NorFlash_Read_Regs(cb,W25Qxx_STA_REG_1)&0x01)
		{
			timeout++;
			if(timeout > FLASH_TIME_OUT)
			{
				break;
			}
		}

		if(timeout <= FLASH_TIME_OUT)
		{
			return S_OK;
		}
		else
		{
			return S_ERROR;
		}
	}
	return S_ERROR;
}

//擦除扇区
unsigned char NorFlash_Erase_Sec(tagNorFlashCB *cb, unsigned short sec_id)
{
	unsigned long sec_addr = 0;
	if(cb)
	{
		//扇区id有效性检查
		if(sec_id > cb->OutPara.totalsize/FLASH_SECTOR_SIZE)
		{
			return S_ERROR;
		}

		//等待芯片空闲
		NorFlash_Wait_Busy(cb);

		//写使能
		NorFlash_Write_Enable(cb);

		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		//扇区的序号转化为扇区地址
		sec_addr = sec_id * FLASH_SECTOR_SIZE;

		if(cb->InFun.SPI_WriteReadByte)
		{
			cb->InFun.SPI_WriteReadByte(W25Qxx_ERASE_SEC);
			cb->InFun.SPI_WriteReadByte((unsigned char)((sec_addr)>>16));
			cb->InFun.SPI_WriteReadByte((unsigned char)((sec_addr)>>8));
			cb->InFun.SPI_WriteReadByte((unsigned char)(sec_addr));
		}

		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}

		//等待芯片空闲
		NorFlash_Wait_Busy(cb);

		return S_OK;
	}
	return S_ERROR;
}

//全片擦除、花费时钟较长
unsigned char NorFlash_Erase_Chip(tagNorFlashCB *cb)
{
	if(cb)
	{
		//等待芯片空闲
		NorFlash_Wait_Busy(cb);

		//先写使能
		NorFlash_Write_Enable(cb);

		//片选
		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		//发送全片擦除指令
		if(cb->InFun.SPI_WriteReadByte)
		{
			cb->InFun.SPI_WriteReadByte(W25Qxx_Erase_Chip1);
		}
		else
		{
			return S_ERROR;
		}

		//取消片选
		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}

		//等待擦操作完成、这里需要下、死等可能导致看门狗复位、
		//有操作系统的话使用可以使用OS的延迟、让出CPU、防止看门狗复位
		//我这里使用的FreeRTOS
		//vTaskDelay(5000);  //延迟5s
		while(NorFlash_Read_Regs(cb,1)&0x1);
		return S_OK;
	}
	return S_ERROR;
}

//读取状态寄存器的值
unsigned char NorFlash_Read_Regs(tagNorFlashCB *cb, emNorFlashReg_ID reg_id)
{
	if(reg_id >= W25Qxx_STA_REG_MAX)
	{
		return S_ERROR;
	}
	unsigned char reg_val = 0;
	if(cb)
	{
		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_WriteReadByte)
		{
			if(W25Qxx_STA_REG_1 == reg_id)
			{
				cb->InFun.SPI_WriteReadByte(W25Qxx_Read_StaReg1);
			}
			else if(W25Qxx_STA_REG_2 == reg_id)
			{
				cb->InFun.SPI_WriteReadByte(W25Qxx_Read_StaReg2);
			}
			else
			{
				cb->InFun.SPI_WriteReadByte(W25Qxx_Read_StaReg3);
			}
			reg_val = cb->InFun.SPI_WriteReadByte(0xFF);
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}
		return reg_val;
	}
	return S_ERROR;
}


//读取指定地址数据
unsigned char NorFlash_ReadData(tagNorFlashCB *cb,unsigned char *pbuffer,unsigned long  r_addr,unsigned short r_len)
{
	if((pbuffer == (void*)0)
	  ||(r_addr + r_len) > cb->OutPara.totalsize)
	{
		return S_ERROR;
	}
	
	unsigned short i;
	
	if(cb)
	{
		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_WriteReadByte)
		{
			cb->InFun.SPI_WriteReadByte(W25Qxx_READ_DATA);
			cb->InFun.SPI_WriteReadByte((unsigned char)(r_addr>>16));
			cb->InFun.SPI_WriteReadByte((unsigned char)(r_addr>>8));
			cb->InFun.SPI_WriteReadByte((unsigned char)(r_addr));

			//读取数据
			for(i = 0; i < r_len; i++)
			{
				pbuffer[i] = cb->InFun.SPI_WriteReadByte(0xFF);
			}
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}
		return S_OK;
	}
	return S_ERROR;
}

//页编程 256个字节
unsigned char NorFlash_Write_Page(tagNorFlashCB *cb,unsigned char *pbuffer,unsigned long  w_addr,unsigned short w_len)
{
	if((pbuffer == (void*)0)
	   ||(w_addr + w_len) > cb->OutPara.totalsize
	   || w_len > FLASH_PAGE_SIZE)   //页大小256字节
	{
		return S_ERROR;
	}
	unsigned short i;
	if(cb)
	{
		//需先写使能
		NorFlash_Write_Enable(cb);

		//片选
		if(cb->InFun.SPI_Select)
		{
			cb->InFun.SPI_Select();
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_WriteReadByte)
		{
			cb->InFun.SPI_WriteReadByte(W25Qxx_PAGE_PROGRAM);  //发送02功能码
			cb->InFun.SPI_WriteReadByte((unsigned char)(w_addr>>16));  //24位写地址
			cb->InFun.SPI_WriteReadByte((unsigned char)(w_addr>>8));  
			cb->InFun.SPI_WriteReadByte((unsigned char)(w_addr));  

			//发送数据
			for(i = 0; i < w_len; i++)
			{
				cb->InFun.SPI_WriteReadByte(pbuffer[i]);  
			}
		}
		else
		{
			return S_ERROR;
		}

		if(cb->InFun.SPI_DisSelect)
		{
			cb->InFun.SPI_DisSelect();
		}
		else
		{
			return S_ERROR;
		}

		//等待数据写入完成
		NorFlash_Wait_Busy(cb);
		
		return S_OK;
	}
	return S_ERROR;
}

//写多页操作
unsigned char NorFlash_Write_Pages(tagNorFlashCB *cb,unsigned char *pbuffer,unsigned long  w_addr,unsigned short w_len)
{
	if((pbuffer == (void*)0)
	   ||(w_addr + w_len) >= cb->OutPara.totalsize)
	{
		return S_ERROR;
	}

	unsigned short page_remain = 0;
	page_remain = FLASH_PAGE_SIZE - w_addr%FLASH_PAGE_SIZE;

	//判断该页的剩余空间是否可以写完
	if(page_remain >= w_len)
	{
		page_remain = w_len;
	}

	while(1)
	{
		NorFlash_Write_Page(cb,pbuffer,w_addr,page_remain);

		if(page_remain == w_len)
		{
			break;
		}
		else
		{
			pbuffer += page_remain;
			w_addr += page_remain;
			w_len -= page_remain;

			if(w_len > FLASH_PAGE_SIZE)
			{
				page_remain = FLASH_PAGE_SIZE;
			}
			else
			{
				page_remain = w_len;
			}
		}
	}
	return S_OK;
}

//写数据 - 有检查是否擦除
unsigned char NorFlash_Wirte(tagNorFlashCB *cb,unsigned char *pbuffer,unsigned long w_addr,unsigned short w_len)
{
	if((pbuffer == (void*)0)
	  ||(w_addr + w_len > cb->OutPara.totalsize))
	{
		return S_ERROR;
	}

	unsigned short i;
	unsigned short erase_flag = 0;
	unsigned short sec_id, sec_offset, sec_remain;

	//计算当前写入的扇区信息
	sec_id = w_addr/FLASH_SECTOR_SIZE;
	sec_offset = w_addr%FLASH_SECTOR_SIZE;
	sec_remain = FLASH_SECTOR_SIZE - sec_offset;

	//判断剩余空间是否可以写完成
	if(sec_remain >= w_len)
	{
		sec_remain = w_len;
	}

	while(1)
	{
		//将当前扇区里面的内容全部读出来
		NorFlash_ReadData(cb,cb->Prv.DataBuff,sec_id*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);

		//判断是否需要擦除

		for(i = sec_offset; i < FLASH_SECTOR_SIZE; i++)
		{
			if(cb->Prv.DataBuff[i] != 0xFF)
			{
				erase_flag = 1;
				break;
			}
			erase_flag = 0;
		}

		//擦除扇区
		if(1 == erase_flag)
		{
			NorFlash_Erase_Sec(cb,sec_id);
		}

		//拷贝数据
		for(i = 0; i < sec_remain; i++)
		{
			cb->Prv.DataBuff[i+sec_offset] = pbuffer[i];
		}

		//写入数据
		NorFlash_Write_Pages(cb,cb->Prv.DataBuff,sec_id*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);

		//判断本次是否写完成
		if(sec_remain == w_len)
		{
			break;
		}
		else
		{
			//这里已经页对齐了
			sec_id++;
			sec_offset = 0;
			
			pbuffer += sec_remain;
			w_addr += sec_remain;
			w_len -= sec_remain;
			if(w_len > FLASH_SECTOR_SIZE)
			{
				sec_remain = FLASH_SECTOR_SIZE;
			}
			else
			{
				sec_remain = w_len;
			}
		}
	}
	return S_OK;
}

6、使用举例

已经在STMF4、W25Q128 norFlash测试ok
其他平台暂未测试、请注意。


static uint16_t dev_id = 0;
uint8_t WBuffer[] = "hello world!";
uint8_t RBuffer[100];
tagNorFlashCB NorFlashcb;  //定义NorFlash控制块
void Flash_Test_Task(void *Para)
{	
	tagNorFlashCB *pcb = &NorFlashcb;
	NorFlash_DeInit(pcb);
	//初始化控制块中输入函数、挂SPI接口
	NorFlash_InitInFun(pcb,	
				       BSP_SPIx_Init,
				       BSP_SPIx_Select,
				       BSP_SPIx_DisSelect,
				       BSP_SPIx_SetSpeed,
				       BSP_SPIx_WriteReadByte);
	//初始化控制输入参数、超时时间
	NorFlash_InitInPara(pcb,0x7FFF);
	//初始化W25Qx128芯片、读写芯片设备号
	NorFlash_Init(pcb);
	
	while(1)
	{
		memset(RBuffer,0,100);
		//写操作
		NorFlash_Wirte(pcb,WBuffer,12000,sizeof(WBuffer));
		vTaskDelay(1000);		
		//读操作
		NorFlash_ReadData(pcb,RBuffer,12000,sizeof(WBuffer));
		vTaskDelay(1000);
	}
}

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐