STM32简单实现IAP功能

以STM32F103为基础。


实现的简单思路:

IAP实际就是自己构建两个程序。将程序分割成两部分保存下来。一部分实现bootloader的功能,一部分是实际程序。


BootLoader部分实现:

其所有配置跟正常程序一致,不需要额外设置。功能方面只需要判断是否需要更新固件,如果不需要则直接跳转运行,如果需要就等待数据发送过来,发送完成后将其保存,最后跳转运行。

//判断持续按下3秒。进入更新固件的状态下。
uint8_t count = 0;
while(WKUP() == 1)//按键按下
{
	delay_ms(500);
	if((++count) >= 6) 
	{	//3s
		printf("进入IAP更新模式,请发送相关bin文件......");
		Mode = MYIAP;
		break;
	}
}

以常用的串口方式接收数据,此时定义一个大数组,用于保存数据,且指定起始地址,方便后面使用。

#define RX_BuffSize	1024*40//最大数据量 即能够更新的最大程序
uint32_t rxlength = 0;//记录数据长度
uint8_t rx_table[RX_BuffSize] __attribute__ ((at(0x20001000)));   //0x20000000到0x20001000是烧录算法 所以采用0x20001000为起始地址
//0x20000000为SRAM的起始地址,接受的临时数据都存储在SRAM中
void Bsp_Receiverdata(UART_DEF *Uart)   //接收数据函数
{
	uint8_t data = USART_ReceiveData(Uart->USARTx);//串口接收
	rx_table[rxlength] = data;//转存
	rxlength++;//数据长度
}

接收完数据后,将数据保存到指定的地址,方便用于使用。此处将数据保存在flash中,且由于本人使用的STM32F103一页为2K,所以一次存入2K的数据,方便计算。但是在保存数据前,可以简单的判断一下数据是否正确。

//判断是否为0x08xxxxxx
//0X20001000为前面指定的数据地址,但是第一个数据是栈顶地址第二个地址才是flash地址
if(((*(uint32_t*)(0X20001000 + 4)) & 0xff000000) == 0x08000000)
//addr:写入的起始地址 注:此地址要与固件地址一致,以及跳转运行时的地址一致
//iapbuff:保存的数据
//iaplength:数据长度
bool Iap_WriteFlashFunction(uint32_t addr,uint8_t *iapbuff,uint32_t iaplength)
{
	bool flag = false;
	uint32_t count = 0,temp = 0;
	uint32_t writebuff[512];//2K内容 4*512
	while(iaplength)
	{
		//发送来的数据是bin文件,其数据是小端模式,所以在此处转化为实际数据
		temp = (uint32_t)iapbuff[3] << 24;
		temp |= (uint32_t)iapbuff[2] << 16;
		temp |= (uint32_t)iapbuff[1] << 8;
		temp |= (uint32_t)iapbuff[0];
		iapbuff += 4;//地址偏移
		iaplength -= 4;//剩余数据长度
		writebuff[count++] = temp;//转存
		if(count == 512)//满足一页内容后去保存
		{
			count = 0;//清零
			flag = Flash_Write(addr,writebuff,512);//2K内容一次存入 一页为2K
			if(!flag)
			{
				return flag;
			}
			addr += 2048;
		}
	}
	if(count != 0)///剩余不足2K
	{
		flag = Flash_Write(addr,writebuff,count);
	}
	return flag;
}	

保存数据后,可以通过简单的操作跳转运行固件程序,在此之前则可以简单判断一下固件是否争取。

//FLASH_APP_BASE_ADDR为写入flash的起始地址,其指向栈顶地址
//其本质与前面保存数据前的判定一个道理
if(((*(uint32_t*)(FLASH_APP_BASE_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0x08xxxxxx
//不支持汇编 内联
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
	MSR MSP, r0 			//set Main Stack value
	BX r14
}

//跳转到用户程序运行
//addr:用户程序的起始地址,也是前面保存数据时的地址
void Iap_Load_App(uint32_t addr)
{
	if(((*(uint32_t *)addr) & 0x2FFE0000) == 0x20000000)//STM32程序起始地址处存的是栈顶地址 0x2FFE0000是由于RAM最大为0x20010000
	{
		
		MSR_MSP(*(uint32_t*)(addr));//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		void (*function)(void);//定义一个函数指针
		function = (void (*)(void))*(uint32_t*)(addr + 4);//用户代码区第二个字为程序开始地址(复位地址)		
		function();
		
	}
}
//function = (void (*)(void))*(uint32_t*)(addr + 4);
//function 为函数指针
//(void (*)(void))用于强转
//(uint32_t*)(addr + 4)表示一个地址
//*(uint32_t*)(addr + 4)其地址对应的值(由于存的是bin文件,所以其值本质也是一个地址,所以赋值给函数指针)

指针问题可以参考这里


用于更新的固件程序:

对于固件程序,跟其他正常程序基本一致,区别在于KEIL的配置,以及改变基地址。

//在main函数第一行加入此地址偏移
//偏移的0x10000是用于固件程序从此处开始
SCB->VTOR = FLASH_BASE | 0x10000;

在魔术棒Target中设置IROM1为0x8010000,此处0x8010000就是前面BootLoader中提到的写入地址,也是基础地址偏移0x10000的原因。此0x10000是为BootLoader程序预留的64K的内存。

由于发送的是bin文件,所以需要使用MDK生成.bin文件。即在魔术棒User中设置。

在这里插入图片描述
红框内容为

//此为一个相对路径
//iap.bin为文件名
//Objects\iap.bin为在当前工程路径下的Objects生成iap.bin
//如果需要上一层文件可以改为..\BIN\iap.bin:此为在工程路径的上一层的BIN文件夹中生成iap.bin
//文件路径操作大致如Linux
$K\ARM\ARMCC\bin\fromelf.exe --bin --output=Objects\iap.bin !L

如此编译后将会生成.bin文件。通过按键的方式让它处于固件更新的状态下,将.bin文件通过串口助手发送即可完成更新。


具体demo下载点击这里

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值