• 互斥量保护资源


    一、概念
     

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

    二、没有使用互斥量的时候

     配置中、高、低三个优先级

      

    1. osThreadDef(TaskH, StartTaskH, osPriorityAboveNormal, 0, 128);
    2. TaskHHandle = osThreadCreate(osThread(TaskH), NULL);
    3. osThreadDef(TaskM, StartTaskM, osPriorityNormal, 0, 128);
    4. TaskMHandle = osThreadCreate(osThread(TaskM), NULL);
    5. osThreadDef(TaskL, StartTaskL, osPriorityBelowNormal, 0, 128);
    6. TaskLHandle = osThreadCreate(osThread(TaskL), NULL);
    7. void StartTaskH(void const * argument)
    8. {
    9. for(;;)
    10. {
    11. xSemaphoreTake(myBinarySemHandle,portMAX_DELAY);
    12. printf("TaskH:我开始进入厕所,发功中。。\r\n");
    13. HAL_Delay(1000);
    14. printf("TaskH:我上完厕所了,真舒服。。。\r\n");
    15. xSemaphoreGive(myBinarySemHandle);
    16. osDelay(1000);
    17. }
    18. }
    19. void StartTaskM(void const * argument)
    20. {
    21. for(;;)
    22. {
    23. printf("TaskM:我就是为了占用资源,带女朋友兜风\r\n");
    24. osDelay(1000);
    25. }
    26. }
    27. void StartTaskL(void const * argument)
    28. {
    29. for(;;)
    30. {
    31. xSemaphoreTake(myBinarySemHandle,portMAX_DELAY);
    32. printf("TaskL:我开始进入厕所,发功中。。\r\n");
    33. HAL_Delay(3000);
    34. printf("TaskL:我上完厕所了,真舒服。。。\r\n");
    35. xSemaphoreGive(myBinarySemHandle);
    36. osDelay(1000);
    37. }
    38. }

     

    互斥量实验(接上半部分)

    首先删除二值信号量

     创建互斥量

    1. void MX_FREERTOS_Init(void) {
    2. osMutexDef(myMutex);
    3. myMutexHandle = osMutexCreate(osMutex(myMutex));
    4. osThreadDef(TaskH, StartTaskH, osPriorityAboveNormal, 0, 128);
    5. TaskHHandle = osThreadCreate(osThread(TaskH), NULL);
    6. osThreadDef(TaskM, StartTaskM, osPriorityNormal, 0, 128);
    7. TaskMHandle = osThreadCreate(osThread(TaskM), NULL);
    8. osThreadDef(TaskL, StartTaskL, osPriorityBelowNormal, 0, 128);
    9. TaskLHandle = osThreadCreate(osThread(TaskL), NULL);
    10. }
    11. void StartTaskH(void const * argument)
    12. {
    13. for(;;)
    14. {
    15. xSemaphoreTake(myMutexHandle,portMAX_DELAY);//句柄变为myMutexHandle
    16. printf("TaskH:我开始进入厕所,发功中。。\r\n");
    17. HAL_Delay(1000);
    18. printf("TaskH:我上完厕所了,真舒服。。。\r\n");
    19. xSemaphoreGive(myMutexHandle);
    20. osDelay(1000);
    21. }
    22. }
    23. void StartTaskM(void const * argument)
    24. {
    25. for(;;)
    26. {
    27. printf("TaskM:我就是为了占用资源,带女朋友兜风\r\n");
    28. osDelay(1000);
    29. }
    30. }
    31. void StartTaskL(void const * argument)
    32. {
    33. for(;;)
    34. {
    35. xSemaphoreTake(myMutexHandle,portMAX_DELAY);
    36. printf("TaskL:我开始进入厕所,发功中。。\r\n");
    37. HAL_Delay(3000);
    38. printf("TaskL:我上完厕所了,真舒服。。。\r\n");
    39. xSemaphoreGive(myMutexHandle);
    40. osDelay(1000);
    41. }
    42. }

    运行结果:

     通过引入互斥量,可以实现资源的保护功能。

    事件标志组

    一、概念

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

     

     

     

     二、实验

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

    CubeMX配置:配置任务1和任务2

     

    代码部分:

    Task1:

    1. void MX_FREERTOS_Init(void) {
    2. osThreadDef(Task1, StartTask1, osPriorityNormal, 0, 128);
    3. Task1Handle = osThreadCreate(osThread(Task1), NULL);
    4. osThreadDef(Task2, StartTask2, osPriorityNormal, 0, 128);
    5. Task2Handle = osThreadCreate(osThread(Task2), NULL);
    6. eventgroup_handle = xEventGroupCreate();//用动态方式创建事件标志组
    7. }
    8. void StartTask1(void const * argument)
    9. {
    10. for(;;)
    11. {
    12. //等待KEY1按下
    13. if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
    14. {
    15. osDelay(20);
    16. if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
    17. {
    18. xEventGroupSetBits(eventgroup_handle,0x01);//设置事件标志位(句柄,设置按位置);
    19. }
    20. while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)== GPIO_PIN_RESET);
    21. }
    22. //等待KEY2按下
    23. if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
    24. {
    25. osDelay(20);
    26. if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
    27. {
    28. xEventGroupSetBits(eventgroup_handle,0x02);//2为事件标志位
    29. }
    30. while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)== GPIO_PIN_RESET);
    31. }
    32. osDelay(1);
    33. }
    34. }

     Task2:

    1. void StartTask2(void const * argument)
    2. {
    3. EventBits_t event_bit = 0;//定义event_bit用于承接xEventGroupWaitBits(等待事件标志位返回值)
    4. for(;;)
    5. {
    6. event_bit = xEventGroupWaitBits(eventgroup_handle,0x01|0x02,pdTRUE,pdFALSE,portMAX_DELAY);//为pdFALSE时为逻辑或,形象理解就是一个领导通过,那么就全部通过。
    7. printf("返回值:%#x,请假成功,可以去大保健了!\r\n",event_bit);
    8. osDelay(1);
    9. }
    10. /* USER CODE END StartTask2 */
    11. }

    实验结果:pdFALSE——等待的事件位有一个为1(逻辑或) ,为pdTRUE——所有等待事件位全为1(逻辑与)。实验现象为pdFALSE的截图。

  • 相关阅读:
    示例:WPF中如何绑定ContextMenu和Menu
    ARM day2
    Webpack4从入门到精通以及和webpack5对比_webpack现在用的是哪个版本
    【机器学习】21天挑战赛学习笔记(四)
    不用写一行代码!Python最强自动化神器!
    UE4 剖析工具
    JS 流行框架(六):Animate
    [移动通讯]【Carrier Aggregation-4】【LTE-5】
    GO语言网络编程(并发编程)原子操作(atomic包)
    【开学季】如何过好大学最后一年(求赞)
  • 原文地址:https://blog.csdn.net/GoodtimeGood/article/details/134423402