偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

從零開(kāi)始構(gòu)建嵌入式實(shí)時(shí)操作系統(tǒng)—重構(gòu)

運(yùn)維 系統(tǒng)運(yùn)維
希望嵌入式操作系統(tǒng)系列文章能對(duì)其它的嵌入式愛(ài)好者能有所幫助,幫助熱愛(ài)嵌入式行業(yè)的朋友快速了解嵌入式操作系統(tǒng)的運(yùn)行原理。

1、前言

本人是一個(gè)普通的中年程序員,并不是圈內(nèi)的大牛,寫嵌入式操作系統(tǒng)這一系列的文章并不是要顯示自己的技術(shù),而是出于對(duì)嵌入式的熱愛(ài)。非常幸運(yùn),本人畢業(yè)后的十幾年一直從事嵌入式行業(yè),遇到過(guò)各種坑,也收獲過(guò)各種喜悅。希望嵌入式操作系統(tǒng)系列文章能對(duì)其它的嵌入式愛(ài)好者能有所幫助,幫助熱愛(ài)嵌入式行業(yè)的朋友快速了解嵌入式操作系統(tǒng)的運(yùn)行原理。

我將一步一步地完善我們的嵌入式實(shí)時(shí)操作系統(tǒng)enuo,每完成一步軟件的構(gòu)建,我將輸出一篇總結(jié)性的文件,來(lái)分享軟件構(gòu)建過(guò)程,并開(kāi)源軟件工程和源碼。

操作系統(tǒng)enuo的名字來(lái)源于我5歲兒子的伊諾,希望在我的守護(hù)下enuo和伊諾都能健康快樂(lè),茁壯成長(zhǎng)!

2、設(shè)計(jì)背景

書接上文我們完成了一個(gè)可以實(shí)現(xiàn)任務(wù)切換的軟件工程V0.01版本。V0.01版本的軟件工程中包含:main.c ,startup_ stm32f401xc.s 和 readme三個(gè)文件。startup_ stm32f401xc.s 文件為STM32F401的啟動(dòng)文件,main.c文件實(shí)現(xiàn)任務(wù)初始化,任務(wù)切換和任務(wù)輪詢調(diào)度功能,readme文件用于記錄版本修改日志。

V0.01版只能算一個(gè)功能驗(yàn)證型軟件,接下來(lái)需要使用正規(guī)的軟件設(shè)計(jì)方法來(lái)改造和重構(gòu)整個(gè)工程,使軟件系統(tǒng)具有較高的擴(kuò)展性,移植性,復(fù)用性和可讀性。

3、設(shè)計(jì)目標(biāo)

首先運(yùn)用軟件設(shè)計(jì)五大原則中的單一原則,建立一個(gè)獨(dú)立的文件夾enuo用于存放于操作系統(tǒng)相關(guān)的源文件,每個(gè)源文件完成一個(gè)單一的功能。使用“分而治之”的設(shè)計(jì)思維,并將操作系統(tǒng)分為多個(gè)功能單一的模塊,每個(gè)模塊以一個(gè)C文件的形式承載,這樣就提高了軟件的可讀性和移植性。

其次同時(shí)使用面向?qū)ο蟮脑O(shè)計(jì)思維,將任務(wù)設(shè)計(jì)為一個(gè)抽象的對(duì)象,任務(wù)對(duì)象將任務(wù)的信息封裝起來(lái),這樣就提高了軟件的擴(kuò)展性和復(fù)用性。雖然C語(yǔ)言不是面向?qū)ο缶幊陶Z(yǔ)言,但是通過(guò)一些設(shè)計(jì)技巧可以實(shí)現(xiàn)面向?qū)ο笤O(shè)計(jì)。

最后構(gòu)建一個(gè)任務(wù)鏈表用于加入和刪除任務(wù)。鏈表數(shù)據(jù)結(jié)構(gòu)可以在保留原有物理順序的情況下,高效地插入和刪除。

4、設(shè)計(jì)環(huán)境

硬件環(huán)境是使用STM32F401RE為核心的自制開(kāi)發(fā)板。

軟件環(huán)境是使用的KEIL V5.2 開(kāi)發(fā)工具。

5、設(shè)計(jì)過(guò)程

(1)構(gòu)建任務(wù)對(duì)象

任務(wù)對(duì)象包含一個(gè)棧指針和一個(gè)任務(wù)鏈表,其定義如下:

任務(wù)棧指針為第一個(gè)元素,這樣棧指針就和任務(wù)對(duì)象為同一個(gè)地址(結(jié)構(gòu)體的第一個(gè)元素就是結(jié)構(gòu)體的首地址),這樣就可以極大的簡(jiǎn)化任務(wù)切換過(guò)程中對(duì)棧指針的操作。

任務(wù)棧指針指向用戶為任務(wù)定義的靜態(tài)數(shù)據(jù)塊,通常情況下任務(wù)棧是一個(gè)全局靜態(tài)數(shù)組,數(shù)組的大小就是任務(wù)棧的大小。

任務(wù)鏈表的作用是將多個(gè)任務(wù)串聯(lián)起來(lái),方便依次檢索和操作。

(2)構(gòu)建鏈表結(jié)構(gòu)

我們使用鏈表結(jié)構(gòu)來(lái)存放任務(wù)對(duì)象,使用鏈表結(jié)構(gòu)有如下優(yōu)勢(shì):

1、在保留原有物理順序的情況下,插入和刪除速度快,效率高。插入和刪除只需要改變幾個(gè)指針變量。

2、鏈表中的表項(xiàng)數(shù)量沒(méi)有上限。存儲(chǔ)的表項(xiàng)上限只與內(nèi)存空間大小有關(guān),理論上如果內(nèi)存無(wú)限大,鏈表中的表項(xiàng)可以動(dòng)態(tài)增加到無(wú)限個(gè)。

3、動(dòng)態(tài)分配內(nèi)存,需要用多少個(gè)表項(xiàng),就分配幾個(gè)表項(xiàng),不需要預(yù)先分配內(nèi)存,不存在內(nèi)存浪費(fèi)的情況。

鏈表結(jié)構(gòu)定義如下:

鏈表采用的是數(shù)據(jù)中包含鏈表的數(shù)據(jù)結(jié)構(gòu),采用這種方式的優(yōu)點(diǎn)是:當(dāng)用戶數(shù)據(jù)結(jié)構(gòu)改變時(shí),整個(gè)鏈表結(jié)構(gòu)可以保持不變,不同的用戶數(shù)據(jù)可以通用這一個(gè)的鏈表結(jié)構(gòu)。示意圖如下:

數(shù)據(jù)中包含鏈表的數(shù)據(jù)結(jié)構(gòu),在操作鏈表后無(wú)法得到整個(gè)數(shù)據(jù)對(duì)象的地址。由下圖可知圖可知我們通過(guò)next指針可以得到下一個(gè)list元素的地址,但是我們無(wú)法獲取整個(gè)數(shù)據(jù)對(duì)象的地址。

在鏈表結(jié)構(gòu)中增加一個(gè)void * owner指針,void * 類型指針可以指向任意類型的對(duì)象,用這個(gè)指針指向整個(gè)數(shù)據(jù)對(duì)象的地址,這樣就可以定位到整個(gè)數(shù)據(jù)對(duì)象的地址。

struct list_node_def
{
struct list_node_def * next; /* 指向下一個(gè)列表節(jié)點(diǎn) */
struct list_node_def * previous; /* 指向上一個(gè)列表節(jié)點(diǎn) */
void *owner; /* 指向鏈表節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu) */
};

哨兵機(jī)制(表頭機(jī)制)

哨兵是一個(gè)啞對(duì)象,作用是簡(jiǎn)化邊界條件處理。使用哨兵機(jī)制(表頭機(jī)制)后鏈表在空狀態(tài)和非空狀態(tài)插入和刪除對(duì)象的操作是相同的,這樣可以使得代碼緊湊,清晰。

typedef struct list_def
{
list_node_t *index; /* 索引指針 */
list_node_t head; /* 列表頭 */
} list_t;

(3)任務(wù)初始化

任務(wù)初始化代碼如下:

在創(chuàng)建任務(wù)之前,需要定義任務(wù)??臻g和用戶任務(wù)函數(shù):

/*********************************************************************************************************
* @名稱 : enuo
* @作者 : 李巍
**********************************************************************************************************/
#define STACK_NUM (64)
/* 任務(wù)0-任務(wù)2??臻g */
uint32_t task0_stack[STACK_NUM];
uint32_t task1_stack[STACK_NUM];
uint32_t task2_stack[STACK_NUM];
/* 任務(wù)0-任務(wù)2對(duì)象*/
task_tcb_t my_task0;
task_tcb_t my_task1;
task_tcb_t my_task2;
void task0(void)
{
static uint16_t clk = 0;
while(1)
{
if( ( ( clk++ )%9999 ) == 0 )
{
task_debug_num0++; /* 測(cè)試跟蹤 */
test_function();
}
}
}

task_create函數(shù)代碼如下:

void task_create(task_tcb_t *task , task_function_t function ,uint32_t *stack_space ,uint32_t stack_number)
{
list_node_t * node_tail = &task_list.head;
/* 尋求列表尾端 */
while( node_tail->next != NULL )
node_tail = node_tail->next;
/* 任務(wù)列表加入下一個(gè)任務(wù) */
node_tail->next = &task->task_list;
/* 列表所有者指針指向任務(wù) */
task->task_list.owner = task;
/* 當(dāng)前任務(wù)下個(gè)列表指針設(shè)置為空 */
task->task_list.next = NULL;
/* 初始化任務(wù)棧 */
task_stack_init( (uint32_t *)task, function , stack_space , stack_number );
}

task_create函數(shù)完成任務(wù)鏈表操作,初始化任務(wù)棧指針和任務(wù)??臻g。

task_stack_init代碼如下:

void task_stack_init(uint32_t     *stack_pointer , 
task_function_t task ,
uint32_t *stack_space ,
uint32_t stack_number)
{
/* 將任務(wù)psp棧指針指向任務(wù)棧底部*/
*stack_pointer = ( (uint32_t)stack_space + ( (stack_number - 16)<<2 ) );
/* 初始化任務(wù)棧中的程序寄存器 */
*( (uint32_t *)( (uint32_t )( *stack_pointer) + (14<<2) ) ) = ( uint32_t )task;
/* 初始化任務(wù)棧中的XPSR*/
*( (uint32_t *)( (uint32_t )( *stack_pointer) + (15<<2) ) ) = 0x01000000;
}

(4)開(kāi)始調(diào)度任務(wù)

完成任務(wù)創(chuàng)建之后,就可以開(kāi)始調(diào)度任務(wù),開(kāi)始調(diào)度任務(wù)代碼如下:

enuo_schedule函數(shù)代碼如下:

/*********************************************************************************************************
* @名稱 : enuo
* @作者 : 李巍
**********************************************************************************************************/
__asm void start_schedule(void)
{
/* 設(shè)置CONTROL寄存器 配置PSP棧指針模式 */
MOV R0 ,#0X02
MSR CONTROL,R0

/*讀取current_task 地址 */
LDR R3, =__cpp(¤t_task)
/* 讀取curr_task中的PSP指針數(shù)值 */
LDR R1,[R3]
LDR R0,[R1]

/* 出棧R4-R11八個(gè)寄存器 */
LDMIA R0!,{R4-R11}
/* 設(shè)置PSP指針 */
MSR PSP,R0
/* POP 寄存器 POP PC實(shí)現(xiàn)跳轉(zhuǎn) */
POP { R0-R3 , R12 ,R11 ,PC }
/* 對(duì)齊 */
ALIGN 4
}

enuo_schedule函數(shù)主要完成3個(gè)功能:

  1. 設(shè)置處理器棧指針模式。
  2. 讀取current_task 地址。
  3. 恢復(fù)任務(wù)寄存器,POP PC實(shí)現(xiàn)跳轉(zhuǎn)用戶任務(wù)代碼。

(5)調(diào)度任務(wù)方法

任務(wù)調(diào)度采用時(shí)間片輪轉(zhuǎn)調(diào)度,使用SysTick定時(shí)器產(chǎn)生定時(shí)中斷,在定時(shí)器中斷函數(shù)中依次從task_list任務(wù)鏈表中讀取任務(wù)對(duì)象,并用next_task指向新讀出的任務(wù)對(duì)象,最后掛起PendSV系統(tǒng)中斷標(biāo)志位,當(dāng)SysTick定時(shí)器中斷函數(shù)退出時(shí)執(zhí)行PendSV_Handler進(jìn)行任務(wù)切換。

SysTick定時(shí)器中斷函數(shù)代碼如下:

/*********************************************************************************************************
* @名稱 : enuo
* @作者 : 李巍
**********************************************************************************************************/
void SysTick_Handler(void)
{
static list_node_t * node_tail = &task_list.head;
/* 輪流切換任務(wù) */
if(node_tail->next != NULL )
{
/* 當(dāng)下一個(gè)列表項(xiàng)不為NULL時(shí) next_task為下一個(gè)列表項(xiàng)指向的任務(wù)*/
next_task = node_tail->next->owner;
/* 更新任務(wù)指針 */
node_tail = node_tail->next;
}
else
{
/* 當(dāng)下一個(gè)列表項(xiàng)為NULL時(shí) ,node_tail指向任務(wù)列表的表頭*/
node_tail = &task_list.head;
/* next_task為表頭指向的下一個(gè)的任務(wù)*/
next_task = node_tail->next->owner;
node_tail = node_tail->next;
}

/* PendSV系統(tǒng)中斷置位 */
SCB->ICSR |=SCB_ICSR_PENDSVSET_Msk;
return;
}

(6)任務(wù)切換

任務(wù)切換在PendSV_Handler異常(中斷)中任務(wù)切換。PendSV_Handler函數(shù)代碼如下:

/*********************************************************************************************************
* @名稱 : enuo
* @作者 : 李巍
**********************************************************************************************************/
__asm void PendSV_Handler(void)
{
/* 讀取當(dāng)前進(jìn)程棧指針數(shù)值 */
MRS R0,PSP
//isb
/* 保存R4-R11八個(gè)寄存器的值到當(dāng)前任務(wù)棧中 同時(shí)將回寫的地址寫入R0 */
STMDB R0!,{R4-R11}
/* 讀取current_task 棧指針地址 */
LDR R3, =__cpp(¤t_task)
LDR R3, [R3]
/* 將當(dāng)前進(jìn)程PSP指針值 寫入 相應(yīng)的 current_task */
STR R0,[R3]
/* 獲取next_task 棧指針地址 */
LDR R4,=__cpp(&next_task)
LDR R4,[R4]
/* 讀取next_task中的stack_point指針 */
LDR R0,[R4]
/* 更新current_task */
LDR R3, =__cpp(¤t_task)
STR R4,[R3]

/* 出棧 R4-R11八個(gè)寄存器 */
LDMIA R0!,{R4-R11}
/* 設(shè)置PSP指針 */
MSR PSP,R0
/* 中斷返回 */
BX LR
/* 對(duì)齊 */
ALIGN 4
}

PendSV_Handler函數(shù)完成以下3個(gè)功能:

  1. 進(jìn)入PendSV_Handler中斷前處理器自動(dòng)保存了R0,R1,R2,R3, R12,LR,PC,XPSR,進(jìn)入中斷后完成R4~R11入棧保存工作,從而實(shí)現(xiàn)任務(wù)保存工作。
  2. 讀取current_task 地址將當(dāng)前進(jìn)程PSP指針值保存到current_task指向的任務(wù)對(duì)象中。讀取next_task指向的任務(wù)對(duì)象,并加載任務(wù)對(duì)象的PSP指針值。
  3. 出棧 R4-R11八個(gè)寄存器,設(shè)置PSP指針。中斷返回時(shí)處理器自動(dòng)保存了R0,R1,R2,R3 ,R12, LR,PC,XPSR,從而實(shí)現(xiàn)任務(wù)恢復(fù)工作。

6、運(yùn)行結(jié)果

代碼仿真運(yùn)行后的結(jié)果如下:

運(yùn)行結(jié)果反應(yīng)創(chuàng)建的3個(gè)任務(wù)都得到了調(diào)度,說(shuō)明enuo系統(tǒng)正常工作。

enuo系統(tǒng)目前包括3個(gè)文件:

(1)task.h文件

本文件中包含任務(wù)對(duì)象的定義,鏈表數(shù)據(jù)結(jié)構(gòu)的定義和任務(wù)列表數(shù)據(jù)結(jié)構(gòu)的定義。

(2)task.c文件

本文件中包含task_create和enuo_schedule兩個(gè)函數(shù)。task.c文件只用于存放與任務(wù)操作相關(guān)的函數(shù)(符合單一原則)。

(3)interface.c文件

本文件中包含SysTick_Handler,PendSV_Handler,start_schedule和task_stack_init三個(gè)函數(shù)。interface.c文件只用與存放跟處理器相關(guān)的操作。后期更換處理器時(shí)只用修改這個(gè)文件即可,增強(qiáng)了系統(tǒng)的移植性。

責(zé)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2022-03-30 08:24:25

操作系統(tǒng)內(nèi)核開(kāi)源軟件

2011-04-14 15:14:36

嵌入式操作系統(tǒng)嵌入式

2012-03-09 09:45:29

Windows嵌入式操作系統(tǒng)

2014-11-17 21:19:58

VxWorks 7風(fēng)河

2010-03-30 15:44:16

Windows CE

2009-12-09 10:34:10

嵌入式Linux操作系

2017-08-03 23:40:49

無(wú)操作系統(tǒng)嵌入式開(kāi)發(fā)

2011-04-25 10:25:43

OpenEmbedde嵌入式Linux

2009-08-21 15:33:56

應(yīng)用技巧嵌入式LinuxLinux操作系統(tǒng)

2022-05-06 15:56:01

開(kāi)源物聯(lián)網(wǎng)邊緣計(jì)算

2017-06-15 10:36:35

WebAssembly計(jì)算模塊

2018-08-29 10:49:00

2024-07-31 08:14:17

2024-03-01 19:53:37

PyBuilderPython開(kāi)發(fā)

2017-02-10 09:30:33

數(shù)據(jù)化運(yùn)營(yíng)流量

2010-02-22 09:39:52

HTML 5Web

2023-10-31 11:12:20

Windows微軟

2009-09-04 08:26:55

Windows 7嵌入式版

2009-07-03 13:24:33

調(diào)試嵌入式操作系統(tǒng)

2018-06-27 09:14:54

嵌入式操作系統(tǒng)Linux
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)