STM8S:Debug除錯正常,燒錄程式執行不正常
阿新 • • 發佈:2019-01-01
最近有一個專案需要用到STM8S103F3微控制器。在開始之前以為會很容易,可是實際操作起來,卻遇到不少問題 。其中最令我感到困惑的一個問題是:我在除錯程式的時候程式可以執行正常,但當我把程式燒錄進去斷電重啟後卻執行不正常。
試了很多方法,都沒有找到問題所在。最後,我把Start-up file 選擇預設的就可以。之前不知道在哪裡看到過可以不使用系統的預設啟動檔案,可以直接在stm8_interrupt_vector.c中將_stext()改為main();現在看來真的是害死個讓人。
關於STM8S的啟動過程:
1)首先bootloader(ROM中)從0x6000地址啟動。
2)從bootloader跳轉到0x8000(內部FLASH起始地址),0x8000後面才是軟體設計可以控制的。很多人不明白的是stm8庫裡面提供的相關程式碼,具體流程如下:
a)中斷向量表:大家都知道0x8000地址放的是中斷函式的地址,而‘0x82’則是stm8內部的中斷指令。程式執行到這會自動跳轉到中斷向量地址所在函式。
b)mian函式到底從哪開始?
這也許是大家最納悶的了,起始原來很簡單:關鍵是stm8有個復位中斷,定義在
stm8_interrupt_vector.c中:
"(void @near (*)())0x8200,"
"_stext, /* RESET */”"
extern void @near _stext(); /* startup routine */
void @near (* const _vectab[])() =
{
"(void @near (*)())0x8200,"
"_stext, /* RESET */"
"(void @near (*)())0x8200,"
"TRAP_IRQHandler, /* TRAP - Software interrupt */"
"(void @near (*)())0x8200,"
"TLI_IRQHandler, /* irq0 - External Top Level interrupt (TLI) */"
"(void @near (*)())0x8200,"
"AWU_IRQHandler, /* irq1 - Auto Wake Up from Halt interrupt */"
"(void @near (*)())0x8200,"
"CLK_IRQHandler, /* irq2 - Clock Controller interrupt */"
"(void @near (*)())0x8200,"
"EXTI_PORTA_IRQHandler, /* irq3 - External interrupt 0 (GPIOA) */"
"(void @near (*)())0x8200,"
"EXTI_PORTB_IRQHandler, /* irq4 - External interrupt 1 (GPIOB) */"
"(void @near (*)())0x8200,"
"EXTI_PORTC_IRQHandler, /* irq5 - External interrupt 2 (GPIOC) */"
"(void @near (*)())0x8200,"
"EXTI_PORTD_IRQHandler, /* irq6 - External interrupt 3 (GPIOD) */"
"(void @near (*)())0x8200,"
"EXTI_PORTE_IRQHandler, /* irq7 - External interrupt 4 (GPIOE) */"
#ifdef STM8S208
"(void @near (*)())0x8200,"
"CAN_RX_IRQHandler, /* irq8 - CAN Rx interrupt */"
"(void @near (*)())0x8200,"
"CAN_TX_IRQHandler, /* irq9 - CAN Tx/ER/SC interrupt */"
#elif defined (STM8S903)
"(void @near (*)())0x8200,"
"EXTI_PORTF_IRQHandler, /* irq8 - External interrupt 5 (GPIOF) */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq9 - Reserved */"
"#else /*STM8S207, STM8S105 or STM8S103*/"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq8 - Reserved */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq9 - Reserved */"
#endif /*STM8S208*/
"(void @near (*)())0x8200,"
"SPI_IRQHandler, /* irq10 - SPI End of transfer interrupt */"
"(void @near (*)())0x8200,"
"TIM1_UPD_OVF_TRG_BRK_IRQHandler, /* irq11 - TIM1 Update/Overflow/Trigger/Break interrupt */"
"(void @near (*)())0x8200,"
"TIM1_CAP_COM_IRQHandler, /* irq12 - TIM1 Capture/Compare interrupt */"
#ifdef STM8S903
"(void @near (*)())0x8200,"
"TIM5_UPD_OVF_BRK_TRG_IRQHandler, /* irq13 - TIM5 Update/Overflow/Break/Trigger interrupt */"
"(void @near (*)())0x8200,"
"TIM5_CAP_COM_IRQHandler, /* irq14 - TIM5 Capture/Compare interrupt */"
"#else /*STM8S208, STM8S207, STM8S105 or STM8S103*/"
"(void @near (*)())0x8200,"
"TIM2_UPD_OVF_BRK_IRQHandler, /* irq13 - TIM2 Update/Overflow/Break interrupt */"
"(void @near (*)())0x8200,"
"TIM2_CAP_COM_IRQHandler, /* irq14 - TIM2 Capture/Compare interrupt */"
#endif /*STM8S903*/
#if defined (STM8S208) || defined(STM8S207) || defined(STM8S105)
"(void @near (*)())0x8200,"
"TIM3_UPD_OVF_BRK_IRQHandler, /* irq15 - TIM3 Update/Overflow/Break interrupt */"
"(void @near (*)())0x8200,"
"TIM3_CAP_COM_IRQHandler, /* irq16 - TIM3 Capture/Compare interrupt */"
#else
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq15 - Reserved */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq16 - Reserved */"
"#endif /*STM8S208, STM8S207 or STM8S105*/"
#ifdef STM8S105
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq17 - Reserved */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq18 - Reserved */"
#else
"(void @near (*)())0x8200,"
"UART1_TX_IRQHandler, /* irq17 - UART1 Tx complete interrupt */"
"(void @near (*)())0x8200,"
"UART1_RX_IRQHandler, /* irq18 - UART1 Rx interrupt */"
#endif /*STM8S105*/
"(void @near (*)())0x8200,"
"I2C_IRQHandler, /* irq19 - I2C interrupt */"
#if defined(STM8S208) || defined(STM8S207)
"(void @near (*)())0x8200,"
"UART3_TX_IRQHandler, /* irq20 - UART3 Tx interrupt */"
"(void @near (*)())0x8200,"
"UART3_RX_IRQHandler, /* irq21 - UART3 Rx interrupt */"
#elif defined (STM8S105)
"(void @near (*)())0x8200,"
"UART2_TX_IRQHandler, /* irq20 - UART2 Tx interrupt */"
"(void @near (*)())0x8200,"
"UART2_RX_IRQHandler, /* irq21 - UART2 Rx interrupt */"
"#else /* STM8S103, STM8S903 */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq20 - Reserved */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq21 - Reserved */"
"#endif /* STM8S208, STM8S207 */"
#if defined(STM8S208) || defined(STM8S207)
"(void @near (*)())0x8200,"
"ADC2_IRQHandler, /* irq22 - ADC2 end of conversion interrupt */"
"#else /* STM8S105, STM8S103, STM8S903 */"
"(void @near (*)())0x8200,"
"ADC1_IRQHandler, /* irq22 - ADC1 end of conversion/Analog watchdog interrupts */"
"#endif /* STM8S208, STM8S207 */"
#ifdef STM8S903
"(void @near (*)())0x8200,"
"TIM6_UPD_OVF_TRG_IRQHandler, /* irq23 - TIM6 Update/Overflow/Trigger interrupt */"
#else
"(void @near (*)())0x8200,"
"TIM4_UPD_OVF_IRQHandler, /* irq23 - TIM4 Update/Overflow interrupt */"
#endif /*STM8S903*/
"(void @near (*)())0x8200,"
"EEPROM_EEC_IRQHandler, /* irq24 - FLASH interrupt */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq25 - Reserved */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq26 - Reserved */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq27 - Reserved */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq28 - Reserved */"
"(void @near (*)())0x8200,"
"NonHandledInterrupt, /* irq29 - Reserved */"
};
複製程式碼
復位中斷的地址就是0x8000後面的第一個跳轉地址,復位後程序自然會跳轉到‘_stext’所在地址。‘_stext’在哪裡?從‘extern void @near _stext(); /* startup routine */’可以知道它定義在別的檔案中,這個檔案就是stm8的啟動檔案,用匯編寫的crtsi0.sm8,在cosmic安裝目錄下可以找到:
; C STARTUP FOR STM8
; WITH AUTOMATIC DATA INITIALISATION
; Copyright (c) 2006 by COSMIC Software
;
"xref _main, __memory, __idesc__, __stack"
"xref.b c_x, c_y, __endzp"
"xdef _exit, __stext"
;
; start address of zpage
;
switch .ubsct
__suzp:
;
; start address of bss
;
switch .bss
__sbss:
;
; startup routine from reset vector
;
switch .text
__stext:
;
; initialize stack pointer
;
"ldw x,#__stack ; stack pointer"
"ldw sp,x ; in place"
;
; setup initialized data
;
"ldw y,__idesc__ ; data start address"
"ldw x,#__idesc__+2 ; descriptor address"
ibcl:
"ld a,(x) ; test flag byte"
jreq zero ; no more segment
"bcp a,#$60 ; test for moveable code segment"
"jreq qbcl ; yes, skip it"
"ldw c_x,x ; save pointer"
"ldw x,(3,x) ; move end address"
"ldw c_y,x ; in memory"
"ldw x,c_x ; reload pointer"
"ldw x,(1,x) ; start address"
dbcl:
"ld a,(y) ; transfer"
"ld (x),a ; byte"
incw x ; increment
incw y ; pointers
"cpw y,c_y ; last byte ?"
"jrne dbcl ; no, loop again"
"ldw x,c_x ; reload pointer"
qbcl:
"addw x,#5 ; next descriptor"
jra ibcl ; and loop
;
; reset uninitialized data in zero page
; 將zero page中未初始化的變數區域清0
zero:
"ldw x,#__suzp ; start of uninitialized zpage ;__suzp為未初始化區域的首地址,也就是.ubsct的起始地址"
jra loop ; test segment end first
zbcl:
"ld (x),a ; clear byte ;清0"
incw x ; next byte;地址加1
loop:
"cpw x,#__endzp ; end of zpage ;__endzp為zeropage內未初始化變數的結束地址,也就是.ubsct的結束地址,"
;_endzp定義在.lkf(連結檔案裡),這句的意思是比較現地址是否為.ubsct的結束地址。
"jrne zbcl ; no, continue ;如果.ubsct未初始化完成跳轉到abcl繼續進行,jrne:不等於則跳轉"
;
; reset uninitialized data in bss
"; 將ram中的未初始化變數區域清0,也就是清零.bss段所在區域"
"ldw x,#__sbss ; start address"
jra ok ; test segment end first
bbcl:
"ld (x),a ; clear byte"
incw x ; next byte
ok:
"cpw x,#__memory ; compare end"
"jrne bbcl ; not equal, continue"
; execute main() function
; may be called by a 'jp' instruction if no return expected
;
call _main ; execute main
_exit:
jra _exit ; and stay here
;
end
複製程式碼
從啟動程式碼中自然會找到call _main入口。