最近在進行一個項目的開發(fā)和調(diào)試,使用的是單片機 + freeRTOS進行開發(fā),通過一段時間的碼代碼和調(diào)試,各個方面都已經(jīng)調(diào)通,功能也順利的實現(xiàn),也在掛機測試了。
在這次開發(fā)中,也是遇到了很多的問題,主要的感想是關(guān)于代碼的框架。在單片機開發(fā)中,特別是使用了RTOS的時候,一個良好的代碼框架真的是相當(dāng)?shù)谋匾摹?/p>
如果一開始沒有仔細(xì)的考慮好該怎么搭載一個代碼框架,寫代碼時想寫什么就寫什么,有什么功能要加找個地方就隨便插入進入,當(dāng)代碼量大的時候就會看起來很亂。甚至將來接手代碼的人,估計內(nèi)心一萬個***從心中飛過,時刻游走在崩潰的邊緣,即使是想改點什么功能也不知道從哪里開始著手,估計會煩躁到喜提地中海!!!
本文就想分享一個我個人使用的單片機+freeRTOS的代碼框架,框架涉及到消息接收、消息處理、消息發(fā)送、其他動作的處理。下面一步步說明代碼框架的搭建過程。
- 創(chuàng)建任務(wù)
當(dāng)開始一個項目代碼的編寫之前,都要考慮這份代碼要實現(xiàn)一些什么樣的功能,并將要實現(xiàn)的功能進行分類,根據(jù)功能的各自屬性可以歸納出幾個Module,然后想想在代碼中哪些功能要放在一塊,哪些功能要區(qū)分開等等的細(xì)節(jié)問題。
并且還需要考慮代碼的耦合性,好的代碼是要能夠做到高內(nèi)聚低耦合的,各個功能模塊之間能夠獨立區(qū)分開,需要產(chǎn)生聯(lián)系的功能代碼,要通過某些通信手段實現(xiàn)(共享內(nèi)存、信號量、消息隊列等等),不要互相拉扯,像你中有我,我中有你這種情況要盡可能的避免。
比如,我手上的項目通過功能歸類劃分,就可以分為接收消息、處理消息、發(fā)送消息、其他功能處理,由此便可以考慮劃分出4個線程去處理。
但是,考慮到項目中使用的是CAN通信的方式,接收消息就可以考慮使用CAN接收中斷的方式,能夠做到及時的響應(yīng)接收消息,所以這個時候只需要3個線程即可。并且消息的接收使用隊列的方式接收,方便管理消息和進行線程之間的同步。消息的發(fā)送也采用先壓入隊列再發(fā)送的方式。
freeRTOS中創(chuàng)建3個線程如下:
#define OTHER_HANDLE_TASK_PRIO 2
#define OTHER_HANDLE_STK_SIZE 256
TaskHandle_t OtherHandleTask_Handler;
#define CAN_HANDLE_MSG_TASK_PRIO 3
#define CAN_HANDLE_MSG_STK_SIZE 256
TaskHandle_t Can_HandleMsgTask_Handler;
#define CAN_SEND_MSG_TASK_PRIO 2
#define CAN_SEND_MSG_STK_SIZE 256
TaskHandle_t Can_SendMsgTask_Handler;
// 其他功能的管理線程
xTaskCreate((TaskFunction_t )OtherHandle_Task,
(const char * )"OtherHandle_Task",
(uint16_t )OTHER_HANDLE_STK_SIZE,
(void * )NULL,
(UBaseType_t )OTHER_HANDLE_TASK_PRIO,
(TaskHandle_t * )&OtherHandleTask_Handler);
// 接收消息的處理線程
xTaskCreate((TaskFunction_t )Can_HandleMsg_Task,
(const char * )"Can_HandleMsg_Task",
(uint16_t )CAN_HANDLE_MSG_STK_SIZE,
(void * )NULL,
(UBaseType_t )CAN_HANDLE_MSG_TASK_PRIO,
(TaskHandle_t * )&Can_HandleMsgTask_Handler);
// 發(fā)送消息的處理線程
xTaskCreate((TaskFunction_t )Can_SendMsg_Task,
(const char * )"Can_SendMsg_Task",
(uint16_t )CAN_SEND_MSG_STK_SIZE,
(void * )NULL,
(UBaseType_t )CAN_SEND_MSG_TASK_PRIO,
(TaskHandle_t * )&Can_SendMsgTask_Handler);
消息接收隊列、消息發(fā)送隊列的創(chuàng)建,如下:
// 消息接收隊列
QueueHandle_t CanRxQueue;
CanRxQueue = xQueueCreate(xxxxxx, xxxxxx);
// 消息發(fā)送隊列
QueueHandle_t CanTxQueue;
CanTxQueue = xQueueCreate(xxxxxx, xxxxxx);
- CAN中斷接收消息 & 消息處理線程
2.1、CAN中斷接收消息如下:
void CAN1_RX0_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken;
/* 其他代碼 */
xResult = xQueueSendFromISR(CanRxQueue, &ptwCanRxMsg, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR (xHigherPriorityTaskWoken);
}
注意:接收很多時候不一定要使用中斷的方式,用查詢的方式也是可以的,只是在RTOS中,查詢接收的話,要考慮消息接收是否及時,接收消息的線程優(yōu)先級要比較高,否則容易造成處理的動作的延遲。
2.2、消息的處理線程
消息處理的線程任務(wù)函數(shù)如下:
void Can_HandleMsg_Task(void *pvParameters)
{
while (1)
{
xQueueReceive(CanRxQueue, xxxxxx, portMAX_DELAY);
/*
處理部分
*/
}
}
消息處理中使用了消息隊列的阻塞的特性,在隊列為空的時候阻塞掛起線程,可以減少CPU調(diào)度線程的壓力;當(dāng)消息隊列不為空的時候,隊列不再阻塞,線程從掛起中恢復(fù),參與調(diào)度并處理任務(wù)。
注意:在freeRTOS中可以用于阻塞的還有信號量、事件標(biāo)志組、消息郵箱。
- 消息的發(fā)送線程
消息的發(fā)送如下:
void Can_SendMsg_Task(void *pvParameters)
{
while (1)
{
xQueueReceive(CanTxQueue, xxxxxx, portMAX_DELAY);
/*
處理部分
*/
}
}
消息的發(fā)送中也使用了消息隊列,需要發(fā)送的消息可以先壓入隊列,然后由發(fā)送線程去發(fā)送。同樣使用隊列的阻塞特性,在隊列為空的時候阻塞掛起發(fā)送線程,減少CPU調(diào)度線程的壓力;當(dāng)發(fā)送消息的隊列不為空的時候,隊列不再阻塞,線程從掛起中恢復(fù),參與調(diào)度并將消息發(fā)送出去。
- 其他功能的處理線程
void OtherHandle_Task(void *pvParameters)
{
while (1)
{
/*
處理部分
*/
}
}
其他功能的處理就放在其他任務(wù)線程中處理,比如GUI顯示、按鍵掃描、和傳感器通信等等的。具體需要幾個線程管理需要根據(jù)實際的項目情況進行安排。另外各個線程的優(yōu)先級也要根據(jù)情況進行安排,確保重要的功能部分能被及時的執(zhí)行到!
-
單片機
+關(guān)注
關(guān)注
6039文章
44575瀏覽量
636400 -
框架
+關(guān)注
關(guān)注
0文章
403瀏覽量
17510 -
RTOS
+關(guān)注
關(guān)注
22文章
817瀏覽量
119715 -
代碼
+關(guān)注
關(guān)注
30文章
4801瀏覽量
68735
發(fā)布評論請先 登錄
相關(guān)推薦
評論