成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從零開始構建嵌入式實時操作系統—重構

運維 系統運維
希望嵌入式操作系統系列文章能對其它的嵌入式愛好者能有所幫助,幫助熱愛嵌入式行業的朋友快速了解嵌入式操作系統的運行原理。

1、前言

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

我將一步一步地完善我們的嵌入式實時操作系統enuo,每完成一步軟件的構建,我將輸出一篇總結性的文件,來分享軟件構建過程,并開源軟件工程和源碼。

操作系統enuo的名字來源于我5歲兒子的伊諾,希望在我的守護下enuo和伊諾都能健康快樂,茁壯成長!

2、設計背景

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

V0.01版只能算一個功能驗證型軟件,接下來需要使用正規的軟件設計方法來改造和重構整個工程,使軟件系統具有較高的擴展性,移植性,復用性和可讀性。

3、設計目標

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

其次同時使用面向對象的設計思維,將任務設計為一個抽象的對象,任務對象將任務的信息封裝起來,這樣就提高了軟件的擴展性和復用性。雖然C語言不是面向對象編程語言,但是通過一些設計技巧可以實現面向對象設計。

最后構建一個任務鏈表用于加入和刪除任務。鏈表數據結構可以在保留原有物理順序的情況下,高效地插入和刪除。

4、設計環境

硬件環境是使用STM32F401RE為核心的自制開發板。

軟件環境是使用的KEIL V5.2 開發工具。

5、設計過程

(1)構建任務對象

任務對象包含一個棧指針和一個任務鏈表,其定義如下:

任務棧指針為第一個元素,這樣棧指針就和任務對象為同一個地址(結構體的第一個元素就是結構體的首地址),這樣就可以極大的簡化任務切換過程中對棧指針的操作。

任務棧指針指向用戶為任務定義的靜態數據塊,通常情況下任務棧是一個全局靜態數組,數組的大小就是任務棧的大小。

任務鏈表的作用是將多個任務串聯起來,方便依次檢索和操作。

(2)構建鏈表結構

我們使用鏈表結構來存放任務對象,使用鏈表結構有如下優勢:

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

2、鏈表中的表項數量沒有上限。存儲的表項上限只與內存空間大小有關,理論上如果內存無限大,鏈表中的表項可以動態增加到無限個。

3、動態分配內存,需要用多少個表項,就分配幾個表項,不需要預先分配內存,不存在內存浪費的情況。

鏈表結構定義如下:

鏈表采用的是數據中包含鏈表的數據結構,采用這種方式的優點是:當用戶數據結構改變時,整個鏈表結構可以保持不變,不同的用戶數據可以通用這一個的鏈表結構。示意圖如下:

數據中包含鏈表的數據結構,在操作鏈表后無法得到整個數據對象的地址。由下圖可知圖可知我們通過next指針可以得到下一個list元素的地址,但是我們無法獲取整個數據對象的地址。

在鏈表結構中增加一個void * owner指針,void * 類型指針可以指向任意類型的對象,用這個指針指向整個數據對象的地址,這樣就可以定位到整個數據對象的地址。

struct list_node_def
{
struct list_node_def * next; /* 指向下一個列表節點 */
struct list_node_def * previous; /* 指向上一個列表節點 */
void *owner; /* 指向鏈表節點數據結構 */
};

哨兵機制(表頭機制)

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

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

(3)任務初始化

任務初始化代碼如下:

在創建任務之前,需要定義任務??臻g和用戶任務函數:

/*********************************************************************************************************
* @名稱 : enuo
* @作者 : 李巍
**********************************************************************************************************/
#define STACK_NUM (64)
/* 任務0-任務2棧空間 */
uint32_t task0_stack[STACK_NUM];
uint32_t task1_stack[STACK_NUM];
uint32_t task2_stack[STACK_NUM];
/* 任務0-任務2對象*/
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++; /* 測試跟蹤 */
test_function();
}
}
}

task_create函數代碼如下:

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;
/* 任務列表加入下一個任務 */
node_tail->next = &task->task_list;
/* 列表所有者指針指向任務 */
task->task_list.owner = task;
/* 當前任務下個列表指針設置為空 */
task->task_list.next = NULL;
/* 初始化任務棧 */
task_stack_init( (uint32_t *)task, function , stack_space , stack_number );
}

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

task_stack_init代碼如下:

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

(4)開始調度任務

完成任務創建之后,就可以開始調度任務,開始調度任務代碼如下:

enuo_schedule函數代碼如下:

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

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

/* 出棧R4-R11八個寄存器 */
LDMIA R0!,{R4-R11}
/* 設置PSP指針 */
MSR PSP,R0
/* POP 寄存器 POP PC實現跳轉 */
POP { R0-R3 , R12 ,R11 ,PC }
/* 對齊 */
ALIGN 4
}

enuo_schedule函數主要完成3個功能:

  1. 設置處理器棧指針模式。
  2. 讀取current_task 地址。
  3. 恢復任務寄存器,POP PC實現跳轉用戶任務代碼。

(5)調度任務方法

任務調度采用時間片輪轉調度,使用SysTick定時器產生定時中斷,在定時器中斷函數中依次從task_list任務鏈表中讀取任務對象,并用next_task指向新讀出的任務對象,最后掛起PendSV系統中斷標志位,當SysTick定時器中斷函數退出時執行PendSV_Handler進行任務切換。

SysTick定時器中斷函數代碼如下:

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

/* PendSV系統中斷置位 */
SCB->ICSR |=SCB_ICSR_PENDSVSET_Msk;
return;
}

(6)任務切換

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

/*********************************************************************************************************
* @名稱 : enuo
* @作者 : 李巍
**********************************************************************************************************/
__asm void PendSV_Handler(void)
{
/* 讀取當前進程棧指針數值 */
MRS R0,PSP
//isb
/* 保存R4-R11八個寄存器的值到當前任務棧中 同時將回寫的地址寫入R0 */
STMDB R0!,{R4-R11}
/* 讀取current_task 棧指針地址 */
LDR R3, =__cpp(¤t_task)
LDR R3, [R3]
/* 將當前進程PSP指針值 寫入 相應的 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八個寄存器 */
LDMIA R0!,{R4-R11}
/* 設置PSP指針 */
MSR PSP,R0
/* 中斷返回 */
BX LR
/* 對齊 */
ALIGN 4
}

PendSV_Handler函數完成以下3個功能:

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

6、運行結果

代碼仿真運行后的結果如下:

運行結果反應創建的3個任務都得到了調度,說明enuo系統正常工作。

enuo系統目前包括3個文件:

(1)task.h文件

本文件中包含任務對象的定義,鏈表數據結構的定義和任務列表數據結構的定義。

(2)task.c文件

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

(3)interface.c文件

本文件中包含SysTick_Handler,PendSV_Handler,start_schedule和task_stack_init三個函數。interface.c文件只用與存放跟處理器相關的操作。后期更換處理器時只用修改這個文件即可,增強了系統的移植性。

責任編輯:姜華 來源: 今日頭條
相關推薦

2022-03-30 08:24:25

操作系統內核開源軟件

2012-03-09 09:45:29

Windows嵌入式操作系統

2011-04-14 15:14:36

嵌入式操作系統嵌入式

2014-11-17 21:19:58

VxWorks 7風河

2010-03-30 15:44:16

Windows CE

2017-08-03 23:40:49

無操作系統嵌入式開發

2009-12-09 10:34:10

嵌入式Linux操作系

2011-04-25 10:25:43

OpenEmbedde嵌入式Linux

2009-08-21 15:33:56

應用技巧嵌入式LinuxLinux操作系統

2022-05-06 15:56:01

開源物聯網邊緣計算

2018-08-29 10:49:00

2017-06-15 10:36:35

WebAssembly計算模塊

2009-09-02 08:46:12

2015-07-17 11:18:14

嵌入式操作系統OpenELEC

2017-02-10 09:30:33

數據化運營流量

2010-02-22 09:39:52

HTML 5Web

2018-06-27 09:14:54

嵌入式操作系統Linux

2023-10-31 11:12:20

Windows微軟

2009-09-04 08:26:55

Windows 7嵌入式版

2009-07-03 13:24:33

調試嵌入式操作系統
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线中文视频 | 亚洲高清一区二区三区 | 中文字幕欧美一区 | 天天天堂| 香蕉婷婷 | 久久91 | 激情一区二区三区 | 国产精品毛片在线 | 91麻豆蜜桃一区二区三区 | 亚洲一区久久 | 中文字幕视频在线观看 | 99精品热视频 | 草久网| 一区二区三区国产在线观看 | 国产午夜高清 | 久久性色 | 亚洲啊v在线 | 国产精品久久久久久婷婷天堂 | 国产欧美精品 | 亚洲人va欧美va人人爽 | 日韩综合在线播放 | 国产综合一区二区 | 成人在线一区二区 | 天堂在线1 | 亚洲精品在线观看视频 | 在线看片国产精品 | 国产一二区视频 | 亚洲视频 欧美视频 | 三级视频在线观看电影 | 成人黄色电影在线观看 | 国产成人在线视频免费观看 | 国产专区在线 | 久夜精品 | 亚洲国产精品区 | 国产一级一片免费播放 | 欧美日批 | www.日本三级| 美人の美乳で授乳プレイ | 亚洲三级在线观看 | 欧美人妇做爰xxxⅹ性高电影 | 成人在线视频网 |