想學習單片機的人注意了,這樣才是更高效系統的學習freertos

2022-03-31   大方老師單片機

原標題:想學習單片機的人注意了,這樣才是更高效系統的學習freertos

想學習單片機的人注意了,這樣才是更高效系統freertosSTM32平台)

摘要:學過用FreeRTOS的人都知道,在創建任務時候我們都要定義一個任務句柄,這個任務句柄有啥含義?書上的解釋是任務創建成功以後會返回此任務的任務句柄,這個句柄就是任務的堆棧。此參數就用來保存這個任務句柄。其API函數可能會使用到這個句柄。

那麼任務句柄是到底是怎麼一回事,它保存的是任務控制塊的首地址。那麼它又是如何來保存任務的首地址呢?這就是我們今天要討論的話題。我儘量寫得通俗易懂,讓大家都能輕鬆理解

1、創建一個任務

動態創建一個任務

#define TASK1_TASK_PRIO 1 //任務優先級

#define TASK1_STK_SIZE 128 //任務棧大

TaskHandle_t Task1Task_Handler; //任務句柄

//動態創建一個任1

xTaskCreate((TaskFunction_t )task1_task, //任務函

(const char* )"task1_task", //任務名

(uint16_t )TASK1_STK_SIZE, //任務堆棧大小

(void* )NULL, //傳遞給任務函數的參

(UBaseType_t )TASK1_TASK_PRIO, //任務優先級

(TaskHandle_t* )&Task1Task_Handler); //任務句

//task1任務函數

void task1_task(void *pvParameters)

{

for(;;)

{

vTaskDelay( 2000 );

}

}

參數:

·pxTaskCode:任務函數。

·pcName:任務名字,一般用於追蹤和調試,任務名字長度不能超過。configMAX_TASK_NAME_LEN,在FreeRTOSConfig.h文件中宏定義16

·usStackDepth:任務堆棧大小,實際申請到的堆棧是usStackDepth4倍。其中空閒任務的任務堆棧大小為configMINIMAL_STACK_SIZE,在FreeRTOSConfig.h文件中宏定義130()

·pvParameters:傳遞給任務函數的參數。

·uxPriority:任務優先級,範圍0configMAX_PRIORITIES-1,在FreeRTOSConfig.h文件中configMAX_PRIORITIES宏定義32

·pxCreatedTask:任務句柄,任務創建成功以後會返回此任務的任務句柄,這個句柄其實就是任務的任務堆棧。此參數就用來保存這個任務句柄。其API函數可能會使用到這個句柄。

返回值:

·pdPASS:任務創建成功pdPASS宏定義1

·errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:任務創建失敗,因為堆內存不足!

在創建一個任務時一般都會在程序開頭都有這三個宏定義

要指定任務的優先級、任務的棧大小,以及任務的句柄。

優先級很好理解,它決定了多個任務之間執行任務的先後順序,任務的棧大小也很理解,在創建任務時,任務的局部變量以及任務切換時的數據都保存在棧裡面。那麼任務句柄是怎麼一回事,它保存的是任務控制塊的首地址。那麼它又是如何來保存任務的首地址呢?這就是我們今天要討論的話題

創建任務是時傳入的是一個指針?

是一個指針嗎?

不是,是一個指針的指針。

為什麼要傳入指針的指針?

什麼是指針的指針?

這些問題都需要搞明白你才能解決這個問題?

【學習交流群】不知道怎麼學?遇到問題沒人問?到處找資料?邀請你加入我的嵌入式物聯網單片機學習交流群。群內氣氛活躍,大咖小白、在職、學生都有,還有群友整理收集100G教程資料,點擊下方進群占位。

二、二級指針

正好前兩天在公眾號看到了這樣一篇文章,裡面有一C語言的題可以引用來解釋我們今天的問題,我們一起來看一下

上面這個代碼有好幾處錯誤,它的目的很簡單,就是想把字符串hello world拷貝給str,但是它能拷貝成功嗎?

很顯然是不可以的。

為了使大家看的更清楚,代碼簡單修改一下

#include

#include

#include

void getmemory(char *p)

{

p = (char *) malloc(100);

strcpy(p,"www.zhiguoxin.cn");

printf("*p:%s &(*p):0x%x\r\n",p,&p);

}

int main()

{

char *str="www.baidu.cn";

getmemory(str);

printf("str:%s &str:0x%x\r\n",str,&str);

free(str);

return 0;

}

按照我們一般人的的想法,結果應該是:

p :www.zhiguoxin.cn &p :xxxxxxx

str:www.zhiguoxin.cn &str:xxxxxxx

但是實際上結果是多少?

完全沒有變化,為了徹底解決這個問題,畫了一個圖,希望大家能夠看的更加清楚一點。

從這裡可以看出來,在分配內存後,strp就分道揚鑣了,str也還是指向www.baidu.cn

如何修改呢?正確的是啥樣的?

#include

#include

#include

void getmemory(char **p)

{

*p = (char *) malloc(100);

strcpy(*p,"www.zhiguoxin.cn");

printf("*p:%s &(*p):0x%x\r\n",*p,&(*p));

}

int main()

{

char *str="www.baidu.cn";

getmemory(&str);

printf("str:%s &str:0x%x\r\n",str,&str);

free(str);

return 0;

}

編譯運行,發現沒問題。

達到了我們想要的目的,字符串也得到了正常的拷貝。

如何解釋?

函數中參數都是傳值,傳指針本質上也是傳值,只不過它的值是指針類型罷了。如果想要改變入參內容,則需要傳該入參的地址,通過解引用修改其指向的內容

這裡的str的值就是*p的值,是多少?它們都是一個指針,就是保存的是一個地址,地址是多少?地址就是使用動態分配內malloc函數分配100位元組的首地址。然後又使用strcpy()函數將hello world拷貝到*p裡面。

這裡面就涉及到了二級指針,首先str毫無疑問是一個指針變量對吧?那麼&str是啥?理所當然就是一個指針的指針吧,就是地址的地址。

所以,我如果在某個地方申請了一塊內存,如果想得到這塊內存的首地址,而此時我們又定義了一個指針變量,想讓這個指針來保存我們申請內存你的首地址,就必須要傳入這個指針的地址,即指針的指針(二級指針)而不是傳入這個指針。

至於原因上面的例子已經非常清楚的講解了原因。

下面接著回到我們最開始的創建函數的任務句柄。在開始之前我們再把上面的函數封裝一下。

#include

#include

#include

typedef char* TaskHandle_t;

void getmemory(TaskHandle_t *p)

{

*p = (char *) malloc(100);

strcpy(*p,"www.zhiguoxin.cn");

printf("*p:%s &(*p):0x%x\r\n",*p,&(*p));

}

int main()

{

TaskHandle_t str;

getmemory(&str);

printf("str:%s &str:0x%x\r\n",str,&str);

free(str);

return 0;

}

沒啥大不了的,就是就是給char*起了一個別名而已,讓下面的代碼看起來更加順暢一寫。

這樣對比一下是不是很清楚了呢?這樣一來我們創建任務時候這個任務句柄就保存的是我TCB控制塊這個結構體的首地址了,知道了一個任務TCB控制塊首地址的話,那麼這個任務的所有信息我是不是都知道了。是的,就是這麼奇妙。通過指針的指針,二級指針來轉換一下。

說了這麼多,大家記得留意下方評論第一條(或者私信我)有干~