怎么做化妆品网站内容规划,潍坊外贸建站,昊客网络,wordpress官使用方法1. 二值信号量简介#xff08;386.11#xff09;
什么是信号量#xff1f;
信号量#xff08;Semaphore#xff09;#xff0c;是在多任务环境下使用的一种机制#xff0c;是可以用来保证两个或多个关键代码段不被并 发调用。信号量这个名字#xff0c;我们可以把它拆…1. 二值信号量简介386.11
什么是信号量
信号量Semaphore是在多任务环境下使用的一种机制是可以用来保证两个或多个关键代码段不被并 发调用。信号量这个名字我们可以把它拆分来看信号可以起到通知信号的作用然后我们的量还可以用来表示资 源的数量当我们的量只有0和1的时候它就可以被称作二值信号量只有两个状态当我们的那个量没有 限制的时候它就可以被称作为计数型信号量。信号量也是队列的一种。
什么是二值信号量
二值信号量其实就是一个长度为1大小为零的队列只有0和1两种状态通常情况下我们用它来进行互 斥访问或任务同步。互斥访问比如门钥匙只有获取到钥匙才可以开门任务同步比如我录完视频你才可以看视频
二值信号量相关 API 函数 创建二值信号量
SemaphoreHandle_t xSemaphoreCreateBinary( void )参数 无 返回值 成功返回对应二值信号量的句柄失败返回 NULL 。
释放二值信号量
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )参数 xSemaphore要释放的信号量句柄 返回值 成功返回 pdPASS 失败返回 errQUEUE_FULL 。
获取二值信号量
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 函数 创建事件标志组
EventGroupHandle_t xEventGroupCreate( void );参数 无 返回值 成功返回对应事件标志组的句柄失败返回 NULL 。
设置事件标志位
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );参数 xEventGroup对应事件组句柄。uxBitsToSet指定要在事件组中设置的一个或多个位的按位值。 返回值 设置之后事件组中的事件标志位值。
清除事件标志位
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );参数 xEventGroup对应事件组句柄。uxBitsToClear指定要在事件组中清除的一个或多个位的按位值。 返回值 清零之前事件组中事件标志位的值。
等待事件标志位
EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait );参数 xEventGroup对应的事件标志组句柄uxBitsToWaitFor指定事件组中要等待的一个或多个事件位的按位值xClearOnExitpdTRUE——清除对应事件位pdFALSE——不清除xWaitForAllBitspdTRUE——所有等待事件位全为 1逻辑与pdFALSE——等待的事件位有一个为 1逻辑或xTicksToWait超时 返回值 等待的事件标志位值等待事件标志位成功返回等待到的事件标志位其他值等待事件标志位失败返回事件组中的事件标志位
7. 事件标志组实操392.17
实验需求
创建一个事件标志组和两个任务 task1 和 task2task1 检测按键如果检测到 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 提供以下几种方式发送通知给任务 发送消息给任务如果有通知未读 不覆盖通知值发送消息给任务直接覆盖通知值发送消息给任务设置通知值的一个或者多个位发送消息给任务递增通知值 通过对以上方式的合理使用可以在一定场合下替代原本的队列、信号量、事件标志组等。
任务通知的优势和劣势
任务通知的优势
使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多。使用其他方法时都要先创建对应的结构体使用任务通知时无需额外创建结构体。
任务通知的劣势
只有任务可以等待通知中断服务函数中不可以因为中断没有 TCB 。通知只能一对一因为通知必须指定任务。等待通知的任务可以被阻塞 但是发送消息的任务任何情况下都不会被阻塞等待。任务通知是通过更新任务通知值来发送数据的任务结构体中只有一个任务通知值只能保持一个数 据。
任务通知相关 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指定在成功接收通知后将通知值清零或减 1pdTRUE把通知值清零二值信号量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.muban 代码9.notify_binary_test
模拟计数型信号量
代码9. notify_counting_test基于9.notify_binary_test修改 3. 模拟事件标志组
代码9. notify_events_test基于9.notify_counting_test修改
模拟邮箱
代码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 、输入捕获、输出比较等等功能强大但是由于硬件的限制个数有限。软件定时器也可以实现定时功能达到定时时间后可调用回调函数可以在回调函数里处理信息。
软件定时器优缺点
优点
简单、成本低只要内存足够可创建多个
缺点 精度较低容易受中断影响。在大多数情况下够用但对于精度要求比较高的场合不建议使用。
软件定时器原理
定时器是一个可选的、不属于 FreeRTOS 内核的功能它是由定时器服务任务来提供的。在调用函数 vTaskStartScheduler() 开启任务调度器的时候会创建一个用于管理软件定时器的任务这个 任务就叫做软件定时器服务任务。
负责软件定时器超时的逻辑判断调用超时软件定时器的超时回调函数处理软件定时器命令队列
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 函数 创建软件定时器
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
开启软件定时器
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xBlockTime );参数 xTimer待开启的软件定时器的句柄xTickToWait发送命令到软件定时器命令队列的最大等待时间 返回值 pdPASS开启成功pdFAIL开启失败
停止软件定时器
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xBlockTime );参数与返回值同上。
复位软件定时器
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xBlockTime );参数与返回值同上。该功能将使软件定时器的重新开启定时复位后的软件定时器以复位时的时刻作为开启时刻重新定时。
更改软件定时器定时时间
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。
相关注意
在中断中必需使用中断相关的函数中断服务函数运行时间越短越好。
实验需求
创建一个队列及一个任务按下按键 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 */
}