赞
踩
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
在 System Core
中选择 SYS
,对 Timebase Source
进行设置,选择 TIM1
作为HAL库的时基(除了 SysTick
外都可以)。
在基于STM32 HAL的项目中,一般需要维护的 “时基” 主要有2个:
而这些 “时基” 该去如何维护,主要分为两种情况考虑:
裸机运行:
可以通过 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要用,最好是要换一个!!!如果共用,潜在一定风险。
在 Middleware
中选择 FREERTOS
设置,并选择 CMSIS_V1
接口版本
CMSIS是一种接口标准,目的是屏蔽软硬件差异以提高软件的兼容性。RTOS v1使得软件能够在不同的实时操作系统下运行(屏蔽不同RTOS提供的API的差别),而RTOS v2则是拓展了RTOS v1,兼容更多的CPU架构和实时操作系统。因此我们在使用时可以根据实际情况选择,如果学习过程中使用STM32F1、F4等单片机时没必要选择RTOS v2,更高的兼容性背后时更加冗余的代码,理解起来比较困难。
在 Config parameters
进行具体参数配置。
Kernel settings:
Enabled
:RTOS使用抢占式调度器;Disabled:RTOS使用协作式调度器(时间片)。1000
,即周期就是1ms。RTOS系统节拍中断的频率,单位为HZ。128
Words,那么真正的堆栈大小就是 128*4 = 512 Byte。Enabled
空闲任务放弃CPU使用权给其他同优先级的用户任务。Memory management settings:
Dynamic/Static
支持动态/静态内存申请heap_4
。Hook function related definitions:
Run time and task stats gathering related definitions:
Co-routine related definitions:
Software timer definitions:
Interrupt nesting behaviour configuration:
在 Mutexes
进行配置。
Dynamic
动态内存创建我们创建三个任务,一个高优先级任务,一个中优先级任务,一个低优先级任务。
Dynamic
动态内存创建查看 STM32CubeMX学习笔记(6)——USART串口使用
输入项目名和项目路径
选择应用的 IDE 开发环境 MDK-ARM V5
每个外设生成独立的 ’.c/.h’
文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。
点击 GENERATE CODE 生成代码
互斥量又称互斥信号量(本质是信号量),是一种特殊的二值信号量,它和信号量不同的是,**它支持互斥量所有权、递归访问以及防止优先级翻转的特性,用于实现对临界资源的独占式处理。**任意时刻互斥量的状态只有两种,开锁或闭锁。当互斥量被任务持有时,该互斥量处于闭锁状态,这个任务获得互斥量的所有权。当该任务释放这个互斥量时,该互斥量处于开锁状态,任务失去该互斥量的所有权。当一个任务持有互斥量时,其他任务将不能再对该互斥量进行开锁或持有。持有该互斥量的任务也能够再次获得这个锁而不被挂起,这就是递归访问,也就是递归互斥量的特性,这个特性与一般的信号量有很大的不同,在信号量中,由于已经不存在可用的信号量,任务递归获取信号量时会发生主动挂起任务最终形成死锁。
如果想要用于实现同步(任务之间或者任务与中断之间),二值信号量或许是更好的选择,虽然互斥量也可以用于任务与任务、任务与中断的同步,但是互斥量更多的是用于保护资源的互锁。
用于互锁的互斥量可以充当保护资源的令牌,当一个任务希望访问某个资源时,它必须先获取令牌。当任务使用完资源后,必须还回令牌,以便其它任务可以访问该资源。是不是很熟悉,在我们的二值信号量里面也是一样的,用于保护临界资源,保证多任务的访问井然有序。当任务获取到信号量的时候才能开始使用被保护的资源,使用完就释放信号量,下一个任务才能获取到信号量从而可用使用被保护的资源。但是**信号量会导致的另一个潜在问题,那就是任务优先级翻转。**而 FreeRTOS 提供的互斥量可以通过优先级继承算法,可以降低优先级翻转问题产生的影响,所以,用于临界资源的保护一般建议使用互斥量。
用互斥量处理不同任务对临界资源的同步访问时,任务想要获得互斥量才能进行资源访问,如果一旦有任务成功获得了互斥量,则互斥量立即变为闭锁状态,此时其他任务会因为获取不到互斥量而不能访问这个资源,任务会根据用户自定义的等待时间进行等待,直到互斥量被持有的任务释放后,其他任务才能获取互斥量从而得以访问该临界资源,此时互斥量再次上锁,如此一来就可以确保每个时刻只有一个任务正在访问这个临界资源,保证了临界资源操作的安全性。
用于创建一个互斥量,并返回一个互斥量ID。
函数 | osMutexId osMutexCreate (const osMutexDef_t *mutex_def) |
---|---|
参数 | mutex_def: 引用由osMutexDef定义的互斥量 |
返回值 | 成功返回互斥量ID,失败返回0 |
用于创建一个递归互斥量,不是递归的互斥量由函数 osMutexCreate() 创建,且只能被同一个任务获取一次,如果同一个任务想再次获取则会失败。递归信号量则相反,它可以被同一个任务获取很多次,获取多少次就需要释放多少次。递归信号量与互斥量一样,都实现了优先级继承机制,可以减少优先级反转的反生。
函数 | osMutexId osRecursiveMutexCreate (const osMutexDef_t *mutex_def) |
---|---|
参数 | mutex_def: 引用由osMutexDef定义的互斥量 |
返回值 | 成功返回互斥量ID,失败返回0 |
要想使用该函数必须在 Config parameters
中把 USE_RECURSIVE_MUTEXES
选择 Enabled
来使能。
用于删除一个互斥量。
函数 | osStatus osMutexDelete (osMutexId mutex_id) |
---|---|
参数 | mutex_id: 互斥量ID |
返回值 | 错误码 |
用于获取互斥量,但是递归互斥量并不能使用这个 API 函数获取。
函数 | osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec) |
---|---|
参数 | mutex_id: 互斥量ID **millisec:**等待信号量可用的最大超时时间,单位为 tick(即系统节拍周期)。如果宏 INCLUDE_vTaskSuspend 定义为 1 且形参 xTicksToWait 设置为 portMAX_DELAY ,则任务将一直阻塞在该信号量上(即没有超时时间) |
返回值 | 错误码 |
用于获取递归互斥量的宏,与互斥量的获取函数一样,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
来使能。
用于释放互斥量,但不能释放由函数 osRecursiveMutexCreate() 创建的递归互斥量。
函数 | osStatus osMutexRelease (osMutexId mutex_id) |
---|---|
参数 | mutex_id: 互斥量ID |
返回值 | 错误码 |
用于释放一个递归互斥量。已经获取递归互斥量的任务可以重复获取该递归互斥量。使用 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()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。