當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式云IOT技術(shù)圈
[導(dǎo)讀]一般來說,學(xué)習(xí)任何一個RTOS,本質(zhì)是沒有什么太大的區(qū)別的,通常在最簡版nano上進行開發(fā),關(guān)于TencentOS tiny,我個人認(rèn)為,掌握以下基礎(chǔ)組件的用法足矣,其它的一些組件,可以等需要使用的時候再參考文檔學(xué)習(xí)應(yīng)用即可。

上節(jié),我們介紹了TencentOS tiny,參考官方給出的移植教程親自動手做了一遍,文章如下:

天?。※Z廠都開始做開發(fā)板了?網(wǎng)紅騰訊物聯(lián)網(wǎng)開發(fā)板終極開箱評測,讓我們一睹為快!

趁著最近有時間,這節(jié),我擼了幾個例程作為后面做項目參考的基本框架,當(dāng)然也有一些是直接拿了官方文檔的例程:

一般來說,學(xué)習(xí)任何一個RTOS,本質(zhì)是沒有什么太大的區(qū)別的,通常在最簡版nano上進行開發(fā),關(guān)于TencentOS tiny,我個人認(rèn)為,掌握以下基礎(chǔ)組件的用法足矣,其它的一些組件,可以等需要使用的時候再參考文檔學(xué)習(xí)應(yīng)用即可。

  • TencentOS tiny多任務(wù)
  • TencentOS tiny RTOS軟件定時器
  • TencentOS tiny RTOS任務(wù)間通信(互斥鎖、信號量、事件、隊列)

在使用基本組件之前,我們需要配置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)先級數(shù)量

#define?TOS_CFG_ROUND_ROBIN_EN??????????0u??//?配置TencentOS?tiny的內(nèi)核是否開啟時間片輪轉(zhuǎn)

#define?TOS_CFG_OBJECT_VERIFY_EN???????????1u?//?配置TencentOS?tiny是否校驗指針合法

#define?TOS_CFG_TASK_DYNAMIC_CREATE_EN??1u??//?TencentOS?tiny?動態(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是否開啟動態(tài)內(nèi)存模塊

#define?TOS_CFG_MMHEAP_DEFAULT_POOL_EN??1u??//?TencentOS?tiny?默認(rèn)動態(tài)內(nèi)存池功能宏

#define?TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE????????0x100?//?配置TencentOS?tiny默認(rèn)動態(tài)內(nèi)存池大小

#define?TOS_CFG_MUTEX_EN????????????????1u??//?配置TencentOS?tiny是否開啟互斥鎖模塊

#define?TOS_CFG_MESSAGE_QUEUE_EN????????1u??//?配置TencentOS?tiny是否開啟消息隊列模塊

#define?TOS_CFG_MAIL_QUEUE_EN???????????1u??//?配置TencentOS?tiny是否開啟消息郵箱模塊

#define?TOS_CFG_PRIORITY_MESSAGE_QUEUE_EN?1u?//?配置TencentOS?tiny是否開啟優(yōu)先級消息隊列模塊

#define?TOS_CFG_PRIORITY_MAIL_QUEUE_EN?1u??//?配置TencentOS?tiny是否開啟優(yōu)先級消息郵箱模塊

#define?TOS_CFG_TIMER_EN????????????????1u??//?配置TencentOS?tiny是否開啟軟件定時器模塊

#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是否開啟信號量模塊

#define?TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN??????1u?//?配置TencentOS?tiny是否開啟任務(wù)棧深度檢測

#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ù)?

對于普通的項目來說,比如密碼鎖類項目,單獨的一個傳感器模塊的開發(fā),某些簡單的儀器儀表等等,對于這類場景單一,業(yè)務(wù)需求也單一的項目來說,使用狀態(tài)機或者事件驅(qū)動的方式就足以完成項目的基本功能了。

但是如果開發(fā)一個巨量代碼的工程項目,項目可能設(shè)計到傳感器數(shù)據(jù)讀取、無線數(shù)據(jù)上傳與接收、數(shù)據(jù)傳輸、UI實時刷新、算法處理等等,功能諸多還需要相互配合的情況下,那么如果還在用裸機的思想去完成,那么開發(fā)者一般會面臨以下兩個問題:

  • 設(shè)計思路過于復(fù)雜,光怎么想程序的設(shè)計思路就得想好久了
  • 設(shè)計下來的各個功能,要考慮相互配合的問題,實時性可能得不到要求

RTOS的多任務(wù)就可以解決對應(yīng)的問題,它既能讓項目開發(fā)起來思路清晰,方便易維護;同時RTOS也能保證整個產(chǎn)品運行的實時性,典型的程序設(shè)計架構(gòu),就可以按下面的方式來劃分:

1.2 TencentOS tiny RTOS多任務(wù)實踐

關(guān)于怎么創(chuàng)建多個任務(wù),可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程是我基于上一節(jié)的移植工程,在移植工程的基礎(chǔ)上,由于官方給的OLED驅(qū)動例程是軟件模擬驅(qū)動的,后來我將其改為I2C硬件驅(qū)動,所以,在STM32CubeMX上對OLED的I2C接口進行了配置:

更改后重新生成軟件工程,然后修改oled.c中關(guān)于寫命令和寫數(shù)據(jù)的接口為硬件I2C驅(qū)動:

//?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);
}

接下來,進入多任務(wù)程序編寫,我們主要實現(xiàn)以下兩個功能:

  • task1以1s的頻率循環(huán)打印 Hello TencentOS tiny
  • task2以100ms的頻率循環(huán)翻轉(zhuǎn)。

main.c

定義兩個基本任務(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)建并啟動一個任務(wù)用于打印調(diào)試信息
????osThreadCreate(osThread(task1),?NULL);
????//創(chuàng)建并啟動一個任務(wù)用于以100ms的間隔翻轉(zhuǎn)LED
????osThreadCreate(osThread(task2),?NULL);
????//啟動內(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ā)板后,運行結(jié)果如下:

task1以1s的頻率循環(huán)打印Hello TencentOS tiny,task2以100ms的頻率循環(huán)翻轉(zhuǎn)。

1.3 總結(jié)

概念性總結(jié):

  • 多任務(wù)適合業(yè)務(wù)場景更加復(fù)雜的應(yīng)用場景
  • 多任務(wù)適合對實時響應(yīng)要求更高的場景

使用總結(jié):

詳情請參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔

2、TencentOS tiny RTOS軟件定時器

2.1、為什么要采用RTOS軟件定時器?

軟件定時器,顧名思義就是軟件實現(xiàn)的定時器,它是和硬件定時器有本質(zhì)區(qū)別的,軟件定時器使用的是系統(tǒng)調(diào)度所依賴的嘀嗒定時器,也就是Systick來實現(xiàn)的,它主要解決一些不需要特別精準(zhǔn)的定時觸發(fā)場合,目前github倉庫上有開源不少軟件定時器的實例,比如multi_timer,TecentOS tiny也在自己的內(nèi)核中集成了自己的一套軟件定時器,實現(xiàn)原理其實也是差不多的。

2.2、TencentOS tiny RTOS軟件定時器實踐

關(guān)于怎么使用定時器,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程基于多任務(wù)例程修改,接下來,進入軟件定時器程序編寫,我們主要實現(xiàn)以下兩個功能:

  • task1以1s的頻率循環(huán)打印 Hello TencentOS tiny
  • 軟件定時器以500ms的頻率翻轉(zhuǎn)LED

main.c

/*定義一個定時器句柄*/
k_timer_t?os_tmr_handler;
//創(chuàng)建一個任務(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);
????}
}

//定時器回調(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)建一個以500ms周期運行的軟件定時器
????tos_timer_create(&os_tmr_handler,?500,?500,?os_tmr_handler_callback,?K_NULL,?TOS_OPT_TIMER_PERIODIC);
????//創(chuàng)建一個任務(wù)
????osThreadCreate(osThread(task1),?NULL);
????//啟動定時器
????tos_timer_start(&os_tmr_handler);
????//啟動內(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ā)板后,運行結(jié)果如下:

task1以1s的頻率循環(huán)打印Hello TencentOS tiny,軟件定時器以500ms的頻率執(zhí)行,此時LED會以500ms的速率循環(huán)翻轉(zhuǎn)。

2.3 總結(jié)

概念性總結(jié):

  • 軟件定時器就是用"軟件邏輯"實現(xiàn)的定時器
  • 軟件定時器適合一些不需要特別精準(zhǔn)的定時觸發(fā)場合.

使用總結(jié):

詳情請參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔

3、TencentOS tiny RTOS任務(wù)間通信

3.1、TencentOS tiny RTOS互斥鎖

3.1.1 、為什么要采用RTOS互斥鎖?

互斥鎖適用于實現(xiàn)臨界區(qū)資源的互斥性訪問,當(dāng)有多個任務(wù)同時并行對一個數(shù)據(jù)操作時,就會存在不確定性,典型的案例就是全局變量,在不帶操作系統(tǒng)的裸機功能開發(fā)中,我們通常會使用全局變量,讓其在整個工程中通過外部引用的方式全局可見,這樣我們就可以很方便的在任何一個地方對其進行讀寫操作,但如果在操作系統(tǒng)中卻恰恰相反,這種奇怪的現(xiàn)象被稱為不可重入,通常在操作系統(tǒng)里叫臨界區(qū)資源,在字符串操作中,典型的不可重入函數(shù)是strtok,strtok函數(shù)內(nèi)部有一個static變量,這種類型的變量可以被多次重入調(diào)用共同控制,其最終的結(jié)果依賴于它們的執(zhí)行順序,所以,使用互斥鎖可以解決這種不確定性的問題,也就是說在任意時刻,只會有一個任務(wù)對其進行訪問。

3.1.2、TencentOS tiny RTOS互斥鎖實踐

關(guān)于怎么使用互斥鎖,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程基于多任務(wù)例程修改,接下來,進入互斥鎖程序編寫,我們主要實現(xiàn)三個任務(wù)同時執(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ù),用于驗證互斥鎖
????osThreadCreate(osThread(task1),?NULL);
????osThreadCreate(osThread(task2),?NULL);
????osThreadCreate(osThread(task3),?NULL);
????//啟動內(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ā)板后,運行結(jié)果如下:

3.1.3、總結(jié)

概念性總結(jié):

  • 互斥鎖在任意時刻,只會有一個任務(wù)對臨界資源進行訪問
  • 互斥鎖用于實現(xiàn)臨界區(qū)資源的互斥性訪問

使用總結(jié):

詳情請參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔

3.2、TencentOS tiny RTOS信號量

3.2.1、為什么要采用RTOS信號量?

信號量,俗話說就是信號的數(shù)量,它是一種任務(wù)間傳遞系統(tǒng)可用資源的機制;舉一個生產(chǎn)者與消費者的問題;也就是說消費者在消費了一個資源之前需要等待資源釋放,生產(chǎn)者生產(chǎn)資源以后要即時去通知其它的消費者,簡單的說就是凡事都要有個先來后到,所以信號量最常用的地方就是實現(xiàn)任務(wù)間同步。

3.2.2、TencentOS tiny RTOS信號量實踐

關(guān)于怎么使用信號量,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程基于多任務(wù)例程修改,接下來,進入信號量程序編寫,我們主要實現(xiàn)生產(chǎn)者和消費者的問題,這段程序在參考文檔里可以找到:

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ù)實現(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);
????//啟動內(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ā)板后,運行結(jié)果如下:

3.2.3、總結(jié)

概念性總結(jié):

  • 信號量可以用于實現(xiàn)任務(wù)間同步
  • 信號量最典型的應(yīng)用就是處理生產(chǎn)者與消費者的問題

使用總結(jié):

詳情請參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔

3.3、TencentOS tiny RTOS事件

3.3.1、為什么要采用RTOS事件?

事件,是RTOS任務(wù)間用來傳遞的一種信號的信息,它可以傳遞多個信息,事件和信號量的區(qū)別就是信號量只能傳遞0和1兩個信息,而事件的類型通常用k_event_flag_t進行描述,它的本質(zhì)是一個uint32_t數(shù)據(jù)類型,也就是說事件最多可以定義32個,使用事件可以很方便的實現(xiàn)任務(wù)間同步和信息傳遞,但是要注意的是事件達(dá)到的是一種類似通知的效果,本身是不帶負(fù)載的。

3.3.2、TencentOS tiny RTOS事件實踐

關(guān)于怎么使用事件,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,以下工程基于多任務(wù)例程修改,我們在多任務(wù)例程的基礎(chǔ)上,移植了multi_button組件,這個組件的移植方法在之前寫小熊派相關(guān)的文章中都有詳細(xì)的方法,這里就不再多說了,參考文章如下:

第1期 | MultiButton,一個小巧簡單易用的事件驅(qū)動型按鍵驅(qū)動模塊

開源按鍵組件MultiButton支持菜單操作(事件驅(qū)動型)

這里的button_ticks()我把它放在SysTick_Handler函數(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?*/
}

接下來進入事件程序的編寫,我們主要實現(xiàn)以下兩個功能:

  • 定義任務(wù)task1,用于初始化multi_button,通過按鍵回調(diào)發(fā)送事件
  • 定義任務(wù)task2,用于接收task1發(fā)送過來的事件,并進行處理

main.c

定義multi_button結(jié)構(gòu)體變量以及事件相關(guān)的變量

#include?"multi_button.h"
/*創(chuàng)建一個按鍵事件*/
k_event_t?key_event;
//創(chuàng)建幾個按鍵的事件標(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)建一個按鍵事件
????tos_event_create(&key_event,?(k_event_flag_t)0u);
????//創(chuàng)建并啟動一個任務(wù)用于通過按鍵發(fā)送事件
????osThreadCreate(osThread(task1),?NULL);
????//創(chuàng)建并啟動一個任務(wù)用于接收按鍵事件并執(zhí)行相應(yīng)的軟件邏輯
????osThreadCreate(osThread(task2),?NULL);
????//啟動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ā)板后,運行結(jié)果如下:

當(dāng)分別按下四個按鍵后,task2接收到具體消息后執(zhí)行不同的邏輯

3.3.3 總結(jié)

概念性總結(jié):

  • 事件區(qū)別于信號量,信號量是0-1傳遞,而事件可以傳遞多個信息
  • 事件是用于RTOS任務(wù)間傳遞的一種沒有信息負(fù)載的信號類信息

使用總結(jié):

詳情請參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔

3.4、TencentOS tiny RTOS隊列

3.4.1、為什么要采用RTOS隊列?

隊列也是任務(wù)間傳遞信息的一種方式,它和事件最本質(zhì)的區(qū)別就是,事件傳遞沒有負(fù)載,而隊列的傳遞是包含數(shù)據(jù)負(fù)載的,在事件章節(jié)中,當(dāng)我們按下按鍵的時候其中一個任務(wù)發(fā)出事件,另一個任務(wù)則接收事件,而接收的這個事件是非常單一的,除此之外并沒有更多具體的新信息載體,而隊列就是為了解決這個問題而誕生的,比如串口接收數(shù)據(jù),當(dāng)數(shù)據(jù)接收滿了,此時我們就可以使用隊列將接收滿的數(shù)據(jù)通過隊列的信息發(fā)出去,然后任務(wù)里進行接收處理。

3.4.2、TencentOS tiny RTOS隊列實踐

關(guān)于怎么使用隊列,可以參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔,但該文檔的API過老,可能不適合現(xiàn)在的版本,于是找來了一個新版的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ù)實現(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);
????//啟動內(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ā)板后,運行結(jié)果如下:

3.4.3、總結(jié)

概念性總結(jié):

  • 事件傳遞不帶信息負(fù)載,而隊列是帶信息負(fù)載的
  • 隊列除了可以告訴我們發(fā)生了什么事,還可以告訴我們發(fā)生這件事情的詳細(xì)過程

使用總結(jié):

詳情請參考騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf文檔

4、總結(jié)

關(guān)于TencentOS tiny還有非常多的組件可以學(xué)習(xí),這里只是列出了最常用的幾種,最后我們給本文做下簡短的總結(jié):

  • 多任務(wù)

解決復(fù)雜需求、實時性問題。

  • 互斥鎖

解決不可重入,資源的競爭關(guān)系。

  • 信號量

解決任務(wù)間同步問題,典型為生產(chǎn)者-消費者的問一體。

  • 事件

解決任務(wù)間同步問題,相比信號量,事件可以傳遞多個,但不帶負(fù)載。

  • 隊列

解決任務(wù)間傳遞帶負(fù)載的問題。

案例下載

公眾號后臺回復(fù):TencentOS tiny即可獲取本節(jié)所有程序案例及參考文檔下載鏈接。

全文參考資料

騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)SDK文檔.pdf

騰訊物聯(lián)網(wǎng)終端操作系統(tǒng)開發(fā)指南.pdf

TencentOS tiny技術(shù)講解與開發(fā)實踐PPT.pdf

云加社區(qū)沙龍(騰訊物聯(lián)網(wǎng)操作系統(tǒng)TencentOS tiny架構(gòu)解析與實踐).pdf

公眾號粉絲福利時刻

這里我給大家申請到了福利,本公眾號讀者購買小熊派開發(fā)板可享受9折優(yōu)惠,有需要購買小熊派以及騰訊物聯(lián)網(wǎng)開發(fā)板的朋友,淘寶搜索即可,跟客服說你是公眾號:嵌入式云IOT技術(shù)圈 的粉絲,立享9折優(yōu)惠!

往期精彩

網(wǎng)紅騰訊物聯(lián)網(wǎng)開發(fā)板終極開箱評測,讓我們一睹為快!

STM32硬核DIY機械鍵盤|藍(lán)牙USB雙模|燈控

一個超火超給力的STM32開源疫情監(jiān)控項目

一個超酷的開源uHand2.0機械手掌項目

居民身份證閱讀器產(chǎn)品開發(fā)學(xué)習(xí)心得(再談標(biāo)準(zhǔn)-軟件-協(xié)議)

覺得本次分享的文章對您有幫助,隨手點[在看]并轉(zhuǎn)發(fā)分享,也是對我的支持。

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉