FreeRTOS第2天:

1. 二值信号量简介(386.11)

什么是信号量?

  • 信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代码段不被并
    发调用。
  • 信号量这个名字,我们可以把它拆分来看,信号可以起到通知信号的作用,然后我们的量还可以用来表示资
    源的数量,当我们的量只有0和1的时候,它就可以被称作二值信号量,只有两个状态,当我们的那个量没有
    限制的时候,它就可以被称作为计数型信号量。
  • 信号量也是队列的一种。

什么是二值信号量?

  • 二值信号量其实就是一个长度为1,大小为零的队列,只有0和1两种状态,通常情况下,我们用它来进行互
    斥访问或任务同步。
  • 互斥访问:比如门钥匙,只有获取到钥匙才可以开门
  • 任务同步:比如我录完视频你才可以看视频
    在这里插入图片描述

二值信号量相关 API 函数

在这里插入图片描述

  1. 创建二值信号量
SemaphoreHandle_t xSemaphoreCreateBinary( void )
  • 参数:
  • 返回值:
    • 成功,返回对应二值信号量的句柄;
    • 失败,返回 NULL 。
  1. 释放二值信号量
BaseType_t  xSemaphoreGive( SemaphoreHandle_t xSemaphore )
  • 参数:
    • xSemaphore:要释放的信号量句柄
  • 返回值:
    • 成功,返回 pdPASS ;
    • 失败,返回 errQUEUE_FULL 。
  1. 获取二值信号量
BaseType_t  xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
  • 参数:
    • xSemaphore:要获取的信号量句柄
    • xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;
  • 返回值:
    • 成功,返回 pdPASS ;
    • 失败,返回 errQUEUE_FULL 。

2. 二值信号量实操(387.12)

实验需求

  • 创建一个二值信号量,按下 KEY1 则释放信号量,按下 KEY2 获取信号量。

cubeMX配置(基于1.muban)

在这里插入图片描述
在这里插入图片描述

代码实现

  • 代码(5.semaphore_binary_test)
myBinarySemHandle = xSemaphoreCreateBinary();
void StartTaskGive(void const * argument)
{/* USER CODE BEGIN StartTaskGive *//* Infinite loop */for(;;){if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){if(xSemaphoreGive(myBinarySemHandle) == pdTRUE)printf("二值信号量放入成功\r\n");elseprintf("二值信号量放入失败\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(10);}/* USER CODE END StartTaskGive */
}
void StartTaskTake(void const * argument)
{/* USER CODE BEGIN StartTaskTake *//* Infinite loop */for(;;){if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){if(xSemaphoreTake(myBinarySemHandle, portMAX_DELAY) == pdTRUE)printf("二值信号量获取成功\r\n");elseprintf("二值信号量获取失败\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(10);}/* USER CODE END StartTaskTake */
}

在这里插入图片描述

3. 计数型信号量简介及实操(388.13)

什么是计数型信号量?

  • 计数型信号量相当于队列长度大于 1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创
    建的时候确定的。
    在这里插入图片描述
    在这里插入图片描述

计数型信号量相关 API 函数

在这里插入图片描述

  • 计数型信号量的释放和获取与二值信号量完全相同!
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
  • 参数:
    • uxMaxCount:可以达到的最大计数值
    • uxInitialCount:创建信号量时分配给信号量的计数值
  • 返回值:
    • 成功,返回对应计数型信号量的句柄;
    • 失败,返回 NULL 。

实验需求

  • 创建一个计数型信号量,按下 KEY1 则释放信号量,按下 KEY2 获取信号量。

cubeMX配置(基于5.semaphore_binary_test)

  • 将 Config parameters 标签里的 USE_COUNTING_SEMAPHORES 设置为 Enabled 。
    在这里插入图片描述
    在这里插入图片描述

代码实现

  • 代码(6.semaphore_counting_test)
myCountingSemHandle = xSemaphoreCreateCounting(3, 0);
void StartTaskGive(void const * argument)
{/* USER CODE BEGIN StartTaskGive *//* Infinite loop */for(;;)
{if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){if (xSemaphoreGive(myCountingSemHandle) == pdTRUE)printf("计数信号量放入成功\r\n");elseprintf("计数信号量放入失败\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(10);
}/* USER CODE END StartTaskGive */
}
void StartTaskTake(void const * argument)
{/* USER CODE BEGIN StartTaskTake *//* Infinite loop */for(;;)
{if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){if (xSemaphoreTake(myCountingSemHandle, 0 ) == pdTRUE)printf("计数信号量获取成功\r\n");elseprintf("计数信号量获取失败\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(10);
}/* USER CODE END StartTaskTake */
}

在这里插入图片描述

4. 互斥量简介(389.14)

什么是互斥量?

  • 在多数情况下,互斥型信号量和二值型信号量非常相似,但是从功能上二值型信号量用于同步,而互斥型信
    号量用于资源保护。
    互斥型信号量和二值型信号量还有一个最大的区别,互斥型信号量可以有效解决优先级反转现象。

什么是优先级翻转?

在这里插入图片描述

  • 以上图为例,系统中有 3 个不同优先级的任务 H/M/L,最高优先级任务 H 和最低优先级任务 L 通过信号量机
    制,共享资源。目前任务 L 占有资源,锁定了信号量,Task H 运行后将被阻塞,直到 Task L 释放信号量后,
    Task H 才能够退出阻塞状态继续运行。但是 Task H 在等待 Task L 释放信号量的过程中,中等优先级任务 M 抢
    占了任务 L,从而延迟了信号量的释放时间,导致 Task H 阻塞了更长时间,这种现象称为优先级倒置或反
    转。
  • 优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获
    取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优
    先级提升到与自己相同的优先级。
  • 优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响。

互斥量相关 API 函数

  • 互斥信号量不能用于中断服务函数中!
    在这里插入图片描述
SemaphoreHandle_t xSemaphoreCreateMutex( void )
  • 参数:
  • 返回值:
    • 成功,返回对应互斥量的句柄;
    • 失败,返回 NULL 。

5. 互斥量实操(390.15)

实验需求 1. 演示优先级翻转

  • cubeMX配置(基于1.muban)
    在这里插入图片描述
    在这里插入图片描述
  • 代码(7.mutex_test)
    在这里插入图片描述

实验需求 2. 使用互斥量优化优先级翻转问题

  • cubeMX配置(基于7.mutex_test)
    在这里插入图片描述
    在这里插入图片描述
  • 代码(7.mutex_test2)
void StartTaskH(void const * argument)
{/* USER CODE BEGIN StartTaskH *//* Infinite loop */for(;;){xSemaphoreTake(myMutexHandle, portMAX_DELAY);printf("TaskH: 我开始进入厕所,发功中。。。\r\n");HAL_Delay(3000);printf("TaskH: 我上完厕所了,真舒服。。。\r\n");xSemaphoreGive(myMutexHandle);osDelay(1000);osDelay(1);}/* USER CODE END StartTaskH */
}
void StartTaskM(void const * argument)
{/* USER CODE BEGIN StartTaskM *//* Infinite loop */for(;;){printf("TaskM: 我就是为了占有CPU资源,带女朋友去兜风~~~\r\n");osDelay(1000);}/* USER CODE END StartTaskM */
}
void StartTaskL(void const * argument)
{/* USER CODE BEGIN StartTaskL *//* Infinite loop */for(;;){	xSemaphoreTake(myMutexHandle, portMAX_DELAY);//获取互斥量printf("TaskL: 我开始进入厕所,发功中。。。\r\n");HAL_Delay(3000);printf("TaskL: 我上完厕所了,真舒服。。。\r\n");xSemaphoreGive(myMutexHandle);//释放互斥量osDelay(1);}/* USER CODE END StartTaskL */
}

在这里插入图片描述

6. 事件标志组简介(391.16)

什么是事件标志组?

  • 事件标志位: 表明某个事件是否发生,联想:全局变量 flag。通常按位表示,每一个位表示一个事件(高 8 位
    不算)
  • 事件标志组 是一组事件标志位的集合, 可以简单的理解事件标志组,就是一个整数。
  • 事件标志组本质是一个 16 位或 32 位无符号的数据类型 EventBits_t ,由 configUSE_16_BIT_TICKS 决定。
  • 虽然使用了 32 位无符号的数据类型变量来存储事件标志, 但其中的高 8 位用作存储事件标志组的控制信息,低 24 位用作存储事件标志 ,所以说一个事件组最多可以存储 24 个事件标志!
    在这里插入图片描述

事件标志组相关 API 函数

在这里插入图片描述

  1. 创建事件标志组
EventGroupHandle_t xEventGroupCreate( void );
  • 参数:
  • 返回值:
    • 成功,返回对应事件标志组的句柄;
    • 失败,返回 NULL 。
  1. 设置事件标志位
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
  • 参数:
    • xEventGroup:对应事件组句柄。
    • uxBitsToSet:指定要在事件组中设置的一个或多个位的按位值。
  • 返回值:
    • 设置之后事件组中的事件标志位值。
  1. 清除事件标志位
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
  • 参数:
    • xEventGroup:对应事件组句柄。
    • uxBitsToClear:指定要在事件组中清除的一个或多个位的按位值。
  • 返回值:
    • 清零之前事件组中事件标志位的值。
  1. 等待事件标志位
EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait );
  • 参数:
    • xEventGroup:对应的事件标志组句柄
    • uxBitsToWaitFor:指定事件组中要等待的一个或多个事件位的按位值
    • xClearOnExit:pdTRUE——清除对应事件位,pdFALSE——不清除
    • xWaitForAllBits:pdTRUE——所有等待事件位全为 1(逻辑与),pdFALSE——等待的事件位有一个为 1(逻辑或)
    • xTicksToWait:超时
  • 返回值:
    • 等待的事件标志位值:等待事件标志位成功,返回等待到的事件标志位
    • 其他值:等待事件标志位失败,返回事件组中的事件标志位

7. 事件标志组实操(392.17)

实验需求

  • 创建一个事件标志组和两个任务( task1 和 task2),task1 检测按键,如果检测到 KEY1 和 KEY2 都按过,
    则执行 task2 。

代码实现

  • 基于1.muban
    在这里插入图片描述
  • 代码(8.events_test)
EventGroupHandle_t  eventgroup_handle;
eventgroup_handle = xEventGroupCreate();
void StartTask1(void const * argument)
{/* USER CODE BEGIN StartTask1 *//* Infinite loop */for(;;){// 等待 KEY1 按下if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){xEventGroupSetBits(eventgroup_handle, 0x01);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}// 等待 KEY2 按下if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){xEventGroupSetBits(eventgroup_handle, 0x02);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(1);}/* USER CODE END StartTask1 */
}
void StartTask2(void const * argument)
{/* USER CODE BEGIN StartTask2 */EventBits_t event_bit =0;/* Infinite loop */for(;;){event_bit = xEventGroupWaitBits(eventgroup_handle, 0x01 | 0x02, pdTRUE, pdFALSE, portMAX_DELAY);printf("返回值:%#x,请假成功,可以去玩啦!\r\n", event_bit);osDelay(1);}/* USER CODE END StartTask2 */
}

在这里插入图片描述

8. 任务通知简介(393.18)

什么是任务通知?

  • FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能,每个任务都有一个 32 位的通知值。按照 FreeRTOS
    官方的说法,使用消息通知比通过二进制信号量方式解除阻塞任务快 45%, 并且更加省内存(无需创建队列)。
  • 在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件标志组,可以替代长度为 1 的队列(可
    以保存一个 32 位整数或指针值),并且任务通知速度更快、使用的RAM更少!

任务通知值的更新方式

  • FreeRTOS 提供以下几种方式发送通知给任务 :
    • 发送消息给任务,如果有通知未读, 不覆盖通知值
    • 发送消息给任务,直接覆盖通知值
    • 发送消息给任务,设置通知值的一个或者多个位
    • 发送消息给任务,递增通知值
  • 通过对以上方式的合理使用,可以在一定场合下替代原本的队列、信号量、事件标志组等。

任务通知的优势和劣势

  • 任务通知的优势
  1. 使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。
  2. 使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。
  • 任务通知的劣势
  1. 只有任务可以等待通知,中断服务函数中不可以,因为中断没有 TCB 。
  2. 通知只能一对一,因为通知必须指定任务。
  3. 等待通知的任务可以被阻塞, 但是发送消息的任务,任何情况下都不会被阻塞等待。
  4. 任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数
    据。

任务通知相关 API 函数

1. 发送通知

在这里插入图片描述

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
  • 参数:
    • xTaskToNotify:需要接收通知的任务句柄;
    • ulValue:用于更新接收任务通知值, 具体如何更新由形参 eAction 决定;
    • eAction:一个枚举,代表如何使用任务通知的值;
      在这里插入图片描述
  • 返回值:
    • 如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回 pdFALSE, 而其他
      情况均返回 pdPASS。
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue );
  • 参数:
    • xTaskToNotify:需要接收通知的任务句柄;
    • ulValue:用于更新接收任务通知值, 具体如何更新由形参 eAction 决定;
    • eAction:一个枚举,代表如何使用任务通知的值;
    • pulPreviousNotifyValue:对象任务的上一个任务通知值,如果为 NULL, 则不需要回传, 这个时候就等价于函数 xTaskNotify()。
  • 返回值:
    • 如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回 pdFALSE, 而其他
      情况均返回 pdPASS。
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
  • 参数:
    • xTaskToNotify:接收通知的任务句柄, 并让其自身的任务通知值加 1。
  • 返回值:
    • 总是返回 pdPASS。

2. 等待通知

等待通知API函数只能用在任务,不可应用于中断中!
在这里插入图片描述

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
  • 参数:
    • xClearCountOnExit:指定在成功接收通知后,将通知值清零或减 1,pdTRUE:把通知值清零(二值信号量);pdFALSE:把通知值减一(计数型信号量);
    • xTicksToWait:阻塞等待任务通知值的最大时间;
  • 返回值:
    • 0:接收失败
    • 非0:接收成功,返回任务通知的通知值
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
  • 参数:
    • ulBitsToClearOnEntry:函数执行前清零任务通知值那些位 。
    • ulBitsToClearOnExit:表示在函数退出前,清零任务通知值那些位,在清 0 前,接收到的任务通知值会先被保存到形参*pulNotificationValue 中。
    • pulNotificationValue:用于保存接收到的任务通知值。 如果 不需要使用,则设置为 NULL 即可 。
    • xTicksToWait:等待消息通知的最大等待时间。

9. 任务通知实操(394.19)

  1. 模拟二值信号量
  • 基于1.muban
    在这里插入图片描述
  • 代码(9.notify_binary_test)
    在这里插入图片描述
    在这里插入图片描述
  1. 模拟计数型信号量
  • 代码(9. notify_counting_test)(基于9.notify_binary_test修改)
    在这里插入图片描述

在这里插入图片描述
3. 模拟事件标志组

  • 代码(9. notify_events_test)(基于9.notify_counting_test修改)
    在这里插入图片描述
    在这里插入图片描述
  1. 模拟邮箱
  • 代码(9. notify_queue_test)(基于9.notify_events_test修改)
    在这里插入图片描述
    在这里插入图片描述

10. 延时函数(395.20)

  • 延时函数分类
    • 相对延时:vTaskDelay
    • 绝对延时:vTaskDelayUntil
      在这里插入图片描述

在这里插入图片描述

  • vTaskDelay 与 HAL_Delay 的区别
    • vTaskDelay 作用是让任务阻塞,任务阻塞后,RTOS 系统调用其它处于就绪状态的优先级最高的任务来执
      行。
    • HAL_Delay 一直不停的调用获取系统时间的函数,直到指定的时间流逝然后退出,故其占用了全部 CPU 时
      间。
      在这里插入图片描述
      在这里插入图片描述

11. 软件定时器简介(396.21)

什么是定时器?

  • 简单可以理解为闹钟,到达指定一段时间后,就会响铃。
  • STM32 芯片自带硬件定时器,精度较高,达到定时时间后会触发中断,也可以生成 PWM 、输入捕获、输出比较,等等,功能强大,但是由于硬件的限制,个数有限。
  • 软件定时器也可以实现定时功能,达到定时时间后可调用回调函数,可以在回调函数里处理信息。

软件定时器优缺点

  • 优点:
  1. 简单、成本低;
  2. 只要内存足够,可创建多个;
  • 缺点:
    • 精度较低,容易受中断影响。在大多数情况下够用,但对于精度要求比较高的场合不建议使用。

软件定时器原理

  • 定时器是一个可选的、不属于 FreeRTOS 内核的功能,它是由定时器服务任务来提供的。
  • 在调用函数 vTaskStartScheduler() 开启任务调度器的时候,会创建一个用于管理软件定时器的任务,这个
    任务就叫做软件定时器服务任务。
  1. 负责软件定时器超时的逻辑判断
  2. 调用超时软件定时器的超时回调函数
  3. 处理软件定时器命令队列
  • FreeRTOS提供了很多定时器有关的API函数,这些API函数大多都使用FreeRTOS的队列发送命令给定时器服
    务任务。这个队列叫做定时器命令队列。 定时器命令队列是提供给FreeRTOS的软件定时器使用的,用户不
    能直接访问!

    在这里插入图片描述

软件定时器相关配置

软件定时器有一个定时器服务任务和定时器命令队列,这两个东西肯定是要配置的,相关的配置也是放到文件 FreeRTOSConfig.h 中的,涉及到的配置如下:

1、configUSE_TIMERS

  • 如果要使用软件定时器的话宏configUSE_TIMERS一定要设置为1,当设置为1的话定时器服务任务就会在启
    动FreeRTOS调度器的时候自动创建。

2、configTIMER_TASK_PRIORITY

  • 设置软件定时器服务任务的任务优先级,可以为0~(configMAX_PRIORITIES-1)。优先级一定要根据实际的应
    用要求来设置。如果定时器服务任务的优先级设置的高的话,定时器命令队列中的命令和定时器回调函数就会及时的得到处理。

3、configTIMER_QUEUE_LENGTH

  • 此宏用来设置定时器命令队列的队列长度。

4、configTIMER_TASK_STACK_DEPTH

  • 此宏用来设置定时器服务任务的任务堆栈大小。

单次定时器和周期定时器

  • 单次定时器: 只超时一次,调用一次回调函数。可手动再开启定时器;
  • 周期定时器: 多次超时,多次调用回调函数。

软件定时器相关 API 函数

在这里插入图片描述

  1. 创建软件定时器
TimerHandle_t xTimerCreate( const char * const pcTimerName,const TickType_t xTimerPeriod,const UBaseType_t uxAutoReload,void * const pvTimerID,TimerCallbackFunction_t pxCallbackFunction );
  • 参数:
    • pcTimerName:软件定时器名称
    • xTimerPeriodInTicks:定时超时时间,单位:系统时钟节拍。宏 pdMS_TO_TICKS() 可用于将以毫秒为单位指定的时间转换为以 tick 为单位指定的时间。
    • uxAutoReload:定时器模式, pdTRUE:周期定时器, pdFALSE:单次定时器
    • pvTimerID:软件定时器 ID,用于多个软件定时器公用一个超时回调函数
    • pxCallbackFunction:软件定时器超时回调函数
  • 返回值:
    • 成功:定时器句柄
    • 失败:NULL
  1. 开启软件定时器
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xBlockTime );
  • 参数:
    • xTimer:待开启的软件定时器的句柄
    • xTickToWait:发送命令到软件定时器命令队列的最大等待时间
  • 返回值:
    • pdPASS:开启成功
    • pdFAIL:开启失败
  1. 停止软件定时器
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xBlockTime );
  • 参数与返回值同上。
  1. 复位软件定时器
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xBlockTime );
  • 参数与返回值同上。
  • 该功能将使软件定时器的重新开启定时,复位后的软件定时器以复位时的时刻作为开启时刻重新定时。
  1. 更改软件定时器定时时间
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, TickType_t xNewPeriod, TickType_t xBlockTime );
  • xNewPeriod:新的定时超时时间,单位:系统时钟节拍。
  • 其余参数与返回值同上。

12. 软件定时器实操(397.22)

实验需求

  • 创建两个定时器:
    • 定时器1,周期定时器,每1 秒打印一次 Jessie shuai
    • 定时器2,单次定时器,启动后 2 秒打印一次 Hi,Jessie!

cubeMX配置

  • 基于1.muban修改
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 代码(10. timer_test)
    在这里插入图片描述
    在这里插入图片描述

13. 中断管理(398.23)

中断定义

  • 请参考 51 及 STM32 中断相关课程。

中断优先级

  • 任何中断的优先级都大于任务!
  • 在我们的操作系统,中断同样是具有优先级的,并且我们也可以设置它的优先级,但是他的优先级并不是从
    0~15 ,默认情况下它是从 5~15 ,0~4 这 5 个中断优先级不是 FreeRTOS 控制的(5是取决于
    configMAX_SYSCALL_INTERRUPT_PRIORITY)。

相关注意

  1. 在中断中必需使用中断相关的函数;
  2. 中断服务函数运行时间越短越好。

实验需求

  • 创建一个队列及一个任务,按下按键 KEY1 触发中断,在中断服务函数里向队列里发送数据,任务则阻塞接
    收队列数据。

cubeMX配置

在这里插入图片描述
在这里插入图片描述

代码实现

  • stm32f1xx_it.c
#include "cmsis_os.h"
extern osMessageQId myQueue01Handle;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{uint32_t snd = 6;xQueueSendFromISR(myQueue01Handle, &snd, NULL);
}
  • freertos.c
void StartDefaultTask(void const * argument)
{/* USER CODE BEGIN StartDefaultTask */uint32_t rev = 0;/* Infinite loop */for(;;)
{if (xQueueReceive(myQueue01Handle, &rev, portMAX_DELAY) == pdTRUE)printf("rev = %d\r\n", rev);osDelay(1);
}/* USER CODE END StartDefaultTask */
}

在这里插入图片描述

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

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

相关文章

【蓝桥杯软件赛 零基础备赛20周】第5周——高精度大数运算与队列

文章目录 1. 数组的应用–高精度大数运算1.1 Java和Python计算大数1.2 C/C高精度计算大数1.2.1 高精度加法1.2.2 高精度减法 2. 队列2.1 手写队列2.1.1 C/C手写队列2.1.2 Java手写队列2.1.3 Python手写队列 2.2 C STL队列queue2.3 Java队列Queue2.4 Python队列Queue和deque2.5 …

边缘数据中心和5G的融合彻底改变数据传输和物联网

伴随着数字化时代的飞速发展,边缘数据中心和5G技术的联袂崛起,正深刻塑造着人们对数据的创造、传输和处理方式。据Gartner公司的预测,到2025年,企业数据的三分之二将在边缘计算设施中涌现,而非传统的集中式数据中心。这…

134. 加油站(贪心算法)

根据题解 这道题使用贪心算法,找到当前可解决问题的状态即可 「贪心算法」的问题需要满足的条件: 最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较大的问题的解只由其中一个规模较小的子问题的解决定&#xff…

分享88个节日PPT,总有一款适合您

分享88个节日PPT,总有一款适合您 88个节日PPT下载链接:https://pan.baidu.com/s/1mfLrdlB9Y1jqz2vkVIwBNA?pwd6666 提取码:6666 Python采集代码下载链接:采集代码.zip - 蓝奏云 学习知识费力气,收集整理更不易…

Markdown学习测试

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

国产Type-C接口逻辑协议芯片:Type-C显示器芯片方案

产品介绍 双Type-C盲插选型: LDR6282 PD3.0认证协议芯片,USB-IF TID号:212 支持iic,USB转UART,CC升级方式,多年市场验证,显示器市场出货量,显示器大厂采用兼容性NO.1。采用QFN32 5…

系列十五、SpringBoot的启动原理分析

一、概述 所谓SpringBoot的启动原理,翻译成大白话就是"当我们在主启动类上运行run方法时,SpringBoot底层到底做了什么事情,能够帮助我们启动一个Spring的web应用",上边用大白话解释了一下什么是SpringBoot的启动原理&am…

vue 解决响应大数据表格渲染崩溃问题

如果可以实现记得点赞分享,谢谢老铁~ 1.场景描述 发起请求获取上万条数据,进行表格渲染,使浏览器卡顿,导致网页崩溃。 2.分析原因 1.大量数据加载,过多操作Dom,消耗性能。 2.表格中包含其他…

数据库设计实践:粒度的理解与应用示例

粒度是描述数据存储和表示的详细程度。在数据库设计中,理解和正确选择粒度是非常重要的,因为它直接影响到数据的存储效率、查询性能和数据分析的灵活性。 文章目录 粒度的类型:案例粒度选择的考虑因素实际应用 粒度的类型: 细粒度…

24双非硕的秋招总结

24 双非硕的秋招总结 结果: 运气捡漏去了腾讯 想想自己整个研究生学习过程,还是挺坎坷的,记录一下,也给未来的同学提供一些参考。 研一 我是研一上开始学前端的,应该是21年10月份左右,我们实验室是专门…

uniapp前端支付篇(微信、抖音、快手、h5)四个平台支付

前言 微信、快手、h5支付步骤大致相同,只有抖音是有自己的支付组件 项目同时支持多个(微信、快手、h5)平台支付,后端那边代码可以封装的 各平台支付大致流程都是相同的,总结了一下分为五个步骤 点击支付创建订单生成密…

网站更换IP的四大注意事项

1.对网站当中的数据进行备份 网站更换IP时可以将页面的数据库文件和站点文件通过下载工具在本地完成备份。 2.更换解析域名 从站点域名管理后台当中更换域名地址,改为新的IP地址。 3.确保IP安全 在用户更换IP前一定要确定IP是否安全,一旦IP存在不良…

IO流--12--Java lO 设计模式

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Java lO 设计模式装饰器模式适配器模式工厂模式观察者模式 Java lO 设计模式 装饰器模式 适配器模式 工厂模式 观察者模式

Windows启动nacos操作文档

Windows启动nacos操作文档 1、新建数据库nacos_config 2、导入nacos\conf\nacos-mysql.sql文件 /******************************************/ /* 数据库全名 nacos_config */ /* 表名称 config_info */ /******************************************/ CREATE T…

【算法】算法题-20231128

这里写目录标题 一、55. 跳跃游戏二、274. H 指数三、125. 验证回文串 一、55. 跳跃游戏 给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以&am…

前端监控学习笔记

现成的SDK SentryFun Debug 需要监控什么? 错误统计 记录我们代码发布到线上各种奇奇怪怪的错误 行为日志埋点 记录用户行为,比如:分析用户浏览时间比较长的页面有哪些,常常点击的有哪些,可以做 相应的推荐 PV/UV统…

自定义类型:结构体、联合、枚举

目录 一、⾃定义类型:结构体 1.结构体类型 1. 1结构体类型的声明 结构体变量的创建和初始化 1.2 结构的特殊声明 1.3 结构的自引用 2. 结构体内存对齐 ①:对齐规则 ②:offsetof函数 ③:为什么存在内存对⻬? ④ 修改默认对⻬…

基于算能的国产AI边缘计算盒子,8核心A53丨10.6Tops算力

边缘计算盒子 8核心A53丨10.6Tops算力 ● 算力高达10.6TOPS,单芯片最高支持8路H.264 & H.265的实时解码能力。 ● 可扩展4G/5G/WIFI无线网络方式,为边缘化业务部署提供便利。 ● 支持RS232/RS485/USB2.0/USB3.0/HDMI OUT/双千兆以太网等。 ● 低功耗设计&a…

hls实现播放m3u8视频将视频流进行切片 HLS.js简介

github官网GitHub - video-dev/hls.js: HLS.js is a JavaScript library that plays HLS in browsers with support for MSE.HLS.js is a JavaScript library that plays HLS in browsers with support for MSE. - GitHub - video-dev/hls.js: HLS.js is a JavaScript library …

BUUCTF-WEB-刷题记录(2)

[网鼎杯 2018]Fakebook 注册一个账户,进去之后查看源代码,感觉存在注入点 是数字型注入,payload: 1%20and(false) 1%20and(true)判断列数 1 order by 5改为4的时候则页面正常 判断显示位,可以看见第二列存在数据回…