STM32快速复制MX25L1606E系列Flash

去年做了一个使用RS485对PIC18F45K80系列单片机进行在线升级的程序,如果是小批量的出厂烧录程序和升级验证(出厂前肯定要测试单片机是否能正常读写Flash)是可以的,但是后来产品订单量很大,生产线的烧录及升级验证就很缓慢,主要是发送升级包这一步,主要原因如下:

1、升级数据包有20多K字节,而PIC18F45K80系列的内存只有3K多;

2、数据包先保存到PIC18F45K80的缓存中,然后再写入MX25L1606E;

3、根据客户软件协议要求,RS485通信需要将一个字节拆分成两个字节进行传输,例如:如果要发送0xAB,则会拆分成0x3A和0x3B两个字节,接收处理程序再将这两个字节合并成0xAB,这无疑使数据的传输时间加倍;

4、RS485通信的波特率被限制为9600,说是为了数据传输的稳定性;

生产部门表达了不满,我本想给他们解释原因,但后来想与其和一些不懂技术的人瞎争论、浪费时间,还不如想点实际的办法。简单分析:整个IAP中最费时间的就是将升级包通过RS485写入到MX25L1606E,那如果有办法直接PIN对PIN通过SPI通信将升级包复制到MX25L1606E不就解决问题了吗?那速度是极快的。软件倒是好做,但问题是:MX25L1606E这个芯片太小了,不好设计工装或者治具,而且必须要先贴片,那说明彻底不能使用治具了。后来负责该项目的硬件工程师在网上找到了个IC夹子,针对各种PIN针的都有,于是用STM32F103VET6开发板写了个测试程序并在原来的PCB板上测试,只要IC夹子和PCB板上的MX25L1606E脚位对齐还真是可以复制!太开心了,赶紧拿来上电测试,果然可以正确执行升级程序!!!

以下是STM32的主程序,内容比较简单,主要就是使用了一个按键触发执行复制操作,最后使用显示屏简单提示操作结果。最主要是它内存资源充足,那20多KB的升级包轻松放入内存,当然也可使用内存资源少的单片机,只是多次读取升级文件,速度也不会慢多少。整个工程可以免费到以下链接下载:【免费】STM32快速复制MX25L1606E资源-CSDN文库,有需要的朋友可以试一下。

主程序如下,升级文件内容是16进制格式的,也就是firm_ware这个数组,内容我就省略了很多,因为太占篇幅了,大概有两万多个字节,这个不是重点:

/******************** (C) COPYRIGHT 2023 DS **************************
 * @File name  :main.c
 * @Description    :Fast flash clone
 * @Hardware platform:STM32F103VET6
 * @Library version :ST3.5.0 *
 * @Developer  :Power
**********************************************************************************/


/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "spi.h"
#include "led.h"
#include "delay.h"
#include "sys_temp.h"
#include "tim.h"
#include "wwdg.h"
#include "usart1.h"
#include "key.h"
#include "bsp_ili9341_lcd.h"

#define     FLASH_PAGE_SIZE                     256

struct ALARM {
    unsigned    KEY_ERASE_PRESSED               : 1;
    unsigned    CLONE_DONE                      : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;

    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
    unsigned                                    : 1;
};

union ALARM_STATUS_UNION {
    struct ALARM    alarm;
    uint16_t    allbits;
};

typedef union ALARM_STATUS_UNION ALARM_STATUS;

uint16_t    led_cnt = 0;//LED test
volatile uint8_t Flag_1ms;      //1 milli-second overflow flag
volatile uint8_t cDelay_80ms;   //80 milli-seconds counter
ALARM_STATUS AlarmStatus;
uint32_t chk_sum = 0;
uint32_t chk_sum_flash = 0;
uint16_t i = 0;
uint16_t data_len = 0;
uint8_t version[8] = {0x00};
uint8_t buffer[FLASH_PAGE_SIZE] = {0x00}; //used to save the data read from MX25L1606E
uint8_t page_num = 0;
uint8_t single_data_num = 0;
uint32_t flash_addr = 0;
uint8_t key = 0xFF;

/* the new firmware hex file */
uint8_t firm_ware[] =
{
	0x12,
	0x40,
	0x18,
	0x00,
	0x00,
	0x0F,
	0xD0,
	0x9E,
	0x90,
	0xFA,
	0x0E,
	0xCF,
	0x6E,
	0x9A,
};

/**
*@name: Process_1MS
*@description: 1 milli-second overflow process
*@params: none
*@return: none
*/
static void Process_1MS(void)
{
    if (Flag_1ms == 0)
    {
        return;
    }

    Flag_1ms = 0;

    if (led_cnt % 500 == 0) //LED Toggle Test
    {
        GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0)));
    }
    led_cnt++;
    if (led_cnt >= 60000)
    {
        led_cnt = 0;
    }
    //if(cDelay10ms != 0)   cDelay10ms--;
    //if(++cDelay10ms1 >= 100)  cDelay10ms1 = 100;
    //if(++cDelay10ms2 >= 100)  cDelay10ms1 = 100;
    //usTmr_ms++;
}

/**
*@name: Copy_Data_Process
*@description: erase the designated area of the target MX25L1606E series flash
               and write it with the firmware
*@params: none
*@return: none
*/
static void Copy_Data_Process(void)
{
    if (AlarmStatus.alarm.KEY_ERASE_PRESSED == 1 && AlarmStatus.alarm.CLONE_DONE == 0)
    {
        uint32_t id = 0;
        LCD9341_Clear(0xFFFF);


        /* STEP1: read the manufacture ID from the target */
        // Manufacture ID: 0x000000EF
        // Device ID: 0x00000016
        id = SPI_FLASH_ReadDeviceID();
        if (id == 0x0000) //invalid device ID
        {
			//LCD_Display_String("Reading device ID failed!", 0);
			//AlarmStatus.alarm.KEY_ERASE_PRESSED = 0;
			//AlarmStatus.alarm.CLONE_DONE = 1;
			//return;
        }
        

        /* STEP2: erase the target */
        SPI_FLASH_SectorErase(FLASH_Sector4);//used to store the version and check sum information
        Delay_ms(100);
        SPI_FLASH_BlockErase(FLASH_Sector3);//one block is enough to store the firmware data
        Delay_ms(100);


        /* STEP3: copy the version data as well as calculate the check sum */
        chk_sum = 0;
        chk_sum_flash = 0;
        data_len = sizeof(firm_ware) / sizeof(uint8_t) -1;//minus 1 here means that the first byte is version and should be excluded from the total data length
        for (i = 0; i < data_len; i++)
        {
            chk_sum += firm_ware[i + 1];
        }
        version[0] = 0x56; //ASCII code standing for letter 'V'
        version[1] = firm_ware[0]; //version, for example, 0x13 means V1.3
        version[2] = data_len >> 8;
        version[3] = data_len & 0xFF;
        version[4] = (chk_sum & 0xFF000000) >> 24;
        version[5] = (chk_sum & 0x00FF0000) >> 16;
        version[6] = (chk_sum & 0x0000FF00) >> 8;
        version[7] = chk_sum & 0x000000FF;
        for (i = 0; i < 8; i++)//the version information check sum
        {
            chk_sum += version[i];
        }
        SPI_FLASH_BufferWrite(version, FLASH_Sector4, sizeof(version) / sizeof(uint8_t));
        Delay_ms(100);


        /* STEP4: copy the firmware data */
        SPI_FLASH_BufferWrite(&firm_ware[1], FLASH_Sector3, data_len);
        Delay_ms(100);
        /* STEP5: check the data */
        for (i = 0; i < 8; i++)
        {
            buffer[i] = 0x00;
        }
        SPI_FLASH_BufferRead(buffer, FLASH_Sector4, 8);//read the version information
        for (i = 0; i < 8; i++)
        {
            chk_sum_flash += buffer[i];
        }
        page_num = data_len / FLASH_PAGE_SIZE; //page number
        single_data_num = data_len % FLASH_PAGE_SIZE; //single data number
        flash_addr = FLASH_Sector3;
        while (page_num--)
        {
            SPI_FLASH_BufferRead(buffer, flash_addr, FLASH_PAGE_SIZE);
            for (i = 0; i < FLASH_PAGE_SIZE; i++)
            {
                chk_sum_flash += buffer[i];
            }
            flash_addr += FLASH_PAGE_SIZE;
        }
        if (single_data_num > 0)
        {
            SPI_FLASH_BufferRead(buffer, flash_addr, single_data_num);
            for (i = 0; i < single_data_num; i++)
            {
                chk_sum_flash += buffer[i];
            }
        }
        if (chk_sum == chk_sum_flash && chk_sum != 0) //the data written to the flash is correct
        {
            LCD_Display_String("Clone Succeeded! Please change another MX25L1606E", 1);
        }
        else
        {
            LCD_Display_String("The Clone operation failed!", 0);
        }
        AlarmStatus.alarm.KEY_ERASE_PRESSED = 0;
        AlarmStatus.alarm.CLONE_DONE = 1;
    }
}

/**
*@name: Process_80MS
*@description: 80 milli-seconds timeout process
*@params: none
*@return: none
*/
static void Process_80MS(void)
{
    if (cDelay_80ms >= 240)
    {
        cDelay_80ms = 0;
    }
}

static void Key_Process(void)
{
    key = KEY_Scan(1);
    if (key == KEY_CLONE && AlarmStatus.alarm.KEY_ERASE_PRESSED == 0)
    {
        AlarmStatus.alarm.KEY_ERASE_PRESSED = 1;
        AlarmStatus.alarm.CLONE_DONE = 0;
        Copy_Data_Process();
    }
}

/*
 main function
 */
int main(void)
{
    /**************************************************************************************************/
    SysTick_Init();   //has already been called in the startup file, so here we don't need to call it explicitly.
    USART1_Config();//print the debug info
    Source_MX25L1605E_Init();//source and target flash initialization
    LED_Init();
    KEY_Init();
    TIM_Configuration();
    bsp_InitLCD();
    LCD9341_Clear(0xFFFF);
    //WWDG_Init(0x7F, 0x5F, WWDG_Prescaler_2);
    while (1)
    {
        /* STM32 software reset */
        //SystemReset();

        //wr=WWDG->CFR&0X7F; // 重新设置看门狗窗口值
        //tr=WWDG->CR&0X7F;  // 重新设置看门狗递减计数器值
        /* 注意tr和wr的比较,确定喂狗时间 */
        //if(tr<wr)//计数器值tr必须小于窗口值wr时才能喂狗,在之前喂狗则太早,会产生看门狗复位
        //{
        //WWDG_SetCounter(WWDG_CNT);//更新计数器
        //printf("Feeding dog now......\r\n");
        //}
        Process_1MS();
        Process_80MS();
        Key_Process();
    }
}

复制MX25L1606E的步骤如下:

1、先读取它的ID相关信息,如果读取正确则证明SPI通信正常;

2、擦除MX25L1606E上的相关块和扇区,比如:本程序一个扇区用于保存事先计算好的校验和及总的升级包长度,一个块用于存储实际的新的升级固件,一个块用于保存旧的在ROM中运行的程序。在执行IAP时会对比事先计算好的校验和及长度是否和实际读取的校验和及长度相同,如果相同证明数据完整无误,否则数据无效不能升级;

3、计算新固件的校验和及长度并写入到第2步擦除的扇区中,同时写入新固件到第2步指定的块;

4、验证MX25L1606E中写入的数据是否和新的固件完全一致并通过显示屏提示操作结果。

使用这个IC夹子后生产效率极大提升,之前光发送升级固件这一个步骤就需要约两分种,现在只要熟悉这个IC夹子和MX25L1606E对应PIN针的位置,基本不到10秒就可完成一个!

附IC夹子示意图,我是使用野火的STM32开发板通过SPI接口连接MX25L1606E

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/327344.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言基础考研向】03混合运算和printf讲解

一.混合运算 类型强制转换场景 整型数进行除法运算时&#xff0c;如果运算结果为小数&#xff0c;那么存储浮点数时一定要进行强制类型转换&#xff0c;请看下面例子 #include <stdio.h> int main() {int i5;float fi/2; //这里做的整型运算printf("%f\n",f…

BIOS知识枝桠——RAID 磁盘阵列

文章目录 前言一、RAID介绍二、RAID等级分类1.RAID02.RAID13.RAID24.RAID3和RAID45.RAID5和RAID66.RAID77.RAID10 BIOS下组建RAID 前言 假设存在多块磁盘&#xff0c;如果不组建阵列&#xff0c;磁盘与磁盘之间是没有任何关系的。磁盘A和B&#xff0c;放在A中的文件与B磁盘没有…

布隆过滤器四种实现(Java,Guava,hutool,Redisson)

1.背景 为预防大量黑客故意发起非法的时间查询请求&#xff0c;造成缓存击穿&#xff0c;建议采用布隆过滤器的方法解决。布隆过滤器通过一个很长的二进制向量和一系列随机映射函数&#xff08;哈希函数&#xff09;来记录与识别某个数据是否在一个集合中。如果数据不在集合中…

msvcr100.dll丢失的6种解决方法

我们来了解一下msvcr100.dll是什么。msvcr100.dll是Microsoft Visual C 2010 Redistributable Package的一部分&#xff0c;它包含了许多运行在Windows操作系统上的应用程序所需的运行时组件。这些组件包括C标准库、MFC&#xff08;Microsoft Foundation Class&#xff09;库等…

vivado 添加现有IP文件、生成IP

添加现有IP文件 作为从AMD IP目录添加和自定义IP的替代方案&#xff0c;您可以直接添加XCI或XCIX文件。此过程不同于从按以下方式编目&#xff1a; •XCI或XCIX文件可能是早期版本&#xff0c;也可能是相同或完全自定义的版本AMD IP目录中发现的类似IP。 •XCI或XCIX文件可能…

meter报OOM错误,如何解决?

根据在之前的压测过程碰到的问题&#xff0c;今天稍微总结总结&#xff0c;以后方便自己查找。 一、单台Mac进行压测时候&#xff0c;压测客户端Jmeter启动超过2000个线程&#xff0c;Jmeter报OOM错误&#xff0c;如何解决&#xff1f; 解答&#xff1a;单台Mac配置内存为8G&…

log4j2漏洞综合利用_CVE-2021-44228_CNVD-2021-95919

1.漏洞利用 1.1.rmi 利用 1、在检测到目标存在 log4j2 漏洞后&#xff0c;确定漏洞参数&#xff0c;尝试接受目标 rmi 请求。 成功接收到请求。 出现 JRMIK 字样即代表可接受 RMI 请求。 2、漏洞利用。 使用JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar执行命令&#xff0…

AI大模型预先学习笔记二:prompt提问大模型、langchain使用大模型框架、fine tune微调大模型

文章目录 一、Prompt Engineering&#xff08;怎么去提问大模型&#xff09;1&#xff09;环境准备2&#xff09;交互代码的参数备注3&#xff09;交互代码 二、LangChain&#xff08;一个框架去使用大模型&#xff09;1&#xff09;LangChain核心介绍&#xff1a;I/O模块、数据…

debian 11 arm64 aarch64 D2000 平台编译 box86 box64 笔记

参考资料 https://github.com/ptitSeb/box86/blob/master/docs/COMPILE.md 源码地址 GitHub - ptitSeb/box86: Box86 - Linux Userspace x86 Emulator with a twist, targeted at ARM Linux devices deb在线源地址&#xff08;打不开&#xff09;&#xff1a; Itais box86…

宿舍管理系统的设计与实现:基于Spring Boot、Java、Vue.js和MySQL的完整解决方案

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

vue前端开发自学,祖孙多层级组件嵌套关系数据传输

vue前端开发自学,祖孙多层级组件嵌套关系数据传输&#xff01;官方提供了一个解决方案&#xff0c;就是&#xff0c;在根组件内使用provide,哪个子孙组件想调用这个数据&#xff0c;就可以inject接收就行了。虽然是方便了&#xff0c;但是这个有点要求&#xff0c;就是只能自上…

05-HAL库硬件SPI点亮板载LCD屏幕

05-HAL库硬件SPI点亮板载LCD屏幕 1、本节内容介绍 1.1、HAL库硬件SPI 在cubemx中的配置及注意事项;1.2、HAL库SPI详解与结构介绍;1.3、实现硬件SPI驱动板载ST7789显示屏,240*240像素&#xff1b; 源码地址&#xff1a;https://gitee.com/MR_Wyf/hal-cubemx-rt-thread/tree/h…

李宏毅LLM——机器学习基础知识

文章目录 机器学习基本概念生成式学习 Structured Learning总结 机器学习基本概念 机器学习 机器自动找出一种函数 根据函数的不同&#xff0c;可以分为回归问题&#xff08;输出数值&#xff09;和分类问题&#xff08;输出类别&#xff0c;选择题&#xff09; 生成式学习 …

【征服redis1】基础数据类型详解和应用案例

博客计划 &#xff0c;我们从redis开始&#xff0c;主要是因为这一块内容的重要性不亚于数据库&#xff0c;但是很多人往往对redis的问题感到陌生&#xff0c;所以我们先来研究一下。 本篇&#xff0c;我们先看一下redis的基础数据类型详解和应用案例。 1.redis概述 以mysql为…

用julia演示蝴蝶效应:洛伦兹吸引子

文章目录 Lorentz吸引子julia绘图关闭抗锯齿 蝴蝶效应的名字来源于蝴蝶扇动翅膀的动作&#xff0c;虽然这个动作微小&#xff0c;但可能会在数周后引起飓风等极端天气的发生。这种现象表明&#xff0c;微小的变化可能会被放大并产生非线性的结果。这个概念最早由美国气象学家爱…

Jmeter接口测试(2024版)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;薪资嘎嘎涨 Jmeter介绍&测试准备&#xff1a; Jmeter介绍&#xff1a;Jmeter是软件行业里面比较常用…

控制网页的灰度显示

1.代码&#xff1a; 普通网页 <style>html {filter: grayscale(100%);}</style> 或是:webkit内核浏览器写法 <style>html {-webkit-filter: grayscale(100%)}</style> 2.说明&#xff1a; grayscale(amount) :进行灰度转换。 amount转换值的大小&…

MySQL系列之数据导入导出

前言 大数据与云计算作为当今时代&#xff0c;数据要素发展的“动力引擎”&#xff0c;已经走进了社会生活的方方方面。而背后承载的云服务或数据服务的高效运转&#xff0c;起了决定作用。 作为数据存储的重要工具&#xff0c;数据库的品类和特性也日新月异。从树型、网络型…

数据库开发工具:Navicat Premium 16 (Win/Mac)中文激活版

Navicat Premium 16 是一款强大的数据库管理工具&#xff0c;旨在帮助用户更轻松地管理和维护各种数据库类型。 以下是关于 Navicat Premium 16 的详细介绍&#xff1a; 数据库支持&#xff1a;Navicat Premium 16 支持多种数据库类型&#xff0c;包括 MySQL、PostgreSQL、SQLi…

C++核心编程之类和对象---C++面向对象的三大特性--多态

目录 一、多态 1. 多态的概念 2.多态的分类&#xff1a; 1. 静态多态&#xff1a; 2. 动态多态&#xff1a; 3.静态多态和动态多态的区别&#xff1a; 4.动态多态需要满足的条件&#xff1a; 4.1重写的概念&#xff1a; 4.2动态多态的调用&#xff1a; 二、多态 三、多…