TencentOS tiny RTOS快速入門
上節(jié),我們介紹了TencentOS tiny,參考官方給出的移植教程親自動(dòng)手做了一遍,文章如下:
天??!鵝廠都開始做開發(fā)板了?網(wǎng)紅騰訊物聯(lián)網(wǎng)開發(fā)板終極開箱評(píng)測(cè),讓我們一睹為快!
趁著最近有時(shí)間,這節(jié),我擼了幾個(gè)例程作為后面做項(xiàng)目參考的基本框架,當(dāng)然也有一些是直接拿了官方文檔的例程:
一般來說,學(xué)習(xí)任何一個(gè)RTOS,本質(zhì)是沒有什么太大的區(qū)別的,通常在最簡(jiǎn)版nano上進(jìn)行開發(fā),關(guān)于TencentOS tiny,我個(gè)人認(rèn)為,掌握以下基礎(chǔ)組件的用法足矣,其它的一些組件,可以等需要使用的時(shí)候再參考文檔學(xué)習(xí)應(yīng)用即可。
-
TencentOS tiny多任務(wù) -
TencentOS tiny RTOS軟件定時(shí)器 -
TencentOS tiny RTOS任務(wù)間通信(互斥鎖、信號(hào)量、事件、隊(duì)列)
在使用基本組件之前,我們需要配置tos_config.h文件:
#ifndef?_TOS_CONFIG_H_
#define??_TOS_CONFIG_H_
//#include?"stm32l0xx.h"?//?目標(biāo)芯片頭文件,用戶需要根據(jù)情況更改
#include?"stm32l4xx_hal.h"
#define?TOS_CFG_TASK_PRIO_MAX???????????10u??//?配置TencentOS?tiny默認(rèn)支持的最大優(yōu)先級(jí)數(shù)量
#define?TOS_CFG_ROUND_ROBIN_EN??????????0u??//?配置TencentOS?tiny的內(nèi)核是否開啟時(shí)間片輪轉(zhuǎn)
#define?TOS_CFG_OBJECT_VERIFY_EN???????????1u?//?配置TencentOS?tiny是否校驗(yàn)指針合法
#define?TOS_CFG_TASK_DYNAMIC_CREATE_EN??1u??//?TencentOS?tiny?動(dòng)態(tài)任務(wù)創(chuàng)建功能宏
#define?TOS_CFG_EVENT_EN????????????????1u??//?TencentOS?tiny?事件模塊功能宏
#define?TOS_CFG_MMBLK_EN????????????????1u??//配置TencentOS?tiny是否開啟內(nèi)存塊管理模塊
#define?TOS_CFG_MMHEAP_EN???????????????1u??//配置TencentOS?tiny是否開啟動(dòng)態(tài)內(nèi)存模塊
#define?TOS_CFG_MMHEAP_DEFAULT_POOL_EN??1u??//?TencentOS?tiny?默認(rèn)動(dòng)態(tài)內(nèi)存池功能宏
#define?TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE????????0x100?//?配置TencentOS?tiny默認(rèn)動(dòng)態(tài)內(nèi)存池大小
#define?TOS_CFG_MUTEX_EN????????????????1u??//?配置TencentOS?tiny是否開啟互斥鎖模塊
#define?TOS_CFG_MESSAGE_QUEUE_EN????????1u??//?配置TencentOS?tiny是否開啟消息隊(duì)列模塊
#define?TOS_CFG_MAIL_QUEUE_EN???????????1u??//?配置TencentOS?tiny是否開啟消息郵箱模塊
#define?TOS_CFG_PRIORITY_MESSAGE_QUEUE_EN?1u?//?配置TencentOS?tiny是否開啟優(yōu)先級(jí)消息隊(duì)列模塊
#define?TOS_CFG_PRIORITY_MAIL_QUEUE_EN?1u??//?配置TencentOS?tiny是否開啟優(yōu)先級(jí)消息郵箱模塊
#define?TOS_CFG_TIMER_EN????????????????1u??//?配置TencentOS?tiny是否開啟軟件定時(shí)器模塊
#define?TOS_CFG_PWR_MGR_EN??????????????0u??//?配置TencentOS?tiny是否開啟外設(shè)電源管理模塊
#define?TOS_CFG_TICKLESS_EN?????????????0u??//?配置Tickless?低功耗模塊開關(guān)
#define?TOS_CFG_SEM_EN??????????????????1u??//?配置TencentOS?tiny是否開啟信號(hào)量模塊
#define?TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN??????1u?//?配置TencentOS?tiny是否開啟任務(wù)棧深度檢測(cè)
#define?TOS_CFG_FAULT_BACKTRACE_EN??????0u??//?配置TencentOS?tiny是否開啟異常?;厮莨δ?/span>
#define?TOS_CFG_IDLE_TASK_STK_SIZE??????128u?//?配置TencentOS?tiny空閑任務(wù)棧大小
#define?TOS_CFG_CPU_TICK_PER_SECOND?????1000u?//?配置TencentOS?tiny的tick頻率
#define?TOS_CFG_CPU_CLOCK???????????????(SystemCoreClock)?//?配置TencentOS?tiny?CPU頻率
#define?TOS_CFG_TIMER_AS_PROC???????????1u??//?配置是否將TIMER配置成函數(shù)模式
#endif
這樣后面我們才能正常使用。
1、TencentOS tiny多任務(wù)
1.1 為什么要采用RTOS多任務(wù)?
對(duì)于普通的項(xiàng)目來說,比如密碼鎖類項(xiàng)目,單獨(dú)的一個(gè)傳感器模塊的開發(fā),某些簡(jiǎn)單的儀器儀表等等,對(duì)于這類場(chǎng)景單一,業(yè)務(wù)需求也單一的項(xiàng)目來說,使用狀態(tài)機(jī)或者事件驅(qū)動(dòng)的方式就足以完成項(xiàng)目的基本功能了。
但是如果開發(fā)一個(gè)巨量代碼的工程項(xiàng)目,項(xiàng)目可能設(shè)計(jì)到傳感器數(shù)據(jù)讀取、無線數(shù)據(jù)上傳與接收、數(shù)據(jù)傳輸、UI實(shí)時(shí)刷新、算法處理等等,功能諸多還需要相互配合的情況下,那么如果還在用裸機(jī)的思想去完成,那么開發(fā)者一般會(huì)面臨以下兩個(gè)問題:
-
設(shè)計(jì)思路過于復(fù)雜,光怎么想程序的設(shè)計(jì)思路就得想好久了 -
設(shè)計(jì)下來的各個(gè)功能,要考慮相互配合的問題,實(shí)時(shí)性可能得不到要求
RTOS的多任務(wù)就可以解決對(duì)應(yīng)的問題,它既能讓項(xiàng)目開發(fā)起來思路清晰,方便易維護(hù);同時(shí)RTOS也能保證整個(gè)產(chǎn)品運(yùn)行的實(shí)時(shí)性,典型的程序設(shè)計(jì)架構(gòu),就可以按下面的方式來劃分:
1.2 TencentOS tiny RTOS多任務(wù)實(shí)踐
關(guān)于怎么創(chuàng)建多個(gè)任務(wù),可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程是我基于上一節(jié)的移植工程,在移植工程的基礎(chǔ)上,由于官方給的OLED驅(qū)動(dòng)例程是軟件模擬驅(qū)動(dòng)的,后來我將其改為I2C硬件驅(qū)動(dòng),所以,在STM32CubeMX上對(duì)OLED的I2C接口進(jìn)行了配置:
更改后重新生成軟件工程,然后修改oled.c中關(guān)于寫命令和寫數(shù)據(jù)的接口為硬件I2C驅(qū)動(dòng):
//?IIC?Write?Command
void?Write_IIC_Command(unsigned?char?IIC_Command)
{
????uint8_t?buf[2]?=?{0};
????buf[0]?=?0x00?;
????buf[1]?=?IIC_Command?;
????HAL_I2C_Master_Transmit(&hi2c3,?0x78,?buf,?2,?HAL_TICK_FREQ_100HZ);
}
/**********************************************
//?IIC?Write?Data
**********************************************/
void?Write_IIC_Data(unsigned?char?IIC_Data)
{
????uint8_t?buf[2]?=?{0};
????buf[0]?=?0x40?;
????buf[1]?=?IIC_Data?;
????HAL_I2C_Master_Transmit(&hi2c3,?0x78,?buf,?2,?HAL_TICK_FREQ_100HZ);
}
接下來,進(jìn)入多任務(wù)程序編寫,我們主要實(shí)現(xiàn)以下兩個(gè)功能:
-
task1以1s的頻率循環(huán)打印 Hello TencentOS tiny -
task2以100ms的頻率循環(huán)翻轉(zhuǎn)。
main.c
定義兩個(gè)基本任務(wù):
//task1
#define?TASK1_STK_SIZE??256
void?task1(void?*pdata);
osThreadDef(task1,?osPriorityNormal,?1,?TASK1_STK_SIZE);
void?task1(void?*pdata)
{
????while(1)
????{
???????printf("Hello?TencentOS?tiny\n");
???????osDelay(1000);
????}
}
//task2
#define?TASK2_STK_SIZE??256
void?task2(void?*pdata);
osThreadDef(task2,?osPriorityNormal,?1,?TASK2_STK_SIZE);
void?task2(void?*pdata)
{
????while(1)
????{
???????HAL_GPIO_TogglePin(DEBUG_LED_GPIO_Port,?DEBUG_LED_Pin);
???????osDelay(100);
????}
}
在main函數(shù)中:
/**
??*?@brief??The?application?entry?point.
??*?@retval?int
??*/
int?main(void)
{
????/*?USER?CODE?BEGIN?1?*/
????/*?USER?CODE?END?1?*/
????/*?MCU?Configuration--------------------------------------------------------*/
????/*?Reset?of?all?peripherals,?Initializes?the?Flash?interface?and?the?Systick.?*/
????HAL_Init();
????/*?USER?CODE?BEGIN?Init?*/
????/*?USER?CODE?END?Init?*/
????/*?Configure?the?system?clock?*/
????SystemClock_Config();
????/*?USER?CODE?BEGIN?SysInit?*/
????/*?USER?CODE?END?SysInit?*/
????/*?Initialize?all?configured?peripherals?*/
????MX_GPIO_Init();
????MX_USART2_UART_Init();
????MX_I2C3_Init();
????/*?USER?CODE?BEGIN?2?*/
????OLED_Init();
????OLED_Clear();
????OLED_ShowString(0,?0,?(uint8_t*)"TencentOS?tiny",?16);
????OLED_ShowString(0,?2,?(uint8_t*)"Bruce.yang",?16);
????//初始化內(nèi)核
????osKernelInitialize();
????//創(chuàng)建并啟動(dòng)一個(gè)任務(wù)用于打印調(diào)試信息
????osThreadCreate(osThread(task1),?NULL);
????//創(chuàng)建并啟動(dòng)一個(gè)任務(wù)用于以100ms的間隔翻轉(zhuǎn)LED
????osThreadCreate(osThread(task2),?NULL);
????//啟動(dòng)內(nèi)核
????osKernelStart();
????/*?USER?CODE?END?2?*/
????/*?Infinite?loop?*/
????/*?USER?CODE?BEGIN?WHILE?*/
????while?(1)
????{
????????/*?USER?CODE?END?WHILE?*/
????????/*?USER?CODE?BEGIN?3?*/
????}
????/*?USER?CODE?END?3?*/
}
編譯后下載到EVB_MX+開發(fā)板后,運(yùn)行結(jié)果如下:
task1以1s的頻率循環(huán)打印Hello TencentOS tiny,task2以100ms的頻率循環(huán)翻轉(zhuǎn)。
1.3 總結(jié)
概念性總結(jié):
-
多任務(wù)適合業(yè)務(wù)場(chǎng)景更加復(fù)雜的應(yīng)用場(chǎng)景 -
多任務(wù)適合對(duì)實(shí)時(shí)響應(yīng)要求更高的場(chǎng)景
使用總結(jié):
詳情請(qǐng)參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔
2、TencentOS tiny RTOS軟件定時(shí)器
2.1、為什么要采用RTOS軟件定時(shí)器?
軟件定時(shí)器,顧名思義就是軟件實(shí)現(xiàn)的定時(shí)器,它是和硬件定時(shí)器有本質(zhì)區(qū)別的,軟件定時(shí)器使用的是系統(tǒng)調(diào)度所依賴的嘀嗒定時(shí)器,也就是Systick來實(shí)現(xiàn)的,它主要解決一些不需要特別精準(zhǔn)的定時(shí)觸發(fā)場(chǎng)合,目前github倉庫上有開源不少軟件定時(shí)器的實(shí)例,比如multi_timer,TecentOS tiny也在自己的內(nèi)核中集成了自己的一套軟件定時(shí)器,實(shí)現(xiàn)原理其實(shí)也是差不多的。
2.2、TencentOS tiny RTOS軟件定時(shí)器實(shí)踐
關(guān)于怎么使用定時(shí)器,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程基于多任務(wù)例程修改,接下來,進(jìn)入軟件定時(shí)器程序編寫,我們主要實(shí)現(xiàn)以下兩個(gè)功能:
-
task1以1s的頻率循環(huán)打印 Hello TencentOS tiny -
軟件定時(shí)器以500ms的頻率翻轉(zhuǎn)LED
main.c
/*定義一個(gè)定時(shí)器句柄*/
k_timer_t?os_tmr_handler;
//創(chuàng)建一個(gè)任務(wù)
#define?TASK1_STK_SIZE??256
void?task1(void?*pdata);
osThreadDef(task1,?osPriorityNormal,?1,?TASK1_STK_SIZE);
void?task1(void?*pdata)
{
????while(1)
????{
???????printf("Hello?TencentOS?tiny\n");
???????osDelay(1000);
????}
}
//定時(shí)器回調(diào)函數(shù)
void?os_tmr_handler_callback(void?*arg)
{
???HAL_GPIO_TogglePin(DEBUG_LED_GPIO_Port,?DEBUG_LED_Pin);
}
在main函數(shù)中:
/**
??*?@brief??The?application?entry?point.
??*?@retval?int
??*/
int?main(void)
{
????/*?USER?CODE?BEGIN?1?*/
????/*?USER?CODE?END?1?*/
????/*?MCU?Configuration--------------------------------------------------------*/
????/*?Reset?of?all?peripherals,?Initializes?the?Flash?interface?and?the?Systick.?*/
????HAL_Init();
????/*?USER?CODE?BEGIN?Init?*/
????/*?USER?CODE?END?Init?*/
????/*?Configure?the?system?clock?*/
????SystemClock_Config();
????/*?USER?CODE?BEGIN?SysInit?*/
????/*?USER?CODE?END?SysInit?*/
????/*?Initialize?all?configured?peripherals?*/
????MX_GPIO_Init();
????MX_USART2_UART_Init();
????MX_I2C3_Init();
????/*?USER?CODE?BEGIN?2?*/
????OLED_Init();
????OLED_Clear();
????OLED_ShowString(0,?0,?(uint8_t*)"TencentOS?tiny",?16);
????OLED_ShowString(0,?2,?(uint8_t*)"Bruce.yang",?16);
????//初始化內(nèi)核
????osKernelInitialize();
????//創(chuàng)建一個(gè)以500ms周期運(yùn)行的軟件定時(shí)器
????tos_timer_create(&os_tmr_handler,?500,?500,?os_tmr_handler_callback,?K_NULL,?TOS_OPT_TIMER_PERIODIC);
????//創(chuàng)建一個(gè)任務(wù)
????osThreadCreate(osThread(task1),?NULL);
????//啟動(dòng)定時(shí)器
????tos_timer_start(&os_tmr_handler);
????//啟動(dòng)內(nèi)核
????osKernelStart();
????/*?USER?CODE?END?2?*/
????/*?Infinite?loop?*/
????/*?USER?CODE?BEGIN?WHILE?*/
????while?(1)
????{
????????/*?USER?CODE?END?WHILE?*/
????????/*?USER?CODE?BEGIN?3?*/
????}
????/*?USER?CODE?END?3?*/
}
編譯后下載到EVB_MX+開發(fā)板后,運(yùn)行結(jié)果如下:
task1以1s的頻率循環(huán)打印Hello TencentOS tiny,軟件定時(shí)器以500ms的頻率執(zhí)行,此時(shí)LED會(huì)以500ms的速率循環(huán)翻轉(zhuǎn)。
2.3 總結(jié)
概念性總結(jié):
-
軟件定時(shí)器就是用"軟件邏輯"實(shí)現(xiàn)的定時(shí)器 -
軟件定時(shí)器適合一些不需要特別精準(zhǔn)的定時(shí)觸發(fā)場(chǎng)合.
使用總結(jié):
詳情請(qǐng)參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔
3、TencentOS tiny RTOS任務(wù)間通信
3.1、TencentOS tiny RTOS互斥鎖
3.1.1 、為什么要采用RTOS互斥鎖?
互斥鎖適用于實(shí)現(xiàn)臨界區(qū)資源的互斥性訪問,當(dāng)有多個(gè)任務(wù)同時(shí)并行對(duì)一個(gè)數(shù)據(jù)操作時(shí),就會(huì)存在不確定性,典型的案例就是全局變量,在不帶操作系統(tǒng)的裸機(jī)功能開發(fā)中,我們通常會(huì)使用全局變量,讓其在整個(gè)工程中通過外部引用的方式全局可見,這樣我們就可以很方便的在任何一個(gè)地方對(duì)其進(jìn)行讀寫操作,但如果在操作系統(tǒng)中卻恰恰相反,這種奇怪的現(xiàn)象被稱為不可重入,通常在操作系統(tǒng)里叫臨界區(qū)資源,在字符串操作中,典型的不可重入函數(shù)是strtok,strtok函數(shù)內(nèi)部有一個(gè)static變量,這種類型的變量可以被多次重入調(diào)用共同控制,其最終的結(jié)果依賴于它們的執(zhí)行順序,所以,使用互斥鎖可以解決這種不確定性的問題,也就是說在任意時(shí)刻,只會(huì)有一個(gè)任務(wù)對(duì)其進(jìn)行訪問。
3.1.2、TencentOS tiny RTOS互斥鎖實(shí)踐
關(guān)于怎么使用互斥鎖,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程基于多任務(wù)例程修改,接下來,進(jìn)入互斥鎖程序編寫,我們主要實(shí)現(xiàn)三個(gè)任務(wù)同時(shí)執(zhí)行一段代碼:
main.c
k_mutex_t?mutex;
int?number?=?0?;
//task1
#define?TASK1_STK_SIZE??256
void?task1(void?*pdata);
osThreadDef(task1,?osPriorityNormal,?1,?TASK1_STK_SIZE);
void?count_sample_code(void)
{
????tos_mutex_pend(&mutex);
????number=0;
????number+=1;
????number+=2;
????number+=3;
????tos_mutex_post(&mutex);
}
void?task1(void?*pdata)
{
????while(1)
????{
????????number?=?0?;
????????osDelay(300);
????????count_sample_code();
????????printf("task1?number=%d\n",number);
????????osDelay(100);
????}
}
//task2
#define?TASK2_STK_SIZE??256
void?task2(void?*pdata);
osThreadDef(task2,?osPriorityNormal,?1,?TASK2_STK_SIZE);
void?task2(void?*pdata)
{
????while(1)
????{
????????osDelay(300);
????????count_sample_code();
????????printf("task2?number=%d\n",number);
????????osDelay(200);
????}
}
//task3
#define?TASK3_STK_SIZE??256
void?task3(void?*pdata);
osThreadDef(task3,?osPriorityNormal,?1,?TASK3_STK_SIZE);
void?task3(void?*pdata)
{
????while(1)
????{
????????osDelay(300);
????????count_sample_code();
????????printf("task3?number=%d\n",number);
????????osDelay(300);
????}
}
在main函數(shù)中:
/**
??*?@brief??The?application?entry?point.
??*?@retval?int
??*/
int?main(void)
{
????/*?USER?CODE?BEGIN?1?*/
????/*?USER?CODE?END?1?*/
????/*?MCU?Configuration--------------------------------------------------------*/
????/*?Reset?of?all?peripherals,?Initializes?the?Flash?interface?and?the?Systick.?*/
????HAL_Init();
????/*?USER?CODE?BEGIN?Init?*/
????/*?USER?CODE?END?Init?*/
????/*?Configure?the?system?clock?*/
????SystemClock_Config();
????/*?USER?CODE?BEGIN?SysInit?*/
????/*?USER?CODE?END?SysInit?*/
????/*?Initialize?all?configured?peripherals?*/
????MX_GPIO_Init();
????MX_USART2_UART_Init();
????MX_I2C3_Init();
????/*?USER?CODE?BEGIN?2?*/
????OLED_Init();
????OLED_Clear();
????OLED_ShowString(0,?2,?(uint8_t*)"TencentOS?tiny",?16);
????tos_mutex_create(&mutex);
????//初始化內(nèi)核
????osKernelInitialize();
????//分別創(chuàng)建任務(wù),用于驗(yàn)證互斥鎖
????osThreadCreate(osThread(task1),?NULL);
????osThreadCreate(osThread(task2),?NULL);
????osThreadCreate(osThread(task3),?NULL);
????//啟動(dòng)內(nèi)核
????osKernelStart();
????/*?USER?CODE?END?2?*/
????/*?Infinite?loop?*/
????/*?USER?CODE?BEGIN?WHILE?*/
????while?(1)
????{
????????/*?USER?CODE?END?WHILE?*/
????????/*?USER?CODE?BEGIN?3?*/
????}
????/*?USER?CODE?END?3?*/
}
編譯后下載到EVB_MX+開發(fā)板后,運(yùn)行結(jié)果如下:
3.1.3、總結(jié)
概念性總結(jié):
-
互斥鎖在任意時(shí)刻,只會(huì)有一個(gè)任務(wù)對(duì)臨界資源進(jìn)行訪問 -
互斥鎖用于實(shí)現(xiàn)臨界區(qū)資源的互斥性訪問
使用總結(jié):
詳情請(qǐng)參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔
3.2、TencentOS tiny RTOS信號(hào)量
3.2.1、為什么要采用RTOS信號(hào)量?
信號(hào)量,俗話說就是信號(hào)的數(shù)量,它是一種任務(wù)間傳遞系統(tǒng)可用資源的機(jī)制;舉一個(gè)生產(chǎn)者與消費(fèi)者的問題;也就是說消費(fèi)者在消費(fèi)了一個(gè)資源之前需要等待資源釋放,生產(chǎn)者生產(chǎn)資源以后要即時(shí)去通知其它的消費(fèi)者,簡(jiǎn)單的說就是凡事都要有個(gè)先來后到,所以信號(hào)量最常用的地方就是實(shí)現(xiàn)任務(wù)間同步。
3.2.2、TencentOS tiny RTOS信號(hào)量實(shí)踐
關(guān)于怎么使用信號(hào)量,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程基于多任務(wù)例程修改,接下來,進(jìn)入信號(hào)量程序編寫,我們主要實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者的問題,這段程序在參考文檔里可以找到:
main.c
#define?STK_SIZE_TASK_PRODUCER?512
#define?STK_SIZE_TASK_CONSUMER?512
k_stack_t?stack_task_producer[STK_SIZE_TASK_PRODUCER];
k_stack_t?stack_task_consumer[STK_SIZE_TASK_CONSUMER];
k_task_t?task_producer;
k_task_t?task_consumer;
extern?void?entry_task_producer(void?*arg);
extern?void?entry_task_consumer(void?*arg);
k_mutex_t?buffer_locker;
k_sem_t?full;
k_sem_t?empty;
#define?RESOURCE_COUNT_MAX?3
struct?resource_st
{
????int?cursor;
????uint32_t?buffer[RESOURCE_COUNT_MAX];
}?resource?=?{?0,?{0}?};
static?void?produce_item(int?salt)
{
????printf("produce?item:\n");
????printf("%d",?salt);
????resource.buffer[resource.cursor++]?=?salt;
????printf("\n");
}
void?entry_task_producer(void?*arg)
{
????size_t?salt?=?0;
????k_err_t?err;
????while?(K_TRUE)
????{
????????err?=?tos_sem_pend(&empty,?TOS_TIME_FOREVER);
????????if?(err?!=?K_ERR_NONE)
????????{
????????????continue;
????????}
????????err?=?tos_mutex_pend(&buffer_locker);
????????if?(err?!=?K_ERR_NONE)
????????{
????????????continue;
????????}
????????produce_item(salt);
????????tos_mutex_post(&buffer_locker);
????????tos_sem_post(&full);
????????tos_task_delay(1000);
????????++salt;
????}
}
static?void?consume_item(void)
{
????printf("cosume?item:\n");
????printf("%d\t",?resource.buffer[--resource.cursor]);
????printf("\n");
}
void?entry_task_consumer(void?*arg)
{
????k_err_t?err;
????while?(K_TRUE)
????{
????????err?=?tos_sem_pend(&full,?TOS_TIME_FOREVER);
????????if?(err?!=?K_ERR_NONE)
????????{
????????????continue;
????????}
????????tos_mutex_pend(&buffer_locker);
????????if?(err?!=?K_ERR_NONE)
????????{
????????????continue;
????????}
????????consume_item();
????????tos_mutex_post(&buffer_locker);
????????tos_sem_post(&empty);
????????tos_task_delay(2000);
????}
}
main函數(shù)實(shí)現(xiàn)如下:
int?main(void)
{
????/*?USER?CODE?BEGIN?1?*/
????/*?USER?CODE?END?1?*/
????/*?MCU?Configuration--------------------------------------------------------*/
????/*?Reset?of?all?peripherals,?Initializes?the?Flash?interface?and?the?Systick.?*/
????HAL_Init();
????/*?USER?CODE?BEGIN?Init?*/
????/*?USER?CODE?END?Init?*/
????/*?Configure?the?system?clock?*/
????SystemClock_Config();
????/*?USER?CODE?BEGIN?SysInit?*/
????/*?USER?CODE?END?SysInit?*/
????/*?Initialize?all?configured?peripherals?*/
????MX_GPIO_Init();
????MX_USART2_UART_Init();
????MX_I2C3_Init();
????/*?USER?CODE?BEGIN?2?*/
????OLED_Init();
????OLED_Clear();
????OLED_ShowString(0,?0,?(uint8_t*)"TencentOS?tiny",?16);
????OLED_ShowString(0,?2,?(uint8_t*)"Bruce.yang",?16);
????//初始化內(nèi)核
????osKernelInitialize();
????tos_mutex_create(&buffer_locker);
????tos_sem_create(&full,?0);
????tos_sem_create(&empty,?RESOURCE_COUNT_MAX);
????(void)tos_task_create(&task_producer,?"producer",?entry_task_producer,?NULL,
??????????????????????????4,?stack_task_producer,?STK_SIZE_TASK_PRODUCER,?0);
????(void)tos_task_create(&task_consumer,?"consumer",?entry_task_consumer,?NULL,
??????????????????????????4,?stack_task_consumer,?STK_SIZE_TASK_CONSUMER,?0);
????//啟動(dòng)內(nèi)核
????osKernelStart();
????/*?USER?CODE?END?2?*/
????/*?Infinite?loop?*/
????/*?USER?CODE?BEGIN?WHILE?*/
????while?(1)
????{
????????/*?USER?CODE?END?WHILE?*/
????????/*?USER?CODE?BEGIN?3?*/
????}
????/*?USER?CODE?END?3?*/
}
編譯后下載到EVB_MX+開發(fā)板后,運(yùn)行結(jié)果如下:
3.2.3、總結(jié)
概念性總結(jié):
-
信號(hào)量可以用于實(shí)現(xiàn)任務(wù)間同步 -
信號(hào)量最典型的應(yīng)用就是處理生產(chǎn)者與消費(fèi)者的問題
使用總結(jié):
詳情請(qǐng)參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔
3.3、TencentOS tiny RTOS事件
3.3.1、為什么要采用RTOS事件?
事件,是RTOS任務(wù)間用來傳遞的一種信號(hào)的信息,它可以傳遞多個(gè)信息,事件和信號(hào)量的區(qū)別就是信號(hào)量只能傳遞0和1兩個(gè)信息,而事件的類型通常用k_event_flag_t進(jìn)行描述,它的本質(zhì)是一個(gè)uint32_t數(shù)據(jù)類型,也就是說事件最多可以定義32個(gè),使用事件可以很方便的實(shí)現(xiàn)任務(wù)間同步和信息傳遞,但是要注意的是事件達(dá)到的是一種類似通知的效果,本身是不帶負(fù)載的。
3.3.2、TencentOS tiny RTOS事件實(shí)踐
關(guān)于怎么使用事件,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程基于多任務(wù)例程修改,我們?cè)诙嗳蝿?wù)例程的基礎(chǔ)上,移植了multi_button組件,這個(gè)組件的移植方法在之前寫小熊派相關(guān)的文章中都有詳細(xì)的方法,這里就不再多說了,參考文章如下:
第1期 | MultiButton,一個(gè)小巧簡(jiǎn)單易用的事件驅(qū)動(dòng)型按鍵驅(qū)動(dòng)模塊
開源按鍵組件MultiButton支持菜單操作(事件驅(qū)動(dòng)型)
這里的button_ticks()我把它放在SysTick_Handler函數(shù)中進(jìn)行處理,實(shí)現(xiàn)如下:
/**
??*?@brief?This?function?handles?System?tick?timer.
??*/
void?SysTick_Handler(void)
{
????/*?USER?CODE?BEGIN?SysTick_IRQn?0?*/
????static?uint8_t?timer_ticks?=?0?;
????++timer_ticks?;
?????//5ms循環(huán)調(diào)用一次button_ticks();
????if(5?==?timer_ticks)
????{
????????timer_ticks?=?0?;
????????button_ticks();
????}
????/*?USER?CODE?END?SysTick_IRQn?0?*/
????HAL_IncTick();
????/*?USER?CODE?BEGIN?SysTick_IRQn?1?*/
????if(tos_knl_is_running())
????{
????????tos_knl_irq_enter();
????????tos_tick_handler();
????????tos_knl_irq_leave();
????}
????/*?USER?CODE?END?SysTick_IRQn?1?*/
}
接下來進(jìn)入事件程序的編寫,我們主要實(shí)現(xiàn)以下兩個(gè)功能:
-
定義任務(wù)task1,用于初始化multi_button,通過按鍵回調(diào)發(fā)送事件 -
定義任務(wù)task2,用于接收task1發(fā)送過來的事件,并進(jìn)行處理
main.c
定義multi_button結(jié)構(gòu)體變量以及事件相關(guān)的變量
#include?"multi_button.h"
/*創(chuàng)建一個(gè)按鍵事件*/
k_event_t?key_event;
//創(chuàng)建幾個(gè)按鍵的事件標(biāo)志
const?k_event_flag_t?event_key1?=?(k_event_flag_t)(1?<0);
const?k_event_flag_t?event_key2?=?(k_event_flag_t)(1?<1);
const?k_event_flag_t?event_key3?=?(k_event_flag_t)(1?<2);
const?k_event_flag_t?event_key4?=?(k_event_flag_t)(1?<3);
const?k_event_flag_t?event_key5?=?(k_event_flag_t)(1?<4);
//定義描述按鍵的結(jié)構(gòu)體變量
struct?Button?button1,?button2,?button3,?button4;
定義按鍵電平讀取以及按鍵處理的函數(shù):
uint8_t?key1_read_pin(void);
uint8_t?key2_read_pin(void);
uint8_t?key3_read_pin(void);
uint8_t?key4_read_pin(void);
void?key_handler(void*?btn);
uint8_t?key1_read_pin(void)
{
????return?HAL_GPIO_ReadPin(KEY1_GPIO_Port,?KEY1_Pin);
}
uint8_t?key2_read_pin(void)
{
????return?HAL_GPIO_ReadPin(KEY2_GPIO_Port,?KEY2_Pin);
}
uint8_t?key3_read_pin(void)
{
????return?HAL_GPIO_ReadPin(KEY3_GPIO_Port,?KEY3_Pin);
}
uint8_t?key4_read_pin(void)
{
????return?HAL_GPIO_ReadPin(KEY4_GPIO_Port,?KEY4_Pin);
}
//按鍵處理
void?key_handler(void*?btn)
{
????struct?Button?*button?=?btn?;
????if(btn?==?&button1)
????{
??????if(button->event?==?PRESS_DOWN)
????????tos_event_post(&key_event,?event_key1);
??????else?if(button->event?==?PRESS_UP)
????????tos_event_post(&key_event,?event_key5);
????}
????else?if(btn?==?&button2)
????{
??????if(button->event?==?PRESS_DOWN)
????????tos_event_post(&key_event,?event_key2);
??????else?if(button->event?==?PRESS_UP)
????????tos_event_post(&key_event,?event_key5);
????}
????else?if(btn?==?&button3)
????{
??????if(button->event?==?PRESS_DOWN)
????????tos_event_post(&key_event,?event_key3);
??????else?if(button->event?==?PRESS_UP)
????????tos_event_post(&key_event,?event_key5);
????}
????else?if(btn?==?&button4)
????{
??????if(button->event?==?PRESS_DOWN)
????????tos_event_post(&key_event,?event_key4);
??????else?if(button->event?==?PRESS_UP)
????????tos_event_post(&key_event,?event_key5);
????}
}
在main函數(shù)中:
/**
??*?@brief??The?application?entry?point.
??*?@retval?int
??*/
int?main(void)
{
????/*?USER?CODE?BEGIN?1?*/
????/*?USER?CODE?END?1?*/
????/*?MCU?Configuration--------------------------------------------------------*/
????/*?Reset?of?all?peripherals,?Initializes?the?Flash?interface?and?the?Systick.?*/
????HAL_Init();
????/*?USER?CODE?BEGIN?Init?*/
????/*?USER?CODE?END?Init?*/
????/*?Configure?the?system?clock?*/
????SystemClock_Config();
????/*?USER?CODE?BEGIN?SysInit?*/
????/*?USER?CODE?END?SysInit?*/
????/*?Initialize?all?configured?peripherals?*/
????MX_GPIO_Init();
????MX_USART2_UART_Init();
????MX_I2C3_Init();
????/*?USER?CODE?BEGIN?2?*/
????OLED_Init();
????OLED_Clear();
????OLED_ShowString(0,?0,?(uint8_t*)"TencentOS?tiny",?16);
????OLED_ShowString(0,?2,?(uint8_t*)"Bruce.yang",?16);
????//初始化內(nèi)核
????osKernelInitialize();
????//創(chuàng)建一個(gè)按鍵事件
????tos_event_create(&key_event,?(k_event_flag_t)0u);
????//創(chuàng)建并啟動(dòng)一個(gè)任務(wù)用于通過按鍵發(fā)送事件
????osThreadCreate(osThread(task1),?NULL);
????//創(chuàng)建并啟動(dòng)一個(gè)任務(wù)用于接收按鍵事件并執(zhí)行相應(yīng)的軟件邏輯
????osThreadCreate(osThread(task2),?NULL);
????//啟動(dòng)TencentOS?tiny內(nèi)核
????osKernelStart();
????/*?USER?CODE?END?2?*/
????/*?Infinite?loop?*/
????/*?USER?CODE?BEGIN?WHILE?*/
????while?(1)
????{
????????/*?USER?CODE?END?WHILE?*/
????????/*?USER?CODE?BEGIN?3?*/
????}
????/*?USER?CODE?END?3?*/
}
編譯后下載到EVB_MX+開發(fā)板后,運(yùn)行結(jié)果如下:
當(dāng)分別按下四個(gè)按鍵后,task2接收到具體消息后執(zhí)行不同的邏輯
3.3.3 總結(jié)
概念性總結(jié):
-
事件區(qū)別于信號(hào)量,信號(hào)量是0-1傳遞,而事件可以傳遞多個(gè)信息 -
事件是用于RTOS任務(wù)間傳遞的一種沒有信息負(fù)載的信號(hào)類信息
使用總結(jié):
詳情請(qǐng)參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔
3.4、TencentOS tiny RTOS隊(duì)列
3.4.1、為什么要采用RTOS隊(duì)列?
隊(duì)列也是任務(wù)間傳遞信息的一種方式,它和事件最本質(zhì)的區(qū)別就是,事件傳遞沒有負(fù)載,而隊(duì)列的傳遞是包含數(shù)據(jù)負(fù)載的,在事件章節(jié)中,當(dāng)我們按下按鍵的時(shí)候其中一個(gè)任務(wù)發(fā)出事件,另一個(gè)任務(wù)則接收事件,而接收的這個(gè)事件是非常單一的,除此之外并沒有更多具體的新信息載體,而隊(duì)列就是為了解決這個(gè)問題而誕生的,比如串口接收數(shù)據(jù),當(dāng)數(shù)據(jù)接收滿了,此時(shí)我們就可以使用隊(duì)列將接收滿的數(shù)據(jù)通過隊(duì)列的信息發(fā)出去,然后任務(wù)里進(jìn)行接收處理。
3.4.2、TencentOS tiny RTOS隊(duì)列實(shí)踐
關(guān)于怎么使用隊(duì)列,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,但該文檔的API過老,可能不適合現(xiàn)在的版本,于是找來了一個(gè)新版的API,參考網(wǎng)友修改的,以下工程基于多任務(wù)例程修改:
main.c
#define?STK_SIZE_TASK_RECEIVER??????512
#define?STK_SIZE_TASK_SENDER????????512
#define?PRIO_TASK_RECEIVER_HIGHER_PRIO??????4
#define?PRIO_TASK_RECEIVER_LOWER_PRIO???????(PRIO_TASK_RECEIVER_HIGHER_PRIO?+?1)
#define?MESSAGE_MAX?????10
k_stack_t?stack_task_receiver_higher_prio[STK_SIZE_TASK_RECEIVER];
k_stack_t?stack_task_receiver_lower_prio[STK_SIZE_TASK_RECEIVER];
k_stack_t?stack_task_sender[STK_SIZE_TASK_SENDER];
uint8_t?msg_pool[MESSAGE_MAX?*?sizeof(void?*)];
k_task_t?task_receiver_higher_prio;
k_task_t?task_receiver_lower_prio;
k_task_t?task_sender;
k_msg_q_t?msg_q;
void?entry_task_receiver_higher_prio(void?*arg)
{
????k_err_t?err;
????void?*msg_received;
????while?(K_TRUE)
????{
????????err?=?tos_msg_q_pend(&msg_q,?&msg_received,?TOS_TIME_FOREVER);
????????if?(err?==?K_ERR_NONE)
????????{
????????????printf("higher:?msg?incoming[%s]\n",?(char?*)msg_received);
????????}
????}
}
void?entry_task_receiver_lower_prio(void?*arg)
{
????k_err_t?err;
????void?*msg_received;
????while?(K_TRUE)
????{
????????err?=?tos_msg_q_pend(&msg_q,?&msg_received,?TOS_TIME_FOREVER);
????????if?(err?==?K_ERR_NONE)
????????{
????????????printf("lower:?msg?incoming[%s]\n",?(char?*)msg_received);
????????}
????}
}
void?entry_task_sender(void?*arg)
{
????int?i?=?1;
????char?*msg_to_one_receiver?=?"message?for?one?receiver(with?highest?priority)";
????char?*msg_to_all_receiver?=?"message?for?all?receivers";
????while?(K_TRUE)
????{
????????if?(i?==?2)
????????{
????????????printf("sender:?send?a?message?to?one?receiver,?and?shoud?be?the?highest?priority?one\n");
????????????tos_msg_q_post(&msg_q,?msg_to_one_receiver);
????????}
????????if?(i?==?3)
????????{
????????????printf("sender:?send?a?message?to?all?recevier\n");
????????????tos_msg_q_post_all(&msg_q,?msg_to_all_receiver);
????????}
????????if?(i?==?4)
????????{
????????????printf("sender:?send?a?message?to?one?receiver,?and?shoud?be?the?highest?priority?one\n");
????????????tos_msg_q_post(&msg_q,?msg_to_one_receiver);
????????}
????????if?(i?==?5)
????????{
????????????printf("sender:?send?a?message?to?all?recevier\n");
????????????tos_msg_q_post_all(&msg_q,?msg_to_all_receiver);
????????}
????????tos_task_delay(1000);
????????++i;
????}
}
main函數(shù)實(shí)現(xiàn):
/**
??*?@brief??The?application?entry?point.
??*?@retval?int
??*/
int?main(void)
{
????/*?USER?CODE?BEGIN?1?*/
????/*?USER?CODE?END?1?*/
????/*?MCU?Configuration--------------------------------------------------------*/
????/*?Reset?of?all?peripherals,?Initializes?the?Flash?interface?and?the?Systick.?*/
????HAL_Init();
????/*?USER?CODE?BEGIN?Init?*/
????/*?USER?CODE?END?Init?*/
????/*?Configure?the?system?clock?*/
????SystemClock_Config();
????/*?USER?CODE?BEGIN?SysInit?*/
????/*?USER?CODE?END?SysInit?*/
????/*?Initialize?all?configured?peripherals?*/
????MX_GPIO_Init();
????MX_USART2_UART_Init();
????MX_I2C3_Init();
????/*?USER?CODE?BEGIN?2?*/
????OLED_Init();
????OLED_Clear();
????OLED_ShowString(0,?0,?(uint8_t*)"TencentOS?tiny",?16);
????OLED_ShowString(0,?2,?(uint8_t*)"Bruce.yang",?16);
????//初始化內(nèi)核
????osKernelInitialize();
????tos_msg_q_create(&msg_q,?msg_pool,?MESSAGE_MAX);
????(void)tos_task_create(&task_receiver_higher_prio,?"receiver_higher_prio",
??????????????????????????entry_task_receiver_higher_prio,?NULL,?PRIO_TASK_RECEIVER_HIGHER_PRIO,
??????????????????????????stack_task_receiver_higher_prio,?STK_SIZE_TASK_RECEIVER,?0);
????(void)tos_task_create(&task_receiver_lower_prio,?"receiver_lower_prio",
??????????????????????????entry_task_receiver_lower_prio,?NULL,?PRIO_TASK_RECEIVER_LOWER_PRIO,
??????????????????????????stack_task_receiver_lower_prio,?STK_SIZE_TASK_RECEIVER,?0);
????(void)tos_task_create(&task_sender,?"sender",?entry_task_sender,?NULL,
??????????????????????????4,?stack_task_sender,?STK_SIZE_TASK_SENDER,?0);
????//啟動(dòng)內(nèi)核
????osKernelStart();
????/*?USER?CODE?END?2?*/
????/*?Infinite?loop?*/
????/*?USER?CODE?BEGIN?WHILE?*/
????while?(1)
????{
????????/*?USER?CODE?END?WHILE?*/
????????/*?USER?CODE?BEGIN?3?*/
????}
????/*?USER?CODE?END?3?*/
}
編譯后下載到EVB_MX+開發(fā)板后,運(yùn)行結(jié)果如下:
3.4.3、總結(jié)
概念性總結(jié):
-
事件傳遞不帶信息負(fù)載,而隊(duì)列是帶信息負(fù)載的 -
隊(duì)列除了可以告訴我們發(fā)生了什么事,還可以告訴我們發(fā)生這件事情的詳細(xì)過程
使用總結(jié):
詳情請(qǐng)參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔
4、總結(jié)
關(guān)于TencentOS tiny還有非常多的組件可以學(xué)習(xí),這里只是列出了最常用的幾種,最后我們給本文做下簡(jiǎn)短的總結(jié):
-
多任務(wù)
解決復(fù)雜需求、實(shí)時(shí)性問題。
-
互斥鎖
解決不可重入,資源的競(jìng)爭(zhēng)關(guān)系。
-
信號(hào)量
解決任務(wù)間同步問題,典型為生產(chǎn)者-消費(fèi)者的問一體。
-
事件
解決任務(wù)間同步問題,相比信號(hào)量,事件可以傳遞多個(gè),但不帶負(fù)載。
-
隊(duì)列
解決任務(wù)間傳遞帶負(fù)載的問題。
案例下載
公眾號(hào)后臺(tái)回復(fù):TencentOS tiny即可獲取本節(jié)所有程序案例及參考文檔下載鏈接。
全文參考資料
騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)SDK文檔.pdf
騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf
TencentOS tiny技術(shù)講解與開發(fā)實(shí)踐PPT.pdf
云加社區(qū)沙龍(騰訊物聯(lián)網(wǎng)操作系統(tǒng)TencentOS tiny架構(gòu)解析與實(shí)踐).pdf
公眾號(hào)粉絲福利時(shí)刻
這里我給大家申請(qǐng)到了福利,本公眾號(hào)讀者購買小熊派開發(fā)板可享受9折優(yōu)惠,有需要購買小熊派以及騰訊物聯(lián)網(wǎng)開發(fā)板的朋友,淘寶搜索即可,跟客服說你是公眾號(hào):嵌入式云IOT技術(shù)圈 的粉絲,立享9折優(yōu)惠!
往期精彩
網(wǎng)紅騰訊物聯(lián)網(wǎng)開發(fā)板終極開箱評(píng)測(cè),讓我們一睹為快!
STM32硬核DIY機(jī)械鍵盤|藍(lán)牙USB雙模|燈控
一個(gè)超火超給力的STM32開源疫情監(jiān)控項(xiàng)目
一個(gè)超酷的開源uHand2.0機(jī)械手掌項(xiàng)目
居民身份證閱讀器產(chǎn)品開發(fā)學(xué)習(xí)心得(再談標(biāo)準(zhǔn)-軟件-協(xié)議)
覺得本次分享的文章對(duì)您有幫助,隨手點(diǎn)[在看]并轉(zhuǎn)發(fā)分享,也是對(duì)我的支持。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!





