3065 views|4 replies

98

Posts

0

Resources
The OP
 

【CH579M-R1】+OTA principle and source code analysis [Copy link]

 

Principle Analysis

First, let's talk about the program image. A complete program image can be divided into two parts: "interrupt vector table" and "logic program" (let's name it this way for the convenience of explanation).

The "interrupt vector table" occupies the first 512 bytes, which is one page of Flash, and the rest is the "logic program".

As pointed out in the previous article , CH579 divides the flash into two areas, each area occupies 125K Flash (0x1F400), which are used to store ImageA and ImageB respectively.

Now we will find a problem, because the startup address selected by the chip is fixed (the starting address of the Flash). If the Flash is arranged as above, the chip will always only run ImageA and not ImageB.

Therefore, when upgrading ImageB, the program will move the "interrupt vector table" of ImageB to the first 512 bytes of Flash, as shown in the following figure.

In this way, ImageB can run. However, there is a conflict with what I said in the previous article: "Even if the upgrade fails, the old image is still available." That is, when upgrading ImageB, the "interrupt vector table" of ImageA is overwritten and permanently lost. If it is found that ImageB has defects and cannot run at this time, the device will be "bricked." I think the official routine is only for demonstration, so it does not provide a complete solution.

Source code analysis

The device creates an OTA SERVICE, under which there is a CHARACTER with UUID 0xFEE1, which is the Bluetooth Profile used for OTA communication. We do not have the source code of the upgraded APP here, but by analyzing the code of CH579, we can infer the communication process between APP and CH579:

First, the APP writes relevant commands (erase, program, get information, etc.) to the CHARACTER of 0xFEE1. Then, after CH579 parses the command, it writes the return result to the data cache of the CHARACTER of 0xFEE1. Then, the APP reads the data of 0xFEE1 through polling to obtain the return result, thus constituting a round-trip communication.

Let's take a look at the CH579 communication parsing code: Rec_OTA_IAP_DataDeal.

The entire OTA process is mainly divided into three steps: erasing, programming and verification.

First, erase:

		case CMD_IAP_ERASE:
		{
			OpAdd = (UINT32)(iap_rec_data.erase.addr[0]);
			OpAdd |= ((UINT32)(iap_rec_data.erase.addr[1]) << 8);
			OpAdd = OpAdd * 4;

			EraseBlockNum = (UINT32)(iap_rec_data.erase.block_num[0]);
			EraseBlockNum |= ((UINT32)(iap_rec_data.erase.block_num[1]) << 8);
			EraseAdd = OpAdd;
			EraseBlockCnt = 0;
			
			/* 检验就放在擦除里清0 */
			VerifyStatus = 0;
			
			PRINT("IAP_ERASE start:%08x num:%d\r\n",(int)OpAdd,(int)EraseBlockNum);

			/* 当前是ImageB的话需要保留ImageA地址第一块 */
			if( CurrImageFlag == IMAGE_B_FLAG )
			{
				EraseBlockNum -= 1;
				EraseAdd += FLASH_BLOCK_SIZE;
			}
			
			/* 启动擦除 */
			tmos_set_event( Peripheral_TaskID, OTA_FLASH_ERASE_EVT );
			break;
		}

This step mainly records the erase address (EraseAdd) and erase size (EraseBlockNum), and then starts the erase event (OTA_FLASH_ERASE_EVT).

Then comes programming:

		case CMD_IAP_PROM:
		{
			UINT32 i;
			UINT8 status;
			
			OpParaDataLen = iap_rec_data.program.len;   
			
			OpAdd = (UINT32)(iap_rec_data.program.addr[0]);
			OpAdd |= ((UINT32)(iap_rec_data.program.addr[1]) << 8);
			OpAdd = OpAdd * 4;

			PRINT("IAP_PROM: %08x len:%d \r\n",(int)OpAdd,(int)OpParaDataLen);

			for(i=0; i<(OpParaDataLen/4); i++)
			{
				OpParaData[i] = (UINT32)(iap_rec_data.program.buf[0+4*i]);
				OpParaData[i] |= ((UINT32)(iap_rec_data.program.buf[1+4*i]) << 8);
				OpParaData[i] |= ((UINT32)(iap_rec_data.program.buf[2+4*i]) << 16);
				OpParaData[i] |= ((UINT32)(iap_rec_data.program.buf[3+4*i]) << 24);
			}
			
			/* 当前是ImageA,直接编程 */
			if(CurrImageFlag == IMAGE_A_FLAG)
			{
				status = FlashWriteBuf(OpAdd, OpParaData, (UINT16) OpParaDataLen);				
			}
			/* 当前是ImageB,除了第一块直接编程 */
			else
			{
				/* 第一块内容 */
				if((OpAdd + OpParaDataLen) <= FLASH_BLOCK_SIZE)
				{
					for(i = 0; i < OpParaDataLen; i++)
					{
						vectors_block_buf[OpAdd + i] = iap_rec_data.program.buf[i];
					}
					status = SUCCESS;
					OTA_IAP_SendCMDDealSta(status);
				}
				/* 其他块 */
				else
				{
					status = FlashWriteBuf(OpAdd, (PUINT32)OpParaData, (UINT16) OpParaDataLen);
					OTA_IAP_SendCMDDealSta(status);
				}
			}
			
			break;
		}

This process is to write the program sent by APP directly into Flash. It is worth noting that if the current execution is ImageB, in other words, the newly upgraded program is ImageA, then we need to temporarily save its first block of content (512 bytes of interrupt vector table) (vectors_block_buf) instead of directly writing the first 512 bytes of Flash, otherwise the current program will run away directly if the interrupt is not turned off.

Finally, the verification:

		case CMD_IAP_VERIFY:
		{
			UINT32 i;
			UINT8 *p_flash;
			UINT8 status = 0;
			
			OpParaDataLen = iap_rec_data.verify.len;
			
			OpAdd = (UINT32)(iap_rec_data.verify.addr[0]);
			OpAdd |= ((UINT32)(iap_rec_data.verify.addr[1]) << 8);
			OpAdd = OpAdd * 4;
			
			PRINT("IAP_VERIFY: %08x len:%d \r\n",(int)OpAdd,(int)OpParaDataLen);
			
			p_flash = (UINT8 *)OpAdd;
			
			/* 当前是ImageA,直接读取ImageB校验 */
			if(CurrImageFlag == IMAGE_A_FLAG)
			{
				for(i=0; i<OpParaDataLen; i++)
				{
					if(p_flash[i] != iap_rec_data.verify.buf[i]) break;
				}
				if(i == OpParaDataLen) status = SUCCESS;
				else                   status = 0xff;
				VerifyStatus |= status;
				OTA_IAP_SendCMDDealSta(VerifyStatus);
			}
			/* 当前是ImageB,第一块和buf里对比,其他直接和ImageA校验 */
			else
			{
				/* 第一块和RAM保存的参数对比 */
				if( (OpAdd + OpParaDataLen) <= FLASH_BLOCK_SIZE )
				{
					for(i=0; i<OpParaDataLen; i++)
					{
						if(vectors_block_buf[OpAdd+i] != iap_rec_data.verify.buf[i])break;
					}
					if(i == OpParaDataLen) status = SUCCESS;
					else                   status = 0xff;
				}
				/* 其他和Flash里对比 */
				else
				{
					for(i=0; i<OpParaDataLen; i++)
					{
						if(p_flash[i] != iap_rec_data.verify.buf[i]) break;
					}
					if(i == OpParaDataLen) status = SUCCESS;
					else                   status = 0xff;
				}
				VerifyStatus |= status;
				OTA_IAP_SendCMDDealSta(VerifyStatus);
			}
			break;
		}

That’s pretty much the general analysis.

I have said so much, but I just want to clarify.

Latest reply

Thanks   Details Published on 2021-3-28 15:04

赞赏

1

查看全部赞赏

 
 

1942

Posts

2

Resources
2
 

Not bad, thanks for sharing!

 
 
 

11

Posts

0

Resources
3
 

Hello, I would like to ask, if I run the upgrade program in ImageA to upgrade ImageB, and if the first thing to do during the upgrade is to overwrite the interrupt vector table before ImageA with the interrupt vector table of ImageB, will the current ImageA still run normally? ? I don't quite understand this!

Comments

[attachimg]530134[/attachimg] You are right in thinking that it cannot run normally.   Details Published on 2021-3-25 22:06
 
 
 

98

Posts

0

Resources
4
 
Floating Dust on the Water published on 2021-3-25 16:08 Hello, I would like to ask, if you run the upgrade program in ImageA to upgrade ImageB, then when upgrading, the first thing to do is to reset the interrupt before ImageA to...

You're right, it doesn't work properly.

Comments

Thanks  Details Published on 2021-3-28 15:04
 
 
 

11

Posts

0

Resources
5
 
Ansersion posted on 2021-3-25 22:06 You are right, it really can't run normally.

Thanks

 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list