[嵌入式开发模块]Motorola S-records(S19)解析模块

Myth丶恋晨 2022-05-30 04:55 378阅读 0赞

飞思卡尔系列单片机程序编译后生成的是S-records格式的文件(.s19)。在实现远程升级的时候,不可避免的需要传输S-record记录(或其转码后的数据),然后根据里头的数据来更新程序。这就需要对ASCII码表示的S-record数据进行解析。

于是就写了这个模块,或说工具包可能更准确点。
顺便一提。我已基于官方的版本基本实现了自定协议的bootloader,改成了软方式进入bootloader而不是官方的使用引脚的方式。代码不准备放出来,但后面会写篇文章介绍下自定的协议,原理,以及实现时遇到的坑等。

下面上代码:

  1. /*
  2. *********************************************************************************************************
  3. *
  4. *
  5. * Motorola S-records Support package
  6. * S-records 支持包
  7. *
  8. * File : SRecord.h
  9. * By : Lin Shijun(http://blog.csdn.net/lin_strong)
  10. * Date : 2018/03/03
  11. * Version : V1.0
  12. * Note : 1. This package provide the functions for converting between the S-record and the
  13. * corresponding string.
  14. * 这个包提供了S-record与字符串间的转换函数
  15. * 2. The package supposed the letter in the string in upper case. user should make
  16. * sure for that.
  17. * 工具包默认字符串中的字母都是大写的,用户需要保证这件事。
  18. * 3. the first thing to do with this package is to check the TYPE DEFINE.
  19. * 用这个包的第一步是检测下定义的类型对不对
  20. *
  21. * the follow description of Motorola S-records Format is from
  22. * http://www.amelek.gda.pl/avr/uisp/srecord.htm
  23. * chinese version: http://blog.csdn.net/lin_strong/article/details/78521950
  24. *
  25. * NAME
  26. * srec - S-record file and record format
  27. * DESCRIPTION
  28. *
  29. * An S-record file consists of a sequence of specially formatted ASCII character strings. An S-record
  30. * will be less than or equal to 78 bytes in length.
  31. *
  32. * The order of S-records within a file is of no significance and no particular order may be assumed.
  33. *
  34. * The general format of an S-record follows:
  35. *
  36. * +-------------------//------------------//-----------------------+
  37. * | type | count | address | data | checksum |
  38. * +-------------------//------------------//-----------------------+
  39. *
  40. * type -- A char[2] field. These characters describe the type of record (S0, S1, S2, S3, S5, S7, S8, or S9).
  41. *
  42. * count -- A char[2] field. These characters when paired and interpreted as a hexadecimal value, display the
  43. * count of remaining character pairs in the record.
  44. *
  45. * address -- A char[4,6, or 8] field. These characters grouped and interpreted as a hexadecimal value, display
  46. * the address at which the data field is to be loaded into memory. The length of the field depends on the number
  47. * of bytes necessary to hold the address. A 2-byte address uses 4 characters, a 3-byte address uses 6 characters,
  48. * and a 4-byte address uses 8 characters.
  49. *
  50. * data -- A char [0-64] field. These characters when paired and interpreted as hexadecimal values represent
  51. * the memory loadable data or descriptive information.
  52. *
  53. * checksum -- A char[2] field. These characters when paired and interpreted as a hexadecimal value display the
  54. * least significant byte of the ones complement of the sum of the byte values represented by the pairs of
  55. * characters making up the count, the address, and the data fields.
  56. *
  57. * Each record is terminated with a line feed. If any additional or different record terminator(s) or delay
  58. * characters are needed during transmission to the target system it is the responsibility of the transmitting
  59. * program to provide them.
  60. *
  61. * S0 Record. The type of record is 'S0' (0x5330). The address field is unused and will be filled with zeros
  62. * (0x0000). The header information within the data field is divided into the following subfields.
  63. *
  64. * mname is char[20] and is the module name.
  65. * ver is char[2] and is the version number.
  66. * rev is char[2] and is the revision number.
  67. * description is char[0-36] and is a text comment.
  68. *
  69. * Each of the subfields is composed of ASCII bytes whose associated characters, when paired, represent one
  70. * byte hexadecimal values in the case of the version and revision numbers, or represent the hexadecimal values
  71. * of the ASCII characters comprising the module name and description.
  72. *
  73. * S1 Record. The type of record field is 'S1' (0x5331). The address field is intrepreted as a 2-byte address.
  74. * The data field is composed of memory loadable data.
  75. *
  76. * S2 Record. The type of record field is 'S2' (0x5332). The address field is intrepreted as a 3-byte address.
  77. * The data field is composed of memory loadable data.
  78. *
  79. * S3 Record. The type of record field is 'S3' (0x5333). The address field is intrepreted as a 4-byte address.
  80. * The data field is composed of memory loadable data.
  81. *
  82. * S5 Record. The type of record field is 'S5' (0x5335). The address field is intrepreted as a 2-byte value and
  83. * contains the count of S1, S2, and S3 records previously transmitted. There is no data field.
  84. *
  85. * S7 Record. The type of record field is 'S7' (0x5337). The address field contains the starting execution
  86. * address and is intrepreted as 4-byte address. There is no data field.
  87. *
  88. * S8 Record. The type of record field is 'S8' (0x5338). The address field contains the starting execution
  89. * address and is intrepreted as 3-byte address. There is no data field.
  90. *
  91. * S9 Record. The type of record field is 'S9' (0x5339). The address field contains the starting execution
  92. * address and is intrepreted as 2-byte address. There is no data field.
  93. *
  94. * EXAMPLE
  95. *
  96. * Shown below is a typical S-record format file.
  97. *
  98. * S00600004844521B
  99. * S1130000285F245F2212226A000424290008237C2A
  100. * S11300100002000800082629001853812341001813
  101. * S113002041E900084E42234300182342000824A952
  102. * S107003000144ED492
  103. * S5030004F8
  104. * S9030000FC
  105. *
  106. * The file consists of one S0 record, four S1 records, one S5 record and an S9 record.
  107. * The S0 record is comprised as follows:
  108. * S0 S-record type S0, indicating it is a header record.
  109. * 06 Hexadecimal 06 (decimal 6), indicating that six character pairs (or ASCII bytes) follow.
  110. * 00 00 Four character 2-byte address field, zeroes in this example.
  111. * 48 44 52 ASCII H, D, and R - "HDR".
  112. * 1B The checksum.
  113. *
  114. * The first S1 record is comprised as follows:
  115. *
  116. * S1 S-record type S1, indicating it is a data record to be loaded at a 2-byte address.
  117. * 13 Hexadecimal 13 (decimal 19), indicating that nineteen character pairs, representing a 2 byte address,
  118. * 16 bytes of binary data, and a 1 byte checksum, follow.
  119. * 00 00 Four character 2-byte address field; hexidecimal address 0x0000, where the data which follows is
  120. * to be loaded.
  121. * 28 5F 24 5F 22 12 22 6A 00 04 24 29 00 08 23 7C Sixteen character pairs representing the actual binary
  122. * data.
  123. * 2A The checksum.
  124. *
  125. * The second and third S1 records each contain 0x13 (19) character pairs and are ended with checksums of
  126. * 13 and 52, respectively. The fourth S1 record contains 07 character pairs and has a checksum of 92.
  127. *
  128. * The S5 record is comprised as follows:
  129. *
  130. * S5 S-record type S5, indicating it is a count record indicating the number of S1 records
  131. * 03 Hexadecimal 03 (decimal 3), indicating that three character pairs follow.
  132. * 00 04 Hexadecimal 0004 (decimal 4), indicating that there are four data records previous to this record.
  133. * F8 The checksum.
  134. *
  135. * The S9 record is comprised as follows:
  136. *
  137. * S9 S-record type S9, indicating it is a termination record.
  138. * 03 Hexadecimal 03 (decimal 3), indicating that three character pairs follow.
  139. * 00 00 The address field, hexadecimal 0 (decimal 0) indicating the starting execution address.
  140. * FC The checksum.
  141. *
  142. *********************************************************************************************************
  143. */
  144. #ifndef SRECORD_H
  145. #define SRECORD_H
  146. /*
  147. *********************************************************************************************************
  148. * INCLUDE
  149. *********************************************************************************************************
  150. */
  151. #include <stddef.h>
  152. /*
  153. *********************************************************************************************************
  154. * TYPE DEFINE
  155. *********************************************************************************************************
  156. */
  157. typedef unsigned char INT8U;
  158. typedef unsigned long INT32U;
  159. /*
  160. *********************************************************************************************************
  161. * CONSTANT
  162. *********************************************************************************************************
  163. */
  164. // S-record only has defined type S0,S1,S2,S3,S5,S7,S8,S9
  165. enum {
  166. SRECORD_TYPE_S0 = 0,
  167. SRECORD_TYPE_S1 = 1,
  168. SRECORD_TYPE_S2 = 2,
  169. SRECORD_TYPE_S3 = 3,
  170. // SRECORD_TYPE_S4,
  171. SRECORD_TYPE_S5 = 5,
  172. // SRECORD_TYPE_S6,
  173. SRECORD_TYPE_S7 = 7,
  174. SRECORD_TYPE_S8 = 8,
  175. SRECORD_TYPE_S9 = 9,
  176. };
  177. #define SRECORD_DATA_MAXCHARCNT 64
  178. #define SRECORD_DATA_MAXLENGTH (SRECORD_DATA_MAXCHARCNT / 2)
  179. /*
  180. *********************************************************************************************************
  181. * ERROR CODE
  182. *********************************************************************************************************
  183. */
  184. enum{
  185. SREC_ERR_NO, // if success
  186. SREC_ERR_FORMAT, // if error in format, e.g. first char is not 'S', char in DATA is not digit.
  187. SREC_ERR_TYPE, // if type of record is invalid
  188. SREC_ERR_CHECKSUM, // if the checksum is wrong.
  189. SREC_ERR_TOOSHORT, // if the length of the record is too short
  190. SREC_ERR_TOOLONG, // if the length of the record is too long
  191. };
  192. /*
  193. *********************************************************************************************************
  194. * TYPE DEFINITION
  195. *********************************************************************************************************
  196. */
  197. typedef struct {
  198. INT8U RecType;
  199. INT8U DataLen;
  200. INT32U LoadAddr;
  201. INT8U Data[SRECORD_DATA_MAXLENGTH]; // hold datas that has been converted from hex char
  202. } SRECORD_STRUCT;
  203. /*
  204. *********************************************************************************************************
  205. * PUBLIC FUNCTION
  206. *********************************************************************************************************
  207. */
  208. INT8U SRecord_StrToRec(INT8U *buf,SRECORD_STRUCT *result);
  209. INT8U SRecord_RecToStr(SRECORD_STRUCT *srec,INT8U *result);
  210. #endif
  211. /*
  212. *********************************************************************************************************
  213. *
  214. *
  215. * Motorola S-records Support packetage
  216. * S-records 支持包
  217. *
  218. * File : SRecord.c
  219. * By : Lin Shijun(http://blog.csdn.net/lin_strong)
  220. * Date : 2018/03/03
  221. * Version : V1.0
  222. * Note :
  223. *
  224. *********************************************************************************************************
  225. */
  226. #include <ctype.h>
  227. #include "SRecord.h"
  228. /*
  229. *********************************************************************************************************
  230. * LOCAL FUNCTION DECLARATION
  231. *********************************************************************************************************
  232. */
  233. // description: convert hex char pair to coresponding byte.
  234. // argument: buf pointer to the char waiting for convert.
  235. // perr return the err
  236. // 0 if success
  237. // 1 if find not xdigit char
  238. // return: the converted value.
  239. static INT8U GetHexByte(INT8U *buf,INT8U *perr);
  240. // description: convert the byte to coresponding hex char pair.
  241. // argument: val the byte to convert
  242. // buf pointer to the buffer to hold the char pair, buffer should have
  243. // two place at least.
  244. // return: pointer to the buf+2
  245. static INT8U* PutHexCharPair(INT8U val,INT8U *buf);
  246. /*
  247. *********************************************************************************************************
  248. * SRecord_RecToStr()
  249. *
  250. * Description : try to convert the string to the S-record. 试着转换字符串为对应的S-record
  251. *
  252. * Arguments : buf pointer to the buffer that holds input. 指向要转换的字符串
  253. * result return the result,if success; 返回成功转换后的S-record
  254. *
  255. * return : SREC_ERR_NO if success.
  256. * SREC_ERR_FORMAT if error in format, e.g. first char is not 'S', char in DATA is
  257. * not digit.
  258. * SREC_ERR_TYPE if type of record is invalid(i.e. S4 or S6)
  259. * SREC_ERR_CHECKSUM if the checksum is wrong.
  260. * SREC_ERR_TOOSHORT if the length of the record is too short(according to the LENGTH field)
  261. * SREC_ERR_TOOLONG if the length of the record is too long(according to the LENGTH field)
  262. *
  263. * Note(s): 1. for the S0 record ,this function just simply mark the result->RecType as S0,
  264. * then return; with no further resolution.
  265. * 对于S0记录,这个函数仅仅直接标记result->RecType为S0,然后就返回了,不做进一步解析。
  266. * 2. there is no need for string to be ended with '\0', for the length of the S-record is
  267. * given in the LENGTH field, and thanks to the CKECKSUM, if anything is wrong, it's
  268. * probobly been found.
  269. * 实际上输入字符串没必要用'\0'来结尾,因为是根据S-record的LENGTH字段得知记录长度的,如果出了
  270. * 任何问题,几乎都能通过校验码来发现。(除非正好让校验码通过了,这概率太小忽略不计)
  271. *********************************************************************************************************
  272. */
  273. INT8U SRecord_StrToRec(INT8U *buf,SRECORD_STRUCT *result){
  274. INT8U c,checksum,err;
  275. INT8U i,bytesLeftCnt,addrBytesCnt;
  276. INT8U *rstBuf;
  277. INT32U address;
  278. // TYPE field
  279. if(*(buf++) != 'S')
  280. return SREC_ERR_FORMAT;
  281. c = *(buf++) - '0';
  282. if(c > 9)
  283. return SREC_ERR_FORMAT;
  284. if(c == 4 || c == 6)
  285. return SREC_ERR_TYPE;
  286. result->RecType = c;
  287. if(c == SRECORD_TYPE_S0)
  288. return SREC_ERR_NO;
  289. // LENGTH field
  290. bytesLeftCnt = GetHexByte(buf,&err);
  291. if(err != 0)
  292. return SREC_ERR_FORMAT;
  293. buf += 2;
  294. checksum = bytesLeftCnt;
  295. // here, c == TYPE, addrBytesCnt == length of ADDRESS
  296. if(c == SRECORD_TYPE_S1 ||c == SRECORD_TYPE_S5 || c== SRECORD_TYPE_S9){
  297. addrBytesCnt = 2;
  298. }else if (c == SRECORD_TYPE_S2 || c == SRECORD_TYPE_S8){
  299. addrBytesCnt = 3;
  300. }else{ // if (c == SREC_TYPE_S3 || c == SREC_TYPE_S7)
  301. addrBytesCnt = 4;
  302. }
  303. if(bytesLeftCnt <= addrBytesCnt) // at least a CHECKSUM field
  304. return SREC_ERR_TOOSHORT;
  305. bytesLeftCnt -= addrBytesCnt;
  306. // only S1,S2,S3 use Data field
  307. if (c > SRECORD_TYPE_S3){
  308. // only a CHECKSUM field
  309. if(bytesLeftCnt > 1)
  310. return SREC_ERR_TOOLONG;
  311. }else{
  312. // only S1,S2,S3 use Data field
  313. if(bytesLeftCnt > SRECORD_DATA_MAXLENGTH + 1)
  314. return SREC_ERR_TOOLONG;
  315. }
  316. address = 0;
  317. // ADDRESS field
  318. for(i = 0; i < addrBytesCnt; i++){
  319. c = GetHexByte(buf,&err);
  320. if(err != 0)
  321. return SREC_ERR_FORMAT;
  322. buf += 2;
  323. address = (address << 8) + c;
  324. checksum += c;
  325. }
  326. result->LoadAddr = address;
  327. result->DataLen = bytesLeftCnt - 1;
  328. rstBuf = result->Data;
  329. // DATA field
  330. for(i = 0; i < bytesLeftCnt - 1; i++){
  331. c = GetHexByte(buf,&err);
  332. if(err != 0)
  333. return SREC_ERR_FORMAT;
  334. buf += 2;
  335. *(rstBuf++) = c;
  336. checksum += c;
  337. }
  338. // CHECKSUM field
  339. c = ~GetHexByte(buf,&err);
  340. if(err != 0)
  341. return SREC_ERR_FORMAT;
  342. if(checksum != c)
  343. return SREC_ERR_CHECKSUM;//*/
  344. return SREC_ERR_NO;
  345. }
  346. /*
  347. *********************************************************************************************************
  348. * SRecord_RecToStr()
  349. *
  350. * Description : Convert the S-record to the string(end with '\0').
  351. *
  352. * Arguments : srec the S-record to be converted; 要转换的S-record
  353. * result pointer to the buffer that holds result. 指向存放结果的缓冲区
  354. *
  355. * return : SREC_ERR_NO if success.
  356. * SREC_ERR_TYPE if type of record is invalid(i.e. S0,S4 or S6 and > 9)
  357. * SREC_ERR_TOOLONG if the length of the record is too long(according to the LENGTH field)
  358. *
  359. * Note(s): 1. this function don't support S0, though it's defined in the standard.
  360. * 虽然标准里有S0,但这个函数不支持转换它
  361. * 2. user should make sure that the result buffer is big enough.
  362. * 用户需要自己确保保存结果的缓冲区足够大
  363. *********************************************************************************************************
  364. */
  365. INT8U SRecord_RecToStr(SRECORD_STRUCT *srec,INT8U *result){
  366. INT8U c,checksum = 0;
  367. INT8U len,i,addrBytesCnt;
  368. c = srec->RecType;
  369. // 不支持转换S0
  370. if( c > 9 || c == 4 || c == 6 || c == SRECORD_TYPE_S0)
  371. return SREC_ERR_TYPE;
  372. // TYPE field
  373. *result++ = 'S';
  374. *result++ = c + '0';
  375. if(c == SRECORD_TYPE_S1 ||c == SRECORD_TYPE_S5 || c== SRECORD_TYPE_S9){
  376. addrBytesCnt = 2;
  377. }else if (c == SRECORD_TYPE_S2 || c == SRECORD_TYPE_S8){
  378. addrBytesCnt = 3;
  379. }else{ // if (c == SREC_TYPE_S3 || c == SREC_TYPE_S7)
  380. addrBytesCnt = 4;
  381. }
  382. if(srec->DataLen > SRECORD_DATA_MAXLENGTH)
  383. return SREC_ERR_TOOLONG;
  384. // 只有S1,S2,S3使用数据字段
  385. // LENGTH field
  386. if(c == SRECORD_TYPE_S1 || c == SRECORD_TYPE_S2 || c == SRECORD_TYPE_S3)
  387. len = srec->DataLen + addrBytesCnt + 1; // ADDRESS + DATA + CHECKSUM
  388. else
  389. len = addrBytesCnt + 1; // ADDRESS + CHECKSUM
  390. result = PutHexCharPair(len,result);
  391. checksum += len;
  392. // ADDRESS field
  393. for(i = addrBytesCnt; i > 0 ; i--){
  394. c = (INT8U)(srec->LoadAddr >> ((i - 1) * 8));
  395. result = PutHexCharPair(c,result);
  396. checksum += c;
  397. }
  398. len -= addrBytesCnt;
  399. // DATA field
  400. for(i = 0;i < len - 1; i++){ // the least is the check sum
  401. c = srec->Data[i];
  402. result = PutHexCharPair(c,result);
  403. checksum += c;
  404. }
  405. // CHECKSUM field
  406. result = PutHexCharPair(~checksum,result);
  407. *result = '\0';
  408. return SREC_ERR_NO;
  409. }
  410. /*
  411. *********************************************************************************************************
  412. * LOCAL FUNCTION DEFINITION
  413. *********************************************************************************************************
  414. */
  415. // description: convert hex char pair to coresponding byte.
  416. // argument: buf pointer to the char waiting for convert.
  417. // perr return the err
  418. // 0 if success
  419. // 1 if find not xdigit char
  420. // return: the converted value.
  421. static INT8U GetHexByte(INT8U *buf,INT8U *perr)
  422. {
  423. INT8U c,rst;
  424. *perr = 0;
  425. c = *(buf++); //get an ASCII hex byte
  426. if (!isxdigit(c)){ //is it a valid hex digit
  427. *perr = 1; //no. return an error
  428. return 0;
  429. }
  430. //convert the ASCII character
  431. rst = isdigit(c) ? (c - '0') : (c - 'A' + 10);
  432. c = *buf; //get next ASCII hex byte
  433. if (!isxdigit(c)){ //is it a valid hex digit
  434. *perr = 1; //no. return an error
  435. return rst;
  436. }
  437. //convert the ASCII character
  438. rst = (isdigit(c) ? (c - '0') : (c - 'A' + 10)) + (rst << 4);
  439. return(rst); //return 'no error'
  440. }
  441. static INT8U* PutHexCharPair(INT8U val,INT8U *buf)
  442. {
  443. INT8U c;
  444. c = val >> 4;
  445. *buf++ = (c > 9) ? (c - 10 + 'A') : (c + '0') ;
  446. c = val & 0x0F;
  447. *buf++ = (c > 9) ? (c - 10 + 'A') : (c + '0') ;
  448. return buf;
  449. }

总共就两个函数,注释的也很清楚了,就不示例了。
要是使用中发现了什么bug或有什么建议意见,欢迎联系我,谢谢。

另附上S-record格式的说明http://blog.csdn.net/lin_strong/article/details/78521950。

发表评论

表情:
评论列表 (有 0 条评论,378人围观)

还没有评论,来说两句吧...

相关阅读