【IAP】篇三 —— 开发属于自己的下载器 附HAL库代码

目录

文章目录

前言

一、固件升级逻辑

1、下载器数据包发送逻辑

2、下载器代码

3、接收端逻辑

4、接收端代码

二、使用步骤

总结


前言

        前两篇介绍了IAP的实现原理和数据包的传输,本篇为本系列的最后一篇,编写自己的下载器将固件升级包发送出去更新数据。


一、固件升级逻辑

        主要的逻辑就是下载器将自己FLASH中的新的固件通过自定义的协议发送给设备,设备收到新的固件就下载到FLASH中完成更新。我的自定义的协议在  【IAP】篇二 —— 自定义数据包传输协议实现固件升级 中有介绍。

1、下载器数据包发送逻辑

        简化版的逻辑就是这样的,因为我有两个固件需要一起完成升级,所以代码会复杂一点

2、下载器代码

        主循环代码,延时1S是为了支持热插拔,等待连接稳定再开始数据包传输。

    while(1)
	{
		//循环判断APP是否在线
		OTA_Send_Package(NULL,0,0x91);
		if(OTA_Wait_Ack(10) == 1)
		{
			//如果APP在线就判断控制端在不在线
			//给1s判断
			for(int i = 0 ; i < 50; i++)
			{
				OTA_Send_Package(NULL,0,0x92);
				//如果有回应就发送固件升级包1然后发2
				if(OTA_Wait_Ack(20) == 1)
				{
					MU_Show_Str(0,56,(uint8_t *)"                                ",WHITE,DARK_BLUE);
					MU_Show_Str(8,56,(uint8_t *)"即将开启传输[     ]",WHITE,DARK_BLUE);
					char str[5];
					//延时1S等待稳定
					for(int i = 100; i > 0; i--)
					{
						sprintf(str,"%1d.%2d",i/100,i%100);
						MU_Show_Str(116,56,(uint8_t *)str,YELLOW,DARK_BLUE);
						HAL_Delay(10);
					}
					OTA_Send_Package1();
				}
			}
			//发送APP升级包
			OTA_Send_Package2();
		}
	}   

        发送数据包代码,发送包一和包二都差不多,这里以包二为例。

        先是一些无关紧要的显示,然后发送384*512字节大小的数据,即192K大小的固件升级包,这里的192K是固件的最大大小,我偷懒没有加检测固件包大小的逻辑。后续优化可以检测一下固件包大小能少发许多字节。

static void OTA_Send_Package2(void)
{
	MU_Show_Str(0,0,(uint8_t *)"                                ",WHITE,DARK_BLUE);
	MU_Show_Str(40,0,(uint8_t *)"APP数据包",WHITE,DARK_BLUE);
	int i;
	char str[5];
	//延时2S等待稳定
	uint8_t data[512] = {0};
	MU_Show_Str(0,56,(uint8_t *)"                                ",WHITE,DARK_BLUE);
	MU_Show_Str(8,56,(uint8_t *)"数据包传输中[     ]",WHITE,DARK_BLUE);
	//192K
	for(i = 0; i < 384; i++)
	{
		sprintf(str,"%3d%%",i*100/384+1);
		MU_Show_Str(116,56,(uint8_t *)str,YELLOW,DARK_BLUE);
		FLASH_Read(APP1_ADDR + 512*i,data,512);
		OTA_Send_Package(data,512,0x91);
		if(OTA_Wait_Ack(1000) == 0)
			break;
	}
	MU_Show_Str(0,56,(uint8_t *)"                                ",WHITE,DARK_BLUE);
	//发送失败
	if(i != 384)
	{
		MU_Show_Str(12,56,(uint8_t *)"数据包传输失败...",WHITE,DARK_BLUE);
	}
	//发送完毕
	else
	{
		MU_Show_Str(12,56,(uint8_t *)"数据包传输成功...",WHITE,DARK_BLUE);
	}
	OTA_Send_End();
}

        下载器的代码就这些了,比较简单,然后看看接收端是怎么处理的。

3、接收端逻辑

        首先先明确一下两个固件升级包的整体逻辑,下载器将两个固件升级包传给APP,APP判断是不是自己的升级包,如果是就升级,不是就转发给控制端。控制端就只需要接收升级包并处理即可。

        下面是接收端的处理逻辑,复杂的地方主要是BootLoader和主程序之间的跳转。

4、接收端代码

        下面是BootLoaer的代码,对应上面的逻辑。tim4_nms用于超时检测。

	//判断是否更新完成
	if(IAP_UpdateFlag_Read(NULL) == APP_UPDATEFLAG)
	{
		//清更新标志位
		IAP_UpdateFlag_Clear();
		//跳转到APP
		IapLoadApp(APP1_ADDR);
	}
	//进行一次更新判断再进入APP
	else
	{
		//超时检测
		MU_Tim4_Init();
		//RS485
		MU_DMX_Init();
		//灯
		MU_Led_Config();
		//内部
		MU_InnerCmd_Init();
		
		tim4_nms = 5000;
		//40ms内判断是否有升级包传来
		for(int i = 0; i < 40; i++)
		{
			//进入更新
			if(ota_package_ne == 1)
			{
				//循环处理直到收到结束包
				while(OTA_Package_Prase() != 0x00);
			}
			HAL_Delay(1);
		}
		//去初始化
		TIM_HandleTypeDef htim4;
		htim4.Instance = TIM4;
		HAL_TIM_Base_DeInit(&htim4);
		HAL_UART_DeInit(&huart1);
		HAL_UART_DeInit(&huart2);
		HAL_DMA_DeInit(&hdma_usart1_rx);
		HAL_DMA_DeInit(&hdma_usart2_tx);
		HAL_DMA_DeInit(&hdma_usart2_rx);
	}
	//跳转主程序
	IapLoadApp(APP1_ADDR);

        主要看看接收数据包的解包逻辑,注释很全,可供参考。

//OTA解包
static uint64_t app_buf_page = 0;
uint8_t ota_type = 0;
uint8_t OTA_Package_Prase(void)
{
    if(ota_package_ne == 1 && tim4_nms != 0)
    {
        //重新设置超时时间
        tim4_nms = 5000;
        ota_package_ne = 0;
        int i = 1;
		uint16_t data_len = 0;
		uint32_t sum = 0;
        switch(ota_package_buf[0])
        {
#ifdef OTA_RECV
            //APP端升级包
            case 0x91:
                ota_type = 0;
                //校验
                data_len = ota_package_buf[1];
                data_len <<= 8;
                data_len |= ota_package_buf[2];
                //开始传输OTA
                if(data_len == 0)
                {
                    OTA_Send_Ack();
					return 0x91;
                }
                for(i = 0; i < data_len+3; i++)
                {
                    sum += ota_package_buf[i];
                }
                //校验错误
				//& 优先级小于 != 等于 ||
                if(((sum>>8)&0xFF) != ota_package_buf[data_len+3] || (sum&0xFF) != ota_package_buf[data_len+4])
				{
					LED2_ON;
                    return 0x00;
				}
                //写入数据到FLASH
                IAP_WriteBin(APP1_ADDR+app_buf_page,ota_package_buf+3,data_len);
                app_buf_page += data_len;
                //回应
                OTA_Send_Ack();
				LED1_TOGGLE;
            return 0x91;
            //电机控制端升级包
            case 0x92:
                ota_type = 1;
                data_len = ota_package_buf[1];
                data_len <<= 8;
                data_len |= ota_package_buf[2];
				

                for(i = 0; i < data_len+3; i++)
                {
                    sum += ota_package_buf[i];
                }
                //校验错误
				//& 优先级小于 != 等于 ||
                if(((sum>>8)&0xFF) != ota_package_buf[data_len+3] || (sum&0xFF) != ota_package_buf[data_len+4])
				{
					LED2_ON;
                    return 0x00;
				}
				//发送升级包给电机控制端
				InnerCmd_OTA_Send(ota_package_buf+3,data_len);
				//等待20ms
				if(data_len == 0)
				{
					//等待电机控制端回应
					if(InnerCmd_Ack_Wait(20) == 1)
					{
						//回应ACK给下载器
						OTA_Send_Ack();
					}
				}
				//等待1s
				else
				{
					//等待电机控制端回应
					if(InnerCmd_Ack_Wait(1000) == 1)
					{
						//回应ACK给下载器
						OTA_Send_Ack();
					}
				}
				LED2_TOGGLE;
            return 0x92;
            case 0xFF:
                for(i = 1; i < 6; i++)
                {
                    if(ota_package_buf[i] != 0xFF)
                        break;
                }
                //收到结束包
                if(i == 6)
                {
                    //本设备升级包
                    if(ota_type == 0)
                    {
                        //更新结束,复位
                        app_buf_page = 0;
                        //设置更新完成标志位
                        IAP_UpdateFlag_Write(0xFFFF);
                        //软件复位
                        __HAL_RCC_CLEAR_RESET_FLAGS();
                        HAL_NVIC_SystemReset();
                    }
                    //控制端升级
                    else if(ota_type == 1)
                    {
                        //转发
                        InnerCmd_Send_End();
                    }
                    
                }
            return 0xFF;
#endif
#ifdef OTA_SEND
            case 0xAA:
                for(i = 1; i < 6; i++)
                {
                    if(ota_package_buf[i] != 0xAA)
                        break;
                }
                if(i == 6)
                    ota_ack = 1;
            return 0xAA;
#endif
        }
    }
    //超时
    if(tim4_nms == 0)
        return 0x00;
    else
        return 0x01;
}

        控制端的接收逻辑就更简单了,不用进行数据包转发,上面的看懂就能写出控制端的升级逻辑。

二、使用步骤

        首先需要将更新的固件下载到下载器的指定内存地址中。可以看【IAP】篇一 —— FLASH内存划分 & Target设置 附Bootloader的Target设置,设置完成后可以双击打开.map文件

        然后往下滑看看偏移地址是否正确,正确就可以下载到下载器中了。

        需要注意下载器的FLASH内存划分要和下载的地址一致

#define BOOTLOADER_ADDR     0x08000000      //64K       BLOCK0~BLOCK1
#define APP1_ADDR           0x08010000      //192K      BLOCK2~BLOCK7
#define APP2_ADDR           0x08040000      //96K       BLOCK8~BLOCK10

        最后将下载器和设备连接,就可以更新固件了 

固件升级


总结

        加油少年,完成一个属于自己的下载器吧!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值