IAP的原理和stm8的IAP

一、引出(IAP的原理和stm8上實現IAP的問題)

        具有IAP功能的單片機,程序可以分爲兩部分:IAP和APP。APP是用來實現真正功能的程序,而IAP是用來遠程重新編程APP的程序。單片機上電時會先執行IAP程序,在IAP中判斷APP是否正常,然後再跳轉到APP中執行。

        這樣就會有一個問題,那就是中斷向量表的問題。當發生中斷時,單片機會去中斷向量表中查詢中斷服務函數的地址,然後才能按照地址跳轉到中斷服務函數去執行。中斷向量表一般都在程序的開頭,當把編譯好的IAP下載到單片機裏,單片機上電執行IAP程序,在執行IAP時發生中斷,單片機就會到IAP的開頭去查詢中斷服務函數地址,然後當從IAP跳轉到APP後,開始執行APP程序,這個時候發生中斷,單片機仍然會到IAP的開頭去查詢中斷服務函數地址,這裏就出現問題了。單片機在執行APP時,應該到APP的開頭去查詢中斷服務函數地址纔對。

        因此需要某種方法通知單片機,當我們執行APP時,要在APP的開頭去查詢中斷服務函數地址。在stm32單片機中,只需要在執行APP程序前加一句:

SCB->VTOR = FLASH_BASE | 0x4000;   //0x4000是APP的開始地址

因爲stm32有地址偏移寄存器SCB->VTOR,當發生中斷時,stm32會去SCB->VTOR讀取中斷向量表的地址。執行上面的語句後,發生中斷時就會到FLASH_BASE | 0x4000地址去查詢中斷服務函數地址。

        而stm8沒有地址偏移寄存器,所以我們需要其他的辦法來解決。中斷向量表實際上就是:跳轉指令+地址。當發生中斷單片機會去執行中斷向量表,也就是執行跳轉指令,跳轉到目標地址。明白了這個,那我們的方法也就出來了:改寫IAP的中斷向量表,把它的地址改爲APP的中斷向量表的地址。這樣,當在APP中發生中斷,單片機會跳轉到IAP的開頭去執行中斷向量表,然後跳轉到APP的中斷向量表,最後才跳轉到APP的中斷服務函數。當然,這樣一來IAP的中斷就用不了了。

二、stm8的IAP

        stm8的Flash是字節編程的,而且不需要先擦除再寫,可以直接寫。因此stm8的IAP處理流程爲:進入IAP---解鎖Flash---接收APP數據寫Flash---改寫IAP的中斷向量表---Flash上鎖----跳轉到APP。

        讀函數、解鎖、上鎖、寫函數爲:

//讀出一個32位數
uint32_t FLASH_Read(uint32_t Address)  
{
    return(*(PointerAttr uint32_t *) (uint16_t)Address);       
}

//解鎖Flash,在寫Flash前,只需要在IAP中調用一次
void FLASH_Unlock(FLASH_MemType_TypeDef FLASH_MemType)  
{
    FLASH->PUKR = FLASH_RASS_KEY1;
    FLASH->PUKR = FLASH_RASS_KEY2;
}

//Flash上鎖,寫完Flash後,調用一次
void FLASH_Lock(FLASH_MemType_TypeDef FLASH_MemType)
{
    FLASH->IAPSR &= (uint8_t)FLASH_MemType;
}

//往Flash中寫入一個8位數
void FLASH_ProgramByte(uint32_t Address, uint8_t Data)
{
    *(PointerAttr uint8_t*) (uint16_t)Address = Data;
}

//往Flash中寫入一個32位數
void FLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
    /* Enable Word Write Once */
    FLASH->CR2 |= FLASH_CR2_WPRG;
    FLASH->NCR2 &= (uint8_t)(~FLASH_NCR2_NWPRG);

    /* Write one byte - from lowest address*/
    *((PointerAttr uint8_t*)(uint16_t)Address) = 0x00;
    *((PointerAttr uint8_t*)(uint16_t)Address) = *((uint8_t*)(&Data));
    /* Write one byte*/
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 1) = 0x00;
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 1) = *((uint8_t*)(&Data)+1); 
    /* Write one byte*/    
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 2) = 0x00;
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 2) = *((uint8_t*)(&Data)+2); 
    /* Write one byte - from higher address*/
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 3) = 0x00;
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 3) = *((uint8_t*)(&Data)+3); 
}

 

        改寫IAP向量表的函數爲:

//重新初始化STM8的中斷向量表  把它重新定義到APP的中斷向量中
void STM8_HanderIqr_Init(void)
{
    uint8_t Index;
    
    disableInterrupts();   //關閉中斷   	
    FLASH_Unlock(FLASH_MEMTYPE_PROG);
    for(Index = 1; Index < 32;Index++)
    {
        FLASH_ProgramWord(0x8000+4*Index,0x82000000+APPLICATION_ADDRESS+Index*4);
    }
    FLASH_Lock(FLASH_MEMTYPE_PROG);
}

 

        跳轉到APP的函數爲:

void JPMainProgram(void)
{
    //跳轉至APP
    asm("LDW X,  SP ");
    asm("LD  A,  $FF");
    asm("LD  XL, A  ");
    asm("LDW SP, X  ");
    asm("JPF $9000");     //0x9000是APP的地址,根據自己的情況來改
}

 

最後:

stm8的Flash很小,建議使用寄存器操作,不要用庫函數,否則不好控制代碼大小。

IAR的SWIM仿真對於Memory的支持不是很好,當調用上面的函數改寫Flash後,從Memory窗口上看可能並未改變,但實際上已經被改寫了。(本人被坑了半天)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章