pixhawk PX4FMU和PX4IO最底层启动过程分析
pixhawk PX4FMU和PX4IO最底層啟動(dòng)過(guò)程分析
1.1 主處理器和協(xié)處理器的固件燒寫和運(yùn)行流程
首先,大體了解PX4IO 與PX4FMU各自的任務(wù)。
PX4IO(STM32F100)為PIXHAWK 中專用于處理輸入輸出的部分,輸入為支持的各類遙控器(PPM,SPKT/DSM,SBUS), 輸出為電調(diào)的PWM 驅(qū)動(dòng)信號(hào), 它與PX4FMU(STM32F427)通過(guò)串口進(jìn)行通信。
PX4FMU :各種傳感器數(shù)據(jù)讀取、姿態(tài)解算、PWM控制量的計(jì)算、與PX4IO通信。負(fù)責(zé)飛控最主要的工作。
其中這兩處理器的固件燒寫運(yùn)行流程如下(也可以搜索相應(yīng)的文章):
主處理器和協(xié)處理器,固件下載到固件運(yùn)行的流程圖。值得說(shuō)明的一點(diǎn)是分別給兩個(gè)處理器下載blootloader以后,主處理器等待下載飛控固件,用地面站通過(guò)USB向飛控固件下載完以后,主處理器的飛控固件啟動(dòng),這時(shí)候通過(guò)串口給協(xié)處理器下載固件(固件只是下載一次)。這個(gè)工作完成之后,主處理器和協(xié)處理器同時(shí)開(kāi)始工作。
詳細(xì)可以看到如下代碼
#if defined(CONFIG_ARCH_BOARD_PX4FMU_V1)
fn[0] = "/etc/extras/px4io-v1.bin";
fn[1] = "/fs/microsd/px4io1.bin";
fn[2] = "/fs/microsd/px4io.bin";
fn[3] = nullptr;
#elif defined(CONFIG_ARCH_BOARD_PX4FMU_V2)
fn[0] = "/etc/extras/px4io-v2.bin";
fn[1] = "/fs/microsd/px4io2.bin";
fn[2] = "/fs/microsd/px4io.bin";
fn[3] = nullptr;
#else
#error "unknown board"
#endif
}
up = new PX4IO_Uploader;
int ret = up->upload(&fn[0]);//向協(xié)處理器下載固件
delete up;
這段代碼在px4io.cpp中,隨著px4io.cpp進(jìn)程啟動(dòng)而啟動(dòng)。功能就是由主處理器向協(xié)處理器燒寫代碼。
其中協(xié)處理器的固件源代碼在:
E:\Firmware-master\Firmware\src\modules\px4iofirmware?這里面主要是是一些,IO口的操作和安全開(kāi)關(guān)的操作。他會(huì)在編譯飛控固件的時(shí)候,一起被編譯成為px4io-v2.bin,放在NUTTX文件系統(tǒng)中,在主處理器運(yùn)行的時(shí)候把這個(gè)固件燒寫進(jìn)入?yún)f(xié)處理器。具體的燒寫到單片機(jī)的FLASH那個(gè)位置可以在px4io.cpp相關(guān)代碼找到。
在軟件層面的詳細(xì)流程圖如下:
1.2 Bootloader和NUTTX啟動(dòng)詳解
?????在嵌入式操作系統(tǒng)中,BootLoader是在操作系統(tǒng)內(nèi)核運(yùn)行之前運(yùn)行。可以初始化硬件設(shè)備、建立內(nèi)存空間映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好正確的環(huán)境。在嵌入式系統(tǒng)中,通常并沒(méi)有像BIOS那樣的固件程序(注,有的嵌入式CPU也會(huì)內(nèi)嵌一段短小的啟動(dòng)程序),因此整個(gè)系統(tǒng)的加載啟動(dòng)任務(wù)就完全由BootLoader來(lái)完成。Bootloader是嵌入式系統(tǒng)在加電后執(zhí)行的第一段代碼,在它完成CPU和相關(guān)硬件的初始化之后,再將操作系統(tǒng)映像或固化的嵌入式應(yīng)用程序裝在到內(nèi)存中然后跳轉(zhuǎn)到操作系統(tǒng)所在的空間,啟動(dòng)操作系統(tǒng)運(yùn)行.
? ?進(jìn)入px4/Bootloader可以看到main_f1、main_f4,分別對(duì)應(yīng)著PX4IO、PX4FMU的Bootloader.
值得注意的是單片機(jī)的二進(jìn)制代碼在單片機(jī)的啟動(dòng)位置
我們知道Pixhawk硬件使用STM32的芯片,
Cortex M3的內(nèi)核有三種啟動(dòng)方式,其分別是:
? ? ? A.通過(guò)boot引腳設(shè)置可以將中斷向量表定位于SRAM區(qū),即起始地址為0x2000000,同時(shí)復(fù)位后PC指針位于0x2000000處;? ? ? B.通過(guò)boot引腳設(shè)置可以將中斷向量表定位于FLASH區(qū),即起始地址為0x8000000,同時(shí)復(fù)位后PC指針位于0x8000000處;
? ? ? C.通過(guò)boot引腳設(shè)置可以將中斷向量表定位于內(nèi)置Bootloader區(qū),
M3單片機(jī)復(fù)位后,從0x00000000取棧指針(SP),從0x00000004取復(fù)位向量(PC),有了棧指針和復(fù)位向量后,單片機(jī)就按照正常流程運(yùn)行了,單片機(jī)啟動(dòng)默認(rèn)先運(yùn)行BootLoader,所以默認(rèn)的中斷向量表位置是BootLoader的中斷向量表。我們知道Bootloader是上電執(zhí)行的第一個(gè)代碼, Pixhawk的啟動(dòng)是通過(guò)B的方式來(lái)啟動(dòng)的,所以我們?cè)谙?/span>Bootloader的時(shí)候也是向FLASH這0x8000000地址燒寫的固件(具體可以看固件燒寫這篇文章),Bootloader燒寫成功以后每次上電就會(huì)啟動(dòng),啟動(dòng)的起始地址就是FLASH的0x8000000。Bootloader啟動(dòng)執(zhí)行到最后,會(huì)用匯編語(yǔ)句,自動(dòng)跳轉(zhuǎn)到FLASH中固件下載的位置,開(kāi)始執(zhí)行固件代碼。
NUTTX啟動(dòng)(也就是上面的固件代碼,匯編語(yǔ)句就是跳轉(zhuǎn)到這個(gè)位置來(lái)執(zhí)行stm32_start.c):
此部分摘自于pixhawk自學(xué)筆記之px4程序啟動(dòng)順序
代碼位置:Firmware/build_px4fmu-v2_default/px4fmu-v2/Nuttx/nuttx/arch/arm/src/stm32/stm32_start.c
__start-- #處理器執(zhí)行的第一條指令(px4使用的是stm32,入口在stm32_start.c中)
???stm32_clockconfig() ? #初始化時(shí)鐘
? ? rcc_reset() ? ? ? ?#復(fù)位rcc
? ? stm32_stdclockconfig() #初始化標(biāo)準(zhǔn)時(shí)鐘
? ? rcc_enableperipherals()#使能外設(shè)時(shí)鐘
------------------------------------------------------------------
? ? stm32_fpuconfig() ? ? ? ?#配置fpu
? ? stm32_lowsetup() ? ? ? ? #基本初始化串口,之后可以使用up_lowputc()
? ? stm32_gpioinit() ? ? ? ? #初始化gpio,只是調(diào)用stm32_gpioremap()設(shè)置重映射
? ? up_earlyserialinit() ? ? ?#初始化串口,之后可以使用up_putc()
? ? stm32_boardinitialize() ??
? ? stm32_spiinitialize() ? ? #初始化spi,只是調(diào)用stm32_configgpio()設(shè)置gpio
? ? stm32_usbinitialize() ? ? #初始化usb,只是調(diào)用stm32_configgpio()設(shè)置gpio
? ? up_ledinit(); ? ? ? ? ?#初始化led,只是調(diào)用stm32_configgpio()設(shè)置gpio
???----------------------------------------------------------------------------------------------
???在stm32_start.c文件中我們會(huì)看到這么一句話:
? ??? /* Then start NuttX */
? ? showprogress('\r');
? ? showprogress('\n');
? ? os_start();系統(tǒng)開(kāi)始啟動(dòng)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
一下是?os_start()內(nèi)容:
? ? dq_init()? ? ? ? ? ? ? ????? ?? ??? ?????#初始化各種狀態(tài)的任務(wù)列表(置為null)
? ? ? ? g_pidhash[i]= ? ? ? ? ?????? ?? ???? #初始化唯一可以確定的元素--進(jìn)程ID
? ? ? ? g_pidhash[PIDHASH(0)]= ? ? ?#分配空閑任務(wù)的進(jìn)程ID為0
? ? ? ? g_idletcb=? ? ? ? ? ? ? ? ? ? ?? ? ? ?#初始化空閑任務(wù)的任務(wù)控制塊
? ? ? ? sem_initialize()-- ? ? ? ? ? ? ?? ? ?#初始化信號(hào)量 ? ? ? ? ? ? ? ?
? ? ? ? dq_init() ? ? ? ? ? ? ? ?? ? ? ? ? ?#將信號(hào)量隊(duì)列置為null
? ? ? ? sem_initholders() ? ? ? ? ? ? #初始化持有者結(jié)構(gòu)以支持優(yōu)先級(jí)繼承
? ? ? ? up_allocate_heap() ? ? ? ? #分配用戶模式的堆(設(shè)置堆的起點(diǎn)和大小)
? ? ? ? kumm_initialize() ? ? ? ? ? ?#初始化用戶模式的堆
? ? ? ? up_allocate_kheap() ? ? ? ?#分配內(nèi)核模式的堆
? ? ? ? kmm_initialize() ? ? ? ? ? ? ? #初始化內(nèi)核模式的堆
? ? ? ? task_initialize() ? ? ? ? ? ? ? ?#初始化任務(wù)數(shù)據(jù)結(jié)構(gòu)
? ? ? ? irq_initialize() ? ? ? ? ? ? ? ?? #將所有中斷向量都指向同一個(gè)異常中斷處理程序
? ? ? ? wd_initialize() ? ? ? ? ? ? ? ?? #初始化看門狗數(shù)據(jù)結(jié)構(gòu)
? ? ? ? clock_initialize() ? ? ? ? ? ? ? #初始化rtc
? ? ? ? timer_initialize() ? ? ? ? ? ? ? #配置POSIX定時(shí)器
? ? ? ? sig_initialize() ? ? ? ? ? ? ? ?? #初始化信號(hào)
? ? ? ? mq_initialize() ? ? ? ? ? ? ? ?? #初始化命名消息隊(duì)列
? ? ? ? pthread_initialize() ? ? ? ? ? #初始化線程特定的數(shù)據(jù),空函數(shù)
? ? ? ? fs_initialize()---? ? ? ? ? ? ? ? #初始化文件系統(tǒng) ? ? ? ? ? ? ?
? ? ? ? sem_init() ? ? ? ? #初始化節(jié)點(diǎn)信號(hào)量為1
? ? ? ? files_initialize() #初始化文件數(shù)組,空函數(shù)
? ? ? ? net_initialize()-- ? ? ? #初始化網(wǎng)絡(luò)
? ? ? ? uip_initialize() ??? ??#初始化uIP層
? ? ? ? net_initroute() ??? ???#初始化路由表
? ? ? ? netdev_seminit() ? #初始化網(wǎng)絡(luò)設(shè)備信號(hào)量
? ? ? ? arptimer_init() ? ?? ??#初始化ARP定時(shí)器
? ? ? ? up_initialize()--- ? ? ? #處理器特定的初始化
? ? ? ? up_calibratedelay()? ??? ?????#校準(zhǔn)定時(shí)器
? ? ? ? up_addregion() ??? ?????? ? ?? #增加額外的內(nèi)存段
? ? ? ? up_irqinitialize()?? ?????? ??? ??#設(shè)置中斷優(yōu)先級(jí),關(guān)聯(lián)硬件異常處理函數(shù)
? ? ? ? up_pminitialize() ?? ?????? ????#初始化電源管理
? ? ? ? up_dmainitialize()?? ?????? ????#初始化DMA
? ? ? ? up_timerinit() ? ??? ?????? ??? ???#初始化定時(shí)器中斷
? ? ? ? devnull_register()?? ?????? ??? ?#注冊(cè)/dev/null
? ? ? ? devzero_register()?? ?????? ????#注冊(cè)/dev/zero
? ? ? ? up_serialinit() ? ?? ?????? ??? ??? ?#注冊(cè)串口控制臺(tái)/dev/console和串口/dev/ttyS0
? ? ? ? up_rnginitialize()?? ?????? ??? ??#初始化并注冊(cè)隨機(jī)數(shù)生成器
? ? ? ? up_netinitialize()?? ?????? ??? ???#初始化網(wǎng)絡(luò),是arch/arm/src/chip/stm32_eth.c中的
? ? ? ? up_usbinitialize()?? ?????? ??? ???#初始化usb驅(qū)動(dòng)
? ? ? ? board_led_on() ? ??? ?????? ??? ???#打開(kāi)中斷使能led,但很快會(huì)被其它地方的led操作改變狀態(tài)
? ? ? ? lib_initialize() ? ? ? ?????? ??? ? ?#初始化c庫(kù),空函數(shù)
? ? ? ? group_allocate() ? ? ? ?????? ??#分配空閑組
? ? ? ? group_setupidlefiles() ? ? ? ?#在空閑任務(wù)上創(chuàng)建stdout、stderr、stdin
? ? ? ? group_initialize() ? ? ? ? ? ? ??#完全初始化空閑組
? ? ? ? os_bringup()------ ? ? ? ? ? ? ?#創(chuàng)建初始任務(wù)
? ? ? ? KEKERNEL_THREAD() ? ?#啟動(dòng)內(nèi)核工作者線程
? ? ? ? board_initialize() ? ? ? ? ? #最后一刻的板級(jí)初始化
? ? ? ? TASK_CREATE() ? ? ? ? ? ? #啟動(dòng)默認(rèn)應(yīng)用程序
? ? ? ? forup_idle() ? ? ? ? ? ? ? ? ? ?? ? ?#空閑任務(wù)循環(huán)
? ? ? ? for(;;) ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ?#不應(yīng)該到達(dá)這里
__start 負(fù)責(zé)STM32 芯片的底層初始化,包括時(shí)鐘,FPU,GPIO 等單元的初始化
os_start 負(fù)責(zé)OS 的底層初始化,包括各種隊(duì)列和進(jìn)程結(jié)構(gòu)的初始化
os_bringup 負(fù)責(zé)OS 基本進(jìn)程的啟動(dòng)以及用戶進(jìn)程的啟動(dòng),用戶啟動(dòng)入口由(CONFIG_USER_ENTRYPOINT)宏進(jìn)行指定是執(zhí)行nsh_main還是user_start。? ??
啟動(dòng)腳本分析
總結(jié)
以上是生活随笔為你收集整理的pixhawk PX4FMU和PX4IO最底层启动过程分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: pixhawk原生固件笔记
- 下一篇: C语言指针详解----指针声明定义赋值