在嵌入式程序开发中,除了与hex文件接触得比较多外,也遇到过不少是用mot文件来进行程序烧写的。我们所说的mot文件实际上就是Motorola S-records文件,是摩托罗拉公司定义的一种S开头的数据记录文件格式。
如下图所示:
平时该格式文件通过专门的烧录工具进行程序烧写,无需知道里面数据的含义,但是当想对接串口、can口等外设进行程序升级时,我们就必须明白mot文件中数据的含义了,只有知道数据的含义,才能提取其关键的信息进行程序开发。
1.格式介绍:
.mot文件每行以‘S’字符开头,第二个字符为数字字符,决定了该行数据的类型,之后的数据为二进制数据转换为hex字符串形式表示,可视为字节数组。
2.数据解析
解析一行数据的代码可参考: https://blog.csdn.net/lin_strong/article/details/79448091
但是仔细分析数据后发现,多行数据其实是一个连续的数据块,将其按数据块解析,将会更加便于我们使用数据。
如下为自行编写的解析代码,若存在问题或bug请留言或与我联系,万分感谢~
mot_file_info.c
/**
* @file mot_file_info.c
* @brief MOT文件数据解析模块
* @author fangye ([email protected])
* @date 2020-07-14
*/
#include
#include
#include
#include "mot_file_info.h"
static MOT_INFO_HEAD *s_mot_info_head = NULL;
static MOT_INFO_NODE *s_node_ptr = NULL;
/**
* @brief 获取MOT文件链表信息指针
* @return 链表头指针
*/
MOT_INFO_HEAD * get_mot_info()
{
return s_mot_info_head;
}
/**
* @brief 初始化链表头
*/
static void init_mot_info_list()
{
s_mot_info_head = (MOT_INFO_HEAD *)malloc(sizeof(MOT_INFO_HEAD));
s_mot_info_head->run_addr = 0;
s_mot_info_head->node_num = 0;
s_mot_info_head->next = NULL;
}
/**
* @brief 添加数据至链表中
* 连续的数据块存储在同一节点上
* @param data_addr 数据写入地址
* @param data 数据内容
* @param len 数据长度
* @return int
* - -1:失败
* - 0:成功
*/
static int add_data_info_to_list(unsigned int data_addr, char *data, unsigned int len)
{
if( data == NULL )
return -1;
if(s_mot_info_head == NULL)
init_mot_info_list();
if(s_mot_info_head->next == NULL) //如果头节点为空
{
s_mot_info_head->next = (MOT_INFO_NODE *)malloc(sizeof(MOT_INFO_NODE));
s_mot_info_head->next->write_addr = data_addr;
s_mot_info_head->next->data_len = len;
s_mot_info_head->next->data = (char *)malloc(len*sizeof(char));
memcpy(s_mot_info_head->next->data, data, len); //保存数据
s_mot_info_head->next->next = NULL;
s_mot_info_head->node_num++; //节点数增加
}
else //节点有数据
{
s_node_ptr = s_mot_info_head->next;
while(s_node_ptr->next != NULL)
{
s_node_ptr = s_node_ptr->next; //找到最新的数据节点
}
if(data_addr == s_node_ptr->write_addr + s_node_ptr->data_len) //如果为连续数据
{
s_node_ptr->data = (char *)realloc(s_node_ptr->data, (s_node_ptr->data_len + len)*sizeof(char)); //重新申请内存
memcpy(&s_node_ptr->data[s_node_ptr->data_len], data, len); //拷贝数据至末尾
s_node_ptr->data_len += len;
}
else //如果为不连续数据
{
s_node_ptr->next = (MOT_INFO_NODE *)malloc(sizeof(MOT_INFO_NODE));
if(s_node_ptr->next == NULL)
printf("malloc next fail!\n");
s_node_ptr = s_node_ptr->next;
s_mot_info_head->node_num++; //节点数增加
s_node_ptr->write_addr = data_addr;
s_node_ptr->data_len = len;
s_node_ptr->data = (char *)malloc(s_node_ptr->data_len*sizeof(char));
if(s_node_ptr->data == NULL)
printf("malloc data fail!\n");
memcpy(s_node_ptr->data, data, len); ///< 保存数据
s_node_ptr->next = NULL;
}
#if 0
printf("data_len[%d] addr[0x%08X]: ",len, data_addr);
int i = 0;
for(i = 0; irun_addr = run_addr;
return 0;
}
/**
* @brief 单字节hex字符转数字
* @param hex hex字符
* @return int
* - -1:失败
* - >=0:成功
*/
static int CharToInt(char hex)
{
if (hex>='0' && hex <='9')
return hex - '0';
if (hex>='A' && hex <= 'F')
return hex-'A'+10;
if(hex>='a' && hex <= 'f')
return hex-'a'+10;
return -1;
}
/**
* @brief 两字节hex字符串转数字
* @param hex 两字节hex字符串
* @return int
* - >=0:成功
* - <0:失败
*/
static int StringToInt(const char *hex)
{
int high = CharToInt(hex[0]);
int low = CharToInt(hex[1]);
if(high != -1 && low != -1)
return high*0x10 + low;
else
{
printf("---StringToInt error!! %s\n",hex);
return -1;
}
}
/**
* @brief 字符串hex数据转字节数组
* @param mot_line_buff Mot文件字符串数据
* @param type 数据类型
* @param data 转换后数据内容
* @param data_len 转换后数据内容长度
* @return int
* - -1:转换失败
* - 0:转换成功
*/
static int str2bytes(const char *mot_line_buff, int *type, char *data, int *data_len)
{
if( mot_line_buff == NULL)
return -1;
int len = strlen(mot_line_buff);
if(len %2 != 0)
return -1;
*type = mot_line_buff[1] - '0';
int i = 0;
int length = 0;
len -= 2; ///< 减去换行符/r/n两个字节
for(i = 2; i < len; i += 2 )
{
int ret = StringToInt(&mot_line_buff[i]);
if(ret >= 0)
{
data[length] = ret;
length++;
}
else
{
return -1;
}
}
*data_len = length;
return 0;
}
/**
* @brief 检测数据合法性
* @param data 数据内容
* @param len 数据长度
* @return int
* - 0:非法
* - 1:合法
*/
static int is_data_avaliable(unsigned char *data, int len)
{
if(data == NULL)
return 0;
int i = 0;
unsigned char add_sum = data[0];
for(i=1; inext != NULL)
{
free_all_node(node->next);
}
free(node->data);
node->data = NULL;
free(node);
node = NULL;
}
/**
* @brief 释放mot信息链表
* @param header 链表头指针
*/
void free_mot_info_list(MOT_INFO_HEAD *header)
{
if( header->next )
{
free_all_node(header->next);
}
free(header);
header = NULL;
}
/**
* @brief 加载mot文件信息
* @param mot_filepath mot文件路径
* @return int
* - 0: 成功
* - -1:失败
*/
int load_mot_file(const char *mot_filepath)
{
if( mot_filepath == NULL)
return -1;
FILE *fp = fopen(mot_filepath,"r");
if(fp)
{
char buff[512] = {0};
while( fgets(buff, sizeof(buff), fp) != NULL) //每次读取一行
{
char line_data[512]={0};
int line_data_len = 0;
int type = 0;
if(0 == str2bytes(buff, &type, line_data, &line_data_len)) //转为数组
{
if( is_data_avaliable(line_data, line_data_len) ) //校验数据合法性
{
unsigned int addr = 0;
char bin_data[512]={0};
int data_len = 0;
if(parse_a_line(line_data, line_data_len, type, &addr, bin_data, &data_len))//解析一行数据
{
if(type == 1 || type == 2 || type == 3) //数据地址及数据内容
{
add_data_info_to_list(addr, bin_data, data_len);
}
else if(type == 7 || type == 8 || type == 9) //运行地址
{
add_run_info_to_head(addr);
}
}
else //解析失败
{
printf("parse a line data fail!\n");
return -1;
}
}
else //数据校验失败
{
printf("mot data is error!\n");
return -1;
}
}
else //转换为数组失败
{
printf("a line str2bytes error!\n");
return -1;
}
memset(buff,0,sizeof(buff));
}
fclose(fp);
fp = NULL;
}
else
{
printf("open mot file %s fail!\n",mot_filepath);
return -1;
}
return 0;
}
mot_file_info.h
/**
* @file mot_file_info.h
* @brief .MOT文件数据解析模块头文件
* @author fangye ([email protected])
* @date 2020-07-14
*/
#ifndef __MOT_FILE_INFO_H__
#define __MOT_FILE_INFO_H__
#ifdef __cplusplus
extern "C"{
#endif /* __cplusplus */
/**
* @brief mot文件数据块节点
*/
typedef struct _MOT_INFO_NODE
{
unsigned int write_addr; ///< 数据写入地址
unsigned int data_len; ///< 数据长度
char *data; ///< 数据内容
struct _MOT_INFO_NODE *next; ///< 下一段地址不连续的数据内容
}MOT_INFO_NODE;
/**
* @brief mot文件信息链表头
*/
typedef struct _MOT_INFO_HEAD
{
unsigned int run_addr; ///< 程序运行地址
unsigned int node_num; ///< 地址段节点个数
struct _MOT_INFO_NODE *next; ///< 链表头节点指针
}MOT_INFO_HEAD;
extern int load_mot_file(const char *mot_filepath);
extern MOT_INFO_HEAD *get_mot_info();
extern void free_mot_info_list(MOT_INFO_HEAD *header);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __MOT_FILE_INFO_H__ */
main.c
#include
#include
#include "mot_file_info.h"
int main(int argc, char *argv[])
{
if(argc != 2)
return 0;
int ret = load_mot_file(argv[1]);
if(ret == 0)
{
printf("load mot file success!!\n");
MOT_INFO_HEAD *mot_info = get_mot_info();//获取mot文件信息
printf("--- run_addr = 0x%08X node_num = %d ---\n", mot_info->run_addr, mot_info->node_num);
MOT_INFO_NODE *node_ptr = mot_info->next;
if(node_ptr)
{
do
{
printf("write_addr = 0x%08X data_len = 0x%08X\n", node_ptr->write_addr, node_ptr->data_len);
node_ptr = node_ptr->next;
}
while(node_ptr);
}
free_mot_info_list(mot_info); ///< 释放链表
}
return 0;
}
执行结果:
如图所示上图所示:
能够直接链表头获取到整个程序运行的起始地址、不连续数据块个数 、每个数据块的大小和写入的起始地址。