一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

stm32裸機(jī)RT—thread開(kāi)始創(chuàng)建線程詳解

嵌入式應(yīng)用開(kāi)發(fā) ? 來(lái)源:嵌入式應(yīng)用開(kāi)發(fā) ? 作者:嵌入式應(yīng)用開(kāi)發(fā) ? 2022-05-19 15:02 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

在裸機(jī)系統(tǒng) 中,他們統(tǒng)統(tǒng)放在一個(gè)叫棧的地方,棧是單片機(jī)RAM里面一段連續(xù)的內(nèi)存空間,棧的大小一般在啟動(dòng)文件或者鏈接腳 本里面指定,最后由C庫(kù)函數(shù)_main進(jìn)行初始化。

在多線程系統(tǒng)中,每個(gè)線程都是獨(dú)立的,互不干擾的,所以要為每個(gè)線程都分配獨(dú)立的??臻g,這個(gè)??臻g通常是一個(gè)預(yù)先定義好的全局?jǐn)?shù)組,也可以是動(dòng)態(tài)分配的一段內(nèi)存空間,但它們都存在于RAM中。

創(chuàng)建線程棧,需要幾個(gè)線程創(chuàng)建幾個(gè)線程棧,這里線程棧實(shí)際上就是全局變量,大小為512,這里創(chuàng)建兩個(gè)工作線程,如下:

ALIGN(RT_ALIGN_SIZE)//
/* 定義線程棧*/
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__ */

下一步,定義線程函數(shù):

這里我們建立線程函數(shù),并給函數(shù)命名flag1_thread_entry,flag2_thread_entry兩個(gè)函數(shù) ,分別代表著兩個(gè)無(wú)限循環(huán)不返回的線程

/* 軟件延時(shí) */
void delay (uint32_t count)
{
    for(; count!====0; count--);
}
/* 線程1 */
void flag1_thread_entry( void *p_arg )//   (1)
{
    for( ;; )
    {
        flag1 ==== 1;
        delay( 100 );
        flag1 ==== 0;
        delay( 100 );

        /* 線程切換,這里是手動(dòng)切換 */
        rt_schedule();
    }
}
/* 線程2 */
void flag2_thread_entry( void *p_arg )//   (2)
{
    for( ;; )
    {
        flag2 ==== 1;
        delay( 100 );
        flag2 ==== 0;
        delay( 100 );

        /* 線程切換,這里是手動(dòng)切換 */
        rt_schedule();
    }
}

下一步定義線程控制塊

在裸機(jī)系統(tǒng)中,程序的主體是CPU按照順序執(zhí)行的。而在多線程系統(tǒng)中,線程的執(zhí)行是由系統(tǒng)調(diào)度的。系統(tǒng)為了順利的調(diào)度線程,為每個(gè)線程都額外定義了一個(gè)線程控制塊,這個(gè)線程控制塊就相當(dāng)于線程的身份證,里面存有線程的所有信息,比如線程的棧指針,線程名稱,線程的形參等。有了這個(gè)線程控制塊之后,以后系統(tǒng)對(duì)線程的全部操作都可以通過(guò)這個(gè)線程控制塊來(lái)實(shí)現(xiàn)。定義一個(gè)線程控制塊需要一個(gè)新的數(shù)據(jù)類(lèi)型,該數(shù)據(jù)類(lèi)型在rtdef.h這個(gè)頭 文件中聲明,使用它可以為每個(gè)線程都定義一個(gè)線程控制塊實(shí)體。下面來(lái)看一下,此結(jié)構(gòu)體定義函數(shù)及內(nèi)容:

struct rt_thread
{
    void        *sp;                    /* 線程棧指針 */
    void        *entry;              /* 線程入口地址 */
    void        *parameter;       /* 線程形參 */
    void        *stack_addr;      /* 線程起始地址 */
    rt_uint32_t stack_size;       /* 線程棧大小,單位為字節(jié) */

    rt_list_t   tlist;            /* 線程鏈表節(jié)點(diǎn) */
};
typedef struct rt_thread *rt_thread_t;// 

我們?cè)趍ain.c文件中為兩個(gè)線程定義的線程控制塊。

/* 定義線程控制塊 */
struct rt_thread rt_flag1_thread;
struct rt_thread rt_flag2_thread;

下一步,創(chuàng)建線程實(shí)現(xiàn)函數(shù)

線程的棧,線程的函數(shù)實(shí)體,線程的控制塊最終需要聯(lián)系起來(lái)才能由系統(tǒng)進(jìn)行統(tǒng)一調(diào)度。那么這個(gè)聯(lián)系的工作就由線 程初始化函數(shù)rt_thread_init()來(lái)實(shí)現(xiàn),該函數(shù)在thread.c(thread.c第一次使用需要自行在文件夾rtthread/3.0.3/src中新建并添加到工程的rtt/source組)中定義,在rtthread.h中聲明,所有跟線程相關(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)

    /* 初始化線程棧,并返回線程棧指針 */ //                      (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是線程控制塊指針。

(2) :entry 是線程函數(shù)名, 表示線程的入口。

(3) :parameter是線程形參,用于傳遞線程參數(shù)。

(4) :stack_start 用于指向線程棧的起始地址。

(5) :stack_size表示線程棧的大小,單位為字節(jié)。

(7) :將線程入口保存到線程控制塊的entry成員中。

(8) :將線程入口形參保存到線程控制塊的parameter成員中。

(9) :將線程棧起始地址保存到線程控制塊的stack_start成員中。

(10) :將線程棧起大小保存到線程控制塊的stack_size成員中。

(11) :初始化線程棧,并返回線程棧頂指針。

rt_hw_stack_init()用來(lái)初始化線程棧, 當(dāng)線程第一次運(yùn)行的時(shí)候,加載到CPU寄存器的參數(shù)就放在線程棧里面,該函數(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)。

pYYBAGKF8peAQwaOAAB3seF5MG4606.png雙向鏈表輪詢示意圖

雙向鏈表的相關(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)行了如下操作:

poYBAGKF9FaAZa-8AAA6i6E1JnY592.png鏈表初始化示意圖

下面在鏈表頭部和尾部分別插入一個(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)建線程初始化函數(shù)

/* 線程棧初始化 */
rt_uint8_t *rt_hw_stack_init(void       *tentry,//                  (1)
                            void       *parameter,//                 (2)
                            rt_uint8_t *stack_addr)// 線程棧頂?shù)刂?4,在該函數(shù)調(diào)用的時(shí)候傳進(jìn)來(lái)的是線程棧的棧頂?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 */

    /* 返回線程棧指針 */
    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)榭偩€寬度是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?。。。。。。?/strong>

這些了解了之后,我們?cè)谥骱瘮?shù)內(nèi)加入線程初始化即可

/* 初始化線程 */
rt_thread_init(&rt_flag1_thread,                 /* 線程控制塊 */
                flag1_thread_entry,               /* 線程入口地址 */
                RT_NULL,                          /* 線程形參 */
                &rt_flag1_thread_stack[0],        /* 線程棧起始地址 */
                sizeof(rt_flag1_thread_stack) );  /* 線程棧大小,單位為字節(jié) */
/* 將線程插入到就緒列表 */

/* 初始化線程 */
rt_thread_init(&rt_flag2_thread,                 /* 線程控制塊 */
                flag2_thread_entry,               /* 線程入口地址 */
                RT_NULL,                          /* 線程形參 */
                &rt_flag2_thread_stack[0],        /* 線程棧起始地址 */
                sizeof(rt_flag2_thread_stack) );  /* 線程棧大小,單位為字節(jié) */


審核編輯:符乾江

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5145

    文章

    19597

    瀏覽量

    316136
  • 線程機(jī)制
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    5630
  • RT Thread操作系統(tǒng)

    關(guān)注

    0

    文章

    4

    瀏覽量

    744
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    深度剖析 RT-Thread 線程調(diào)度流程

    RT-Thread調(diào)度第一個(gè)線程的主要流程分如下:rtthread_startup:RTT的啟動(dòng)函數(shù),主要負(fù)責(zé)板級(jí)驅(qū)動(dòng),調(diào)度器,系統(tǒng)線程初始化,啟動(dòng)調(diào)度的工作
    的頭像 發(fā)表于 06-25 18:24 ?386次閱讀
    深度剖析 <b class='flag-5'>RT-Thread</b> <b class='flag-5'>線程</b>調(diào)度流程

    RT-Thread Nano移植后動(dòng)態(tài)創(chuàng)建線程創(chuàng)建不了怎么處理?

    RT-Thread Nano移植后動(dòng)態(tài)創(chuàng)建線程創(chuàng)建不了,靜態(tài)可以.直接燒錄DEMO也一樣,將RT_USING_HEAP開(kāi)起來(lái),使用動(dòng)態(tài)
    發(fā)表于 06-11 06:36

    STM32已經(jīng)用標(biāo)準(zhǔn)庫(kù)寫(xiě)好的代碼,怎么導(dǎo)入RT THREAD

    之前已經(jīng)在裸機(jī)上實(shí)現(xiàn)了SAE J1939協(xié)議,因?yàn)橐砑覫OT 功能,現(xiàn)在想添加RT THREAD.因?yàn)橹皩?xiě)裸機(jī)程序的時(shí)候用的是標(biāo)準(zhǔn)庫(kù)。而RT
    發(fā)表于 05-27 06:01

    創(chuàng)建stm32f103c8工程后為什么終端沒(méi)有打印Hello RT_Thread?

    創(chuàng)建stm32f103c8工程后為什么終端沒(méi)有打印Hello RT_Thread!,程序好像也沒(méi)有下載進(jìn)去
    發(fā)表于 04-01 06:55

    請(qǐng)問(wèn)rt-thread studio如何進(jìn)行多線程編譯?

    使用 rt-thread studio 在工程配置 C/C++構(gòu)建->Behavior->parallel build 數(shù)量修改,CPU的占用率沒(méi)有明顯的改變
    發(fā)表于 02-19 08:30

    RT-Thread上CAN實(shí)踐

    開(kāi)箱測(cè)試RT-Thread官方已完成了對(duì)英飛凌XMC7200EVK的移植,通過(guò)shell可以看到做好了uart3的console。本文將介紹如何進(jìn)行RT-ThreadCan移植。接下來(lái)我們要完成CAN_FD的驅(qū)動(dòng)移植,并正常啟動(dòng)RT-T
    的頭像 發(fā)表于 11-13 01:03 ?1982次閱讀
    <b class='flag-5'>RT-Thread</b>上CAN實(shí)踐

    Nordic-RT-Thread5.1.0移植筆記

    Nordic-RT-Thread5.1.0移植筆記
    的頭像 發(fā)表于 10-16 08:09 ?1207次閱讀
    Nordic-<b class='flag-5'>RT-Thread</b>5.1.0移植筆記

    為什么在rt-thread studio創(chuàng)建不了gd32的項(xiàng)目?

    為什么在rt-thread studio中創(chuàng)建一個(gè)gd32項(xiàng)目時(shí)候,他打開(kāi)的那個(gè)選型芯片型號(hào)是,安裝好了,沒(méi)有確認(rèn)鍵啊,只有退出sdk管理器,然后就卡在那里了,創(chuàng)建不了gd32的項(xiàng)目
    發(fā)表于 09-27 09:52

    線程創(chuàng)建成功了,為啥ai_thread_entry()函數(shù)不運(yùn)行呢?

    我這個(gè)線程創(chuàng)建成功了,為啥ai_thread_entry()函數(shù)不運(yùn)行呢? void airun_thread() { /* 創(chuàng)建 se
    發(fā)表于 09-27 09:35

    stm32cubmx生成的makefile編譯無(wú)法啟動(dòng)線程怎么解決?

    stm32cubmx生成的makefile編譯無(wú)法啟動(dòng)線程,rt_thread_startup返回 RT_EOK無(wú)錯(cuò)誤 但是使用生成的 keil工程編譯就沒(méi)問(wèn)題, 板子是
    發(fā)表于 09-27 09:27

    如何在RT-thread studio的裸機(jī)例程上移植freertos?

    如何在RT-thread studio的裸機(jī)例程上移植freertos
    發(fā)表于 09-13 06:32

    2024 RT-Thread全球巡回 線下培訓(xùn)火熱來(lái)襲!

    親愛(ài)的RT-Thread社區(qū)成員們:我們非常高興地宣布,2024年RT-Thread全球開(kāi)發(fā)者線下培訓(xùn)即將拉開(kāi)帷幕!24年全球巡回培訓(xùn)將覆蓋超10座城市及國(guó)家,為開(kāi)發(fā)者提供一個(gè)深入學(xué)習(xí)RT-Thread嵌入式開(kāi)發(fā)的絕佳機(jī)會(huì)。
    的頭像 發(fā)表于 08-07 08:35 ?2693次閱讀
    2024 <b class='flag-5'>RT-Thread</b>全球巡回 線下培訓(xùn)火熱來(lái)襲!

    玩轉(zhuǎn)RT-Thread之消息隊(duì)列的應(yīng)用

    在嵌入式系統(tǒng)開(kāi)發(fā)中,實(shí)時(shí)處理串口和ADC數(shù)據(jù)是一項(xiàng)重要的任務(wù)。本文將介紹如何在RT-Thread實(shí)時(shí)操作系統(tǒng)中,利用消息隊(duì)列來(lái)同時(shí)處理來(lái)自串口和ADC的數(shù)據(jù)。通過(guò)這種方法,我們能夠高效地管理和處理
    的頭像 發(fā)表于 07-23 08:11 ?886次閱讀
    玩轉(zhuǎn)<b class='flag-5'>RT-Thread</b>之消息隊(duì)列的應(yīng)用

    使用rt_thread_mdelay函數(shù)后出現(xiàn)hardfault的原因?

    問(wèn)題:在一個(gè)線程中共有前后2部分使用rt_thread_mdelay()進(jìn)行延時(shí)等待,前面部分延時(shí)是正常的,后面部分進(jìn)入rt_thread_mdelay()函數(shù)后立馬打印hardfault錯(cuò)誤,定位
    發(fā)表于 07-16 07:07

    lvgl移植到RT-Thread Nano后進(jìn)入硬件錯(cuò)誤中斷的原因?

    使用的是RT-Thread Nano的最新版,第一次移植,不知道是不是因?yàn)檫@個(gè)lvgl只能移植到標(biāo)準(zhǔn)版里? 嘗試過(guò)給lvgl的線程分配更大的棧,但是依舊會(huì)卡死在硬件錯(cuò)誤中斷里。但只要把用戶的ui
    發(fā)表于 07-04 07:40