SWD离线烧写器

本项目使用STM32F103RET6作为主控,基于ST官方CMSIS-DAP项目进行修改,实现STM32F103系列可脱机、可去读保护烧写(因项目暂时只有103系列脱机烧写需求,所以暂时只添加了103的烧写算法)。

GitHub项目地址: OfflineSWD

外观

烧写器正面
烧写器背面


项目功能

1.使用8M FLASH,可使用虚拟U盘向FLASH放置HEX文件或BIN文件进行烧写(BIN较快)
2.可去除目标板的读保护,直接烧写(烧写后可能需要手动重启)
3.支持手动切换模式,使离线烧写器支持DAP仿真
4.安装驱动后可支持虚拟USB转TTL
5.可离线使用串口调试功能(仅支持英文及常用数字符号)

使用教程

功能使用
1.长按SELECT键开机可进入DAP仿真模式,屏幕上显示DAP-CONNECT
2.直接插入电脑USB端口可向离线烧写器放入相关烧写文件,并可使用虚拟串口功能
3.连接目标板开机,选择文件后选择FLASH后即可进行烧写
4.选择“>>”,进入DEBUG MODE后选择ENTER即可进行离线串口调试

项目介绍

SWD接口

SWD接口的全称为Serial Wire Debug(串行调试接口),类似于SPI传输协议。其包含一个数据口(SWDIO)和一个时钟口(SWCLK),数据口用于传输数据,时钟口则用于控制什么时候进行数据传输。SWD调试接口作为一个新兴的调试接口,由于其传输数据速度快,有着烧录速度快,占用IO口少,出错率小等优点。相较于之前流行的JATG调试接口,往往需要占用大量的IO口和板子空间。JTAG接口

CMSIS-DAP

在早期的STM32调试中,我们一般采用的是ST官方的STLink进行程序的烧写和调试,后来ARM开发了一个新兴的项目CMSIS-DAP,CMSIS-DAP是一个ARM开源的一个调试器项目,其支持所有Cortex-A/R/M的元器件,采用了HID连接,可实现无驱动连接电脑并进行调试。CMSIS-DAP的理解可以分为两部分,CMSIS和DAP。CMSIS全称为ARMCortex-M SoftWare Interface Standard,是一种ARM的软件接口标准;而DAP全程为Debug Access Port,即软件调试访问口,CMSIS-DAP可以理解为ARM开发的一种软件调试接口,可以使用JATG或SWD的连接方式连接到ARM下的Cortex-A/R/M系列元器件。CMSIS-DAP支持包含一个或多个Cortex处理器的目标设备。也就是说CMSIS-DAP支持多设备同时烧写。
CMSIS-DAP连接框图

DAP协议

离线烧写器的实现主要通过CMSIS-DAP里的DAP连接协议来进行实现,这一部分ARM官方已经帮我们写好了,不需要像之前一样研究J-Link一样拿一个笨重的示波器一部分一部分进行研究协议,效率又慢,过程极为的痛苦。DAP烧写主要分为几个过程:初始化DAP–>DAP连接芯片–>确认连接方式–>清除目标板读保护(可忽略)–>清除目标板Flash–>烧写程序–>复位运行。而清除都保护则是进行调用Keil烧写算法中的OPT进行目标FLASH读保护寄存器的清除。
OPT编程算法
这里的编程算法是由FLM文件生成的,由以下开源库进行文件转换:

FlashAlgo

该编程算法为Python编写而成,需要其他支持库支持,这里提供Pflash_algo.py文件打包exe后的文件下载。

FlashAlgo EXE文件下载

提取码:g0p3


由于烧写算法的可多样性,理论可实现全平台的离线烧写支持。只需通过手动切换烧写算法即可达到该目的,后期考虑迭代升级时实现。

项目框架及实现

项目框架主要为4个部分,CMSIS-DAP模块,虚拟u盘、串口模块,离线调试模块和离线烧写器部分。当初始化识别到选择按钮按下时进行CMSIS-DAP模块的初始化,并进行CMSIS-DAP进程。

if(Scan_Key() == 1 ){ //按住SELTCT开机进入CMSIS-DAP模式
		Draw_Logo(); //绘制LOGO
		OLED_DrawBMP(0,0,34,34,USBLogo); //绘制图标
		OLED_ShowString(38,1,"DAP Connect",1,0); //绘制提示词
		Init_DAPUSB(); //初始化DAP
		Do_DAPUSB(); //DAP循环
	}

其他情况下,如果插入USB,屏幕上则会显示USB Connect,进入USB连接进程。

      Set_System(); //设置USB系统
	  Set_USBClock(); //设置USB时钟
	  USB_Interrupts_Config(); //设置USB中断
	  USB_Init(); //初始化USB

USB连接中使用了复合USB设备连接,在请求设备标识号时同时请求了大容量存储设备以及串口设备两个设备,同时开启了四个EP进程进行数据交换,其中EP1和EP2进行大容量存储设备的数据交换,EP4和EP6用于进行串口数据的交换。

void EP1_IN_Callback(void)
{
  Mass_Storage_In();
}

void EP2_OUT_Callback(void)
{
  Mass_Storage_Out();
}

void EP6_OUT_Callback(void)
{
	int i = 0;

// tf("\r\nEP6_OUT_Callback\r\n");

  /* 清空上一次操作 */
  for(i=0; i<temp_cnt; i++)
  {
  	buffer_out[i] = 0;
  }

  /* 接收 */
  count_out = GetEPRxCount(ENDP6);
  PMAToUserBufferCopy(buffer_out, ENDP6_RXADDR, count_out);
  SetEPRxValid(ENDP6);

  temp_cnt = 	count_out;
	
  usart_sendCommand(buffer_out,temp_cnt);
}

void EP4_IN_Callback(void)
{
  count_in = 0;
}

离线烧写的核心由FATFS文件系统组成,由FATFS进行文件读取,选取目标文件后进行读出,并调用相关烧写方法进行烧写。由于文件烧写的核心是使用BIN文件进行烧写,所以遇到HEX文件需要先进行解析。由于BIN文件烧写时我们是分段进行烧写,每1024B进行一次FLASH的写入,HEX文件由于包含着烧写起始地址以及数据类型和校验位,需要先进行解析才能进行烧写。暂时我采用了简单粗暴的方式,将HEX文件全部进行转换为BIN文件再进行烧写,但是这种方式由于FATFS文件系统在STM32上运行的速度较慢,造成转换的速度较慢(有待优化)。

char HexFormatUncode(unsigned char *File)
{
	  FIL fp_hex,fp_bin;
    u8 a,b = 0;
	  char buff[64] = "" ;

    unsigned char length = 0;
    unsigned short offset = 0;  //0~65535
    unsigned char type = 0;
    unsigned char checksum = 0;
    unsigned char i = 0;
	  f_open(&fp_hex, (const TCHAR*)File,FA_READ);
	  f_open(&fp_bin, (const TCHAR*)"write.bin",FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
		u8 WaitTips[] = "...";
		OLED_ShowString(45,2,"INVER",1,1);
    while(f_gets(buff,64,&fp_hex)!=NULL){
		a++;
		if(a==100){
			OLED_ShowChar(75+b*6,2,WaitTips[b],1);
			b++;
			if(b>3){ 
				b=0;
				OLED_ShowString(75,2,"     ",1,1);
			}
			a=0;
		}
 
    if(buff[0] != ':') return 0;
    else
    {
        length=Char2toByte(&buff[1]);
        offset=Char2toByte(&buff[3])*256+Char2toByte(&buff[5]);
        type=Char2toByte(&buff[7]);
        if(type==0)
        {
            f_lseek(&fp_bin,offset);
            for(i=0; i<strlen(buff); i++)
                f_putc(Char2toByte(&buff[9+2*i]), &fp_bin);
        }

    }
	}
	OLED_ShowString(45,2,"          ",1,1);
	f_close(&fp_bin);
	f_close(&fp_hex);
	return 1;
}

离线调试功能则是整个工程中最简单的一部分,直接将串口接收缓冲区里的数据时刻使用屏幕进行显示出来,由于本工程使用了DMA模式发送和接收串口数据,需要显示以及虚拟串口回传时只需在DMA接收时进行相关处理即可。

void usart_receive_parse(u8 *shell_string)
{
	UserToPMABufferCopy((unsigned char *)shell_string, ENDP4_TXADDR,uart1_rev_len);
  SetEPTxCount(ENDP4, uart1_rev_len);
  SetEPTxValid(ENDP4);
	if(debugMode){
		OLED_ShowString(0,-1,"                     ",1,1);
		OLED_ShowString(0,0,"                     ",1,1);
		OLED_ShowString(0,1,"                     ",1,1);
		OLED_ShowString(0,-1,shell_string,1,1);
	}
}
后续项目优化

项目目前主要有两个比较大的不足需要优化,一是HEX文件转换的时间较长,大约在5-10秒左右,可能造成用户体验不佳,后续考虑想办法在读取的同时进行转换并进行烧写,并每次烧录定长数据段,以达到快速烧写的方法。第二个不足是离线烧写只添加了F103系列的烧写算法,因为手头上开发板没有那么多,无法测试其他MCU是否能够成功,如果有需要的话会添加其他如F407系列MCU的离线烧写算法,并添加切换功能。

相关演示

DAP仿真功能

DAP连接状态
DAP上位机连接信息

虚拟串口及U盘模拟

USB连接状态
虚拟串口
放置烧写文件

离线文件烧写

离线烧写界面
HEX文件转换
HEX文件烧写
BIN文件烧写

离线串口调试功能

离线串口界面
离线串口调试

项目意见

如果项目有问题或者有新功能需求,欢迎向作者提交意见!
欢迎加入QQ交流群 群号:912015729
Logo

鸿蒙生态一站式服务平台。

更多推荐