12998|20

26

帖子

0

TA的资源

一粒金砂(中级)

[学习笔记]使用GNU Toolchain在STM32上跑起一个最小OS [复制链接]

本帖最后由 john0517 于 2015-5-1 02:24 编辑

[学习笔记]使用GNU Toolchain在STM32上跑起一个最小OS

前言:

我是从小学2年级开始接触电脑的,那个时候的系统满大街都是windows95,如果再早一点,接触到的系统应该是在小霸王游戏机上。十几年过去了(好伤感XD),现在终于知道一个最简单的操作系统,从硬件到软件,完整的,是怎么怎么运作的。


这是我这学期旁听一门嵌入式系统课程的其中某一节课的作业笔记

从基本的寄存器开始到完整的跑起一个最最最基本的OS

整个学习过程感觉受益非浅

因此在这里,把学习心得分享给大家,特别是初学者啦

之前是在Hackpad上做的笔记

搬到这里来可能格式不太好看,还望见谅(图很多,点开即可看清)
(支持open source,若转载请注明出处)


最后要感谢这门课的老师——Jserv大,其教书育人的热血信念和对技术的严谨与痴迷令人佩服不已!

很难想象这是一门大学部的课,正是得益于他不断的鞭策,才让学生有更多前进的动力!


Happy hacking,

-Luckyjoou



最新回复

你好,我在用GNUC编写STM32107VC程序,现在从keil里拿来的Systick中断程序在开完中断的瞬间程序就跑飞了,不停地跑Systickhandler,主程序执行顺序都是乱的,找了半天没有什么头绪。 请问你有没有写好的systick延时程序GNUC编译通过的? QQ467431321求联系!!求指教!!  详情 回复 发表于 2016-7-12 11:30

赞赏

2

查看全部赞赏


回复
举报

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:04 编辑

STM32 程式發開:以GNU Toolchain







回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:02 编辑

STM32F429驗證 Lab1
  • ld文件中,.text : {}的.text與:中間必須有空格
  • STM32F429的schematics在此文檔裏:
  • LED部分的電路圖如下



  • Discovery所用的STM32F429ZIT6:
  • STM32F429ZIT6的Flash Memory也是從0x0800 0000開始, SRAM從0x2000 0000開始, 不過注意到不同是F429的SRAM分成了112KB、16KB、64KB,Google了一下也沒看到爲什麼要這麼分,好奇
  • 關於時鐘源:





  • 關於GPIO:
  • port13 14,output push-pull,high speed











  • make時提示:gcc: warning: ‘-mcpu=’ is deprecated; use ‘-mtune=’ or ‘-march=’ instead
  • CROSS_COMPILE ?= arm-none-eabi-
  • .PHONY: all
  • all: blink.bin
  • blink.o: blink.c
  •                 $(CROSS_COMPILE)gcc -mtune=cortex-m4 -mthumb -nostartfiles -c blink.c -o blink.o
  • blink.out: blink.o simple.ld
  •                 $(CROSS_COMPILE)ld -T simple.ld -o blink.out blink.o
  • blink.bin: blink.out
  •                 $(CROSS_COMPILE)objcopy -j .text -O binary blink.out blink.bin
  • flash: $(blink.bin)
  •         openocd \
  •                 -f interface/stlink-v2.cfg \
  •                 -f target/stm32f4x.cfg \
  •                 -c "init" \
  •                 -c "reset init" \
  •                 -c "flash probe 0" \
  •                 -c "flash info 0" \
  •                 -c "flash write_image erase blink.bin 0x8000000" \
  •                 -c "reset run" -c shutdown || \
  •         st-flash write blink.bin 0x8000000
  • clean:
  •                 rm -f *.o *.out *.bin

  • 接着make flash,可見LED3和LED4間隔閃爍,如下:




回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:03 编辑

STM32F429驗證 Lab2
  • 依照文檔將
  •     FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 2048k
  •     SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 112k
  • 編譯運行正常,現象和lab1相同



回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:04 编辑

STM32F429驗證 Lab3(SYSTICK精確定時)



  • 自己又給自己上了一課,白白耗費了幾個小時,以後困了真不如先睡飽再寫...:(2個問題如下)
  • #define STK_BASE (*((volatile unsigned long*)(0xE000E010)))
  • #define STK_CRTL (*((volatile unsigned long*)(STK_BASE)))
  • ...
  • 解決完問題才想到gdb,假如直接用gdb來debug應該會很快
  • 使用systic定時,時間計算:
  • 採用HSE時鐘源(F429板子上已焊有8MHz晶振),通過AHB PRESC時不分頻,經過System Timer的bus時8分頻,這樣到達system timer的頻率就是1Mhz,每一個tick就是1us
  • 然後STK_LOAD設爲1000000,STK_VAL設爲0,這樣count完一次就是1000000*1us=1s
  • 實際現象請看:



回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:05 编辑

MINI-ARM-OS
注1:这个操作系统是课程老师为了课程而开发的,后续还有发展在这里不赘述,源代码请上Github搜索mini-arm-os
注2:以下实验均在qemu模拟器上执行,等我有空再实际搬到STM32F429Discovery上


00-HelloWorld
  • 先make然後make qemu後出現hello world,然後輸入arm-none-eabi-objdump -D hello.elf查看executable and linkable文件




  • 這裏值得注意的是,在M3上電取出MSP後,會取出PC的值,圖中可見是0x000000ad。但實際上reset_handler地址是從000000ac開始的。這是因爲:
  • 當一個例外處理程式(exception handler)的位址在LSB設定為1,代表該例外處理程式運作於Thumb模式(Thumb mode),對ARM Cortex-M3來說,這是必要的,因為該處理器核心只支援Thumb-2指令集,而不支援ARM模式(也稱ARM code或ARM state)。
  • 搭配hello.ld和startup.c一起看終於有種恍然大悟的感覺。.text section最前面的位置就是isr_vector,然後再是其他.text文件。當m3從Flash開始讀入數據時,首先就是isr_vector的內容,而isr_vector中首先給MSP地址賦0,然後是reset_handler,而reset_handler指向Main函數,這樣就導入我們寫的主函數了




回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:05 编辑

01-HelloWorld
  • 此實驗和00相比在ld上作了更多文章。00-HelloWorld的ld文件僅配置了Flash和.text section,而01中則多配置了RAM,將運行時不會更改的.text、.rodata、放入.text section中,.data放入.data section中
  • AT(_sidata)的_sidata表示了.data載入RAM後的VMA。因爲data section會從flash搬移到SRAM中,因此它的LMA和VMA會不同。連結器腳本的一個重要作用,就是管理個別section的LMA和VMA,並在必要的情況下,把有關資訊提供給程式程式碼使用。
  • /* Copy the data segment initializers from flash to SRAM */
  •         uint32_t *idata_begin = &_sidata;
  •         uint32_t *data_begin = &_sdata;
  •         uint32_t *data_end = &_edata;
  •         while (data_begin < data_end) *data_begin++ = *idata_begin++;
  • 由startup.c中的這段代碼可知,_sidata中存放的是LMA



回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:06 编辑

02-ContextSwitch-1
  • 在startup.c中定義了nmi_handler和hardfault_handler,地址在reset_handler之後,什麼時候會觸發他們呢?
  • msp的值爲_estack=RAM的初始位置加上RAM的寬度,即0x20000000+40k,這樣意味這stack的爲40K大小,這個大小的stack感覺足夠寬裕!
  • 接着看os.c裏的main函數:
  • int main(void)
  • {
  •         /* Initialization of process stack.
  •          * r4, r5, r6, r7, r8, r9, r10, r11, lr */
  •         unsigned int usertask_stack[256];
  •         unsigned int *usertask_stack_start = usertask_stack + 256 - 16;
  •         usertask_stack_start[8] = (unsigned int) &usertask;

  •         usart_init();

  •         print_str("OS Starting...\n");
  •         activate(usertask_stack_start);

  • //        while (1); /* We can't exit, there is nowhere to go */

  •         return 0;
  • }
  • 首先定義額usertask的堆棧大小256*4byte=1k,然後定義一個指針*usertask_stack_start指向usertask堆棧加256-16的距離,而usertask的地址從usertask_stack_start棧底-8才開始,這中間多出了24byte。
  • 我想這應該是call stack的概念,爲了後門調用usertask函數的argument、return address、saved ebp%等留下空間(24byte應該是6個空格)
  • contex_swtich.s中.syntax unified:Cortex-m3爲了兼容thumb指令和thumb2指令,使這兩種指令可以使用統一的格式,引入了一種叫做“UAL”的語法機制。簡單說來就是程序員不用關心自己使用的是thumb還是thumb2指令,而是統一使用32位thumb2指令的語法格式書寫。具體的機器指令是16位還是32位由編譯器來決定。.syntax unified的作用就是制定使用這一功能,具體可以參考《cortex-m3權威指南》。
  • 接着這段代碼:
  • /* save kernel state */
  •         mrs ip, psr
  •         push {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr}
  •         /* switch to process stack */
  •         msr psp, r0
  •         mov r0, #3
  •         msr control, r0
  •         /* load user state */
  •         pop {r4, r5, r6, r7, r8, r9, r10, r11, lr}
  •         /* jump to user task */
  •         bx lr
  • MRS:Move to ARM register from system coprocessor register. MRS Rn, coproc_register中Rn is the ARM destination register, and Rn must not be PC.System coprocessor register應該是那些special registers,其中包含psr(Program status register)。MSR反之。
  • 將main函數(kernel)的狀態存入堆棧後,將r0數據存入程序狀態寄存器(此時r0應該存放了usertask_stack_start地址),然後設定control寄存器爲0b11:


  • 所以現在stack pointer是PSP,而PSP中存放的是usertask_stack_start地址,至於Thread mode是Unprivileged。
  • 最後把usertask_stack中的寄存器值拿出來,就可以跳轉到user state了



回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:09 编辑

03-ContextSwitch-2
  • startup.c中多了一些聲明:
  • void nmi_handler(void) __attribute((weak, alias("default_handler")));
  • void hardfault_handler(void) __attribute((weak, alias("default_handler")));
  • void memmanage_handler(void) __attribute((weak, alias("default_handler")));
  • void busfault_handler(void) __attribute((weak, alias("default_handler")));
  • void usagefault_handler(void) __attribute((weak, alias("default_handler")));
  • void svc_handler(void) __attribute((weak, alias("default_handler")));
  • alias ("target")The alias attribute causes the declaration to be emitted as an alias for another symbol, which must be specified. For instance,
  •               void __f () { /* Do something. */; }
  •               void f () __attribute__ ((weak, alias ("__f")));
  • defines ‘f’ to be a weak alias for ‘__f’. In C++, the mangled name for the target must be used. It is an error if ‘__f’ is not defined in the same translation unit.
  • Not all target machines support this attribute.
  • 所以*_handler全部alias到了default_handler上,而default_handler目前爲一個死循環:
  • void default_handler(void)
  • {
  •         while (1);
  • }
  • syscall的定義:
  • syscall:
  •         svc 0
  •         nop
  •         bx lr

  • 在context_switch.S中定義了svc_handler函數,聯想到startup.c中的weak alias,可以理解weak alias的意思就是編譯器假如在文件中找到了某個函數的定義就會編譯,假如沒有找到就會將其alias到指定的weak alias函數。

  • svc_handler:
  •         /* save user state */
  •         mrs r0, psp
  •         stmdb r0!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}

  •         /* load kernel state */
  •         pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr}
  •         msr psr, ip

  •         bx lr
  • 關於stmdb,stm代表存儲更多寄存器,db代表在獲取前將地址減小。所以就是r0地址會增加9*4bytes,然後將{}中的9個寄存器從左至右從最低位置開始存放


  • 程序run可見當usertask觸發syscall後會保存其狀態,然後切回main函數輸出兩個print_str,然後回到usertask後會繼續接着上一次的執行到的地方繼續執行,再輸出兩句話,然後回到main函數,也是接着上次執行後的地方繼續執行。這驗證了之前代碼裏關於user state和kernel state的save和load動作。



回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:10 编辑

04-Multitasking


  • (先猜測main裏有個while循環,裏面來回activate task1和task2,而在task1和2中print完後便調用syscall回到kernel(main)
  • diff context_switch.S ../03-ContextSwitch-2/context_switch.S ,發現:
  • svc_handler中的msr psr, ip變成了msr psr_nzcvq, ip
  • nzcv爲psr的四個標誌位,只是q是什麼@@
  • 詫異爲何在ARM官網看到的第27位保留了,仔細看才發現是cortex-m0的手冊
  • 另外在activate中,在切換到process stask後多一條isb指令:
  • Instruction Synchronization Barrier flushes the pipeline in the processor, so that all instructions following the ISB are fetched from cache or memory, after the instruction has been completed. It ensures that the effects of context altering operations, such as changing the ASID, or completed TLB maintenance operations, or branch predictor maintenance operations, as well as all changes to the CP15 registers, executed before the ISB instruction are visible to the instructions fetched after the ISB.
  • In addition, the ISB instruction ensures that any branches that appear in program order after it are always written into the branch prediction logic with the context that is visible after the ISB instruction. This is required to ensure correct execution of the instruction stream.

  • 重頭戲果然在os.c,並且代碼的設計比我一開始預測的要有規劃性一些,具體分析如下:
  • 首先宏定義了三個地址:
  • #define THREAD_PSP    0xFFFFFFFD

  • 接着是新建一個task:
  • unsigned int *create_task(unsigned int *stack, void (*start)(void))
  • {
  •         static int first = 1;
  •         stack += STACK_SIZE - 32; /* End of stack, minus what we are about to push */
  •         if (first) {
  •                 stack[8] = (unsigned int) start;
  •                 first = 0;
  •         } else {
  •                 stack[8] = (unsigned int) THREAD_PSP;
  •                 stack[15] = (unsigned int) start;
  •                 stack[16] = (unsigned int) 0x01000000; /* PSR Thumb bit */
  •         }
  •         stack = activate(stack);
  •         return stack;
  • }

  • 而在main函數中,對應的調用是:
  • unsigned int user_stacks[TASK_LIMIT][STACK_SIZE];     //定義了程式stack的數量(2個)和大小(256*4bytes)
  • unsigned int *usertasks[TASK_LIMIT];                               //定義了用戶程式的數量(2個)
  • usertasks[0]=create_task(user_stack[0],&task_func);       //將之前定義的stack和function地址傳給create_task配置
  • usertasks[0]=create_task(user_stack[0],&task_func);
  • 當使用create_task函數第一次進行初始化時,在create_task中對task的stack進行了配置,並且因爲是第一次設定,將stack[8]存放了task_func的地址,然後進入到task_func中echo"task1 Created"&"return"後調用syscall回到了main函數。
  • 當第二次呼叫create_task時,comment也有說明,因爲activate是從exception中返回,故將lr保存爲THREAD_PSP,而此時的func從stack[15]開始
  • 爲何緊接着的stack[16]就要設epsr爲0x01000000?爲何processor可以認出這條指令是設置espr的?




回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:10 编辑

05-TimerInterrupt
  • void __attribute__((interrupt)) systick_handler(void)
  • The compiler generates function entry and exit sequences suitable for use in an interrupt handler when this attribute is present. With Epiphany targets it may also generate a special section with code to initialize the interrupt vector table.(https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html)



回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:10 编辑

06-Preemptive
  • 在context_switch.s中對systick_handler定義了一樣的行爲,當systick exception觸發時,也會將進程切換到kernel mode
  • 一直沒找到delay()和systick有什麼直接聯繫,恍然大悟:當一個task在運行時,由於發生了systick exception,就會切換到kernel mode然後運行下一個task,這樣就如同preemptive



回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:11 编辑

——————————————END——————————————

回复

26

帖子

0

TA的资源

一粒金砂(中级)

感慨一句,可能gnu toolchain不少人都不熟,其實我也才入門不久
以前都是用MDK IDE,覺得好用,但毫無疑問用的盜版,這些行爲屬於灰色的
使用GNU toolchain不僅僅是因爲免費,更是因爲它的強大
GNU/linux名字是怎麼得來的無需贅言
將來繼續做Embedded system,基本也離不開linux系統

那麼在linux上,GNU software就是王道

(MS剛剛宣佈全新的visual studio code將支持linux,不過目前也還只是編輯器而已)

点评

确实用gcc的人很少,其时就算能用IAR,MDK等命令行工具做工程的人就不多,何况gcc。 期待有更多心得分享  详情 回复 发表于 2015-5-3 13:15

回复

7504

帖子

2

TA的资源

五彩晶圆(高级)

本帖最后由 freebsder 于 2015-5-3 13:20 编辑
john0517 发表于 2015-5-2 23:50
感慨一句,可能gnu toolchain不少人都不熟,其實我也才入門不久
以前都是用MDK IDE,覺得好用,但毫無疑問用的盜版,這些行爲屬於灰色的
使用GNU toolchain不僅僅是因爲免費,更是因爲它的強大
GNU/linux名字是怎麼得來的無需贅言
將來繼續做Embedded system,基本也離不開linux系統
那麼在linux上,GNU software就是王道

(MS剛剛宣佈全新的visual studio code將支持linux,不過目前也還只是編輯器而已)

确实用gcc的人很少,其时就算能用IAR,MDK等命令行工具做工程的人就不多,何况gcc。
除了版面格式太伤眼,期待更多交流和分享

点评

关于版面格式,因为从Hackpad直接贴过来的缘故,没有用心去整理,实在是抱歉!!! 此处补上hackpad地址:https://embedded2015.hackpad.com/Week-7-8--ID8HJ3uW0MO IAR,MDK的命令行工具我也是第  详情 回复 发表于 2015-5-4 00:00
个人签名

默认摸鱼,再摸鱼。2022、9、28


回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 00:12 编辑
freebsder 发表于 2015-5-3 13:15
确实用gcc的人很少,其时就算能用IAR,MDK等命令行工具做工程的人就不多,何况gcc。
除了版面格式太伤眼,期待更多交流和分享

关于版面格式,因为从Hackpad直接贴过来的缘故,没有用心去整理,实在是抱歉!!!

此处补上hackpad地址:https://embedded2015.hackpad.com/Week-7-8--ID8HJ3uW0MO

IAR,MDK的命令行工具我也是第一次听说@@

可能跟教学的资源太少有关吧

点评

一般有info manual或help就可以,这种产品性的东西,说明书最靠谱。  详情 回复 发表于 2015-5-4 09:38

回复

7504

帖子

2

TA的资源

五彩晶圆(高级)

本帖最后由 freebsder 于 2015-5-4 09:52 编辑
john0517 发表于 2015-5-4 00:00
关于版面格式,因为从Hackpad直接贴过来的缘故,没有用心去整理,实在是抱歉!!!

此处补上hackpad地址:https://embedded2015.hackpad.com/Week-7-8--ID8HJ3uW0MO

IAR,MDK的命令行工具我也是第一次听说@@

可能跟教学的资源太少有关吧

一般有info manual或help就可以,这种产品性的东西,说明书最靠谱。



ps:

最好整理一下,你的链接我这边是被墙了。

点评

完全贊同你!說明書是最嚴謹也是最完善的開發指南 我抽空整理~ 國內現在封了github、hackpad很無奈  详情 回复 发表于 2015-5-4 16:46
个人签名

默认摸鱼,再摸鱼。2022、9、28


回复

26

帖子

0

TA的资源

一粒金砂(中级)

本帖最后由 john0517 于 2015-5-4 16:49 编辑
freebsder 发表于 2015-5-4 09:38
一般有info manual或help就可以,这种产品性的东西,说明书最靠谱。



ps:

最好整理一下,你的链接我这边是被墙了。

完全贊同你!說明書是最嚴謹也是最完善的開發指南

但是,你也知道,不少人都希望有不用思考的完全教學指南:)

我會抽空整理~

國內現在封了github、hackpad很無奈

点评

呵呵,只以我个人经历来说,建议你不要管“不少人”怎么怎么希望,自己按照结构性的,系统性的学习,基础性的学习,某一天这些乱七八糟的“技术”对你来说就是纸老虎了。  详情 回复 发表于 2015-5-4 21:11

回复

7504

帖子

2

TA的资源

五彩晶圆(高级)

john0517 发表于 2015-5-4 16:46
完全贊同你!說明書是最嚴謹也是最完善的開發指南

但是,你也知道,不少人都希望有不用思考的完全教學指南:)

我會抽空整理~

國內現在封了github、hackpad很無奈

呵呵,只以我个人经历来说,建议你不要管“不少人”怎么怎么希望,自己按照结构性的,系统性的学习,基础性的学习,某一天这些乱七八糟的“技术”对你来说就是纸老虎了。

点评

對,我大概一年半前就意識到這個問題了 其實道理很簡單,越容易上手的越難學到真東西,也越沒競爭力  详情 回复 发表于 2015-5-6 00:38
个人签名

默认摸鱼,再摸鱼。2022、9、28


回复

26

帖子

0

TA的资源

一粒金砂(中级)

freebsder 发表于 2015-5-4 21:11
呵呵,只以我个人经历来说,建议你不要管“不少人”怎么怎么希望,自己按照结构性的,系统性的学习,基础性的学习,某一天这些乱七八糟的“技术”对你来说就是纸老虎了。

對,我大概一年半前就意識到這個問題了

其實道理很簡單,越容易上手的越難學到真東西,也越沒競爭力

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表