用Arduino做SPI FLASH的编程器
用来干什么
手上有两个路由器,一个小米的r3gv2(已砖,BootLoader莫得了),一个斐讯K3(已砖,nand闪存内的BootLoader也莫得了),对于小米的路由器,把spi flash焊下来,用编程器刷入breed就可以了。而对于斐讯K3来说,PCB上也预留了SPI flash的焊盘,可以改SPI启动,再向nand闪存中刷入CFE(BootLoader的一种)就可以。
无奈的是,因为疫情原因,买编程器都不发货,所以苦思冥想,终于想到可以使用带有SPI功能的可编程设备就可以做一个编程器。至于如何向spi中写入文件,不是有sscom嘛,可以直接通过串口发送文件,然后在Arduino中做个串口接收SPI写入就可以了。
SPI Flash
SPI flash的品牌有很多,GD或者winbond,大多数都是8PIN的,在路由器,机顶盒中随处可见,小米路由器用的128Mbit(16M)的。以winbond为例,命名为W25QXX,图片如下:
不同品牌的这种芯片在引脚定义和读取命令上几乎是兼容的。在Arduino Mega 2560中,SPI引脚默认是50,51,52,53这四个管脚。
- DI----MOSI(51)
- DO----MISO(50)
- CS----SS(53)
- CLK—SLK(52)
- VCC,WP/,HOLD/----3.3V
SPI 的读写
spi flash的一个基本的特点就是,在写入之前必须擦除,擦除的地方都是0xFF,我们在写入SPI Flash的过程主要用到的就是读取ID(校验程序是否正确)、芯片擦除、页面编程(写入数据)和数据读取(数据校验)三个命令。
程序
功能函数
#include <SPI.h>
#define WB_WRITE_ENABLE 0x06
#define WB_WRITE_DISABLE 0x04
#define WB_CHIP_ERASE 0xc7
#define WB_READ_STATUS_REG_1 0x05
#define WB_READ_DATA 0x03
#define WB_PAGE_PROGRAM 0x02
#define WB_JEDEC_ID 0x9f
//打印256字节的数据(16x16)
void print_page_bytes(byte* page_buffer) {char buf[10];for (int i = 0; i < 16; ++i) {for (int j = 0; j < 16; ++j) {sprintf(buf, "%02x", page_buffer[i * 16 + j]);Serial.print(buf);}Serial.println();}
}void get_jedec_id(void) {Serial.println("command: get_jedec_id");byte b1, b2, b3;_get_jedec_id(&b1, &b2, &b3);char buf[128];sprintf(buf, "Manufacturer ID: %02xh\nMemory Type ID: %02xh\nCapacity ID: %02xh",b1, b2, b3);Serial.println(buf);Serial.println("Ready");
}void chip_erase(void) {Serial.println("command: chip_erase");_chip_erase();Serial.println("Ready");
}void read_page(unsigned int page_number) {char buf[80];sprintf(buf, "command: read_page(%04xh)", page_number);Serial.println(buf);byte page_buffer[256];_read_page(page_number, page_buffer);print_page_bytes(page_buffer);Serial.println("Ready");
}void read_all_pages(void) {Serial.println("command: read_all_pages");byte page_buffer[256];for (int i = 0; i < 4096; ++i) {_read_page(i, page_buffer);print_page_bytes(page_buffer);}Serial.println("Ready");
}void _get_jedec_id(byte* b1, byte* b2, byte* b3) {digitalWrite(SS, LOW);SPI.transfer(WB_JEDEC_ID);*b1 = SPI.transfer(0xFF); // manufacturer id*b2 = SPI.transfer(0xFF); // memory type*b3 = SPI.transfer(0xFF); // capacitydigitalWrite(SS, HIGH);
}void _chip_erase(void) {digitalWrite(SS, LOW);SPI.transfer(WB_WRITE_ENABLE);digitalWrite(SS, HIGH);delay(10);digitalWrite(SS, LOW);SPI.transfer(WB_CHIP_ERASE);digitalWrite(SS, HIGH);delay(10);digitalWrite(SS, LOW);SPI.transfer(WB_WRITE_DISABLE);digitalWrite(SS, HIGH);not_busy();
}void _read_page(word page_number, byte* page_buffer) {digitalWrite(SS, LOW);SPI.transfer(WB_READ_DATA);SPI.transfer((page_number >> 8) & 0xFF);SPI.transfer((page_number >> 0) & 0xFF);SPI.transfer(0);for (int i = 0; i < 256; ++i) {page_buffer[i] = SPI.transfer(0xFF);}digitalWrite(SS, HIGH);not_busy();
}void _write_page(word page_number, byte* page_buffer) {digitalWrite(SS, LOW);SPI.transfer(WB_WRITE_ENABLE);digitalWrite(SS, HIGH);delay(10);digitalWrite(SS, LOW);SPI.transfer(WB_PAGE_PROGRAM);SPI.transfer((page_number >> 8) & 0xFF);SPI.transfer((page_number >> 0) & 0xFF);SPI.transfer(0);for (int i = 0; i < 256; ++i) {SPI.transfer(page_buffer[i]);}digitalWrite(SS, HIGH);digitalWrite(SS, LOW);SPI.transfer(WB_WRITE_DISABLE);digitalWrite(SS, HIGH);not_busy();}uint8_t read_status(void)
{digitalWrite(SS, LOW);SPI.transfer(WB_READ_STATUS_REG_1);uint8_t data = SPI.transfer(0xFF);digitalWrite(SS, HIGH);return data;
}void not_busy(void) {while (read_status() & 0x01){delay(1);}
}
串口转发数据到SPI
每次读取一个字节,读满256个字节后,通过spi写入flash,通过指定文件大小,判断是否结束,不满一页的数据剩下的都是0xFF。因为有些BootLoader的文件末尾都是0xFF,所以可以指定文件大小来提前结束写入过程,节省时间,要写入的二进制文件可以通过winhex查看。
在写入完一页后,读出这一页,和写入数据做校验,如果有错误,程序return,停止运行。
因为是一个字节一个字节的从串口读,所以串口的波特率不要太大,否则会丢数据,9600bps比较合适。可以在调试时,可以吧FILE_SIZE改为256,把打印页面数据的注释去掉。
没问题后,通过sscom发送文件的功能,在擦除芯片几秒后,点击发送文件,串口会输出当前写入的页面序号。
#define FILE_SIZE 524288 //512KB
void setup()
{SPI.begin();SPI.setDataMode(0);SPI.setBitOrder(MSBFIRST);digitalWrite(SS, HIGH);Serial.begin(9600);Serial.println("Ready");init_printf();not_busy();get_jedec_id();chip_erase();while (Serial.read() > 0);
}void loop()
{if (!writed && Serial.available() > 0) {bytes++;page[currentPos] = Serial.read();currentPos++;if (currentPos == 256) {Serial.println("Receive Page:" + String(currentPage));//print_page_bytes(page);_write_page(currentPage, page);delay(10);byte page_temp[256];_read_page(currentPage, page_temp);//Serial.println("Read Page:" + String(currentPage));//print_page_bytes(page_temp);for (int k = 0; k < 256; k++){if (page_temp[k] != page[k]){Serial.println("Error!");return;}}if (bytes >= FILE_SIZE){writed = true;Serial.println("WRITE FINISH!!!");}memset(page, 0xFF, 256 * sizeof(byte));currentPos = 0;currentPage++;}else if (bytes >= FILE_SIZE){Serial.println("Receive Page:" + String(currentPage));//print_page_bytes(page);_write_page(currentPage, page);delay(10);byte page_temp[256];_read_page(currentPage, page_temp);//Serial.println("Read Page:" + String(currentPage));//print_page_bytes(page_temp);for (int k = 0; k < 256; k++){if (page_temp[k] != page[k]){Serial.println("Error!");return;}}writed = true;Serial.println("WRITE FINISH!!!");}}if (writed){Serial.read();}
}
如果遇到写入数据不正确,随机错误、重复错误或者很多0x00的情况,八成是电压的问题,我用3.3V的电压,操作GD的Flash就没有问题,写winbond的Flash就写不进去,读出来都是乱的,然后在Flash擦除之后,断了一下芯片的供电,再写入就好了。
用Arduino做SPI FLASH的编程器相关推荐
- Arduino应用开发——spi flash(以esp32和w25qxx为例)
Arduino应用开发--spi flash 目录 Arduino应用开发--spi flash 前言 1 硬件介绍 1.1 模块简介 1.2 硬件连接 2 软件开发 2.1 寄存器介绍 2.2 编程 ...
- 自制Flash FLV视频播放器
大家好,前不久笔者将CenFun Music Player加入flv视频播放功能,这里单独拿出来给大家做一个Flash FLV播放器(仅ActionScript控制,不使用 FLVPlayback 组 ...
- 仿真器+编程器+实验板与开发板的区别
目前市面上出现了各种各样针对单片机爱好者的实验板.编程器和仿真器,其售价一般相对比较低廉,这给单片机爱好者提供了有利的学习途径.但是大多只具备单一的功能,必须配套使用.假如购买了一套学习板,那肯定还要 ...
- USBISP/USBasp编程器给Atmega32U4下载Arduino bootloader引导程序
用USBISP/USBasp编程器给ATmega32U4下载Arduino bootloader引导程序 ATmega32U4 bootloader引导程序是什么 引导程序下载接口与连接 配置熔丝位与 ...
- 编程器烧写NAND flash的一些说明
注意事项: 1.大小端模式,也即在使用编程器时需不需要做字节反序 2.Spare area处理方式,需要还是不需要,是否含有私有ECC算法. 3.坏块处理方式. 摘要一段说明如下:(虽然针对西尔特SU ...
- Arduino SPI + SPI Flash芯片W25Q80BV
W25Q80BV是台湾华邦电子(Winbond)生产的8M-bit串行flash芯片.主要特性有: 工作电压:2.5 ~ 3.6 V 功耗:读写(active)时4mA,低功耗(power-down) ...
- 串行 spi Flash 跨页编程的注意点
spi Flash 进行 page program的时候,编程的数据为1-256个字节不等.在Datasheet中,如果编程的数据为256个字节,那么,输入的起始地址需要256个字节对齐,即需要页对齐 ...
- CH341A及XTW 2两种SPI FLASH烧录器的硬件使用说明_20220920【可用于升级主板BIOS】
目录 一.XTW 2(烧录速度快一些) 二.CH341A(烧录速度慢,但貌似兼容性更高,同时价格较便宜) 三.烧录器驱动及软件的下载链接 一.XTW 2(烧录速度快一些) 3.3V SPI FLASH ...
- 硕飞编程器 量产烧录 Flash芯片(W25Q16DV)Bin文件流程说明
[硕飞Flash编程器上位机软件FlyPRO V4.46]链接:https://pan.baidu.com/s/1aRxJ5n2FEDB00lut1vXJZA 提取码:jdp0 [硕飞Fla ...
最新文章
- 【第20周复盘】转换思路,让更多的小朋友们参与进来!
- python开发是做什么的-python开发工程师是做什么的
- java深度优先迷宫生成_通过深度优先搜索产生的迷宫的Java代码
- codeblock 显示 no such file(头文件)
- Java常考面试题(一)
- javase 的一些基础常用类
- Notepad++在编程使用时的小技巧
- edite not the main type
- 【Docker端口映射】
- 服务器主板能插几块硬盘,如何知道自己的主板最大支持多少的硬盘啊
- 图书管理系统数据库设计实验报告
- 儒豹公布09年7月手机搜索热门关键词排行榜
- 科技热点周刊|ClickHouse 融资 2.5 亿美元、个人信息保护法正式实施、Facebook 改名 Meta
- 30岁学习编程晚么?
- 【C++】Web服务器项目所用到的函数详解
- NetVLAD场景识别模型解读
- Git 修改 commit author
- 淘宝标题怎么写?写标题营销词怎么应用恰当?
- 如何将数据库中text字段中返回的数据转换成数组的形式,并且将字符串的数字转换成数字的形式显示
- java-php-python-ssm网课信息管理系统计算机毕业设计