IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 压缩算法在单片机升级中的应用 -> 正文阅读

[嵌入式]压缩算法在单片机升级中的应用

? ? ? ?在项目开发过程中,难免会遇到需要在线升级的情况,而升级包过大会导致升级时间过长,影响产品性能和用户体验,因此我们可以将需要升级的程序压缩,然后在bootloader中解压。差分升级实际上就是对比出两个升级包的差异,然后再对差异进行压缩。这里我们不讲差分,只讲压缩。

? ? ? ?压缩算法有很多,QuickLZ是比较适合单片机的一种轻量级无损压缩算法。QuickLZ是一个号称压缩速度最快的压缩库,以下是几种较流行的压缩库的压缩率和速度对比。虽然QuickLZ的压缩率没有Zlib高,但压缩率相差无几,且解压速度上QuickLZ完胜Zlib,且QuickLZ消耗的资源少(仅需0.5KB的ROM,自身几乎不占RAM),可运行在嵌入式设备上,因此选择QuickLZ作为升级程序包的压缩算法。

Library

Level

Compressed size

Compression Mbyte/s

Decompression Mbyte/s

QuickLZ C 1.5.0

1

47.9%

308

358

QuickLZ C 1.4.0

1

47.9%

272

332

QuickLZ C 1.4.0

2

42.3%

131

309

QuickLZ C 1.4.0

3

40.0%

31

516

QuickLZ C# 1.4.0

1

47.9%

133

132

QuickLZ Java 1.4.0

1

47.9%

127

95

LZF 3.1

UF

54.9%

204

396

LZF 3.1

VF

51.9%

193

384

FastLZ 0.1.0

1

53.0%

173

442

FastLZ 0.1.0

2

50.7%

167

406

LZO 1X 2.02

1

48.3%

169

434

zlib 1.22

1

37.6%

55

234

? ? ? ?下面给出一个QuickLZ在单片机升级的应用。我们可以在编译完后使用QuickLz对生成的升级文件进行压缩,升级时传输压缩后的升级文件,然后后在Boot中进行解压验证,最后擦写Flash完成升级。

? ? ? ?由于单片机的内存十分有限,在RAM中接收升级包并解压不现实。因此我们将升级包进行分段,以4KB为单位进行分段压缩,bootloader中就可以以4KB为单位进行分段解压。需要注意的是,接收的升级数据都是先存放在FLASH的压缩区域里(定义一块分区专门存放压缩的数据),接收完成后,每次从压缩区域里读取4K压缩数据进行解压,解压完成后将解压后的数据写回到FLASH的解压区域里(定义一块分区专门存放解压后数据),全部解压

? ? ? ?压缩过程如下:将升级包进行累加求和,得到2BYTE的校验和,并将该校验和追加到升级包数据尾部得到带求和的升级包,将该升级包数据按4KB分割成一个个PACK,最后不足4KB的部分也当成一个PACK处理,然后对每个PACK进行压缩,得到一个4字节的压缩长度和压缩后的数据,将所有PACK压缩后的这两个数据进行“串联”,得到最终的压缩数据包。

升级包压缩流程

? ? ? ?明确了压缩的流程后,解压流程就很简单了,解压过程就是压缩过程的逆向反推。 流程如下图所示。首先进行解压初始化,这一步主要是完成SPI FLASH的初始化,检测升级标志位是否存在,若升级标志位存在,则开始解压流程。首先读取4字节数据,该4字节表示后面紧跟着的压缩数据的长度,根据长度读取压缩数据并进行解压,解压成功则重复上述过程直到解压完整个bin文件。若中途有出现解压错误,那么解压就失败了。解压的过程中也会对解压数据进行累计求和,当解压完成后,比较该累计的求和与解压后的最后两字节数据是否一致,若一致则表示解压成功,否则解压失败。最后去除这两个字节的求和校验,因为这两个字节是压缩时额外添加的,并不是真正的升级数据。

升级包解压流程

?附上压缩和解压的部分程序,压缩程序在PC上运行,在程序编译后执行压缩程序,解压程序则是在单片机运行。

//压缩程序

#include "quicklz.h"
#include "stdio.h"
#include "stdlib.h"

#define RT_NULL						   0
#define BLOCK_HEADER_SIZE              4
#define COMPRESS_BUFFER_SIZE           4096
#define DCOMPRESS_BUFFER_SIZE          4096

#define BUFFER_PADDING                 QLZ_BUFFER_PADDING

typedef unsigned char	uint8_t;
typedef unsigned short	uint16_t; 

uint8_t filebuff[256 * 1024];

int main(int argc, char * argv[])
{
	qlz_state_compress *state_compress = RT_NULL;
	uint8_t *cmprs_buffer = RT_NULL, *buffer = RT_NULL;
	uint8_t buffer_hdr[BLOCK_HEADER_SIZE] = { 0 };
	size_t cmprs_size = 0, block_size = 0, totle_cmprs_size = 0;
	size_t file_size = 0, i = 0;
	int ret = 0;
	uint16_t sum = 0;
	uint8_t sumdata[2];
	FILE *f_in = NULL, *f_out = NULL;

	printf("[qlz] QuizkLz Start!\n");

	f_in = fopen(".\\Tools\\old.bin","ab+");

	f_out= fopen(".\\Tools\\new.bin", "wb");

	if (f_in == NULL || f_out == NULL)
	{
		printf("[qlz] File open err\n");
		goto _exit;
	}

	fseek(f_in, 0L, SEEK_END);
	file_size = ftell(f_in);
	fseek(f_in, 0L, SEEK_SET);

	if (file_size == 0)
	{
		printf("[qlz] File size = 0\n");
		goto _exit;
	}

	fread(filebuff, 1, file_size, f_in);

	for (size_t j = 0; j < file_size; j++)
	{
		sum += filebuff[j];
	}
	sumdata[0] = sum >> 8;
	sumdata[1] = sum &0x00FF;
	fseek(f_in, 0L, SEEK_END);
	fwrite(sumdata, 1, 2, f_in);
	file_size += 2;
	fseek(f_in, 0L, SEEK_SET);

	cmprs_buffer = (uint8_t *)malloc(COMPRESS_BUFFER_SIZE + BUFFER_PADDING);
	buffer = (uint8_t *)malloc(COMPRESS_BUFFER_SIZE);
	if (!cmprs_buffer || !buffer)
	{
		printf("[qlz] No memory for cmprs_buffer or buffer!\n");
		ret = -1;
		goto _exit;
	}
	state_compress = (qlz_state_compress *)malloc(sizeof(qlz_state_compress));
	if (!state_compress)
	{
		printf("[qlz] No memory for state_compress struct, need %d byte, or you can change QLZ_HASH_VALUES to 1024 !\n",
			sizeof(qlz_state_compress));
		ret = -1;
		goto _exit;
	}
	memset(state_compress, 0x00, sizeof(qlz_state_compress));

	printf("[qlz] Compress start : ");
	for (i = 0; i < file_size; i += COMPRESS_BUFFER_SIZE)
	{
		if ((file_size - i) < COMPRESS_BUFFER_SIZE)
		{
			block_size = file_size - i;
		}
		else
		{
			block_size = COMPRESS_BUFFER_SIZE;
		}

		memset(buffer, 0x00, COMPRESS_BUFFER_SIZE);
		memset(cmprs_buffer, 0x00, COMPRESS_BUFFER_SIZE + BUFFER_PADDING);

		fread( buffer,1, block_size, f_in);



		/* The destination buffer must be at least size + 400 bytes large because incompressible data may increase in size. */
		cmprs_size = qlz_compress(buffer, (char *)cmprs_buffer, block_size, state_compress);

		/* Store compress block size to the block header (4 byte). */
		buffer_hdr[3] = cmprs_size % (1 << 8);
		buffer_hdr[2] = (cmprs_size % (1 << 16)) / (1 << 8);
		buffer_hdr[1] = (cmprs_size % (1 << 24)) / (1 << 16);
		buffer_hdr[0] = cmprs_size / (1 << 24);

		fwrite(buffer_hdr, 1,BLOCK_HEADER_SIZE, f_out);
		fwrite(cmprs_buffer,1, cmprs_size, f_out);

		totle_cmprs_size += cmprs_size + BLOCK_HEADER_SIZE;
		printf(">");
	}

	printf("\n");
	printf("[qlz] Compressed %d bytes into %d bytes , compression ratio is %d%!\n", file_size, totle_cmprs_size,
		(totle_cmprs_size * 100) / file_size);

_exit:

	if (f_in)
		fclose(f_in);
	if (f_out)
		fclose(f_out);

	if (cmprs_buffer)
	{
		free(cmprs_buffer);
	}
	if (buffer)
	{
		free(buffer);
	}
	if (state_compress)
	{
		free(state_compress);
	}
	if (file_size == 0 || totle_cmprs_size == 0)
	{
		printf("[qlz] Compressed failed!!!\n");
		remove("old.bin");
		remove("new.bin");
	}
	printf("[qlz] QuickLz End!\n");

	return ret;
}
//解压程序
u32 app_QuickLz_Decompress(u32 u32Addr,u32 u32FileSize)
{
	u16 u16Sum = 0;
	u8 u8ReadBuffer_hdr[BLOCK_HEADER_SIZE] = { 0 };
	u32 u32DcmprsSize = 0, u32BlockSize = 0, u32TotalDcmprsSize = 0, u32AddrRead = 0,u32AddrWrite, i = 0,j = 0;
	
	if(u32FileSize < DCOMPRESS_BUFFER_SIZE)
	{
		return 0;
	}
	
	if(sys_SpiFlash_Erase(1,UPDATE_BUFF_DECOMPRESS_ADDR,UPDATE_BUFF_DECODE_SIZE) == RET_ERR)		//先清除解压缓冲区
	{
		return 0;
	}
	memset(&s_stDecompressState,0,sizeof(s_stDecompressState));
	
	u32AddrRead = u32Addr;
	u32AddrWrite = UPDATE_BUFF_DECOMPRESS_ADDR;
	for(i = 0; i < u32FileSize; i+= BLOCK_HEADER_SIZE + u32BlockSize)							//分段解压数据
	{
		if(sys_SpiFlash_Read(1,u32AddrRead,u8ReadBuffer_hdr,BLOCK_HEADER_SIZE) == RET_ERR)		
		{
			return 0;
		}
		u32AddrRead += BLOCK_HEADER_SIZE;
		u32BlockSize = u8ReadBuffer_hdr[0] * (1 << 24) + u8ReadBuffer_hdr[1] * (1 << 16) + u8ReadBuffer_hdr[2] * (1 << 8) + u8ReadBuffer_hdr[3];	
		if(!u32BlockSize)
		{
			return 0;
		}
		memset(u8ReadBuffer, 0x00, sizeof(u8ReadBuffer));
		memset(s_u8DcmprsBuffer, 0x00, DCOMPRESS_BUFFER_SIZE);
		
		if(sys_SpiFlash_Read(1,u32AddrRead,u8ReadBuffer,u32BlockSize) == RET_ERR)
		{
			return 0;
		}
		u32AddrRead += 	u32BlockSize;
		u32DcmprsSize = qlz_decompress((const char *)u8ReadBuffer, s_u8DcmprsBuffer, &s_stDecompressState);
		if(!u32DcmprsSize)
		{
			return 0;
		}
		for(j = 0; j < u32DcmprsSize; j++)
		{
			u16Sum += s_u8DcmprsBuffer[j];
		}
		if(sys_SpiFlash_Write(1,u32AddrWrite,s_u8DcmprsBuffer,u32DcmprsSize) == RET_ERR)
		{
			return 0;
		}
		u32AddrWrite += u32DcmprsSize;
		
		u32TotalDcmprsSize += u32DcmprsSize;
		app_Iwdg_Refresh();
	}
	u16Sum -= (s_u8DcmprsBuffer[u32DcmprsSize-2] + s_u8DcmprsBuffer[u32DcmprsSize-1]);		
	if(u16Sum != ((s_u8DcmprsBuffer[u32DcmprsSize-2] <<8) + s_u8DcmprsBuffer[u32DcmprsSize-1]))
	{
		return 0;
	}
	DEBUG_MSG(1,"Decompress ok ,file size = %d",u32TotalDcmprsSize - 2);
	return u32TotalDcmprsSize - 2;									//原始数据的最后两字节是求和校验
}

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-16 11:25:15  更:2022-05-16 11:25:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:45:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码