STM32F103ZE CUBEMX 硬件SPI读写W25Q64

SPI介绍

        SPI接口是Motorola 首先提出的全双工三线同步串行外围接口,采用主从模式(Master Slave)架构;支持多slave模式应用,一般仅支持单Master。时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first);SPI接口有2根单向数据线,为全双工通信,目前应用中的数据速率可达几Mbps的水平。

SPI协议

        SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。MOSI及MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行的。SPI每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制

FLASH介绍

          FLSAH 存储器又称闪存,它与 EEPROM 都是掉电后数据不丢失的存储器,但 FLASH存储器容量普遍大于 EEPROM,现在基本取代了它的地位。我们生活中常用的 U盘、SD卡、SSD 固态硬盘以及我们 STM32 芯片内部用于存储程序的设备,都是 FLASH 类型的存储器。在存储控制上,最主要的区别是FLASH 芯片只能一大片一大片地擦写,而EEPROM可以单个字节擦写。

W25Q64

        w25q64有64MB内存,一页256个字节4096字节也就是(4KB)为一个扇区16个扇区为一块(65536字节)一共128块(8388608字节)

接线

        MOIS->PB15(SPI2)

        MISO->PB14(SPI2)

       SCK->PB13(SPI2)

       CS->PG2

配置

w25q64.c

#include "W25Q64.h"
/**********************
spi协议发送1个字节函数
uint8_t BYTE :你需要发送的字节数据
*********************/
uint8_t W25QX_SendByte(uint8_t BYTE)
{
	uint8_t SNED_BYTE = BYTE;
	uint8_t READ_BYTE = 0;
	HAL_SPI_TransmitReceive(&hspi2, &SNED_BYTE, &READ_BYTE, 1, 1000);
	return READ_BYTE;
}

/**********************
读id函数 
返回芯片的ID
*********************/
uint16_t W25QX_Read_ID(void)
{
	uint16_t W25QX_ID;
	uint8_t temp0, temp1;
	CS_LOW;
	W25QX_SendByte(W25QX_Manufacturer_Device_ID);
	W25QX_SendByte(kong_DATA);
	W25QX_SendByte(kong_DATA);
	W25QX_SendByte(kong_DATA);
	temp0 = W25QX_SendByte(0xff); // 第一次返回厂家id
	temp1 = W25QX_SendByte(0xff); // 第二次返回唯一id
	CS_HIGH;
	W25QX_ID = (temp0 << 8) | temp1;
	return W25QX_ID;
}

/**********************
验证id函数 
uint16_t ID : 你需要验证的芯片id
成功返回1,否则返回0
*********************/

uint8_t Verification_ID(uint16_t ID)
{

	if (W25QX_Read_ID() == w25Q64)
	{
		return 1;
	}
	else
	{

		return 0;
	}
}

/**********************
等待flash空闲函数 擦除写入时需
要等待flash完成调用此函数
*********************/
void Await_Status_Register(void)
{

	CS_LOW;
	W25QX_SendByte(Read_Status_Register_CMD);
	while (W25QX_SendByte(kong_DATA) & 0x01)
	{
	};
	CS_HIGH;
}

/**********************
写使能函数
使能falsh的写入功能
*********************/

void W25QX_Write_Enable(void)
{
	CS_LOW;
	W25QX_SendByte(Write_Enable_CMD);
	CS_HIGH;
}
/**********************
失能函数
失能falsh的写入功能
*********************/

void W25QX_Write_Disable(void)
{
	CS_LOW;
	W25QX_SendByte(Write_Disable_CMD);
	CS_HIGH;
}


/**********************
扇区擦除函数
uint32_t addr :要擦除扇区的地址4096的倍数
*********************/

void W25QX_Sector_Erase(uint32_t addr)
{
	W25QX_Write_Enable();
	CS_LOW;
	W25QX_SendByte(Sector_Erase_CMD);
	W25QX_SendByte((addr >> 16) & 0xff); // 一次传输一个字节右移16位取高八位
	W25QX_SendByte((addr >> 8) & 0xff);	 // 一次传输一个字节右移8位取中八位
	W25QX_SendByte(addr & 0xff);		 // 取低八位
	CS_HIGH;
	Await_Status_Register(); // 等待擦除完成
}
/**********************
块擦除函数
uint32_t addr :要擦除块区的地址65536的倍数
*********************/

void W25QX_Block_Erase(uint32_t addr)
{
	W25QX_Write_Enable();
	CS_LOW;
	W25QX_SendByte(Block_Erase_CMD);
	W25QX_SendByte((addr >> 16) & 0xff); // 一次传输一个字节右移16位取高八位
	W25QX_SendByte((addr >> 8) & 0xff);	 // 一次传输一个字节右移8位取中八位
	W25QX_SendByte(addr & 0xff);		 // 取低八位
	CS_HIGH;
	Await_Status_Register(); // 等待擦除完成
}
/*
全部擦除函数
等待时间比较长,别不信
你用下就知道了
*/
void W25QX_Erase_Chip(void)
{
	W25QX_Write_Enable(); // 开启写使能
	CS_LOW;
	W25QX_SendByte(Chip_Erase_CMD);
	CS_HIGH;
	Await_Status_Register(); // 等待擦除完成
}

/*
进入省电模式
*/
void W25QX_Power_Down(void)   
{ 
  	CS_LOW;                           
    W25QX_SendByte(Power_Down_CMD	);
	  CS_HIGH;                              	      
    HAL_Delay(1);
}   

/*
快来唤醒它吧嘿嘿嘿
*/

void W25QX_Wake_Up(void)   
{  
    CS_LOW;                           
    W25QX_SendByte(Wake_Up_CMD);
	  CS_HIGH;                              	      
    HAL_Delay(1);                        
}   





/**********************

读数据函数
uint8_t * Rdata_Buf:要存放读出flash的数据包地址
uint32_t Write_addr : 开始读flash地址
uint32_t Write_size : 要读出的字节数


*********************/

void W25QX_Read_Data(uint8_t *Rdata_Buf, uint32_t Read_addr, uint32_t Read_size)
{
	CS_LOW;
	W25QX_SendByte(Read_Data_CMD);
	W25QX_SendByte((Read_addr >> 16) & 0xff); // 一次传输一个字节右移16位取高八位
	W25QX_SendByte((Read_addr >> 8) & 0xff);  // 一次传输一个字节右移8位取中八位
	W25QX_SendByte(Read_addr & 0xff);		  // 取低八位

	// flash内部会自动增加地址只管一直读就完事了
	while (Read_size--)
	{

		*Rdata_Buf = W25QX_SendByte(kong_DATA);
		Rdata_Buf++;
	}
	// 一直读到我们将片选拉高为止
	CS_HIGH;
}

/**********************

页写入函数
uint8_t * Wdata_Buf :要写入flash的数据包地址
uint32_t Write_addr : 开始写入flash的地址
uint32_t Write_size :要写入的字节数


*********************/
void W25QX_Write_Data(uint8_t *Wdata_Buf, uint32_t Write_addr, uint32_t Write_size)
{

	if (Write_size > Page_Size)
	{
		Write_size = Page_Size;
	}
	W25QX_Write_Enable(); // 开启写使能
	CS_LOW;
	W25QX_SendByte(Write_Data_CMD);
	W25QX_SendByte((Write_addr >> 16) & 0xff); // 一次传输一个字节右移16位取高八位
	W25QX_SendByte((Write_addr >> 8) & 0xff);  // 一次传输一个字节右移8位取中八位
	W25QX_SendByte(Write_addr & 0xff);		   // 取低八位

	while (Write_size--)
	{
		W25QX_SendByte(*Wdata_Buf);
		Wdata_Buf++;
	}
	CS_HIGH;
	Await_Status_Register(); // 等待写完成
}

/********************************

升级版函数可以写入任意地址
uint8_t * Write_data :要写入flash的数据包地址
uint32_t Write_addr : 开始写入flash的地址
uint32_t Write_size :要写入的字节数

*********************************/

void W25QX_Write_Data_ProMAX(uint8_t *Write_data, uint32_t Write_addr, uint32_t Write_size)
{
	uint32_t PageNum, BYTES, Misaligned_value, Residual_value;

	Misaligned_value = Write_addr % PAGESIZE;
	Residual_value = PAGESIZE - Misaligned_value;
	/***
	未页对其
	Misaligned_value 偏差
	计算有没有对齐,对齐就是0,没对齐就有偏差数据
	列如果Write_addr填入的0 or 256,一页的倍数取余数就是0,
	如果填入257 就余1,说明这1页Write_addr的位置在地址1这里

	Residual_value 剩余
	就可以用页数减去Write_addr的位置值,就得到了这页还剩余的字节数,
	我们就可以先把这1页剩余的字节数给写满
	***/

	if ((Misaligned_value != 0) && (Write_size > Residual_value)) // 如果余数不为0和写数据字节数大于这一页剩余字节数数说明没有对齐
	{
		W25QX_Write_Data(Write_data, Write_addr, Residual_value); // 首先将这一页剩余的字节数写满
		Write_size -= Residual_value;							  // 要写入的字节数减去这一页写满的字节数得到剩余要写的字节数
		Write_data += Residual_value;							  // 写入的数据包地址加上这一页写满的字节数得到下一次数据包需要写入的地址
		Write_addr += Residual_value;							  // 开始地址加上这一页写满的字节数得到下一次开始写的地址
	}

	/*页对齐*/
	PageNum = Write_size / PAGESIZE; // 计算出有多少页
	BYTES = Write_size % PAGESIZE;	 // 计算还余下多少字节

	if (PageNum >= 1) // 说明有1页及以上数据
	{
		while (PageNum--) // 页数不为0一直按页写入
		{
			W25QX_Write_Data(Write_data, Write_addr, PAGESIZE); // 写入一个页的字节数
			Write_addr += PAGESIZE;								// 地址加上一页的字节数等于下一次要写入的地址
			Write_data += PAGESIZE;								// 数据包地址加上一页的字节数等于下一次数据包开始的地址
		}
	}
	if (BYTES) // 说明有还有不满足一页条件的数据
	{

		W25QX_Write_Data(Write_data, Write_addr, BYTES); // 写入剩余的字节数
	}
}



w25q64.h

#ifndef __w25q64_H__
#define __w25q64_H__

#include "stm32f1xx_hal.h"
#include "spi.h"
#include "SYSTEM.h"
#define w25Q64 	0X0b16 //ID号
#define Page_Size  256
#define Sector_Size  4096

/**
本程序用于w25q64,其他内存flash通用
有些地方稍做修改即可
w25q64有64MB内存
一页256个字节
4096字节也就是(4KB)为一个扇区
16个扇区为一块(65536字节)
一共128块(8388608字节)
**/


/**
指令兼容w25qx全系列
**/
#define  W25QX_Manufacturer_Device_ID   0X90 //读取厂商id
#define  Write_Enable_CMD               0X06 //开启写使能
#define  Write_Disable_CMD              0X04 //关闭写使能
#define  Read_Status_Register_CMD       0X05 //读状态寄存器
#define  Sector_Erase_CMD               0X20 //扇区擦除4kB
#define  Block_Erase_CMD                0XD8 //块擦除64kB
#define  Chip_Erase_CMD			            0xC7 //全部擦除
#define  Write_Data_CMD                 0X02 //写命令
#define  Read_Data_CMD                  0X03 //读命令
#define  Power_Down_CMD		              0xB9 //省电模式
#define  Wake_Up_CMD		                0XAB //唤醒模式

#define kong_DATA                       0X00                 //空字节
#define CS_LOW                          PGout(13)=0
#define CS_HIGH                         PGout(13)=1






uint8_t W25QX_SendByte(uint8_t BYTE);//发送一个字节
uint16_t W25QX_Read_ID(void);//读取id
uint8_t Verification_ID(uint16_t ID);//验证id成功返回1,否则返回0
void  W25QX_Sector_Erase(uint32_t addr);//擦除扇区
void W25QX_Block_Erase(uint32_t addr);
void W25QX_Erase_Chip(void);//擦除整块芯片
void W25QX_Read_Data(uint8_t * Rdata_Buf,uint32_t Read_addr,uint32_t Read_size);//读数据
void W25QX_Write_Data(uint8_t * Wdata_Buf,uint32_t Write_addr,uint32_t Write_size);//页写数据
void W25QX_Write_Data_ProMAX(uint8_t * Write_data,uint32_t Write_addr,uint32_t Write_size);//随意写
void W25QX_Power_Down(void);
void W25QX_Wake_Up(void) ;

#endif

        

        

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是基于STM32F103C8T6芯片的硬件SPI驱动及W25Q64读写的示例代码,供参考。 SPI驱动: ``` #include "stm32f10x.h" #define SPI_PORT GPIOA #define SPI_SCK GPIO_Pin_5 #define SPI_MOSI GPIO_Pin_7 #define SPI_MISO GPIO_Pin_6 void SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); GPIO_InitStructure.GPIO_Pin = SPI_SCK | SPI_MOSI; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(SPI_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = SPI_MISO; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(SPI_PORT, &GPIO_InitStructure); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); } uint8_t SPI_SendByte(uint8_t byte) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, byte); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI1); } ``` W25Q64读写: ``` #include "stm32f10x.h" #define W25Q64_CS GPIO_Pin_4 void W25Q64_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = W25Q64_CS; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits(GPIOA, W25Q64_CS); } void W25Q64_WriteEnable(void) { GPIO_ResetBits(GPIOA, W25Q64_CS); SPI_SendByte(0x06); GPIO_SetBits(GPIOA, W25Q64_CS); } void W25Q64_ReadStatus(uint8_t *status) { GPIO_ResetBits(GPIOA, W25Q64_CS); SPI_SendByte(0x05); *status = SPI_SendByte(0xFF); GPIO_SetBits(GPIOA, W25Q64_CS); } void W25Q64_WriteStatus(uint8_t status) { GPIO_ResetBits(GPIOA, W25Q64_CS); SPI_SendByte(0x01); SPI_SendByte(status); GPIO_SetBits(GPIOA, W25Q64_CS); } void W25Q64_EraseChip(void) { W25Q64_WriteEnable(); GPIO_ResetBits(GPIOA, W25Q64_CS); SPI_SendByte(0xC7); GPIO_SetBits(GPIOA, W25Q64_CS); } void W25Q64_EraseSector(uint32_t address) { W25Q64_WriteEnable(); GPIO_ResetBits(GPIOA, W25Q64_CS); SPI_SendByte(0x20); SPI_SendByte((address >> 16) & 0xFF); SPI_SendByte((address >> 8) & 0xFF); SPI_SendByte(address & 0xFF); GPIO_SetBits(GPIOA, W25Q64_CS); } void W25Q64_WritePage(uint32_t address, uint8_t *data, uint32_t length) { uint32_t i; W25Q64_WriteEnable(); GPIO_ResetBits(GPIOA, W25Q64_CS); SPI_SendByte(0x02); SPI_SendByte((address >> 16) & 0xFF); SPI_SendByte((address >> 8) & 0xFF); SPI_SendByte(address & 0xFF); for (i = 0; i < length; i++) { SPI_SendByte(data[i]); } GPIO_SetBits(GPIOA, W25Q64_CS); } void W25Q64_ReadData(uint32_t address, uint8_t *data, uint32_t length) { uint32_t i; GPIO_ResetBits(GPIOA, W25Q64_CS); SPI_SendByte(0x03); SPI_SendByte((address >> 16) & 0xFF); SPI_SendByte((address >> 8) & 0xFF); SPI_SendByte(address & 0xFF); for (i = 0; i < length; i++) { data[i] = SPI_SendByte(0xFF); } GPIO_SetBits(GPIOA, W25Q64_CS); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值