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 }