固件解析-S19文件

 1:前言     

       Motorola S-record是由Motorola创建的一种文件格式,它以 ASCII十六进制文本形式传送二进制信息。这种文件格式也可以称为SRECORD、SREC、S19、S28、S37。它通常用于对微控制器、EPROM 和其他类型的可编程逻辑设备进行编程。 
     S-record格式是在1970年代中期为 Motorola 6800处理器创建的。该处理器和其他嵌入式处理器的软件开发工具将生成S-record格式的可执行代码和数据。程序员将读取S-record格式并将数据“刻录”到嵌入式系统中使用的PROM或EPROM中。

2:S19文件 格式分析

     找份单片机工程编译下,基本都支持S19文件格式输出,本文分析一份STM32固件数据。

       选择输出类型为:Motorola S-records  编译源代码输出*.s19文件。通过Notepad++打开,看到它通过颜色帮我们分辨出各个数据类型。

类型+长度+地址+[数据]+校验   数据可选  

长度 = 地址+数据+校验

校验和 = 类型+长度+地址+[数据] 累计 低字节取反

05+08+00+4A+D1 = 0x128   0x28取反 = 0xD7

记录

类型

功能

地址

长度

描述备注
S0标题2字节用于记录文件信息如:文件名第一行
S1数据2字节记录带2个地址的数据
S2数据3字节记录带3个地址的数据
S3数据4字节记录带4个地址的数据
S7结束4字节结束S3数据记录与S3配对出现
S8结束3字节结束S2数据记录与S2配对出现
S9结束2字节结束S1数据记录与S1配对出现

3:代码解析S19文件

       通过C++解析S19文件得到若干个数据块,开发过程中没有设置段基本就是一块数据。我们定义数据结构如下:

class RecordBlock
{
     public:
     int address;         //地址
     QByteArray data;//数据
};
                        

int S19Firmware::ParseFile(QString path, QList<RecordBlock> &BlockList)
     {
         //step1:清空块列表
         BlockList.clear();
         //step2:清空记录行列表
         recordLines.clear();

         QByteArray HLine;
         //step3:声明一个流读取类
         QFile aFile(path);
          if (!aFile.exists()) //文件不存在
              return -1;
          if (!aFile.open(QIODevice::ReadOnly | QIODevice::Text)) //打开失败
              return -2;

         QByteArray data;
         int dataLenght;
         bool ok;
        while (!aFile.atEnd())
         {
             HLine = aFile.readLine();
             if(HLine.size()<8) return -5;//数据长度错误

             //step4:如果读取一行代码是以“S0”开头的话,则不进行解析
             if (HLine[0] != 'S')//"S0"开头的不用打印
             {
                  return -3;//不以“S”开头不解析  头格式错误
             }
             else
             {
                 int type = HLine.mid(1, 1).toInt(&ok,16);//文件类型
                 if(ok==false) return -4; //类型错误
                 unsigned  int address;
                 int lineLen;
                 int offse;
                 switch (type)
                 {
                    case 0:
                       HeadLineStr =  QString(HLine);
                       // qDebug()<<"HeadLineStr"<<HeadLineStr;
                     break;
                    case 1://数据记录(Data Record) S1
                    case 2://数据记录(Data Record) S2
                    case 3://数据记录(Data Record) S3
                         {
                             //去掉"S0"
                             HLine.remove(0,2);
                             //数据长度
                             dataLenght  = HLine.mid(0,2).toInt(&ok,16);
                             if(ok==false) return -4; //类型错误

                             lineLen = 2+ dataLenght*2;
                             if(HLine.size()<lineLen) return -5;//数据长度错误

                             //取地址 高在前
                             if(type==1)
                             {
                                 address= HLine.mid(2,4).toInt(&ok,16);
                                 if(ok==false) return -4; //类型错误
                                 offse = 6;
                             }
                             else if(type==2)
                             {
                                  address = HLine.mid(2,6).toInt(&ok,16);
                                 if(ok==false) return -4; //类型错误
                                  offse = 8;
                             }
                             else if(type==3)
                             {
                                  address = HLine.mid(2,8).toInt(&ok,16);
                                 if(ok==false) return -4; //类型错误
                                  offse = 10;
                             }
                             //取加法校验
                             int checkNum =  HLine.mid(lineLen-2,2).toInt(&ok,16);
                             if(ok==false) return -4; //类型错误
                             //HEX文件校验和的计算方式是将除了开头的“:”和最后两位的校验和去掉后的其他数相加
                             if (checkSum(data,HLine,lineLen-2,offse, checkNum))
                             {
                                 //add
                                 RecordLine S19Data;
                                 S19Data.address = address;
                                 //纯数据,10 =  总个数2 + 地址4 + 类型2 + 校验2
                                 S19Data.data = data;
                                 //纯数据长度,去掉地址2位,校验1位 = 3
                                 S19Data.count = data.size();
                                 recordLines.append(S19Data);
                                // qDebug()<<  S19Data.data.toHex();//
                             }
                             else
                             {
                                 return -7;//校验错误
                             }
                         }
                         break;

                    case 7: //
                    case 8: //
                    case 9: //
                         TailLineStr =  QString(HLine);
                         //qDebug()<<"TailLineStr"<<TailLineStr;
                         break;
                 }

             }

         }
         aFile.close();
        //升序排序
         devListSort(recordLines);
          /*for(int i = 0; i <recordLines.length(); i++)
           {
               qDebug()<< i << recordLines.at(i).address;
           }
          */

         //把文件中每一行的数据组合成若干个地址开头的整块数据,放在s19Block里用于发送
         int nextAddress = 0xFFFF;
         RecordBlock S19Block;
         for (int i = 0; i < recordLines.size(); i++)
         {
             if (i == 0)//第一个循环
             {
                // HexBlock = new HexRecordBlock();
                 //获取下一条记录的起始地址,并将本条记录存储再数据块中
                 nextAddress = getNextAddress(S19Block, recordLines, i);
                 if (i == recordLines.size() - 1)
                 {
                     //已经循环到底,只有一个block
                     BlockList.push_back(S19Block);
                 }
             }
             else//如果不是第一条记录
             {
                 if (nextAddress == recordLines[i].address)
                 {
                     joinHexBlockData(S19Block.data, recordLines[i].data);
                     nextAddress = recordLines[i].address + recordLines[i].count;
                     if (i == recordLines.size() - 1)
                     {
                         //已经循环到底,只有一个block
                         BlockList.append(S19Block);
                     }
                 }
                 else if (nextAddress < recordLines[i].address)
                 {
                     BlockList.append(S19Block);
                     S19Block.data.clear();
                     nextAddress = getNextAddress(S19Block, recordLines, i);
                     if (i == recordLines.size() - 1)
                     {
                         //已经循环到底,只有一个block
                         BlockList.append(S19Block);
                     }
                 }
                 else
                 {
                    qDebug()<<"地址覆盖";
                     return -8;
                 }
             }
         }
         return 0;
     }


void S19Firmware::devListSort(QList<RecordLine > &list)
{
  qSort(list.begin(), list.end(), compare);
}


bool S19Firmware::checkSum(QByteArray &hex,QByteArray data, int len, int offset,unsigned char checkNum)
    {
        //校验
        unsigned int sum = 0;
        bool ok;
        hex.clear();
        for (int i = 0; i <len; i = i + 2)
        {
            unsigned char value = data.mid(i,2).toInt(&ok,16);
            if(ok==false) return false;
            if(i>=offset)
            hex.append(value);
            sum += value;
        }

        //取低字节,1位
       unsigned  char lowSum = sum&0xff;
        if ((0xff - lowSum) == checkNum)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

获得数据后就可以通过CAN传输来给设备升级!!!

1:代码通过一行一行的解析数据,并对数据检验核实。

2:通过地址排序行数据。理论是不需要的,生成是按照地址顺序生成。

3:把连续地址合并成大块。好处在于升级可以按自定义数据长度传输如ISO-15765 最大一帧可以传输4k数据

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
CAPL是一种编程语言,通常用于编写测试和仿真脚本,不直接支持解析S19文件。但是,CAPL提供了一个二进制文件I/O库,可以用于读写二进制文件,因此可以使用CAPL来读取S19文件并提取其中的数据。 S19文件是一种十六进制文件格式,用于存储嵌入式系统的程序和数据。它包含了一些元数据和十六进制数据记录。下面是一个S19文件的示例: ``` S1130000C0F2BEEF00000000000000000000000000B6 S11300100000000000000000000000000000000000A6 S9030000FC ``` 每行记录由以下几部分组成: - 起始符号"S1"或"S9",表示数据记录类型。S1表示数据记录,S9表示文件结束记录。 - 记录长度,以十六进制表示。例如,S113表示记录长度为19字节(0x13)。 - 起始地址,以十六进制表示。例如,0000表示起始地址为0。 - 记录类型,以十六进制表示。例如,C0表示数据记录。 - 数据,以十六进制表示。例如,F2BEEF表示数据为0xF2, 0xBE, 0xEF。 要解析S19文件,可以使用CAPL的二进制文件I/O库和字符串处理函数。基本的步骤如下: 1. 打开S19文件,使用fopen函数打开文件并返回一个文件指针。 2. 读取每行记录,使用fgets函数读取文件中的每一行。 3. 解析每行记录,使用字符串处理函数和类型转换函数将每行记录转换为数据。 4. 处理解析后的数据,根据记录类型将数据存储到相应的位置。 以下是一个简单的CAPL代码示例,演示了如何读取S19文件并提取其中的数据。 ``` variables { FILE* fp; char line[80]; unsigned char data[1024]; unsigned long address = 0; } on start { fp = fopen("program.s19", "r"); if (fp == NULL) { write("Failed to open file"); return; } while (fgets(line, sizeof(line), fp)) { if (line[0] != 'S') { continue; // skip comments and other lines } int len = (int)strtol(line + 2, NULL, 16); // parse length field int type = (int)strtol(line + 8, NULL, 16); // parse type field if (type == 0) { int i; for (i = 0; i < len - 5; i += 2) { int byte = (int)strtol(line + 10 + i, NULL, 16); // parse data byte data[address++] = (unsigned char)byte; } } else if (type == 9) { break; // end of file } } fclose(fp); // process data here } ``` 该代码打开名为“program.s19”的文件,并逐行读取文件内容。对于每个数据记录,它解析长度、类型和数据,将数据存储到data数组中,并将地址address增加相应的字节数。最后,它关闭文件并在此处处理data数组中的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值