LarduinoISP for LGT8FX8D SWD通訊協議源碼簡析

LGT8FX8D/P系列的CPU能夠指令級兼容avr芯片,引腳定義也相近.將avr的程序移植到LGT8FX8D/P只需做少許的修改,並且加強了一些性能,價格卻更低,性價比高.
要將程序寫入空片,其flash燒寫方式與avr並不同,須要專門的調試下載器.使用說明
LarduinoISP for LGT8FX8D公開了份代碼,其中實現了經過SWD接口實現LGT8FX8D的讀寫.咱們經過閱讀這份代碼來看看經過SWD通訊方式來實現flash燒寫的過程.git

SWD通訊硬件要求

需使用的引腳:github

引腳 傳輸方向 描述
SWD 輸入與輸出 用做傳輸數據比特,雙向
SWC 輸出 用做時鐘信號
RST 輸出 SWD模式時RST拉低

SWD對應PB5,SWC對應PB4,RST對應PB2ide

#define SWDIF_PIN        PINB
#define SWDIF_DIR        DDRB
#define SWDIF_PORT        PORTB
#define SWDIF_CLK        (1 << 5)    // PB5
#define SWDIF_DAT          (1 << 4)    // PB4
#define SWDIF_RSTN        (1 << 2)    // PB2
SWD通訊時序分析

全部時序的實現,由CPU操做IO口完成.性能

通訊速率

在一個通訊時鐘週期會調用二次SWD_Delay()即$12*2$個NOP指令,再加上設置SWC SWD電平的指令,總約需30個指令週期,在16MHz的系統時鐘下,通訊速率可達500Kbps.若是一個位輸出中有更多的邏輯操做,則通訊頻率會更低一些.ui

#define SWD_Delay()    do {\
    NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); \
    NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); \
} while(0);
//一個時鐘週期的模擬
        SWC_CLR();
        SWD_Delay();
        SWD_CLR();
        SWD_Delay();
        SWC_SET();
        SWD_Delay();
通訊時序

數據通訊由起始位SWD_CLR開始,而後是輸出一個字節是由一串8比特數據一個結束位SWD_SET組成,若有多個字節輸出,則中間結束位爲SWD_CLR,最後的結束位爲SWD_SET.每一個字節的先輸出最低位,再到最高位的順序輸出,即LSB First. 調試

雙向引腳SWD輸出數據時,在時鐘SWC低電平時改變SWD輸出數據,寫入目標芯片會在SWC的上升沿檢測SWD數據.code

void SWD_WriteByte(uint8_t start, uint8_t data, uint8_t stop)
{
    volatile uint8_t cnt;
    
    if(start) {
        SWC_CLR();
        SWD_Delay();
        SWD_CLR();
        SWD_Delay();
        SWC_SET();
        SWD_Delay();
    }
    
    // send data
    for(cnt = 0; cnt < 8; cnt++)
    {
        SWC_CLR();
        if(data & 0x1) SWD_SET();
        else SWD_CLR();
        SWD_Delay();
        data >>= 1;
        SWC_SET();
        SWD_Delay();
    }
    
    SWC_CLR();
    if(stop) SWD_SET();
    else SWD_CLR();

    SWD_Delay();
    SWC_SET();
    SWD_Delay();
}

雙向引腳SWD輸入數據時,在時鐘SWC高電平時設爲輸入並上拉,由寫入目標芯片控制SWD,在SWC的降低沿檢測SWD數據.接口

uint8_t SWD_ReadByte(uint8_t start, uint8_t stop)
{
    volatile uint8_t cnt;
    volatile uint8_t bRes = 0;
    
    if(start)
    {
        SWC_CLR();
        SWD_CLR();
        SWD_Delay();
        SWC_SET();
        SWD_Delay();        
    }
    
    SWD_IND();
    //SWD_Delay();
    for(cnt = 0; cnt < 8; cnt++)
    {
        bRes >>= 1;
        SWC_CLR();
        SWD_Delay();
        if(SWDIF_PIN & SWDIF_DAT)
            bRes |= 0x80;

        SWC_SET();
        SWD_Delay();
    }
    
    SWD_OUD();
    
    SWC_CLR();
    if(stop) SWD_SET();
    else SWD_CLR();

    SWD_Delay();
    SWC_SET();
    SWD_Delay();
    
    return bRes;
}

一些操做須要經過一些時序才能完成,故有些需加上SWD_Idle等待.get

void SWD_Idle(uint8_t cnt)
{
    volatile uint8_t i;

    SWD_SET();
    
    for(i = 0; i < cnt; i++)
    {
        SWC_CLR();
        SWD_Delay();
        SWC_SET();
        SWD_Delay();
    }
}
Flash寫入相關
  • 讀取SWD ID

SWD_ReadSWDID()flash

  • Unlock()操做,因芯片的保護機制,掉電再上電後,是不能經過SWD接口來讀取Flash中的數據的,此Unlock操做至關於對芯片進行全芯片擦除,以後所讀數據所有爲0xff,並容許寫入操做.
uint8_t SWD_UnLock()
{
...
    SWD_UnLock0();
    SWD_EEE_UnlockTiming();
    SWD_UnLock1();
    delayus(100);
    SWD_UnLock0();
    delayus(100);
    SWD_UnLock1();    
...
}
  • 讀取Read(),因flash爲16位,參數addrword地址,每次讀取兩個字節,返回一個word數據
uint16_t SWD_EEE_Read(uint16_t addr)
{
    volatile uint8_t hbyte, lbyte;
    
    SWD_EEE_CSEQ(0x00, addr);
    SWD_EEE_CSEQ(0xa0, addr);
    
    SWD_WriteByte(1, 0xaa, 1);
    lbyte = SWD_ReadByte(1, 0);
    hbyte = SWD_ReadByte(0, 1);
    SWD_Idle(10);
    SWD_EEE_CSEQ(0x00, addr);

    return (hbyte << 8) | lbyte;
}
  • 寫入write(),因flash爲16位,參數addr爲word地址,每次寫入兩個字節,即一個word數據
void SWD_EEE_Write(uint16_t data, uint16_t addr)
{
    volatile uint8_t ib;
    volatile uint8_t timout = 0x1f;
    
    SWD_EEE_CSEQ(0x00, addr);
    SWD_EEE_DSEQ(data);
    SWD_EEE_CSEQ(0x04, addr);
    SWD_EEE_CSEQ(0x84, addr);
    SWD_EEE_CSEQ(0x02, addr);
    
    do {
        delayus(50);
        ib = SWD_EEE_GetBusy();
        --timout;
    } while(ib == 1 && timout > 0);
    
    SWD_EEE_CSEQ(0x00, addr);
}

以上16位地址和數據輸入輸出均爲低字節優先,而後再高字節,即little-endian模式.

相關文章
相關標籤/搜索