嵌入式代码升级——IAP

news2024/11/14 15:13:26

目录

 IAP的特点

实现 IAP 功能

STM32 正常的程序运行流程

STM32 加入IAP后的运行流程

 程序执行流程

BootLoader程序

APP1程序

APP2程序

验证操作步骤


        IAP(In-Application Programming)指的是在应用程序运行时对其自身的Flash存储器进行编程的操作。这种技术允许嵌入式设备在不需要外部编程器或调试器的情况下,通过其自身的程序来更新、修改或升级存储在Flash存储器中的代码或数据。

        OTA:空中下载技术--通过联网模块下载程序,更新本地运行的程序。

 IAP的特点

1.传统的嵌入式系统更新通常需要连接外部编程器或使用特定的调试接口,这增加了开发的复杂性和设备部署后的维护难度。而IAP技术使得设备可以通过预留的通信接口(如串口、USB、网口等)接收新的代码或数据,并在运行时写入Flash存储器,从而实现了免拆机壳的升级。

2.对于具备网络通信功能的嵌入式设备,IAP技术还可以通过网络实现远程升级。

3.IAP技术减少了因频繁拆装机壳和连接外部设备而带来的成本和时间消耗,提高了升级效率。

实现 IAP 功能

        想要实现程序的更新操作,需要我们在编写两部分程序代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、 USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分程序都烧录在单片机的FLASH中,芯片上电后,第一部分的代码先执行,检测是否对第二部分的代码更新,如果不需要更新则直接运行第二部分的代码;如果需要更新,执行更新的相关操作,再运行第二部分的代码。

        其中第一部分的代码通过ST_Link、JTAG、SWD等方式烧录;第二部分的代码则通过第一部分代码的IAP来烧录进单片机中,或者在首次烧录的时候和第一部分的代码一块烧录,后续需要跟新的时候,再利用IAP进行更新。

        在上面的过程中,将第一个部分代码称之为 Bootloader 程序(引导加载程序),第二部分代码称为 APP 程序,他们存放在 STM32的FLASH 的不同地址范围,一般从最低地址区开始存放 Bootloader,紧跟其后的就是 APP 程序(注意,如果 FLASH 容量足够,是可以设计很多 APP 程序的,我们按最常用的两个 APP 程序的情况来学习IAP)。这样我们就是要实现 3 个程序:Bootloader 和 APP1和APP2。

STM32 正常的程序运行流程

        STM32 的内部闪存(FLASH)地址起始于 0x08000000,一般情况下,程序文件就从此地址开始写入。此外 STM32 是基于 Cortex-M3 内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是 0x08000004,当中断来临,STM32 的内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序

        STM32 在复位后,先从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过程中,如果收到中断请求(发生重中断),此时 STM32 强制将 PC 指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。(PC指针,用于存储CPU接下来要执行的指令的内存地址。换句话说,它指向了当前指令序列中的下一条指令

STM32 加入IAP后的运行流程

        在加入 IAP 之后程序运行流程图中,可以看到,STM32 在复位后,还是从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数,如图标号①所示,在执行完 IAP 以后(即将新的 APP 代码写入 STM32的 FLASH,灰底部分。新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,如图标号②和③所示,同样 main 函数为一个死循环,并且注意到此时 STM32 的 FLASH,在不同位置上,共有两个中断向量表。

        在 main 函数执行过程中,如果 CPU 得到一个中断请求,PC 指针仍强制跳转到地址0X08000004 中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回 main 函数继续运行,如图标号⑥所示。

(IAP程序须满足两个要求:新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始,必须将新程序的中断向量表相应的移动,移动的偏移量为 x)

STM32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等 3 部分组成。代码最终都会被编译成二进制文件hex并保存在Flash中。

对FLASH的主存储器进行分区,使用的是STM32F103ZE,共512K的Flash大小,我们将它分成三个区,BootLoader区存放启动代码、App1区存放应用代码、App2区(备份区)存放暂存的升级代码,最好是升级的代码限制在250K以内(不要求)

 程序执行流程

        在程序运行开始时,单片机先执行BootLoader程序,同时检测APP2备份区有没有用于升级的代码(检查标志位,一般设置在此区域的最后四字节)。如果检测到备份区有需要升级的代码,就将APP2部分的代码拷贝到APP1区域中,再去运行APP1区域的代码程序;如果检测到备份区没有相关的代码,就直接去执行APP1的代码;如果APP1也没有可执行的代码,则就只能执行Bootloader区域的代码。

        通过上面的图看到,BootLoader和App1这两个程序的中断向量表位置不一样, 所以跳转到App1区域内首先去更改程序的向量表,然后再去执行其他的应用程序。需要执行升级的代码部分放到APP2内,重启时就可以按上述内容去更新程序了。

BootLoader程序

在编写此部分的代码时,主要的内容就是:读取到APP2备份区的标志位,将APP2的代码写入到APP1中,然后执行APP1。

#include "update.h"
#include "stmflash.h"
#include "stdio.h"

//检查是否有更新标志
//1.有更新标志  将APP2区域的拷贝到APP1区域,并且跳转到APP1区域执行
//2.没有更新标志  执行原有APP1区域的代码
//3.APP1和APP2区域都没有代码   不跳转,执行bootloader
void Check_UPdate_Flag(void)
{
	uint16_t App2FlagBuff[2] = {0};
//1.读APP2区域存放的标志位,如果有0xAAAA,表示APP2中有待更新的程序
	STMFLASH_Read(APP2_FLAG_ADDR, App2FlagBuff, 2);
	if(App2FlagBuff[0] == 0xAAAA && App2FlagBuff[1] == 0xAAAA) {
		//APP2区域有更新程序
		printf("有更新程序,正在执行代码升级\r\n");
		UpdateFun();   //2.将程序从APP2搬运到APP1
	}
	else {
		//APP2区域没有新的程序
		printf("没有新的程序,执行原有APP\r\n");
		UserFlashAppRun(); //3.没有新的APP2程序,执行原有的APP1
	}	
}


//擦除APP1区域,方便接收新的代码  FLASH必须先擦除才能写入
void Erase_APP1(void)
{
	STMFLASH_Erase(FLASH_APP1_ADDR, APP_MAX_SIZE);
	printf("APP1备份区域擦除成功\r\n");
}

//擦除APP2区域,方便接收新的代码  FLASH必须先擦除才能写入
void Erase_APP2(void)
{
	STMFLASH_Erase(FLASH_APP2_ADDR, APP_MAX_SIZE);
	printf("APP2备份区域擦除成功\r\n");
}

//固件更新函数  将APP2区域的代码搬运到APP1区域  每次搬运2K
uint16_t ReadBuff[STM_SECTOR_SIZE/2] = {0};  
void UpdateFun(void)
{
//	uint16_t App2FlagBuff[2] = {0xFFFF, 0xFFFF};  
	printf("开始更新固件...\r\n");
	Erase_APP1();   //擦除APP1区域250K
	for(uint16_t i=0; i<APP_SIZE; i++) {   //每次读2K
		printf("正在更新固件%d...\r\n", i);
		STMFLASH_Read(FLASH_APP2_ADDR+i*STM_SECTOR_SIZE, ReadBuff, STM_SECTOR_SIZE/2);   //从APP2的起始地址开始读2K
		STMFLASH_Write_NoCheck(FLASH_APP1_ADDR+i*STM_SECTOR_SIZE, ReadBuff, STM_SECTOR_SIZE/2);	 //将读到的数据写到APP1的区域
	}
	printf("固件更新完成!\r\n");	
//	STMFLASH_Write_NoCheck(APP2_FLAG_ADDR, App2FlagBuff, 2);  //清除APP2区域的标志位
	Erase_APP2();   //擦除APP2区域
	UserFlashAppRun();   //跳转到APP1区域去执行
}

//跳转到Flash中用户代码执行
void UserFlashAppRun(void)
{
	printf("开始执行FLASH用户代码!!\r\n");  
								//0x08003000     0x08003004
	if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.  如果不是表示地址不合法
	{	 
		printf("成功跳转APP1区域执行\r\n");
		IAP_Load_App(FLASH_APP1_ADDR);//执行FLASH APP1代码
	}else 
	{
		printf("APP程序加载失败!\r\n");   
	}									 
}

pFunction Jump_To_Application;
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void IAP_Load_App(u32 AppxAddr)
{
	if(((*(__IO uint32_t*)AppxAddr)&0x2FFE0000)==0x20000000)	//检查栈顶地址是否合法.
	{ 
		Jump_To_Application=(pFunction)*(uint32_t*)(AppxAddr+4);		//用户代码区第二个字为程序开始地址(复位地址)		
		__set_MSP(*(__IO uint32_t*)AppxAddr);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)   在core_cm3.c  28行
		Jump_To_Application();									//跳转到APP.
	}	
}
#ifndef __UPDATE_H_
#define __UPDATE_H_

#include "stm32f10x.h"

//我们使用的FLASH大小:512K
//0x08000000-0x0807FFFF
//Boot -- 12K			0x08000000-0x08002FFF		0x3000
//APP1 -- 250K		0x08003000-0x080417FF		0x3E800
//APP2 -- 250K		0x08041800-0x0807FFFF		0x3E800
#define FLASH_APP1_ADDR		0x08003000  		//第一个应用程序APP1起始地址(存放在FLASH)
#define FLASH_APP2_ADDR		0x08041800      //第二个应用程序APP2的起始地址
#define APP1_FLAG_ADDR		(FLASH_APP2_ADDR-4)	//APP1是否有更新程序标志位
#define APP2_FLAG_ADDR		(0x08080000-4)	//APP2是否有更新程序标志位
#define APP_SIZE	(0x3E800/STM_SECTOR_SIZE)	//分给APP的页数量
#define APP_MAX_SIZE	0x3E800   //250K

typedef  void (*pFunction)(void);   //函数指针  函数指针是一个指针,指向1个函数
//char *pFunction(void); //指针函数   指针函数是一个函数,返回的是1个指针(地址)

void Check_UPdate_Flag(void);
void UpdateFun(void);
void UserFlashAppRun(void);
void IAP_Load_App(u32 AppxAddr);
void Erase_APP1(void);
void Erase_APP2(void);

#endif

APP1程序

        进入该部分,首先修改向量表, 因为本程序是由BootLoader跳转过来的, 不修改向量表后面会出现问题;需要在APP的基本功能上加入串口接收数据并保存到APP2(备份区)的功能代码。

(生成的hex文件中包含地址信息,在生成app部分代码的hex文件时,注意更改ROM的地址位置,RAM的地址在0X2000xxxx上位置才算合理)

如果APP的代码使用下载器下载,那么我们就需要修改下载的属性

#include "update.h"
#include "stmflash.h"
#include "stdio.h"

//生成二进制文件
//D:\Keil5\ARM\ARMCC\bin\fromelf.exe --bin -o .\Objects\Demo.bin .\Objects\Demo.axf

uint8_t RecvBuff[2] = {0};   //存放准备写入APP2的数据,在串口中断中调用,没收到2个字节,写入一次
uint32_t RecvNum = 0;   //接收的数量  标记升级文件的大小
uint32_t Addr = FLASH_APP2_ADDR;   //写入APP2的地址   最开始是APP2区域的起始地址
uint8_t RecvTime = 0;  //用来判断是否接收完成
uint8_t RecvOver = 0;   //升级文件接收完成  1接收完成
uint16_t App2FlagBuff[2] = {0xAAAA, 0xAAAA};  //APP2区域是否有升级文件的标志  获取升级文件完成之后,写入APP2区域有升级文件的标记

//擦除APP2区域,方便接收新的代码  FLASH必须先擦除才能写入
void Erase_APP2(void)
{
	STMFLASH_Erase(FLASH_APP2_ADDR, APP_MAX_SIZE);
	printf("APP2备份区域擦除成功\r\n");
}

//判断从串口发送的升级文件是否发送完成
//如何确定  最后一个字节收到之后,计时会溢出
void RecvOverFun(void)
{
	if(RecvOver == 1) {
		printf("APP数据接收完成:%d\r\n", RecvNum);
		RecvNum = 0;
		RecvOver = 0;
		RecvTime = 0;
		Addr = FLASH_APP2_ADDR;
		
		STMFLASH_WriteHalfWord(APP2_FLAG_ADDR, App2FlagBuff[0]);    //写APP2区域有升级文件的标志
		STMFLASH_WriteHalfWord(APP2_FLAG_ADDR+2, App2FlagBuff[0]);
		printf("核对数据无误后,请按下复位按键进行数据更新\r\n");   //也可以选择调用复位函数   看门狗复位   NVIC_SystemReset();
	}
}

//修改中断向量表的地址偏移
void NVIC_SETVectorTable(void)
{
//	SCB->VTOR = FLASH_BASE | 0x3000;//中断向量表的地址偏移,寄存器写法
//	void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);   //库函数写法  misc.h 198行
	NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x3000);
}

void RecvTimeOut(void)	//1ms一次
{
	if(RecvTime) {
		RecvTime++;
		if(RecvTime >= 100) {
			RecvOver = 1;
			RecvTime = 0;
		}
	}
}

APP2程序

该程序只需要写需要升级的代码即可。然后生成bin文件即可。

#include "update.h"
#include "stmflash.h"
#include "stdio.h"

//生成二进制文件
//D:\MDK5\ARM\ARMCC\bin\fromelf.exe --bin -o .\Objects\Demo.bin .\Objects\Demo.axf

uint8_t RecvBuff[2] = {0};   //存放准备写入APP2的数据,在串口中断中调用,没收到2个字节,写入一次
uint32_t RecvNum = 0;   //接收的数量  标记升级文件的大小
uint32_t Addr = FLASH_APP2_ADDR;   //写入APP2的地址   最开始是APP2区域的起始地址
uint8_t RecvTime = 0;  //用来判断是否接收完成
uint8_t RecvOver = 0;   //升级文件接收完成  1接收完成
uint16_t App2FlagBuff[2] = {0xAAAA, 0xAAAA};  //APP2区域是否有升级文件的标志  获取升级文件完成之后,写入APP2区域有升级文件的标记

//擦除APP2区域,方便接收新的代码  FLASH必须先擦除才能写入
void Erase_APP2(void)
{
	STMFLASH_Erase(FLASH_APP2_ADDR, APP_MAX_SIZE);
	printf("APP2备份区域擦除成功\r\n");
}

//判断从串口发送的升级文件是否发送完成
//如何确定  最后一个字节收到之后,计时会溢出
void RecvOverFun(void)
{
	if(RecvOver == 1) {
		printf("APP数据接收完成:%d\r\n", RecvNum);
		RecvNum = 0;
		RecvOver = 0;
		RecvTime = 0;
		Addr = FLASH_APP2_ADDR;
		
		STMFLASH_WriteHalfWord(APP2_FLAG_ADDR, App2FlagBuff[0]);    //写APP2区域有升级文件的标志
		STMFLASH_WriteHalfWord(APP2_FLAG_ADDR+2, App2FlagBuff[0]);
		printf("核对数据无误后,请按下复位按键进行数据更新\r\n");   //也可以选择调用复位函数   看门狗复位   NVIC_SystemReset();
	}
}

//修改中断向量表的地址偏移
void NVIC_SETVectorTable(void)
{
//	SCB->VTOR = FLASH_BASE | 0x3000;//中断向量表的地址偏移,寄存器写法
//	void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);   //库函数写法  misc.h 198行
	NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x3000);
}

void RecvTimeOut(void)	//1ms一次
{
	if(RecvTime) {
		RecvTime++;
		if(RecvTime >= 100) {
			RecvOver = 1;
			RecvTime = 0;
		}
	}
}

D:\Keil5\ARM\ARMCC\bin\fromelf.exe是你的KEIL5安装路径下的romelf.exe是一个keil自带的生成bin文件的工具绝对路径;

--bin -o .\Objects\Demo.bin .\Objects\Demo.axf将这部分的可执行程序改成自己的可执行程序文件名;

然后将D:\Keil5\ARM\ARMCC\bin\fromelf.exe --bin -o .\Objects\Demo.bin .\Objects\Demo.axf复制到下图位置上。

        在 MDK 编译成功之后,调用 fromelf.exe(注意,我的 MDK 是安装在 D盘文件夹下,如果你是安装在其他目录,请根据自己的目录修改fromelf.exe 的路径),根据当前工程的 Demo.axf(如果是其他的名字,请记住修改,这个文件存放在 Objects 目录下面,格式为 xxx.axf),生成一个 .bin 的文件。并存放在 axf 文件相同的目录下,即工程的 Objects 文件夹里面。

        在得到.bin 文件之后,我们只需要将这个 bin 文件传送给单片机,即可执行 IAP 升级。(我们也可以将bin文件无线发送,存放在SD卡内,存放在外部FLASH内等等方式进行代码升级,其中无线发送的形式叫OTA)

        把APP2生成的bin文件,通过串口,发送到APP1的运行设备上,就会自动的保存APP2的代码数据到对应的Flash地址下,那么按下复位按键后(也可以软件复位),再次运行bootloader代码,就会加载APP2的数据到APP1的地址下,并运行新的程序。

最后重启或者按下复位键即可。

验证操作——步骤

1.将BOOTLoader程序编译后下载到单片机中,打开串口助手显示bootloader执行,led1、led2同时闪烁。

2.下载APP1程序到单片机中,观察现象。led3、led4同时闪烁。

3.编译APP2程序生成bin文件。

按下复位键后,更新代码,蜂鸣器响。

同理,可以先下载APP2,在发送APP1的bin文件。验证IAP的功能。

另外利用STM32ST—LINK Utility也可以将程序烧录到单片机中。将hex文件直接托拽到软件界面中去然后烧录即可。

拓展:

hex文件:包含地址信息;bin文件:不包含地址信息。HEX文件比BIN文件大,HEX文件有地址信息,BIN文件没有地址信息。HEX文件和BIN文件都可以是程序文件,但是HEX文件放的信息比BIN多,所以代码会比较大。一般远程升级用bin文件。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

修复 Ubuntu 24.04 Dock 丢失应用程序图标

找出应用程序窗口的类名 首先&#xff0c;您需要启动应用程序窗口。然后&#xff0c;按 Alt F2 启动“运行 Command”对话框。当对话框打开时&#xff0c;输入 lg 并按 Enter 键。 在该窗口中&#xff0c;单击Windows按钮&#xff0c;然后找出目标应用程序窗口的类名称。 在/…

激光干涉仪可以完成哪些测量:全面应用解析

在高端制造领域&#xff0c;精度是衡量产品质量的关键指标之一。激光干涉仪作为一项高精度测量技术&#xff0c;其应用广泛&#xff0c;对于提升产品制造精度具有重要意义。 线性测量&#xff1a;精确定位的基础 激光干涉仪采用迈克尔逊干涉原理&#xff0c;实现线性测量。该…

怎么转播别人的直播

转播别人的直播&#xff0c;特别是实现无缝的实时转播&#xff0c;可以通过一些平台的功能来实现&#xff0c;比如快手和抖音。下面是一个基本的步骤说明&#xff0c;但请注意&#xff0c;具体操作可能会因平台更新或政策变化而有所不同&#xff1a; 找到想要转播的直播间&…

Vue打包文件dist放在SpringBoot项目下运行(正确实现全过程)

项目开发中&#xff0c;一般我们都会使用SpringBootVue进行前后端开发。 在部署时&#xff0c;会后端启动一个服务&#xff0c;再启动一个nginx&#xff0c;nginx中配置前端打包文件dist进行项目访问。 实际上&#xff0c;我们也可以把打包好的dist目录放在SpringBoot项目下进…

初识STM32:寄存器编程 × 库函数编程 × 开发环境

STM32的编程模型 假如使用C语言的方式写了一段程序&#xff0c;这段程序首先会被烧录到芯片当中&#xff08;Flash存储器中&#xff09;&#xff0c;Flash存储器中的程序会逐条的进入CPU里面去执行。 CPU相当于人的一个大脑&#xff0c;虽然能执行运算和执行指令&#xff0c;…

C++视觉开发 五.答题卡识别

目录 一.单道题目的识别 1.基本流程及原理 2.实现程序 二.整张答题卡原理 1.图像预处理 Canny 边缘检测 2.答题卡处理 cv::warpPerspective cv::getPerspectiveTransform 3.筛选出所有选项 cv::boundingRect 4.将选项按题目分组 三.完整实现程序 1.实现代码 2.结…

支付宝沙箱对接(GO语言)

支付宝沙箱对接 1.1 官网1.2 秘钥生成&#xff08;系统默认&#xff09;1.3 秘钥生成&#xff08;软件生成&#xff09;1.4 golan 安装 SDK1.5 GoLand 代码1.6 前端代码 1.1 官网 沙箱官网: https://open.alipay.com/develop/sandbox/app 秘钥用具下载&#xff1a; https://ope…

MSPM0G3507——超声波模块移植代码

超声波没有做单独的代码文件 直接自己创建.c.h文件&#xff0c;将这些复制粘贴即可&#xff0c;然后进行SYSCFG配置按照这些配置即可&#xff0c;有啥问题直接评论区提出&#xff0c;如果看不懂的话评论区说一下&#xff0c;再出讲解 超声波.c文件 #include "ti_msp_dl…

线程并发库复习

1.进行和线程 什么是进程&#xff1a;进程是内存分配的基本单位&#xff0c;它是程序执行时的一个实例&#xff0c;会被放到进程就绪队列&#xff0c;等进程调度器选择它&#xff0c;给它时间片&#xff0c;它才会运行。在java中启动进程&#xff0c;main&#xff0c;test&…

【无标题】维度模型:

维度模型 基本概念维度模型中设计表主要分为2大类事实表维度表 基本概念 事实 &#xff1a;行为所产生的事情&#xff08;数据&#xff09; 维度&#xff1a;分析数据的角度&#xff08;状态&#xff09; 维度模型中设计表主要分为2大类 事实表&#xff1a;用于保存行为所产生…

实战 | YOLOv8使用TensorRT加速推理教程(步骤 + 代码)

导 读 本文主要介绍如何使用TensorRT加速YOLOv8模型推理的详细步骤与演示。 YOLOv8推理加速的方法有哪些? YOLOv8模型推理加速可以通过多种技术和方法实现,下面是一些主要的策略: 1. 模型结构优化 网络剪枝:移除模型中不重要的神经元或连接,减少模型复杂度。 模型精…

【通信协议】八、CDL(Caterpillar Data Link)协议解析

1、协议简介 CDL(Caterpillar Data Link)是caterpillar的通信协议,该品牌发动机ECM与各控制单元进行通信时,采用基于RS-485的物理层规范进行开发的CDL协议进行通信; 2、物理层 信号传输方式:差分信号(通过两条线的电压差识别逻辑0或逻辑1) 通信方式:半双工通信(只允…

iPhone短信被拉黑了怎么恢复?4步快速移除黑名单

在日常使用iPhone的过程中&#xff0c;可能会因为误操作或其他原因将某些联系人拉入黑名单&#xff0c;导致无法接收他们发送的短信。那么&#xff0c;iPhone短信被拉黑了怎么恢复&#xff1f; 其实&#xff0c;只需要简单的4步操作&#xff0c;就能快速将联系人移出黑名单&am…

Windows 安装 PyCharm

PyCharm下载 PyCharm官网&#xff1a;http://www.jetbrains.com/pycharm/download/ Professional 专业的&#xff0c;Community 社区&#xff0c;这里我们点击Community下的 DOWNLOAD下载。 PyCharm 安装使用 PyCharm是一种Python IDE&#xff0c;带有一整套可以帮助用户在使…

猎人维修大师免狗版

技术文档摘要 标题&#xff1a; 多功能维修工具集合概述 摘要&#xff1a; 本文档提供了一组多功能维修工具的概述&#xff0c;这些工具旨在为专业技术人员提供便利&#xff0c;以执行设备维修和软件解锁等任务。文档列出了各个工具的主要功能和应用场景。 关键词&#xff1…

【最新版】手把手Claude 3.5 Sonnet 详细步骤注册方法!

目录 01 Claude 3.5 Sonnet 是什么 02 Claude 3.5 Sonnet 注册方法 Step1&#xff1a;注册 Wildcard Step2&#xff1a;注册Claude3.5 Step3&#xff1a;接收并输入验证 03 使用Claude 3.5 Sonnet 04 升级Claude 3 Opus 05 结语 01 Claude 3.5 Sonnet 是什么 蛰伏了三…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【签名/验签介绍及算法规格】

签名/验签介绍及算法规格 为实现数据完整性保护和防抵赖&#xff0c;可使用生成/导入的密钥&#xff0c;对数据进行签名验签操作。 支持的算法 以下为密钥签名/验签支持的规格说明。 面向OpenHarmony的厂商适配密钥管理服务规格分为必选规格和可选规格。必选规格为所有厂商…

苍穹外卖--启用和禁用员工

实现 package com.sky.controller.admin;import com.sky.constant.JwtClaimsConstant; import com.sky.dto.EmployeeDTO; import com.sky.dto.EmployeeLoginDTO; import com.sky.dto.EmployeePageQueryDTO; import com.sky.entity.Employee; import com.sky.properties.JwtPro…

赛力斯25亿收购华为“问界”商标,估值102亿!

近日赛力斯发布公告&#xff0c;拟收购华为持有的商标及知识产权&#xff0c;华为其下属企业持有的九百一十九项商标权&#xff0c;其中七百四十项商标权已获授权&#xff0c;剩余商标权正在申请中&#xff0c;尚未获得商标注册证书&#xff0c;持有的商标及知识产权无形资产评…

Skywork-MoE,1460亿MoE模型,采用MoE Upcycling技术

Skywork-MoE&#xff0c;1460亿MoE模型&#xff0c;采用MoE Upcycling技术 原创 每日发现最新LLM 机器之心SOTA模型 2024年06月04日 18:27 北京 &#x1f3c6; 基座模型 ①项目名称&#xff1a;Skywork-MoE ★Skywork-MoE是一款千亿模型&#xff0c;具有1460亿参数、16个专家…