STM32CubeMX學習筆記(33)——FreeRTOS實時操作系統使用(軟件定時器)

Leung_ManWah 2022-01-08 07:24:45 阅读数:511

stm32cubemx stm cubemx freertos 操作

一、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 版本足以。

二、新建工程

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個:

  1. HAL的時基,SYS Timebase Source
  2. 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: 設置空閑任務的最小任務堆棧大小,以字為單比特,而不是字節。如該值設置為128 Words,那麼真正的堆棧大小就是 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 創建軟件定時器Timer

要想使用軟件定時器必須在 Config parameters 中把 USE_TIMERS 選擇 Enabled 來使能。

Timers and Semaphores 進行配置。

我們創建兩個定時器,一個周期定時器,一個單次定時器。

  • Timer Name: 定時器名稱
  • Callback: 回調函數名稱
  • Type: 定時器類型,osTimerPeriodic周期定時器,osTimerOnce單次定時器
  • Code Generation Option: 代碼生成選項
  • Parameter: 回調函數形參,不用的時候配置為0或NULL即可
  • Allocation: 分配方式:Dynamic 動態內存創建
  • Conrol Block Name: 控制塊名稱

五、UART串口打印

查看 STM32CubeMX學習筆記(6)——USART串口使用

六、生成代碼

輸入項目名和項目路徑

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

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

點擊 GENERATE CODE 生成代碼

七、軟件定時器

7.1 基本概念

軟件定時器在被創建之後,當經過設定的時鐘計數值後會觸發用戶定義的回調函數。 定時精度與系統時鐘的周期有關。一般系統利用 SysTick 作為軟件定時器的基礎時鐘,軟件定時器的回調函數類似硬件的中斷服務函數,所以,回調函數也要快進快出,而且回調函數中不能有任何阻塞任務運行的情况(軟件定時器回調函數的上下文環境是任務),比如 vTaskDelay() 以及其它能阻塞任務運行的函數,兩次觸發回調函數的時間間隔 xTimerPeriodInTicks 叫定時器的定時周期。

FreeRTOS 提供的軟件定時器支持單次模式和周期模式,單次模式和周期模式的定時時間到之後都會調用軟件定時器的回調函數,用戶可以在回調函數中加入要執行的工程代碼。

  • 單次模式:當用戶創建了定時器並啟動了定時器後,定時時間到了,只執行一次回調函數之後就將該定時器删除,不再重新執行。
  • 周期模式:這個定時器會按照設置的定時時間循環執行回調函數,直到用戶將定時器删除。

    FreeRTOS 通過一個 prvTimerTask 任務(也叫守護任務 Daemon)管理軟定時器,它是在啟動調度器時自動創建的,為了滿足用戶定時需求。prvTimerTask 任務會在其執行期間檢查用戶啟動的時間周期溢出的定時器,並調用其回調函數。只有設置 FreeRTOSConfig.h 中的宏定義configUSE_TIMERS 設置為 1 ,將相關代碼編譯進來,才能正常使用軟件定時器相關功能。

7.2 時間精度

在操作系統中,通常軟件定時器以系統節拍周期為計時單比特。系統節拍是系統的心跳節拍,錶示系統時鐘的頻率,就類似人的心跳,1s 能跳動多少下,系統節拍配置為 configTICK_RATE_HZ,該宏在 FreeRTOSConfig.h 中有定義,默認是 1000。那麼系統的時鐘節拍周期就為 1ms(1s 跳動 1000 下,每一下就為 1ms)。軟件定時器的所定時數值必須是這個節拍周期的整數倍,例如節拍周期是 10ms,那麼上層軟件定時器定時數值只能是10ms,20ms,100ms 等,而不能取值為 15ms。由於節拍定義了系統中定時器能够分辨的精確度,系統可以根據實際系統 CPU 的處理能力和實時性需求設置合適的數值,系統節拍周期的值越小,精度越高,但是系統開銷也將越大,因為這代錶在 1 秒中系統進入時鐘中斷的次數也就越多。

7.3 注意要點

  • 軟件定時器的回調函數中應快進快出,絕對不允許使用任何可能引軟件定時器起任務掛起或者阻塞的 API 接口,在回調函數中也絕對不允許出現死循環。
  • 軟件定時器使用了系統的一個隊列和一個任務資源,軟件定時器任務的優先級默
    認為 configTIMER_TASK_PRIORITY,為了更好響應,該優先級應設置為所有任務中最高的優先級。
  • 創建單次軟件定時器,該定時器超時執行完回調函數後,系統會自動删除該軟件定時器,並回收資源。
  • 定時器任務的堆棧大小默認為 configTIMER_TASK_STACK_DEPTH 個字節。

八、相關API說明

8.1 osTimerCreate

創建一個軟件定時器,並返回一個定時器ID。

函數 osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument)
參數 timer_def: 引用由osTimerDef定義的定時器

type: 設置為 osTimerPeriodic,那麼軟件定時器的工作模式就是周期模式, 一直會以用戶指定的 xTimerPeriod 周期去執行回調函數。如果設置為 osTimerOnce,那麼軟件定時器就在用戶指定的 xTimerPeriod 周期下運行一次後就進入休眠態

argument: 回調函數傳入形參
返回值 成功返回定時器ID,失敗返回0

8.2 osTimerStart

啟動軟件定時器。該函數可以在中斷中使用。

函數 osStatus osTimerStart (osTimerId timer_id, uint32_t millisec)
參數 timer_id: 定時器ID

millisec: 用戶指定的超時時間,單比特為系統節拍周期(即 tick),如果在 FreeRTOS 調度器開啟之前調用 osTimerStart(),該形參將不起作用
返回值 錯誤碼

8.3 osTimerStop

停止一個軟件定時器,讓其進入休眠態。該函數可以在中斷中使用。

函數 osStatus osTimerStop (osTimerId timer_id)
參數 timer_id: 定時器ID
返回值 錯誤碼

8.4 osTimerDelete

用於删除一個已經被創建成功的軟件定時器,删除之後就無法使用該定時器,並且定時器相應的資源也會被系統回收釋放。

函數 osStatus osTimerDelete (osTimerId timer_id)
參數 timer_id: 定時器ID
返回值 錯誤碼

九、示例

/* 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;
osTimerId Swtmr1Handle;
osTimerId Swtmr2Handle;
/* USER CODE BEGIN PV */
static uint32_t TmrCb_Count1 = 0; /* 記錄軟件定時器 1 回調函數執行次數 */
static uint32_t TmrCb_Count2 = 0; /* 記錄軟件定時器 2 回調函數執行次數 */
/* 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 Swtmr1_Callback(void const * argument);
void Swtmr2_Callback(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 */
/* USER CODE END 2 */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* Create the timer(s) */
/* definition and creation of Swtmr1 */
osTimerDef(Swtmr1, Swtmr1_Callback);
Swtmr1Handle = osTimerCreate(osTimer(Swtmr1), osTimerPeriodic, NULL);
/* definition and creation of Swtmr2 */
osTimerDef(Swtmr2, Swtmr2_Callback);
Swtmr2Handle = osTimerCreate(osTimer(Swtmr2), osTimerOnce, NULL);
/* 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);
/* 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 */
osTimerStart(Swtmr1Handle, 1000);
osTimerStart(Swtmr2Handle, 5000);
/* Infinite loop */
for(;;)
{

osDelay(1);
}
/* USER CODE END 5 */
}
/* Swtmr1_Callback function */
void Swtmr1_Callback(void const * argument)
{

/* USER CODE BEGIN Swtmr1_Callback */
TickType_t tick_num1;
TmrCb_Count1++; /* 每回調一次加一 */
tick_num1 = osKernelSysTick(); /* 獲取滴答定時器的計數值 */
printf("swtmr1_callback %d\n", TmrCb_Count1);
printf("tick_num=%d\n", tick_num1);
/* USER CODE END Swtmr1_Callback */
}
/* Swtmr2_Callback function */
void Swtmr2_Callback(void const * argument)
{

/* USER CODE BEGIN Swtmr2_Callback */
TickType_t tick_num2;
TmrCb_Count2++; /* 每回調一次加一 */
tick_num2 = osKernelSysTick(); /* 獲取滴答定時器的計數值 */
printf("swtmr2_callback %d\n", TmrCb_Count2);
printf("tick_num=%d\n", tick_num2);
/* USER CODE END Swtmr2_Callback */
}
/** * @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/1n9Qn1atCu_HCaWInX6b0GA 提取碼:1kar

十一、注意事項

用戶代碼要加在 USER CODE BEGIN NUSER CODE END N 之間,否則下次使用 STM32CubeMX 重新生成代碼後,會被删除。


• 由 Leung 寫於 2022 年 1 月 4 日

• 參考:STM32CubeMX FreeRTOS軟件定時器實驗
STM32CubeIDE(十一):FreeRTOS選項中Disable、CMSIS_V1和CMSIS_V2的區別
HAL庫中的 SYS Timebase Source 和 SysTick_Handler()

版权声明:本文为[Leung_ManWah]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201080724444817.html