STM32_IAP远程升级及C#上位机

STM32的IAP功能在一些需要升级维护的场景下显得十分的重要,当然在实际项目中,我们需要远程发送升级指令,使得主控进入升级模式,进而将固件下发升级。很多网上的资源中,只有IAP跳转至APP,并且不带有任何协议,直接将bin文件一次性下发。但是这样的话,在项目实际使用过程中,会非常不稳定。还有一些是有代码,但是上位机代码或者stm32的代码不给你,只是提供思路。也是挺麻烦的一件事情。

我先讲讲自己的思路,C#上位机方面,首先打开串口,串口的波特率使用115200(上位机中没有给出设置界面)。打开需要升级的bin文件,左侧textbox显示文件绝对路径,下侧textbox显示文件大小。此程序将bin文件分成2k字节一个包,并且在每个包的头部加入0x5a的包头,再接着包的序号。以及包尾部加入0xaa。总共2051字节一包。下位机接收并保存之后,返回0x5a,包序号,0xaa。上位机接收到返回之后,继续下发后序bin包。其中最后一包序号为0xa5。下位机接收到0xa5序号的包时,下位机开始跳转至APP。

下位机跳转至APP之后,上位机点击进入升级模式按钮后,上位机下发AA,BB,CC,DD,EE指令,下位机接收到指令,跳转回IAP程序,等待固件的下发。若一分钟后没有收到固件,重新跳转回APP。

下面是bin文件组包。

        public byte[] SplitArray(byte[] Source, int StartIndex, int EndIndex,byte Block)
        {
            try
            {
                byte[] result = new byte[EndIndex - StartIndex + 1+2+1];//加入帧头(2 bytes)尾(1 byte)
                result[0] = 0x5a;
                result[1] = Block;
                for (int i = 0; i <= EndIndex - StartIndex; i++) result[i+2] = Source[i + StartIndex];
                result[EndIndex - StartIndex + 1 + 2] = 0xaa;
                return result;
            }
            catch (IndexOutOfRangeException ex)
            {
                throw new Exception(ex.Message);
            }
        }

接收到返回之后继续下发后序bin文件包

 int recl;
            if (serialPort1.BytesToRead > 0)
            {
                timer1.Stop();
                recl = serialPort1.BytesToRead;//读取串口接收的长度
                byte[] recFile = new byte[recl];
                for (int i = 0; i < recl; i++)
                {
                    recFile[i] = (byte)(serialPort1.ReadByte());
                }
                if (recFile[0] == 0x5a && recFile[2] == 0xaa)
                {
                    if (recFile[1] == 0xa5)//接收完成
                    {
                        progressBar1.Value = 100;
                        sendflag = false;
                        textBox2.AppendText("单片机升级完成\r\n");
                    }
                    else if(recFile[1] == 0xff)
                    {
                        textBox2.AppendText("单片机已经进入升级模式\r\n");
                    }
                    else if ((file_len - read_len * recFile[1]) / read_len < 1)
                    {
                        progressBar1.Value = read_len * recFile[1] * 100 / file_len;
                        textBox2.AppendText("单片机升级........." + progressBar1.Value + "%\r\n");
                        try
                        {
                            sendchar = SplitArray(binchar, read_len * recFile[1], file_len - 1, 0xa5);
                            serialPort1.Write(sendchar, 0, file_len - read_len * recFile[1] + 3);
                            timer1.Start();
                        }
                        catch (Exception)
                        {

                        }
                    }
                    else
                    {
                        progressBar1.Value = read_len * recFile[1] * 100 / file_len;
                        textBox2.AppendText("单片机升级......." + progressBar1.Value + "%\r\n");
                        try
                        {
                            sendchar = SplitArray(binchar, read_len * recFile[1], read_len * (recFile[1] + 1) - 1, (byte)((recFile[1] + 1) & 0xff));
                            serialPort1.Write(sendchar, 0, read_len + 3);
                            timer1.Start();
                        }
                        catch (Exception)
                        {

                        }
                    }

                }
                else
                {
                    if (sendflag == true)
                    {
                        textBox2.AppendText("单片机接收错误,请重新发送\r\n");
                        sendflag = false;
                    }
                }
            }
        }

下位机方面:IAP跳转程序

//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
	u8 i;
	if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)	//检查栈顶地址是否合法.
	{ 
		jump2app=(iapfun)*(vu32*)(appxaddr+4);		//用户代码区第二个字为程序开始地址(复位地址)		
		MSR_MSP(*(vu32*)appxaddr);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		for(i = 0; i < 8; i++)
		{			
			NVIC->ICER[i] = 0xFFFFFFFF;	/* 关闭中断*/
			NVIC->ICPR[i] = 0xFFFFFFFF;	/* 清除中断标志位 */
		}
		jump2app();									//跳转到APP.
	}
}	

跳转之前,需要将中断关闭,APP中使用了串口IDLE的中断,不清楚为什么,一定需要在程序中将其DISABLE,不然跳转回IAP之后,不能跳回APP。

APP中采用了FreeRTOS实时系统,APP程序起始偏移如下。

NVIC_SetVectorTable(FLASH_APP1_ADDR,0);

说明:STM32程序大部分参考正点原子例程。稍作修改。

源码位置:https://download.csdn.net/download/qq_23229787/10807490

上传的c#程序有mysql的登录和注册,可以注册再登录,也可以在登录界面点击版本号进入系统。对了,本该将bin文件分包之后,采用较为标准的协议,但是自己为了方便就自定义了协议。另外代码中有什么不好之处还请大家提出,一起学习。

  • 8
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值