ARM MCU SWD离线调试器

#include "SWD_Module.h"

vu8 slowModeEnable = 0;  // 默认不使能。0:不使能。1:使能!
vu8 AutoDownloadEnable = 0;
void SWD_Delay( void )  
{
    for( vu32 i=0;i<=3;i++ );   // 用于方便移植操作
}
/**
 *  @B 初始化SWD引脚和需要的功能!
 *
 */

void SWDInit( void )
{
    { // IO引脚初始化
        { // 警告:使用了PB3/PB4,而这个是调试接口,默认是这个功能,所以需要去掉。
            RCC->APB2ENR.AFIOClockEnable_RW = ENABLE;     // 使能时钟
            AFIO->MAPR.SerialWireJTAGConfiguration_W = 2; // 只启用SWD引脚!
        }
        
        if( HARDWARE_VERSION == LCD_VERSION )
        {
            RCC->APB2ENR.IOPBClockEnable_RW = ENABLE;
            SWD_SWDIO_MODE_OUT;  // 输出模式
            SWD_SWDIO_PIN_OUT = 1;  // 输出为1
            
            GPIOB->CRL.InOutMode6_RW = GPIO_CR_INOUNTMODE_OUTPUT_50MHZ;
            GPIOB->CRL.PinConfig6_RW = GPIO_CR_PINCONFG_OUT_GENERAL_PURPOSE_PUSHPULL;  // RESET
            SWD_RESET_PIN = 1;   // 低电平复位,所以不复位
            {  // SWCLK引脚控制:因为本身含有上拉电阻,所以使用开漏输出到1。
                RCC->APB2ENR.IOPAClockEnable_RW = ENABLE;
                GPIOA->CRH.InOutMode8_RW = GPIO_CR_INOUNTMODE_OUTPUT_50MHZ;
                GPIOA->CRH.PinConfig8_RW = GPIO_CR_PINCONFG_OUT_GENERAL_PURPOSE_PUSHPULL;  // 根据发现:使用开漏引脚,也会使输出0的能力降低,造成通信距离太短,出现dpID和错误和过程中容易出错!
                SWD_SWCLK_PIN = 1;   // 默认为高电平
                
                GPIOA->CRL.InOutMode0_RW = GPIO_CR_INOUNTMODE_OUTPUT_50MHZ;
                GPIOA->CRL.PinConfig0_RW = GPIO_CR_PINCONFG_OUT_GENERAL_PURPOSE_OPEN_DRAIN;  // 引脚开漏输出
                GPIOA->ODR.OutputData0_RW = 1;
                
                GPIOA->CRL.InOutMode1_RW = GPIO_CR_INOUNTMODE_OUTPUT_50MHZ;
                GPIOA->CRL.PinConfig1_RW = GPIO_CR_PINCONFG_OUT_GENERAL_PURPOSE_OPEN_DRAIN;  // 引脚开漏输出
                GPIOA->ODR.OutputData1_RW = 1;
            }
        }
        else
        {
            RCC->APB2ENR.IOPAClockEnable_RW = ENABLE;
            SWD_SWDIO_MODE_OUT;  // 输出模式
            SWD_SWDIO_PIN_OUT = 1;  // 输出为1
            
            GPIOB->CRL.InOutMode6_RW = GPIO_CR_INOUNTMODE_OUTPUT_50MHZ;
            GPIOB->CRL.PinConfig6_RW = GPIO_CR_PINCONFG_OUT_GENERAL_PURPOSE_PUSHPULL;  // RESET
            SWD_RESET_PIN = 1;   // 低电平复位,所以不复位
            { // 因为两个RESET引脚都用来复位,所以还需要PB1
                GPIOB->CRL.InOutMode1_RW = GPIO_CR_INOUNTMODE_OUTPUT_50MHZ;
                GPIOB->CRL.PinConfig1_RW = GPIO_CR_PINCONFG_OUT_GENERAL_PURPOSE_PUSHPULL;  // RESET
                GPIOB->ODR.OutputData1_RW = 1;
            }
            {  // SWCLK引脚控制:因为本身含有上拉电阻,所以使用开漏输出到1。
                RCC->APB2ENR.IOPAClockEnable_RW = ENABLE;
                GPIOA->CRL.InOutMode2_RW = GPIO_CR_INOUNTMODE_OUTPUT_50MHZ;
                GPIOA->CRL.PinConfig2_RW = GPIO_CR_PINCONFG_OUT_GENERAL_PURPOSE_PUSHPULL;  // 根据发现:使用开漏引脚,也会使输出0的能力降低,造成通信距离太短,出现dpID和错误和过程中容易出错!
                SWD_SWCLK_PIN = 1;   // 默认为高电平
            }
        }
    }
}

void RAISE( u32 test )
{
    GPIOC->ODR.OutputData8_RW = test&0x1;  //
    if( AutoDownloadEnable != ENABLE )
    {
        BeepCtrol(2,200);
    }
}

/**
 *  @B 暂时的目标是要求稳定!所以其中的延时比较多!
 *  @Warning 在转换方向的时候,需要注意,因为自己加了缓冲器,方向需要注意!
 *  结尾后:SWCLK保持为1
 */
void WRITE_BIT( u8 bit )
{
    if( slowModeEnable == ENABLE )
    {
        SWD_Delay();
        SWD_SWDIO_PIN_OUT = bit;
        SWD_Delay();
        SWD_SWCLK_PIN = 0;
        SWD_Delay();
        SWD_SWCLK_PIN = 1;
        SWD_Delay();
    }
    else
    {
        SWD_SWDIO_PIN_OUT = bit;
        SWD_SWCLK_PIN = 0;
        SWD_SWCLK_PIN = 1;
    }
}

u8 READ_BIT( void )
{
    u8 bit;
    if( slowModeEnable == ENABLE)
    {
        SWD_Delay( );
        SWD_SWCLK_PIN = 0;
        SWD_Delay( );
        bit = SWD_SWDIO_PIN_IN;
        SWD_Delay( );
        SWD_SWCLK_PIN = 1;
        SWD_Delay( );
    }
    else
    {
        SWD_SWCLK_PIN = 0;
        bit = SWD_SWDIO_PIN_IN;
        SWD_SWCLK_PIN = 1;
    }
    return bit;
}
/**
 *  @B 可以理解为TurnAround
 *
 */
void SWCLK_CYCLE( void  )
{
    if( slowModeEnable == ENABLE)
    {
        SWD_Delay();
        SWD_SWCLK_PIN = 0;
        SWD_Delay();
        SWD_SWCLK_PIN = 1;
        SWD_Delay();
    }
    else
    {
        SWD_SWCLK_PIN = 0;
        SWD_SWCLK_PIN = 1;
    }
}

/**
 *  @B 可以理解为TurnAround
 *
 */
void SWDIO_CYCLE( void )
{
    if( slowModeEnable == ENABLE)
    {
        SWD_Delay();
        SWD_SWDIO_PIN_OUT = 0;
        SWD_Delay();
        SWD_SWDIO_PIN_OUT = 1;
        SWD_Delay();
    }
    else
    {
        SWD_SWDIO_PIN_OUT = 0;
        SWD_SWDIO_PIN_OUT = 1;
    }
}
/**
 *  @B 从AP或者DP寄存器里读出数据
 *     reg:显然只有4个(暂定的取值范围0-3)。  data为什么用指针,因为这样才能改变传递值,也就是指针指向的值
 *  结尾后:SWCLK保持为1
 */
u32 readReg( u8 APnDPReg,u8 reg, u32 *data )
{
    u8 i = 0;
    u8 cb = 0;  //
    u8 parity;  // 校验值
    u8 b = 0;                // 用于读ACK的位
    u8 ack = 0; // ACK的值
    u8 ret = SWD_ERROR_OK;
    
    *data = 0;
    
    int _APnDPReg = (int) APnDPReg;
  int _read = (int) 1;  // 读请求值为1
    
    u8 A2 = reg & 0x01;
    u8 A3 = ( reg>>1 ) & 0x01;
    
    parity = ( _APnDPReg + _read + A2 + A3 ) & 0x01;
    
    SWD_SWDIO_MODE_OUT;   // 设置为输出模式
//    SWD_SWDIO_DIR_CTR2 = BUFFER_IC_DIR_OUT;  // 缓冲器设置为输出模式
    
    {  // 启动发送序列
        // 发送序列的问题:发送后,可以看出,其中SWCLK保持为1:
        WRITE_BIT( 1 );
        WRITE_BIT( _APnDPReg );
        WRITE_BIT( _read );
        WRITE_BIT( A2 );
        WRITE_BIT( A3 );
        WRITE_BIT( parity );
        WRITE_BIT( 0 );
        WRITE_BIT( 1 );   // SWDIO = 1, SWCLK = 0, SWCLK = 1
    }
    { // TurnAround
        {
            SWD_SWDIO_MODE_IN;   // 设置为输入模式
//            SWD_SWDIO_DIR_CTR2 = BUFFER_IC_DIR_IN;  // 缓冲器设置为输入模式
        }
        SWCLK_CYCLE();
    }
    {  // 读ACK
        for( i=0;i<3;i++ )
        {
            b = READ_BIT( );
            ack |= b<<i;
        }
          // 按照我的理解,这里不是应该有一个Trn的么?但是却没有
    }
    { // 判断ACK位
        if( ack == ACK_OK )
        {
            for( i=0;i<32;i++)
            {
                b = READ_BIT( );
                *data |= b<<i;
                if(b)
                    cb = !cb;  // cb之前已经初始化了
            }
            parity = READ_BIT();  // 最后再读一下校验位
                                  // 要使系统稳定的话,处理错误至关重要
            if( cb == parity )
            {
                ret = SWD_ERROR_OK;  // 系统正常
            }
            else
            {
                ret = SWD_ERROR_PARITY;  // 校验错误,检验错误就可能是位操作不对,当然这个概率是50%
            }
        }
        else if( ack == ACK_WAIT )
        {
            ret = SWD_ERROR_WAIT;
        }
        else if( ack == ACK_FAULT )
        {
            ret = SWD_ERROR_FAULT;
        }
        else
        {
            ret = SWD_ERROR_PROTOCOL;  //协议出错,这个要注意了
        }
    }
    
    { // Turnaround
        SWCLK_CYCLE();
    }
    { // 进入8个Idle状态,确保传输
        {
            SWD_SWDIO_MODE_OUT;   // 设置为输入模式
//            SWD_SWDIO_DIR_CTR2 = BUFFER_IC_DIR_OUT;  // 缓冲器设置为输入模式
        }
        for( i=0;i<8;i++ )
        {
            WRITE_BIT(0);
        }
    }
    return ret;    // 返回值
}


/**
 *  @B 这里有个要注意:可以忽视Ack,因为清除错误需要访问内容寄存器
 *
 */
u32 writeReg( u8 APnDPReg,u8 reg,u32 data,u8 ignoreAck)
{
    u8 ack = 0;
    u8 i;
    u8 parity = 0;
    u8 b;
    u8 ret = SWD_ERROR_OK;
    
    u8 _APnDPReg = APnDPReg;
    u8 _read = 0;
    
    u8 A2 = reg&0x1;
    u8 A3 = (reg>>1)&0x1;
    
    parity = ( _APnDPReg + _read +A2 + A3 )&0x1;  // 计算校验值
    
    SWD_SWDIO_MODE_OUT;   // 设置为输出模式
//    SWD_SWDIO_DIR_CTR2 = BUFFER_IC_DIR_OUT;  // 缓冲器设置为输出模式
    
    {  // 启动发送序列
        // 发送序列的问题:发送后,可以看出,其中SWCLK保持为1:
        WRITE_BIT( 1 );
        WRITE_BIT( _APnDPReg );
        WRITE_BIT( _read );
        WRITE_BIT( A2 );
        WRITE_BIT( A3 );
        WRITE_BIT( parity );
        WRITE_BIT( 0 );
        WRITE_BIT( 1 );   // SWDIO = 1, SWCLK = 0, SWCLK = 1 (这个由主机驱动为1的!手册说是不用主机驱动,但是由于上拉总线上会为1)
    }
    {
        { // 发现一个特别奇葩的问题:就是如果把这一句,放到SWCLK_CYCLE();之后就会出现:ACK错误,比较费解!
            // 可能的原因:就是从机这个时候要驱动SWDIO,但是发现驱动不下来!!
            // 最好的解决办法:就是通过读DP寄存器找到这个问题!
            SWD_SWDIO_MODE_IN;
//            SWD_SWDIO_DIR_CTR2 = BUFFER_IC_DIR_IN;
        }
        SWCLK_CYCLE();
    }
    {
        {
            
            // 读ACK
            for( i=0;i<3;i++ )
            {
                b = READ_BIT( );
                ack |= b<<i;
            }
                // 按照我的理解,这里不是应该有一个Trn的么?但是却没有,(最后发现读是没有的人,但是写是有的!写的在下面)
        }
    }
    { //
        if( (ack == ACK_OK) || ignoreAck )
        {
            
            { // 设置为输出
                SWD_SWDIO_MODE_OUT;   // 设置为输出模式
//                SWD_SWDIO_DIR_CTR2 = BUFFER_IC_DIR_OUT;  // 缓冲器设置为输出模式
            }
            SWCLK_CYCLE();  // 写的时候要用Trn一次。但是读的时候没有在代码里面看到。
            parity = 0;    // 这个函数有问题,当我改parity的时候,出现可以读状态,说明给器件的值存在检验错误!!这个是不应该的。这个要检查!
            for( i=0;i<32;i++ )
            {
                b = ( data >> i)&0x1;
                WRITE_BIT(b);
                if(b)
                    parity = !parity;
            }
            WRITE_BIT(parity);
        }
        else if( ack == ACK_WAIT )
        {
            ret = SWD_ERROR_WAIT;
        }
        else if( ack == ACK_FAULT )
        {
            ret = SWD_ERROR_FAULT;
        }
        else
        {
            ret = SWD_ERROR_PROTOCOL;  //协议出错,这个要注意了
        }
        
        { // 进入8个Idle状态,确保传输,这个可以优化!
            {  // 这个其实是多余的,一直是输出的,所以我都考虑这个源代码的性能了。
                SWD_SWDIO_MODE_OUT;   // 设置为输入模式
//                SWD_SWDIO_DIR_CTR2 = BUFFER_IC_DIR_OUT;  // 缓冲器设置为输入模式
            }
            for( i=0;i<8;i++ )
            {
                WRITE_BIT(0);
            }
        }
    }
    return ret;
}


/**
 *  @B 上电执行JTAG转SWD的序列函数!
 *
 */
void JTAG_To_SWD_Sequence( void )
{
  int i;
  int b;
 
    SWD_SWDIO_MODE_OUT;   // 设置为输出模式
//    SWD_SWDIO_DIR_CTR2 = BUFFER_IC_DIR_OUT;  // 缓冲器设置为输出模式
    {
        SWD_SWDIO_PIN_OUT = 1;   // 先输出为1
        for( i=0;i<80;i++ )
        {
                SWCLK_CYCLE();
        }
    }
    for( i=0;i<16;i++ )
    {
        b = ( JTAG_TO_SWD_VALUE>>i )&0x1;
        WRITE_BIT(b);
    }
    {
        SWD_SWDIO_PIN_OUT = 1;
        for( i=0;i<60;i++ )
        {
                SWCLK_CYCLE();
        }
    }
    { // 执行16个空闲周期
        SWD_SWDIO_PIN_OUT = 0;
        for( i=0;i<16;i++ )
        {
            SWCLK_CYCLE();
        }
        SWD_SWDIO_PIN_OUT = 1;  // 实际中,因为少了,这一句导致只能下载F1,F4/F2总是出现问题。
    }
        
}


/**
 *  @B 写AP寄存器,在当前选择的APBANK。
 *
 */


void writeAP( u8 reg,u32 data)
{
    u8 forFault = 0;
    forWait:
    {
        u8 swdStatus;
        u8 retry = 1;  // 这个是重试次数,但是日后应该做下载器,则这个变量是一定要提取出来的
        do
        {
            swdStatus = writeReg(1,reg,data,FALSE);
            retry--;
        }while( (swdStatus == SWD_ERROR_WAIT) && retry>0 );
        if( swdStatus != SWD_ERROR_OK )
        {
            if( swdStatus == SWD_ERROR_WAIT )
            {
                goto forWait;  // 在测试F2的时候,出现了不定时的这个问题,所以才用这个方法!
            }
            else if( swdStatus == SWD_ERROR_FAULT )
            {
                if( forFault == 0 ) // 重试次数超出
                {
                    forFault ++;
                    writeDP( DP_ABORT,0x1F);
                    goto forWait;
                }
                else
                {
                    RAISE( swdStatus );
                }
            }
            else
            {
                RAISE( swdStatus );  // 根据查询源码,这个函数的操作相对比较麻烦,需要自己日后重新定义!
            }
        }
    }
}

/**
 *  @B 写DP寄存器
 *
 */
void writeDP( u8 reg,u32 data )
{
    u8 forFault = 0;
    forWait:
    {
        u8 swdStatus;
        u8 retry = 10;  // 重试次数
        do
        {
            swdStatus = writeReg(0,reg,data,FALSE);
            retry--;
        }while( (swdStatus == SWD_ERROR_WAIT) && retry>0 );
        if( swdStatus != SWD_ERROR_OK )
        {
            if( swdStatus == SWD_ERROR_WAIT )
                {
                    goto forWait;  // 在测试F2的时候,出现了不定时的这个问题,所以才用这个方法!
                }
                else if( swdStatus == SWD_ERROR_FAULT )
                {
                    if( forFault == 0 ) // 重试次数超出
                    {
                        forFault ++;
                        writeDP( DP_ABORT,0x1F);
                        goto forWait;
                    }
                    else
                    {
                        RAISE( swdStatus );
                    }
                }
                else
                {
                    RAISE( swdStatus );
                }
        }
    }
}

/**
 *  @B 写DP寄存器但是忽略ACK位,这个显然是出错之后的操作!
 *
 */
void writeDPIgnoreAck( u8 reg,u32 data )
{
    u8 forFault = 0;
    forWait:
    {
        u8 swdStatus;
        u8 retry = 10;  // 重试次数
        do
        {
            swdStatus = writeReg(0,reg,data,TRUE);
            retry--;
        }while( (swdStatus == SWD_ERROR_WAIT) && retry>0 );
        if( swdStatus != SWD_ERROR_OK )
        {
            if( swdStatus == SWD_ERROR_WAIT)
                {
                    goto forWait;  // 在测试F2的时候,出现了不定时的这个问题,所以才用这个方法!
                }
                else if( swdStatus == SWD_ERROR_FAULT )
                {
                    if( forFault == 0 ) // 重试次数超出
                    {
                        forFault ++;
                        writeDP( DP_ABORT,0x1F);
                        goto forWait;
                    }
                    else
                    {
                        RAISE( swdStatus );
                    }
                }
                else
                {
                    RAISE( swdStatus );  // 出错处理特别重要!
                }
        }
    }
}

/**
 *  @B 读AP
 *
 */
void readAP( u8 reg,u32 *data )
{
    u8 forFault = 0;
    forWait:
    {
        u8 swdStatus;
        u8 retry = 10;  // 重试次数
        do
        {
            swdStatus = readReg( 1,reg,data );
            retry--;
        }while( (swdStatus == SWD_ERROR_WAIT) && retry>0 );
        if( swdStatus != SWD_ERROR_OK )
        {
            if( swdStatus == SWD_ERROR_WAIT)
                {
                    goto forWait;  // 在测试F2的时候,出现了不定时的这个问题,所以才用这个方法!
                }
                else if( swdStatus == SWD_ERROR_FAULT )
                {
                    if( forFault == 0 ) // 重试次数超出
                    {
                        forFault ++;
                        writeDP( DP_ABORT,0x1F);
                        goto forWait;
                    }
                    else
                    {
                        RAISE( swdStatus );
                    }
                }
                else
                {
                    RAISE( swdStatus );  // 出错处理特别重要!
                }
        }
    }
}

/**
 *  @B 读DP
 *
 */
void readDP( u8 reg,u32 * data )
{
    u8 forFault = 0;
    forWait:
    {
        u8 swdStatus;
        u8 retry = 10;  // 重试次数
        do
        {
            swdStatus = readReg( 0,reg,data );
            retry--;
        }while( (swdStatus == SWD_ERROR_WAIT) && retry>0 );
        if( swdStatus != SWD_ERROR_OK )
        {
            if( swdStatus == SWD_ERROR_WAIT)
                {
                    goto forWait;  // 在测试F2的时候,出现了不定时的这个问题,所以才用这个方法!
                }
                else if( swdStatus == SWD_ERROR_FAULT )
                {
                    if( forFault == 0 ) // 重试次数超出
                    {
                        forFault ++;
                        writeDP( DP_ABORT,0x1F);
                        goto forWait;
                    }
                    else
                    {
                        RAISE( swdStatus );
                    }
                }
                else
                {
                    RAISE( swdStatus );  // 出错处理特别重要!
                }
        }
    }
}


/**
 *  @B 初始化DP,先发送一个转换序列,然后读出IDCode的值。
 *
 */
u32 initDp( void )
{
    u32 dpId;
    JTAG_To_SWD_Sequence(  );
    readDP(DP_IDCODE,&dpId);  //
    
    //Debug power up request,这个应该是打开调试组件的电源,方便调试!
    writeDP( DP_CTRL, DP_CTRL_CSYSPWRUPREQ | DP_CTRL_CDBGPWRUPREQ );
    
    // Wait until we receive powerup ACK
    int retry = 300;
    u32 status;
    while( retry>0 )
    {
        readDP(DP_CTRL,&status);
        // 下面的这个是什么意思?
        if ( ( status & ( DP_CTRL_CDBGPWRUPACK | DP_CTRL_CSYSPWRUPACK ) )
      == ( DP_CTRL_CDBGPWRUPACK | DP_CTRL_CSYSPWRUPACK ) )
    {
      break;
    }
    retry--;
    }
    
    /* Throw error if we failed to power up the debug interface */
  if ( ( status & ( DP_CTRL_CDBGPWRUPACK | DP_CTRL_CSYSPWRUPACK ) )
    != ( DP_CTRL_CDBGPWRUPACK | DP_CTRL_CSYSPWRUPACK ) )
  {
//    RAISE( SWD_ERROR_DEBUG_POWER );  // 这个出错不好管理!
        RAISE( status );
  }
 
  /* Select first AP bank */
  writeDP( DP_SELECT, 0x00 );
 
  return dpId;  // 应该是1BA01477(F4读出的值为:2BA01477)  F2测试也是:2BA01477
}

/**
 *  @B 这个可能是针对EMF32单片机的,但是有一定的参考价值!这也能读出来东西,但是不知道是什么含义!
 */
u32 readApID( void )
{
    u32 apId;
    /* Select last AP bank */
  writeDP( DP_SELECT, 0xf0 );  // 写DP寄存器中的APBBANKSEL位为0xF
//    writeDP( DP_SELECT, 0x08000003 );
 
  /* Dummy read AP ID */
  readAP( AP_IDR, &apId );
 
  /* Read AP ID */
  readDP( DP_RDBUFF, &apId );
 
  /* Select first AP bank again */
  writeDP( DP_SELECT, 0x00 );
 
  return apId; // EMF手册中说明这个值应该是:0x24770011(F4读出的值是这个!),但是实际读出来是0x14770011(F103)。不知道这是什么含义!
}

/**
 *  @B 不明觉厉
 *
 */
/**********************************************************
 * Sends the AAP Window Expansion Sequence. This sequence
 * should be sent on SWDIO/SWCLK when reset is held low.
 * This will cause the AAP window to be 255 times longer
 * when reset is released.
 **********************************************************/
void aapExtensionSequence( void )
{
  int i;
 
    SWD_RESET_PIN = 0;
    
  SWD_SWCLK_PIN = 1;
 
  for ( i = 0; i < 4; i++ )
  {
    SWDIO_CYCLE( );
  }
 
  SWD_SWCLK_PIN = 0;
 
  for ( i = 0; i < 4; i++ )
  {
    SWDIO_CYCLE( );
  }
 
  SWD_SWCLK_PIN = 0;
    
    SWD_RESET_PIN = 1;
}

/* 从下面开始,就是移植的另外一个程序--在SRAM中编程 ,具有不错的参考价值 */
/**
 *  @B 对于程序,务必要停止运行后,再写FLash操作!所以这个函数存在
 *
 */
void connect_and_halt_core( void )
{
    u32 rw_data;
    rw_data = CHIPAP_BANK_F;
    
    
    if( rw_data != 0x2430002 )
    {
        return;
    }
    
    
}

/*以上,就是移植的另外一个程序--在SRAM中编程 ,具有不错的参考价值 */

/* 从下面开始,是自己的测试程序 */
u32 readMemoryFormStm32( void )
{
    u32 apId = 0;
    vu32 address = 0x08000000;
    /* Select last AP bank */
//    while(1)
    {  // 按照下面的程序测试,速度大约是:4MHZ左右(3.7MHZ)测试一次!还是比较给力的。
        writeDP( DP_SELECT, 0x00 );  // 访问寄存器
    //    writeDP( DP_SELECT, 0x08000003 );
        
        writeAP( AP_CSW,0x2);   // 长度为32bit
        
        writeAP( AP_TAR,address);   // 写目标地址
        readAP( AP_DRW,&apId);
        
        /* Read AP ID */
        readDP( DP_RDBUFF, &apId );
        address++;
  }
 
  return apId;
}

/**
 *  @B 测试
 *
 */

void testFlashChearAndWrite( void )
{
    {    // 清空Flash位
        
    }
    { // 写Flash
        
    }
}


#define CMX_AIRCR 0xE000ED0C
#define RUN_CMD_CRJ 0x05FA0007  // 这个是从:DAP中提取出来的,不知道行不行!!

#define CM3_DHCSR 0xE000EDF0
#define CM3_DEMCR 0xE000EDFC
/* Write these to DHCSR */
#define RUN_CMD  0xA05F0001
#define STOP_CMD 0xA05F0003
#define STEP_CMD 0xA05F0005
#define DEBUG_EVENT_TIMEOUT 200
/**
 *  @B Halting the MCU
 *
 */
void haltTarget(void)
{
    u32 tmp;
    writeAP(AP_TAR, CM3_DEMCR);
    readAP(AP_DRW, &tmp);
    readDP(DP_RDBUFF, &tmp);
    
    
    writeAP( AP_CSW,0x23000002);   // 一定要先提前写上这个,不仅仅表示是32位,同时表明访问的方向是写访问!否则会报错!!
  int timeout = DEBUG_EVENT_TIMEOUT;
  writeAP(AP_TAR, CM3_DHCSR);
  writeAP(AP_DRW, STOP_CMD);
    
    writeAP(AP_TAR, CM3_DEMCR);
  writeAP(AP_DRW, 0x01000401);  // Write 1 to bit VC_CORERESET in DEMCR. This will enable halt-on-reset
    

  uint32_t dhcrState;
  do {
    writeAP(AP_TAR, CM3_DHCSR);
    readAP(AP_DRW, &dhcrState);
    readDP(DP_RDBUFF, &dhcrState);
    timeout--;
  } while ( !(dhcrState & CoreDebug_DHCSR_S_HALT_Msk) && timeout > 0 );
 
  if ( !(dhcrState & CoreDebug_DHCSR_S_HALT_Msk) ) {
//        RAISE(dhcrState);
  }
    { // 手册给的建议是:使能一个断点!!
    }
}

/**
 *  @B 测试成功,可以启动程序!但是要注意CSW的值,需要设置为:0x23000002(暂时不明白含义,这个要注意!日后再做!)
 *
 */
void runTarget(void)
{
    uint32_t i;
    writeAP( AP_CSW,0x23000002);
    
    writeAP(AP_TAR, CM3_DEMCR);
  writeAP(AP_DRW, 0x01000400);  // 避免复位停在Reset!
    for (i=0; i<100; i++);
  writeAP(AP_TAR, CM3_DHCSR);
  writeAP(AP_DRW, RUN_CMD);
    
    
    
    for (i=0; i<100; i++);
    writeAP(AP_TAR, CMX_AIRCR);
  writeAP(AP_DRW, RUN_CMD_CRJ);
    for (i=0; i<100; i++);
    writeAP(AP_TAR, CMX_AIRCR);
  writeAP(AP_DRW, RUN_CMD_CRJ);
}

/**********************************************************
 * Reads one word from internal memory
 *
 * @param addr
 *    The address to read from
 *
 * @returns
 *    The value at @param addr
 **********************************************************/
uint32_t readMem(uint32_t addr)
{
  uint32_t ret;
  writeAP(AP_TAR, addr);
  readAP(AP_DRW, &ret);
  readDP(DP_RDBUFF, &ret);
  return ret;
}
/**********************************************************
 * Writes one word to internal memory
 *
 * @param addr
 *    The address to write to
 *
 * @param data
 *    The value to write
 *
 * @returns
 *    The value at @param addr
 **********************************************************/
void writeMem(uint32_t addr, uint32_t data)
{
  writeAP(AP_TAR, addr);
  writeAP(AP_DRW, data);
}
/**********************************************************
 * Resets the target CPU by using the AIRCR register.
 * The target will be halted immediately when coming
 * out of reset. Does not reset the debug interface.
 **********************************************************/
void resetAndHaltTarget(void)
{
  uint32_t dhcsr;
  int timeout = DEBUG_EVENT_TIMEOUT;
 
  /* Halt target first. This is necessary before setting
   * the VECTRESET bit */
  haltTarget();
 
  /* Set halt-on-reset bit */
  writeMem(DEMCR, CoreDebug_DEMCR_VC_CORERESET_Msk);
 
  /* Clear exception state and reset target */
  writeAP(AP_TAR, AIRCR);
  writeAP(AP_DRW, (0x05FA << SCB_AIRCR_VECTKEY_Pos) |
                  SCB_AIRCR_VECTCLRACTIVE_Msk |
                  SCB_AIRCR_VECTRESET_Msk);
    
  /* Wait for target to reset */
  do {
    TimeDelayOfSoftAtMs(1);
    timeout--;
    dhcsr = readMem(DHCSR);
  } while ( dhcsr & CoreDebug_DHCSR_S_RESET_ST_Msk );
 
 
  /* Check if we timed out */
  dhcsr = readMem(DHCSR);
  if ( dhcsr & CoreDebug_DHCSR_S_RESET_ST_Msk )
  {
//    RAISE(SWD_ERROR_TIMEOUT_WAITING_RESET);
        RAISE(1);
  }
 
  /* Verify that target is halted */
  if ( !(dhcsr & CoreDebug_DHCSR_S_HALT_Msk) )
  {
//    RAISE(SWD_ERROR_TARGET_NOT_HALTED);
        RAISE(1);
  }
}

/**
 *  @B 清空STM32 Flash操作
 *
 */
void clearAllFlash( void )
{
    OS_ERR err;
    writeAP( AP_CSW,0x23000002);   // 一定要先提前写上这个,不仅仅表示是32位,同时表明访问的方向是写访问!否则会报错!!
    
  writeAP(AP_TAR, (u32)&FLASH->KEYR.FlashProgramAndEraseControllerKey_W);
  writeAP(AP_DRW, FLASH_KEY1);
    
    writeAP(AP_TAR, (u32)&FLASH->KEYR.FlashProgramAndEraseControllerKey_W);
  writeAP(AP_DRW, FLASH_KEY2);
    
    { // 清除错误
            writeAP(AP_TAR, (u32)&FLASH->SR.All ) ;
            writeAP(AP_DRW, _0b00110100);
    }
    
    writeAP( AP_CSW,0x00000002);   // 读
    u32 readWord0 = 0;
    do
    {
        writeAP(AP_TAR, (u32)&FLASH->SR.All);
        readAP(AP_DRW,&readWord0);
        readDP(DP_RDBUFF,&readWord0);
    }while(readWord0&0x1); // 当为1的时候说明没有操作完成!
    
    writeAP( AP_CSW,0x23000002);   // 一定要先提前写上这个,不仅仅表示是32位,同时表明访问的方向是写访问!否则会报错!!
    writeAP( AP_TAR, (u32)&FLASH->CR.All );
  writeAP( AP_DRW, _0b00000100 );  // 注意:擦除的时候要分开写。
    
    writeAP( AP_TAR, (u32)&FLASH->CR.All );
  writeAP( AP_DRW, _0b01000100 );  // 先写清除全片,再写开始位!!
    
    writeAP( AP_CSW,0x00000002);   // 读
    u32 readWord1 = 0;
    do
    {
        writeAP(AP_TAR, (u32)&FLASH->SR.All);
        readAP(AP_DRW,&readWord1);
        readDP(DP_RDBUFF,&readWord1);
    }while(readWord1&0x1); // 当为1的时候说明没有操作完成!
    { // 根据客户反应,全片擦除有可能存在没有完成的地方,所以本次严格的按照操作来操作!!
        //
            do
            {
                writeAP(AP_TAR, (u32)&FLASH->SR.All);
                readAP(AP_DRW,&readWord1);
                readDP(DP_RDBUFF,&readWord1);
            }while( ( readWord1&_0b00100000 ) != _0b00100000);
    }
    { // 清除错误
            writeAP( AP_CSW,0x23000002);   // 一定要先提前写上这个,不仅仅表示是32位,同时表明访问的方向是写访问!否则会报错!!
            writeAP(AP_TAR, (u32)&FLASH->SR.All);
              writeAP(AP_DRW, _0b00110100);
    }
    OSTimeDlyHMSM(0,0,0,1000,OS_OPT_TIME_HMSM_NON_STRICT,&err);
}


/**
 *  @B 写Flash程序操作!测试!
 *
 */

void writeFlash( void )
{
    
}


/**
 *  @B F0系列的保护操作选项,注意:F0系列的FLASH编程也是16bit一次编程这个和F1的操作是一样的。
    @warning 实际中发现F0的寄存器分配和F1分配是一样的,所以选择用F1的寄存器进行操作。
 *  
 */
void stm32f0Protection( u8 readProtectionEnable,u8 writeProtectionEnable )
{
    u32 readWord1 = 0;
//    if( (readProtectionEnable == 1) || (writeProtectionEnable == 1 ))  //
//    {
        {  // 解锁Flash操作
            writeAP( AP_CSW,0x23000002);   // 一定要先提前写上这个,不仅仅表示是32位,同时表明访问的方向是写访问!否则会报错!!
            
            writeAP(AP_TAR, (u32)&FLASH->KEYR.FlashProgramAndEraseControllerKey_W);
            writeAP(AP_DRW, FLASH_KEY1);
            
            writeAP(AP_TAR, (u32)&FLASH->KEYR.FlashProgramAndEraseControllerKey_W);
            writeAP(AP_DRW, FLASH_KEY2);
        }
        {
            do
            {
                writeAP(AP_TAR, (u32)&FLASH->SR.All);
                readAP(AP_DRW,&readWord1);
                readDP(DP_RDBUFF,&readWord1);
            }while(readWord1&0x1); // 当为1的时候说明没有操作完成!
        }        
        { // 解锁OptionsByte操作
            writeAP(AP_TAR, (u32)&FLASH->OPTKEYR.OptionByteKey_W);
            writeAP(AP_DRW, FLASH_KEY1);
            
            writeAP(AP_TAR, (u32)&FLASH->OPTKEYR.OptionByteKey_W);
            writeAP(AP_DRW, FLASH_KEY2);
        }
        {
            writeAP(AP_TAR, (u32)&FLASH->CR.All);
            writeAP(AP_DRW, _0b00100000 + 0x200 );  // 清除选项字节,注意:此时读保护依然在,写保护被清除,用户字节被清除
            
            writeAP(AP_TAR, (u32)&FLASH->CR.All);
            writeAP(AP_DRW, _0b01100000 + 0x200 );  // 开始操作
            
            do
            {
                writeAP(AP_TAR, (u32)&FLASH->SR.All);
                readAP(AP_DRW,&readWord1);
                readDP(DP_RDBUFF,&readWord1);
            }while(readWord1&0x1); // 忙时卡住
        }
        { // 先不要置位读保护
            
        }
        {
            writeAP(AP_TAR, (u32)&FLASH->CR.All);
            writeAP(AP_DRW, _0b00000000 + 0x200 );  // 恢复现场
            
            writeAP(AP_TAR, (u32)&FLASH->CR.All);
            writeAP(AP_DRW, _0b00010000 + 0x200 );  // 选项字节编程使能!
        }
        {
            if( readProtectionEnable == ENABLE )  // 默认状态下就是读保护使能的!(因为清除了!)
            {
//                writeAP(AP_TAR, (u32)&OB->RDP);
//                writeAP(AP_DRW, 0x00);  // 0x00也是读保护
            }
            else
            {
                writeAP( AP_CSW,0x23000001 ); // 长度为16bit,写(操作Flash必须用u16形式,否则会出问题!)
                writeAP( AP_TAR, (u32)&OB->RDP );
                if( ((u32)&OB->RDP)%4 != 0 )
                {
                    writeAP( AP_DRW, 0xAA<<16 );  // 清除读保护,当值为0xAA时,关读保护!
                }
                else
                {
                    writeAP( AP_DRW, 0xAA);  // 清除读保护
                }
            }
            if( writeProtectionEnable == ENABLE )
            {
                writeAP( AP_CSW,0x23000001 ); // 长度为16bit,写(操作Flash必须用u16形式,否则会出问题!)
                writeAP( AP_TAR, (u32)&OB->WRP0 );
                writeAP( AP_DRW, 0x00 );
                
                writeAP( AP_TAR, (u32)&OB->WRP1 );
                writeAP( AP_DRW, 0x00 );
                
                writeAP( AP_TAR, (u32)&OB->WRP2 );
                writeAP( AP_DRW, 0x00 );
                
                writeAP( AP_TAR, (u32)&OB->WRP3 );
                writeAP( AP_DRW, 0x00 );
                
            }
            else // 之前已经清除了,所以不需要管!
            {
//                writeAP(AP_TAR, (u32)&OB->RDP);
//                writeAP(AP_DRW, 0xFFFFFFFF);  // 清除读保护
            }
            
            writeAP( AP_CSW,0x23000002 ); // 恢复为32位
            
            do
            {
                writeAP(AP_TAR, (u32)&FLASH->SR.All);
                readAP(AP_DRW,&readWord1);
                readDP(DP_RDBUFF,&readWord1);
            }while(readWord1&0x1); // 忙时卡住
            
        }
        {
            writeAP(AP_TAR, (u32)&FLASH->CR.All);
            writeAP(AP_DRW, _0b00000000 + 0x200 );  // 恢复现场
            
            writeAP(AP_TAR, (u32)&FLASH->CR.All);
            writeAP(AP_DRW, _0b10000000);      // 锁定操作
        }
}

相关推荐

  1. ARM MCU SWD线调试器

    2023-12-05 23:44:02       31 阅读
  2. C语言实现ARM MCUJTAG线调试器

    2023-12-05 23:44:02       40 阅读
  3. Docker线安装

    2023-12-05 23:44:02       32 阅读
  4. 线安装docker

    2023-12-05 23:44:02       37 阅读
  5. docker线安装

    2023-12-05 23:44:02       21 阅读
  6. Ansible线安装

    2023-12-05 23:44:02       23 阅读
  7. Ubuntu下载线包、安装线包(dpkg)

    2023-12-05 23:44:02       46 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-05 23:44:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-05 23:44:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-05 23:44:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-05 23:44:02       20 阅读

热门阅读

  1. .sketch的文件转.psd文件

    2023-12-05 23:44:02       37 阅读
  2. 【无标题】

    2023-12-05 23:44:02       44 阅读
  3. 代码解读:Zero-shot 视频生成任务 Text2Video-Zero

    2023-12-05 23:44:02       49 阅读
  4. 解读文生视频大模型ModelScopeT2V技术报告

    2023-12-05 23:44:02       41 阅读
  5. 【音频】Glitch、降噪相关

    2023-12-05 23:44:02       34 阅读
  6. 如何使用Go与MQTT进行通信

    2023-12-05 23:44:02       26 阅读
  7. C语言初学4:C 存储类

    2023-12-05 23:44:02       41 阅读
  8. 如何拆解Unity 2022.3版本的AssetBundle

    2023-12-05 23:44:02       44 阅读
  9. Mybatis-plus逻辑删除

    2023-12-05 23:44:02       35 阅读