繼續學習freertos消息隊列

来源:https://www.cnblogs.com/iot-dev/archive/2019/10/15/11681049.html
-Advertisement-
Play Games

寫在前面:傑傑這個月很忙~所以並沒有時間更新,現在健身房閉館裝修,晚上有空就更新一下!其實在公眾號沒更新的這段日子,每天都有兄弟在來關註我的公眾號,這讓我受寵若驚,在這裡謝謝大家的支持啦!!謝謝^ 在這裡我們就跟著火哥的書來學習一下FreeRTOS的消息隊列,這本書我覺得寫得很好,基本都講解到了,關 ...


寫在前面:傑傑這個月很忙~所以並沒有時間更新,現在健身房閉館裝修,晚上有空就更新一下!其實在公眾號沒更新的這段日子,每天都有兄弟在來關註我的公眾號,這讓我受寵若驚,在這裡謝謝大家的支持啦!!謝謝^

在這裡我們就跟著火哥的書來學習一下FreeRTOS的消息隊列,這本書我覺得寫得很好,基本都講解到了,關於什麼是消息隊列,就請大家去看書,基礎知識我暫時不說了。

聲明:本書絕大部分內容來自《FreeRTOS 內核實現與應用開發實戰指南—基於野火 STM32 全系列(M3/4/7)開發板》,如涉及侵權請聯繫傑傑刪除

FreeRTOS的消息隊列支持

  • FreeRTOS 中使用隊列數據結構實現任務非同步通信工作,具有如下特性:
  • 消息支持先進先出方式排隊,支持非同步讀寫工作方式。
  • 讀寫隊列均支持超時機制。
  • 消息支持後進先出方式排隊, 往隊首發送消息(LIFO) 。
  • 可以允許不同長度(不超過隊列節點最大值)的任意類型消息。
  • 一個任務能夠從任意一個消息隊列接收和發送消息。
  • 多個任務能夠從同一個消息隊列接收和發送消息。
  • 當隊列使用結束後,可以通過刪除隊列函數進行刪除。

FreeRTOS隊列的特點

一般來說,魚與熊掌不可兼得,如果數據太多,那數據傳輸的速度必然是會慢下來,而如果採用引用傳遞的方式,當原始數據被修改的時候,數據有變得不安全,但是FreeRTOS支持拷貝與引用的方式進行數據的傳輸,變得更加靈活。
隊列是通過拷貝傳遞數據的,但這並不妨礙隊列通過引用來傳遞數據。當信息的大小到達一個臨界點後,逐位元組拷貝整個信息是不實際的,可以定義一個指向數據區域的指針,將指針傳遞即可。這種方法在物聯網中是非常常用的。

消息隊列控制塊

其實消息隊列不僅僅是用於當做消息隊列,FreeRTOS還把他當做信號量的數據結構來使用

typedef struct QueueDefinition
{
    int8_t *pcHead;             /* 指向隊列存儲區起始位置,即第一個隊列項 */
    int8_t *pcTail;             /* 指向隊列存儲區結束後的下一個位元組 */
    int8_t *pcWriteTo;          /* 指向下隊列存儲區的下一個空閑位置 */
 
 
    union                       /* 使用聯合體用來確保兩個互斥的結構體成員不會同時出現 */
    {
        int8_t *pcReadFrom;     /* 當結構體用於隊列時,這個欄位指向出隊項目中的最後一個. */
        UBaseType_t uxRecursiveCallCount;/* 當結構體用於互斥量時,用作計數器,保存遞歸互斥量被"獲取"的次數. */
    } u;
 
 
    List_t xTasksWaitingToSend;      /* 因為等待入隊而阻塞的任務列表,按照優先順序順序存儲 */
    List_t xTasksWaitingToReceive;   /* 因為等待隊列項而阻塞的任務列表,按照優先順序順序存儲 */
 
 
    volatile UBaseType_t uxMessagesWaiting;/*< 當前隊列的隊列項數目 */
    UBaseType_t uxLength;            /* 隊列項的數目 */
    UBaseType_t uxItemSize;          /* 每個隊列項的大小 */
 
 
    volatile BaseType_t xRxLock;   /* 隊列上鎖後,存儲從隊列收到的列表項數目,如果隊列沒有上鎖,設置為queueUNLOCKED */
    volatile BaseType_t xTxLock;   /* 隊列上鎖後,存儲發送到隊列的列表項數目,如果隊列沒有上鎖,設置為queueUNLOCKED */
 
    /* 刪除部分源碼 */

} xQUEUE;
 
typedef xQUEUE Queue_t;

先過一遍消息隊列的數據結構,其實沒啥東西的,記不住也沒啥大問題,下麵會用到就行了。

創建消息隊列

FreeRTOS創建隊列API函數是xQueueCreate(),但其實這是一個巨集。真正被執行的函數是xQueueGenericCreate(),我們稱這個函數為通用隊列創建函數。

    QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
    {
    Queue_t *pxNewQueue;
    size_t xQueueSizeInBytes;
    uint8_t *pucQueueStorage;

        configASSERT( uxQueueLength > ( UBaseType_t ) 0 );

        if( uxItemSize == ( UBaseType_t ) 0 )
        {
            /* 如果 uxItemSize 為 0,也就是單個消息空間大小為 0,這樣子就不
需要申請記憶體了,那麼 xQueueSizeInBytes 也設置為 0 即可,設置為 0 是可以的,用作信號
量的時候這個就可以設置為 0。*/
            xQueueSizeInBytes = ( size_t ) 0;
        }
        else
        {
            /* 分配足夠消息存儲空間,空間的大小為隊列長度*單個消息大小 */
            xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
        }
        /* FreeRTOS 調用 pvPortMalloc()函數向系統申請記憶體空間,記憶體大
小為消息隊列控制塊大小加上消息存儲空間大小,因為這段記憶體空間是需要保證連續的 */
        pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

        if( pxNewQueue != NULL )
        {
            /* 計算出消息存儲空間的起始地址 */
            pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );

            #if( configSUPPORT_STATIC_ALLOCATION == 1 )
            {
                pxNewQueue->ucStaticallyAllocated = pdFALSE;
            }
            #endif /* configSUPPORT_STATIC_ALLOCATION */

            prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
        }

        return pxNewQueue;
    }

真正的初始化在下麵這個函數中:

BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

    configASSERT( pxQueue );

    taskENTER_CRITICAL();
    {
        /* 消息隊列數據結構的相關初始化 */
        pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
        pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
        pxQueue->pcWriteTo = pxQueue->pcHead;
        pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize );
        pxQueue->cRxLock = queueUNLOCKED;
        pxQueue->cTxLock = queueUNLOCKED;

        if( xNewQueue == pdFALSE )
        {
            if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
            {
                if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
                {
                    queueYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            /* Ensure the event queues start in the correct state. */
            vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
            vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
        }
    }
    taskEXIT_CRITICAL();

    return pdPASS;
}

初始化完成之後,為了讓大家理解,消息隊列是怎麼樣的,就給出一個示意圖,黃色部分是消息隊列的控制塊,而綠色部分則是消息隊列的存放消息的地方,在創建的時候,我們知道的消息隊列長度與單個消息空間大小。
此圖截自野火FreeRTOS書籍

消息隊列發送

任務或者中斷服務程式都可以給消息隊列發送消息,當發送消息時,如果隊列未滿或者允許覆蓋入隊, FreeRTOS 會將消息拷貝到消息隊列隊尾,否則,會根據用戶指定的阻塞超時時間進行阻塞,在這段時間中,如果隊列一直不允許入隊,該任務將保持阻塞狀態以等待隊列允許入隊。當其它任務從其等待的隊列中讀取入了數據(隊列未滿),該任務將自動由阻塞態轉為就緒態。當任務等待的時間超過了指定的阻塞時間,即使隊列中還不允許入隊,任務也會自動從阻塞態轉移為就緒態,此時發送消息的任務或者中斷程式會收到一個錯誤碼 errQUEUE_FULL。
發送緊急消息的過程與發送消息幾乎一樣,唯一的不同是,當發送緊急消息時,發送的位置是消息隊列隊頭而非隊尾,這樣,接收者就能夠優先接收到緊急消息,從而及時進行消息處理。
下麵是消息隊列的發送API介面,函數中有FromISR則表明在中斷中使用的。
消息隊列入隊(發送)的API介面

1 /*-----------------------------------------------------------*/
 2 BaseType_t xQueueGenericSend( QueueHandle_t xQueue,      (1) 
 3                               const void * const pvItemToQueue,  (2)
 4                               TickType_t xTicksToWait,       (3)
 5                               const BaseType_t xCopyPosition )   (4)
 6 {
 7     BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
 8     TimeOut_t xTimeOut;
 9     Queue_t * const pxQueue = ( Queue_t * ) xQueue;
10 
11     /* 已刪除一些斷言操作 */
12 
13     for ( ;; ) {
14         taskENTER_CRITICAL();                    (5)
15         {
16             /* 隊列未滿 */
17             if ( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength )
18                  || ( xCopyPosition == queueOVERWRITE ) ) {  (6) 
19                 traceQUEUE_SEND( pxQueue );
20                 xYieldRequired =
21           prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); (7)
22 
23                 /* 已刪除使用隊列集部分代碼 */
24                 /* 如果有任務在等待獲取此消息隊列 */
25       if ( listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive))==pdFALSE){ (8)
26                     /* 將任務從阻塞中恢復 */
27             if ( xTaskRemoveFromEventList(
28                   &( pxQueue->xTasksWaitingToReceive ) )!=pdFALSE) { (9)
29                         /* 如果恢復的任務優先順序比當前運行任務優先順序還高,
30                         那麼需要進行一次任務切換 */
31                         queueYIELD_IF_USING_PREEMPTION();    (10)
32                     } else {
33                         mtCOVERAGE_TEST_MARKER();
34                     }
35                 } else if ( xYieldRequired != pdFALSE ) {
36                     /* 如果沒有等待的任務,拷貝成功也需要任務切換 */
37                     queueYIELD_IF_USING_PREEMPTION();        (11)
38                 } else {
39                     mtCOVERAGE_TEST_MARKER();
40                 }
41 
42                 taskEXIT_CRITICAL();             (12)
43                 return pdPASS;
44             }
45             /* 隊列已滿 */
46             else {                       (13)
47                 if ( xTicksToWait == ( TickType_t ) 0 ) {
48                     /* 如果用戶不指定阻塞超時時間,退出 */
49                     taskEXIT_CRITICAL();         (14)
50                     traceQUEUE_SEND_FAILED( pxQueue );
51                     return errQUEUE_FULL;
52                 } else if ( xEntryTimeSet == pdFALSE ) { 
53                  /* 初始化阻塞超時結構體變數,初始化進入
54              阻塞的時間xTickCount和溢出次數xNumOfOverflows */
55                     vTaskSetTimeOutState( &xTimeOut );       (15)
56                     xEntryTimeSet = pdTRUE;
57                 } else {
58                     mtCOVERAGE_TEST_MARKER();
59                 }
60             }
61         }
62         taskEXIT_CRITICAL();                 (16)
63         /* 掛起調度器 */
64         vTaskSuspendAll();
65         /* 隊列上鎖 */
66         prvLockQueue( pxQueue );
67 
68         /* 檢查超時時間是否已經過去了 */
69         if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait)==pdFALSE){ (17)
70             /* 如果隊列還是滿的 */
71             if ( prvIsQueueFull( pxQueue ) != pdFALSE ) {    (18)    
72                 traceBLOCKING_ON_QUEUE_SEND( pxQueue );  
73                 /* 將當前任務添加到隊列的等待發送列表中
74                    以及阻塞延時列表,延時時間為用戶指定的超時時間xTicksToWait */
75                 vTaskPlaceOnEventList(
76                    &( pxQueue->xTasksWaitingToSend ), xTicksToWait );(19)
77                 /* 隊列解鎖 */
78                 prvUnlockQueue( pxQueue );           (20)
79 
80                 /* 恢復調度器 */
81                 if ( xTaskResumeAll() == pdFALSE ) {
82                     portYIELD_WITHIN_API();
83                 }
84             } else {
85                 /* 隊列有空閑消息空間,允許入隊 */
86                 prvUnlockQueue( pxQueue );           (21)
87                 ( void ) xTaskResumeAll();
88             }
89         } else {
90             /* 超時時間已過,退出 */
91             prvUnlockQueue( pxQueue );               (22)
92             ( void ) xTaskResumeAll();
93 
94             traceQUEUE_SEND_FAILED( pxQueue );
95             return errQUEUE_FULL;
96         }
97     }
98 }
99 /*-----------------------------------------------------------*/

如果阻塞時間不為 0,任務會因為等待入隊而進入阻塞, 在將任務設置為阻塞的過程中, 系統不希望有其它任務和中斷操作這個隊列的 xTasksWaitingToReceive 列表和 xTasksWaitingToSend 列表,因為可能引起其它任務解除阻塞,這可能會發生優先順序翻轉。比如任務 A 的優先順序低於當前任務,但是在當前任務進入阻塞的過程中,任務 A 卻因為其它原因解除阻塞了,這顯然是要絕對禁止的。因此FreeRTOS 使用掛起調度器禁止其它任務操作隊列,因為掛起調度器意味著任務不能切換並且不准調用可能引起任務切換的 API 函數。但掛起調度器並不會禁止中斷,中斷服務函數仍然可以操作隊列事件列表,可能會解除任務阻塞、可能會進行上下文切換,這也是不允許的。於是,解決辦法是不但掛起調度器,還要給隊列上鎖,禁止任何中斷來操作隊列。
再借用朱工精心製作的流程圖加以理解:圖片出自:https://blog.csdn.net/zhzht19861011/article/details/51510384
消息隊列入隊流程

消息隊列出隊的API函數介面:
在這裡插入圖片描述
消息隊列出隊過程分析,其實跟入隊差不多,請看註釋:

 1 /*-----------------------------------------------------------*/
 2 BaseType_t xQueueGenericReceive( QueueHandle_t xQueue,       (1) 
 3                                  void * const pvBuffer,      (2)
 4                                  TickType_t xTicksToWait,    (3) 
 5                                  const BaseType_t xJustPeeking ) (4)
 6 {
 7     BaseType_t xEntryTimeSet = pdFALSE;
 8     TimeOut_t xTimeOut;
 9     int8_t *pcOriginalReadPosition;
10     Queue_t * const pxQueue = ( Queue_t * ) xQueue;
11 
12     /* 已刪除一些斷言 */
13     for ( ;; ) {
14         taskENTER_CRITICAL();                    (5)
15         {
16             const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; 
17 
18             /* 看看隊列中有沒有消息 */
19             if ( uxMessagesWaiting > ( UBaseType_t ) 0 ) {   (6) 
20                 /*防止僅僅是讀取消息,而不進行消息出隊操作*/
21                 pcOriginalReadPosition = pxQueue->u.pcReadFrom;  (7)
22                 /* 拷貝消息到用戶指定存放區域pvBuffer */
23                 prvCopyDataFromQueue( pxQueue, pvBuffer );   (8)
24 
25                 if ( xJustPeeking == pdFALSE ) {     (9)
26                     /* 讀取消息並且消息出隊 */
27                     traceQUEUE_RECEIVE( pxQueue );   
28 
29                     /* 獲取了消息,當前消息隊列的消息個數需要減一 */
30                     pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;  (10)
31                     /* 判斷一下消息隊列中是否有等待發送消息的任務 */
32                     if ( listLIST_IS_EMPTY(          (11)
33                              &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) {
34                         /* 將任務從阻塞中恢復 */
35                         if ( xTaskRemoveFromEventList(       (12)
36                                  &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) {
37                             /* 如果被恢復的任務優先順序比當前任務高,會進行一次任務切換 */
38                             queueYIELD_IF_USING_PREEMPTION();    (13)
39                         } else {
40                             mtCOVERAGE_TEST_MARKER();
41                         }
42                     } else {
43                         mtCOVERAGE_TEST_MARKER();
44                     }
45                 } else {                 (14)
46                     /* 任務只是看一下消息(peek),並不出隊 */   
47                     traceQUEUE_PEEK( pxQueue );
48 
49                     /* 因為是只讀消息 所以還要還原讀消息位置指針 */
50                     pxQueue->u.pcReadFrom = pcOriginalReadPosition; (15)
51 
52                     /* 判斷一下消息隊列中是否還有等待獲取消息的任務 */
53                     if ( listLIST_IS_EMPTY(          (16)
54                              &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) {
55                         /* 將任務從阻塞中恢復 */
56                         if ( xTaskRemoveFromEventList(           
57                               &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) {
58                             /* 如果被恢復的任務優先順序比當前任務高,會進行一次任務切換 */
59                             queueYIELD_IF_USING_PREEMPTION();    
60                         } else {
61                             mtCOVERAGE_TEST_MARKER();
62                         }
63                     } else {
64                         mtCOVERAGE_TEST_MARKER();
65                     }
66                 }
67 
68                 taskEXIT_CRITICAL();             (17)
69                 return pdPASS;
70             } else {                     (18)
71                 /* 消息隊列中沒有消息可讀 */
72                 if ( xTicksToWait == ( TickType_t ) 0 ) {    (19)    
73                     /* 不等待,直接返回 */
74                     taskEXIT_CRITICAL();
75                     traceQUEUE_RECEIVE_FAILED( pxQueue );
76                     return errQUEUE_EMPTY;
77                 } else if ( xEntryTimeSet == pdFALSE ) {     
78                     /* 初始化阻塞超時結構體變數,初始化進入
79                     阻塞的時間xTickCount和溢出次數xNumOfOverflows */
80                     vTaskSetTimeOutState( &xTimeOut );       (20)
81                     xEntryTimeSet = pdTRUE;
82                 } else {
83                     mtCOVERAGE_TEST_MARKER();
84                 }
85             }
86         }
87         taskEXIT_CRITICAL();                 
88 
89         vTaskSuspendAll();
90         prvLockQueue( pxQueue );             (21)
91 
92         /* 檢查超時時間是否已經過去了*/
93         if ( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) {(22)
94             /* 如果隊列還是空的 */
95             if ( prvIsQueueEmpty( pxQueue ) != pdFALSE ) {
96                 traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );   (23)    
97                 /* 將當前任務添加到隊列的等待接收列表中
98                    以及阻塞延時列表,阻塞時間為用戶指定的超時時間xTicksToWait */
99                 vTaskPlaceOnEventList(               
100                     &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
101                 prvUnlockQueue( pxQueue );
102                 if ( xTaskResumeAll() == pdFALSE ) {        
103                     /* 如果有任務優先順序比當前任務高,會進行一次任務切換 */
104                     portYIELD_WITHIN_API();         
105                 } else {
106                     mtCOVERAGE_TEST_MARKER();
107                 }
108             } else {
109                 /* 如果隊列有消息了,就再試一次獲取消息 */
110                 prvUnlockQueue( pxQueue );          (24)
111                 ( void ) xTaskResumeAll();
112             }
113         } else {
114             /* 超時時間已過,退出 */
115             prvUnlockQueue( pxQueue );              (25)
116             ( void ) xTaskResumeAll();
117 
118             if ( prvIsQueueEmpty( pxQueue ) != pdFALSE ) {
119                 /* 如果隊列還是空的,返回錯誤代碼errQUEUE_EMPTY */
120                 traceQUEUE_RECEIVE_FAILED( pxQueue );
121                 return errQUEUE_EMPTY;              (26)
122             } else {
123                 mtCOVERAGE_TEST_MARKER();
124             }
125         }
126     }
127 }
128 /*-----------------------------------------------------------*/

關註我

歡迎關註我公眾號

更多資料歡迎關註“物聯網IoT開發”公眾號!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、背景 代碼實例:https://gitee.com/D_C_L/CurtainEtcAOP.git我們實際系統中有很多操作,是不管做多少次,都應該產生一樣的效果或返回一樣的結果。 例如: 1. 前端重覆提交選中的數據,應該後臺只產生對應這個數據的一個反應結果。 2. 我們發起一筆付款請求,應該只 ...
  • 本篇主要寫一些 腳本排序工具的使用。 sort 概述 是一個以行為單位對文件內容進行排序的工具,也可以根據不同的數據類型來排序。 用法 sort [選項] 參數 :忽略大小寫 :忽略每行前面的空格 :按照月份進行排序 :按照數字進行排序 :反向排序 :等同於 ,表示相同的數據僅顯示一行 :指定分隔符 ...
  • 本篇主要寫一些 腳本編輯工具 的使用。 概述 是一個功能強大的編輯工具,逐行讀取輸入文本,並根據指定的匹配模式進行查找,對符合條件的內容進行格式化輸出或者過濾處理。 傾向於將一行分成多個欄位然後再進行處理,且預設情況下欄位的分隔符為 或者 鍵。 執行結果可以通過 的功能將欄位數據列印顯示。 可以使用 ...
  • 1.先apt-get update一下當前預設的源,更新完成後先把vim命令安裝一下,再修改源倉庫為阿裡雲,否則無法直接編輯文件 2.先添加阿裡雲的源,編輯文件/etc/apt/sources.list,編輯完再次更新一下 deb http://mirrors.aliyun.com/ubuntu/ ...
  • 作為一個技術純小白,在Linux伺服器初始化MySQL資料庫的時候遇到了一點小問題: ​ 1、不會使用MySQL圖形工具,幾乎沒玩過 ​ 2、客戶的VPN沒有開放3306埠,沒法用navicat等工具連接資料庫 ​ 3、懶的再打開圖形工具,畢竟命令行接近萬能了…… 所以: 方法一、在初始化腳本文件 ...
  • 對稱加密演算法 非對稱加密演算法 單向散列(hash演算法) CA和證書 證書獲取 安全協議 OpenSSL openssl命令 創建CA和申請證書 ...
  • awk介紹 awk的工作原理 awk基本格式 awk之print格式 awk變數 awk之printf awk操作符 awk PATTERN awk action awk控制語句 awk控制語句if-else awk控制語句 while迴圈 awk控制語句do-while迴圈 awk控制語句 for ...
  • 準備 在移植之前,我們首先要獲取到FreeRTOS的官方的源碼包。這裡我們提供兩個下載鏈接: 一個是官網:http://www.freertos.org/ 另外一個是代碼托管網站:https://sourceforge.net/projects/freertos/files/FreeRTOS/ 這裡 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...