基于“ModBus写文件”实现STM32串口IAP升级固件(上)

ModBus协议

ModBus是一个应用层的通信协议,广泛应用于工业控制等领域。
主要功能码有0x03(读多个寄存器),0x10(写多个寄存器),0x15(写文件)。
这里主要说明0x15(写文件)功能码

0x15(写文件)

请求

说明 长度
地址域 1个字节
功能码 1个字节 0x15
请求数据长度 1个字节 0x07到0xF5
子请求x,参考类型 1个字节 0x06
子请求x,文件号 2个字节 0x0000到0xFFFF
子请求x,记录号 2个字节 0x0000到0x270F
子请求x,记录长度 2个字节 N
子请求x,记录数据 N*2个字节
子请求x+1 …… ……
CRC 2个字节

响应

说明 长度
地址域 1个字节
功能码 1个字节 0x15
请求数据长度 1个字节 0x07到0xF5
子请求x,参考类型 1个字节 0x06
子请求x,文件号 2个字节 0x0000到0xFFFF
子请求x,记录号 2个字节 0x0000到0x270F
子请求x,记录长度 2个字节 N
子请求x,记录数据 N*2个字节
子请求x+1 …… ……
CRC 2个字节

例程

请求 十六进制 响应 十六进制
域名 0x09 域名 0x09
功能码 0x15 功能码 0x15
请求数据长度 0x0d 请求数据长度 0x0d
子请求1,参考类型 0x06 子请求1,参考类型 0x06
子请求1,文件号H 0x00 子请求1,文件号H 0x00
子请求1,文件号L 0x04 子请求1,文件号L 0x04
子请求1,记录号H 0x00 子请求1,记录号H 0x00
子请求1,记录号L 0x07 子请求1,记录号L 0x07
子请求1,记录长度H 0x00 子请求1,记录长度H 0x00
子请求1,记录长度L 0x03 子请求1,记录长度L 0x03
子请求1,记录数据H 0x22 子请求1,记录数据H 0x22
子请求1,记录数据L 0x11 子请求1,记录数据L 0x11
子请求1,记录数据H 0x22 子请求1,记录数据H 0x22
子请求1,记录数据L 0x11 子请求1,记录数据L 0x11
子请求1,记录数据H 0x22 子请求1,记录数据H 0x22
子请求1,记录数据L 0x11 子请求1,记录数据L 0x11
CRC L crc L
CRC H crc H

总结:请求和响应一样

上位机

上位机使用C#,在Visual Studio环境下开发。

预定义

地址 功能 说明
0xFF00 OTA状态 0:APP状态,正常运行中;1:BootLoader状态,等待固件升级
0xFF02 固件版本 1个寄存器
0xFF04 固件长度 2个寄存器

主要流程图

Created with Raphaël 2.1.2 Start 写OTA_STATUS=1(ModBus写寄存器) OTA_STATUS==1?(ModBus读寄存器) 写SoftwareLength=xx(ModBus写寄存器) SoftwareLength==xx?(ModBus读寄存器) 写固件(ModBus写文件) OTA_STATUS==0?(ModBus读寄存器) End 错误,返回 yes no yes no yes no

界面

这里写图片描述

主要程序

读取信息按钮-点击事件

/*
         * 读取信息按钮-点击事件
         * 读取固件信息
         * 
         */
        private void ReadSoftwareBtn_Click(object sender, EventArgs e)
        {
            ReadSoftware();
        }

        /*
         * 读取RTU固件
         * 
         */
        private void ReadSoftware()
        {
            if (!isSerialOpen)
            {
                MessageBox.Show("请先打开串口");
                return;
            }
            if (workerStatus.isBackgroundRun)
            {
                MessageBox.Show("请稍后,后台任务运行中");
                return;
            }
            StatusLabel.Text = "connecting...";
            readSoftwareWorker.RunWorkerAsync();
        } 

        /*
         * backgroundWorker-DoWork
         * 读取RTU固件
         * 
         */
        private void readSoftwareWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            workerStatus.isBackgroundRun = true;   //后台任务-运行中
            workerStatus.isRunError = false;       //后台任务-运行正常 

            //1. 读取固件版本
            if (!ModBus_Function_3(readSoftwareVersionBuff, readSoftwareVersionBuff.Length))
            { 
                workerStatus.isRunError = true;    //后台任务-运行出错
                return;
            }
            byte high = RxBuff[3];
            byte low = RxBuff[4];
            if (high != 0xff || low != 0xff)
            {
                softwareVersion = high + low * 1.0 / 10.0;
            }

            //2. 读取固件长度
            if (!ModBus_Function_3(readSoftwareLengthBuff, readSoftwareLengthBuff.Length))
            {
                workerStatus.isRunError = true;    //后台任务-运行出错
                return;
            }
            fileLength = RxBuff[3] + ((UInt32)RxBuff[4] << 8) + ((UInt32)RxBuff[5] << 16) + ((UInt32)RxBuff[6] << 24);

            //3. 读取OTA状态
            if (!ModBus_Function_3(readOTAStatusBuff, readOTAStatusBuff.Length))
            {
                workerStatus.isRunError = true;    //后台任务-运行出错
                return;
            }
            otaStatus = (UInt16)((RxBuff[3] << 8) + RxBuff[4]);

        } 

        /*
         * backgroundWork-RunWorkerCompleted
         * 读取RTU固件
         * 
         */
        private void readSoftwareWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            workerStatus.isBackgroundRun = false;  //后台任务-运行结束
            if (workerStatus.isRunError)
            {
                StatusLabel.Text = "Connect failed!";
                return;
            } 
            StatusLabel.Text = "Connected!";
            softwareVersionLable.Text = "V "+softwareVersion.ToString("f1");
            softwareLengthLabel.Text = fileLength.ToString()+" byte";
            if(otaStatus==0)
            {
                otaStatusLabel.Text = "APP";
            }
            else
            {
                otaStatusLabel.Text = "砖头(请升级固件)";
            }
        }

升级固件按钮-点击事件

/*
         * 升级固件按钮-点击事件
         * 
         */
        private void UpgradeSoftwareBtn_Click(object sender, EventArgs e)
        { 
            if (!isSerialOpen)
            {
                MessageBox.Show("请先打开串口");
                return;
            }
            if (workerStatus.isBackgroundRun)
            {
                MessageBox.Show("请稍后,后台任务运行中");
                return;
            } 

            openFileDialog.Filter = "程序文件(*.bin)|*.bin";
            openFileDialog.FilterIndex = 1;
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                filePathName = openFileDialog.FileName;
                int indexOf = filePathName.IndexOf("RTU_V"); 
                if (indexOf!=-1)
                {
                    fileName = filePathName.Substring(indexOf);
                    if(fileName.Length!=12)
                    {
                        MessageBox.Show("固件名错误!");
                        return; 
                    }
                }  

                StatusLabel.Text = "downloading...";
                upgradeSoftwareWorker.RunWorkerAsync();
                toolStripProgressBar.Visible = true;
                toolStripProgressBar.Value = 0;
            }
        } 


        private void upgradeSoftwareWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            workerStatus.isBackgroundRun = true;    //后台任务-开始运行
            workerStatus.isRunError = false;        //后台任务-运行正常 

            //写固件
            byte[] writeSoftwareBuff = new byte[0xfc];

            //1. 写OTA状态,0:无/成功/跳转 ; 1:准备升级
            if(!ModBus_Funtion_16(writeOTAStatusBuff,writeOTAStatusBuff.Length))
            {   
                workerStatus.isRunError = true;
                return;
            }
            //2. 读OTA状态
            if(!ModBus_Function_3(readOTAStatusBuff,readOTAStatusBuff.Length))
            {
                workerStatus.isRunError = true;
                return;
            }
            else
            {
                if(RxBuff[4]!=0x01)
                {
                    workerStatus.isRunError = true;
                    return;
                }
            }
            //3. 写固件长度 
            file = new FileStream(filePathName, FileMode.Open);
            file.Seek(0, SeekOrigin.Begin);
            fileLength = file.Length;
            writeSoftwareLengthBuff[7] = (byte)fileLength;
            writeSoftwareLengthBuff[8] = (byte)(fileLength>>8);
            writeSoftwareLengthBuff[9] = (byte)(fileLength>>16);
            writeSoftwareLengthBuff[10] = (byte)(fileLength>>24);
            Console.WriteLine("fileLength=" + fileLength);
            UInt16 crc = 0;
            crc = CRC16(writeSoftwareLengthBuff,writeSoftwareLengthBuff.Length-2);
            writeSoftwareLengthBuff[11] = (byte)crc;
            writeSoftwareLengthBuff[12] = (byte)(crc>>8); 
            if (!ModBus_Funtion_16(writeSoftwareLengthBuff,writeSoftwareLengthBuff.Length))
            {
                workerStatus.isRunError = true;
                return;
            }
            //4. 读固件长度  
            if (!ModBus_Function_3(readSoftwareLengthBuff,readSoftwareLengthBuff.Length))
            {
                workerStatus.isRunError = true;
                return;
            }
            else
            {
                long temp_fileLength = RxBuff[3] + ((UInt32)RxBuff[4] << 8) + ((UInt32)RxBuff[5] << 16) + ((UInt32)RxBuff[6] << 24);
                if(temp_fileLength!=fileLength)
                {
                    workerStatus.isRunError = true;
                    return;
                }
            }
            //5. 写文件 
            UInt16 recordNumber = 0;
            writeSoftwareBuff[0] = DEFAULT_ADDR;                //地址域
            writeSoftwareBuff[1] = 0x15;                        //功能码
            writeSoftwareBuff[2] = 0xFC;                        //请求数据长度
            writeSoftwareBuff[3] = 0x06;                        //参考类型
            writeSoftwareBuff[4] = (byte)(fileName[5] - '0');   //文件号
            writeSoftwareBuff[5] = (byte)(fileName[7] - '0');

            writeSoftwareBuff[8] = 0x00;                        //记录长度
            writeSoftwareBuff[9] = 0xf0;

            for (; recordNumber < fileLength/0xf0; recordNumber++)
            { 
                writeSoftwareBuff[6] = (byte)(recordNumber >> 8);   //记录号
                writeSoftwareBuff[7] = (byte)recordNumber;
                file.Read(writeSoftwareBuff, 10, 0xf0);             //记录数据 
                crc = CRC16(writeSoftwareBuff, writeSoftwareBuff.Length - 2);
                writeSoftwareBuff[0xfa] = (byte)crc;
                writeSoftwareBuff[0xfb] = (byte)(crc >> 8);
                if(!ModBus_Function_15(writeSoftwareBuff,writeSoftwareBuff.Length))
                {   
                    workerStatus.isRunError = true;
                    return;
                } 
                upgradeSoftwareWorker.ReportProgress((int)(recordNumber*0xf0/((int)fileLength*1.0)*100));//报告进度
            }
            int leftLength = (int)fileLength - recordNumber * 0xf0;
            if(leftLength!=0)
            { 
                writeSoftwareBuff[6] = (byte)(recordNumber >> 8);   //记录号
                writeSoftwareBuff[7] = (byte)recordNumber;
                file.Read(writeSoftwareBuff, 10, leftLength);             //记录数据 
                crc = CRC16(writeSoftwareBuff, 10 + leftLength);
                writeSoftwareBuff[10 + leftLength] = (byte)crc;
                writeSoftwareBuff[11 + leftLength] = (byte)(crc >> 8); 
                if (!ModBus_Function_15(writeSoftwareBuff, leftLength+12))
                {
                    workerStatus.isRunError = true;
                    return;
                }
            }

            Thread.Sleep(100);

            //6. 读取OTA状态
            if (!ModBus_Function_3(readOTAStatusBuff, readOTAStatusBuff.Length))
            {
                workerStatus.isRunError = true;
                return;
            }
            else
            {
                if (RxBuff[4] != 0x00)
                {
                    workerStatus.isRunError = true;
                    return;
                }
            }
        }

        private void upgradeSoftwareWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            workerStatus.isBackgroundRun = false;
            toolStripProgressBar.Visible = false;
            file.Close();
            if (workerStatus.isRunError)
            {
                MessageBox.Show("升级失败");
                StatusLabel.Text = "OTA fail";
            }
            else
            {
                MessageBox.Show("升级成功");
                StatusLabel.Text = "OTA success";
            }
            workerStatus.isRunError = false;
        }

        private void upgradeSoftwareWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            toolStripProgressBar.Value = e.ProgressPercentage;
        }

这里写图片描述

猜你喜欢

转载自blog.csdn.net/kangweijian/article/details/80369418