stm32_<一文通>_cubemx_freertos

文章目录

  • 前言
  • 一、任务调度
    • 1.1 延时
      • 1.1.1 相对延时
      • 1.1.2 绝对延时
    • 1.2 挂起和恢复
      • 1.2.1 cmsis的挂起和恢复函数
      • 1.2.2 freertos的挂起和恢复函数
    • 1.3 删除
      • 1.3.1 cmsis的删除任务函数
      • 1.3.2 freertos的删除任务函数
  • 二、Freertos任务与中断
  • 三、消息队列
    • 3.1 写入和读取一个数据
    • 3.2 写入和读取一个段数据 ==(指针)== (分配内存法)
  • 四、二值信号量
  • 五、互斥量
  • 六、事件组
    • 6.1 设置位
    • 6.2 等待位
  • 七、任务通知
    • 7.1 CMSIS-RTOS API 线程标志
    • 7.2 Free-RTOS API 任务通知 ==(待填)==
  • 八、流缓冲区 ==(待填)==
  • 九、消息缓冲区 ==(待填)==
  • 十、软件定时器
    • 10.1 任务中开启定时器
      • 10.1.1 周期定时器
      • 10.1.2单次定时器
    • 10.2 中断中开启定时器 ==(待填)==
  • 十一、空闲任务与低功耗 ==(待填)==
  • 总结


前言

cubemx_freertos
在这里插入图片描述


一、任务调度

文件

1.1 延时

1.1.1 相对延时

vTaskDelay(ticks1);

void appTask_led1(void *argument)
{/* USER CODE BEGIN appTask_led1 */TickType_t ticks1=pdMS_TO_TICKS(1000);/* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);vTaskDelay(ticks1);}/* USER CODE END appTask_led1 */
}

osDelay();

void appTask_led1(void *argument)
{/* USER CODE BEGIN appTask_led1 *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);osDelay(1000);}/* USER CODE END appTask_led1 */
}

1.1.2 绝对延时

vTaskDelayUntil(&wake_time,ticks2);

void appTask_led2(void *argument)
{/* USER CODE BEGIN appTask_led2 */TickType_t ticks2=pdMS_TO_TICKS(2000);	TickType_t wake_time=xTaskGetTickCount();/* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);vTaskDelayUntil(&wake_time,ticks2);		}/* USER CODE END appTask_led2 */
}

1.2 挂起和恢复

led1和led2程序不变 增加两个按键,当其中一个按键按下挂起任务,另一个按键按下恢复任务。

1.2.1 cmsis的挂起和恢复函数

osThreadSuspend

osThreadResume

void appTask_key1(void *argument)
{/* USER CODE BEGIN appTask_key1 *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(key0_GPIO_Port,key0_Pin)==GPIO_PIN_RESET){osThreadSuspend(Task_led1Handle);}osDelay(20);}/* USER CODE END appTask_key1 */
}void appTask_key2(void *argument)
{/* USER CODE BEGIN appTask_key2 *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(key1_GPIO_Port,key1_Pin)==GPIO_PIN_RESET){osThreadResume(Task_led1Handle);}osDelay(20);}/* USER CODE END appTask_key2 */
}

1.2.2 freertos的挂起和恢复函数

vTaskSuspend

vTaskResume

void appTask_key1(void *argument)
{/* USER CODE BEGIN appTask_key1 *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(key0_GPIO_Port,key0_Pin)==GPIO_PIN_RESET){vTaskSuspend((TaskHandle_t)Task_led1Handle);}osDelay(20);}/* USER CODE END appTask_key1 */
}
void appTask_key2(void *argument)
{/* USER CODE BEGIN appTask_key2 *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(key1_GPIO_Port,key1_Pin)==GPIO_PIN_RESET){vTaskResume((TaskHandle_t)Task_led1Handle);}osDelay(20);}/* USER CODE END appTask_key2 */
}

1.3 删除

1.3.1 cmsis的删除任务函数

osThreadTerminate

void appTask_key1(void *argument)
{/* USER CODE BEGIN appTask_key1 *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(key0_GPIO_Port,key0_Pin)==GPIO_PIN_RESET){osThreadTerminate(Task_led1Handle);}osDelay(20);}/* USER CODE END appTask_key1 */
}

1.3.2 freertos的删除任务函数

直接使用vTaskDelete会导致程序卡死,进入HardFault_Handler函数

二、Freertos任务与中断

介绍Freertos与硬件中断的关系以及如何正确使用硬件中断。

FreeRTOS中的任务与中断的关系:
在FreeRTOS中,任务(Task)和中断服务例程(Interrupt Service Routine,ISR)是两种不同的执行环境。任务通常用于执行应用级别的功能,比如处理用户输入,执行通信协议等,而ISR则用于处理硬件中断,比如外部中断,定时器中断等。
在FreeRTOS中,中断优先级是高于任何任务的。这意味着,当中断事件发生时,不论CPU正在执行何种任务,都会立刻停下来,跳转到相应的ISR来处理中断。一旦ISR处理完成,CPU就会恢复之前被打断的任务继续执行。
因此,为了保证实时性,通常我们会把需要快速响应的操作放在ISR中处理。但由于ISR的执行会打断任务的运行,如果ISR执行时间过长,可能会导致任务延时,影响系统的实时性。所以,通常我们建议尽量减少在ISR中执行的操作,比如可以在ISR中只做一些简单的标记或者数据传递,然后通过任务来做实际的处理。

FreeRTOS中使用任务与中断需要注意的点:
不同的API: FreeRTOS为任务和ISR提供了不同的API。比如任务可以使用xQueueSend来发送消息,而ISR需要使用xQueueSendFromISR。尽管这两个函数的功能类似,但后者是为中断环境特别设计的,可以确保在中断中安全使用。
中断嵌套: 在某些高性能的MCU中,支持中断嵌套,也就是说一个ISR可以被另一个优先级更高的ISR打断。在这种情况下,你需要正确地设置你的中断优先级,确保你的中断能被正确地响应。
临界区保护: 在任务中,如果你需要访问一些共享资源,或者执行一些不能被打断的操作,你需要使用临界区来保护这些操作。在FreeRTOS中,你可以使用taskENTER_CRITICAL和taskEXIT_CRITICAL来定义一个临界区。在这个区域内的代码不会被任何中断打断。但注意临界区不应过长,否则会影响中断的响应。
中断同步: 很多时候,我们需要在ISR和任务之间进行一些同步操作,比如任务需要等待某个中断发生。在FreeRTOS中,你可以使用二值信号量(Binary Semaphore)或者直接任务通知(Task Notification)来实现这种同步。

避免的问题:
避免在ISR中执行过多的操作: 如上所述,ISR的执行会打断任务,如果ISR执行时间过长,可能会导致任务延时,影响系统的实时性。
避免在ISR中调用可能阻塞的函数: 这一点非常重要,因为ISR不能被阻塞。如果你在ISR中调用了可能阻塞的函数(比如xQueueSend),可能会导致系统崩溃。
避免在中断中直接操作共享资源: 如果你在ISR中直接操作一些在任务中也会被访问的共享资源,可能会导致数据不一致。在这种情况下,你可以使用队列(Queue)或者二值信号量(Binary Semaphore)来在任务和ISR之间传递数据。
避免频繁的中断: 如果你的系统中有过多的中断发生,可能会导致CPU大部分时间都在处理中断,而无法执行任务。这通常会导致系统的性能下降,甚至崩溃。你需要合理地设计你的硬件和软件,以减少不必要的中断。

三、消息队列

文件

队列(Queue)是一种非常重要的通信机制,可以用来在任务之间,或者任务与中断之间传递数据
消息队列在FreeRTOS或CMSIS-RTOS中每次发送或接收的都是一个消息。这个“消息”可以是一个整数,也可以是一个指针。

在这里插入图片描述

osMessageQueuePut() and osMessageQueueGet() 都可以在中断服务程序中使用。

3.1 写入和读取一个数据

osMessageQueuePut 将一个新的项目发送到队列的末尾
osMessageQueueGet 从队列的前面读取一个(读取以后会自动删除那个)

void appTask_led2(void *argument)
{/* USER CODE BEGIN appTask_led2 */uint32_t data = 0;		/* Infinite loop */for(;;){data++;	osMessageQueuePut(myQueue01Handle,&data,0,0);osDelay(1000);}/* USER CODE END appTask_led2 */
}
void appTask_key1(void *argument)
{/* USER CODE BEGIN appTask_key1 */uint32_t receivedData = 0;/* Infinite loop */for(;;){osMessageQueueGet(myQueue01Handle, &receivedData, 0, osWaitForever);/* 处理数据,这里我们只是简单地打印出来 */printf("Received data: %d\n", receivedData);}/* USER CODE END appTask_key1 */
}

osMessageQueuePuttimeout参数是一个表示操作超时时间的毫秒数,这用于在尝试将消息放入队列时设置阻塞时间。以下是一些常用的设定值:

  1. 如果你希望调用osMessageQueuePut后立即返回,无论消息是否成功放入队列,你可以设定timeout为0。这是非阻塞调用的方式。
  2. 如果你希望osMessageQueuePut在队列满的情况下阻塞,直到有空间可以放入新的消息,可以设定timeoutosWaitForever。这将会在队列空间可用之前一直阻塞当前任务。
  3. 如果你希望osMessageQueuePut在队列满的情况下阻塞一段特定的时间,可以设定timeout为这个时间的毫秒数。例如,timeout设定为1000,表示如果队列满,osMessageQueuePut将会阻塞最多1秒钟。如果在这1秒钟内队列有空间可用,它会立即返回成功。否则,1秒后它会因超时而返回失败。

osMessageQueueGet() 函数有一个参数 osWaitForever。这意味着如果队列中没有数据,appTask_key1将会一直阻塞等待,直到有数据到达队列。
在这种情况下,FreeRTOS会自动将osMessageQueueGet() 置入阻塞状态,其CPU时间片会被其他任务或者空闲任务(idle task)所使用。只有当队列中有数据到达时,osMessageQueueGet() 才会从阻塞状态被唤醒并开始运行。
因此,即使appTask_key1并没有使用 osDelay() 函数来主动让出CPU,但由于使用了 osMessageQueueGet() 函数的阻塞等待,也能保证其在没有数据可处理时不会占用CPU资源。

3.2 写入和读取一个段数据 (指针) (分配内存法)

创建用于存储数据的缓冲区。这个缓冲区可以是静态分配的,也可以是动态分配的,具体取决于你的需求和系统资源。
当需要发送数据时,将数据复制到缓冲区,并发送指向缓冲区的指针作为消息队列的消息。
接收任务收到指针后,可以直接访问缓冲区中的数据。
当接收任务完成对数据的处理后,如果缓冲区是动态分配的,应该释放它。

osMessageQueuePut
osMessageQueueGet

pvPortMalloc
vPortFree

#define ARRAY_SIZE 20
void appTask_led2(void *argument)
{/* USER CODE BEGIN appTask_led2 *//* Infinite loop */for(;;){uint32_t* array = (uint32_t*)pvPortMalloc(ARRAY_SIZE * sizeof(uint32_t));for (int i = 0; i < ARRAY_SIZE; i++) {array[i] = i;}osMessageQueuePut(myQueue01Handle, &array, 0, 0);osDelay(1000);}/* USER CODE END appTask_led2 */
}
void appTask_key1(void *argument)
{/* USER CODE BEGIN appTask_key1 *//* Infinite loop */for(;;){uint32_t* receivedArray;osMessageQueueGet(myQueue01Handle, &receivedArray, 0, osWaitForever);// Process the arrayfor (int i = 0; i < ARRAY_SIZE; i++) {printf("Received: %d\n", receivedArray[i]);HAL_Delay(10);}vPortFree(receivedArray);}/* USER CODE END appTask_key1 */
}

appTask_led2 中,我们使用 pvPortMalloc 分配了一块新的内存,并将指针指向这块内存。然后我们把这个指针放入消息队列。在 appTask_key1 中,我们从队列中获取指针,并把它指向 uint32_t* receivedArray,这个指针现在指向 appTask_led2 分配的那块内存。
appTask_key1 完成处理这块内存后,它应该使用 vPortFree 释放这块内存,这样它就可以被系统再次使用。这个过程不断重复,每次 appTask_led2 都会分配新的内存块,并把它的地址发送给 appTask_key1

四、二值信号量

文件

二值信号量可以作用与进程间的通信,
如果不使用二值信号量而是自定义一个全局变量来实现同步,则任务需要不停的查询全局变量的值,而二值信号量可以使任务直接进入阻塞态等待。二值信号量进行同步的效率高

在这里插入图片描述

osSemaphoreAcquire(): 获取一个信号量。如果信号量当前计数为0,则线程将阻塞(等待)直到信号量变得可用。
osSemaphoreRelease(): 释放一个信号量,增加信号量的计数值。

void appTask_led2(void *argument)
{/* USER CODE BEGIN appTask_led2 *//* Infinite loop */for(;;){osSemaphoreAcquire(Bin_key0_readyHandle,osWaitForever);printf("hello world\n");}/* USER CODE END appTask_led2 */
}
void appTask_key1(void *argument)
{/* USER CODE BEGIN appTask_key1 *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(key0_GPIO_Port,key0_Pin)==GPIO_PIN_RESET){osSemaphoreRelease(Bin_key0_readyHandle);}}/* USER CODE END appTask_key1 */
}

本代码段的本意是按下按键会存入二值信号量,在appTask_led2中进行读取当有二值信号量的时候就会执行打印。

但是

由于cubemx生成的代码中导致二值信号量初始状态就已经存入了一个信号量,导致appTask_led2中打印程序会执行一次。
目前cubemx还无法修改这个值只能在void MX_FREERTOS_Init(void) 函数中将 Bin_key0_readyHandle = osSemaphoreNew(1, 1, &Bin_key0_ready_attributes);中的第二个参数1修改为0
即如下 Bin_key0_readyHandle = osSemaphoreNew(1, 0, &Bin_key0_ready_attributes);
但是这样会导致cubemx下次生成代码的时候把这里重置。目前无完美解决方案。

有效方案是:
在使用那个二值信号量的任务主循环之前先调用一次, osSemaphoreAcquire(Bin_key0_readyHandle,0);
要注意位置!!

void appTask_led2(void *argument)
{/* USER CODE BEGIN appTask_led2 *//* Infinite loop */osSemaphoreAcquire(Bin_key0_readyHandle,0);for(;;){osSemaphoreAcquire(Bin_key0_readyHandle,osWaitForever);printf("hello world\n");}/* USER CODE END appTask_led2 */
}

五、互斥量

互斥量(mutex)在实时操作系统中常常被用于保护共享资源,避免在并发访问时出现问题。使用互斥量可以确保一次只有一个任务访问特定的共享资源。

在这里插入图片描述

互斥量不可以在ISR中使用
osMutexAcquire(): 获取互斥量,如果互斥量不可用,任务会被阻塞(根据指定的超时时间)。
osMutexRelease(): 释放互斥量,使其他任务能获取它。

void appTask_led2(void *argument)
{/* USER CODE BEGIN appTask_led2 *//* Infinite loop */for(;;){osMutexAcquire(usart_MutexHandle, osWaitForever);// Use the UART here...printf("hello world user cc\n");		osMutexRelease(usart_MutexHandle);osDelay(20);}/* USER CODE END appTask_led2 */
}
void appTask_key1(void *argument)
{/* USER CODE BEGIN appTask_key1 *//* Infinite loop */for(;;){osMutexAcquire(usart_MutexHandle, osWaitForever);printf("hello\n");		osMutexRelease(usart_MutexHandle);osDelay(5);}/* USER CODE END appTask_key1 */
}

实际上,互斥量的真正价值在于它能够保护那些可能被多个任务同时访问的共享资源,例如,当你在实现一个驱动程序,或者需要访问一个共享数据结构时,互斥量是非常有用的。
再者,互斥量提供了一种方法来处理优先级反转的问题。如果一个低优先级的任务获取了互斥量,而此时一个高优先级的任务也需要这个互斥量,那么FreeRTOS会临时提高低优先级任务的优先级,以防止高优先级任务被无限期地阻塞。这是一个非常重要的特性,可以在复杂的实时系统中确保任务的可响应性。

六、事件组

文件
在这里插入图片描述

事件组(Event Groups)是一种同步机制,允许一个任务等待一个或多个位在一个事件组中被设置。当任务等待一组事件时,可以选择等待所有事件位都被设置,或者任何一个事件位被设置

osEventFlagsNew:创建一个新的事件标志组。
osEventFlagsSet:在一个事件标志组中设置一个或多个位。可在中断中使用
osEventFlagsClear:在一个事件标志组中清除一个或多个位。可在中断中使用
osEventFlagsWait:等待一个或多个位在一个事件标志组中被设置。

6.1 设置位

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART1){memcpy(MainBuf_1, RxBuf_1, Size);	//将接收缓冲区的数据复制到主缓冲区/* 再次启动 DMA */HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *) RxBuf_1, RxBuf_SIZE_1);__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);if(MainBuf_1[0]==1){osEventFlagsSet(myEvent01Handle,EVENT_0_BIT);}if(MainBuf_1[0]==2){osEventFlagsSet(myEvent01Handle,EVENT_1_BIT);}}}

6.2 等待位

当两个标志位都被置为的时候才能进行任务。
flags 得到的值是EVENT_0_BIT和EVENT_1_BIT相与的值。

所以

#define EVENT_0_BIT (1 << 0) // Event bit 0, which corresponds to event 1

#define EVENT_1_BIT (1 << 1) // Event bit 1, which corresponds to event 2

其他的就应该 (1<<?)

  for(;;){uint32_t flags = osEventFlagsWait(myEvent01Handle,EVENT_0_BIT|EVENT_1_BIT,osFlagsWaitAll,osWaitForever);if (flags & EVENT_0_BIT) {// Event 1 has occurredprintf("over 1\n");}if (flags & EVENT_1_BIT) {// Event 2 has occurredprintf("over 2\n");}printf("over 5\n");		}

当只有一个事件需要等待的时候填osFlagsWaitAllosFlagsWaitAny是一样的效果
如下
osEventFlagsWait(myEvent01Handle, EVENT_0_BIT, osFlagsWaitAny, osWaitForever);
`osEventFlagsWait(myEvent01Handle, EVENT_0_BIT, osFlagsWaitAll, osWaitForever);

osFlagsNoClear 是一个选项,当你使用 osEventFlagsWait() 函数时,你可以选择是否在等待的事件标志位被检测到后自动清除这些标志位。
如果你使用了 osFlagsNoClear 选项,那么当事件标志位被 osEventFlagsWait() 检测到后,这些标志位不会被自动清除,它们将保持设置的状态。
如果你没有使用 osFlagsNoClear 选项,那么在 osEventFlagsWait() 检测到事件标志位后,这些标志位会被自动清除。
所以,osFlagsNoClear 的使用取决于你的具体需求。如果你希望事件标志位在被一个任务检测到后仍然保持设置的状态,那么你可以使用 osFlagsNoClear。如果你希望事件标志位在被一个任务检测到后立即被清除,那么你就不应该使用 osFlagsNoClear

七、任务通知

任务通知是FreeRTOS为每个任务提供的一种轻量级、低延迟的机制,可以用来向任务发送一个事件或者唤醒一个任务。FreeRTOS任务通知功能的优点在于它比队列、信号量等内核对象消耗更少的RAM,并且速度更快。任务通知可以被看作是只有一个数据槽的邮箱。

7.1 CMSIS-RTOS API 线程标志

无需配置cubemx

任务通知的线程标志(Thread Flags)

osThreadFlagsSet(): 用于给指定任务发送通知。可在中断中使用
osThreadFlagsClear(): 用于清除任务的通知标志。
osThreadFlagsWait(): 用于让任务等待接收通知。

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#define Thread_0_BIT (1 << 0)  // Event bit 0, which corresponds to event 1
#define Thread_1_BIT (1 << 1)  // Event bit 1, which corresponds to event 2extern osThreadId_t Task_led2Handle;void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART1){memcpy(MainBuf_1, RxBuf_1, Size);	//将接收缓冲区的数据复制到主缓冲区/* 再次启动 DMA */HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *) RxBuf_1, RxBuf_SIZE_1);__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);if(MainBuf_1[0]==1){osThreadFlagsSet(Task_led2Handle,Thread_0_BIT);//xTaskNotifyFromISR()}if(MainBuf_1[0]==2){osThreadFlagsSet(Task_led2Handle,Thread_1_BIT);}}
}void appTask_led2(void *argument)
{/* USER CODE BEGIN appTask_led2 *//* Infinite loop */
#define Thread_0_BIT (1 << 0)  // Event bit 0, which corresponds to event 1
#define Thread_1_BIT (1 << 1)  // Event bit 1, which corresponds to event 2for(;;){uint32_t flags = osThreadFlagsWait(Thread_0_BIT|Thread_1_BIT,osFlagsWaitAny,osWaitForever);if (flags & Thread_0_BIT) {// Event 1 has occurredprintf("over 1\n");}if (flags & Thread_1_BIT) {// Event 2 has occurredprintf("over 2\n");}printf("over 5\n");		}/* USER CODE END appTask_led2 */
}

任务通知(线程标志)和事件组在功能上确实有很多相似之处,都可以用于通知线程某种状态的发生。然而,它们之间还是有一些关键的区别:

适用对象不同: 任务通知是针对特定任务的,而事件组则可以被多个任务共享。也就是说,任务通知只能由任务自身或者其他任务来设置、清除和等待,而事件组可以被任何拥有其句柄的任务设置、清除和等待。
性能: 任务通知通常具有更高的效率。在FreeRTOS中,任务通知是直接实现在TCB(任务控制块)中的,这使得它在处理速度和内存使用上优于事件标志。
复杂性: 任务通知比事件标志简单,不支持“自动清除”和“多任务等待同一事件”等高级功能。如果你的需求比较简单,可能会更喜欢任务通知的简洁性。然而,如果你需要更复杂的同步机制,可能会发现事件标志更有用。

7.2 Free-RTOS API 任务通知 (待填)

在原生的FreeRTOS API中,任务通知(Task Notification)功能是非常强大的。除了基本的二值信号量和事件组功能外,它还可以做到"直接到任务"的通信,包括发送整数值,设置/清除/切换二进制位等等。这使得任务通知在很多场景下可以替代队列、信号量、事件组等,而且由于其实现简单直接,效率更高。
然而,在CMSIS-RTOS API中,为了通用性和简化,这个复杂的功能被划分为线程标志(Thread Flags)和事件标志(Event Flags)。其中的线程标志基本上等价于原生FreeRTOS中的事件组功能,即每一位代表一个事件,可以单独设置、清除、等待。这确实减少了FreeRTOS任务通知的一些功能。

八、流缓冲区 (待填)

九、消息缓冲区 (待填)

十、软件定时器

软件定时器是一个非常有用的功能,它允许你在指定的时间后执行某个函数,或者每隔指定的时间执行某个函数。这个功能非常适合用来执行周期性的任务,比如每隔一秒钟闪烁一次LED,或者每隔一分钟检查一次温度等。

osStatus_t osTimerStart (osTimerId_t timer_id, uint32_t ticks): 启动或重新启动一个软件定时器。
timer_id: 这是你要启动的定时器的ID,这个ID是osTimerNew()函数返回的。
ticks: 这是定时器的时间长度(以系统时钟周期为单位)。
osStatus_t osTimerStop (osTimerId_t timer_id): 停止一个软件定时器。
timer_id: 这是你要停止的定时器的ID。
osStatus_t osTimerDelete (osTimerId_t timer_id): 删除一个软件定时器。
timer_id: 这是你要删除的定时器的ID。

10.1 任务中开启定时器

10.1.1 周期定时器

在这里插入图片描述

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#define Thread_0_BIT (1 << 0)  // Event bit 0, which corresponds to event 1
#define Thread_1_BIT (1 << 1)  // Event bit 1, which corresponds to event 2extern osThreadId_t Task_led2Handle;extern osTimerId_t myTimer01Handle;void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART1){memcpy(MainBuf_1, RxBuf_1, Size);	//将接收缓冲区的数据复制到主缓冲区/* 再次启动 DMA */HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *) RxBuf_1, RxBuf_SIZE_1);__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);if(MainBuf_1[0]==1){osThreadFlagsSet(Task_led2Handle,Thread_0_BIT);}if(MainBuf_1[0]==2){osThreadFlagsSet(Task_led2Handle,Thread_1_BIT);}}}
void appTask_led2(void *argument)
{/* USER CODE BEGIN appTask_led2 *//* Infinite loop */
#define Thread_0_BIT (1 << 0)  // Event bit 0, which corresponds to event 1
#define Thread_1_BIT (1 << 1)  // Event bit 1, which corresponds to event 2for(;;){uint32_t flags = osThreadFlagsWait(Thread_0_BIT,osFlagsWaitAny,osWaitForever);osTimerStart(myTimer01Handle,1000);}/* USER CODE END appTask_led2 */
}
void Callback01(void *argument)
{/* USER CODE BEGIN Callback01 */HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);/* USER CODE END Callback01 */
}

由于直接在中断函数里面调用osTimerStart(myTimer01Handle,1000);不成功所以直接用第七章的任务通知去一个任务,在另一个任务中来开启软件定时器。

10.1.2单次定时器

对于单次定时器,定时器在到期并执行回调函数后会自动停止,除非再次调用 osTimerStart()
代码与上面的相同

在这里插入图片描述

10.2 中断中开启定时器 (待填)

十一、空闲任务与低功耗 (待填)


总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/15366.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是数据一致性

什么是数据一致性 数据一致性这个单词在平常开发中&#xff0c;或者各种文章中都能经常看见&#xff0c;我们常常听见什么东西数据不一致了&#xff0c;造成了一定的损失&#xff0c;赶快修复一下。但是很多同学对一致性具体代表什么意思&#xff0c;他有什么作用依然不是很了解…

一文弄懂Java日志框架

文章目录 日志的概念日志门面JUL日志框架JUL架构入门案例日志的级别Logger之间的父子关系日志的配置文件日志原理解析 LOG4J日志框架Log4j入门Log4j组件LoggersAppendersLayouts Layout的格式Appender的输出自定义Logger JCL日志门面JCL入门JCL原理 SLF4J日志门面SLF4J入门绑定…

Redis优化

目录 一、Redis高可用 二、Redis持久化 1.RDB持久化 1.1触发条件 1.1.1手动触发 1.1.2自动触发 1.2其他自动触发机制 1.3执行流程 1.4启动时加载 2.AOF 持久化 2.1开启AOF 2.2执行流程 2.2.1命令追加(append) 2.2.2文件写入(write)和文件同步(sync) 2.2.3文件重…

TCP Socket性能优化秘籍:掌握read、recv、readv、write、send、sendv的最佳实践

TCP Socket性能优化秘籍&#xff1a;掌握read、recv、readv、write、send、sendv的最佳实践 博主简介一、引言1.1、TCP Socket在网络通信中的重要性1.2、为什么需要优化TCP Socket的性能&#xff1f; 二、TCP Socket读操作的性能优化2.1、read、recv、readv的功能和用法2.2、提…

S32K14x FlexCAN入门

每个系列S32K14x支持的邮箱个数。 基本每个系列的FlexCAN0可以支持32个报文缓存和支持CANFD。 中断源 mail 最多有32个mail • 灵活的消息缓冲区 (MB)&#xff0c;总共 32 个消息缓冲区&#xff0c;数据长度为 8 字节 每个&#xff0c;可配置为 Rx 或 Tx 过滤掩码功能 强…

应对Python爬虫IP被封的策略及建议。

我们在进行数据抓取使用代理ip的时候需要有一些约束规定&#xff0c;才能保证我们持续稳定的抓取数据。 大概整理了一下&#xff0c;需要注意以下几点&#xff0c;仅供参考&#xff1a; 1、使用高质量的代理服务器&#xff1a;选择一些可靠的代理服务器&#xff0c;确保它们的…

Intellij IDEA 初学入门图文教程(八) —— IDEA 在提交代码时 Performing Code Analysis 卡死

在使用 IDEA 开发过程中&#xff0c;提交代码时常常会在碰到代码中的 JS 文件时卡死&#xff0c;进度框上显示 Performing Code Analysis&#xff0c;如图&#xff1a; 原因是 IDEA 工具默认提交代码时&#xff0c;分析代码功能是打开的&#xff0c;需要通过配置关闭下就可以了…

.net项目开发-EF框架解决添加默认值问题

文章目录 前言EF中核心类DbContextDbContext中的SaveChanges()方法重写SaveChanges()方法注意点-Modified 其它状态下的实体如何操作 前言 最近开发.net项目&#xff0c;持久层用的是EF框架&#xff0c;也是第一次使用这个框架&#xff0c;用这个框架的好处就是基于实体的开发…

MyBatis中的动态SQL(sql标签、where标签、set标签、批量增加与批量删除)

目录 sql标签 ​编辑 where标签 set标签 foreach标签 批量增加 批量删除 将基础SQL语句中重复性高的增加它的复用性&#xff0c;使得sql语句的灵活性更强 sql标签<sql> <sql id"text">select * from user</sql><select id"selectA…

基于simulink处理监控视频以选择包含运动的帧(附源码)

一、前言 此示例演示如何处理监控视频以选择包含运动的帧。安全问题要求使用摄像机对重要位置进行持续监控。为了有效地记录、查看和存档这些海量数据&#xff0c;您可以减小视频帧大小或减少录制的视频帧总数。此示例说明了后一种方法。在其中&#xff0c;相机视野中的运动会…

Win10 显示WLAN不安全,并且链路速度54/54 (Mbps),通过K3C路由器修改协议解决,无线网卡连接速度只有54Mbps

省流 换个安全协议就好了。 使用有线等同隐私(WEP)或临时密钥完整性协议(TKIP)加密配置时&#xff0c;客户端设备的WiFi数据传输速率不会超过54Mbps&#xff0c; 问题 我用的是K3C路由器&#xff0c;今天跑百度网盘感觉很奇怪&#xff0c;突然就只有10MB/s了&#xff0c;感觉…

Go 程序是怎样跑起来的

Go 程序是怎样跑起来的 引入 我们从一个 helloworld 的例子开始 package mainimport "fmt"func main() {fmt.Println("hello world") }用 vim 要打开&#xff0c;输入命令&#xff1a; :%!xxd下面是输出 00000000:7061 636b 6167 6520 6d61 696e 0a0a…