当前位置:   article > 正文

SWD脱机下载器的简易实现(一)_swd下载器

swd下载器

前言:

本文主要参考:HEX文件格式详解_bollxin的博客-CSDN博客_hex文件地址

应公司需求,通过笔者半个月的努力,设计并实现了一个简易的脱机下载器。主要功能为通过一片MCU(以下称为:源MCU)在只有电源的情况下,向另一片MCU(以下称为:目标MCU)进行程序烧录(以下称为:目标程序),并且限制下载次数和下载时间。实现脱机下载器主要分为以下几个步骤:

1、解析HEX文件

2、将HEX文件导入源MCU

3、通过SWD协议将目标程序烧录到目标MCU。

一、解析HEX文件

HEX文件是通过编译器(例如Keil5)编译源代码生成的十六进制文件。一般来说HEX文件是不能直接拿来使用的,需要通过解析后烧录到目标MCU才能正常运行。下面笔者将简单介绍一下HEX文件的格式与解析。

HEX文件的解析笔者是在电脑端完成的,先打开一个HEX文件,再将读到的HEX文件在终端以十六进制格式输出,以下就是笔者获得的HEX文件数据。由于数据较长,中间部分省略。

3A 02 00 00 04 10 00 EA
3A 10 10 00 00 B0 38 00 20 69 14 00 10 00 00 00 00 00 00 00 00 4B
3A 10 10 10 00 00 04 01 00 00 01 00 00 00 F0 02 F8 00 F0 3E F8 BA
3A 10 10 20 00 0C A0 30 C8 08 38 24 18 2D 18 A2 46 67 1E AB 46 FD                                              ……
3A 10 4C 20 00 04 E2 1A 04 08 1A 02 10 0A 15 01 EE 01 2A 83 70 20
3A 0C 4C 30 00 17 1E E0 40 03 48 E4 04 C1 02 17 FF 17 
3A 04 00 00 05 10 00 10 19 BE   
3A 00 00 00 01 FF

1、由以上数据不难看出,每一条HEX语句都是由0x3A(也就是字符‘:’)开头,这相当于HEX语句的句柄,读HEX语句一开始就要先检测第一个字符是不是0x3A,否则读取失败,或者自动过滤掉。(天蓝色部分)

2、每条语句的第二个数据表示数据该条HEX语句的数据长度。(橙色部分)

3、第三和第四个数据表示该条语句要写入目标MCU FLash的偏移地址。(蓝紫色部分)

4、第五个数据表示该条HEX语句所保存的数据类型。(红色部分)数据类型分为以下六种。      ① 0x00 表示要存入目标MCU Flash对应地址的数据

② 0x01 表示HEX文件结束的数据,如果读到了0x01类型的数据,表示HEX文件结束了。

③ 0x02 表示扩展段地址。

④ 0x03 表示开始段地址。

⑤ 0x04 表示代码段存储在FLash中的基地址的高16位,以XMC1000系列的MCU为例,其Flash基地址为0x10000000,则上面第一条语句3A 02 00 00 04 10 00 EA表示XMC1300的Flash基地址为(0x1000 << 16)也就是0x10000000。如果是STM32 的话则第一条语句为3A 02 00 00 04 08 00 F2表示STM32的的Flash基地址为(0x0800 << 16)也就是0x08000000。

⑥ 0x05 表示程序开始执行地址,以3A 04 00 00 05 10 00 10 19 BE为例,表示程序从0x10001019开始执行。

5、第六个~倒数第二个数据,表示要写入的数据内容,如果数据长度为0则没有数据内容例如上面数据的最后一条(黑色部分)

6、倒数第一个数据表示该条HEX语句的校验码。(棕色部分)以3A 10 4C 20 00 04 E2 1A 04 08 1A 02 10 0A 15 01 EE 01 2A 83 70 20为例:20 = ~(10 4C 20 00 + 04 + E2 + 1A + 04 + 08 + 1A + 02 + 10 + 0A + 15 + 01 + EE + 01 + 2A + 83 + 70) + 1,(此计算用unsigned char类型,或者只取数据和的低八位)。

综合以上描述以3A 10 10 20 00 0C A0 30 C8 08 38 24 18 2D 18 A2 46 67 1E AB 46 FD 为例,该条HEX语句表示将0C A0 30 C8 08 38 24 18 2D 18 A2 46 67 1E AB 46这些数据写入(0x1000 << 16) | 0x1020地址(地址递增)(0x1000 << 16) | 0x1020 = 0x10001020。

二、HEX文件解析源代码 

由于HEX文件本身是字符串的形式存储的,所以我们要先将读出的字符串转换成unsigned char型。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

char Read_buf[60000] = {0};
unsigned char Read_bufData[128] = {0};
unsigned short Data_Addre = 0; 
int Size = 0;

//将一个字符转换成4位的数据 
char Get_A_CharData(char Achar)
{
    if(Achar > 47 && Achar < 58)
    {
        return (Achar - 48);
    }
    else if(Achar > 64 && Achar < 71)
    {
        return (Achar - 55);
    }
    return 0xFF;
}

//函数作用:合并相邻的字符并转换成十六进制数据
//lenght :要转换的字符个数,必须是偶数个
//buf:存放字符的缓冲区
//buf1:存放转换后数据的缓冲区 
char String_To_Data(int lenght, char buf[256],unsigned char buf1[128])
{
    unsigned char i = 0;
    unsigned char Low_4Bit = 0,high_4Bit = 0;
    if(lenght > 128)
    {
        return -1;
    }
    for(i = 0;i < lenght;i++)
    {
        buf1[i] = 0;
        high_4Bit = Get_A_CharData(buf[i * 2]);
        Low_4Bit = Get_A_CharData(buf[i * 2 + 1]);
        if(high_4Bit == 0xFF || Low_4Bit == 0xFF)
        {
            return -1;    
        }
        buf1[i] = (high_4Bit << 4) | Low_4Bit;
    }
    return i;
}

//解析一条hex语句并在标准输出中输出
char Parsing_A_Statement(char buf[256]) 
{    
    unsigned char i = 0, j = 0;
    unsigned char Check_Code = 0; 
    if(buf[i] == ':')
    {
        printf("3A");
    }
    i++;
    String_To_Data(1, &buf[i],&Read_bufData[0]);//获取数据长度
    i += 2;
    
    String_To_Data(2, &buf[i],&Read_bufData[i/2]);//获取地址 
    i += 4;

    String_To_Data(1, &buf[i],&Read_bufData[i/2]);//获取数据类型 
    i += 2;
        
    String_To_Data(Read_bufData[0], &buf[i],&Read_bufData[i/2]);//获取数据
    i += Read_bufData[0] * 2;
    
    String_To_Data(1, &buf[i],&Read_bufData[i/2]);//获取校验码 
    i += 2;
    for(j=0;j < i/2 ;j++)
    {
        printf(" %02X",Read_bufData[j]);//打印数据 
        if(j < i/2 - 1)
        Check_Code += Read_bufData[j];
    }
    Check_Code = ~Check_Code + 1;
    
    if(Check_Code == Read_bufData[j - 1])//比对数据 
    {
        Size += i/2 + 1;//计算生成数据的大小 
        printf("\n");
        Data_Addre += (j + 1) / 2;
    }
    else
    {
        printf("本条语句解析失败!\n"); 
        return -1;
    }
    return 0;
}

int main()
{
    int re = 0;
    int fd = 0;//文件描述符 
    unsigned char Lenght = 0;
    fd = open("SLBLDC.hex",_O_RDONLY);//以只读的方式打开文件 SLBLDC.hex
    if(fd < 0)
    {
        printf("打开SLBLDC.hex失败\n");
    } 
    while(1)
    {
        re = read(fd,Read_buf,1);//读取一个字节
        if(re <= 0)break;//如果读到的字节数为零表示文件已经读完,小于零则表示出错 
        if(Read_buf[0] == ':')//判断第一个字符是不是':' 
        {
            re = read(fd,&Read_buf[1],8);//读取8个字节
            if(re <= 0)break;
            String_To_Data(1, &Read_buf[1],&Lenght);//获取数据长度,将两个相邻的字符转换成长度数据 
            re = read(fd,&Read_buf[9],Lenght*2 + 3);
            if(re <= 0)break;
            Parsing_A_Statement(Read_buf);//解析并打印一条HEX语句 
        }    
    }
    printf("%d\n",Size);
    close(fd);
    return 0;
}

以上就是HEX文件解析的全部内容,后续内容敬请期待!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/905352
推荐阅读
相关标签
  

闽ICP备14008679号