# FreeRTOS project 探討 ###### tags: `FreeRTOS 探討` . ./start_qemu.sh image.bin -debug gdb-multiarch -ex="target remote localhost:1234" ./image.elf \Wedge https://github.com/jkovacic/FreeRTOS-GCC-ARM926ejs/blob/master/drivers/uart.c https://hkt999.medium.com/%E4%BD%BF%E7%94%A8-qemu-freertos-visual-studio-code-%E4%BE%86%E9%96%8B%E7%99%BC%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%B3%BB%E7%B5%B1-204c5483f62 1.mag char 是甚麼 (資料結構) print.h 2.為甚麼其中一個要queue print.h queue誰接誰 print.h 一個守門人任務,等待消息出現在打印隊列中並打印它們。 這可以防止在實際嘗試打印的任務被搶占時損壞打印的消息。 ```c= void printGateKeeperTask(void* params) { portCHAR* message; for ( ; ; ) { /* The task is blocked until something appears in the queue */ xQueueReceive(printQueue, (void*) &message, portMAX_DELAY); /* Print the message in the queue */ uart_print(printUartNr, message); } /* if it ever breaks out of the infinite loop... */ vTaskDelete(NULL); /* suppress a warning since 'params' is ignored */ (void) params; } ``` 以Thread-safe的方式打印訊息 - 即使調用任務被搶占, 也會打印整個訊息。 * [Thread-safe class](https://fu-sheng-wang.blogspot.com/2016/12/java-concurrency-1-thread-safe.html) 可以定義成:如果一個class instance在多個threads存取他的過程中,所有可能的執行緒的存取順序都能產出spec定義的行為,而且存取他的threads並不用做任何synchronization之類的工程,那這個class就可以說是thread safe ```c= void vPrintMsg(const portCHAR* msg) { if ( NULL != msg ) { xQueueSendToBack(printQueue, (void*) &msg, 0); } } ``` 4.arm 架構(暫存器) 中斷怎麼設定? interrupt.h 5.freeRTOS memory management ? (heap)記憶體回收機制 FreeRTOS 創建任務、Queue、semaphore等的時候有兩種方法、一種是動態申請所需的RAM、一種是由用戶自訂義所需的RAM,這種方法也稱靜態方法。 標準函數庫中的malloc()和free()函數有些時候能夠用於完成這個任務,但是: * 在嵌入式系統中,它們並不總是可以使用的; * 它們會佔用更多寶貴的代碼空間; * 它們沒有線程保護; * 它們不具有確定性(每次調用執行的時間可能會不同); FreeRTOS提供的內存分配方案分別位於不同的源文件(heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c)之中 #### heap_1 * 用於從不會刪除任務、隊列、信號量、互斥量等的應用程序(實際上大多數使用FreeRTOS的應用程序都符合這個條件) * 執行時間是確定的並且不會產生內存碎片 * 實現和分配過程非常簡單,需要的內存是從一個靜態數組中分配的,意味著這種內存分配通常只是適用於那些不進行動態內存分配的應用 **沒有提供釋放的函數** ```c= void *pvPortMalloc( size_t xWantedSize ) { void *pvReturn = NULL; static uint8_t *pucAlignedHeap = NULL; /* Ensure that blocks are always aligned to the required number of bytes. */ #if( portBYTE_ALIGNMENT != 1 ) { if( xWantedSize & portBYTE_ALIGNMENT_MASK ) { /* Byte alignment required. */ xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); } } #endif vTaskSuspendAll(); { if( pucAlignedHeap == NULL ) { /* Ensure the heap starts on a correctly aligned boundary. */ pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); } /* Check there is enough room left for the allocation. */ if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) && ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */ { /* Return the next free byte then increment the index past this block. */ pvReturn = pucAlignedHeap + xNextFreeByte; xNextFreeByte += xWantedSize; } traceMALLOC( pvReturn, xWantedSize ); } ( void ) xTaskResumeAll(); #if( configUSE_MALLOC_FAILED_HOOK == 1 ) { if( pvReturn == NULL ) { extern void vApplicationMallocFailedHook( void ); vApplicationMallocFailedHook(); } } #endif return pvReturn; } ``` 步驟: 1. 是否進行記憶體對齊(Alignment)? [為什麼要對齊?](http://opass.logdown.com/posts/743054-about-memory-alignment) xWantedSize 必須與8 Byte對齊,如果沒對齊就必須進行對齊處理 2. 調用vTaskSuspendAll()掛起所有的Task,因為在申請記憶體過程中要做保護,不能被其他任務打斷 3. 確保記憶體堆的可用起始位字也是8 byte對齊的,ucHeap代表申請記憶體的起始位子(這邊由編譯器去分配),假設沒有對齊的情況,則使用pucAlignedHeap表示(由公式去計算) ![](https://i.imgur.com/HbWpF4o.png) 4. 檢查一下可用的記憶體是否足夠分配,使用xNextFreeByte這個全局變數,用來保存pucAlignedHeap到記憶體堆之間的偏移值 ![](https://i.imgur.com/ygNwjKQ.png) 5. 如果記憶體分配並不會產生越界,將申請到記憶體起始位字傳給pvReturn,並更新xNextFreeByte,申請失敗就反回NULL, 6. 用vTaskResumeAll()恢復所有的Task #### Heap_2 heap_2 加入vPortFree() 函數來釋放申請的記憶體,並使用單向鏈結連接起來,也就是說在申請內存時,便從這些鏈結串列表中尋找最小可滿足需求的記憶體塊進行分配 [參考資料](https://www.freertos.org/a00111.html) #### 可延伸閱讀之項目 [C語言之前置處理器](https://hackmd.io/@sysprog/c-preprocessor?type=view) IRQ實際流程的部分需要搞懂 ###### tags: FreeRTOS 探討