1、輕型操作系統同步的方案詳解
1)信號量
假設有兩個任務 Task1 和 Task2,第一個任務進行按鍵的掃描,第二個任務進行LED燈的點亮
需求:
掃描到按鍵按下后點亮 LED 燈,也就是說第二個任務永遠在等待第一個任務按鍵的掃描
實現:
首先 Task1 一直檢測按鍵是否按下,如果按鍵按下以后,使用一個全局變量 flag 并設置 flag=1
而在 Task2 當中,不停檢測 flag 值是否為 1,如果為 1,點亮 led 燈并把flag清零
此時 flag 提供的是一個信號量的作用,也就是說 Task1 按下按鍵以后,開始向 Task2 發送一個信號量 flag,Task2 接收到了 flag 信號量以后,就把LED燈點亮
2)互斥性信號量
假設有兩個任務 Task1 和 Task2,都需要來訪問一個共享的資源,如要訪問一個共享的打印機
需求:
假設第一個任務 Task1 要打印 hello,第二個任務 Task2 要打印 world,Task1 在使用打印機的時候,Task2是絕對不能使用的,兩個任務屬于互斥關系
如果 Task1 在使用打印機,Task2 也在使用打印機,那打印出來的數據可能會出現亂碼
實現:
為了防止 Task1 和 Task2 共同使用打印機,配置的時候就要使用一個約束,假設還是使用一個全局變量 flag 來表示
如果 flag=1,表示這個打印機處于空閑狀態,假設這個時候 Task1 要使用打印機,它首先要判斷 flag 的狀態,如果判斷 flag=1,它就開始使用打印機并且把 flag 置 0
同樣,假設這個時候,Task2也來使用打印機,它同樣要判斷 flag 的狀態是否等于 1,如果判斷這個時候flag=0,它就知道這個時候打印機處于忙的狀態
它就要等待 Task1 把打印機使用完畢,同時會把 flag 置為 1,這個時候 Task2 任務就可以使用打印機了
3)事件標志組
假設兩個任務 Task1 和 Task2,Task1 進行按鍵掃描,Task2 進行 LED 燈的點亮
同樣的道理,按鍵按下時 LED 燈點亮,但是如果是 N 個按鍵 控制 N 個 LED
使用一個全局變量 flag,但是使用 flag 的各個位來表明了按鍵按下的狀態,flag 的第 0 位為 1 表明第一個按鍵按下
同樣 flag 的第 1 位按鍵為 1,表明第二個按鍵已經按下,依次類推
此時 flag 已經不再是一個信號量了,而是一個事件的標志,它的一位標志著一個事件是否發生,比如說第0位為0,表明這個事件沒發生,第1位為1,表明這個事件發生了,這個時候這個flag就被稱為一個事件的標志
那Task2在使用的過程中,它就需要來判斷flag這個事件的各個位
當然事件的標志還有一些其他的高級標志,比如說你各個位能判斷某一個事件,還可以判斷一個組合事件:
比如第一個按鍵按下了,并且第二個按鍵也按下了,并且第三個按鍵也按下了,那你們都按下以后,我才讓某個燈亮
這樣我們就可以通過事件標志組來通過各個標志位,來相互的判斷,那這個就被稱為事件標志組,它不是信號量,但是它還是屬于信號量的范疇
2、freeRTOS事件標志組詳解
需求:
任務可能會需要與多個事件或任務進行同步,此時信號量就無
能為力了。 FreeRTOS 為此提供了一個可選的解決方法,那就是事件標志組。
簡介:
1)、事件位(事件標志)
事件位用來表明某個事件是否發生,事件位通常用作事件標志
比如下面的幾個例子:
● 當收到一條消息并且把這條消息處理掉以后就可以將某個位(標志)置 1,當隊列中沒有
消息需要處理的時候就可以將這個位(標志)置 0;
● 當把隊列中的消息通過網絡發送輸出以后就可以將某個位(標志)置 1,當沒有數據需要從網絡發送出去的話就將這個位(標志)置 0;
● 現在需要向網絡中發送一個心跳信息,將某個位(標志)置 1。現在不需要向網絡中發送心跳信息,這個位(標志)置 0。
2)、事件組
一個事件組就是一組的事件位, 事件組中的事件位通過位編號來訪問
同樣,以上面列出的三個例子為例:
● 事件標志組的 bit0 表示隊列中的消息是否處理掉。
● 事件標志組的 bit1 表示是否有消息需要從網絡中發送出去。
● 事件標志組的 bit2 表示現在是否需要向網絡發送心跳信息。
3)、事件標志組和事件位的數據類型
事件標志組的數據類型為 EventGroupHandle_t, 當 configUSE_16_BIT_TICKS 為 1 的時候事件標志組可以存儲 8 個事件位,當 configUSE_16_BIT_TICKS 為 0 的時候事件標志組存儲 24個事件位。
事件標志組中的所有事件位都存儲在一個無符號的 EventBits_t 類型的變量中, EventBits_t在 event_groups.h 中有如下定義:
typedef TickType_t EventBits_t;
數據類型 TickType_t 在文件 portmacro.h 中有如下定義:
#if( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#define portTICK_TYPE_IS_ATOMIC 1
#endif
可以看出當 configUSE_16_BIT_TICKS 為 0 的時候 TickType_t 是個 32 位的數據類型, 因此 EventBits_t 也是個 32 位的數據類型。 EventBits_t 類型的變量可以存儲 24 個事件位,另外的那高 8 位有其他用。事件位 0 存放在這個變量的 bit0 上,變量的 bit1 就是事件位 1,以此類推。對于 STM32 來說一個事件標志組最多可以存儲 24 個事件位。
3、測試試驗詳解
1)、需求
學習 FreeROTS 事件標志組的使用,包括創建事件標志組、將相應的事件位置 1、等待相應
的事件位置 1 等操作。
2)、實現
設計四個任務: start_task、 eventsetbit_task、 eventgroup_task 和 eventquery_task
這四個任務的任務功能如下:
start_task:用來創建其他三個任務和事件標志組。
eventsetbit_task: 讀取按鍵值,根據不同的按鍵值將事件標志組中相應的事件位置 1,用來
模擬事件的發生。
eventgroup_task:同時等待事件標志組中的多個事件位,當這些事件位都置 1 的話就執行相應的處理,例程中是刷新 LCD 指定區域的背景色。
eventquery_task:查詢事件組的值,也就是各個事件位的值。獲取到事件組值以后就將其顯示到 LCD 上,并且也通過串口打印出來。
實驗中還創建了一個事件標志組: EventGroupHandler,實驗中用到了這個事件標志組的三個事件位,分別位 bit0, bit1 和 bit2。
實驗中會用到 3 個按鍵: KEY0、 KEY1 和 KEY2,其中按鍵 KEY1 和 KEY2 為普通的輸入模式。按鍵 KEY0 為中斷輸入模式,KEY0 用來演示如何在中斷服務程序調用事件標志組的 API函數。
3)、工程
●任務設置
#define START_TASK_PRIO 1 //任務優先級
#define START_STK_SIZE 256 //任務堆棧大小
TaskHandle_t StartTask_Handler; //任務句柄
void start_task(void *pvParameters); //任務函數
#define EVENTSETBIT_TASK_PRIO 2 //任務優先級
#define EVENTSETBIT_STK_SIZE 256 //任務堆棧大小
TaskHandle_t EventSetBit_Handler; //任務句柄
void eventsetbit_task(void *pvParameters); //任務函數
#define EVENTGROUP_TASK_PRIO 3 //任務優先級
#define EVENTGROUP_STK_SIZE 256 //任務堆棧大小
TaskHandle_t EventGroupTask_Handler; //任務句柄
void eventgroup_task(void *pvParameters); //任務函數
#define EVENTQUERY_TASK_PRIO 4 //任務優先級
#define EVENTQUERY_STK_SIZE 256 //任務堆棧大小
TaskHandle_t EventQueryTask_Handler; //任務句柄
void eventquery_task(void *pvParameters); //任務函數
EventGroupHandle_t EventGroupHandler; //事件標志組句柄
#define EVENTBIT_0 (1<<0) //事件位
#define EVENTBIT_1 (1<<1)
#define EVENTBIT_2 (1<<2)
#define EVENTBIT_ALL (EVENTBIT_0|EVENTBIT_1|EVENTBIT_2)
//LCD 刷屏時使用的顏色
int lcd_discolor[14]={ WHITE, BLACK, BLUE, BRED,
GRED, GBLUE, RED, MAGENTA,
GREEN, CYAN, YELLOW, BROWN,
BRRED, GRAY };
● main()函數
int main(void)
{
HAL_Init(); //初始化 HAL 庫
Stm32_Clock_Init(360,25,2,8); //設置時鐘,180Mhz
delay_init(180); //初始化延時函數
uart_init(115200); //初始化串口
LED_Init(); //初始化 LED
KEY_Init(); //初始化按鍵
PCF8574_Init(); //初始化 PCF8574
EXTI_Init(); //初始化外部中斷
SDRAM_Init(); //初始化 SDRAM
LCD_Init(); //初始化 LCD
my_mem_init(SRAMIN); //初始化內部內存池
POINT_COLOR = RED;
LCD_ShowString(30,10,200,16,16,"Apollo STM32F4/F7");
LCD_ShowString(30,30,200,16,16,"FreeRTOS Examp 16-1");
LCD_ShowString(30,50,200,16,16,"Event Group");
LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,90,200,16,16,"2016/11/11");
POINT_COLOR = BLACK;
LCD_DrawRectangle(5,130,234,314); //畫矩形
POINT_COLOR = BLUE;
LCD_ShowString(30,110,220,16,16,"Event Group Value:0");
//創建開始任務
xTaskCreate((TaskFunction_t )start_task, //任務函數
(const char* )"start_task", //任務名稱
(uint16_t )START_STK_SIZE, //任務堆棧大小
(void* )NULL, //傳遞給任務函數的參數
(UBaseType_t )START_TASK_PRIO, //任務優先級
(TaskHandle_t* )&StartTask_Handler); //任務句柄
vTaskStartScheduler(); //開啟任務調度
}
● 任務函數
//開始任務任務函數
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //進入臨界區
//創建事件標志組
EventGroupHandler=xEventGroupCreate(); //創建事件標志組 (1)
//創建設置事件位的任務
xTaskCreate((TaskFunction_t )eventsetbit_task,
(const char* )"eventsetbit_task",
(uint16_t )EVENTSETBIT_STK_SIZE,
(void* )NULL,
(UBaseType_t )EVENTSETBIT_TASK_PRIO,
(TaskHandle_t* )&EventSetBit_Handler);
//創建事件標志組處理任務
xTaskCreate((TaskFunction_t )eventgroup_task,
(const char* )"eventgroup_task",
(uint16_t )EVENTGROUP_STK_SIZE,
(void* )NULL,
(UBaseType_t )EVENTGROUP_TASK_PRIO,
(TaskHandle_t* )&EventGroupTask_Handler);
//創建事件標志組查詢任務
xTaskCreate((TaskFunction_t )eventquery_task,
(const char* )"eventquery_task",
(uint16_t )EVENTQUERY_STK_SIZE,
(void* )NULL,
(UBaseType_t )EVENTQUERY_TASK_PRIO,
(TaskHandle_t* )&EventQueryTask_Handler);
vTaskDelete(StartTask_Handler); //刪除開始任務
taskEXIT_CRITICAL(); //退出臨界區
}
//設置事件位的任務
void eventsetbit_task(void *pvParameters)
{
u8 key;
while(1)
{
if(EventGroupHandler!=NULL)
{
key=KEY_Scan(0);
switch(key)
{
case KEY1_PRES:
xEventGroupSetBits(EventGroupHandler,EVENTBIT_1); (2)
break;
case KEY2_PRES:
xEventGroupSetBits(EventGroupHandler,EVENTBIT_2); (3)
break;
}
}
}
//事件標志組處理任務
void eventgroup_task(void *pvParameters)
{
u8 num;
EventBits_t EventValue;
while(1)
{
if(EventGroupHandler!=NULL)
{
//等待事件組中的相應事件位
EventValue=xEventGroupWaitBits((EventGroupHandle_t )EventGroupHandler, (4)
(EventBits_t ) EVENTBIT_ALL,
(BaseType_t )pdTRUE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
printf("事件標志組的值:%drn",EventValue);ALIENTEK 阿波羅 FreeRTOS 開發教程
303
STM32F429 FreeRTOS 開發手冊
LCD_ShowxNum(174,110,EventValue,1,16,0);
num++;
LED1=!LED1;
LCD_Fill(6,131,233,313,lcd_discolor[num%14]);
}
else
{
vTaskDelay(10); //延時 10ms,也就是 10 個時鐘節拍
}
}
}
//事件查詢任務
void eventquery_task(void *pvParameters)
{
u8 num=0;
EventBits_t NewValue,LastValue;
while(1)
{
if(EventGroupHandler!=NULL)
{
NewValue=xEventGroupGetBits(EventGroupHandler); //獲取事件組的 (5)
if(NewValue!=LastValue)
{
LastValue=NewValue;
printf("事件標志組的值:%drn",NewValue);
LCD_ShowxNum(174,110,NewValue,1,16,0);
}
num++;
if(num==0) //每 500msLED0 閃爍一次
{
num=0;
LED0=!LED0;
}
vTaskDelay(50); //延時 50ms,也就是 50 個時鐘節拍
}
}
(1)、首先調用函數 xEventGroupCreate()創建一個事件標志組 EventGroupHandler。
(2)、按下 KEY1 鍵的時候就調用函數 xEventGroupSetBits()將事件標志組的 bit1 置 1。
(3)、按下 KEY2 鍵的時候調用函數 xEventGroupSetBits()將事件標志組的 bit2 值 1。
(4)、調用函數 xEventGroupWaitBits()同時等待事件標志組的 bit0, bit1 和 bit2,只有當這三個事件都置 1 的時候才會執行任務中的其他代碼。
(5)、調用函數 xEventGroupGetBits()查詢事件標志組 EventGroupHandler 的值變化,通過查看這些值的變化就可以分析出當前哪個事件位置 1 了。
● 中斷初始化及處理過程
事件標志組 EventGroupHandler的事件位 bit0 是通過 KEY0 的外部中斷服務函數來設置的,
注意中斷優先級的設置!本例程的中斷優先級設置如下:
//中斷線 3-PH3
HAL_NVIC_SetPriority(EXTI3_IRQn,6,0); //搶占優先級為 6,子優先級為 0
HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能中斷線 3
KEY0 的外部中斷服務函數如下:
//事件標志組句柄
extern EventGroupHandle_t EventGroupHandler;
//中斷服務函數
void EXTI3_IRQHandler(void)
{
BaseType_t Result,xHigherPriorityTaskWoken;
delay_xms(50); //消抖
if(KEY0==0)
{
Result=xEventGroupSetBitsFromISR(EventGroupHandler,EVENTBIT_0, (1)
&xHigherPriorityTaskWoken);
if(Result!=pdFAIL)
{
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清除中斷標志位
}
(1)、 在中斷服務函數中通過調用 xEventGroupSetBitsFromISR()來將事件標志組的事件位
bit0 置 1。
上一篇:STM32Cube 工具說明和安裝淺析
下一篇:STM32 IIC 詳解 之 stm32 IIC 從機模式(中斷方式收發數據)
推薦閱讀
史海拾趣
在競爭激烈的電子行業中,DACHANG公司始終堅持以品質為核心。公司不斷引進先進的生產設備和技術,嚴格把控產品質量,確保每一件產品都能達到客戶的期望。正是這種對品質的執著追求,讓DACHANG公司的產品在市場上贏得了良好的口碑,公司也逐漸擴大了自己的市場份額。
DACHANG公司深知創新是企業發展的根本動力。因此,公司始終將創新作為自己的核心競爭力,不斷推出具有創新性的產品和解決方案。無論是產品設計、技術研發還是市場營銷,DACHANG公司都積極引入新的理念和模式,為公司的發展注入了源源不斷的活力。
在競爭激烈的電子行業中,DACHANG公司始終堅持以品質為核心。公司不斷引進先進的生產設備和技術,嚴格把控產品質量,確保每一件產品都能達到客戶的期望。正是這種對品質的執著追求,讓DACHANG公司的產品在市場上贏得了良好的口碑,公司也逐漸擴大了自己的市場份額。
隨著LED背光液晶電視市場的快速發展,AnalogicTech敏銳地捕捉到了這一機遇。公司推出了直接式和邊緣式LED背光驅動器系列,顯著增強了LED背光液晶電視的能效和用戶體驗。這一創新舉措不僅為AnalogicTech贏得了市場份額,也進一步鞏固了其在電子行業中的領先地位。
隨著全球化趨勢的加強,CTS公司開始積極拓展國際市場。公司在全球范圍內設立了多個生產基地和銷售網絡,以便更好地服務全球客戶。同時,CTS還加強了與國際知名企業的合作,共同推動電子行業的發展。
ENTRELEC UK深知品質對于企業的重要性,因此公司始終堅持嚴格的質量管理體系。從原材料采購到產品制造,再到售后服務,每一個環節都經過嚴格把關。這種對品質的執著追求使ENTRELEC UK贏得了客戶的信任和好評。公司還建立了完善的客戶服務體系,確保客戶在使用產品過程中得到及時、有效的支持。
定點運算DSP在應用中已取得了極大的成功,而且仍然是DSP應用的主體。然而,隨著對DSP處理速度與精度、存儲器容量、編程的靈活性和方便性要求的不斷提高、自80年代中后期以來,各DSP生產廠家陸續推出了各自的32bit浮點運算DSP。… 查看全部問答∨ |
自己制作電路板時劃好線總想把多余的銅去掉,如果你用刀子在上面劃沒有什么用處,只好是將制作好的板子放在三氯化鐵中這樣多余的銅才會去掉,用覆蓋劑劃好圖<油漆當覆蓋劑也可以>然后放在三氯化鐵中溶解掉銅,業余制作印刷板有很多方法,用什 ...… 查看全部問答∨ |
我們公司要招一名Linux下的藍牙開發工程師。公司地址在上海中山公園附近。薪資待遇1萬/月。 職位要求: 1、精通LINUX系統以及C/C++編程(必須)。 2、熟悉嵌入式系統、單片機的開發。 3、精通藍牙協議、藍牙應用開發(必須)。 4、至少三年以 ...… 查看全部問答∨ |
如題。 現在要做USB插入和拔除的探測,并得到其盤符。 我是直接解析系統廣播的消息:DBT_DEVICEREMOVECOMPLETE 和 DBT_DEVICEARRIVAL 但是問題比較奇怪,拔插U盤,與拔插SD卡得到的消息與參數是一樣的: DEV_BROADCAST_HDR 結構的dbcp_devicety ...… 查看全部問答∨ |
無線傳輸數據,發射端移動的時候,對傳輸質量影響很大,請問有沒有什么好的解決方法? 現在有一個無線的項目(不采用跳頻),我對無線接觸不多,碰到這樣的問題,發射端在移動的時候,對發射質量影響特別大,請教一下這方面的知識,和解決這個問題的方法。謝謝!… 查看全部問答∨ |
|
在脈沖RD的上升沿將內存的數據讀到芯片的數據端口DOUT,怎樣實現? 可以讓這個脈沖信號直接做clock使用嗎?? 例如下面這樣寫:always @(posedge RD ) DOUT_EN==1\'b1;產生一個enable 信號,綜合時要對這個RD create clock 嗎??… 查看全部問答∨ |