用来干什么

手上有两个路由器,一个小米的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的编程器相关推荐

  1. Arduino应用开发——spi flash(以esp32和w25qxx为例)

    Arduino应用开发--spi flash 目录 Arduino应用开发--spi flash 前言 1 硬件介绍 1.1 模块简介 1.2 硬件连接 2 软件开发 2.1 寄存器介绍 2.2 编程 ...

  2. 自制Flash FLV视频播放器

    大家好,前不久笔者将CenFun Music Player加入flv视频播放功能,这里单独拿出来给大家做一个Flash FLV播放器(仅ActionScript控制,不使用 FLVPlayback 组 ...

  3. 仿真器+编程器+实验板与开发板的区别

    目前市面上出现了各种各样针对单片机爱好者的实验板.编程器和仿真器,其售价一般相对比较低廉,这给单片机爱好者提供了有利的学习途径.但是大多只具备单一的功能,必须配套使用.假如购买了一套学习板,那肯定还要 ...

  4. USBISP/USBasp编程器给Atmega32U4下载Arduino bootloader引导程序

    用USBISP/USBasp编程器给ATmega32U4下载Arduino bootloader引导程序 ATmega32U4 bootloader引导程序是什么 引导程序下载接口与连接 配置熔丝位与 ...

  5. 编程器烧写NAND flash的一些说明

    注意事项: 1.大小端模式,也即在使用编程器时需不需要做字节反序 2.Spare area处理方式,需要还是不需要,是否含有私有ECC算法. 3.坏块处理方式. 摘要一段说明如下:(虽然针对西尔特SU ...

  6. Arduino SPI + SPI Flash芯片W25Q80BV

    W25Q80BV是台湾华邦电子(Winbond)生产的8M-bit串行flash芯片.主要特性有: 工作电压:2.5 ~ 3.6 V 功耗:读写(active)时4mA,低功耗(power-down) ...

  7. 串行 spi Flash 跨页编程的注意点

    spi Flash 进行 page program的时候,编程的数据为1-256个字节不等.在Datasheet中,如果编程的数据为256个字节,那么,输入的起始地址需要256个字节对齐,即需要页对齐 ...

  8. CH341A及XTW 2两种SPI FLASH烧录器的硬件使用说明_20220920【可用于升级主板BIOS】

    目录 一.XTW 2(烧录速度快一些) 二.CH341A(烧录速度慢,但貌似兼容性更高,同时价格较便宜) 三.烧录器驱动及软件的下载链接 一.XTW 2(烧录速度快一些) 3.3V SPI FLASH ...

  9. 硕飞编程器 量产烧录 Flash芯片(W25Q16DV)Bin文件流程说明

    [硕飞Flash编程器上位机软件FlyPRO V4.46]链接:https://pan.baidu.com/s/1aRxJ5n2FEDB00lut1vXJZA     提取码:jdp0  [硕飞Fla ...

最新文章

  1. 【第20周复盘】转换思路,让更多的小朋友们参与进来!
  2. python开发是做什么的-python开发工程师是做什么的
  3. java深度优先迷宫生成_通过深度优先搜索产生的迷宫的Java代码
  4. codeblock 显示 no such file(头文件)
  5. Java常考面试题(一)
  6. javase 的一些基础常用类
  7. Notepad++在编程使用时的小技巧
  8. edite not the main type
  9. 【Docker端口映射】
  10. 服务器主板能插几块硬盘,如何知道自己的主板最大支持多少的硬盘啊
  11. 图书管理系统数据库设计实验报告
  12. 儒豹公布09年7月手机搜索热门关键词排行榜
  13. 科技热点周刊|ClickHouse 融资 2.5 亿美元、个人信息保护法正式实施、Facebook 改名 Meta
  14. 30岁学习编程晚么?
  15. 【C++】Web服务器项目所用到的函数详解
  16. NetVLAD场景识别模型解读
  17. Git 修改 commit author
  18. 淘宝标题怎么写?写标题营销词怎么应用恰当?
  19. 如何将数据库中text字段中返回的数据转换成数组的形式,并且将字符串的数字转换成数字的形式显示
  20. java-php-python-ssm网课信息管理系统计算机毕业设计

热门文章

  1. 小白必看-最简单的VSCode C语言安装方法
  2. live800:网站如何添加客服在线咨询功能?
  3. 几种常用的图像分割方法
  4. 五连珠c语言,c语言连珠五子棋(未完成)
  5. 【二进制基础篇-1】汇编基础
  6. UWB精准定位:让电厂更安全
  7. 全面的Android studio的setting设置,
  8. Comikon(漫画控)
  9. 微信的7个隐藏技巧,真的很实用,不知道的可惜了
  10. 帆船回顾会议_我在帆船上学到的有关系统设计的关键课程