手把手帶你做LiteOS的樹莓派移植
摘要:樹莓派是英國的慈善組織“Raspberry Pi 基金會”開發的一款基於arm的微型電腦主機板。本文介紹基於LiteOS的樹莓派移植過程。
本文分享自華為雲社群《2021 LiteOS樹莓派移植指南(一)》,作者: Lionlace 。
樹莓派是英國的慈善組織“Raspberry Pi 基金會”開發的一款基於arm的微型電腦主機板。本文介紹基於LiteOS的樹莓派移植過程。
硬體資訊
開發板:Raspberry Pi 2 Model B(樹莓派2B)
CPU:Broadcom BCM2836
主頻:900MHz
記憶體:1GB
GPU:VideoCore IV GPU
移植準備
硬體環境
本實驗使用了Raspberry Pi 2 Model B開發板、USB轉TTL模組、SDcard和讀卡器。
軟體環境
- 本實驗需要先按照碼雲上的LiteOS教程搭建好linux環境(make、arm-none-eabi編譯工具鏈)。環境搭建教程:https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Build_and_IDE.md
- 本實驗需要下載官方的映象製作工具(Raspberry Pi Imager),下載地址:https://www.raspberrypi.org/software/
移植步驟
建立目錄結構
在targets目錄下新增Raspberry_Pi2B目錄,參考與cortex-A7架構差異較小的realview-pbx-a9的啟動流程進行移植。
- 將realview-pbx-a9目錄下的reset_vector.S和main.c拷貝到Raspberry_Pi2B目錄下並將reset_vector.S重新命名為los_startup_gcc.S。
- 將realview-pbx-a9目錄下的board.ld和liteos.ld中內容合併到Raspberry_Pi2B目錄下liteos.ld檔案中。
- 拷貝realview-pbx-a9目錄下include、os_adapt資料夾到Raspberry_Pi2B目錄下,並刪除不需要的dma相關標頭檔案include/asm/dma.h。
關閉SMP和MMU
在los_startup_gcc.S檔案中增加關閉SMP和MMU的程式碼。
- 關閉SMP功能
mrc p15, 0, r0, c1, c0, 1 bic r0, r0, #0x40 mcr p15, 0, r0, c1, c0, 1
上表是ACTLR(Auxiliary Control Register)暫存器bit6功能描述資訊,瞭解更多暫存器相關資訊可以參考Cortex-A7 MPCore Technical Reference Manual。
- 關閉MMU的功能
mrc p15, #0, r0, c1, c0, #0 bic r0, r0, #1 mcr p15, #0, r0, c1, c0, #0 @ clear mmu bit
上表是SCTLR (System Control Register)暫存器bit0功能描述資訊,瞭解更多暫存器相關資訊可以參考Cortex-A7MPCore Technical Reference Manual。
- 刪除呼叫SMP相關函式
刪除los_startup_gcc.S中的enable_scu和secondary_cpu_start。
使能FPU/ENON
配置FPU/NEON:
/* enable fpu+neon */ LDR r0, =(0xF << 20) MCR p15, 0, r0, c1, c0, 2 MOV r3, #0x40000000 VMSR FPEXC, r3
以上前兩行程式碼用於設定CP10和CP11的訪問許可權,後兩行用於設定暫存器FPEXC的EN位來使能FPU。
注:在arm的協處理器設計中,最多可以支援16個協處理器,通常被命名為cp0~cp15。
上表為暫存器CPACR bit20-23功能描述資訊,瞭解更多暫存器相關資訊可以參考Cortex-A7 MPCore Technical Reference Manual。
修改連結指令碼
樹莓派啟動時首先載入SD卡中的start.elf檔案,該程式會讀取SD卡中的config.txt檔案內容,該檔案記錄了一些配置資訊。如果沒有設定啟動地址和啟動檔案,則預設會載入kernel8.img檔案,該檔案是aarch64編譯的程式,啟動地址為0x80000。如果SD卡中無kernel8.img映象檔案,則會載入kernel7.img映象檔案,該檔案是32位編譯器編譯的程式,啟動地址為0x8000。樹莓派2B的cpu是32位架構,因此設定liteos.ld檔案中啟動地址為0x8000。
棧初始化
樹莓派2B啟動檔案los_startup_gcc.S中只設置了SVC模式的sp暫存器,新增cpuInit函式來初始化其他模式的sp指標。如下所示:
VOID cpuInit(VOID) { __asm__ ( "msr cpsr_c, %1\n\t" "mov sp, %0\n\t" "msr cpsr_c, %3\n\t" "mov sp, %2\n\t" "msr cpsr_c, %5\n\t" "mov sp, %4\n\t" "msr cpsr_c, %7\n\t" "mov sp, %6\n\t" "msr cpsr_c, %8\n\t" : : "r" (__irq_stack_top), "I" (PSR_F_BIT | PSR_I_BIT | CPSR_IRQ_MODE), "r" (__abt_stack_top), "I" (PSR_F_BIT | PSR_I_BIT | CPSR_ABT_MODE), "r" (__undef_stack_top), "I" (PSR_F_BIT | PSR_I_BIT | CPSR_UNDEF_MODE), "r" (__fiq_stack_top), "I" (PSR_F_BIT | PSR_I_BIT | CPSR_FIQ_MODE), "I" (PSR_F_BIT | PSR_I_BIT | CPSR_SVC_MODE) : "r14"); }
配置動態記憶體地址
#define OS_SYS_MEM_ADDR ((void *)(&__bss_end)) #define LOS_HEAP_ADDR_END (void*)(0x0 + 4 * 1024 * 1024) #define OS_SYS_MEM_SIZE (UINT32)(((UINT32)LOS_HEAP_ADDR_END - (UINT32)OS_SYS_MEM_ADDR + (64 - 1)) & ~(64 - 1))
以上程式碼定義OS_SYS_MEM_ADDR為動態記憶體起始地址,LOS_HEAP_ADDR_END為動態記憶體結束地址,OS_SYS_MEM_SIZE為動態記憶體大小。
串列埠實現
樹莓派2B原理圖引出了mini_uart串列埠TXD0、RXD0,對應的引腳為GPIO14、GPIO15,如下圖所示:
建立usart.c和usart.h檔案,在usart.c中編寫串列埠初始化函式UartInit,並實現uart_debug.c檔案中uart_getc、uart_hwiCreate、uart_write介面,實現printf函式從串列埠輸出。
適配中斷
樹莓派2B的中斷屬於bcm特定的中斷控制器。在drivers/interrupt目錄下新增arm_control.c檔案,並在該檔案中實現HwiControllerOps結構體內的回撥函式。
STATIC const HwiControllerOps g_armControlOps = { .enableIrq = HalIrqUnmask, .disableIrq = HalIrqMask, .getCurIrqNum = HalCurIrqGet, .getIrqVersion = HalIrqVersion, .getHandleForm = HalIrqGetHandleForm, .handleIrq = IrqEntryArmControl, .clearIrq = HalIrqClear, .triggerIrq = HalIrqPending, };
以上表格是interrupt暫存器偏移地址,讀者想了解詳細暫存器相關資訊請參考官方晶片手冊。
適配systick
樹莓派2B通過Timer(arm side)來觸發systick中斷。具體操作細節請參考檔案:drivers\timer\rasp_systick.c。
/* systime=250000000 */ timer->preDivider = (OS_SYS_CLOCK / OS_SYS_US_PER_SECOND - 1); timer->reload = 0; timer->load = 0; timer->IRQClear = 0; timer->control = 0; timer->reload = LOSCFG_BASE_CORE_TICK_PER_SECOND; timer->load = LOSCFG_BASE_CORE_TICK_PER_SECOND; /* 23-bit counter, enable interrupt, enable timer */ timer->control = (1 << 1) | (1 << 5) | (1 << 7); UINT32 ret = LOS_HwiEnable(ARM_TIMER_INI);
以上程式碼配置定時器Timer為每1ms觸發一次systick中斷。
以上是Timer暫存器偏移地址,讀者想了解詳細暫存器相關資訊請參考官方晶片手冊。
配置編譯
在targets目錄下新增kconfig.raspberry檔案:
ConfigLOSCFG_PLATFORM config LOSCFG_PLATFORM string default "Raspberry_Pi2B" if LOSCFG_PLATFORM_Raspberry_Pi2B choice prompt "Board" depends on LOSCFG_FAMILY_RASPBERRY default LOSCFG_PLATFORM_Raspberry_Pi2B help Raspberry_Pi2B config LOSCFG_PLATFORM_Raspberry_Pi2B bool "Raspberry_Pi2B" select LOSCFG_ARCH_CORTEX_A7 select LOSCFG_USING_BOARD_LD select LOSCFG_PLATFORM_ARM_CONTROL select LOSCFG_Raspberry_Pi2B_SYSTICK endchoice
修改Makefile檔案
分別修改以下路徑Makefile(詳情請參考gitee倉庫對應檔案):driver/timer/Makefiledriver/interrupt/Makefiletargets/Raspberry_Pi2B/Makefile
新增.img生成指令
在根目錄下Makefile中新增指令$(OBJCOPY) -O binary $(OUT)/[email protected] $(OUT)/kernel7.img,用來將生成的elf檔案轉換生成kernel7.img檔案。
製作啟動SDcard
- 使用Raspberry Pi Imager工具製作Raspberry Pi系統。
Raspberry Pi Imager下載連結:https://www.raspberrypi.org/software/
- 將編譯生成的kernel7.img檔案替換掉SDcard中kernel7.img檔案。
- 將寫入映象檔案的SDcard插入樹莓派2B中並上電,樹莓派2B即可執行LiteOS系統。執行結果如下:
********Hello Huawei LiteOS******** LiteOS Kernel Version : 5.1.0 build data : Jul 13 2021 16:40:42 ********************************** OsAppInit cpu 0 entering scheduler app init! Hello, welcome to liteos demo! Huawei LiteOS #
至此,LiteOS系統成功啟動和執行。該移植工程已經在GiteeLiteOS社群上線,相關程式碼連結地址為:https://gitee.com/LiteOS/LiteOS/tree/master/targets/Raspberry_Pi2B
參考文獻連結
[1]Raspberry Pihardware - Raspberry Pi Documentation:https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md
[2]樹莓派官方晶片手冊:
https://datasheets.raspberrypi.org/bcm2835/bcm2835-peripherals.pdf
[3] Cortex-A7MPCoreTechnical Reference Manual:
https://developer.arm.com/documentation/ddi0464/f?lang=en