当前位置: 首页 > ops >正文

51核和ARM核单片机OTA实战解析(二)

文章目录

  • 摘要
  • 三、OTA代码分析
    • 3.1 DATA FLASH空间使用
    • 3.2 OTA整个流程
    • 3.2 OTA参数初始化
    • 3.2 DATA FLASH写入数据
    • 3.3 DATA FLASH读数据



摘要

本篇文章主要是从代码实现角度去进行分析。

三、OTA代码分析

3.1 DATA FLASH空间使用

前面一篇文章已经说了DATA FLASH和EEPROM作用是一样的。

而在OTA过程中主要是存放一些OTA升级标志位。

明确data flash大小


#define DATA_BUFF_ADDR           0X000		        //Date Flash的起始位置#define DATA_BUFF_SIZE           (0x1ff - DATA_BUFF_ADDR + 1)	//Date Flash缓存区前512个字节
  • 显式表达意图​:

    使用 (0x1ff - DATA_BUFF_ADDR + 1) 明确表示缓冲区大小是从起始地址 0x000 到结束地址 0x1ff 的连续空间。这种写法更直观地反映了内存布局的设计意图,便于其他开发者理解。

  • 适应地址变更​:

    若未来 DATA_BUFF_ADDR 的起始地址需要调整(例如改为 0x100),DATA_BUFF_SIZE 会自动重新计算大小,而无需手动修改 0x200。这种动态计算减少了硬编码带来的维护风险。

1、初始化的时候读取Data Flash里面存储的数据,

需要传入参数:起始地址、接收数组的其实地址、数组的长度,

使用库函数,读取Data Flash存储的数据,逐个地址去读取。

根据数据手册可以得知

FLASH 存储器包含程序存储器(APROM)与非易失数据存储器(Data FLASH)。程序存储器空间最大为 64KB,分为 128个扇区,每个扇区包含 512B。数据存储器(Data FLASH)空间最大为 1KB,分为 2 个扇区,每个扇区包含 512B。

通过存储器模块接口,可对存储器进行读取/写入/擦除操作存储器允许字节读写,写入时间由片上定时器控制,在写入新数据之前需确保该地址中的数据已被擦除。写入和擦除电压是由片上电荷泵产生,此电荷泵额定工作电压在器件的电压范围内,用于进行字节操作。

Flash 存储器擦除操作仅支持扇区擦除不支持字节擦除。在修改某个地址的数据之前,建议先将其他数据保存后,再擦除当前扇区,最后进行数据写入操作。

在写之前先要擦除,擦除操作模式(擦除操作的范围为:当前地址所在的整个扇区); 这是该芯片的要求。

![[Pasted image 20250724105924.png]]

3.2 OTA整个流程

关于OTA思路有很多种,本文就先以这一种思路进行讲解,该方法可能比较繁琐,后续会逐渐优化该思路和方法,使用不同的程序实现思路,尽量做到解耦。本文这个仅做参考。
在这里插入图片描述
都是基于对FLASH存储器的控制

FLASH 存储器包含程序存储器(APROM)与非易失数据存储器(Data FLASH)。程序存储器空间最大为 64KB,分为 128个扇区,每个扇区包含 512B。数据存储器空间最大为 1KB,分为 2 个扇区,每个扇区包含 512B。

可通过相关特殊功能寄存器(SFR)对 FLASH 存储器进行存取操作以实现 IAP 功能,另外通过特殊功能寄存器(SFR)也可对程序空间进行 CRC 校验。用于访问 FLASH 空间的 SFR 寄存器如下:
⚫ MLOCK
⚫ MDATA
⚫ MADRL
⚫ MADRH
⚫ PCRCDL
⚫ PCRCDH
⚫ MCTRL

MLOCK 寄存器用于使能存储器操作,MDATA 寄存器形成一个字节用于保存要读/写的 8 位数据,MADRL/MADRH 寄存器存放被访问的 MDATA 单元的地址或 CRC 校验的地址,PCRCDL/PCRCDH 寄存器用于保持程序 CRC 的运行结果,MCTRL 寄存器用于存储器操作控制。

通过存储器模块接口,可对存储器进行读取/写入/擦除操作。存储器允许字节读写,写入时间由片上定时器控制,在写入新数据之前需确保该地址中的数据已被擦除。写入和擦除电压是由片上电荷泵产生,此电荷泵额定工作电压在器件的电压范围内,用于进行字节操作。

Flash 存储器擦除操作仅支持扇区擦除,不支持字节擦除。在修改某个地址的数据之前,建议先将其他数据保存后,再擦除当前扇区,最后进行数据写入操作。

芯片支持对程序空间代码进行 CRC 校验。

3.2 OTA参数初始化

这一部分的主要目的是验证DATA FLASH空间是否有效。

在这里插入图片描述

读取DATA_FLASH函数:IAP_ReadByte();

参数:读取的基地址、数组存储读取数据、长度、区域(DATA_ADDRESS_BEGIN, u8IapData_Read, SUM_FLASH_LEN, FLASH_DATA)

这是因为DATA_FLASH和CODE_FLASH使用共同的FLASH读取库函数因此需要区分是那个FLASH。

其中CODE_FLASH又强制的区分为BOOT和APP区,其中BOOT用于实现OTA。

3.2 DATA FLASH写入数据

在这里插入图片描述

在这里插入图片描述

关于擦除、写入、读取实现说明:

1、首先擦除是按照扇区擦除,并且不管是擦除、写入还是读取操作之前一定是解锁,操作之后一定是加锁。并且还需要根据Demo中提示擦除后填充0xFF。原因如下​:注释提到需增加“循环写入256字节延时”,因Flash写入需时间完成内部电荷注入,连续操作可能导致总线阻塞或看门狗触发。

2、写入函数:IAP_WriteByte();

参数:写入的基地址、数组存储读取数据、长度、区域
(DATA_ADDRESS_BEGIN, u8IapData_Write, SUM_FLASH_LEN, FLASH_DATA)
写的过程也是按照字节写入,IAP_WriteOneByte(StartAddr + j, Area, *(WriteBuffer + j));通过该函数实现,具体可以理解为:通过For循环j实现自增,又因为写入的是单个字节,因此数据之间的地址距离就是1,所以直接使用StartAddr + j就可以依次找到下一个地址,最后在将要存入数据传递进去。此外还需要注意的一点就是,我们在存的时候一定要存一个以后立马读取,逐个验证存入的有效性。最后我们是单个字节存入,因此每个字节的存都需要解锁和加锁。

擦除代码实现:需要注意的是擦除后需要立即写入“1”。

void IAP_Erase_All(u8 area)
{u16 i;u16 k = 0;u32 Begin_Addr = 0; if(area == APPROM_AREA) 																    // APP应用区{k = (APP_SIZE / ONE_PAGE_SIZE);Begin_Addr = APP_ADDRESS_BEGIN;area = FLASH_CODE;}else if(area == DATA_AREA)																	// DATA区,也即是存入标志位的区域{k = (DATA_SIZE / ONE_PAGE_SIZE);Begin_Addr = DATA_ADDRESS_BEGIN;area = FLASH_DATA;}FLASH_UnLock();                                             //先解锁for(i = 0 ; i < k ; i++) {		FLASH_Erase(area , i*ONE_PAGE_SIZE + Begin_Addr);         //确定擦除类型以及对应扇区的首地址     NWS20240103 需在此操作后,增加循环写入256字节延时if(area == FLASH_CODE)                                    // APP应用区{for(ii = 0;ii < 256; ii++){FLASH_Write(area,0xEFFF, 0xff);                       //ROM最后一个地址}}else if(area == FLASH_DATA)	                              // DATA_FLASH区,也即是存入标志位的区域{for(ii = 0;ii < 256; ii++){FLASH_Write(FLASH_DATA,0x3FF, 0xFF);                 //DATA区,最后一个地址,demo规定。}}		WDT_ClearWDT(); }FLASH_Lock();                                               //在上锁
}

擦除后立即写入的必要性

  • 确保数据一致性​:Flash擦除会将目标区域恢复为全1(0xFF),而写入只能将1改为0。若擦除后不立即写入,残留的旧数据可能影响后续读取(如ECC校验错误或数据混淆)。

  • 避免意外干扰​:擦除后的Flash区域处于不稳定状态(尤其是NOR Flash),立即写入可减少因电压波动或干扰导致的数据异常风险。

代码中的具体实现与潜在问题

  • 擦除后填充0xFF的意图​:

    示例代码在擦除后循环写入0xFF(如FLASH_Write(area, 0x3FF, 0xff)),看似冗余(因擦除后已为全1),但实际可能出于以下目的:

    • 验证擦除操作​:通过写入0xFF确认擦除成功(若擦除不完全,写入会失败)。

    • 预置稳定状态​:某些Flash芯片(如NAND)擦除后可能存在位翻转,写入全1可强制稳定单元状态。

  • 延时需求​:注释提到需增加“循环写入256字节延时”,因Flash写入需时间完成内部电荷注入,连续操作可能导致总线阻塞或看门狗触发。

  for(i = 0 ;i< 256 ;i++)		//连续256 bytes的写等待Flash执行完成{			FLASH_Write(FLASH_DATA,0x3FF, 0xFF); //写地址使用最后的地址(任意地址都可以,建议用使用较少的地址)}		

这是官方例程给的Demo。目的是:连续256 bytes的写等待Flash执行完成。

接下来是写操作

uint8_t IAP_WriteByte(uint32_t StartAddr, uint8_t * WriteBuffer, uint16_t WriteSize, uint8_t Area)//写单字节IAP操作   20221026
{uint16_t i;uint8_t  j;uint8_t State = 0;FLASH_UnLock();	FLASH_Erase(Area, StartAddr); //NWS20240103 需在此操作后,增加循环写入256字节延时	for(i = 0; i < 256; i++){FLASH_Write(Area, 0x1FF , 0xFF);//写FLASH_DATA地址}for(j = 0; j < WriteSize; j++){State = f_DealOTA_IAP_WriteOneByte(StartAddr + j, Area, *(WriteBuffer + j));if(State == 0){break;		}}FLASH_Lock();	return State;	
}

在写之前还需要进行擦除操作,但是次次擦除操作就是就是使用512个字节 也就是扇区0,

然后写的时候是按照单个字节写的,使用循环的方法,依次遍历需要写入的地址,通过StartAddr + j 实现地址增加,然后将数据写入该地址。

uint8_t IAP_WriteOneByte(uint32_t Addr, uint8_t Area, uint8_t Data)
{   uint8_t WriteState = 0;FLASH_UnLock();FLASH_Write(Area, Addr, Data);if(FLASH_Read(Area,Addr) == Data){WriteState = 1;																				//写入准确}else{WriteState = 0;																				//写入有误}FLASH_Lock();	return WriteState;																			//返回写入状态
}

每次在操作前和操作后一定要注意解锁和加锁。

3.3 DATA FLASH读数据

在这里插入图片描述



专栏介绍

《嵌入式通信协议解析专栏》
《PID算法专栏》
《C语言指针专栏》
《单片机嵌入式软件相关知识》
《FreeRTOS源码理解专栏》



文章源码获取方式:
如果您对本文的源码感兴趣,欢迎在评论区留下您的邮箱地址。我会在空闲时间整理相关代码,并通过邮件发送给您。由于个人时间有限,发送可能会有一定延迟,请您耐心等待。同时,建议您在评论时注明具体的需求或问题,以便我更好地为您提供针对性的帮助。

【版权声明】
本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议。这意味着您可以自由地共享(复制、分发)和改编(修改、转换)本文内容,但必须遵守以下条件:
署名:您必须注明原作者(即本文博主)的姓名,并提供指向原文的链接。
相同方式共享:如果您基于本文创作了新的内容,必须使用相同的 CC 4.0 BY-SA 协议进行发布。

感谢您的理解与支持!如果您有任何疑问或需要进一步协助,请随时在评论区留言。

http://www.xdnf.cn/news/16405.html

相关文章:

  • day062-监控告警方式与Grafana优雅展示
  • 【通识】设计模式
  • Ashampoo Background Remover(照片去背景工具) v2.0.2 免费版
  • MyBatis-Plus IService 接口全量方法实现与测试(续)
  • 【Python系列】从内存分析到性能剖析
  • 【c++】从 “勉强能用” 到 “真正好用”:中文问答系统的 200 行关键优化——关于我用AI编写了一个聊天机器人……(16)
  • HBuilder X打包发布微信小程序
  • 详解力扣高频SQL50题之180. 连续出现的数字【困难】
  • Product Hunt 每日热榜 | 2025-07-27
  • 如何思考一个动态规划问题需要几个状态?
  • J2EE模式---服务层模式
  • springboot基于Java与MySQL库的健身俱乐部管理系统设计与实现
  • 【前后端】node mock.js+json-server
  • vscode找不到python解释器的解决方案
  • listen() 函数详解
  • Petalinux驱动开发
  • 多智能体系统设计:协作、竞争与涌现行为
  • 零基础学习性能测试第六章:性能难点-Jmeter实现海量用户压测
  • 【奔跑吧!Linux 内核(第二版)】第5章:内核模块
  • 关于“PromptPilot” 之2 -目标系统:Prompt构造器
  • Linux c网络专栏第三章DPDK
  • Rust与Java DynamoDB、MySQL CRM、tokio-pg、SVM、Custors实战指南
  • UV: 下一代 Python 包管理工具
  • Unity 实时 CPU 使用率监控
  • 前缀和-560.和为k的子数组-力扣(LeetCode)
  • XFile 系统架构设计文档
  • iOS安全和逆向系列教程 第20篇:Objective-C运行时机制深度解析与Hook技术
  • 七、搭建springCloudAlibaba2021.1版本分布式微服务-skywalking9.0链路追踪
  • 前端基础班学习路线
  • GPGPU基本概念