RTThread入门
RT-Thread入門
1.初識RT-Thread
嵌入式系統(tǒng)是一種完全嵌入在裝置或設(shè)備內(nèi)部,為滿足特定需求而設(shè)計的計算機系統(tǒng),譬如生活中常見的嵌入式系統(tǒng)就有:電視機頂盒、路由器、電冰箱、微波爐與移動電話等。
嵌入式操作系統(tǒng)是應(yīng)用于嵌入式系統(tǒng)的軟件。
2.動態(tài)內(nèi)存堆的使用
- 裸機系統(tǒng)動態(tài)內(nèi)存
動態(tài)內(nèi)存配置
動態(tài)內(nèi)存使用 - RTT動態(tài)內(nèi)存
動態(tài)內(nèi)存配置
動態(tài)內(nèi)存使用(源碼解析) - 動態(tài)內(nèi)存注意事項
內(nèi)存復(fù)位
內(nèi)存泄漏 - 其他相關(guān)API
rt_realloc
rt_calloc
2.1 簡述堆棧
在單邊機應(yīng)用中,我們經(jīng)常提到堆棧這個詞,實際上堆和棧是兩個不同的概念。
棧(stack):由編譯器自動分配釋放
堆(heap):一般由程序員分配和釋放
全局初始化區(qū)
全局靜態(tài)初始化區(qū)
全局未初始化區(qū)
常量區(qū)
棧區(qū)
堆區(qū)
2.2 MDK裸機系統(tǒng)動態(tài)內(nèi)存配置和使用
使用方式1:startup_stm32f103xe.s
; Amount of memory (in bytes) allocated for Stack ; Tailor this value to your application needs ; <h> Stack Configuration ; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h>Stack_Size EQU 0x00000400AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp; <h> Heap Configuration ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h>Heap_Size EQU 0x00000200AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size __heap_limit??臻g的設(shè)置和堆空間的設(shè)置
使用方式2:
char *p; p = (char*)malloc(10) free(p);2.3 RT-Thread動態(tài)內(nèi)存配置和使用
與裸機系統(tǒng)下使用相差不大。
RT-Thread中,使用動態(tài)內(nèi)存前,需要使用動態(tài)內(nèi)存函數(shù)rt_system_heap_init 配置好動態(tài)內(nèi)存區(qū)。
之后,使用rt_malloc及rt_free函數(shù)獲得動態(tài)內(nèi)存及釋放動態(tài)內(nèi)存。
2.3.1 rt_system_heap_init
board.c
rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);參數(shù)為起始地址和結(jié)束地址,兩地址之間的空間作為動態(tài)內(nèi)存的空間
可以從芯片總的內(nèi)存空間中提取一部分作為動態(tài)內(nèi)存空間。
起始地址 HEAP_BEGIN
#define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit)ImageRWIRAM1RW_IRAM1RWI?RAM1ZI$$Limit 是一個鏈接器導(dǎo)出的符號。代表ZI段的結(jié)束,也就是程序執(zhí)行區(qū)的RAM結(jié)束后的地址,另一個角度,也就是我們執(zhí)行區(qū)RAM未使用的區(qū)域的起始地址。
從工程的map文件中,可以看到:
Total RO Size (Code + RO Data) 63528 ( 62.04kB)Total RW Size (RW Data + ZI Data) 22576 ( 22.05kB)Total ROM Size (Code + RO Data + RW Data) 63676 ( 62.18kB)整個工程中用到的靜態(tài)RAM空間用掉了22.05K
芯片總RAM有64K,除去用掉的22.05K的空間,剩余的空間的起始地址,在MDK中使用 宏
Image$$RW_IRAM1$$ZI$$Limit 表示。
結(jié)束地址 HEAP_END
#define HEAP_END STM32_SRAM_END #define STM32_SRAM_END (0x20000000 + STM32_SRAM_SIZE * 1024)使用片內(nèi)RAM的結(jié)束地址作為動態(tài)內(nèi)存的結(jié)束地址。
2.4 動態(tài)內(nèi)存堆使用注意點
內(nèi)存復(fù)位
當我們每次申請到新的內(nèi)存塊之后,建議對所申請到的內(nèi)存塊進行清零操作。
內(nèi)存泄漏
內(nèi)存泄漏(Memory Leak)是指程序中已動態(tài)分配的堆內(nèi)存,由于某種原因未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費,導(dǎo)致程序運行速度減慢甚至系統(tǒng)崩潰等嚴重后果。
我們在使用動態(tài)內(nèi)存時需要注意:rt_malloc 需要和 rt_free 配套使用。
2.5 其他動態(tài)內(nèi)存相關(guān)的API
void *rt_realloc(void *rmem,rt_size_t newsize)
在已分配內(nèi)存塊的基礎(chǔ)上重新分配內(nèi)存塊的大小(增加或減少)
在進行重新分配內(nèi)存塊時,原來的內(nèi)存塊數(shù)據(jù)保持不變(縮小的情況下,后面的數(shù)據(jù)被自動截斷)
void *rt_calloc(rt_size_t count,rt_size_t size)
從內(nèi)存堆中分配連續(xù)內(nèi)存地址的多個相同大小的內(nèi)存塊
2.6 動態(tài)內(nèi)存使用的Demo代碼
int i = 0;char *ptr = RT_NULL; //內(nèi)存塊指針for(i=0;;i++){ptr = rt_malloc(1<<i); //每次申請 (1<<i)大小的內(nèi)存if(ptr != RT_NULL){rt_kprintf("rt_malloc OK! size=%d\n",(1<<i));rt_memset(ptr,0,(1<<i)); //清空申請到的內(nèi)存rt_kprintf("rt_memset OK!\n");rt_free(ptr); //釋放申請到的內(nèi)存rt_kprintf("rt_free OK!\n");}else{rt_kprintf("rt_malloc Err!\n");return -1;}}3.線程的創(chuàng)建
3.1 線程的概念
RT-Thread名為實施線程RTOS,那么什么叫線程?
3.2 線程的組成
RT-Thread中,線程由三部分組成:線程代碼(入口函數(shù))、線程控制塊、線程堆棧。
線程代碼
//無限循環(huán)結(jié)構(gòu) void thread_entry(void *parameter) {while(1){/* 等待事件發(fā)生 *//* 處理事件 */} }//順序執(zhí)行結(jié)構(gòu) void thread_entry(void *parameter) {/* 事務(wù)1處理 *//* 事務(wù)2處理 *//* 事務(wù)N處理 */ }無限循環(huán)結(jié)構(gòu)中一般加入讓出CPU的API調(diào)用,否則這個線程一直占用CPU,其他線程無法被執(zhí)行。
線程控制塊
線程控制塊是操作系統(tǒng)用于管理線程的一個數(shù)據(jù)結(jié)構(gòu)。它會存放線程的一些信息,例如優(yōu)先級、線程名稱、線程狀態(tài)等,也包括線程與線程之間連接用的鏈表結(jié)構(gòu),線程等待事件集合等。
struct rt_thread struct tr_thread *rt_thread_t線程棧
RT-Thread每個線程都具有獨立的??臻g,當進行線程切換時,系統(tǒng)會將當前線程的上下文保存在線程棧中,當線程要恢復(fù)運行時,再從線程棧中讀取上下文信息,恢復(fù)線程的運行。
線程上下文是指線程執(zhí)行的環(huán)境,具體說就是各個變量和數(shù)據(jù),包括所有寄存器變量、堆棧信息、內(nèi)存信息等。
線程棧在形式上是一段連續(xù)的內(nèi)存空間,我們可以通過定義一個數(shù)組或者申請一段動態(tài)內(nèi)存來作為線程的棧。
3.3 線程創(chuàng)建
創(chuàng)建線程
創(chuàng)建動態(tài)線程
rt_thread_t rt_thread_create(const char *name,void (*entry)(void *parameter),void *parameter,rt_uint32_t stack_size,rt_uint8_t priority,rt_uint32_t tick);創(chuàng)建靜態(tài)線程
rt_err_t rt_thread_init(struct rt_thread *thread,const char *name,void (*entry)(void *parameter),void *parameter,void *stack_start,rt_uint32_t stack_size,rt_uint8_t priority,rt_uint32_t tick);動態(tài)線程的線程控制塊和線程棧都是自動分配的。
創(chuàng)建靜態(tài)線程需要提前定義好線程控制塊和線程棧。
啟動線程
rt_err_t rt_thread_startup(rt_thread_t thread);調(diào)用此函數(shù)后,創(chuàng)建的線程會被加入到線程的就緒隊列,執(zhí)行調(diào)度。
3.4 靜態(tài)線程VS動態(tài)線程
相關(guān)資源分配形式
靜態(tài)線程需要將線程控制塊與線程棧先定義出來,動態(tài)線程不需要,動態(tài)線程的線程控制塊和線程棧是自動分配的。
運行效率
1.如果動態(tài)線程和靜態(tài)線程的線程控制塊和線程棧都處于芯片的RAM中,則動態(tài)線程和靜態(tài)線程的運行效率沒有區(qū)別。
2.如果系統(tǒng)使用了外擴的外部RAM,動態(tài)線程的線程控制塊和線程棧是處于外部RAM中。則動態(tài)線程的運行效率有所下降。
3.兩種方式創(chuàng)建的線程,本質(zhì)上沒有區(qū)別。
3.5 線程創(chuàng)建Demo
/* 創(chuàng)建兩個線程,一個動態(tài)線程,一個靜態(tài)線程 */#define THREAD_PORITY 25 #define THREAD_SLICE 5 #define THREAD_STCK_SIZE 512//動態(tài)線程 rt_thread_t tid1 = RT_NULL; //靜態(tài)線程 struct rt_thread thread2; //靜態(tài)線程 char thread2_stack[THREAD_STCK_SIZE];/*** @brief 線程1的入口函數(shù)* * @param parameter [IN]線程的參數(shù)*/ void my_thread1_entry(void *parameter) {uint32_t count = 0;while(1){count ++;rt_kprintf("thread1 count = %d\r\n",count);rt_thread_mdelay(100);}}/*** @brief 線程2的入口函數(shù)* * @param parameter [IN]線程的參數(shù)*/ void my_thread2_entry(void *parameter) {uint32_t count = 0;for(count=0;count<10;count++){rt_kprintf("thread2 count = %d\r\n",count);}rt_kprintf("thread2 exit\r\n"); }int main(void) { //創(chuàng)建動態(tài)線程tid1 = rt_thread_create("thread1",my_thread1_entry,RT_NULL,THREAD_STCK_SIZE,THREAD_PORITY,THREAD_SLICE);if(tid1 != RT_NULL){rt_thread_startup(tid1); //將線程添加到線程就緒隊列rt_kprintf("thread1 creater OK!\r\n");}else{rt_kprintf("thread1 creater Err!\r\n");}//創(chuàng)建靜態(tài)線程rt_thread_init(&thread2,"thread2",my_thread2_entry,RT_NULL,&thread2_stack[0],THREAD_STCK_SIZE,THREAD_PORITY-1,THREAD_SLICE);rt_thread_startup(&thread2); //將線程2添加到線程就緒列表return 0; }運行結(jié)果:
thread1 creater OK!
msh >thread2 count = 0
thread2 count = 1
thread2 count = 2
thread2 count = 3
thread2 count = 4
thread2 count = 5
thread2 count = 6
thread2 count = 7
thread2 count = 8
thread2 count = 9
thread2 exit
thread1 count = 1
thread1 count = 2
thread1 count = 3
thread1 count = 4
thread1 count = 5
thread1 count = 6
…
4.跑馬燈線程實例
4.1 線程狀態(tài)
線程狀態(tài)轉(zhuǎn)換圖
4.2 系統(tǒng)滴答時鐘
? 每個操作系統(tǒng)中都存在一個“系統(tǒng)心跳”時鐘,是操作系統(tǒng)中最小的時鐘單位。這個時鐘負責系統(tǒng)和時間相關(guān)的一些操作。作為操作系統(tǒng)運行的時間尺度,心跳時鐘是由硬件定時器的定時中斷產(chǎn)生。
? 系統(tǒng)的心跳時鐘我們也稱之為系統(tǒng)滴答或時鐘節(jié)拍,系統(tǒng)滴答的頻率需要我們根據(jù)CPU的處理能力來決定。
? 時鐘節(jié)拍使得內(nèi)核可以將線程延時若干個整數(shù)倍時鐘節(jié)拍,以及線程等待事件發(fā)生時,提供等待超時的依據(jù)。
? 頻率越快,內(nèi)核函數(shù)介入系統(tǒng)運行的幾率就越大,內(nèi)核占用的處理器時間就越長,系統(tǒng)的負荷就變大;頻率太小,時間處理精度又不夠。
? 我們在STM32平臺上一般設(shè)置系統(tǒng)滴答的頻率為100Hz,即每個滴答的時間是10ms。
4.2.1 STM32上系統(tǒng)滴答的配置
//board.c void SystemClock_Config(void) {...//設(shè)置系統(tǒng)中斷時間HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND);... }//系統(tǒng)定時器中斷 void SysTick_Handler(void) {...rt_tick_increase();... }//rt_config.h #define RT_TICK_PER_SECOND 100 //配置滴答頻率為100Hzrt_thread_mdealy(500) 延時500ms。這個API的延時就是由系統(tǒng)滴答完成的。
4.3 GPIO驅(qū)動架構(gòu)操作IO
IO初始化
void rt_pin_mode(rt_base_t pin,rt_base_t mode); pin:引腳索引 mode:引腳模式PIN_MODE_OUTPUTPIN_MODE_INPUTPIN_MODE_INPUT_PULLUPPIN_MODE_INPUT_PULLDOWNPIN_MODE_OUTPUT_ODIO寫入
void rt_pin_write(rt_base_t pin,rt_base_t value); pin:引腳索引 value:引腳電平PIN_HIGHPIN_LOWIO讀出
int rt_pin_read(rt_base_t pin); pin:引腳索引 返回值:引腳電平4.4 跑馬燈Demo
/* 跑馬燈 */ #include <rtthread.h> #include <rtdevice.h> #include <drv_gpio.h>//線程信息 #define THREAD_PORITY 25 #define THREAD_SLICE 5 #define THREAD_STCK_SIZE 512//線程控制塊指針 rt_thread_t led_tid = RT_NULL;//LED引腳 #define MY_LED_PIN 14/*** @brief 線程的入口函數(shù)* * @param parameter [IN]線程的參數(shù)*/ void led_entry(void *parameter) {//設(shè)置LED引腳為推挽輸出rt_pin_mode(MY_LED_PIN,PIN_MODE_OUTPUT); while(1){rt_pin_write(MY_LED_PIN,PIN_HIGH); //引腳置高rt_thread_delay(50); //延時50個時鐘節(jié)拍 rt_thread_sleep(50)rt_pin_write(MY_LED_PIN,PIN_LOW); //引腳置低rt_thread_mdelay(500); //延時500ms}}int main(void) { //創(chuàng)建線程led_tid = rt_thread_create("led",led_entry,RT_NULL,THREAD_STCK_SIZE,THREAD_PORITY,THREAD_SLICE);if(led_tid != NULL){rt_thread_startup(led_tid); //加入到線程就緒隊列}return 0; }4.5 線程棧大小分配
? 先將線程棧大小設(shè)置一個固定值(比如2048),在線程運行時通過查看線程棧的使用情況,了解線程棧使用的實際情況,根據(jù)實際情況設(shè)置合理的線程棧大小。
msh >list_thread thread pri status sp stack size max used left tick error ------ --- ------- ---------- ---------- ------ ---------- --- led 25 suspend 0x00000074 0x00000200 24% 0x00000005 000 tshell 20 ready 0x00000080 0x00001000 07% 0x00000005 000 tidle 31 ready 0x00000054 0x00000100 35% 0x00000010 000 msh >? 一般將線程棧最新大使用量設(shè)置為70%。
5.線程的時間片輪詢調(diào)度
5.1 線程優(yōu)先級
? 優(yōu)先級和時間片是線程的兩個重要參數(shù)
總結(jié)
以上是生活随笔為你收集整理的RTThread入门的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一阶电路暂态响应的结果分析。_电路之暂态
- 下一篇: 基于C#.NET的高端智能化网络爬虫(二