• SWD通讯


    这几日看到坛里有几个关于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中有说明。

    读命令为数据头+Trn+ACK+RDATA+Parity构成,但实际操作发现Trn这位是忽略掉的(所以不知道对此Trn的理解是否有误),及发送完数据头后立即读入ACK,判断Target是否正确响应。

    写命令为数据头+Trn+ACK+Trn+WDATA+Parity,在这不同的是,在写命令时必须要考虑2个Trn的位置。

    看协议中首先在连接Target时需要进行LineReset,这个是最基础也是最最简单的命令。

    具体实现为首先保证Host连续送出至少50个“1”,使得Target进行Line Reset,至少插入2个Idle,然后可以读取目标板的IDR,判断Target的类型。
    理解了整个,然后就进行操作验证,发现偶尔可以有数据ACK,继续查看手册,发现需要进行JTAG和SWD的切换操作。查看手册发现切换操作的时序如下。

    可以简化为先进行一次LineReset,随后发送0X79,0XE7(高字节首先传送),接着再一次LineReset,随后便可以读IDR。但是发现了问题,用此方式可以读取到STM32F103的IDR,但STM32F030不行,在ARM网站查阅相关资料,发现了这个。

    上图主要说在一个更早的协议中需要发送如下命令才能进行JTAG和SWD的切换。就是要发送0X6D,0XB7,尝试了下,这下能顺利读取到IDR了。疑惑的是STM32F0系列比F1出来要晚,居然用的老版本的协议?
    既然能够获取到IDR了,那接下来可以尝试着进行连接到AHB-AP了。用DP寄存器的SELECT来进行选择。这儿为了能够使得结果明显和确切,我选择了读取AP 0XFC 的IDR寄存器,来获取AP的特性,因为这个数据是只读的和确切的。
    首先要用写入DP寄存器SELECT。

    SELECT的具体描述可以参见ADI V5手册,在这有个说明,当时走了弯路。SELECT寄存器中有个APSEL选择位,这个是选择当前连接的AP,手册中没有详细说明它的定义。后来在另外的文档中发现该值为0x00,AHB-AP。APBANKSEL为选择需要访问的BANK地址,比如IDR寄存器的地址为0XFC,那它的BANKSEL为F,如果为TAR寄存器,那它的BANKSEL为0。
    连接到AHB-AP后就能进行你想要的操作了。比如我可以读取MCU的独立ID,就可以通过MEM-AP来操作。也可以对MCU进行擦除或者编程。
    在做编程之前,首先将MCU进入Halt状态,然后访问MCU相关FLASH控制寄存器进行读写即可。
    在STM32F030编程时需要注意的是STM32F030的FLASH的传输方式,我未采用Packet的传输方式。

    LineReset代码

    1. static void SwdLineReset(void)
    2. {
    3.   u8 i; 
    4.   SWD_OUT;
    5.   SWD_DIO_H;
    6.   for(i=0;i<56;i++)
    7. {
    8.     SWD_Delay();
    9.     SWD_CLK_H;
    10.     SWD_Delay();
    11.     SWD_CLK_L;
    12. }
    13. }
    复制代码


    写命令头函数

    1. static void SwdSendByte(u8 dat)
    2. {
    3.   u8 i;
    4.   SWD_OUT;
    5.   for(i=0;i<8;i++)
    6.   {  
    7.     if((dat&0x80)==0x80)
    8.     {
    9.       SWD_DIO_H;
    10.     }
    11.     else
    12.     {
    13.        SWD_DIO_L;
    14.     }
    15.     dat<<=1; 
    16.     SWD_CLK_H;
    17.     SWD_Delay();
    18.     SWD_CLK_L;
    19.     SWD_Delay();
    20.   }
    21. }
    复制代码


    读取一个data函数

    1. static u32 SeqRead(u8 cmd)
    2. {
    3.   u32 dat=0;
    4.   u8 i=0;
    5.   SwdSendByte(cmd);
    6.   SWD_IN;
    7.   for(i=0;i<3;i++)//ack 此处需要处理判断
    8.   { 
    9.    SWD_CLK_H;
    10.     SWD_Delay();
    11.     SWD_CLK_L;
    12.     SWD_Delay();
    13.   }
    14. dat=0;
    15.   for(i=0;i<32;i++)
    16.   {
    17.     dat=dat>>1; 
    18.     SWD_CLK_H;
    19.     SWD_Delay();
    20.     SWD_CLK_L;
    21.     if(SWD_DII)
    22.     {
    23.       dat|=0x80000000;
    24.     }
    25.     SWD_Delay(); 
    26.   }
    27.   //parity
    28.   SWD_IN; //trn
    29.         for(i=0;i<2;i++)
    30.   {
    31.         SWD_CLK_H;
    32.         SWD_Delay();
    33.         SWD_CLK_L;
    34.         SWD_Delay();
    35.         }
    36.         SWD_DIO_L;
    37.         SWD_OUT;
    38.         for(i=0;i<5;i++)
    39.   {
    40.         SWD_CLK_H;
    41.         SWD_Delay();
    42.         SWD_CLK_L;
    43.         SWD_Delay();
    44.         SWD_CLK_H;
    45.         SWD_Delay();
    46.         SWD_CLK_L;
    47.         SWD_Delay();
    48.         }
    49.   SWD_IN; //trn
    50.   return dat;
    51. }
    复制代码


    以上函数未加入ACK判断处理,实际使用时需要考虑并判断! 
    下面是用逻辑分析仪抓取的相关时序。
    JTAG到SWD的切换

    读取IDR寄存器

    数据传输


    识别STM32F103


    飞思卡尔KL16识别


    STM32F030 编程测试



    先写到这,有空再继续分享。上传几份相关ARM的说明,欢迎拍砖。

  • 相关阅读:
    python 不可变类型
    python 不定长参数
    Codeforces Round #749
    [提高组集训2021] Round1
    AtCoder Regular Contest 128
    [提高组互测] Day6
    [提高组互测] Day5
    [提高组互测] Day1
    [提高组互测] Day2
    [提高组集训2021] 一拳超人
  • 原文地址:https://www.cnblogs.com/lize19940412/p/7119487.html
Copyright © 2020-2023  润新知