STM32F103C8T6 CAN总线IAP(CANPro上位机发送bin文件程序升级)

一、程序升级流程

1、MCU 的APP程序区接收到上位机“升级指令”,(上电直接从BL区启动)

2、MCU 跳转到BL程序区,应答上位机,“是否要升级”,一直发送

3、上位机应答“需要升级”,下发给MCU

4、MCU若果200ms没有收到“需要升级”,跳转到APP中

5、MCU若果200ms收到“需要升级”,应答上位机“开始升级”

6、上位机收到后,发送bin文件

7、MCU接收bin文件,存储到Flash,

8、MCU接收过程中,如果存在接收失败,返回上位机“接收失败”

9、上位机收到“接收失败”,重新开始发送bin文件

二、代码和参数设置

1、keil中 bin文件自动生成

    $K\ARM\ARMCC\bin\fromelf.exe --bin --output=Bin\@L.bin !L

2、APP区启动程序地址设定

APP程序启动的地址:0x04000

3、BL程序区跳转地址设置 

4、跳转代码

5、CANPro上位机发送bin文件

三、程序调试中注意细节

1、由于stm32f103c8t6,RAM只有20k,所以采用一边接收,一边写入Flash,CAN总线接收中断服务程序中完成

2、flash擦写规则

   

  • 最小擦除单位:扇区

  • 可选择擦除单位:扇区、块、全片

  • 最大编程(写入)单位:页( 256 Byte),大于256 Byte则需要循环写入。

  • 最小编程(写入)单位:1 Byte,即一次可写入 1~256 Byte的任意长度字节。

  • 未写入时FLASH里面的数据为全1,即0xFF。

  • 只能由 1 —> 0 写入,不能由 0 —> 1 写入,即如果已经写入过了,则需要先擦除(擦除后数据变为全1)再写入。

  • 示例:0xF0(1111 0000),即高4位可写入,低4位不可写入。

3、由于flash擦除 需要时间,所以数据发送间隔20-40ms。时间太短否则容易出现擦写超时

4、BL程序区,跳转前关闭中断;在APP程序区要允许中断打开

5、bin文件大小 : Code+RO-data+RW-data

      Program Size: Code=15128 RO-data=268 RW-data=164 ZI-data=2140  

四、代码部分

1、BL程序主函数:

int main(void)
{		
	
	u8 key;

	
	
	delay_init();	   	 	//延时初始化 
 	LED_Init();		  			//初始化与LED连接的硬件接口
	KEY_Init();					//初始化按键
	InitCAN_GPIO();
	CAN_Config(); 
	USART_RX_CNT = 0;
	uIAP.Step = 0;
	uIAP.Wait=0;
	uIAP.addr_off=0;
	uIAP.FlashWriteOK = 0;
 
     BootLoadSendMsg();
	
	while(1)
	{
		
		delay_ms(200);
	    GPIOC->ODR ^= GPIO_Pin_13;  //程序升级指示灯
		key=KEY_Scan(0);

	  
		switch (uIAP.Step)
		{
			case 0:      // *1*收到上位机 IAP 指令,准备升级    *2*没有收到IAP指令直接跳转
				
			  if(++uIAP.Wait >= 5*4) //等待3秒自动进入APP
				{
					
					uIAP.Step = 4;
				}	
				if(key==KEY0_PRES) //testing  第一次按   BL区前3秒  准备接受bin文件
                {
					uIAP.Step = 1;
					uIAP.Wait = 0;
				}					
				else if(uIAP.PC_Request == 1) // 按键或者 上位机请求信号
				{
					 uIAP.Step = 1;
					 uIAP.Wait = 0;
				}
       
				break;
		
			case 1:   //bin文件接收处理   flash处理完立刻启动
				if(++uIAP.Wait >= 5*180) //等待180秒自动进入APP
				{
					 uIAP.Step = 4;
				}
				else if(uIAP.FlashWriteOK ==1) // 收到上位机 发送完毕的信号
				{
					uIAP.Step = 4;
				}	
				
        
				break;
			
			case 4:        
					uIAP.Wait = 0;
				//if(key==KEY0_PRES)
					{
						uIAP.addr_off=0;
						uIAP.FlashWriteOK = 0;
						USART_RX_CNT = 0;
						if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
						{	 
						//	printf("执行FLASH APP代码!\r\n"); 
							
							iap_load_app(FLASH_APP1_ADDR);//关闭中断   执行FLASH APP代码
							
						}
						else 
						{
						//	printf("非FLASH应用程序,无法执行!\r\n");
						  uIAP.Step = 0;
						}	
					}			
				break;
			default:
				break;
		}
		
   
	    	   
 }

}

   2、  CAN接收中断和写入flash

void GetCanData(CanRxMsg * rxMsg)
{
	uint8_t 		length	= 0,i=0;

 

	switch (rxMsg->StdId) //判断帧标识符
		{
		case 0xB1: 
			;
			break;
    case 0xB2: 
			;
			break;
		case 0xBD:   //bin文件解析
			length = rxMsg->DLC;
		 
		 
			USART_RX_BUF[USART_RX_CNT++] = rxMsg->Data[0];
			USART_RX_BUF[USART_RX_CNT++] = rxMsg->Data[1];
			USART_RX_BUF[USART_RX_CNT++] = rxMsg->Data[2];
			USART_RX_BUF[USART_RX_CNT++] = rxMsg->Data[3];
			USART_RX_BUF[USART_RX_CNT++] = rxMsg->Data[4];
			USART_RX_BUF[USART_RX_CNT++] = rxMsg->Data[5];
			USART_RX_BUF[USART_RX_CNT++] = rxMsg->Data[6];
			USART_RX_BUF[USART_RX_CNT++] = rxMsg->Data[7];
			
		
			  
		   iap_write_appbin(FLASH_APP1_ADDR+uIAP.addr_off,USART_RX_BUF,length);//更新FLASH代码
				uIAP.addr_off+=USART_RX_CNT;
				if(USART_RX_CNT%8==0)  
				USART_RX_CNT=0;
			
		  
			break;
		default:
			break;
		}
}


void USB_LP_CAN1_RX0_IRQHandler(void)
{

	if (CAN_MessagePending(CAN1, CAN_FIFO0) != 0)
		{
		CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
	
		GetCanData(&RxMessage);
		} 
}

3、写flash程序

void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
	u16 t;
	u16 i=0;
	u16 temp;
	u32 fwaddr=appxaddr;//当前写入的地址
	u8 *dfu=appbuf;
	for(t=0;t<appsize;t+=2)
	{						    
		temp=(u16)dfu[1]<<8;
		temp+=(u16)dfu[0];	  
		dfu+=2;//偏移2个字节
		iapbuf[i++]=temp;	    
		if(i==1024)
		{
			i=0;
			STMFLASH_Write(fwaddr,iapbuf,1024);	
			fwaddr+=2048;//偏移2048  16=2*8.所以要乘以2.
		}
	}
	if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.  
}

void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	   //扇区地址
	u16 secoff;	   //扇区内偏移地址(16位字计算)
	u16 secremain; //扇区内剩余地址(16位字计算)	   
 	u16 i;    
	u32 offaddr;   //去掉0X08000000后的地址
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
	FLASH_Unlock();						//解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~127 for STM32F103RBT6
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2个字节为基本单位.)
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   
	if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
	while(1) 
	{	
		STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除  不是1	  
		}
		if(i<secremain)//需要擦除
		{
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
			for(i=0;i<secremain;i++)//复制
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}
		else 
			STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
		if(NumToWrite==secremain)
			break;//写入结束了
		else//写入未结束
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0 	 
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain;	//写地址偏移	   
		   	NumToWrite-=secremain;	//字节(16位)数递减
			if(NumToWrite>(STM_SECTOR_SIZE/2))
				secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
			else 
				secremain=NumToWrite;//下一个扇区可以写完了
		}	 
	};	
	FLASH_Lock();//上锁
}


  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值