Cortex-M3内核_IAP升级讲解(包括代码)

Cortex-M3内核的STM32F1单片机启动方式

首先上几幅Cortex-M3内核的参考手册的图:
在这里插入图片描述
在这里插入图片描述
一般情况下我们用到的开发板,例如带一键下载功能的都是从系统存储器开始启动的。

  • 系统存储器是一块存储空间,他里面的内容就是Bootloader(程序)/启动程序,不管怎么理解反正它是一段程序即可,不同厂家生产的芯片BootLoader程序可能不同。这段Bootloader程序是芯片出厂时固化在系统存储区这块FLASH空间当中的。
  • 我们刚开始也许会有这样的疑问,为什么可以用串口烧录程序和用到CH340典型的USB转串口芯片呢?这是因为这段Bootloader程序本身其实就是一个串口1的功能,生产厂家留出PA9TX和PA10RX两根线可接收外界的程序(也就是烧录的hex/Bin文件)。他是固化在这段存储空间当中的,我们修改不了。
  • 举个例子,STM32留有BOOT1和BOOT0两个引脚,当我们上电时检测这两个引脚的电平而选择启动方式,当我们想要用串口工具烧录新的代码的时候,由硬件电路决定使BOOT1和BOOT0为0和1这样电平,此时系统存储区的Bootloader程序开始执行等待从串口1接收到数据(其实这一部分的时许图可以参考我上一篇文章ISP一键下载原理),当我们烧录完成数据也就是(Bootloader串口接收完成数据之后)通过C语言的指针绝对地址的访问而回到FLASH存储区的开头地址0x0800 0000开始执行第一条指令。由硬件电路决定,每次我们上电之后都可以从主闪存存储器也就是FLASH的0x0800 0000开始执行程序。
    在这里插入图片描述
    这也就是为什么这里配置为这样的,忙猜就是在编译的时候把该信息加入到你写的程序当中一块送给Bootloader,告诉Bootloader从0x0800 0000开始写入。
    总结一下:这种方式叫做ICP下载方式,包括后面的JTAG/SWD都属于ICP下载方式。

IAP概念(在程序中编程)

先来看一句话:“IAP方式需要至少有一部分程序已经使用ICP方式(上文固化的Bottloader启动方式)烧到FALSH当中(运行)”
其实IAP方式(他就是一段程序)也叫做Bootloader启动程序,这样的话除了厂家固化到固定地址不可修改的Bootloader程序外,又有了一个可以自己编写和可修改的Bootloader程序。说一下它的流程就明白什么意思了:

  • 现在我们通过Keil编程好了一段IAP启动程序,大概的功能就是配置串口,接收串口的消息存储到单片机的SRAM空间当中,然后往某个FLASH地址搬运串口接收的消息(其实就可以是新的应用程序)。
  • 我们通过固化的Bootloader程序也就是ICP方式把这个IAP启动程序烧录到FLASH0x0800 0000地址开头存储空间当中,之后程序开始运行等待外界通过串口给他发消息。其实这个时候,你写的这段IAP程序也叫做Bootloader程序。
  • 最后,当我们通过串口助手或者其他的软件工具把新的Keil程序(假如就是流水灯工程)的Bin二进制文件通过串口发送给单片机,这个时候我们不再从FLASH的开始地址0x0800 0000地址开始烧录,而是重新开辟一个地址,从这个新的地址开始烧录,假如就是0x0801 0000地址开始,完成这一功能的就如上面提到的Keil工程当中的配置,上一幅图,注意观察区别在哪里!
    在这里插入图片描述
    很简单,这个时候我们的FlASH当中就有两段应用程序了,一个从0x0800 0000开始的IAP(Bootloader)程序,一个就是从0x0801 0000开始的流水灯任务。
    至此,IAP是什么就应该明白了,之后我们可以 扩展为远程重新更新应用程序,比如通过ESP8266WIFI模块。

代码程序讲解(不好的地方请指正)

在这里插入图片描述
这个就是我们自己写的IAP方式Bootloader程序,其实就是一个串口1的配置而已是吧。
在这里插入图片描述
USART1_ISR也很简单,先查询一下RXNE(接收缓冲区非空标志位)是否真的被硬件置1了(当有数据向DR寄存器写入的时候,RXNE被硬件自动置1,如果开启了中断则可以响应中断),然后访问USART->DR寄存器读入到事先开辟好的数组当中,也很简单,这就是传说中高大上的Bootloader程序。

u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));

这里有一个attribute指令,接触过51的应该知道这其实就是个绝对地址访问。如下图:参考数据手册了解到SRAM的地址是从哪里开始的,因为你的IAP程序多多少少是要用到SRAM空间的,比如声明非const变量,所以我们偏移4kb存储空间。
在这里插入图片描述

if(key==KEY1_PRES)
{
	printf("开始执行FLASH用户代码!!\r\n");
	if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
	{	 
		iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
	}else 
	{
		printf("非FLASH应用程序,无法执行!\r\n");
	}									   
}

主要解释一下第三行程序是什么意思:

if( ((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000 )==0x08000000)
FLASH_APP1_ADDR=0x0801 0000
vu32就是volatileunsigned int类型的别名
(vu32*)(FLASH_APP1_ADDR+4)的意思就是强制类型转化为指针类型,也就是成为
了一个指针,这个指针指向0x0801 0004地址。
(*(vu32*)(FLASH_APP1_ADDR+4)*号访问该地址下的内存空间,再与上0xFF00 0000
其实就是判断一下该地址的数据是否是0x08开头的来简单的确定一下本次接收到的
数据是否正常,因为FLASH的存储空间就是从0x0800 0000开始的一共512kb(F103)
为什么地址为0x0801 0000还要加上4呢?下面参考数据手册:

在这里插入图片描述
其中0x0000 0000和IAP程序的起始地址0x0800 0000存放着相同的内容(数据手册表明的)

  • 我们从上文知道我们的流水灯任务是从0x0801 0000开始写入的,我们地址加上4之后开始执行代码。
  • 意思就是地址0x0801 0004下的内存单元存储着启动地址0x08XX XXXX开头的一个地址,这个地址才真正的指向我们的第一行程序(比如F1系列的启动中序当中的第一条汇编指令EQU开辟堆栈),然后开始执行。
  • 地址0x0801 0004下的内容可以是0x0801 1000地址也可以是0x0801 2000,这个是随便的(可能由编译原理决定的)。

因为我们的程序代码都在SRAM当中,所以我们要搬运到FLASH当中:
在这里插入图片描述

  • 第一行先判断SRAM当中的内容是否是FLASH规定的以0x08开头的数据,如果是则初步判断为接收的正确。
  • 第二行就是往固定地址也就是#define FLASH_APP1_ADDR 0x08010000开始写入数据,每次写1kb的。

最后就是iap_load_app( )程序理解:

//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
	if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)	//检查栈顶地址是否合法.
	{ 
		jump2app=(iapfun)*(vu32*)(appxaddr+4);		//用户代码区第二个字为程序开始地址(复位地址)		
		MSR_MSP(*(vu32*)appxaddr);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		jump2app();									//跳转到APP.
	}
}	
  • 第二行代码函数指针,这个就难了。我们知道地址+4之后开始执行程序。简单分析一下,先强制类型转化为指针,然后访问该地址下的内容(也是个地址)再强制类型转化为函数指针,赋给一个函数指针变量,这个时候这个函数指针变量就指向了汇编的第一条指令(其实是指向了汇编函数,没毛病吧),然后执行,开始了我们的流水灯任务。

最后,如果想看FLASH是如何写入的,可以看看原子、野火的视频,也可以参考一下数据手册,其实就是提高用户权限往某些地址写入数据应该。
最后的最后,我就给我的ucos版本尝试进行远程IAP升级。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值