一、FreeRTOS簡介
FreeRTOS 是一個可裁剪、可剝奪型的多任務內核,而且沒有任務數限制,FreeRTOS 提供了實時作業系統所需的所有功能,包括資源管理、同步、任務通信等,
FreeRTOS 是用 C 和匯編來寫的,其中絕大部分都是用 C 語言撰寫的,只有極少數的與處理器密切相關的部分代碼才是用匯撰寫的,FreeRTOS 結構簡潔,可讀性很強!最主要的是非常適合初次接觸嵌入式實時作業系統學生、嵌入式系統開發人員和愛好者學習,
最新版本 V9.0.0(2016年),盡管現在 FreeRTOS 的版本已經更新到 V10.4.1 了,但是我們還是選擇 V9.0.0,因為內核很穩定,并且網上資料很多,因為 V10.0.0 版本之后是亞馬遜收購了FreeRTOS之后才出來的版本,主要添加了一些云端組件,一般采用 V9.0.0 版本足以,
- FreeRTOS官網:http://www.freertos.org/
- 代碼托管網站:https://sourceforge.net/projects/freertos/files/FreeRTOS/
二、新建工程
1. 打開 STM32CubeMX 軟體,點擊“新建工程”

2. 選擇 MCU 和封裝

3. 配置時鐘
RCC 設定,選擇 HSE(外部高速時鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)

選擇 Clock Configuration,配置系統時鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 后,輸入回車,軟體會自動修改所有配置

4. 配置除錯模式
非常重要的一步,否則會造成第一次燒錄程式后續無法識別除錯器
SYS 設定,選擇 Debug 為 Serial Wire

三、SYS Timebase Source
在 System Core 中選擇 SYS ,對 Timebase Source 進行設定,選擇 TIM1 作為HAL庫的時基(除了 SysTick 外都可以),

在基于STM32 HAL的專案中,一般需要維護的 “時基” 主要有2個:
- HAL的時基,SYS Timebase Source
- OS的時基(僅在使用OS的情況下才考慮)
而這些 “時基” 該去如何維護,主要分為兩種情況考慮:
-
裸機運行:
可以通過SysTick(滴答定時器)或 (TIMx)定時器 的方式來維護SYS Timebase Source,也就是HAL庫中的uwTick,這是HAL庫中維護的一個全域變數,在裸機運行的情況下,我們一般選擇默認的SysTick(滴答定時器) 方式即可,也就是直接放在SysTick_Handler()中斷服務函式中來維護, -
帶OS運行:
前面提到的SYS Timebase Source是STM32的HAL庫中的新增部分,主要用于實作HAL_Delay()以及作為各種 timeout 的時鐘基準,在使用了OS(作業系統)之后,OS的運行也需要一個時鐘基準(簡稱“時基”),來對任務和時間等進行管理,而OS的這個 時基 一般也都是通過
SysTick(滴答定時器) 來維護的,這時就需要考慮 “HAL的時基” 和 “OS的時基” 是否要共用SysTick(滴答定時器) 了,如果共用SysTick,當我們在CubeMX中選擇啟用FreeRTOS之后,在生成代碼時,CubeMX一定會報如下提示:

強烈建議用戶在使用FreeRTOS的時候,不要使用
SysTick(滴答定時器)作為 “HAL的時基”,因為FreeRTOS要用,最好是要換一個!!!如果共用,潛在一定風險,
四、FreeRTOS
4.1 引數配置
在 Middleware 中選擇 FREERTOS 設定,并選擇 CMSIS_V1 介面版本

CMSIS是一種介面標準,目的是屏蔽軟硬體差異以提高軟體的兼容性,RTOS v1使得軟體能夠在不同的實時作業系統下運行(屏蔽不同RTOS提供的API的差別),而RTOS v2則是拓展了RTOS v1,兼容更多的CPU架構和實時作業系統,因此我們在使用時可以根據實際情況選擇,如果學習程序中使用STM32F1、F4等單片機時沒必要選擇RTOS v2,更高的兼容性背后時更加冗余的代碼,理解起來比較困難,
在 Config parameters 進行具體引數配置,
Kernel settings:
- USE_PREEMPTION:
Enabled:RTOS使用搶占式調度器;Disabled:RTOS使用協作式調度器(時間片), - TICK_RATE_HZ: 值設定為
1000,即周期就是1ms,RTOS系統節拍中斷的頻率,單位為HZ, - MAX_PRIORITIES: 可使用的最大優先級數量,設定好以后任務就可以使用從0到(MAX_PRIORITIES - 1)的優先級,其中0位最低優先級,(MAX_PRIORITIES - 1)為最高優先級,
- MINIMAL_STACK_SIZE: 設定空閑任務的最小任務堆疊大小,以字為單位,而不是位元組,如該值設定為
128Words,那么真正的堆疊大小就是 128*4 = 512 Byte, - MAX_TASK_NAME_LEN: 設定任務名最大長度,
- IDLE_SHOULD_YIELD:
Enabled空閑任務放棄CPU使用權給其他同優先級的用戶任務, - USE_MUTEXES: 為1時使用互斥信號量,相關的API函式會被編譯,
- USE_RECURSIVE_MUTEXES: 為1時使用遞回互斥信號量,相關的API函式會被編譯,
- USE_COUNTING_SEMAPHORES: 為1時啟用計數型信號量, 相關的API函式會被編譯,
- QUEUE_REGISTRY_SIZE: 設定可以注冊的佇列和信號量的最大數量,在使用內核除錯器查看信號量和佇列的時候需要設定此宏,而且要先將訊息佇列和信號量進行注冊,只有注冊了的佇列和信號量才會在內核除錯器中看到,如果不使用內核除錯器的話次宏設定為0即可,
- USE_APPLICATION_TASK_TAG: 為1時可以使用vTaskSetApplicationTaskTag函式,
- ENABLE_BACKWARD_COMPATIBILITY: 為1時可以使V8.0.0之前的FreeRTOS用戶代碼直接升級到V8.0.0之后,而不需要做任何修改,
- USE_PORT_OPTIMISED_TASK_SELECTION: FreeRTOS有兩種方法來選擇下一個要運行的任務,一個是通用的方法,另外一個是特殊的方法,也就是硬體方法,使用MCU自帶的硬體指令來實作,STM32有計算前導零指令嗎,所以這里強制置1,
- USE_TICKLESS_IDLE: 置1:使能低功耗tickless模式;置0:保持系統節拍(tick)中斷一直運行,假設開啟低功耗的話可能會導致下載出現問題,因為程式在睡眠中,可用ISP下載辦法解決,
- USE_TASK_NOTIFICATIONS: 為1時使用任務通知功能,相關的API函式會被編譯,開啟了此功能,每個任務會多消耗8個位元組,
- RECORD_STACK_HIGH_ADDRESS: 為1時堆疊開始地址會被保存到每個任務的TCB中(假如堆疊是向下生長的),
Memory management settings:
- Memory Allocation:
Dynamic/Static支持動態/靜態記憶體申請 - TOTAL_HEAP_SIZE: 設定堆大小,如果使用了動態記憶體管理,FreeRTOS在創建 task, queue, mutex, software timer or semaphore的時候就會使用heap_x.c(x為1~5)中的記憶體申請函式來申請記憶體,這些記憶體就是從堆ucHeap[configTOTAL_HEAP_SIZE]中申請的,
- Memory Management scheme: 記憶體管理策略
heap_4,
Hook function related definitions:
- USE_IDLE_HOOK: 置1:使用空閑鉤子(Idle Hook類似于回呼函式);置0:忽略空閑鉤子,
- USE_TICK_HOOK: 置1:使用時間片鉤子(Tick Hook);置0:忽略時間片鉤子,
- USE_MALLOC_FAILED_HOOK: 使用記憶體申請失敗鉤子函式,
- CHECK_FOR_STACK_OVERFLOW: 大于0時啟用堆疊溢位檢測功能,如果使用此功能用戶必須提供一個堆疊溢位鉤子函式,如果使用的話此值可以為1或者2,因為有兩種堆疊溢位檢測方法,
Run time and task stats gathering related definitions:
- GENERATE_RUN_TIME_STATS: 啟用運行時間統計功能,
- USE_TRACE_FACILITY: 啟用可視化跟蹤除錯,
- USE_STATS_FORMATTING_FUNCTIONS: 與宏configUSE_TRACE_FACILITY同時為1時會編譯下面3個函式prvWriteNameToBuffer()、vTaskList()、vTaskGetRunTimeStats(),
Co-routine related definitions:
- USE_CO_ROUTINES: 啟用協程,
- MAX_CO_ROUTINE_PRIORITIES: 協程的有效優先級數目,
Software timer definitions:
- USE_TIMERS: 啟用軟體定時器,
Interrupt nesting behaviour configuration:
- LIBRARY_LOWEST_INTERRUPT_PRIORITY: 中斷最低優先級,
- LIBRARY_LOWEST_INTERRUPT_PRIORITY: 系統可管理的最高中斷優先級,
4.2 創建互斥量Mutex
在 Mutexes 進行配置,

- Mutex Name: 互斥量名稱
- Allocation: 分配方式:
Dynamic動態記憶體創建 - Conrol Block Name: 控制塊名稱
4.3 創建任務Task
我們創建三個任務,一個高優先級任務,一個中優先級任務,一個低優先級任務,


- Task Name: 任務名稱
- Priority: 優先級,在 FreeRTOS 中,數值越大優先級越高,0 代表最低優先級
- Stack Size (Words): 堆疊大小,單位為字,在32位處理器(STM32),一個字等于4位元組,如果傳入512那么任務大小為512*4位元組
- Entry Function: 入口函式
- Code Generation Option: 代碼生成選項
- Parameter: 任務入口函式形參,不用的時候配置為0或NULL即可
- Allocation: 分配方式:
Dynamic動態記憶體創建 - Buffer Name: 緩沖區名稱
- Conrol Block Name: 控制塊名稱
五、UART串口列印
查看 STM32CubeMX學習筆記(6)——USART串口使用
六、生成代碼
輸入專案名和專案路徑

選擇應用的 IDE 開發環境 MDK-ARM V5

每個外設生成獨立的 ’.c/.h’ 檔案
不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對應的外設檔案, 如 GPIO 初始化代碼生成在 gpio.c 中,

點擊 GENERATE CODE 生成代碼

七、互斥量
7.1 基本概念
互斥量又稱互斥信號量(本質是信號量),是一種特殊的二值信號量,它和信號量不同的是,**它支持互斥量所有權、遞回訪問以及防止優先級翻轉的特性,用于實作對臨界資源的獨占式處理,**任意時刻互斥量的狀態只有兩種,開鎖或閉鎖,當互斥量被任務持有時,該互斥量處于閉鎖狀態,這個任務獲得互斥量的所有權,當該任務釋放這個互斥量時,該互斥量處于開鎖狀態,任務失去該互斥量的所有權,當一個任務持有互斥量時,其他任務將不能再對該互斥量進行開鎖或持有,持有該互斥量的任務也能夠再次獲得這個鎖而不被掛起,這就是遞回訪問,也就是遞回互斥量的特性,這個特性與一般的信號量有很大的不同,在信號量中,由于已經不存在可用的信號量,任務遞回獲取信號量時會發生主動掛起任務最終形成死鎖,
如果想要用于實作同步(任務之間或者任務與中斷之間),二值信號量或許是更好的選擇,雖然互斥量也可以用于任務與任務、任務與中斷的同步,但是互斥量更多的是用于保護資源的互鎖,
用于互鎖的互斥量可以充當保護資源的令牌,當一個任務希望訪問某個資源時,它必須先獲取令牌,當任務使用完資源后,必須還回令牌,以便其它任務可以訪問該資源,是不是很熟悉,在我們的二值信號量里面也是一樣的,用于保護臨界資源,保證多任務的訪問井然有序,當任務獲取到信號量的時候才能開始使用被保護的資源,使用完就釋放信號量,下一個任務才能獲取到信號量從而可用使用被保護的資源,但是**信號量會導致的另一個潛在問題,那就是任務優先級翻轉,**而 FreeRTOS 提供的互斥量可以通過優先級繼承演算法,可以降低優先級翻轉問題產生的影響,所以,用于臨界資源的保護一般建議使用互斥量,
7.2 運作機制

用互斥量處理不同任務對臨界資源的同步訪問時,任務想要獲得互斥量才能進行資源訪問,如果一旦有任務成功獲得了互斥量,則互斥量立即變為閉鎖狀態,此時其他任務會因為獲取不到互斥量而不能訪問這個資源,任務會根據用戶自定義的等待時間進行等待,直到互斥量被持有的任務釋放后,其他任務才能獲取互斥量從而得以訪問該臨界資源,此時互斥量再次上鎖,如此一來就可以確保每個時刻只有一個任務正在訪問這個臨界資源,保證了臨界資源操作的安全性,
7.3 互斥量與遞回互斥量
- 互斥量更適合于可能會引起優先級翻轉的情況,
- 遞回互斥量更適用于任務可能會多次獲取互斥量的情況下,這樣可以避免同一任務多次遞回持有而造成死鎖的問題,
八、相關API說明
8.1 osMutexCreate
用于創建一個互斥量,并回傳一個互斥量ID,
| 函式 | osMutexId osMutexCreate (const osMutexDef_t *mutex_def) |
|---|---|
| 引數 | mutex_def: 參考由osMutexDef定義的互斥量 |
| 回傳值 | 成功回傳互斥量ID,失敗回傳0 |
8.2 osRecursiveMutexCreate
用于創建一個遞回互斥量,不是遞回的互斥量由函式 osMutexCreate() 創建,且只能被同一個任務獲取一次,如果同一個任務想再次獲取則會失敗,遞回信號量則相反,它可以被同一個任務獲取很多次,獲取多少次就需要釋放多少次,遞回信號量與互斥量一樣,都實作了優先級繼承機制,可以減少優先級反轉的反生,
| 函式 | osMutexId osRecursiveMutexCreate (const osMutexDef_t *mutex_def) |
|---|---|
| 引數 | mutex_def: 參考由osMutexDef定義的互斥量 |
| 回傳值 | 成功回傳互斥量ID,失敗回傳0 |
要想使用該函式必須在 Config parameters 中把 USE_RECURSIVE_MUTEXES 選擇 Enabled 來使能,

8.3 osMutexDelete
用于洗掉一個互斥量,
| 函式 | osStatus osMutexDelete (osMutexId mutex_id) |
|---|---|
| 引數 | mutex_id: 互斥量ID |
| 回傳值 | 錯誤碼 |
8.4 osMutexWait
用于獲取互斥量,但是遞回互斥量并不能使用這個 API 函式獲取,
| 函式 | osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec) |
|---|---|
| 引數 | mutex_id: 互斥量ID **millisec:**等待信號量可用的最大超時時間,單位為 tick(即系統節拍周期),如果宏 INCLUDE_vTaskSuspend 定義為 1 且形參 xTicksToWait 設定為 portMAX_DELAY ,則任務將一直阻塞在該信號量上(即沒有超時時間) |
| 回傳值 | 錯誤碼 |
8.5 osRecursiveMutexWait
用于獲取遞回互斥量的宏,與互斥量的獲取函式一樣,osMutexWait()也是一個宏定義,它最終使用現有的佇列機制,實際執行的函式是 xQueueTakeMutexRecursive() , 獲取遞回互斥量之前必須由 osRecursiveMutexCreate() 這個函式創建,要注意的是該函式不能用于獲取由函式 osMutexCreate() 創建的互斥量,
| 函式 | osStatus osRecursiveMutexWait (osMutexId mutex_id, uint32_t millisec) |
|---|---|
| 引數 | mutex_id: 互斥量ID **millisec:**等待信號量可用的最大超時時間,單位為 tick(即系統節拍周期),如果宏 INCLUDE_vTaskSuspend 定義為 1 且形參 xTicksToWait 設定為 portMAX_DELAY ,則任務將一直阻塞在該信號量上(即沒有超時時間) |
| 回傳值 | 錯誤碼 |
要想使用該函式必須在 Config parameters 中把 USE_RECURSIVE_MUTEXES 選擇 Enabled 來使能,

8.6 osMutexRelease
用于釋放互斥量,但不能釋放由函式 osRecursiveMutexCreate() 創建的遞回互斥量,
| 函式 | osStatus osMutexRelease (osMutexId mutex_id) |
|---|---|
| 引數 | mutex_id: 互斥量ID |
| 回傳值 | 錯誤碼 |
8.7 osRecursiveMutexRelease
用于釋放一個遞回互斥量,已經獲取遞回互斥量的任務可以重復獲取該遞回互斥量,使用 osRecursiveMutexWait() 函式成功獲取幾次遞回互斥量,就要使用 osRecursiveMutexRelease() 函式返還幾次,在此之前遞回互斥量都處于無效狀態,別的任務就無法獲取該遞回互斥量,使用該函式介面時,只有已持有互斥量所有權的任務才能釋放它,每釋放一該遞回互斥量,它的計數值就減 1,當該互斥量的計數值為 0 時(即持有任務已經釋放所有的持有操作),互斥量則變為開鎖狀態,等待在該互斥量上的任務將被喚醒,如果任務的優先級被互斥量的優先級翻轉機制臨時提升,那么當互斥量被釋放后,任務的優先級將恢復為原本設定的優先級,
| 函式 | osStatus osRecursiveMutexRelease (osMutexId mutex_id) |
|---|---|
| 引數 | mutex_id: 互斥量ID |
| 回傳值 | 錯誤碼 |
要想使用該函式必須在 Config parameters 中把 USE_RECURSIVE_MUTEXES 選擇 Enabled 來使能,

九、示例
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;
osThreadId defaultTaskHandle;
osThreadId LowPriorityHandle;
osThreadId MidPriorityHandle;
osThreadId HighPriorityHandle;
osMutexId MuxSemHandle;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
void StartDefaultTask(void const * argument);
void LowPriorityTask(void const * argument);
void MidPriorityTask(void const * argument);
void HighPriorityTask(void const * argument);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("The default value of parking space is 5, Press key1 to apply for parking space, and press key2 to release parking space!\n\n");
/* USER CODE END 2 */
/* Create the mutex(es) */
/* definition and creation of MuxSem */
osMutexDef(MuxSem);
MuxSemHandle = osMutexCreate(osMutex(MuxSem));
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* definition and creation of defaultTask */
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
/* definition and creation of LowPriority */
osThreadDef(LowPriority, LowPriorityTask, osPriorityIdle, 0, 128);
LowPriorityHandle = osThreadCreate(osThread(LowPriority), NULL);
/* definition and creation of MidPriority */
osThreadDef(MidPriority, MidPriorityTask, osPriorityIdle, 0, 128);
MidPriorityHandle = osThreadCreate(osThread(MidPriority), NULL);
/* definition and creation of HighPriority */
osThreadDef(HighPriority, HighPriorityTask, osPriorityIdle, 0, 128);
HighPriorityHandle = osThreadCreate(osThread(HighPriority), NULL);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
/* DMA1_Channel5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, LED_G_Pin|LED_B_Pin|LED_R_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : KEY2_Pin */
GPIO_InitStruct.Pin = KEY2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(KEY2_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : KEY1_Pin */
GPIO_InitStruct.Pin = KEY1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : LED_G_Pin LED_B_Pin LED_R_Pin */
GPIO_InitStruct.Pin = LED_G_Pin|LED_B_Pin|LED_R_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/**
* @brief 重定向c庫函式printf到USARTx
* @retval None
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* @brief 重定向c庫函式getchar,scanf到USARTx
* @retval None
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
/* USER CODE END 4 */
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_LowPriorityTask */
/**
* @brief Function implementing the LowPriority thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_LowPriorityTask */
void LowPriorityTask(void const * argument)
{
/* USER CODE BEGIN LowPriorityTask */
static uint32_t i;
osStatus xReturn;
/* Infinite loop */
for(;;)
{
printf("LowPriority_Task Get Mutex\n");
//獲取互斥量 MuxSem,沒獲取到則一直等待
xReturn = osMutexWait(MuxSemHandle, /* 互斥量句柄 */
osWaitForever); /* 等待時間 */
if(osOK == xReturn)
{
printf("LowPriority_Task Runing\n\n");
}
for(i = 0; i < 2000000; i++)
{ //模擬低優先級任務占用互斥量
taskYIELD();//發起任務調度
}
printf("LowPriority_Task Release Mutex\r\n");
xReturn = osMutexRelease(MuxSemHandle);//給出互斥量
osDelay(1000);
}
/* USER CODE END LowPriorityTask */
}
/* USER CODE BEGIN Header_MidPriorityTask */
/**
* @brief Function implementing the MidPriority thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_MidPriorityTask */
void MidPriorityTask(void const * argument)
{
/* USER CODE BEGIN MidPriorityTask */
/* Infinite loop */
for(;;)
{
printf("MidPriority_Task Runing\n");
osDelay(1000);
}
/* USER CODE END MidPriorityTask */
}
/* USER CODE BEGIN Header_HighPriorityTask */
/**
* @brief Function implementing the HighPriority thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_HighPriorityTask */
void HighPriorityTask(void const * argument)
{
/* USER CODE BEGIN HighPriorityTask */
osStatus xReturn;
/* Infinite loop */
for(;;)
{
printf("HighPriority_Task Get Mutex\n");
//獲取互斥量 MuxSem,沒獲取到則一直等待
xReturn = osMutexWait(MuxSemHandle, /* 互斥量句柄 */
osWaitForever); /* 等待時間 */
if(osOK == xReturn)
{
printf("HighPriority_Task Runing\n");
}
printf("HighPriority_Task Release Mutex\r\n");
xReturn = osMutexRelease(MuxSemHandle);//給出互斥量
osDelay(1000);
}
/* USER CODE END HighPriorityTask */
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM1 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM1) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
查看列印:

十、工程代碼
鏈接:https://pan.baidu.com/s/1X3erVkBowNZrdMIWbzciQg 提取碼:amcv
十一、注意事項
用戶代碼要加在 USER CODE BEGIN N 和 USER CODE END N 之間,否則下次使用 STM32CubeMX 重新生成代碼后,會被洗掉,

? 由 Leung 寫于 2021 年 12 月 30 日
? 參考:STM32CubMx+FreeRTOS互斥鎖和遞回互斥鎖(五)
STM32CubeIDE(十一):FreeRTOS選項中Disable、CMSIS_V1和CMSIS_V2的區別
HAL庫中的 SYS Timebase Source 和 SysTick_Handler()
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/398499.html
標籤:其他
上一篇:STM32(2.1)——GPIO

