首页 > 其他分享 >基于“ModBus写文件”实现STM32串口IAP升级固件(上)

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

时间:2023-04-21 16:35:45浏览次数:44  
标签:ModBus 请求 记录 STM32 串口 byte workerStatus 固件



文章目录

  • 一、ModBus协议
  • 1.1 0x15(写文件)
  • 请求
  • 响应
  • 例程
  • 二、上位机
  • 2.1 预定义
  • 2.2 主要流程图
  • 2.3 界面
  • 2.4 主要程序
  • 读取信息按钮-点击事件
  • 升级固件按钮-点击事件


一、ModBus协议

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

1.1 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环境下开发。

2.1 预定义

地址

功能

说明

0xFF00

OTA状态

0:APP状态,正常运行中;1:BootLoader状态,等待固件升级

0xFF02

固件版本

1个寄存器

0xFF04

固件长度

2个寄存器

2.2 主要流程图


Created with Raphaël 2.3.0 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


2.3 界面

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

2.4 主要程序

读取信息按钮-点击事件

/*
         * 读取信息按钮-点击事件
         * 读取固件信息
         * 
         */
        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;
        }



标签:ModBus,请求,记录,STM32,串口,byte,workerStatus,固件
From: https://blog.51cto.com/u_16081772/6213483

相关文章

  • Modbus协议整理
    文章目录01读线圈状态示例02读输入位状态示例03读保持寄存器示例04读输入寄存器示例05写单个线圈示例06写单个保持寄存器示例15写多个线圈示例16写多个保持寄存器示例01读线圈状态读取从机的线圈状态(ON/OFF),位操作。例:请求从机设备17读00020-00056线圈。其中00020-00056......
  • Python下使用串口发送十六进制数据
    importserialfromtimeimportsleepdefrecv(serial):whileTrue:data=serial.read_all()ifdata=='':continueelse:breaksleep(0.2)returndataif__name__=='__main_......
  • 二、STM32Cube生态系统操作
    一、嵌入式硬件二、嵌入式软件三、核心板设计3.1电源供电3.2时钟电路3.3复位电路3.4调试电路四、外围电路设计4.1ArduinoUNO引脚4.2指示灯电路4.3按键电路五、STM32CubeMX软件5.1目标板选择5.2引脚分配5.3外设配置......
  • stm32f407的学习
    最近的学习:运用hal库驱动了AD9854信号源,不过信号过一会儿不是很稳定学到了串口打印实现了ADC的调试,其中由于时钟的调试不对,显示乱码问题,改了两个文件的hse_value为8000000就行了学会了oled屏幕的显示,包括汉字显示,用到了点阵工具等在学oled浮点显示出adc的值,还没成功。......
  • 分享下之前做的STM32嵌入式Web完整设计教程和案例html,Ajax,Javacript,XML,cgi等
    这段时间好些网友咨询,特此再分享下:http://www.armbbs.cn/forum.php?mod=viewthread&tid=26034如果需要外网也可以访问,此贴:【专题教程第3期】开发板搭建Web服务器,利用花生壳让电脑和手机可以外网远程监控,手机4G访问域名效果:   微信公众号:armfly_com......
  • FDCAN输出数据(STM32H7)
    STM32配置FDCAN总线上有的设备可能不支持FDCAN,FDCAN配置为传统CAN。配置外部时钟  配置FDCAN1  GPIO配置 定义变量FDCAN_RxHeaderTypeDefRxHeader;FDCAN_TxHeaderTypeDefTxHeader;uint8_tRxData[8];uint8_tTxData[64]={0x24,0x47,0x4E,0x47,0x53,0......
  • [PLC]三菱Q系列MODBUS通信(QJ71C24N串口模块)
    三菱Q系列MODBUS通信(QJ71C24N串口模块)CPUQ01通信模块:QJ71C24N通信协议:MODBUSRTU编程软件:GXWORK2 打开GXWORK2,新建工程,然后右键点击智能功能模块 安装位置根据硬件实际情况设定,此处注意起始XY地址,后面会用到。     双击开关设置 CH2设置如下,通信协议......
  • STM32定时器的输出比较功能——输出PWM波形
    输出比较OC(OutputCompare)输入捕获IC(InputCapture)输入捕获和输出比较单元CC(Capture/Compare)输出比较模块的最主要功能:通过比较CNT(时基单元里的计数器,计数自增)和CCR寄存器(比较捕获寄存器,给定值)的关系,来对输出电平进行置1,置0或翻转的操作,用于输出一定频率和占空比的PWM波形......
  • stm32文件系统读写操作调试总结
    一问题最近使用到了文件系统的读写,中间遇到了一些问题值得深思。 二源码解析创建文件:FRESULTres;do{sprintf(filename,"/sensor_signal/sensor_%d.bin",file_num++);mprintf("filenameis:%s\n\r",filename);res=f_open(&file,fi......
  • STM32F103与407区别
    STM32F103与407区别......