嵌入式开发——SPI Flash
发布时间
阅读量:
阅读量
学习目标
- 掌握w25q128的移植
- 熟悉驱动开发学习方式
学习内容
原理图

W25Q128介绍
W25Q128是一种广泛应用的串行闪存器件,在SPI(Serial Peripheral Interface)接口协议的支持下运行良好。该器件具备快速读取和擦除数据的能力,并广泛应用于存储和读取操作。芯片容量为128 M-bit(16 M-byte),其中名称后的数字代表不同的容量选项。不同的型号和容量选项可以满足不同应用的需求,例如W25Q16、W25Q32、W25Q64等。通常被用于嵌入式设备、存储设备以及路由器等高性能电子设备中。
W25Q128闪存芯片遵循扇区和块的划分原则进行内存分配。每个扇区容量设置为4KB;每一块由16个扇区组成;由此可知每一块的总容量为64KB。

实现参考
封装改造
官方示例中,耦合太强,进行一些修改,修改后如下:
bsp_w25q64.h
#ifndef _BSP_W25Q64_H__
#define _BSP_W25Q64_H__
#include "gd32f4xx.h"
#include "SPI.h"
#define W25Q_CS_PORT_RCU RCU_GPIOF
#define W25Q_CS_PORT GPIOF
#define W25Q_CS_PIN GPIO_PIN_6
#define W25Q_CS_SELECT() gpio_bit_write(W25Q_CS_PORT, W25Q_CS_PIN, RESET)
#define W25Q_CS_UNSELECT() gpio_bit_write(W25Q_CS_PORT, W25Q_CS_PIN, SET)
#define W25Q_SPI_RD_WR(data) SPI4_read_write(data)
void W25Q64_init(void);
uint16_t W25Q64_readID(void);
void W25Q64_write(uint8_t* buffer, uint32_t addr, uint16_t numbyte);
void W25Q64_read(uint8_t* buffer,uint32_t read_addr,uint16_t read_length) ;
#endif
bsp_w25q64.c
#include "bsp_w25q64.h"
void W25Q64_init(void)
{
//开启CS引脚时钟
rcu_periph_clock_enable(W25Q_CS_PORT_RCU);
//配置CS引脚模式
gpio_mode_set(W25Q_CS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, W25Q_CS_PIN);
//配置CS输出模式
gpio_output_options_set(W25Q_CS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q_CS_PIN);
//W25Q64不选中
W25Q_CS_UNSELECT();
}
/****************************************************************** * 函 数 名 称:spi_read_write_byte
* 函 数 说 明:硬件SPI的读写
* 函 数 形 参:dat=发送的数据
* 函 数 返 回:读取到的数据
* 作 者:LC
* 备 注:无
******************************************************************/
static uint8_t spi_read_write_byte(uint8_t dat)
{
return W25Q_SPI_RD_WR(dat);
}
/****************************************************************** * 函 数 名 称:W25Q64_readID
* 函 数 说 明:读取W25Q64的厂商ID和设备ID
* 函 数 形 参:无
* 函 数 返 回:设备正常返回EF16
* 作 者:LC
* 备 注:无
******************************************************************/
uint16_t W25Q64_readID(void)
{
uint16_t temp = 0;
gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);
spi_read_write_byte(0x90);//发送读取ID命令
spi_read_write_byte(0x00);
spi_read_write_byte(0x00);
spi_read_write_byte(0x00);
//接收数据
temp |= spi_read_write_byte(0xFF)<<8;
temp |= spi_read_write_byte(0xFF);
gpio_bit_write(GPIOF, GPIO_PIN_6, SET);
return temp;
}
/********************************************************** * 函 数 名 称:W25Q64_wait_busy
* 函 数 功 能:判断W25Q64是否忙
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
**********************************************************/
static void W25Q64_wait_busy(void)
{
unsigned char byte = 0;
do
{
gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);
spi_read_write_byte(0x05);
byte = spi_read_write_byte(0Xff);
gpio_bit_write(GPIOF, GPIO_PIN_6, SET);
}while( ( byte & 0x01 ) == 1 );
}
/********************************************************** * 函 数 名 称:W25Q64_write_enable
* 函 数 功 能:发送写使能
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
**********************************************************/
void W25Q64_write_enable(void)
{
W25Q_CS_SELECT();
spi_read_write_byte(0x06);
W25Q_CS_UNSELECT();
}
/********************************************************** * 函 数 名 称:W25Q64_erase_sector
* 函 数 功 能:擦除一个扇区
* 传 入 参 数:addr=擦除的扇区号
* 函 数 返 回:无
* 作 者:LC
* 备 注:addr=擦除的扇区号,范围=0~15
**********************************************************/
void W25Q64_erase_sector(uint32_t addr)
{
addr *= 4096;
W25Q64_write_enable(); //写使能
W25Q64_wait_busy(); //判断忙
gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);
spi_read_write_byte(0x20);
spi_read_write_byte((uint8_t)((addr)>>16));
spi_read_write_byte((uint8_t)((addr)>>8));
spi_read_write_byte((uint8_t)addr);
gpio_bit_write(GPIOF, GPIO_PIN_6, SET);
//等待擦除完成
W25Q64_wait_busy();
}
/********************************************************** * 函 数 名 称:W25Q64_write
* 函 数 功 能:写数据到W25Q64进行保存
* 传 入 参 数:buffer=写入的数据内容 addr=写入地址 numbyte=写入数据的长度
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
**********************************************************/
void W25Q64_write(uint8_t* buffer, uint32_t addr, uint16_t numbyte)
{ //0x02e21
unsigned int i = 0;
W25Q64_erase_sector(addr/4096);//擦除扇区数据
W25Q64_write_enable();//写使能
W25Q64_wait_busy(); //忙检测
//写入数据
W25Q_CS_SELECT();
spi_read_write_byte(0x02);
spi_read_write_byte((uint8_t)((addr)>>16));
spi_read_write_byte((uint8_t)((addr)>>8));
spi_read_write_byte((uint8_t)addr);
for(i=0;i<numbyte;i++)
{
spi_read_write_byte(buffer[i]);
}
W25Q_CS_UNSELECT();
W25Q64_wait_busy(); //忙检测
}
/********************************************************** * 函 数 名 称:W25Q64_read
* 函 数 功 能:读取W25Q64的数据
* 传 入 参 数:buffer=读出数据的保存地址 read_addr=读取地址 read_length=读去长度
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
**********************************************************/
void W25Q64_read(uint8_t* buffer,uint32_t read_addr,uint16_t read_length)
{
uint16_t i;
W25Q_CS_SELECT();
spi_read_write_byte(0x03);
spi_read_write_byte((uint8_t)((read_addr)>>16));
spi_read_write_byte((uint8_t)((read_addr)>>8));
spi_read_write_byte((uint8_t)read_addr);
for(i=0;i<read_length;i++)
{
buffer[i]= spi_read_write_byte(0XFF);
}
W25Q_CS_UNSELECT();
}
测试逻辑如下:
main.c
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "Usart0.h"
#include "SPI.h"
#include "bsp_w25q64.h"
#include "string.h"
uint8_t cnt = 0;
void Usart0_on_recv(uint8_t* data, uint32_t len) {
printf("recv: %X\r\n", data[0]);
if(data[0] == 0x00) {
printf("ID = %X\r\n",W25Q64_readID());
} else if(data[0] == 0x01) {
char buff[128];
sprintf(buff, "hello: %d", cnt++);
printf("write: %s (%d)\r\n", buff, strlen(buff));
W25Q64_write((uint8_t*)buff, 0, strlen(buff));
} else if(data[0] == 0x02) {
uint8_t buff[128];
W25Q64_read(buff, 0, 16);
printf("read: %s (%d)\r\n", (char*)buff, strlen((char*)buff));
}
}
int main(void)
{
systick_config();
Usart0_init();
SPI_init();
W25Q64_init();
while(1)
{
}
}
练习题
- 实现GD32平台w25q128的驱动移植
- 实现STM32平台w25q128的驱动移植
全部评论 (0)
还没有任何评论哟~
