当您在Keil MDK中点击"Download"或"Load"按钮时,会触发一个完整的CMSIS-DAP协议通信流程。以下是从点击按钮到程序运行 的完整过程,包含每一个DAP_Transfer命令及其具体参数。
第一阶段:连接初始化(约10-15个DAP命令)
1.1 获取DAPLink信息
Keil发送: DAP_Info(0x00) [查询固件版本]
请求包: [0x00] [0x01] [0xF1] [0x00...]
解释: 查询固件版本信息,确认DAPLink设备
响应: [0x00] [0x01] [0x00] ["CMSIS-DAP V2.0"...]
1.2 连接SWD接口
Keil发送: DAP_Connect(0x02) [选择SWD模式]
请求包: [0x02] [0x01] [0x01] [0x00...]
解释: 0x01表示选择SWD模式
响应: [0x02] [0x01] [0x00] [0x01]
1.3 配置SWD时钟频率
Keil发送: DAP_SWJ_Clock(0x0A) [设置4MHz时钟]
请求包: [0x0A] [0x04] [0x00 0x00 0x00 0x3D 0x09 0x00 0x00]
解释: 0x00093D00 = 4,000,000 Hz (4MHz)
响应: [0x0A] [0x01] [0x00]
1.4 配置传输参数
Keil发送: DAP_TransferConfigure(0x04) [优化传输]
请求包: [0x04] [0x03] [0x00] [0x64] [0x00] [0x0A] [0x00]
解释:
空闲周期 = 0x00 (0个空闲周期)
重试次数 = 0x0064 (100次)
匹配重试 = 0x000A (10次)
响应: [0x04] [0x01] [0x00]
1.5 配置SWD时序
Keil发送: DAP_SWD_Configure(0x05) [设置Trn周期]
请求包: [0x05] [0x01] [0x01] [0x00...]
解释: 设置Trn周期为1个时钟周期
响应: [0x05] [0x01] [0x00]
第二阶段:调试端口初始化(约8-12个DAP_Transfer)
2.1 读取DP-IDCODE验证连接
Keil发送: DAP_Transfer(0x07) [读DP-IDCODE]
请求包: [0x07] [0x01] [0x00] [0x0F] [0x00...]
解释:
0x07 - DAP_Transfer命令
0x01 - 1个传输请求
0x00 - DAP索引
0x0F - 传输请求: RnW=1(读), APnDP=0(DP), 地址=0xC(DP_RDBUFF)
响应: [0x07] [0x01] [0x00] [0x00] [0x77] [0x14] [0xA0] [0x2B]
解释: 返回IDCODE 0x2BA01477 (STM32F103)
2.2 清除错误状态
Keil发送: DAP_Transfer(0x07) [写DP-ABORT]
请求包: [0x07] [0x01] [0x00] [0x08] [0x1F] [0x00] [0x00] [0x00]
解释:
0x08 - 写DP, 地址0x0 (DP_ABORT)
0x0000001F - 清除所有错误标志
响应: [0x07] [0x01] [0x00] [0x00]
2.3 配置调试电源
Keil发送: DAP_Transfer(0x07) [写DP-CTRL/STAT]
请求包: [0x07] [0x01] [0x00] [0x09] [0x00] [0x00] [0x00] [0x50]
解释:
0x09 - 写DP, 地址0x4 (DP_CTRLSTAT)
0x50000000 - 设置CDBGPWRUPREQ和CSYSPWRUPREQ
响应: [0x07] [0x01] [0x00] [0x00]
2.4 轮询电源就绪
Keil发送: DAP_Transfer(0x07) [读DP-CTRL/STAT]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x00...]
解释: 0x0D - 读DP, 地址0x4 (DP_CTRLSTAT)
响应: [0x07] [0x01] [0x00] [0x00] [0xF0] [0x00] [0x00] [0x50]
解释: 返回0x500000F0, 确认CDBGPWRUPACK和CSYSPWRUPACK已置位
2.5 选择AP0(MEM-AP)
Keil发送: DAP_Transfer(0x07) [写DP-SELECT]
请求包: [0x07] [0x01] [0x00] [0x08] [0x00] [0x00] [0x00] [0x00]
解释:
0x08 - 写DP, 地址0x8 (DP_SELECT)
0x00000000 - 选择AP0
响应: [0x07] [0x01] [0x00] [0x00]
2.6 配置AP-CSW(32位访问)
Keil发送: DAP_Transfer(0x07) [写AP-CSW]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x12] [0x00] [0x00] [0x23]
解释:
0x0B - 写AP, 地址0x0 (AP_CSW)
0x23000012 - Size=2(32位), AddrInc=1(自动递增)
响应: [0x07] [0x01] [0x00] [0x00]
第三阶段:Flash算法加载(约20-50个DAP_TransferBlock)
3.1 设置RAM起始地址
Keil发送: DAP_Transfer(0x07) [写AP-TAR]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x00] [0x00] [0x00] [0x20]
解释:
0x0D - 写AP, 地址0x4 (AP_TAR)
0x20000000 - RAM起始地址
响应: [0x07] [0x01] [0x00] [0x00]
3.2 批量写入Flash算法(以第一个块为例)
Keil发送: DAP_TransferBlock(0x06) [批量写入算法数据]
请求包: [0x06] [0x40 0x00] [0x00] [0x0B]
[0x00 0x10 0x00 0x20] // 第一个字: 0x20001000
[0x01 0x20 0x00 0x20] // 第二个字: 0x20002001
[0x02 0x30 0x00 0x20] // 第三个字: 0x20003002
... // 总共64个字(256字节)
解释:
0x06 - DAP_TransferBlock命令
0x0040 - 64个传输请求
0x00 - DAP索引
0x0B - 写AP, 地址0x0 (AP_DRW)
响应: [0x06] [0x03] [0x00] [0x40 0x00] [0x00]
解释: 返回64个成功传输
3.3 设置算法参数结构体地址
Keil发送: DAP_Transfer(0x07) [写AP-TAR]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x00] [0x10 0x00 0x20]
解释: 设置AP_TAR = 0x20001000 (参数结构体地址)
响应: [0x07] [0x01] [0x00] [0x00]
3.4 写入算法参数
Keil发送: DAP_TransferBlock(0x06) [写入参数]
请求包: [0x06] [0x06 0x00] [0x00] [0x0B]
[0x00 0x00 0x80 0x08] // device_base = 0x08000000
[0x00 0x00 0x01 0x00] // device_size = 0x00010000 (64KB)
[0x00 0x04 0x00 0x00] // sector_size = 0x00000400 (1KB)
[0x40 0x00 0x00 0x00] // sector_count = 0x00000040 (64)
[0x00 0x01 0x00 0x00] // program_page = 0x00000100 (256字节)
[0x00 0x00 0x7A 0x00] // clock_speed = 0x00007A00 (8MHz)
响应: [0x06] [0x03] [0x00] [0x06 0x00] [0x00]
第四阶段:CPU控制与Flash操作(约15-30个DAP_Transfer)
4.1 停止CPU内核
Keil发送: DAP_Transfer(0x07) [写DHCSR停止CPU]
请求包: [0x07] [0x01] [0x00] [0x0D] [0xF0 0xED 0x00 0xE0]
解释: 设置AP_TAR = 0xE000EDF0 (DHCSR地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写DHCSR值]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x03 0x00 0x5F 0xA0]
解释:
0x0B - 写AP_DRW
0xA05F0003 - C_DEBUGEN=1, C_HALT=1
响应: [0x07] [0x01] [0x00] [0x00]
4.2 验证CPU已停止
Keil发送: DAP_Transfer(0x07) [读DHCSR]
请求包: [0x07] [0x01] [0x00] [0x2F] [0x00...]
解释: 0x2F - 读AP_DRW
响应: [0x07] [0x01] [0x00] [0x00] [0x03 0x00 0x5F 0xA0]
解释: 返回0xA05F0003, 确认CPU已停止
4.3 调用Flash算法初始化函数
Keil发送: DAP_Transfer(0x07) [设置R0=参数地址]
请求包: [0x07] [0x01] [0x00] [0x0D] [0xFC 0xED 0x00 0xE0]
解释: 设置AP_TAR = 0xE000EDFC (DCRDR地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写DCRDR]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x00 0x10 0x00 0x20]
解释: 设置R0 = 0x20001000 (参数地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [设置PC=初始化函数地址]
请求包: [0x07] [0x01] [0x00] [0x0D] [0xFC 0xED 0x00 0xE0]
解释: 设置AP_TAR = 0xE000EDFC (DCRDR地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写PC值]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x00 0x00 0x00 0x20]
解释: 设置PC = 0x20000000 (算法入口地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [清除HALT位运行CPU]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x01 0x00 0x5F 0xA0]
解释: 写DHCSR = 0xA05F0001 (清除C_HALT)
响应: [0x07] [0x01] [0x00] [0x00]
4.4 等待初始化完成(轮询)
Keil发送: DAP_Transfer(0x07) [读DHCSR检查状态]
请求包: [0x07] [0x01] [0x00] [0x2F] [0x00...]
解释: 读取DHCSR检查C_HALT位
响应: [0x07] [0x01] [0x00] [0x00] [0x01 0x00 0x5F 0xA0]
解释: 返回0xA05F0001, C_HALT=0表示CPU在运行
// 循环读取直到C_HALT=1
Keil发送: DAP_Transfer(0x07) [再次读DHCSR]
请求包: [0x07] [0x01] [0x00] [0x2F] [0x00...]
响应: [0x07] [0x01] [0x00] [0x00] [0x03 0x00 0x5F 0xA0]
解释: 返回0xA05F0003, C_HALT=1表示初始化完成
第五阶段:Flash擦除(约5-10个DAP_Transfer)
5.1 解锁Flash(针对STM32)
Keil发送: DAP_Transfer(0x07) [设置Flash KEYR地址]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x04 0x20 0x00 0x40]
解释: 设置AP_TAR = 0x40022004 (FLASH_KEYR地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写KEY1]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x23 0x01 0x56 0x45]
解释: 写KEY1 = 0x45670123
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写KEY2]
请求包: [0x07] [0x01] [0x00] [0x0B] [0xAB 0x89 0xEF 0xCD]
解释: 写KEY2 = 0xCDEF89AB
响应: [0x07] [0x01] [0x00] [0x00]
5.2 执行擦除操作
Keil发送: DAP_Transfer(0x07) [设置Flash CR地址]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x10 0x20 0x00 0x40]
解释: 设置AP_TAR = 0x40022010 (FLASH_CR地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写擦除命令]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x02 0x02 0x00 0x00]
解释: 写FLASH_CR = 0x00000202 (SER=1, STRT=1, SNB=0)
响应: [0x07] [0x01] [0x00] [0x00]
5.3 等待擦除完成(轮询)
Keil发送: DAP_Transfer(0x07) [设置Flash SR地址]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x0C 0x20 0x00 0x40]
解释: 设置AP_TAR = 0x4002200C (FLASH_SR地址)
响应: [0x07] [0x01] [0x00] [0x00]
// 循环读取SR直到BSY位清除
Keil发送: DAP_Transfer(0x07) [读Flash状态]
请求包: [0x07] [0x01] [0x00] [0x2F] [0x00...]
响应: [0x07] [0x01] [0x00] [0x00] [0x01 0x00 0x00 0x00]
解释: 返回0x00000001, BSY=1表示正在擦除
// 继续轮询...
Keil发送: DAP_Transfer(0x07) [再次读Flash状态]
请求包: [0x07] [0x01] [0x00] [0x2F] [0x00...]
响应: [0x07] [0x01] [0x00] [0x00] [0x00 0x00 0x00 0x00]
解释: 返回0x00000000, BSY=0表示擦除完成
第六阶段:Flash编程(大量DAP_TransferBlock)
6.1 设置Flash编程地址
Keil发送: DAP_Transfer(0x07) [设置Flash起始地址]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x00 0x00 0x80 0x08]
解释: 设置AP_TAR = 0x08000000 (Flash起始地址)
响应: [0x07] [0x01] [0x00] [0x00]
6.2 批量编程数据(以第一页256字节为例)
Keil发送: DAP_TransferBlock(0x06) [编程64个字]
请求包: [0x06] [0x40 0x00] [0x00] [0x0B]
[0x00 0x00 0x00 0x20] // 第一个指令字
[0x01 0x00 0x00 0x20] // 第二个指令字
[0x02 0x00 0x00 0x20] // 第三个指令字
... // 总共64个字(256字节)
响应: [0x06] [0x03] [0x00] [0x40 0x00] [0x00]
6.3 调用编程函数
Keil发送: DAP_Transfer(0x07) [设置编程函数参数]
请求包: [0x07] [0x01] [0x00] [0x0D] [0xFC 0xED 0x00 0xE0]
解释: 设置AP_TAR = 0xE000EDFC (DCRDR地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写编程参数]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x00 0x00 0x80 0x08]
解释: 设置R0 = 0x08000000 (目标地址)
响应: [0x07] [0x01] [0x00] [0x00]
// 设置R1 = 数据长度
Keil发送: DAP_Transfer(0x07) [写数据长度]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x00 0x01 0x00 0x00]
解释: 设置R1 = 0x00000100 (256字节)
响应: [0x07] [0x01] [0x00] [0x00]
// 设置PC = 编程函数地址
Keil发送: DAP_Transfer(0x07) [写PC值]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x04 0x00 0x00 0x20]
解释: 设置PC = 0x20000004 (编程函数地址)
响应: [0x07] [0x01] [0x00] [0x00]
// 运行编程函数
Keil发送: DAP_Transfer(0x07) [清除HALT]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x01 0x00 0x5F 0xA0]
解释: 写DHCSR = 0xA05F0001
响应: [0x07] [0x01] [0x00] [0x00]
6.4 等待编程完成(轮询)
// 轮询直到编程完成
Keil发送: DAP_Transfer(0x07) [读DHCSR]
请求包: [0x07] [0x01] [0x00] [0x2F] [0x00...]
响应: [0x07] [0x01] [0x00] [0x00] [0x03 0x00 0x5F 0xA0]
解释: C_HALT=1表示编程完成
6.5 重复6.1-6.4直到所有数据编程完成
// 对于64KB Flash,可能需要重复256次(每次256字节)
// 每次重复都会发送:
// 1. 设置新的Flash地址
// 2. DAP_TransferBlock写入数据
// 3. 调用编程函数
// 4. 等待完成
第七阶段:校验与复位(约5-10个DAP_Transfer)
7.1 验证Flash数据
Keil发送: DAP_Transfer(0x07) [设置验证函数参数]
请求包: [0x07] [0x01] [0x00] [0x0D] [0xFC 0xED 0x00 0xE0]
解释: 设置AP_TAR = 0xE000EDFC
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写验证参数]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x00 0x00 0x80 0x08]
解释: R0 = 0x08000000 (起始地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写数据长度]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x00 0x00 0x01 0x00]
解释: R1 = 0x00010000 (64KB)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写CRC值]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x78 0x56 0x34 0x12]
解释: R2 = 预期的CRC32值
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [设置PC=验证函数]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x08 0x00 0x00 0x20]
解释: PC = 0x20000008 (验证函数地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [运行验证]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x01 0x00 0x5F 0xA0]
解释: 清除C_HALT
响应: [0x07] [0x01] [0x00] [0x00]
// 等待验证完成
Keil发送: DAP_Transfer(0x07) [读验证结果]
请求包: [0x07] [0x01] [0x00] [0x2F] [0x00...]
响应: [0x07] [0x01] [0x00] [0x00] [0x00 0x00 0x00 0x00]
解释: R0 = 0表示验证成功
7.2 锁定Flash(可选)
Keil发送: DAP_Transfer(0x07) [设置Flash CR地址]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x10 0x20 0x00 0x40]
解释: AP_TAR = 0x40022010
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写LOCK位]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x80 0x00 0x00 0x00]
解释: FLASH_CR = 0x00000080 (LOCK=1)
响应: [0x07] [0x01] [0x00] [0x00]
7.3 复位目标芯片
Keil发送: DAP_Transfer(0x07) [设置AIRCR地址]
请求包: [0x07] [0x01] [0x00] [0x0D] [0x0C 0xED 0x00 0xE0]
解释: AP_TAR = 0xE000ED0C (AIRCR地址)
响应: [0x07] [0x01] [0x00] [0x00]
Keil发送: DAP_Transfer(0x07) [写复位命令]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x04 0x00 0xFA 0x05]
解释: AIRCR = 0x05FA0004 (VECTKEY + SYSRESETREQ)
响应: [0x07] [0x01] [0x00] [0x00]
7.4 恢复CPU运行
Keil发送: DAP_Transfer(0x07) [清除调试使能]
请求包: [0x07] [0x01] [0x00] [0x0B] [0x00 0x00 0x00 0xA0]
解释: DHCSR = 0xA0000000 (清除C_DEBUGEN)
响应: [0x07] [0x01] [0x00] [0x00]
第八阶段:断开连接
8.1 断开DAP连接
Keil发送: DAP_Disconnect(0x03) [断开连接]
请求包: [0x03] [0x00...]
响应: [0x03] [0x01] [0x00]
完整流程统计
DAP命令数量统计
| 阶段 | DAP_Transfer数量 | DAP_TransferBlock数量 | 其他DAP命令 |
|---|---|---|---|
| 连接初始化 | 10-15 | 0 | 3-5 |
| 调试端口初始化 | 8-12 | 0 | 0 |
| Flash算法加载 | 5-10 | 20-50 | 0 |
| CPU控制与Flash操作 | 15-30 | 0 | 0 |
| Flash擦除 | 5-10 | 0 | 0 |
| Flash编程 | 5-10 | 256-1024 | 0 |
| 校验与复位 | 5-10 | 0 | 0 |
| 总计 | 53-97 | 276-1074 | 3-5 |
典型64KB Flash下载的完整命令序列
1. DAP_Info (获取信息)
2. DAP_Connect (连接SWD)
3. DAP_SWJ_Clock (设置时钟)
4. DAP_TransferConfigure (配置传输)
5. DAP_SWD_Configure (配置时序)
6. DAP_Transfer x12 (初始化调试端口)
7. DAP_TransferBlock x30 (加载算法)
8. DAP_Transfer x8 (停止CPU)
9. DAP_Transfer x6 (调用初始化函数)
10. DAP_Transfer x8 (解锁和擦除Flash)
11. DAP_Transfer x5 (设置编程地址)
12. DAP_TransferBlock x256 (编程数据,每256字节一次)
13. DAP_Transfer x6 (调用编程函数,每次编程后)
14. DAP_Transfer x8 (验证数据)
15. DAP_Transfer x4 (复位芯片)
16. DAP_Disconnect (断开连接)
关键时间节点
t=0ms: Keil发送DAP_Info
t=5ms: 连接SWD完成
t=20ms: Flash算法加载完成
t=50ms: Flash擦除开始
t=500ms: Flash擦除完成(64KB约450ms)
t=501ms: Flash编程开始
t=3000ms: Flash编程完成(64KB约2500ms)
t=3005ms: 验证完成
t=3010ms: 复位完成
t=3015ms: 断开连接
总结
Keil点击下载时,通过CMSIS-DAP协议执行了300-1200个DAP命令,其中:
-
DAP_Transfer命令:约50-100次,用于寄存器访问和CPU控制
-
DAP_TransferBlock命令:约300-1100次,用于批量数据传输
-
其他DAP命令:约3-5次,用于连接和配置
整个流程严格按照ARM Cortex-M调试架构设计,通过SWD接口精确控制目标芯片的每一个操作。每个DAP_Transfer命令都对应特定的调试寄存器访问,共同完成了从连接、算法加载、Flash操作到验证复位的完整下载过程。