在裸機(jī)系統(tǒng) 中,他們統(tǒng)統(tǒng)放在一個(gè)叫棧的地方,棧是單片機(jī)RAM里面一段連續(xù)的內(nèi)存空間,棧的大小一般在啟動(dòng)文件或者鏈接腳 本里面指定,最后由C庫(kù)函數(shù)_main進(jìn)行初始化。
在多線(xiàn)程系統(tǒng)中,每個(gè)線(xiàn)程都是獨(dú)立的,互不干擾的,所以要為每個(gè)線(xiàn)程都分配獨(dú)立的棧空間,這個(gè)棧空間通常是一個(gè)預(yù)先定義好的全局?jǐn)?shù)組,也可以是動(dòng)態(tài)分配的一段內(nèi)存空間,但它們都存在于RAM中。
創(chuàng)建線(xiàn)程棧,需要幾個(gè)線(xiàn)程創(chuàng)建幾個(gè)線(xiàn)程棧,這里線(xiàn)程棧實(shí)際上就是全局變量,大小為512,這里創(chuàng)建兩個(gè)工作線(xiàn)程,如下:
ALIGN(RT_ALIGN_SIZE)//
/* 定義線(xiàn)程棧*/
rt_uint8_t rt_flag1_thread_stack[512];
rt_uint8_t rt_flag2_thread_stack[512];
ALIGN是一個(gè) 帶參宏,在rtdef.h中定義,設(shè)置變量需要多少個(gè)字節(jié)對(duì)齊,對(duì)在它下面的變量起作用。RT_ALIGN_SIZE是一個(gè)在rtconfig.h(rtconfig.h第一次使用需要在User文件夾下面新建然后添加到工程user這個(gè)組文件)中定義 的宏,默認(rèn)為4,表示4個(gè)字節(jié)對(duì)齊。
#ifndef __RTTHREAD_CFG_H__
#define __RTTHREAD_CFG_H__
#define RT_THREAD_PRIORITY_MAX 32 /* 最大優(yōu)先級(jí) */
#define RT_ALIGN_SIZE 4 /* 多少個(gè)字節(jié)對(duì)齊 */
#endif /* __RTTHREAD_CFG_H__ */
下一步,定義線(xiàn)程函數(shù):
這里我們建立線(xiàn)程函數(shù),并給函數(shù)命名flag1_thread_entry,flag2_thread_entry
兩個(gè)函數(shù) ,分別代表著兩個(gè)無(wú)限循環(huán)不返回的線(xiàn)程
/* 軟件延時(shí) */
void delay (uint32_t count)
{
for(; count!====0; count--);
}
/* 線(xiàn)程1 */
void flag1_thread_entry( void *p_arg )// (1)
{
for( ;; )
{
flag1 ==== 1;
delay( 100 );
flag1 ==== 0;
delay( 100 );
/* 線(xiàn)程切換,這里是手動(dòng)切換 */
rt_schedule();
}
}
/* 線(xiàn)程2 */
void flag2_thread_entry( void *p_arg )// (2)
{
for( ;; )
{
flag2 ==== 1;
delay( 100 );
flag2 ==== 0;
delay( 100 );
/* 線(xiàn)程切換,這里是手動(dòng)切換 */
rt_schedule();
}
}
下一步定義線(xiàn)程控制塊
在裸機(jī)系統(tǒng)中,程序的主體是CPU按照順序執(zhí)行的。而在多線(xiàn)程系統(tǒng)中,線(xiàn)程的執(zhí)行是由系統(tǒng)調(diào)度的。系統(tǒng)為了順利的調(diào)度線(xiàn)程,為每個(gè)線(xiàn)程都額外定義了一個(gè)線(xiàn)程控制塊,這個(gè)線(xiàn)程控制塊就相當(dāng)于線(xiàn)程的身份證,里面存有線(xiàn)程的所有信息,比如線(xiàn)程的棧指針,線(xiàn)程名稱(chēng),線(xiàn)程的形參等。有了這個(gè)線(xiàn)程控制塊之后,以后系統(tǒng)對(duì)線(xiàn)程的全部操作都可以通過(guò)這個(gè)線(xiàn)程控制塊來(lái)實(shí)現(xiàn)。定義一個(gè)線(xiàn)程控制塊需要一個(gè)新的數(shù)據(jù)類(lèi)型,該數(shù)據(jù)類(lèi)型在rtdef.h這個(gè)頭 文件中聲明,使用它可以為每個(gè)線(xiàn)程都定義一個(gè)線(xiàn)程控制塊實(shí)體。下面來(lái)看一下,此結(jié)構(gòu)體定義函數(shù)及內(nèi)容:
struct rt_thread
{
void *sp; /* 線(xiàn)程棧指針 */
void *entry; /* 線(xiàn)程入口地址 */
void *parameter; /* 線(xiàn)程形參 */
void *stack_addr; /* 線(xiàn)程起始地址 */
rt_uint32_t stack_size; /* 線(xiàn)程棧大小,單位為字節(jié) */
rt_list_t tlist; /* 線(xiàn)程鏈表節(jié)點(diǎn) */
};
typedef struct rt_thread *rt_thread_t;//
我們?cè)趍ain.c文件中為兩個(gè)線(xiàn)程定義的線(xiàn)程控制塊。
/* 定義線(xiàn)程控制塊 */
struct rt_thread rt_flag1_thread;
struct rt_thread rt_flag2_thread;
下一步,創(chuàng)建線(xiàn)程實(shí)現(xiàn)函數(shù)
線(xiàn)程的棧,線(xiàn)程的函數(shù)實(shí)體,線(xiàn)程的控制塊最終需要聯(lián)系起來(lái)才能由系統(tǒng)進(jìn)行統(tǒng)一調(diào)度。那么這個(gè)聯(lián)系的工作就由線(xiàn) 程初始化函數(shù)rt_thread_init()來(lái)實(shí)現(xiàn),該函數(shù)在thread.c(thread.c第一次使用需要自行在文件夾rtthread/3.0.3/src中新建并添加到工程的rtt/source組)中定義,在rtthread.h中聲明,所有跟線(xiàn)程相關(guān)的函數(shù)都在這個(gè)文件定義。rt_thread_init()函數(shù)的實(shí)現(xiàn)如下 :
rt_err_t rt_thread_init(struct rt_thread *thread,// (1)
void (*entry)(void *parameter),// (2)
void *parameter,// (3)
void *stack_start,// (4)
rt_uint32_t stack_size)// (5)
{
rt_list_init(&(thread->tlist));// (6)
thread->entry ==== (void *)entry;// (7)
thread->parameter ==== parameter;// (8)
thread->stack_addr ==== stack_start;// (9)
thread->stack_size ==== stack_size;// (10)
/* 初始化線(xiàn)程棧,并返回線(xiàn)程棧指針 */ // (11)
thread->sp ==== (void *)rt_hw_stack_init( thread->entry,
thread->parameter,
(void *)((char *)thread->stack_addr + thread->stack_size - 4) );
return RT_EOK;// (12)
}
(1) :thread是線(xiàn)程控制塊指針。
(2) :entry 是線(xiàn)程函數(shù)名, 表示線(xiàn)程的入口。
(3) :parameter是線(xiàn)程形參,用于傳遞線(xiàn)程參數(shù)。
(4) :stack_start 用于指向線(xiàn)程棧的起始地址。
(5) :stack_size表示線(xiàn)程棧的大小,單位為字節(jié)。
(7) :將線(xiàn)程入口保存到線(xiàn)程控制塊的entry成員中。
(8) :將線(xiàn)程入口形參保存到線(xiàn)程控制塊的parameter成員中。
(9) :將線(xiàn)程棧起始地址保存到線(xiàn)程控制塊的stack_start成員中。
(10) :將線(xiàn)程棧起大小保存到線(xiàn)程控制塊的stack_size成員中。
(11) :初始化線(xiàn)程棧,并返回線(xiàn)程棧頂指針。
rt_hw_stack_init()用來(lái)初始化線(xiàn)程棧, 當(dāng)線(xiàn)程第一次運(yùn)行的時(shí)候,加載到CPU寄存器的參數(shù)就放在線(xiàn)程棧里面,該函數(shù)在cpuport.c中實(shí)現(xiàn)。cpuport.c第一次使用需要自行 在rtthread/3.0.3/ libcpu/arm/cortex-m3(cortex-m4或cortex-m7)文件夾下新建,然后添加到工程 的rtt/ports組中。
下一步,定義鏈表節(jié)點(diǎn)數(shù)據(jù)類(lèi)型:
struct rt_list_node
{
struct rt_list_node *next; /* 指向后一個(gè)節(jié)點(diǎn) */
struct rt_list_node *prev; /* 指向前一個(gè)節(jié)點(diǎn) */
};
typedef struct rt_list_node rt_list_t;
rt_list_t 類(lèi)型的節(jié)點(diǎn)里面有兩個(gè)rt_list_t類(lèi)型的節(jié)點(diǎn)指針next和prev,分別用來(lái)指向鏈表中的下一個(gè)節(jié)點(diǎn)和上一個(gè)節(jié)點(diǎn)。

雙向鏈表的相關(guān)操作,這些函數(shù)均在rtservice.h中實(shí)現(xiàn),rtservice.h第一次使用需要自行在rtthread/3.0.3/include文件夾下新建,然后添加到工程的rtt/source組中。下面從鏈表操作逐步實(shí)現(xiàn)。
鏈表節(jié)點(diǎn)初始化:
rt_inline void rt_list_init(rt_list_t *l)
{
l->next ==== l->prev ==== l;
}
實(shí)際上進(jìn)行了如下操作:

下面在鏈表頭部和尾部分別插入一個(gè)鏈表節(jié)點(diǎn):
表頭后面插入一個(gè)節(jié)點(diǎn)
/* 在雙向鏈表頭部插入一個(gè)節(jié)點(diǎn)*/
rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
l->next->prev ==== n; /* 第 1 步*/
n->next ==== l->next; /* 第 2 步*/
l->next ==== n; /* 第 3 步*/
n->prev ==== l; /* 第 4 步*/
}
表頭前邊插入一個(gè)節(jié)點(diǎn)
rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
l->prev->next ==== n; /* 第 1 步*/
n->prev ==== l->prev; /* 第 2 步*/
l->prev ==== n; /* 第 3 步*/
n->next ==== l; /* 第 4 步*/
}
從雙向鏈表刪除一個(gè)節(jié)點(diǎn)
rt_inline void rt_list_remove(rt_list_t *n)
{
n->next->prev ==== n->prev; /* 第 1 步*/
n->prev->next ==== n->next; /* 第 2 步*/
n->next ==== n->prev ==== n; /* 第 3 步*/
}
創(chuàng)建線(xiàn)程初始化函數(shù)
/* 線(xiàn)程棧初始化 */
rt_uint8_t *rt_hw_stack_init(void *tentry,// (1)
void *parameter,// (2)
rt_uint8_t *stack_addr)// 線(xiàn)程棧頂?shù)刂?4,在該函數(shù)調(diào)用的時(shí)候傳進(jìn)來(lái)的是線(xiàn)程棧的棧頂?shù)刂?4
{
struct stack_frame *stack_frame;// (4)
rt_uint8_t *stk;
unsigned long i;
/* 獲取棧頂指針
rt_hw_stack_init 在調(diào)用的時(shí)候,傳給stack_addr的是(棧頂指針)*/
stk ==== stack_addr + sizeof(rt_uint32_t);// (5)
/* 讓stk指針向下8字節(jié)對(duì)齊 */
stk ==== (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);// (6)
/* stk指針繼續(xù)向下移動(dòng)sizeof(struct stack_frame)個(gè)偏移 */
stk -==== sizeof(struct stack_frame);// (7)
/* 將stk指針強(qiáng)制轉(zhuǎn)化為stack_frame類(lèi)型后存到stack_frame */
stack_frame ==== (struct stack_frame *)stk;// (8)
/* 以stack_frame為起始地址,將??臻g里面的sizeof(struct stack_frame)
個(gè)內(nèi)存初始化為0xdeadbeef */
for (i ==== 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)// (9)
{
((rt_uint32_t *)stack_frame)[i] ==== 0xdeadbeef;
}
/* 初始化異常發(fā)生時(shí)自動(dòng)保存的寄存器 */// (10)
stack_frame->exception_stack_frame.r0 ==== (unsigned long)parameter; /* r0 : argument */
stack_frame->exception_stack_frame.r1 ==== 0; /* r1 */
stack_frame->exception_stack_frame.r2 ==== 0; /* r2 */
stack_frame->exception_stack_frame.r3 ==== 0; /* r3 */
stack_frame->exception_stack_frame.r12 ==== 0; /* r12 */
stack_frame->exception_stack_frame.lr ==== 0; /* lr */
stack_frame->exception_stack_frame.pc ==== (unsigned long)tentry; /* entry point, pc */
stack_frame->exception_stack_frame.psr ==== 0x01000000L; /* PSR */
/* 返回線(xiàn)程棧指針 */
return stk;// (11)
}
(5) :獲取棧頂指針,將棧頂指針傳給指針stk。rt_hw_stack_init()函數(shù) 在rt_thread_init ()函數(shù)中調(diào)用的時(shí)候傳給形參stack_addr的值是棧頂指針減去4,所以現(xiàn)在 加上sizeof(rt_uint32_t)剛好與減掉的4相互抵消,即傳遞給stk的是棧頂指針。
(6) :讓stk這個(gè)指針向下8個(gè)字節(jié)對(duì)齊,確保stk是8字節(jié)對(duì)齊的地址。 在Cortex-M3(Cortex-M4或Cortex-M7)內(nèi)核的單片機(jī)中,因?yàn)榭偩€(xiàn)寬度是32位的,通常只要棧保持4字節(jié)對(duì)齊就 行,可這樣為啥要8字節(jié)?難道有哪些操作是64位的?確實(shí)有,那就是浮 點(diǎn)運(yùn)算,所以要8字節(jié)對(duì)齊(但是目前我們都還沒(méi)有涉及到浮點(diǎn)運(yùn)算,只是為了后續(xù)兼容浮點(diǎn)運(yùn)行的考慮)。 如果棧頂指針是8字節(jié)對(duì)齊的,在進(jìn)行向下8字節(jié)對(duì)齊的時(shí)候,指針不會(huì)移動(dòng),如果不是8字節(jié)對(duì)齊的, 在做向下8字節(jié)對(duì)齊的時(shí)候,就會(huì)空出幾個(gè)字節(jié),不會(huì)使用,比如當(dāng)stk是33,明顯不能整除8, 進(jìn)行向下8字節(jié)對(duì)齊就是32,那么就會(huì)空出一個(gè)字節(jié)不使用。
(7) :stk指針繼續(xù)向下移動(dòng)sizeof(struct stack_frame) 個(gè)偏移,即16個(gè)字的大小。
ok?。。。。。?!
這些了解了之后,我們?cè)谥骱瘮?shù)內(nèi)加入線(xiàn)程初始化即可
/* 初始化線(xiàn)程 */
rt_thread_init(&rt_flag1_thread, /* 線(xiàn)程控制塊 */
flag1_thread_entry, /* 線(xiàn)程入口地址 */
RT_NULL, /* 線(xiàn)程形參 */
&rt_flag1_thread_stack[0], /* 線(xiàn)程棧起始地址 */
sizeof(rt_flag1_thread_stack) ); /* 線(xiàn)程棧大小,單位為字節(jié) */
/* 將線(xiàn)程插入到就緒列表 */
/* 初始化線(xiàn)程 */
rt_thread_init(&rt_flag2_thread, /* 線(xiàn)程控制塊 */
flag2_thread_entry, /* 線(xiàn)程入口地址 */
RT_NULL, /* 線(xiàn)程形參 */
&rt_flag2_thread_stack[0], /* 線(xiàn)程棧起始地址 */
sizeof(rt_flag2_thread_stack) ); /* 線(xiàn)程棧大小,單位為字節(jié) */
審核編輯:符乾江
-
嵌入式
+關(guān)注
關(guān)注
5175文章
19973瀏覽量
324377 -
線(xiàn)程機(jī)制
+關(guān)注
關(guān)注
0文章
2瀏覽量
5646 -
RT Thread操作系統(tǒng)
+關(guān)注
關(guān)注
0文章
4瀏覽量
785
發(fā)布評(píng)論請(qǐng)先 登錄
使用rt_thread無(wú)法輸出PWM怎么解決?
RTThread線(xiàn)程退出后rt_malloc動(dòng)態(tài)創(chuàng)建的資源沒(méi)有釋放怎么解決?
rt-thread studio 如何進(jìn)行多線(xiàn)程編譯?
線(xiàn)程問(wèn)題,線(xiàn)程已經(jīng)創(chuàng)建成功了,為什么線(xiàn)程調(diào)用的函數(shù)不會(huì)運(yùn)行呢?
創(chuàng)建stm32f103c8工程后為什么終端沒(méi)有打印Hello RT_Thread?
RT-Thread Nano移植后動(dòng)態(tài)創(chuàng)建線(xiàn)程創(chuàng)建不了怎么解決?
STM32已經(jīng)用標(biāo)準(zhǔn)庫(kù)寫(xiě)好的代碼,怎么導(dǎo)入RT THREAD?
在線(xiàn)程刪除時(shí)遇到斷言,是什么原因?qū)е碌模?/a>
線(xiàn)程中調(diào)用rt_thread_mdelay()函數(shù)卡死的原因?怎么解決?
安裝RT thread studio后創(chuàng)建RT thread 項(xiàng)目報(bào)錯(cuò)怎么解決?
如何移植 RT-Thread Nano 并創(chuàng)建 2 個(gè)線(xiàn)程?
深度剖析 RT-Thread 線(xiàn)程調(diào)度流程

評(píng)論