×
单片机 > 单片机程序设计 > 详情

STM32_IAP远程升级及C#上位机

发布时间:2020-05-19 发布时间:
|

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("单片机升级完成rn");

                    }

                    else if(recFile[1] == 0xff)

                    {

                        textBox2.AppendText("单片机已经进入升级模式rn");

                    }

                    else if ((file_len - read_len * recFile[1]) / read_len < 1)

                    {

                        progressBar1.Value = read_len * recFile[1] * 100 / file_len;

                        textBox2.AppendText("单片机升级........." + progressBar1.Value + "%rn");

                        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 + "%rn");

                        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("单片机接收错误,请重新发送rn");

                        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文件分包之后,采用较为标准的协议,但是自己为了方便就自定义了协议。另外代码中有什么不好之处还请大家提出,一起学习。



关键字:STM32  IAP  远程升级  上位机 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32单片机的复用端口初始化的步骤及方法