最近在進行一個項目的開發和調試,使用的是單片機 + freeRTOS進行開發,通過一段時間的碼代碼和調試,各個方面都已經調通,功能也順利的實現,也在掛機測試了。
在這次開發中,也是遇到了很多的問題,主要的感想是關于代碼的框架。在單片機開發中,特別是使用了RTOS的時候,一個良好的代碼框架真的是相當的必要的。
如果一開始沒有仔細的考慮好該怎么搭載一個代碼框架,寫代碼時想寫什么就寫什么,有什么功能要加找個地方就隨便插入進入,當代碼量大的時候就會看起來很亂。甚至將來接手代碼的人,估計內心一萬個***從心中飛過,時刻游走在崩潰的邊緣,即使是想改點什么功能也不知道從哪里開始著手,估計會煩躁到喜提地中海!!!
本文就想分享一個我個人使用的單片機+freeRTOS的代碼框架,框架涉及到消息接收、消息處理、消息發送、其他動作的處理。下面一步步說明代碼框架的搭建過程。
- 創建任務
當開始一個項目代碼的編寫之前,都要考慮這份代碼要實現一些什么樣的功能,并將要實現的功能進行分類,根據功能的各自屬性可以歸納出幾個Module,然后想想在代碼中哪些功能要放在一塊,哪些功能要區分開等等的細節問題。
并且還需要考慮代碼的耦合性,好的代碼是要能夠做到高內聚低耦合的,各個功能模塊之間能夠獨立區分開,需要產生聯系的功能代碼,要通過某些通信手段實現(共享內存、信號量、消息隊列等等),不要互相拉扯,像你中有我,我中有你這種情況要盡可能的避免。
比如,我手上的項目通過功能歸類劃分,就可以分為接收消息、處理消息、發送消息、其他功能處理,由此便可以考慮劃分出4個線程去處理。
但是,考慮到項目中使用的是CAN通信的方式,接收消息就可以考慮使用CAN接收中斷的方式,能夠做到及時的響應接收消息,所以這個時候只需要3個線程即可。并且消息的接收使用隊列的方式接收,方便管理消息和進行線程之間的同步。消息的發送也采用先壓入隊列再發送的方式。
freeRTOS中創建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);
// 發送消息的處理線程
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);
消息接收隊列、消息發送隊列的創建,如下:
// 消息接收隊列
QueueHandle_t CanRxQueue;
CanRxQueue = xQueueCreate(xxxxxx, xxxxxx);
// 消息發送隊列
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中,查詢接收的話,要考慮消息接收是否及時,接收消息的線程優先級要比較高,否則容易造成處理的動作的延遲。
2.2、消息的處理線程
消息處理的線程任務函數如下:
void Can_HandleMsg_Task(void *pvParameters)
{
while (1)
{
xQueueReceive(CanRxQueue, xxxxxx, portMAX_DELAY);
/*
處理部分
*/
}
}
消息處理中使用了消息隊列的阻塞的特性,在隊列為空的時候阻塞掛起線程,可以減少CPU調度線程的壓力;當消息隊列不為空的時候,隊列不再阻塞,線程從掛起中恢復,參與調度并處理任務。
注意:在freeRTOS中可以用于阻塞的還有信號量、事件標志組、消息郵箱。
- 消息的發送線程
消息的發送如下:
void Can_SendMsg_Task(void *pvParameters)
{
while (1)
{
xQueueReceive(CanTxQueue, xxxxxx, portMAX_DELAY);
/*
處理部分
*/
}
}
消息的發送中也使用了消息隊列,需要發送的消息可以先壓入隊列,然后由發送線程去發送。同樣使用隊列的阻塞特性,在隊列為空的時候阻塞掛起發送線程,減少CPU調度線程的壓力;當發送消息的隊列不為空的時候,隊列不再阻塞,線程從掛起中恢復,參與調度并將消息發送出去。
- 其他功能的處理線程
void OtherHandle_Task(void *pvParameters)
{
while (1)
{
/*
處理部分
*/
}
}
其他功能的處理就放在其他任務線程中處理,比如GUI顯示、按鍵掃描、和傳感器通信等等的。具體需要幾個線程管理需要根據實際的項目情況進行安排。另外各個線程的優先級也要根據情況進行安排,確保重要的功能部分能被及時的執行到!
-
單片機
+關注
關注
6061文章
44866瀏覽量
645981 -
框架
+關注
關注
0文章
404瀏覽量
17771 -
RTOS
+關注
關注
24文章
838瀏覽量
120682 -
代碼
+關注
關注
30文章
4886瀏覽量
70175
發布評論請先 登錄
聊聊我對單片機程序的整體框架設計的一些思路體會
單片機資源少但是不能拒絕RTOS
單片機資源這么少為什么還要用RTOS

單片機就那點資源,為啥還要用RTOS?

為什么單片機的代碼在Flash中運行,單片機的代碼運行位置跟電腦有什么不同?

新唐單片機代碼評審總結

評論