Linux 下离线烧写 SPI 闪存

手上的嵌入式设备一多,尤其是路由器一多,就会发现时不时的要用到 SPI Flash 编程器。然而,市售的 SPI NOR Flash 编程器虽然价格已经非常便宜,但是速度不算给力,而且都是基于私有协议的,完全不能在 Linux 下用。虽然可以开虚拟机,但是咱还是希望方便点,不是吗?

好在后来无意中发现了 coreboot (原 LinuxBIOS )项目开发的 flashrom 工具居然带有离线编程功能,除了支持一票比较贵的专用编程器/基于 FTDIxxxxH 的编程器外,还支持一种叫做 serprog 的协议,可以通过串口操作单片机来给 SPI 闪存编程。这样一来就方便了。flashrom 的 wiki 里给了基于 Arduino 的 serprog 的固件源码,其实就是为 ATMEGA 系列 AVR 单片机设计的。

既然手头有片 ATMEGA8L ,那么就开始吧。首先布下板子。电路很简单。

main

由于 ATMEGA8L 似乎有频率上的限制,这里用了 12MHz 的晶体。实际上有 ATMEGA88A 的话可以用 16MHz 的晶体。

P1120707

在面包板上测试成功。

P1120726

固化到万用板上。顺便吐个槽,双面万用板真是闹眼子啊,正面背面都被过孔连在一起了还能叫双面么?!

用法也很简单,首先你的机器上要安装有 flashrom 。Ubuntu 的软件库里自带了:

sudo apt-get install flashrom

但是官方 SVN 仓库里的版本更新一些,而且支持 serprog 的 spispeed 选项。不怕麻烦的最好自己编译一下:

svn co svn://flashrom.org/flashrom/trunk flashrom
cd flashrom
make
sudo make install

连接一个 USB to UART bridge ,装上 SPI 闪存,接上电脑,输入以下命令:

flashrom -p serprog:dev=/dev/ttyUSB0:2000000 -r somefile.bin
flashrom -p serprog:dev=/dev/ttyUSB0:2000000 -E
flashrom -p serprog:dev=/dev/ttyUSB0:2000000 -w somefile.bin

即可读/擦/写 SPI Flash 。写入的时候会自动查空、擦除、写入和校验。

当然,就像 wiki 里说的那样,这个简陋的编程器也有诸多问题:

  • AVR 的 5V 工作电压导致其 IO 电平和 3.3V 的闪存不兼容;
  • 速度慢( UART 只有 2Mbps ,最快 250KB/s );
  • 诸多涉及到 UART 的麻烦,如波特率、转换器和连接等。

考虑到手头上的 STM32F103C8T6 核心板也是个闲得没事干的便宜货,而且还带了硬件 USB2.0 FullSpeed 接口,而且 ST 还有 USB 虚拟串口的例程,不如拉出来溜溜干点活。

STM32F103R8T6 有以下优点:

  • 是最容易买到的 STM32F103 系列里最小的一款,而且也最便宜;
  • 最高 36MHz 的 SPI 接口;
  • 多通道 DMA 引擎;
  • 硬件 USB 2.0 FullSpeed 控制器;
  • 片上 20KB RAM 和 128KB Flash ,足够实现虚拟串口模拟;
  • 有虚拟串口模拟就意味着不需要硬件 UART ,不需要转换器,不需要杜邦线,不需要设置波特率,不会被 UART 掐住速度和稳定性;
  • 90DMIPS 的运算速度是编程器性能的有力保证;
  • 纯天然的 3.3V I/O ,和 SPI Flash 的电气兼容性好。

经过一个星期的折腾,我终于得到了基于 STM32 的 serprog 的第一个可用版本。我把它叫作 serprog-stm32vcp 。这时它只有 200KB/s 到 300KB/s 的读取速度。

P1120840

最初的硬件是用一块带 USB 的核心板配合面包板完成的。核心板是淘宝上买的,回流焊接的,价格不到 ¥30 。

接下来,我做了一系列优化,包括尽量成块地传送数据,提供 SPI 时钟设定以便更快地操作闪存,使代码在 SRAM 中执行以避免片上 Flash 的等待造成性能上的影响等等。此时 serprog-stm32vcp 的读取速度已经有 500KB/s 左右了。后来我就想,既然 STM32 带了 DMA 引擎,何必不利用一下呢?只可惜网上找的 DMA 例子都极具误导性,折腾了一整天总算搞定了,于是有了第二个版本的 serprog-stm32vcp 固件。此时 serprog-stm32vcp 的读取已经高达 850KB/s ,接近 USB2.0 FullSpeed 的极限了( 12Mbps )。

P1130038

随后,有了一个固化的版本。这个板子也是淘宝上买的,也是回流焊接的,可能是最有用的 STM32 核心板了吧,价格 ¥35 。

P1130040

以及……一些对付贴片芯片的专业好帮手——弹跳座。

我觉得开个专业修砖头的店问题不大了。

偷懒的同学,这里有编译好的固件,以及 github 上的仓库

用法很简单,和基于 AVR 的 serprog 基本相同,只不过在最新的 flashrom 里可以加上 spispeed 参数:

flashrom -p serprog:dev=/dev/ttyACM0:4000000,spispeed=36000000 -r somefile.bin
flashrom -p serprog:dev=/dev/ttyACM0:4000000,spispeed=36000000 -E
flashrom -p serprog:dev=/dev/ttyACM0:4000000,spispeed=36000000 -w somefile.bin

ttyACM0 后面的 4000000 是波特率,因为这个编程器并没有用到任何物理上的 UART 所以可以随便填,只要你的系统允许既可,不会影响速度。影响速度的是后面的 spispeed 参数。

最后给点性能测试的结果:

闪存型号 闪存容量 读取速度 写入速度 擦除时间 CH341A 参考速度
EN25F16 2048 KiB 855.5 KiB/s 118.2 KiB/s 20.4 秒
MX25L3205D 4096 KiB 795.3 KiB/s 136.2 KiB/s 62.2 秒 117.0 KiB/s
MX25L6445E 8192 KiB 803.3 KiB/s 144.6 KiB/s 93.9 秒 117.0 KiB/s
MX25L12845E 16384 KiB 797.7 KiB/s 146.8 KiB/s 224.7 秒 18.2 KiB/s

注:CH341A 的参考速度为经销商或网友给出,未注明读取或写入,但是根据使用经验来说应该是读取。flashrom 对 MX25L12845E 的支持状态为“ UNTESTED ”。

今后的速度测试会在仓库里的 PERFORMANCE 文件里更新。


附:某种 CH341A 编程器的照片

P1120845

P1120844

P1120846

P1120847

P1120848

顺便拆了锁紧座,看看里面啥结构。锁紧座这个东西还真是鸡肋。

《Linux 下离线烧写 SPI 闪存》有36个想法

  1. 真是个好东东,前几天为了写SPI,做了4个编程器,目前只有2个成功,一个是AVR EJTAG-ICE固件的,另一个是USBASP硬件+BRSPI固件,两个都是V-USB模拟,都超级慢,读一个2Mb居然要好几分钟……

  2. 请教一下,我有CH341的编程器
    对你的这个编程器也感兴趣。请问电路板在什么地方可以买到
    另外,这个编程器对有加密区域的SPI flash支持吗
    最大支持多大的flash。淘宝上有点店里出售的ch341编程器支持32M(256mbit)

    1. 做这个玩意主要是配合 Linux 下的 flashrom 的。不过现在 Windows 版的 flashrom 也支持 serprog 了,所以问题应该不大。

      flashrom 的闪存支持列表见 http://www.flashrom.org/Supported_hardware#Supported_chips

      里面列举的 3.3V 的 SPI 闪存都支持,不过目前没做 256Mbit 的支持——其 serprog 协议用的仍然是 24 位地址,目前其开发者正打算将其扩展到 32bit 。

      至于板子,在淘宝搜 STM32F103 ,价格 25-40 间带 USB 的基本都能用,注意看下别买 USB 只能供电或者做 ISP 的板子就行。弹跳座也有不少便宜的,但是图中三个座子加起来比板子本身贵多了。

  3. 我下载了一个windows版本的flashrom。我用的是stm32.已经安装好了windows下面的驱动。但是不知道windows版本的flash应该怎么使用?

    1. Windows 下可能还需要 ST 的 VCP 驱动,另外只有最近的 flashrom 才对 Windows 有比较好的支持。在命令提示符里运行,语法估计跟 Linux 上的会有不同,可能需要把 /dev/ttyACM0 换成 COMx 之类的。

      1. C:\flashrom-0.9.6.1-r1705-mingw>flashrom -p serprog:dev=COM8:4000000,spispeed=36
        000000 -r 2.bin
        flashrom v0.9.6.1-r1704 on Windows 6.1 (x86)
        flashrom is free software, get the source code at http://www.flashrom.org

        Calibrating delay loop… OK.
        Warning: given baudrate 4000000 rounded down to 115200.

        这是我的命令运行结果。为什么波特率会被限制在115200呢?我装的是VCP的驱动。命令是无尽的等待,没有任何反应

  4. 用的你预编译的固件 http://dword1511.info/dword/projects/serprog-stm32vcp/prebuilt/20130331.hex 写入到stm32f103c8t6的开发板里面了。
    把开发板的usb接到windows主机,然后在设备管理器里面能看到 flashrom.org serprog-STM32VCP 字样。
    然后按照这里 http://flashrom.org/Windows 用 libusb-win32 驱动起来了, 也编译了windows下的flashrom。
    之后应该怎么使用呢,用libusb驱动的没有串口设备,应当使用的设备名是什么呢?
    用stm的vcp倒是能驱动,但是使用的时候速度比较慢。

    多谢

    1. 不好意思,本来是打算在 Linux 下用的,所以对 Windows 下的情况不清楚。在“ Making libusb-based driver for your device ”这一步的时候插上并选择了 stm32vcp 吗?

        1. 刚刚看了一下,serprog 似乎是不需要这个驱动的,只需要串口驱动就够了。可以试试指定 SPI 时钟频率: serprog:dev=[/dev/device]:[baud],spispeed=36M

          另外你用的闪存和得到的速度大概是多少?

          1. 都弄好了,在windows下加上spispeed=36M读写速度差不多你是给你值的一半吧 MX25L3205D 。很不多了。多谢博主

  5. 冒昧的问下如果用 gd32f103ret6 配合 12Mhz的晶振的板子, 怎么改程序呢。 我只改了
    FLASH_SIZE = 512K
    SRAM_SIZE = 64K
    但是插上USB不识别。 多谢

      1. usb识别了,原来我这个板子需要某个GPIO去上拉,很奇怪为什么不直接焊上上拉。 看到你重开了个新版的程序,那个对GD32支持的怎么样了呢?

      2. 你那个新版的支持GD32的程序,我改成用SPI2能读取到flash型号, 但是无法读取flash内容。Reading flash… 这里卡着不动, 而且没有建立读取的文件。

        我改动的方式如下。
        spi.c spi_setup函数开头增加 rcc_periph_clock_enable(RCC_GPIOB);

        替换 spi.c spi.h 中的SPI1为SPI2 GPIOA 为 GPIOB

        擦除操作有进度,但非常非常慢。
        请问是有哪里改的不对么, 多谢

      1. 您好,我现在用您的固件在gd上跑过,实际上用是可以用。
        后来为了方便,我把用您的源代码(github上的老项目)重新建了个工程,修改了下能在spi2接口下也能用,然后无论在spi1或者spi2下(实验环境都是gd的片子,72mhz,flash用的MX25L3206E,windows),重新新建工程中,对spi和dma的配置都没有改动,vcp部分用了正点原子的枚举初始化,数据发送的endp1还是用您的代码,但是在大量的实验下有一些组实验会出现断流的现象,用bushuond看,主机发送了命令(0x13),没回复,就直接卡死在那了,单片机控制的指示忙的led灯也熄灭了,然后那次就失败了,只能关了重来。这个问题好像在提高spi速度后会有所缓解,但是还是会出现。因为对您的固件实验的次数也只有几次,暂未在您的固件上遇到这问题,所以还是不太具体清楚问题点具体在哪里,遂来请教。还有一个问题也想请教您就是,您在配置spi时用的是sck空闲是为低,第一上升沿采样,而大部分历程(包括st的demo中)都用了sck空闲是为高,第二上升沿采样,在您的配置的情况下能用,但是按大部分历程的方式就不能用,也想请问下原因是啥,谢谢。代码在这里https://github.com/posystorage/serprog-stm32-gd32

  6. 请问你那个最新的stm32-vserprog项目里电路图上那些分立元件的参数都应该取多少?我想做一个,但是(当年就没学好的)电路知识已经全还给老师了,悲催……

发表评论

电子邮件地址不会被公开。 必填项已用*标注