S19文件格式与mot文件类似,这里以S19文件为例
S19文件每一行数据全部由记录类型和十六进制数字组成,包含类型、长度、地址、数据和校验和五个部分。
上图中“S3”为类型,“25”为长度,意味着整行记录(除类型和长度)总共有37字节(74字符),“00008020”为该记录起始地址,
“000003E80004BAF00003D090000000000003345000061A8000035B6000061A80”为数据,共有32字节(64字符)数据,“B9”为校验和;
此行就是将数据依次刷写到起始地址为“00008020”的连续地址中
每字节数据被编码成2个16进制字符,第一个字符代表数据的高四位,第二个字符代表数据的低4位。
1.类型
S19中记录类型包括S0、S1、S2、S3、S5、S7、S8、S9等
S0,位于文件的第一行,和其他行不同,地址部分没有使用,用0000置位,整行表示记录的开始
“S00600004844521B”中“484452”为文件路径,“1B”为校验和
S1表示地址长度为两字节(4字符)的记录,包含类型、长度、地址、数据和校验和五个部分
S2表示地址长度为三字节(6字符)的记录,包含类型、长度、地址、数据和校验和五个部分
S3表示地址长度为四字节(8字符)的记录,包含类型、长度、地址、数据和校验和五个部分
S5表示文件中含有S1、S2、S3记录的个数,其后不接数据,包含S5的记录并不是每个文件必须的
S7表示地址长度为四字节(8字符)的记录,包含类型、长度、地址和校验和四个部分,此行表示程序的结束
S8表示地址长度为三字节(6字符)的记录,包含类型、长度、地址和校验和四个部分,此行表示程序的结束
S9表示地址长度为两字节(4字符)的记录,包含类型、长度、地址和校验和四个部分,此行表示程序的结束
只有S1、S2、S3、S5需要写入Flash中
“S50300807C”中“03”表示S3的记录。“0080”表示含有128行的S3记录
“S7050000000FA”表示结束
2.长度
包含一个字节(两个字符),长度=地址字节数+数据字节数+校验和字节数
3.地址
表示该行的其实地址,字节数由类型决定,S1位两个字节,S2为三个字节,S3位四个字节
4.数据
包含0-32个字节
5.校验和
包含一个字节,校验和=0xFF-长度字节-地址字节-数据字节
0x25+0x00+0x00+0x80+0x20+0x00+0x00+...+0x1A+0x80=0x746
取46,0xFF-0x46=0xB9
6.读写修改S19文件代码
如有错误,欢迎指正

1 public class S19 2 { 3 public string path; //选择文件的路径 4 public List<string> listAllline; //存储所有的记录包括(S0、S5等) 5 6 7 /// <summary> 8 /// 读取S19文件,将所有记录存储在List中,便于调用修改 9 /// </summary> 10 /// <param name="file_path"></param> 11 public bool Read_S19(string file_path) 12 { 13 int line = 0; //记录当前读取的行数 14 FileStream File_Stream = null; 15 StreamReader Stream_Reader = null; 16 try 17 { 18 path = file_path; 19 File_Stream = new FileStream(this.path, FileMode.Open, FileAccess.Read); 20 Stream_Reader = new StreamReader(File_Stream, Encoding.Default); 21 listAllline = new List<string>(); 22 while (true) 23 { 24 line++; 25 string str = Stream_Reader.ReadLine(); //一行一行读取数据 26 if (str == null || str.Trim().Length == 0) 27 { 28 break; 29 } 30 listAllline.Add(str.Trim()); 31 } 32 33 Console.WriteLine("文件中共有" + line + "行数据"); 34 return true; 35 } 36 catch (Exception ex) 37 { 38 Console.WriteLine(ex.ToString() + line); 39 return false; 40 } 41 finally 42 { 43 File_Stream.Close(); 44 Stream_Reader.Close(); 45 } 46 } 47 48 /// <summary> 49 /// 依据地址和数据修改S19文件 50 /// </summary> 51 /// <param name="startAdd"></param> 52 /// <param name="modifyStr"></param> 53 /// <returns></returns> 54 public bool Modify_S19(uint startAdd, string modifyStr) 55 { 56 try 57 { 58 List<string> listTemp = new List<string>(); 59 foreach (var item in listAllline) 60 { 61 listTemp.Add(item); 62 } 63 listAllline.Clear(); 64 65 string lineData = " "; //当前行数据 66 int line = 0; //当前行数 67 int modifyLen = modifyStr.Length; //修改的数据长度,判断修改是否完成 68 69 if (path.Substring(path.Length - 4, 4) == ".s19" || path.Substring(path.Length - 4, 4) == ".S19") 70 { 71 while (lineData != null) 72 { 73 if (lineData.Length >= 10) //跳过非记录数据,所有记录数据中最短S5包含10个字符 74 { 75 if (lineData.Substring(0, 2) == "S3") //选择S3记录 76 { 77 uint currentAdd = Convert.ToUInt32(lineData.Substring(4, 8), 16); //当前行记录中包含的地址 78 if (startAdd >= currentAdd && Convert.ToUInt32(startAdd - currentAdd) < Convert.ToUInt32(lineData.Substring(2, 2), 16) - 5)//找到需要修改的数据的起始地址所在行 79 { 80 while (modifyLen > 0) 81 { 82 currentAdd = Convert.ToUInt32(lineData.Substring(4, 8), 16); 83 int differenceAdd = Convert.ToInt32(startAdd - currentAdd); //一行中需修改的起始地址之前的数据地址 84 int modifyAdd = Convert.ToInt32(lineData.Substring(2, 2), 16) * 2 - 10 - differenceAdd * 2;//每行中需要修改的数据长度 85 86 if (Convert.ToInt32(lineData.Substring(2, 2), 16) - 5 - differenceAdd >= modifyLen / 2)//判断需要修改的数据地址是不是在同一行,5 = 地址4字节+校验位1字节 87 { 88 string str1 = lineData.Remove(12 + (differenceAdd) * 2, modifyLen);//从修改的起始地址位置开始移除旧数据,长度为需要修改的数据长度,12 = 类型2字符+长度2字符+地址8字符 89 string str2 = str1.Insert(12 + (differenceAdd) * 2, modifyStr);//将新数据插入当前记录行 90 string sum_string = Checksum(str2.Remove(str2.Length - 2));//计算新的校验位字节 91 lineData = str2.Remove(str2.Length - 2, 2) + sum_string;//移除旧校验位,加入新校验位 92 modifyLen = modifyLen - modifyAdd;//正常情况下,修改的数据地址在同一行,此时计算之后modifyLen=0,跳出循环 93 } 94 else //地址不在同一行 95 { 96 string str1 = lineData.Remove(12 + (differenceAdd) * 2, Convert.ToInt32(lineData.Substring(2, 2), 16) * 2 - 10 - differenceAdd * 2);//移除地址后面的所有数据和校验位 97 string str2 = str1.Insert(12 + (differenceAdd) * 2, modifyStr.Substring(0, (Convert.ToInt32(lineData.Substring(2, 2), 16) - 5 - (differenceAdd)) * 2));//加入新数据 98 string sum_string = Checksum(str2.Remove(str2.Length - 2)); 99 lineData = str2.Remove(str2.Length - 2, 2) + sum_string; 100 modifyLen = modifyLen - modifyAdd;//计算剩余需要修改的数据长度 101 modifyStr = modifyStr.Remove(0, modifyAdd);//移除已经修改的数据 102 startAdd = startAdd + (Convert.ToUInt32(lineData.Substring(2, 2), 16) - 5 - Convert.ToUInt32(differenceAdd));//计算剩余数据的起始地址 103 } 104 listAllline.Add(lineData);//将修改好的或者不许修改的记录行存入list 105 if (line == listTemp.Count) 106 { 107 break; 108 } 109 lineData = listTemp[line];//提取新的记录行 110 line++; 111 } 112 } 113 } 114 listAllline.Add(lineData); 115 } 116 if (line == listTemp.Count)//判断是否到最后一行 117 { 118 break; 119 } 120 lineData = listTemp[line]; 121 line++; 122 } 123 } 124 125 } 126 catch (Exception ex) 127 { 128 MessageBox.Show(ex.ToString()); 129 return false; 130 } 131 132 return true; 133 } 134 135 /// <summary> 136 /// 计算一行数据的校验和 137 /// </summary> 138 /// <param name="line"> 不包含校验位的行数据</param> 139 /// <returns></returns> 140 private string Checksum(string line) 141 { 142 string checkSum = null; 143 int sum = 0; 144 string[] byteNum = new string[line.Length / 2];//计算字节数 145 for (int i = 1; i < line.Length / 2; i++) 146 { 147 byteNum[i] = line.Substring(i * 2, 2);//从长度位将一行数据按照字节分割为字节数组 148 sum += Convert.ToInt32(byteNum[i], 16);//将数据进行累加 149 } 150 151 string check = Convert.ToString(255 - sum, 16); //计算校验位 152 checkSum = check.Substring(check.Length - 2, 2).ToUpper();//校验位字节 153 return checkSum; 154 155 } 156 157 /// <summary> 158 /// 生成新的S19文件 159 /// </summary> 160 /// <param name="newFilepath"></param> 161 /// <returns></returns> 162 public bool Write_S19(string newFilepath) 163 { 164 StreamWriter Stream_Writer =null; 165 try 166 { 167 string[] allLineArray = listAllline.ToArray(); 168 Stream_Writer = new StreamWriter(newFilepath + ".s19", false); 169 int lines = listAllline.Count; 170 171 while (lines != 0) 172 { 173 Stream_Writer.WriteLine(allLineArray[listAllline.Count - lines]); 174 lines--; 175 } 176 return true; 177 } 178 catch(Exception ex) 179 { 180 Console.WriteLine(ex.Message ); 181 return false; 182 } 183 finally 184 { 185 Stream_Writer.Close(); 186 } 187 188 } 189 190 /// <summary> 191 /// 检查每行记录数据是否有缺失 192 /// </summary> 193 /// <returns></returns> 194 public bool CompleteCheck() 195 { 196 string currentLine = string.Empty ; 197 string type = string.Empty; 198 string length = string.Empty; 199 string address = string.Empty; 200 int dataLen; 201 int currentLineLen; 202 for (int i = 0; i < listAllline .Count; i++) 203 { 204 currentLine = listAllline[i]; 205 if (currentLine.Length >= 10) 206 { 207 type = currentLine.Substring(0, 2); 208 if(type=="S1" || type == "S2"|| type == "S3") 209 { 210 length = currentLine.Substring(2, 2); 211 switch (type) 212 { 213 case "S1": 214 address = currentLine.Substring(4, 4); 215 dataLen = Convert.ToInt32(length, 16) - 6; 216 currentLineLen = currentLine.Length - 10; 217 if (dataLen != currentLineLen) 218 { 219 Console.WriteLine("第" + (i + 1) + "行数据缺失"); 220 return false; 221 } 222 break; 223 case "S2": 224 address = currentLine.Substring(4, 6); 225 dataLen = Convert.ToInt32(length, 16) - 8; 226 currentLineLen = currentLine.Length - 12; 227 if (dataLen != currentLineLen) 228 { 229 Console.WriteLine("第" + (i + 1) + "行数据缺失"); 230 return false; 231 } 232 break; 233 case "S3": 234 address = currentLine.Substring(4, 8); 235 dataLen = Convert.ToInt32(length, 16) - 10; 236 currentLineLen = currentLine.Length - 14; 237 if (dataLen != currentLineLen) 238 { 239 Console.WriteLine("第" + (i + 1) + "行数据缺失"); 240 return false; 241 } 242 break; 243 default: 244 break; 245 } 246 } 247 248 } 249 } 250 return true; 251 } 252 253 254 /// <summary> 255 /// 检查记录地址连续性 并提取每行记录的地址,判断每个区间起始地址是否正确 256 /// </summary> 257 /// <param name="startAdd">包含每个区间的起始地址</param> 258 /// <param name="addList">返回所有记录地址</param> 259 /// <returns></returns> 260 public bool RecordStartAdd(string[] startAdd,ref List<string> addList) 261 { 262 bool firstLine = true; //判断文件第一行记录 263 int addLen = 0; //S19记录地址长度s3为4字节,s2为三字节,s1为两字节 264 uint currentAdd = 0; 265 uint dataLength = 0 ; 266 uint lastDataLength = 0; 267 uint lastAdd = 0; 268 int index = 0; 269 270 foreach (string currentLine in listAllline) 271 { 272 if (currentLine.Length < 10) 273 { 274 continue; 275 } 276 else 277 { 278 if (currentLine.Substring(0, 2) == "S0")//跳过不需刷写的记录行 279 { 280 continue; 281 } 282 switch (currentLine.Substring(0, 2)) 283 { 284 case "S1": 285 addLen = 2; 286 break; 287 case "S2": 288 addLen = 3; 289 break; 290 case "S3": 291 addLen = 4; 292 break; 293 default: 294 addLen = 4; 295 break; 296 } 297 if (firstLine) 298 { 299 if(currentLine.Substring(4, addLen * 2)!=startAdd [index])//检查第一行记录起始地址是否错误 300 { 301 Console.WriteLine("第"+ index +"区间起始地址错误"); 302 addList.Clear(); 303 return false; 304 } 305 addList.Add(currentLine.Substring(4, addLen * 2)); //取出第一行记录起始地址 306 firstLine = false; 307 currentAdd = Convert.ToUInt32(currentLine.Substring(4, addLen * 2), 16); //本行文件开始地址 308 dataLength = Convert.ToUInt32(currentLine.Substring(2, 2), 16) - Convert.ToUInt32(addLen) - 1; 309 continue; 310 } 311 312 if (currentLine.Substring(0, 2) == "S7" || currentLine.Substring(0, 2) == "S8" || currentLine.Substring(0, 2) == "S9")//记录结束 313 { 314 break; 315 } 316 317 lastAdd = currentAdd; //记录上一行地址 318 lastDataLength = dataLength; //记录上一行数据长度 319 currentAdd = Convert.ToUInt32(currentLine.Substring(4, addLen * 2), 16); //本行文件开始地址 320 dataLength = Convert.ToUInt32(currentLine.Substring(2, 2), 16) - Convert.ToUInt32(addLen) - 1; 321 322 if(lastAdd + lastDataLength == currentAdd)//判断相邻行地址是否连续 323 { 324 addList.Add(Convert.ToString(lastAdd + lastDataLength, 16).PadLeft(8, '0').ToUpper()); //依据上一行记录的地址和长度计算出当前行的地址,存入list 325 } 326 else //不连续意味着大区间地址结束,继续下一个大区间起始地址 327 { 328 index++; 329 if (currentLine.Substring(4, addLen * 2) != startAdd[index]) //检查每个区间起始地址是否错误 330 { 331 Console.WriteLine("第" + index + "区间起始地址错误"); 332 addList.Clear(); 333 return false; 334 } 335 addList.Add(Convert.ToString(currentAdd, 16).PadLeft(8, '0').ToUpper()); 336 } 337 } 338 } 339 return true; 340 } 341 342 343 /// <summary> 344 /// 记录每个区间第一行及它们的起始位置和起始地址 345 /// </summary> 346 /// <param name="intervalList"></param> 347 /// <returns></returns> 348 public Dictionary<int,string> RecordInterval(ref List<string> intervalList) 349 { 350 Dictionary<int, string> intervaiDic = new Dictionary<int, string>(); 351 bool firstLine = true; //判断文件第一行记录 352 int addLen = 0; //S19记录地址长度s3为4字节,s2为三字节,s1为两字节 353 uint currentAdd = 0; 354 uint dataLength = 0; 355 uint lastDataLength = 0; //上一行数据长度 356 uint lastAdd = 0; //上一行地址 357 int index = 0; //当前行数 358 359 foreach (string currentLine in listAllline) 360 { 361 index++; 362 if (currentLine.Length < 10) 363 { 364 continue; 365 } 366 else 367 { 368 if (currentLine.Substring(0, 2) == "S0")//跳过不需刷写的记录行 369 { 370 continue; 371 } 372 switch (currentLine.Substring(0, 2)) 373 { 374 case "S1": 375 addLen = 2; 376 break; 377 case "S2": 378 addLen = 3; 379 break; 380 case "S3": 381 addLen = 4; 382 break; 383 default: 384 addLen = 4; 385 break; 386 } 387 if (firstLine) 388 { 389 firstLine = false; 390 intervaiDic.Add(index, currentLine.Substring(4, addLen * 2)); 391 intervalList.Add(currentLine.Substring(4, addLen * 2)); 392 currentAdd = Convert.ToUInt32(currentLine.Substring(4, addLen * 2), 16); //本行文件开始地址 393 dataLength = Convert.ToUInt32(currentLine.Substring(2, 2), 16) - Convert.ToUInt32(addLen) - 1; 394 continue; 395 } 396 397 if (currentLine.Substring(0, 2) == "S7" || currentLine.Substring(0, 2) == "S8" || currentLine.Substring(0, 2) == "S9")//记录结束 398 { 399 break; 400 } 401 402 lastAdd = currentAdd; //记录上一行地址 403 lastDataLength = dataLength; //记录上一行数据长度 404 currentAdd = Convert.ToUInt32(currentLine.Substring(4, addLen * 2), 16); //本行文件开始地址 405 dataLength = Convert.ToUInt32(currentLine.Substring(2, 2), 16) - Convert.ToUInt32(addLen) - 1; 406 407 if (lastAdd + lastDataLength != currentAdd)//判断相邻行地址是否连续 //不连续意味着大区间地址结束,继续下一个大区间起始地址 408 { 409 intervaiDic.Add(index, currentLine.Substring(4, addLen * 2)); 410 intervalList.Add(currentLine.Substring(4, addLen * 2)); 411 } 412 } 413 } 414 415 return intervaiDic; 416 } 417 418 419 420 /// <summary> 421 /// 将区间不连续的地址补全 422 /// </summary> 423 /// <param name="completeValue">用于补全的字符</param> 424 /// <returns></returns> 425 public List<string> CompleteFile(string completeValue) 426 { 427 List<string> list = new List<string>(); 428 bool firstLine = true; //判断文件第一行记录 429 int addLen = 0; //S19记录地址长度s3为4字节,s2为三字节,s1为两字节 430 uint currentAdd = 0; 431 uint dataLength = 0; 432 uint lastDataLength = 0; //上一行数据长度 433 uint lastAdd = 0; //上一行地址 434 int completeLine = 0; //计算补全的行数 435 string lastLine; //上一行的记录 436 437 try 438 { 439 foreach (string currentLine in listAllline) 440 { 441 if (currentLine.Length < 10) 442 { 443 list.Add(currentLine); 444 continue; 445 } 446 else 447 { 448 if (currentLine.Substring(0, 2) == "S0")//跳过不需刷写的记录行 449 { 450 list.Add(currentLine); 451 continue; 452 } 453 switch (currentLine.Substring(0, 2)) 454 { 455 case "S1": 456 addLen = 2; 457 break; 458 case "S2": 459 addLen = 3; 460 break; 461 case "S3": 462 addLen = 4; 463 break; 464 default: 465 addLen = 4; 466 break; 467 } 468 469 if (firstLine) 470 { 471 firstLine = false; 472 list.Add(currentLine); 473 currentAdd = Convert.ToUInt32(currentLine.Substring(4, addLen * 2), 16); //本行文件开始地址 474 dataLength = Convert.ToUInt32(currentLine.Substring(2, 2), 16) - Convert.ToUInt32(addLen) - 1; 475 continue; 476 } 477 478 if (currentLine.Substring(0, 2) == "S7" || currentLine.Substring(0, 2) == "S8" || currentLine.Substring(0, 2) == "S9")//记录结束 479 { 480 list.Add(currentLine); 481 break; 482 } 483 484 lastAdd = currentAdd; //记录上一行地址 485 lastDataLength = dataLength; //记录上一行数据长度 486 lastLine = currentLine; 487 currentAdd = Convert.ToUInt32(currentLine.Substring(4, addLen * 2), 16); //本行文件开始地址 488 dataLength = Convert.ToUInt32(currentLine.Substring(2, 2), 16) - Convert.ToUInt32(addLen) - 1; 489 490 if (lastAdd + lastDataLength < currentAdd) //判断相邻行地址是否连续 ,不连续意味着大区间地址结束,开始做补全 491 { 492 int completeLen = Convert.ToInt32(currentAdd - lastAdd - lastDataLength); //计算需要补全的长度 493 uint newAdd = lastAdd; //每行新记录填补的地址 494 StringBuilder temp = new StringBuilder(); 495 while (completeLen > 0) 496 { 497 if (completeLen <= lastDataLength) //需要补的数据长度不足上一行记录的数据长度 498 { 499 completeLine++; 500 temp.Clear(); 501 temp.Append(lastLine.Substring(0, 2)); //记录上一行的类型 502 string newLen = Convert.ToString(completeLen + 5, 16).PadLeft(2, '0').ToUpper(); 503 temp.Append(newLen); 504 string newAdd_string = Convert.ToString(newAdd + completeLen, 16).PadLeft(8, '0').ToUpper(); 505 temp.Append(newAdd_string); 506 for (int i = 0; i < completeLen; i++) 507 { 508 temp.Append(completeValue); 509 } 510 temp.Append(Checksum(temp.ToString())); 511 list.Add(temp.ToString()); 512 completeLen -= completeLen; //减去已补过的长度 513 } 514 else 515 { 516 completeLine++; 517 temp.Clear(); 518 temp.Append(lastLine.Substring(0, 4)); 519 string newAdd_string = Convert.ToString(newAdd + lastDataLength, 16).PadLeft(8, '0').ToUpper(); 520 temp.Append(newAdd_string); 521 for (int i = 0; i < Convert.ToInt32(lastDataLength); i++) 522 { 523 temp.Append(completeValue); 524 } 525 temp.Append(Checksum(temp.ToString())); 526 list.Add(temp.ToString()); 527 completeLen -= Convert.ToInt32(lastDataLength); 528 } 529 530 newAdd = newAdd + lastDataLength; 531 } 532 currentAdd = newAdd + lastDataLength; //当前行的地址 533 534 //if (currentLine.Substring(4, 8) == "0000C000")//针对氮氧二代这一行记录特殊情况采取的措施,上下都不连续 535 //{ 536 // currentAdd = 50144; 537 //} 538 539 list.Add(currentLine); 540 } 541 else 542 { 543 list.Add(currentLine); 544 } 545 } 546 } 547 Console.WriteLine(completeLine); 548 return list; 549 } 550 catch (Exception ex) 551 { 552 Console.WriteLine(ex.Message); 553 MessageBox.Show(ex.Message); 554 return null; 555 } 556 } 557 }