这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【分享开发笔记,赚取电动螺丝刀】Arduino下ESP8266读写W25Q128

共2条 1/1 1 跳转至

【分享开发笔记,赚取电动螺丝刀】Arduino下ESP8266读写W25Q128

专家
2025-07-22 09:44:28     打赏

W25Q128是一款大容量SPI FLASH 产品,容量为128Mb。擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V,支持高达104MHz的串行时钟通讯。

图片1.png

我买的是W25Q128模块,HOLD引脚和WP引脚都接入了3.3V。

图片2.png


原理图:

图片3.png

ESP8266模块的引脚图:

图片4.png

使用D5\D6\D7\D8四个引脚连接模块

图片5.png

为了验证去读取的操作是否正常,预先使用其它编程器改写了部分单元的内容(从地址0xFFFFF0开始的额6个单元的数据:00、01、02、03、04、05、06、07、08、09、10、11、12、13、14、15)。

W25Q128的资料在其它帖子中简单做了说明(W25Q128的简单说明资料)。

程序代码如下:

/**
本例使用软件模拟方式读写操作W25Q128
*/
#include <SPI.h>
#define NORFLASH_CS_PIN         15        // D8
#define NORFLASH_CLK_PIN        14        // D5
#define NORFLASH_MOSI_PIN       13        // D7       
#define NORFLASH_MISO_PIN       12        // D6
#define NORFLASH_HOLD_PIN       -1        // hold引脚接3.3V
#define NORFLASH_WP_PIN         -1        // wp引脚接3.3V
// 操作W25Q128的指令
#define ManufactDeviceID_CMD    0x90   // 制造商设备ID
#define READ_JEDEC_ID_CMD       0x9F   // JEDEC ID:标识制造商和设备类型的重要代码
#define WRITE_STATUS            0x01   // 写状态寄存器
#define READ_STATU_REGISTER_1   0x05   // 读状态寄存器1
#define READ_STATU_REGISTER_2   0x35   // 读状态寄存器2
#define READ_DATA_CMD           0x03   // 读数据
#define WRITE_ENABLE_CMD        0x06   // 写使能
#define WRITE_DISABLE_CMD       0x04   // 写失能
#define SECTOR_ERASE_CMD        0x20   // 扇区擦除
#define CHIP_ERASE_CMD          0xC7   // 全片擦除
#define PAGE_PROGRAM_CMD        0x02   // 页编程
#define ONE_PAGE_SIZE           256    // 页数据字节数量 
#define SPI_FREQUENCY           40 * 1000000    // SPI通讯速度,SCK频率
/* 初始化SPI */
void norflash_spi_init() {
    // gpio init
    pinMode(NORFLASH_HOLD_PIN, OUTPUT);
    pinMode(NORFLASH_WP_PIN, OUTPUT);  
    digitalWrite(NORFLASH_HOLD_PIN, HIGH);
    digitalWrite(NORFLASH_WP_PIN, HIGH);
    pinMode(NORFLASH_CS_PIN, OUTPUT);  
    digitalWrite(NORFLASH_CS_PIN, HIGH);
    // 软件模拟方式
    pinMode(NORFLASH_CLK_PIN, OUTPUT);  
    pinMode(NORFLASH_MOSI_PIN, OUTPUT);
    pinMode(NORFLASH_MISO_PIN, INPUT);
    digitalWrite(NORFLASH_CLK_PIN, LOW);
    delay(1);
    uint8_t data = 0;
    write_enable();                         // 开放写
    data = read_status();                   // 读状态,确认是否可写
    Serial.printf("norflash write enable status:");
    Serial.println(data, BIN);
    // 读取设备ID
    uint16_t device_id = 0;
    device_id = read_norflash_id();
    Serial.printf("norflash device id: 0x%04X", device_id);
}
/* 写字节数据 */
void write_byte(uint8_t data) {
    for(uint8_t i = 0; i < 8; i++) {    
        uint8_t status;
        status = data & (0x80 >> i);
        digitalWrite(NORFLASH_MOSI_PIN, status);
        digitalWrite(NORFLASH_CLK_PIN, LOW);
        digitalWrite(NORFLASH_CLK_PIN, HIGH);  
    }
}
/* 读一字节 */
uint8_t read_byte(uint8_t tx_data) {  
    // 软件模拟方式
    uint8_t i = 0, data = 0;
    for(i = 0; i < 8; i++) {        
        digitalWrite(NORFLASH_CLK_PIN, HIGH);
        digitalWrite(NORFLASH_CLK_PIN, LOW);
        if(digitalRead(NORFLASH_MISO_PIN)) {
            data |= 0x80 >> i;
        }
    }
    return data;
}
/* 允许写 */
void write_enable() {
    digitalWrite(NORFLASH_CS_PIN, LOW);
    write_byte(WRITE_ENABLE_CMD);
    digitalWrite(NORFLASH_CS_PIN, HIGH);
}
/* 禁止写*/
void write_disable() {
    digitalWrite(NORFLASH_CS_PIN, LOW);
    write_byte(WRITE_DISABLE_CMD);
    digitalWrite(NORFLASH_CS_PIN, HIGH);
}
/* 读状态 */
uint8_t read_status() {
    uint8_t status = 0;
    digitalWrite(NORFLASH_CS_PIN, LOW);
    write_byte(READ_STATU_REGISTER_1);      
    status = read_byte(0);                      
    digitalWrite(NORFLASH_CS_PIN, HIGH);  
    return status;
}
/* 检查是否忙 */
void check_busy(char *str) {
    while(read_status() & 0x01) {
    #ifdef NORFLASH_DEBUG_ENABLE
        Serial.printf("status = 0, flash is busy of %s\n", str);  
    #endif 
    }
}
/* 写入少于一个块(256字节)的数据 */
void write_one_block_data(uint32_t addr, uint8_t * pbuf, uint16_t len) {
    uint16_t i;
    check_busy("write_one_block_data");
    write_enable();                              
    digitalWrite(NORFLASH_CS_PIN, LOW);          
    write_byte(PAGE_PROGRAM_CMD);                
    write_byte((uint8_t)(addr >> 16));            
    write_byte((uint8_t)(addr >> 8));  
    write_byte((uint8_t)addr);      
    for(i = 0; i < len; i++) {
        write_byte(*pbuf++);  
    }
    digitalWrite(NORFLASH_CS_PIN, HIGH);                              
    check_busy("write_one_block_data");
} 
/* 写入少于一个扇区(4096字节)长度的数据  */
void write_one_sector_data(uint32_t addr, uint8_t * pbuf, uint16_t len) { 
    uint16_t free_space, head, page, remain;  
    free_space = ONE_PAGE_SIZE - addr % ONE_PAGE_SIZE;  
    if(len <= free_space)  {
        head = len;
        page = 0;                      
        remain = 0;
    }
    if(len > free_space) {
        head = free_space;
        page = (len - free_space) / ONE_PAGE_SIZE;    
        remain = (len - free_space) % ONE_PAGE_SIZE;
    }
   
    if(head != 0) {
        Serial.print("head:");
        Serial.println(head);
        write_one_block_data(addr, pbuf, head);
        pbuf = pbuf + head;
        addr = addr + head;
    }
    if(page != 0) {
        Serial.print("page:");
        Serial.println(page);
        for(uint16_t i = 0; i < page; i++) {
            write_one_block_data(addr, pbuf, ONE_PAGE_SIZE);
            pbuf = pbuf + ONE_PAGE_SIZE;
            addr = addr + ONE_PAGE_SIZE;
        }
    }
    if(remain != 0) {
        Serial.print("remain:");
        Serial.println(remain);
        write_one_block_data(addr, pbuf, remain);
    }  
}
/* Write arbitrary length data */
void write_arbitrary_data(uint32_t addr, uint8_t* pbuf, uint32_t len)  { 
    uint32_t secpos;
    uint16_t secoff;
    uint16_t secremain;    
    uint16_t i;    
    uint8_t *write_buf = pbuf;  
    uint8_t save_buffer[4096];  // 保存扇区原始数据并添加新数据  
    secpos = addr / 4096;       // 扇区编号
    secoff = addr % 4096;       // 扇区地址偏移量
    secremain = 4096 - secoff;  // 扇区剩余空间   
   
    if(len <= secremain) {
        secremain = len;        // 扇区剩余空间小于4096
    }
    while(1) {  
        read_data(secpos * 4096, save_buffer, 4096); // read sector data
        for(i = 0; i < secremain; i++) {
            // 检查数据,如果所有数据都是0xFF,则不需要擦除扇区
            if(save_buffer[secoff + i] != 0XFF) {
                // 不需要擦除扇区
                break;    
            }
        }
        if(i < secremain) {
            // 擦除扇区并写入数据
            sector_erase(secpos);
            for(i = 0; i < secremain; i++)  {
                save_buffer[i + secoff] = write_buf[i];  // add new data 
            }
            write_one_sector_data(secpos * 4096, save_buffer, 4096); // write sector
        } else {
            // 不需要擦除扇区
            write_one_sector_data(addr, write_buf, secremain);  
        }
        if(len == secremain) {
            // 完成写入
            break;
        } else {
            // 继续写入
            secpos ++;  // 扇区编号 + 1
            secoff = 0; // 扇区地址偏移量 = 0   
            write_buf += secremain;  // 写缓冲偏移
            addr += secremain;       // 地址     
            len -= secremain;        // 数据长度
            if(len > 4096) {
                // 剩余数据超过一个扇区(4096字节)
                secremain = 4096;    
            } else {
                // 剩余数据少于一个扇区(4096字节)
                secremain = len;            
            }
        }    
    }    
}
/* 读取任意长度的数据 */
void read_data(uint32_t addr24, uint8_t *pbuf, uint32_t len) {
    check_busy("read_data");
    digitalWrite(NORFLASH_CS_PIN, LOW);
    write_byte(READ_DATA_CMD);
    write_byte((uint8_t)(addr24 >> 16));  
    write_byte((uint8_t)(addr24 >> 8));  
    write_byte((uint8_t)addr24);  
    for(uint32_t i = 0; i < len; i++) {
        *pbuf = read_byte(0xFF);
        pbuf ++;
    }
    digitalWrite(NORFLASH_CS_PIN, HIGH);
}
/* 擦除扇区 */
void sector_erase(uint32_t addr24)  {
    addr24 *= 4096;  
    check_busy("sector_erase");
    write_enable();                           // WP=0,允许写操作          
   
    digitalWrite(NORFLASH_CS_PIN, LOW);       // CS = 0,允许操作
    write_byte(SECTOR_ERASE_CMD);             // 发送清除扇区指令:   
    write_byte((uint8_t)(addr24 >> 16));      // 发送地址
    write_byte((uint8_t)(addr24 >> 8));  
    write_byte((uint8_t)addr24);  
     
    digitalWrite(NORFLASH_CS_PIN, HIGH);      // CS = 1,禁止操作
    check_busy("sector_erase");
} 
/* Read norflash id */
uint16_t read_norflash_id() {
    uint8_t data = 0;
    uint16_t device_id = 0;
    digitalWrite(NORFLASH_CS_PIN, LOW);
    write_byte(ManufactDeviceID_CMD);             // 发送读取ID的指令
    write_byte(0x00);
    write_byte(0x00);
    write_byte(0x00);
    data = read_byte(0);
    device_id |= data;  // low byte
    data = read_byte(0);
    device_id |= (data << 8);  // high byte
    digitalWrite(NORFLASH_CS_PIN, HIGH);
   
    return device_id;
}
void setup() {
    uint16_t id=0;
    Serial.begin(115200);
    norflash_spi_init();
    int g = 0;
    uint8_t str[1280];
    memset(str, 0, sizeof(str));
    unsigned int  j = 0;      
    for(int k=0; k < 5; k++) {
        for(int i = 0; i < 256; i++) {
            str[j] = i;
            j++;
        }
    }
    Serial.println("");
    Serial.println("-----write data-------");
    sector_erase(0x00);
    write_one_sector_data(0x00, str, 512);
    memset(str, 0, sizeof(str));
    id = read_norflash_id();
    Serial.printf("IC ID = %04X \n", id);
    read_data(0x00, str, 512);
    Serial.println("data:");
    for(int k = 0; k < 512; k++) {
        if(g == 16) {
            Serial.println("|");
            if(k % 256 == 0) {
                Serial.println("---------------");
            }
            g = 1;
        } else  {
            g++;
        }
        Serial.printf("%02X ", str[k]);
    }
   
    //为了验证读操作,预先用其它编程器改写了 0x00FFFFF0 所在单元的数据,计16字节
    read_data(0x00fffff0, str, 16);  // 读取地址=0x00FFFFF0所在单元的16个字节
    Serial.println("\n 0xFFFFF0:");
    g=0;
    for(int k = 0; k < 16; k++) {
        if(g == 16) {
            Serial.println("|");
            if(k % 256 == 0) {
                Serial.println("---------------");
            }
            g = 1;
        } else  {
            g++;
        }
        Serial.printf("%02X ", str[k]);
    }
}
void loop() {
}

测试时串口输出的信息:

图片6.png



院士
2025-07-23 11:05:40     打赏
2楼

王哥,您这是使用GPIO来模拟的SPI的时序来读取与写入Flash吗?

这样是不是意义不大?!


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]