芯片简介
这个驱动实际是基于AT26DF321写的,但是由于看了其他AT26DF系列芯片的手册,发现命令几乎一致,所以应该是可以直接使用在其他芯片上的。所以就直接写成AT26DF的驱动了。当然,如果真要用在其他芯片上,可能需要用户自己做一些测试。
AT26DF321是一款使用SPI通信的Flash芯片,容量4MB。其特性如下:
特性
- 2.7V-3.6V单电源
- 兼容串行外设接口(SPI)
— 支持SPI模式 0 和 3 - 66 MHz最大时钟频率
- 灵活、统一的擦除架构
— 4-Kbyte擦除块
— 32-Kbyte擦除块
— 64-Kbyte擦除块
— 整片擦除 - 独立的扇区保护,也可以全局保护/全局取消保护
— 64个64KB物理扇区 - 可使用硬件来锁定扇区的保护状态
- 灵活的编程
— 字节/页 编程(1至256字节) - 自动地检查并报告擦除/编程故障
- 遵从JEDEC标准的制造商及器件ID读取方法
- 低功耗
— 7 mA 活跃地读取时的电流(典型)
— 4 μA 深度下电模式下的电流(典型) - 可擦写次数:100,000次编程/擦除周期
- 数据保存:20年
- 符合工业级温度范围
- 可选的工业标准绿色(无铅/无卤化物/符合RoHS)封装
— 8脚SOIC(200-mil宽度)
— 16脚SOIC(300-mil宽度)
封装
框图
内存分布
操作简介
如上图所示,内存范围从0x000000到0x3FFFFF。共4M字节(特别注意,很容易看到手册上几个大字32-megabit就以为是32MB的了,那个是b,位,所以要除以8)。
整个4MB的范围按如上划分擦除的粒度为64KB、32KB和4KB,当然还有完全擦除,自然擦除越多花的时间越多。每次擦除时都需要传递一个地址,根据选择的粒度,地址所在的地址块会被完全擦除(全1)。如我地址为0x001034,擦除粒度为4KB,则0x001FFF到0x001000这个范围的数据会被擦除。
编程时以256字节的块为单位。一次编程操作只能在这个块中进行。如果传输的字节数超过了256个字节,则会循环回来覆盖之前的。另外,如果超过了区块的边界,也会转回当前区块的最前面开始编程。比如,我从0xFE开始写了4个字节的数据:A、B、C、D。则如成功的话,实际A、B、C、D分别在地址0xFE、0xFF、0x00、0x01。
另外,很多操作(编程、擦除、扇区保护、扇区解保护、写状态寄存器)执行前都需要先提交WriteEnable命令,使得状态寄存器中的WEL位变为1,然后才能执行。而且提交后,不管成功与否,WEL都会重新变为0。
发出指令或操作的时序如下:
- 拉低CS引脚
- SPI主机发出一个有效的8位操作码
- 然后继续发跟指令相关的信息
- 拉高CS引脚
而所有命令如下:
驱动已经封装了所有的这些原子的操作,所以其实不用太关注这些。
保护策略简介
这块flash芯片有一个比较复杂的保护策略,混合了软件和硬件保护,这里只简单的说一下。
首先,每个物理扇区(64KB)都有一个扇区保护寄存器(Sector Protection Registers),简称SPR,上电后默认是1,保护状态。这个时候是无法对这块物理扇区进行编程或擦除操作的,需要提交扇区解保护命令对其解除绑定使其变为0,或者提交全局解保护命令让所有扇区的SPR变为0。
然后为了能成功提交解保护命令,又涉及到两个东西。一个是WP引脚,一个是状态寄存器SR中的SPRL(扇区保护寄存器锁定)位。
WP引脚不直接连接到内存阵列的本身,而是与SPRL位一同用于控制硬件锁定机制。为了锁定硬件,需要同时断言WP引脚(给它0)并设置SPRL位为1。激活硬件锁定的效果就是无法改变扇区保护寄存器和SPRL位的状态,于是保护扇区、解保护扇区和写状态寄存器命令会被忽略。为了解除硬件锁定,需要先解除断言WP引脚,然后使用写状态寄存器指令将SPRL位设置回0。
如果WP引脚永久连接地,那一旦SPRL位被设为了1。那唯一将其设回0的方法就是重启设备。SPRL位默认为0。
如果WP引脚连到了Vcc,SPRL位则可以随意更改。
另。全局解保护和全局保护命令是通过写状态寄存器来提交的,而写状态寄存器的同时也是在(尝试)修改SPRL的值,这在后面的图中会体现。
简要来说就是:
- 每个扇区都有一个SPR扇区保护寄存器,想要对其进行擦写操作,则这个寄存器必须为0。可它默认为1,需要提交扇区解保护命令或全局解保护命令来改它为0;
- 状态寄存器SR的SPRL位为1时无法更改扇区保护寄存器的状态,为0时可以更改,它默认为0;
- WP引脚上为0时,SPRL位只可以从默认的0改为1,而不能从1改为0;WP引脚为1时,则可以任意修改SPRL位。
后面几张图更详细的总结了各种情况:
状态寄存器SR
SR是很重要的一个寄存器,它反应了整个芯片的当前状态。大部分操作后你都会需要读取这个寄存器以确认操作的完成以及结果。
其有以下8个bit:
SPRL位用于控制是否可以修改扇区保护寄存器。详见之前对保护策略的介绍。
WPP位用于判断WP引脚的状态。
EPE位表明上一次擦除或者编程操作成功与否。当擦除或编程操作由于任何原因被抛弃时,EPE位不会置位。EPE位会在每次擦除或编程操作后更新。
SWP位用于表明设备的软保护状态,是全部都被保护了,部分被保护了,还是一个都没被保护。
WEL位表明当前是否可以写入。WEL位在设备重启或重置后默认为0。当编程\擦除\保护的操作码被完整写入后,不管操作成功与否,WEL都会被重置。
RDY/BSY位用于确定是否内部操作在进行中。可以轮询其以等待操作完成。
我们可以看到,这么多个位中,只有SPRL位是可以写的。其他都是只读的,所以写状态寄存器指令的目的只是修改SPRL的值,以及发出全局保护/解保护指令,详见前面的图。需要注意的是,提交写状态寄存器指令前也得提交WriteEnable指令另WEL位变为1。
驱动模块
模块简介
这个模块只封装了最底层的操作,全都是基于一个个命令的原子操作,所以要求用户对该芯片的运作原理有一定的知识。比如在编程命令发出后,如果你需要等待其完成,应该轮询状态寄存器,等待RDY/BSY位变为0。更底层的操作提供了更大的灵活性,但也对用户的要求更高,也许以后我会考虑在这之上做一个更高级的封装。
另外,这个模块也在头文件中定义了芯片的一些常量和结构体,如SR中各个位的掩码和偏移量等。
使用时最重要的就是要先向模块注册CS片选和SPI通信用的接口。
然后就是调用接口发送各个指令的事情了。
头文件
/*
*******************************************************************************************
*
* AT26DF MODULE
*
* File : AT26DF.h
* By : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date: 2019/04/08
* version: V1.0
* History: 2019/04/08 V1.0 the prototype
* NOTE(s): 1. Developed based on AT26DF321.
*******************************************************************************************
*/
#ifndef _AT26DF_H
#define _AT26DF_H
/*
*******************************************************************************************
* INCLUDES
*******************************************************************************************
*/
#include <stdint.h>
#include "common.h"
/*
*******************************************************************************************
* CONSTANT
*******************************************************************************************
*/
#define AT26DF_fSCK 66000000L // max Serial Clock (SCK) Frequency
#define AT26DF_fRDLF 33000000L // SCK Frequency for Read Array (Low Frequency–03h opcode)
#define AT26DF_tEDPD 3 // Chip Select High to Deep Power-Down (ns)
#define AT26DF_tRDPD 3 // Chip Select High to Standby Mode (ns)
#define AT26DF_OPC_READARRAY 0x0B
#define AT26DF_OPC_READARRAY_LOWFREQ 0x03
#define AT26DF_OPC_PROGRAM 0x02
#define AT26DF_OPC_BLOCKERASE_4KB 0x20
#define AT26DF_OPC_BLOCKERASE_32KB 0x52
#define AT26DF_OPC_BLOCKERASE_64KB 0xD8
#define AT26DF_OPC_CHIPERASE 0x60 // or 0xC7
#define AT26DF_OPC_WRITEENABLE 0x06
#define AT26DF_OPC_WRITEDISABLE 0x04
#define AT26DF_OPC_PROTECTSECTOR 0x36
#define AT26DF_OPC_UNPROTECTSECTOR 0x39
#define AT26DF_OPC_READSPR 0x3C // Read Sector Protection Registers
#define AT26DF_OPC_READSR 0x05 // Read Status Register
#define AT26DF_OPC_WRITESR 0x01 // Write Status Register
#define AT26DF_OPC_READDEVINFO 0x9F // Read Manufacturer and Device ID
#define AT26DF_OPC_DPD 0xB9 // Deep Power-Down
#define AT26DF_OPC_DPD_RESUME 0xAB // Resume from Deep Power-Down
// mask of Status Register bit fields
#define AT26DF_SRMASK_SPRL 0x80 // mask of Sector Protection Registers Locked bit(1 for locked)
#define AT26DF_SRMASK_RES 0x40 // mask of Reserved for future use bit
#define AT26DF_SRMASK_EPE 0x20 // mask of Erase/Program Error bit(1 for error detected)
#define AT26DF_SRMASK_WPP 0x10 // mask of Write Protect (WP) Pin Status bit(1 for high/deasserted)
#define AT26DF_SRMASK_SWP 0x0C // mask of Software Protection Status bits(see AT26DF_SWP_XXX)
#define AT26DF_SRMASK_WEL 0x02 // mask of Write Enable Latch Status bit(1 for write enabled)
#define AT26DF_SRMASK_BSY 0x01 // mask of Ready/Busy Status bit(1 for busy)
#define AT26DF_SRMASK_GUP 0x00 // mask to set SR with operation of Global Unprotect(xx0000xx)
#define AT26DF_SRMASK_GP 0x3C // mask to set SR with operation of Global Protect (xx1111xx)
// offset of Status Register bit fields
#define AT26DF_SROFST_SPRL 7 // offset of Sector Protection Registers Locked bit
#define AT26DF_SROFST_RES 6 // offset of Reserved for future use bit
#define AT26DF_SROFST_EPE 5 // offset of Erase/Program Error bit
#define AT26DF_SROFST_WPP 4 // offset of Write Protect (WP) Pin Status bit
#define AT26DF_SROFST_SWP 2 // offset of Software Protection Status bits
#define AT26DF_SROFST_WEL 1 // offset of Write Enable Latch Status bit
#define AT26DF_SROFST_BSY 0 // offset of Ready/Busy Status(1 for busy) bit
// value of Software Protection Status
// All sectors are software unprotected (all Sector Protection Registers are 0).
#define AT26DF_SWP_NONE 0
// Some sectors are software protected. Read individual Sector Protection Registers to
// determine which sectors are protected.
#define AT26DF_SWP_PART 1
// All sectors are software protected (all Sector Protection Registers are 1 – default).
#define AT26DF_SWP_ALL 3
/*
******************************************************************************************
* TYPE DEFINE
******************************************************************************************
*/
typedef uint32_t AT26DF_ADDR;
typedef struct {
uint8_t ManuID;
uint8_t DevID[2];
} AT26DF_DevInfo;
/*
************************************************************************************
* INTERFACES
************************************************************************************
*/
// description: Register SPI call back functions
// parameter : spi_rb callback function to read byte using SPI
// spi_wb callback function to write byte using SPI
// return :
// note : you should register SPI functions through this interface before use the driver.
void AT26DF_regFuncSPI(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb));
// description: Register SPI call back functions
// parameter : spi_rb callback function to burst read byte using SPI
// NULL if want to use the default function
// spi_wb callback function to burst write byte using SPI
// NULL if want to use the default function
// return :
// note : if you don't register any functions, the default functions will call the functions
// registered by the ATD26DF_regFuncSPI.
void AT26DF_regFuncSPIBurst(void (*spi_rb)(uint8_t* pBuf, uint16_t len),
void (*spi_wb)(const uint8_t* pBuf, uint16_t len));
// description: Registers call back function for ATD26DF select & deselect.
// parameter : cs_sel callback function for WIZCHIP select
// cs_desel callback fucntion for WIZCHIP deselect
// return :
// note : you should register CS functions through this interface before use the driver.
void AT26DF_regFuncCS(void(*cs_sel)(void), void(*cs_desel)(void));
// description: Sequentially read a continuous stream of data begun from the address specified.
// parameter : addr the starting address
// buf buffer to store the result.
// len the length of data you want to read.
// return :
// note : this function can be used at any SCK frequency up to the maximum specified by fSCK.
// if len is equal to 0, or buf == NULL, this function will do nothing.
void AT26DF_readArray(AT26DF_ADDR addr, uint8_t *buf, uint16_t len);
// description: Sequentially read a continuous stream of data begun from the address specified.
// parameter : addr the starting address
// buf buffer to store the result.
// len the length of data you want to read.
// return :
// note : this function can be used for lower frequency read operations up to the maximum
// specified by fRDLF
// if len is equal to 0, or buf == NULL, this function will do nothing.
void AT26DF_readArrayLowFreq(AT26DF_ADDR addr, uint8_t *buf, uint16_t len);
// description: allows anywhere from a single byte of data to 256 bytes of data to be programmed
// into previously erased memory locations.
// parameter : addr the starting address
// buf data buffer.
// len the length of data you want to write.
// return :
// note : if data goes beyond the end of the page will wrap around back to the beginning
// of the same page. So, only the last 256 bytes(size of a page) will be programmed
// into the page specified by addr.
void AT26DF_programInPage(AT26DF_ADDR addr, const uint8_t *buf, uint16_t len);
// description: Erase a block of 4K-, 32K-, or 64K-bytes
// parameter : addr any address in the block to be erased.
// return :
// note : Before a Block Erase command can be started, the WriteEnable command must have
// been previously issued to the device to set the WEL bit of the Status Register
// to a logical "1" state.
// for a 4K-byte erase, address bits A11-A0 will be ignored by the device and their
// values can be either a logical "1" or "0". For a 32K-byte erase, address bits
// A14-A0 will be ignored, and for a 64K-byte erase, address bits A15-A0 will be
// ignored by the device.
void AT26DF_blockErase4KB(AT26DF_ADDR addr);
void AT26DF_blockErase32KB(AT26DF_ADDR addr);
void AT26DF_blockErase64KB(AT26DF_ADDR addr);
// description: Erase the entire memory array.
// parameter :
// return :
// note : Before a Chip Erase command can be started, the Write Enable command must have
// been previously issued to the device to set the WEL bit of the Status Register
// to a logical "1" state.
// The erasing of the device is internally self-timed and should take place in a
// time of tCHPE.
// if any sector of the memory array is in the protected state, then the Chip Erase
// command will not be executed, and the device will return to the idle state once
// the CS pin has been deasserted
void AT26DF_chipErase(void);
// description: used to set the Write Enable Latch (WEL) bit in the Status Register to a logical
// "1" state.
// parameter :
// return :
// note : The WEL bit must be set before a program, erase, Protect Sector, Unprotect Sector,
// or Write Status Register command can be executed.
void AT26DF_writeEnable(void);
// description: used to reset the Write Enable Latch (WEL) bit in the Status Register to the
// logical "0" state.
// parameter :
// return :
// note : With the WEL bit reset, all program, erase, Protect Sector, Unprotect Sector,
// and Write Status Register commands will not be executed. The Write Disable
// command is also used to exit the Sequential Program Mode.
void AT26DF_writeDisable(void);
// description: to set the corresponding Sector Protection Register of the particular sector
// address to the logical "1" state indicating that sector are protected and
// cannot be programmed or erased.In addition, the WEL bit in the Status Register
// will be reset back to the logical "0" state.
// parameter : addr any address within the sector to be locked.
// return :
// note : Before the Protect Sector command can be issued, the Write Enable command must
// have been previously issued to set the WEL bit in the Status Register to a
// logical "1".
// Upon device power-up or after a device reset, each Sector Protection Register
// will default to the logical "1" state.
void AT26DF_protectSector(AT26DF_ADDR addr);
// description: to reset the corresponding Sector Protection Register to the logical "0" state.
// parameter : addr any address within the sector to be unlocked.
// return :
// note : Before the Protect Sector command can be issued, the Write Enable command must
// have been previously issued to set the WEL bit in the Status Register to a
// logical "1".
void AT26DF_unprotectSector(AT26DF_ADDR addr);
// description: Read the Sector Protection Registers to determine the current software protection
// status of the sector specified by the addr.
// parameter : addr any address within the sector.
// return : 00h if Sector Protection Register value is 0 (sector is unprotected).
// FFh if Sector Protection Register value is 1 (sector is protected).
// note :
uint8_t AT26DF_readSPR(AT26DF_ADDR addr);
// description: Read the Status Register to determine the device’s ready/busy status, as well as
// the status of many other functions such as Hardware Locking and Software Protection.
// parameter :
// return : the current value of SR.
// note : The Status Register can be read at any time, including during an internally
// self-timed program or erase operation.
uint8_t AT26DF_readSR(void);
// description: Continuously read the Status Register and pass it to the 'cond' callback function
// till the return value of 'cond' is TRUE.
// parameter : cond the call back function to return whether the SR match condition.
// return : the last value of SR(the one that make cond True).
// note :
uint8_t AT26DF_pendSRCondition(BOOL (*cond)(uint8_t sr));
// description: To modify the SPRL bit of the Status Register and/or perform a Global Protect or
// Global Unprotect operation.
// parameter : sr the byte(SPRL bit value, a don't care bit, four data bits to denote whether
// a Global Protect or Unprotect should be performed, and two additional don't
// care bits) to be written to SR.
// return :
// note : Before the Write Status Register command can be issued, the Write Enable command
// must have been previously issued to set the WEL bit in the Status Register to a
// logical “1”.
// To perform Global Protect or Global Unprotect operation, with SPRL be 0, writeSR
// with AT26DF_SRMASK_GUP or AT26DF_SRMASK_GP
void AT26DF_writeSR(uint8_t sr);
// description: Electronically query and identify the device.
// parameter : info return the device information, including the JEDEC defined Manufacturer ID,
// the vendor specific Device ID. See datasheet for details.
// return :
// note :
void AT26DF_readDevInfo(AT26DF_DevInfo *info);
// description: Place the device into an even lower power consumption state called the
// Deep Power-Down mode.
// parameter :
// return :
// note : When the CS pin is deasserted, the device will enter the Deep Power-Down mode
// within the maximum time of tEDPD.
// When the device is in the Deep Power-Down mode, all commands including the
// Read Status Register command will be ignored with the exception of the Resume
// from Deep Power-Down command.
// The Deep Power-Down command will be ignored if an internally self-timed operation
// such as a program or erase cycle is in progress. The Deep Power-Down command must
// be reissued after the internally self-timed operation has been completed in order
// for the device to enter the Deep Power-Down mode.
void AT26DF_deepPowerDown(void);
// description: To exit the Deep Power-Down mode and resume normal device operation.
// parameter :
// return :
// note : When the CS pin is deasserted, the device will exit the Deep Power-Down mode
// within the maximum time of tRDPD and return to the standby mode.
void AT26DF_deepPowerDownResume(void);
#endif
源文件
/*
*******************************************************************************************
*
* AT26DF MODULE
*
* File : AT26DF.c
* By : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date: 2019/04/08
* version: V1.0
* History: 2019/04/08 V1.0 the prototype
* NOTE(s):
*******************************************************************************************
*/
/*
*******************************************************************************************
* INCLUDES
*******************************************************************************************
*/
#include "AT26DF.h"
/*
*******************************************************************************************
* CONSTANT
*******************************************************************************************
*/
#define DONTCAREBYTE 0x37
/*
*******************************************************************************************
* LOCAL FUNCTIONS
*******************************************************************************************
*/
static uint8_t _spi_rb_default(void);
static void _spi_wb_default(uint8_t wb);
static void _spi_burst_rb_default(uint8_t* pBuf, uint16_t len);
static void _spi_burst_wb_default(const uint8_t* pBuf, uint16_t len);
static void _nullFunc(void);
static uint8_t (*_spiRead)(void) = _spi_rb_default;
static void (*_spiWrite)(uint8_t wb) = _spi_wb_default;
static void (*_spiBurstRead)(uint8_t* pBuf, uint16_t len) = _spi_burst_rb_default;
static void (*_spiBurstWrite)(const uint8_t* pBuf, uint16_t len) = _spi_burst_wb_default;
static void(*_csSel)(void) = _nullFunc;
static void(*_csDesel)(void) = _nullFunc;
static uint8_t _spi_rb_default(void){return 0;}
static void _spi_wb_default(uint8_t wb){(void)wb;}
static void _spi_burst_rb_default(uint8_t* pBuf, uint16_t len){
while(len > 0){
--len;
*pBuf++ = _spiRead();
}
}
static void _spi_burst_wb_default(const uint8_t* pBuf, uint16_t len){
while(len > 0){
--len;
_spiWrite(*pBuf++);
}
}
static void _nullFunc(void){}
static void _writeAddr(AT26DF_ADDR addr){
_spiWrite((uint8_t)(addr >> 16));
_spiWrite((uint8_t)(addr >> 8));
_spiWrite((uint8_t)addr);
}
static void _cmd(uint8_t op){
_csSel();
_spiWrite(op);
_csDesel();
}
static void _cmdWithAddr(uint8_t op, AT26DF_ADDR addr){
_csSel();
_spiWrite(op);
_writeAddr(addr);
_csDesel();
}
/*
*******************************************************************************************
* PUBLIC FUNCTIONS IMPLEMENTATION
*******************************************************************************************
*/
void AT26DF_regFuncSPI(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb)){
_spiRead = spi_rb;
_spiWrite = spi_wb;
}
void AT26DF_regFuncSPIBurst(void (*spi_rb)(uint8_t* pBuf, uint16_t len), void (*spi_wb)(const uint8_t* pBuf, uint16_t len)){
if(spi_rb)
_spiBurstRead = spi_rb;
else
_spiBurstRead = _spi_burst_rb_default;
if(spi_wb)
_spiBurstWrite = spi_wb;
else
_spiBurstWrite = _spi_burst_wb_default;
}
void AT26DF_regFuncCS(void(*cs_sel)(void), void(*cs_desel)(void)){
_csSel = cs_sel;
_csDesel = cs_desel;
}
void AT26DF_readArray(AT26DF_ADDR addr, uint8_t *buf, uint16_t len){
if(buf == NULL || len == 0)
return;
_csSel();
_spiWrite(AT26DF_OPC_READARRAY);
_writeAddr(addr);
_spiWrite(DONTCAREBYTE);
_spiBurstRead(buf,len);
_csDesel();
}
void AT26DF_readArrayLowFreq(AT26DF_ADDR addr, uint8_t *buf, uint16_t len){
if(buf == NULL || len == 0)
return;
_csSel();
_spiWrite(AT26DF_OPC_READARRAY_LOWFREQ);
_writeAddr(addr);
_spiBurstRead(buf,len);
_csDesel();
}
void AT26DF_programInPage(AT26DF_ADDR addr, const uint8_t *buf, uint16_t len){
if(buf == NULL || len == 0)
return;
_csSel();
_spiWrite(AT26DF_OPC_PROGRAM);
_writeAddr(addr);
_spiBurstWrite(buf,len);
_csDesel();
}
void AT26DF_blockErase4KB(AT26DF_ADDR addr){
_cmdWithAddr(AT26DF_OPC_BLOCKERASE_4KB,addr);
}
void AT26DF_blockErase32KB(AT26DF_ADDR addr){
_cmdWithAddr(AT26DF_OPC_BLOCKERASE_32KB,addr);
}
void AT26DF_blockErase64KB(AT26DF_ADDR addr){
_cmdWithAddr(AT26DF_OPC_BLOCKERASE_64KB,addr);
}
void AT26DF_chipErase(void){
_cmd(AT26DF_OPC_CHIPERASE);
}
void AT26DF_writeEnable(void){
_cmd(AT26DF_OPC_WRITEENABLE);
}
void AT26DF_writeDisable(void){
_cmd(AT26DF_OPC_WRITEDISABLE);
}
void AT26DF_protectSector(AT26DF_ADDR addr){
_cmdWithAddr(AT26DF_OPC_PROTECTSECTOR,addr);
}
void AT26DF_unprotectSector(AT26DF_ADDR addr){
_cmdWithAddr(AT26DF_OPC_UNPROTECTSECTOR,addr);
}
uint8_t AT26DF_readSPR(AT26DF_ADDR addr){
uint8_t ret;
_csSel();
_spiWrite(AT26DF_OPC_READSPR);
_writeAddr(addr);
ret = _spiRead();
_csDesel();
return ret;
}
uint8_t AT26DF_readSR(void){
uint8_t ret;
_csSel();
_spiWrite(AT26DF_OPC_READSR);
ret = _spiRead();
_csDesel();
return ret;
}
uint8_t AT26DF_pendSRCondition(BOOL (*cond)(uint8_t sr)){
uint8_t ret;
_csSel();
_spiWrite(AT26DF_OPC_READSR);
do{
ret = _spiRead();
} while (cond(ret) == FALSE);
_csDesel();
return ret;
}
void AT26DF_writeSR(uint8_t sr){
_csSel();
_spiWrite(AT26DF_OPC_WRITESR);
_spiWrite(sr);
_csDesel();
}
void AT26DF_readDevInfo(AT26DF_DevInfo *info){
_csSel();
_spiWrite(AT26DF_OPC_READDEVINFO);
info->ManuID = _spiRead();
_spiBurstRead(info->DevID,2);
_csDesel();
}
void AT26DF_deepPowerDown(void){
_cmd(AT26DF_OPC_DPD);
}
void AT26DF_deepPowerDownResume(void){
_cmd(AT26DF_OPC_DPD_RESUME);
}
使用示例
已略去不重要的代码,另外,assert只是随便用的,只是要表达“应该成立”的意思。
#include <stdio.h>
#include "SPI.h"
#include "AT26DF.h"
#define AT26DFSPI SPI0
#define AT26DFCSDDR DDRS_DDRS7
#define AT26DFCS PTS_PTS7
static const uint8_t data[4] = {0x12,0x34,0x56,0x78};
static uint8_t got[sizeof(data)];
static BOOL _SRnotBusy(uint8_t sr){
return !(sr & AT26DF_SRMASK_BSY);
}
static const SPI_INIT_STRUCT spi_init = {
TRUE,
SPI_SS_CTRL_OFF,
SPI_BITORDER_MSB,
SPI_INWAITMODE_STOP,
SPI_SCKFMT_MODE_3,
0,
0
};
static uint8_t AT26DFSPI_readByte(void){
return SPI_ExchangeChar(AT26DFSPI,0xEE);
}
static void AT26DFSPI_writeByte(uint8_t wb){
(void)SPI_ExchangeChar(AT26DFSPI,wb);
}
static void AT26DFSPI_CSselect(void){
AT26DFCS = 0;
}
static void AT26DFSPI_CSdeselect(void){
AT26DFCS = 1;
}
void main(void) {
int c;
uint8_t i;
uint8_t sr;
AT26DF_DevInfo info;
……
AT26DFCSDDR = 1;
AT26DFSPI_CSdeselect();
(void)SPI_InitExt(AT26DFSPI,(pSPI_INIT_STRUCT)&spi_init);
(void)SPI_Enable(AT26DFSPI);
// 注册SPI函数。SPI大量读写的注册可选,默认多次调用单次读写函数
AT26DF_regFuncSPI(AT26DFSPI_readByte, AT26DFSPI_writeByte);
AT26DF_regFuncCS(AT26DFSPI_CSselect, AT26DFSPI_CSdeselect);
// 读取设备信息
AT26DF_readDevInfo(&info);
// 读取状态寄存器
sr = AT26DF_readSR();
// 验证所有扇区都是保护状态
for(i = 0; i < 64; i++)
assert(0xFF == AT26DF_readSPR(((uint32_t)i) << 16));
// 使能写
AT26DF_writeEnable();
// WEL位应该变为1
assert(AT26DF_readSR() & AT26DF_SRMASK_WEL);
// 全局解保护
AT26DF_writeSR(AT26DF_SRMASK_GUP);
// WEL位应该变为0
assert(!(AT26DF_readSR() & AT26DF_SRMASK_WEL));
// 验证所有扇区都是无保护状态
for(i = 0; i < 64; i++)
assert(0x00 == AT26DF_readSPR(((uint32_t)i) << 16));
AT26DF_writeEnable();
// 编程0x34开始的地址,写入data中的数据
AT26DF_programInPage(0x34,data,sizeof(data));
// 等待操作完成(通过SR寄存器的值)
(void)AT26DF_pendSRCondition(_SRnotBusy);
// 读出来的值应该与data中的一致
AT26DF_readArray(0x34,got,sizeof(data));
……
for(;;) {
// 接收字符并回显
c = getchar();
putchar((char)c);
} /* loop forever */
/* please make sure that you never leave main */
}