合宙Air103|FLASH|内存|SPI|W25Q128 |数据手册|使能指令|抽象设备|ZBUFF|官方demo|学习(7):SPI FLASH-W25QXX

基础资料

基于Air103开发板:Air103 - LuatOS 文档

上手:开发上手 - LuatOS 文档

探讨重点

对官方SPI FLASH demo中功能的复现,进行相关内容的学习及探讨。

实现功能

功能:lua快速驱动-W25QXX(XX代表内存容量,本例采用128MB) ;

硬件准备

Air103开发板1块,面包板1块,W25Q128,导线若干。

软件版本

AIR103:LuatOS@AIR103 base 22.10 bsp V0013

软件使用

接口文档可参考:SPI库

硬件准备

  • air103开发板

  • SPI FLASH 这里选择W25Q128

  • 数据手册可参考同系列W25Q128数据手册

接线示意

SPI_CS/GPIO20 ----- CS

SPI_MISO/GPIO19 ----- DO

SPI_MOSI/GPIO21 ----- DI air103

SPI_CLK/GPIO18 ----- CLK

SPI_FLASH 3.3V ----- VCC

GND ----- GND

操作方式

SPI的操作有两种方式,一种是直接对SPI通道进行读写,一种是抽象为一个SPI设备再进行读写

1、直接对SPI通道进行读写

初始化CS引脚手动控制

使用air103时,需要我们手动控制CS拉低来使能设备

将spi.transfer封装成一个新的函数起来前后用CS操作环绕

function transfer(CS, spiID, data, sendLen, recvLen)
    CS(0)
    local res = spi.transfer(spiID, data, sendLen, recvLen)
    CS(1)
    return res
end

初始化SPI

初始化SPI通道0,24MHz CLK,半双工模式

local spiID, CS_GPIO = 0, 20
-- CS 参数传255代表手动控制CS引脚,最后一个mode参数传0代表半双工模式
local setupRes = spi.setup(spiID, 255, 0, 0, 8, 24 * 1000 * 1000, spi.MSB, 1, 0)
if setupRes ~= 0 then
    log.error(PROJECT .. ".setup", "ERROR")
    return
end

读取SPI FLASH的制造商和设备ID

查阅W25Q128数据手册可知 查询制造商和设备ID的指令为0x90

指令描述如下图

Read Manufacturer / Device ID (90h)

The Read Manufacturer/Device ID instruction is an alternative to the Release from Power-down / Device ID instruction that provides both the JEDEC assigned manufacturer ID and the specific device ID.

The Read Manufacturer/Device ID instruction is very similar to the Release from Power-down / Device ID instruction. The instruction is initiated by driving the /CS pin low and shifting the instruction code “90h” followed by a 24-bit address (A23-A0) of 000000h. After which, the Manufacturer ID for Winbond (EFh) and the Device ID are shifted out on the falling edge of CLK with most significant bit (MSB) first as shown in figure 26. The Device ID values for the W25Q80, W25Q16, and W25Q32 are listed in Manufacturer and Device Identification table. If the 24-bit address is initially set to 000001h the Device ID will be read first and then followed by the Manufacturer ID. The Manufacturer and Device IDs can be read continuously, alternating from one to the other. The instruction is completed by driving /CS high.

log.info(PROJECT .. ".chipID", string.toHex(transfer(CS, spiID, string.char(0x90, 0, 0, 0), 4, 2)))

日志如下

I/user.spi.chipID EF17 4

返回值0xEF17中EF为制造商Winbond Serial Flash,17为型号W25Q128JV

擦除指定地址扇区

查阅W25Q128数据手册可知 擦除指定地址扇区的指令为0x20(Sector Erase (4KB))

在执行擦除指令之前需要执行写使能指令0x06

擦除地址0x01开始的4K-bytes大小的扇区

1.1.1 Instruction Set Table 1 (1)

INSTRUCTION NAME

BYTE 1 (CODE)

BYTE 2

BYTE 3

BYTE 4

BYTE 5

BYTE 6

Write Enable

06h

Write Disable

04h

Read Status
Register-1

05h

(S7–S0) (2)

Read Status
Register-2

35h

(S15-S8) (2)

Write Status
Register

01h

(S7–S0)

(S15-S8)

Page Program

02h

A23–A16

A15–A8

A7–A0

(D7–D0)

Quad Page
Program

32h

A23–A16

A15–A8

A7–A0

(3)
(D7–D0, …)

Block Erase
(64KB)

D8h

A23–A16

A15–A8

A7–A0

Block Erase
(32KB)

52h

A23–A16

A15–A8

A7–A0

Sector Erase
(4KB)

20h

A23–A16

A15–A8

A7–A0

Chip Erase

C7h/60h

Erase Suspend

75h

Erase Resume

7Ah

Power-down

B9h

High Performance Mode

A3h

dummy

dummy

dummy

Mode Bit Reset (4)

FFh

FFh

Release Power down
or HPM / Device ID

ABh

dummy

dummy

dummy

(ID7-ID0) (5)

Manufacturer/ Device
ID(6)

90h

dummy

dummy

00h

(M7-M0)

(ID7-ID0)

Read Unique
ID(7)

4Bh

dummy

dummy

dummy

Dummy

(ID63-ID0)

JEDEC ID

9Fh

(M7-M0)
Manufacturer

(ID15-ID8)
Memory Type

(ID7-ID0)
Capacity

代码如下

spiFlash:send(string.char(0x06))
sys.wait(100)
spiFlash:send(string.char(0x20, 0x00, 0x00, 0x01))
-- 擦除需要消耗一定的时间
sys.wait(1000)

20h

A23–A16

A15–A8

A7–A0

-- 20h开头,每个字节描述,发送4byte,接收0byte transfer(CS, spiID, string.char(0x20, 0x00, 0x00, 0x01), 4, 0) -- 擦除需要消耗一定的时间 sys.wait(1000)

读写SPI FLASH

Page Program

02h

A23–A16

A15–A8

A7–A0

(D7–D0)

查阅W25Q128数据手册可知 页写的指令为0x02,一次最多写入256字节的数据,读取数据的指令为0x03

1.1.2 Instruction Set Table 2 (Read Instructions)

INSTRUCTION NAME

BYTE 1 (CODE)

BYTE 2

BYTE 3

BYTE 4

BYTE 5

BYTE 6

Read Data

03h

A23–A16

A15–A8

A7–A0

(D7–D0)

Fast Read

0Bh

A23–A16

A15–A8

A7–A0

dummy

(D7–D0)

Fast Read Dual Output

3Bh

A23–A16

A15–A8

A7–A0

dummy

(1)
(D7–D0, …)

Fast Read Dual I/O

BBh

(2)
A23-A8

(2)
A7-A0, M7-M0

(1)
(D7–D0, …)

Fast Read
Quad Output

6Bh

A23–A16

A15–A8

A7–A0

dummy

(3)
(D7–D0, …)

Fast Read
Quad I/O

EBh

(4)
A23-A0, M7-M0

(5)
(x,x,x,x, D7–D0, …)

(3)
(D7-D0, …)

在执行擦除指令之前需要执行写使能指令0x06

代码如下

spiFlash:send(string.char(0x06))
sys.wait(100)
spiFlash:send(string.char(0x02, 0x00, 0x00, 0x01) .. PROJECT)
sys.wait(100)
local readRes = spiFlash:transfer(string.char(0x03, 0x00, 0x00, 0x01), 4, string.len(PROJECT))
log.info(PROJECT .. ".readRes", readRes)

日志如下

I/user.spi.readRes spi

观察日志,读出的数据与我们写入的数据一致

禁用写使能并关闭对应的SPI通道和CS GPIO

查阅W25Q128数据手册可知 禁用写使能的指令为0x04

1.1.1 rite Disable (04h)

The Write Disable instruction (Figure 5) resets the Write Enable Latch (WEL) bit in the Status Register to a 0. The Write Disable instruction is entered by driving /CS low, shifting the instruction code “04h” into the DI pin and then driving /CS high. Note that the WEL bit is automatically reset after Power-up and upon completion of the Write Status Register, Page Program, Sector Erase, Block Erase and Chip Erase instructions.

代码如下

spiFlash:send(string.char(0x04))
log.info(PROJECT .. ".device_close", spiFlash:close())

2、抽象为一个SPI设备再进行读写

抽象SPi外设

抽象SPI通道0的SPI FLASH,24MHz CLK,半双工模式

代码如下

local spiID, CS_GPIO = 0, 20  --20(GPIO)=pin.PB04
-- CS 参数传255代表手动控制CS引脚,最后一个mode参数传0代表半双工模式
    --spi.setup(id, cs(255,手动控制), CPHA(极性), CPOL(相位), dataw(数据宽度,默认8bit), bandrate(默认2M=2000000), bitdict(大小端, 默认spi.MSB(大端首先存储 MSB 字节,即高字节存储在低地址;)
                    --, 可选spi.LSB(小端,则首先存储LSB字节,即低字节存储在低地址。)),ms(主从设置, 默认主1, 可选从机0. 通常只支持主机模式), mode(工作模式, 全双工1, 半双工0, 默认全双工))
    
local spiFlash = spi.deviceSetup(spiID, CS_GPIO, 0, 0, 8, 24 * 1000 * 1000, spi.MSB, 1, 0)

读取SPI FLASH的制造商和设备ID

查阅W25Q128数据手册可知 查询制造商和设备ID的指令为0x90

代码如下

log.info(PROJECT .. ".chipID", string.toHex(spiFlash:transfer(string.char(0x90, 0, 0, 0), 4, 2)))

日志如下

I/user.spi.chipID EF16 4

返回值0xEF17中EF为制造商Winbond Serial Flash,16为型号W25Q64

擦除指定地址扇区

查阅W25Q128数据手册可知 擦除指定地址扇区的指令为0x20

在执行擦除指令之前需要执行写使能指令0x06

擦除地址0x01开始的4K-bytes大小的扇区

代码如下

spiFlash:send(string.char(0x06))
sys.wait(100)
spiFlash:send(string.char(0x20, 0x00, 0x00, 0x01))
-- 擦除需要消耗一定的时间
sys.wait(1000)

读写SPI FLASH

查阅W25Q128数据手册可知 页写的指令为0x02,一次最多写入256字节的数据,读取数据的指令为0x03

在执行擦除指令之前需要执行写使能指令0x06

spiFlash:send(string.char(0x06))
sys.wait(100)
spiFlash:send(string.char(0x02, 0x00, 0x00, 0x01) .. PROJECT)
sys.wait(100)
local readRes = spiFlash:transfer(string.char(0x03, 0x00, 0x00, 0x01), 4, string.len(PROJECT))
log.info(PROJECT .. ".readRes", readRes)

日志如下

I/user.spi.readRes spi

观察日志,读出的数据与我们写入的数据一致

禁用写使能并关闭对应的SPI通道

查阅W25Q128数据手册可知 禁用写使能的指令为0x04

spiFlash:send(string.char(0x04)) log.info(PROJECT .. ".device_close", spiFlash:close())

代码注释

function transfer(CS, spiID, data, sendLen, recvLen)
    CS(0)
    local res = spi.transfer(spiID, data, sendLen, recvLen)
    CS(1)
    return res
end

local function test()
    local spiID, CS_GPIO = 0, 20
    local setupRes = spi.setup(spiID, 255, 0, 0, 8, 10 * 1000 * 1000, spi.MSB, 1, 0)
    if setupRes ~= 0 then
        log.error(PROJECT .. ".setup", "ERROR")
        return
    end
    local CS = gpio.setup(CS_GPIO, 0)
    log.info(PROJECT .. ".chipID", string.toHex(transfer(CS, spiID, string.char(0x90, 0, 0, 0), 4, 2)))
    transfer(CS, spiID, string.char(0x06), 1, 0)
    sys.wait(100)
    transfer(CS, spiID, string.char(0x20, 0x00, 0x00, 0x01), 4, 0)
    sys.wait(1000)
    transfer(CS, spiID, string.char(0x06), 1, 0)
    sys.wait(100)
    transfer(CS, spiID, string.char(0x02, 0x00, 0x00, 0x01) .. PROJECT, 4 + string.len(PROJECT), 0)
    sys.wait(100)
    local readRes = transfer(CS, spiID, string.char(0x03, 0x00, 0x00, 0x01), 4, string.len(PROJECT))
    log.info(PROJECT .. ".readRes", readRes)
    transfer(CS, spiID, string.char(0x04), 1, 0)
    gpio.close(CS_GPIO)
    spi.close(spiID)
    local spiFlash = spi.deviceSetup(spiID, CS_GPIO, 0, 0, 8, 10 * 1000 * 1000, spi.MSB, 1, 0)
    log.info(PROJECT .. ".chipID", string.toHex(spiFlash:transfer(string.char(0x90, 0, 0, 0), 4, 2)))
    spiFlash:send(string.char(0x06))
    sys.wait(100)
    spiFlash:send(string.char(0x20, 0x00, 0x00, 0x01))
    sys.wait(1000)
    spiFlash:send(string.char(0x06))
    sys.wait(100)
    spiFlash:send(string.char(0x02, 0x00, 0x00, 0x01) .. PROJECT)
    sys.wait(100)
    local readRes = spiFlash:transfer(string.char(0x03, 0x00, 0x00, 0x01), 4, string.len(PROJECT))
    log.info(PROJECT .. ".readRes", readRes)
    spiFlash:send(string.char(0x04))
    log.info(PROJECT .. ".device_close", spiFlash:close())
end
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

打酱油的工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值