STM32快速复制MX25L1606E系列Flash

news/2024/2/25 14:08:50/文章来源:https://blog.csdn.net/jmmx/article/details/135592669

去年做了一个使用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                     256struct 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 flashand 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: 0x00000016id = 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 informationDelay_ms(100);SPI_FLASH_BlockErase(FLASH_Sector3);//one block is enough to store the firmware dataDelay_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 lengthfor (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.3version[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 informationfor (i = 0; i < 8; i++){chk_sum_flash += buffer[i];}page_num = data_len / FLASH_PAGE_SIZE; //page numbersingle_data_num = data_len % FLASH_PAGE_SIZE; //single data numberflash_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 infoSource_MX25L1605E_Init();//source and target flash initializationLED_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

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

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

相关文章

Android Retrofit使用详情

一、 Retrofit是什么 Retrofit是Android用来接口请求的网络框架&#xff0c;内部是基于OkHttp实现的&#xff0c;retrofit负责接口请求的封装&#xff0c;retrofit可以直接将接口数据解析为Bean类、List集合等&#xff0c;直接简化了中间繁琐的数据解析过程 二、 Retrofit的简单…

使用 Apache POI 更新/覆盖 特定的单元格

使用 Apache POI 更新特定的单元格 一. 需求二. 实现三. 效果 一. 需求 将以下表中第4行&#xff0c;第4列的单元格由“张宇”更新为“汤家凤”&#xff0c;并将更行后的结果写入新的Excel文件中&#xff1b; 二. 实现 使用Apache POI&#xff0c;可以精确定位到需要更改的单…

stm32学习笔记:USART串口通信

1、串口通信协议&#xff08;简介软硬件规则&#xff09; 全双工&#xff1a;打电话。半双工&#xff1a;对讲机。单工&#xff1a;广播 时钟&#xff1a;I2C和SPI有单独的时钟线&#xff0c;所以它们是同步的&#xff0c;接收方可以在时钟信号的指引下进行采样。串口、CAN和…

Picturesocial | 开发实践:如何在15分钟内将应用容器化

在常见的软件架构体系中&#xff0c;容器无疑是一个技术热点。有些开发者在工作中熟练使用容器技术&#xff0c;有些可能刚刚开始容器之旅。 面对容器使用经验不同的各类开发者&#xff0c;我们希望通过这个系列文章&#xff0c;由浅入深地介绍如何使用容器技术来构建&#xf…

C语言天花板——指针(经典题目)

指针我们已经学习的差不多了&#xff0c;今天我来给大家分享几个经典的题目&#xff0c;来让我们相互学习&#x1f3ce;️&#x1f3ce;️&#x1f3ce;️ int main() {int a[4] { 1, 2, 3, 4 };int* ptr1 (int*)(&a 1);int* ptr2 (int*)((int)a 1);printf("%x,%…

C++ OJ基础

C OJ基础 在学校学习C程序设计基础课程的OJ题目 缺少第二十题 这里写目录标题 C OJ基础习题练习(一)打印图形习题练习(二)数据的输入输出习题练习(三)函数重载习题练习(四)设计矩形类习题练习(五)定义Tree类习题练习(六)完善职工工资类Salary的设计习题练习(七)设计矩形类recta…

运维工具之iptables命令

运维工具之iptables命令 1.iptables防火墙介绍 ​ iptables其实并不是真正的防火墙&#xff0c;我们可以理解成一个客户端代理&#xff0c;用户通过 IPTables这个代理&#xff0c;将用户的安全设定执行到对应的"安全框架"中&#xff0c;这个"安全框架"才…

Laravel 框架中队列的使用

概述 Laravel 框架内置了强大的队列系统&#xff0c;用于处理异步任务、提高系统性能等。队列可以让任务异步执行&#xff0c;而不会阻塞当前进程&#xff0c;可以提高系统的处理能力。 Laravel 的队列系统支持多种驱动&#xff0c;如 Redis、Beanstalkd、SQS 等&#xff0c;…

Android WiFi Service启动-Android13

Android WiFi Service启动 - Android13 1、SystemServer中入口2、WifiService启动2.1 关键类概要2.2 启动时序图 Android WiFi基础概览 AOSP > 文档 > 心主题 > WiFi概览 1、SystemServer中入口 编译生成对应的jar包&#xff1a;"/apex/com.android.wifi/javalib…

桌面显示器type-c接口方案6020

TYPE-C接口桌面显示器&#xff0c;与传统的显示器不同的是 新一类的显示器不仅仅支持视频传输&#xff0c;还可以利用显示器的DC电源转成PD协议充电给设备端&#xff08;笔记本&#xff0c;任天堂等HOST设备&#xff09;充电。 这种新型的TYPE-C接口桌面显示器&#xff0c;不仅…

基于机器学习的高考志愿高校及专业分析系统

本项目在“基于 Python 的高考志愿高校及专业分析系统”基础上补充添加了机器学习算法对高考总问进行预测&#xff1b; 项目采用了网络爬虫技术&#xff0c;从指定的高考信息网站上抓取了各大高校的历年录取分数线数据。 通过精细的数据清洗过程&#xff0c;这些数据被存储于…

物联网智能控制器—福建蜂窝物联网科技有限公司

什么是物联网智能控制器&#xff1f; 物联网智能控制器是蜂窝物联自主研发的一种远程测控设备(RTU)&#xff0c;负责对现场信号、工业设备的监测和控制。本质上是一个模块化封装的微型计算机设备&#xff0c;将相应的一些功能进行了封装&#xff0c;无需进行电路设计和硬件程序…

为什么使用双token实现无感刷新用户认证?

单token机制 认证机制&#xff1a;对与单token的认证机制在我们项目中仅使用一个Access Token的访问令牌进行用户身份认证和授权的方案处理。 不足之处&#xff1a; 安全性较低(因为只有一个token在客户端和服务器端之间进行传递&#xff0c;一目Acess Token被截获或者被泄露…

在程序中链接静态库 和 动态库

9. 链接库 在编写程序的过程中&#xff0c;可能会用到一些系统提供的动态库或者自己制作出的动态库 或者静态库文件&#xff0c;cmake中也为我们提供了相关的加载动态库的命令hehedalinux:~/Linux/loveDBTeacher-v3$ tree . ├── CMakeLists.txt ├── include │ └── …

远程开发之vscode端口转发

远程开发之vscode端口转发 涉及的软件forwarded port 通过端口转发&#xff0c;实现在本地电脑上访问远程服务器上的内网的服务。 涉及的软件 vscode、ssh forwarded port 在ports界面中的port字段&#xff0c;填需要转发的IP:PORT&#xff0c;即可转发远程服务器中的内网端…

Monorepo-uniapp 构建分享

Monorepo uniapp 构建灵感&#xff1a;刚好要做一个项目&#xff0c;于是想到升级一下之前自己写的一个vue3tspiniauno的模版框架&#xff0c;其实那个框架也不错&#xff1b;只是感觉还差点东西&#xff0c;我已经用那个小框架写了两三个项目&#xff1b;轻巧实用。为什么选…

idea使用docker-compose发布应用程序

非常重要的话说在前头 idea要想使用docker-compose&#xff0c;不能使用ssh创建idea Docker&#xff0c;而需要使用socket创建idea Docker。 socket docker是不安全的&#xff0c;任何人都可以访问你的docker&#xff0c;所以只能测试环境使用&#xff0c;请勿在正式环境使用s…

ubuntu安装mysql(tar.xz)

0:本机Ubuntu的版本为 腾讯云 18.04 1&#xff1a;下载地址 MySQL &#xff1a;&#xff1a; 下载 MySQL 社区服务器 2&#xff1a;上传文件到服务器 3:解压 sudo sumv mysql-8.2.0-linux-glibc2.17-x86_64-minimal.tar.xz /usrtar -xvf mysql-8.2.0-linux-glibc2.17-x86_6…

推荐一款.NET开发的物联网开源项目

物联网&#xff08;IoT&#xff09;是一个正在快速发展的技术领域&#xff0c;它涉及到各种设备、物体和系统的互联。所以各种物联网平台和物联网网关项目层出不穷&#xff0c;在物联网&#xff08;IoT&#xff09;领域&#xff0c;.NET平台扮演着重要的角色。作为一款广泛使用…

【日常聊聊】ChatGPT和文心一言哪个更好用

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; 日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 介绍 哪个在文本生成上有优势 结语 我的其他博客 前言 随着人工智能技术的不断发展&#xff0c;自然语言处理领域也取得了显…