关于STM32单片机IAP升级中if(((*(__IO uint32_t*)ulAddr_App) & 0x2FFE0000) == 0x20000000)语句的理解

初见if((((__IO uint32_t)ulAddr_App) & 0x2FFE0000) == 0x20000000)

在STM32单片机IAP升级程序中常见的一个语句:

if(((*(__IO uint32_t*)ulAddr_App) & 0x2FFE0000) == 0x20000000)

传说(因为搜索多方资料都是这样写)这是一个判断检查栈顶地址是否合法的语句,新人刚接触可能会有点懵。这一个套一个的*是干嘛呀,为什么要跟0x2FFE0000相与又为什么等于0x20000000,下面记录一下个人理解,给新学习这方面的朋友一点不一样的资料,也为避免自己再踩同样的坑。

语句理解

(__IO uint32_t*)ulAddr_App意思是把ulAddr_App强转成一个地址,(__IO uint32_t)ulAddr_App就是取地址ulAddr_App里的数据

语句功能

那么为什么要用ulAddr_App里的数据跟0x2FFE0000相与呢,因为ulAddr_App指向的数据是RAM地址,而0x20000000是RAM的首地址,假如你的STM32单片机RAM有128K,那他RAM的末地址就是0x2001FFFF。所以与0x2FFE0000相与就是确保ulAddr_App指向的数据在0x20000000~0x2001FFFF之间(其实用大于小于程序会更清晰吧?),也就是栈顶指针要在这个范围。下图是STM32F103ZE的RAM地址及大小:在这里插入图片描述
这里可能很多同学会问,为什么你的单片机RAM只有10000(64K)大小,而你的代码却判定20000(128K)大小呢。这是因为这个代码不是我写的,是我在一个付费课程获得的,然后我在网上搜索资料时发现很多不管你是128K还是64K还是其它的,统一都用了0x2FFE0000相与(很坑有木有-_-),其实64K这里用0x2FFF0000做与运算就可以了。

为什么ulAddr_App里存的是RAM地址呢

上面搞定了语句的意思跟功能后,接下来的问题就是:为什么ulAddr_App里存的是RAM地址呢?
这里ulAddr_App是单片机固件存储在flash的首地址,所以上面的问题就相当于:为什么ROM首地址存的是RAM地址。
为弄清这个问题搞了我一天时间,网上各种重复资料,还很多点到即止。正当我烦恼的时候,突然看到一个帖子,里面有这么一段话:在这里插入图片描述
好家伙,这个MSP是什么,它跟0x8000000又有什么关系,为了弄清这个问题,我查阅了《ARM Cortex-M3与Cortex-M4权威指南》,在7.5节有这么一个图:在这里插入图片描述
网上查阅资料后发现,这里0x00000000就是对应STM32单片机的0x8000000,所以这时候我们只有找出MSP的初始值是什么就能真相大白了。然后我又发现了下面的资料:在这里插入图片描述
在这里插入图片描述
这两个图片什么意思呢,其实就是说MSP的初始值是程序占用RAM的大小,按地址理解就是MSP的初始值是程序在RAM中的末地址+1。为了加强理解,我改了下代码做验证,代码如下:

	if(((*(__IO uint32_t*)ulAddr_App) & 0x2FFE0000) == 0x20000000)	  
	{
		printf("%X\n",*(__IO uint32_t*)ulAddr_App);		
			
//		printf("栈顶合法,运行APP\r\n");
//		pJump2App = (pIapFun_TypeDef) *(__IO uint32_t *)(ulAddr_App + 4);	//用户代码区第二个字为程序开始地址(复位地址)
//		MSR_MSP(*(__IO uint32_t *)ulAddr_App );					                  //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
//		pJump2App ();								                                    	//跳转到APP
	}

下载固件后可以看到打印信息:在这里插入图片描述
再看看固件的情况:在这里插入图片描述
120+1152换算成16进制就是4F8,验证正确。

结论

经过一番折腾,前面的问题就可以得到结论:程序存储在flash的首地址指向的是MSP的初始值,而这个初始值是程序占用RAM的大小,也是程序在RAM中的末地址+1。

最后一点小折腾

还有一点不太理解的就是,为什么MSP存的初始值是从0x20000000开始的算的(上面验算程序只是接收了固件并存放在flash,并还没重定向栈指针),不是应该算上boot程序占用的RAM而从0x20000000+N开始吗,我个人理解是这个MSP是固件的MSP,是存放在0x8000000+N的MSP,所以这个MSP是在固件中独立计算的,就是发送前已经从0x20000000开始算好了,跟boot的MSP是无关的。限于时间跟需求,这个问题就先到这里了,以后有机会再来验证。

参考

1.https://www.amobbs.com/thread-5761200-1-1.html
2.https://www.cnblogs.com/DF11G/p/9722778.html
3.https://blog.csdn.net/lushoumin/article/details/78641104
4.https://blog.csdn.net/weixin_42231514/article/details/106178653

  • 70
    点赞
  • 202
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
引用给出了一个USB的CDC接收的代码片段,其调用了解析接口PacketParse来解析传输的数据包。同时还调用了一些USB设备的相关函数来设置接收缓冲区和接收数据包。这段代码主要是处理USB接收数据的逻辑。引用提到了一个完整的传输流程,其涉及到了ymodem通信实现和一些主要的接口函数,如PacketParse用于解析CDC包,YmodemHandshakeCb用于在建立连接前定期发送'C',YmodemPacketHandle用于处理ymodem包。引用给出了一个github上的stm32f4_SerialPort_bootloader项目的ymodem.c文件,可能是一个相关的实现。 根据问题提供的函数签名COM_StatusTypeDef Ymodem_Transmit (uint8_t *p_buf, const uint8_t *p_file_name, uint32_t file_size),这个函数的作用可能是用于在Ymodem传输发送数据。具体的实现细节需要查看函数的具体实现。但是可以推测,该函数可能会将指定的缓冲区的数据通过Ymodem协议进行传输。 需要注意的是,由于代码片段不完整,无法给出完整的函数实现和详细的步骤说明。如果需要更详细的信息,建议查看引用给出的github项目或者进一步阅读相关的文档和资料。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [STM32使用USB虚拟串口+YMODEM实现IAP升级](https://blog.csdn.net/victor_zy/article/details/124338566)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [STM32基于YModem协议串口升级程序的实现](https://blog.csdn.net/lbaihao/article/details/124024242)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值