本帖最后由 czdavid 于 2014-9-5 16:47 编辑
这几日看到坛里有几个关于SWD协议相关的文章,自己也尝试了下,有点体会,也有些疑惑,写出来与大家分享和交流下。
以下我的模拟SWD接口的板子简称为Host,目标MCU(即我要连接的板子)简称为Target。
SWD协议
故名思议,串行总线调试接口。我们需要3根线与目标MCU相连,SWDIO,SWDCLK和GND。
-SWDIO 为双向Data口,主机到目标的数据传送。
-SWDCLK 为时钟口,主机驱动。
-GND GND脚。
首先参考《ARM Debug Interface V5》(注:该文档已有更新版本,并且对V5版本做了勘误),对一些相关的协议相关说明有了较浅的认识。那接下来便找了个带SWD接口的板子,我这首先选了STM32F030,因为以后可以为生产线做离线编程器,当然随后也出现了一些问题,下文会说明。
连上相关物理连线,开始折腾。
看手册中有几个相对较重要的时序说明。
Trn-Trn:即Line turn-round,当总线上的数据传输方向发送改变时(比如由Host->Target变为Target->Host),需要插入Trn,Trn为一个CLK时序,关于对于Trn的理解自己也有些疑问。
Idle cycles:在一个总线完成后,可以立即进入下一个总线操作或者是勒令总线进入Idle 状态,此时可以插入Idle cycle。在这我用连续送出8个’0b0’来使得总线进入Idle状态。
Parity :校验位,这个比较简单。分两个内容对命令头进行校验和对数据进行校验。命令头下文会说明。数据校验是对Data的0b0-0b31进行校验,如果‘1’的个数为奇数那校验位就为‘0b1’,如果‘0b1’的个数为偶数校验位就为‘0b0’。
理解了这几个,我们接下来看读写命令。
每个读写命令之前都会有个Host->Target的数据头。每个数据头为1Byte.
-Start 起始位,始终为1,这也是Target判断总线从空闲状态退出的条件。
-APnDP 选择要访问的是DP寄存器还是AP寄存器。
- Rnw 选择是读还是写。
-A[2:3] DP或者AP寄存器的地址,注意它是低位在前。比如寄存器DP寄存器 Select 它的地址为0x08,那这儿的A为C(0b1000),显然A[2:3]就为01。
-Praity 校验位,它是APnDP、RnW和A[2:3]共4个bit的校验位。
-Stop 停止位。始终为0。
-Park 该位确切来说应该始终为1,ADI V5中描述此位由总线上拉,但由于总线的上拉能力不足,会导致Target识别不了这个1。该勘误在ADI V5.2中有说明。
STM32F030下载.JPG (314.2 KB, 下载次数: 0)
下载附件
2014-9-5 16:21 上传
先写到这,有空再继续分享。上传几份相关ARM的说明,欢迎拍砖。
ADIV5.pdf
(1.67 MB, 下载次数: 386)
2014-9-5 16:24 上传
点击文件名下载附件
ADIV5.2.pdf
(2.21 MB, 下载次数: 413)
2014-9-5 16:24 上传
点击文件名下载附件
coresight_components_trm.pdf
(1.8 MB, 下载次数: 426)
2014-9-5 16:24 上传
点击文件名下载附件
按你的方式 每个周期为先高后低能读DP_ID,但是写CTRL通不过, 示波器抓了JLINK波形也是跟你差不多。 后来参考DAP时序,每个周期为先低后高,全部通信都没问题,能下载程序了。
#if 0 //周期先高后低 读DP_ID没问题 其他有问题
#define swd_clk_cyc() {swd_clk_h();clk_delay();swd_clk_l();clk_delay();} //发一个脉冲
#define swd_w_h() {swd_dio_h();swd_clk_cyc()} //写1
#define swd_w_l() {swd_dio_l();swd_clk_cyc()} //写0
#define swd_w(x) {if(x&bv(0)) swd_dio_h(); else swd_dio_l(); swd_clk_cyc()} //写一位
#define swd_r_bit(x) {swd_clk_h();clk_delay();x=swd_dio_r();swd_clk_l();clk_delay();} //读一位
#else //周期先低后高 所有操作正常
#define swd_clk_cyc() {swd_clk_l();clk_delay();swd_clk_h();clk_delay();} //发一个脉冲
#define swd_w_h() {swd_dio_h();swd_clk_cyc()} //写1
#define swd_w_l() {swd_dio_l();swd_clk_cyc()} //写0
#define swd_w(x) {if(x&bv(0)) swd_dio_h(); else swd_dio_l(); swd_clk_cyc()} //写一位
#define swd_r_bit(x) {swd_clk_l();clk_delay();x=swd_dio_r();swd_clk_h();clk_delay();} //读一位
#endif
一周热门 更多>