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

文章目录

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


摘要

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

三、OTA代码分析

3.1 DATA FLASH空间使用

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

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

明确data flash大小

c 复制代码
#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 存储器擦除操作仅支持扇区擦除不支持字节擦除。在修改某个地址的数据之前,建议先将其他数据保存后,再擦除当前扇区,最后进行数据写入操作。

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

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"。

c 复制代码
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写入需时间完成内部电荷注入,连续操作可能导致总线阻塞或看门狗触发。

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

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

接下来是写操作

c 复制代码
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 实现地址增加,然后将数据写入该地址。

c 复制代码
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 协议进行发布。

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

相关推荐
橙小花1 小时前
C语言:指针、变量指针与指针变量、数组指针与指针数组
c语言·开发语言
YouQian7721 小时前
问题 C: 字符串匹配
c语言·数据结构·算法
SoveTingღ1 小时前
【开发环境配置】VScode里面配置cmake遇到的问题
c语言·vscode·cmake·嵌入式软件·开发环境配置
艾莉丝努力练剑1 小时前
【LeetCode&数据结构】二叉树的应用(二)——二叉树的前序遍历问题、二叉树的中序遍历问题、二叉树的后序遍历问题详解
c语言·开发语言·数据结构·学习·算法·leetcode·链表
Despacito0o2 小时前
STM32 I2C通信完整教程:从协议原理到硬件实现
stm32·单片机·嵌入式硬件
你好,奋斗者!2 小时前
小电流驱动大电流:原理、实现方式与应用前景
stm32·单片机·嵌入式硬件·电路设计
潼心1412o5 小时前
C语言(长期更新)第6讲:函数
c语言·开发语言
XINVRY-FPGA5 小时前
XCZU4EV-1FBVB900E Xilinx FPGA AMD Zynq UltraScale+ MPSoC EV(Embedded Vision)
arm开发·嵌入式硬件·计算机视觉·fpga开发·硬件架构·硬件工程·fpga
猫猫的小茶馆5 小时前
【STM32】FreeRTOS 任务的删除(三)
java·linux·stm32·单片机·嵌入式硬件·mcu·51单片机
学不动CV了6 小时前
单片机ADC采集机理层面详细分析(二)
c语言·arm开发·stm32·单片机·嵌入式硬件·开源·51单片机