移植後的代碼戳這裏: https://code.csdn.net/KISSMonX/freertos_f3discovery_test
在上一篇文章 ARM 彙編中的 "B ." 語句意義.時, 順帶介紹了 [WEAK] 的作用.
昨天再思考移植問題的時候(也就是執行第一個任務時直接跳到 SVC_Handler 裏的 B . 處),
想到了這個問題, 然後在移植配置文件中添加了幾個宏定義就解決了問題, 移植成功, 具體下文介紹.
這裏再做一次解釋. 看看自己是不是真正的理解了. :)
第一步明顯是要貼代碼裝逼, 去啓動文件裏摘取出要介紹的部分. 然後記筆記的形式摘錄下來.如下:
; Reset handler 這裏纔是啓動文件的重點啊. 不過沒見到爲 C 程序建立棧空間操作啊??? 直接調用 main 大丈夫? MAN???
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMPER_STAMP_IRQHandler [WEAK]
EXPORT RTC_WKUP_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_TS_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT DMA1_Channel7_IRQHandler [WEAK]
EXPORT ADC1_2_IRQHandler [WEAK]
EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK]
EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_TIM15_IRQHandler [WEAK]
EXPORT TIM1_UP_TIM16_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_TIM17_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT USBWakeUp_IRQHandler [WEAK]
EXPORT TIM8_BRK_IRQHandler [WEAK]
EXPORT TIM8_UP_IRQHandler [WEAK]
EXPORT TIM8_TRG_COM_IRQHandler [WEAK]
EXPORT TIM8_CC_IRQHandler [WEAK]
EXPORT ADC3_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_DAC_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Channel1_IRQHandler [WEAK]
EXPORT DMA2_Channel2_IRQHandler [WEAK]
EXPORT DMA2_Channel3_IRQHandler [WEAK]
EXPORT DMA2_Channel4_IRQHandler [WEAK]
EXPORT DMA2_Channel5_IRQHandler [WEAK]
EXPORT ADC4_IRQHandler [WEAK]
EXPORT COMP1_2_3_IRQHandler [WEAK]
EXPORT COMP4_5_6_IRQHandler [WEAK]
EXPORT COMP7_IRQHandler [WEAK]
EXPORT USB_HP_IRQHandler [WEAK]
EXPORT USB_LP_IRQHandler [WEAK]
EXPORT USBWakeUp_RMP_IRQHandler [WEAK]
EXPORT FPU_IRQHandler [WEAK]
然後到 keil 的幫助文檔裏找到這麼一句話:
/* WEAK : symbol is only imported into other sources if no other source exports an alternative symbol.
If [WEAK] is used without symbol, all exported symbols are weak. */
緊接着下面就有這種介紹:
比我介紹的詳細準確多了.
英語不好的就算了吧. 大致意思就是:
// 意思就是告訴鏈接器:
// "我略弱但我很紳士, 如果你在別處看到和我一樣的符號實例.你就用它吧. 表管我, 求忽視! "
所以......知道這個, 就可以解決爲什麼 FreeRTOS 在執行到下面這段代碼老是跳轉到 SVC_Handler 處了.
<亂入> SVC 作用: SVCall A supervisor call (SVC) is
an exception that is triggered by the SVC instruction. In an OS environment, applications can use SVC instructions to
access OS kernel functions and device drivers.
__asm void prvStartFirstTask( void )
{
PRESERVE8
/* Use the NVIC offset register to locate the stack. */
ldr r0, =0xE000ED08
ldr r0, [r0]
ldr r0, [r0]
/* Set the msp back to the start of the stack. */
msr msp, r0
/* Globally enable interrupts. */
cpsie i
dsb
isb
/* Call SVC to start the first task. */
svc 0 // 0 號系統調用, 更多關於 SVC 可以參考 google 或者 ARM Cortex-M3 權威指南
nop
}
因爲各個開發工具廠家對這些啓動文件的異常和中斷只做了簡單的處理. 基本都是死循環(也就是"B .").而且特意加上了 [WEAK] 修飾. 這樣用戶可以根據自己的需要重新編寫自己的處理函數, 而且只要命名一樣就 OK 了.
那名字要是不一樣怎麼辦? 然後, 我去找了 SVC_Handler 這個名字, 並沒有找到, 但是在 port.c 中找到了
下面這個函數:
__asm void vPortSVCHandler( void )
{
PRESERVE8
/* Get the location of the current TCB. */
ldr r3, =pxCurrentTCB
ldr r1, [r3]
ldr r0, [r1]
/* Pop the core registers. */
ldmia r0!, {r4-r11, r14}
msr psp, r0
isb
mov r0, #0
msr basepri, r0
bx r14
}
這不就是披了馬甲的 SVC_Handler 嗎? 尼瑪......顯然裏面的內容我不太明白, 就不解釋了. 再學.然後我當然沒有那麼聰明, 我找到了以前移植過的文件看了一下, 發現在 portmacro.h 中有下面三個宏定義:
#define vPortSVCHandler SVC_Handler
#define xPortSysTickHandler SysTick_Handler
#define xPortPendSVHandler PendSV_Handler
掃噶. 原來宏名也比 WEAK 修飾過的強啊. 重新編譯鏈接沒錯. 燒寫, 各任務運行正常.