• 2.3 任务间通信
    • 2.3.1 互斥量
      • 概述
      • API讲解
      • 编程实例
      • 运行效果
    • 2.3.2 信号量
      • 概述
      • API讲解
      • 编程实例
      • 运行效果
    • 2.3.3 事件
      • 概述
      • API讲解
      • 编程实例
      • 运行效果
    • 2.3.4 队列
      • 概述
      • API讲解
      • 编程实例
      • 运行效果

    2.3 任务间通信

    2.3.1 互斥量

    概述

    互斥量又称互斥锁,一般用于共享资源的互斥排他性访问保护。

    互斥量在任意时刻处于且仅会处于解锁或锁定状态,当一个任务获取到一把锁后(互斥量锁定),其他任务再尝试获得这把锁时会失败或进入阻塞状态,当该任务释放持有的锁时(互斥量解锁),会唤醒一个正阻塞等待此互斥量的任务,被唤醒的任务将会获取这把锁。

    在多任务运行环境中,有些共享资源不具有多线程可重入性,对于这类不希望被多任务同时访问的资源(临界资源),可以采用互斥量来进行保护,后面的编程实例章节会演示这一编程范式。

    API讲解
    编程实例

    1、在tos_config.h中,配置互斥量组件开关TOS_CFG_MUTEX_EN:

    #define TOS_CFG_MUTEX_EN 1u

    2、编写main.c示例代码:

    1. #include "tos.h"
    2. #include "mcu_init.h"
    3.  
    4. #define STK_SIZE_TASK_WRITER 512
    5. #define STK_SIZE_TASK_READER 512
    6.  
    7. k_stack_t stack_task_writer[STK_SIZE_TASK_WRITER];
    8. k_stack_t stack_task_reader[STK_SIZE_TASK_READER];
    9.  
    10. k_task_t task_writer;
    11. k_task_t task_reader;
    12.  
    13. extern void entry_task_writer(void *arg);
    14. extern void entry_task_reader(void *arg);
    15.  
    16. k_mutex_t critical_resource_locker;
    17.  
    18. // 一片临界区内存
    19. static uint32_t critical_resource[3];
    20.  
    21. static void write_critical_resource(int salt)
    22. {
    23. size_t i = 0;
    24. // 此函数每次向共享内存中按递增顺序写入三个无符号整数
    25. printf("writting critical resource:\n");
    26. for (i = 0; i < 3; ++i) {
    27. printf("%d\t", salt + i);
    28. critical_resource[i] = salt + i;
    29. }
    30. printf("\n");
    31. }
    32.  
    33. void entry_task_writer(void *arg)
    34. {
    35. size_t salt = 0;
    36. k_err_t err;
    37.  
    38. while (K_TRUE) {
    39. // 在向临界区写入数据之前,先尝试获取临界区保护锁
    40. err = tos_mutex_pend(&critical_resource_locker);
    41. if (err == K_ERR_NONE) {
    42. // 成功获取锁之后,向临界区写入数据
    43. write_critical_resource(salt);
    44. // 写完数据后,释放互斥锁
    45. tos_mutex_post(&critical_resource_locker);
    46. }
    47. tos_task_delay(1000);
    48. ++salt;
    49. }
    50. }
    51.  
    52. static void read_critical_resource(void)
    53. {
    54. size_t i = 0;
    55.  
    56. // 从临界区读取数据
    57. printf("reading critical resource:\n");
    58. for (i = 0; i < 3; ++i) {
    59. printf("%d\t", critical_resource[i]);
    60. }
    61. printf("\n");
    62. }
    63.  
    64. void entry_task_reader(void *arg)
    65. {
    66. k_err_t err;
    67.  
    68. while (K_TRUE) {
    69. // 读取临界区数据之前,先尝试获取临界区保护锁
    70. err = tos_mutex_pend(&critical_resource_locker);
    71. if (err == K_ERR_NONE) {
    72. // 成功获取锁之后,从临界区读取数据
    73. read_critical_resource();
    74. // 读取数据完毕后,释放互斥锁
    75. tos_mutex_post(&critical_resource_locker);
    76. }
    77. tos_task_delay(1000);
    78. }
    79. }
    80.  
    81. int main(void)
    82. {
    83. board_init();
    84. tos_knl_init();
    85. // 创建临界区保护互斥锁
    86. tos_mutex_create(&critical_resource_locker);
    87. (void)tos_task_create(&task_writer, "writer", entry_task_writer, NULL,
    88. 4, stack_task_writer, STK_SIZE_TASK_WRITER, 0);
    89. (void)tos_task_create(&task_reader, "reader", entry_task_reader, NULL,
    90. 4, stack_task_reader, STK_SIZE_TASK_READER, 0);
    91. tos_knl_start();
    92. }
    运行效果

    writting critical resource:0 1 2reading critical resource:0 1 2writting critical resource:1 2 3reading critical resource:1 2 3writting critical resource:2 3 4reading critical resource:2 3 4writting critical resource:3 4 5reading critical resource:3 4 5writting critical resource:4 5 6reading critical resource:4 5 6writting critical resource:5 6 7reading critical resource:5 6 7writting critical resource:6 7 8reading critical resource:6 7 8writting critical resource:7 8 9reading critical resource:7 8 9

    实例代码

    2.3.2 信号量

    概述

    信号量是一种实现任务间同步的机制,一般用于多个任务间有限资源竞争访问。

    通常来说,一个信号量中持有一个整形数值,用以表示可用资源的数量。当一个信号量的可用资源数量大于0时,任务尝试获取该信号量成功,信号量的可用资源数减一;当一个信号量的可用资源数等于0时,任务尝试获取该信号量失败或进入阻塞状态。信号量的这一模式,当可用资源数为1时,可将其用于资源的互斥访问;或者解决生产者-消费者问题中的资源生产-消费问题。编程实例章节会演示生产者-消费者问题的解决范式。

    API讲解
    编程实例

    1、在tos_config.h中,配置信号量组件开关TOS_CFG_SEM_EN:

    #define TOS_CFG_SEM_EN 1u

    2、编写main.c示例代码:

    1. #include "tos.h"
    2. #include "mcu_init.h"
    3.  
    4. #define STK_SIZE_TASK_PRODUCER 512
    5. #define STK_SIZE_TASK_CONSUMER 512
    6.  
    7. k_stack_t stack_task_producer[STK_SIZE_TASK_PRODUCER];
    8. k_stack_t stack_task_consumer[STK_SIZE_TASK_CONSUMER];
    9.  
    10. k_task_t task_producer;
    11. k_task_t task_consumer;
    12.  
    13. extern void entry_task_producer(void *arg);
    14. extern void entry_task_consumer(void *arg);
    15.  
    16. k_mutex_t buffer_locker;
    17. k_sem_t full;
    18. k_sem_t empty;
    19.  
    20. #define RESOURCE_COUNT_MAX 3
    21.  
    22. struct resource_st {
    23. int cursor;
    24. uint32_t buffer[RESOURCE_COUNT_MAX];
    25. } resource = { 0, {0} };
    26.  
    27. static void produce_item(int salt)
    28. {
    29. printf("produce item:\n");
    30.  
    31. printf("%d", salt);
    32. resource.buffer[resource.cursor++] = salt;
    33. printf("\n");
    34. }
    35.  
    36. void entry_task_producer(void *arg)
    37. {
    38. size_t salt = 0;
    39. k_err_t err;
    40.  
    41. while (K_TRUE) {
    42. err = tos_sem_pend(&empty, TOS_TIME_FOREVER);
    43. if (err != K_ERR_NONE) {
    44. continue;
    45. }
    46. err = tos_mutex_pend(&buffer_locker);
    47. if (err != K_ERR_NONE) {
    48. continue;
    49. }
    50.  
    51. produce_item(salt);
    52.  
    53. tos_mutex_post(&buffer_locker);
    54. tos_sem_post(&full);
    55. tos_task_delay(1000);
    56. ++salt;
    57. }
    58. }
    59.  
    60. static void consume_item(void)
    61. {
    62. printf("cosume item:\n");
    63. printf("%d\t", resource.buffer[--resource.cursor]);
    64. printf("\n");
    65. }
    66.  
    67. void entry_task_consumer(void *arg)
    68. {
    69. k_err_t err;
    70.  
    71. while (K_TRUE) {
    72. err = tos_sem_pend(&full, TOS_TIME_FOREVER);
    73. if (err != K_ERR_NONE) {
    74. continue;
    75. }
    76. tos_mutex_pend(&buffer_locker);
    77. if (err != K_ERR_NONE) {
    78. continue;
    79. }
    80.  
    81. consume_item();
    82.  
    83. tos_mutex_post(&buffer_locker);
    84. tos_sem_post(&empty);
    85. tos_task_delay(2000);
    86. }
    87. }
    88.  
    89. int main(void)
    90. {
    91. board_init();
    92. tos_knl_init();
    93. tos_mutex_create(&buffer_locker);
    94. tos_sem_create(&full, 0);
    95. tos_sem_create(&empty, RESOURCE_COUNT_MAX);
    96. (void)tos_task_create(&task_producer, "producer", entry_task_producer, NULL,
    97. 4, stack_task_producer, STK_SIZE_TASK_PRODUCER, 0);
    98. (void)tos_task_create(&task_consumer, "consumer", entry_task_consumer, NULL,
    99. 4, stack_task_consumer, STK_SIZE_TASK_CONSUMER, 0);
    100. tos_knl_start();
    101. }
    运行效果

    produce iterm:0cosume iterm:0produce iterm:1produce iterm:2cosume iterm:2produce iterm:3produce iterm:4cosume iterm:4produce iterm:5cosume iterm:5produce iterm:6cosume iterm:6produce iterm:7cosume iterm:7produce iterm:8cosume iterm:8produce iterm:9cosume iterm:9produce iterm:10

    实例代码

    2.3.3 事件

    概述

    事件提供了一种任务间实现同步和信息传递的机制。一般来说,一个事件中包含了一个旗标,这个旗标的每一位表示一个“事件”。

    一个任务可以等待一个或者多个“事件”的发生,其他任务在一定的业务条件下可以通过写入特定“事件”唤醒等待此“事件”的任务,实现一种类似信号的编程范式。

    API讲解
    编程实例

    1、在tos_config.h中,配置事件组件开关TOS_CFG_EVENT_EN:

    #define TOS_CFG_EVENT_EN 1u

    2、编写main.c示例代码:

    1. #include "tos.h"
    2. #include "mcu_init.h"
    3.  
    4. #define STK_SIZE_TASK_LISTENER 512
    5. #define STK_SIZE_TASK_TRIGGER 512
    6.  
    7. k_stack_t stack_task_listener1[STK_SIZE_TASK_LISTENER];
    8. k_stack_t stack_task_listener2[STK_SIZE_TASK_LISTENER];
    9. k_stack_t stack_task_trigger[STK_SIZE_TASK_TRIGGER];
    10.  
    11. k_task_t task_listener1;
    12. k_task_t task_listener2;
    13. k_task_t task_trigger;
    14.  
    15. extern void entry_task_listener1(void *arg);
    16. extern void entry_task_listener2(void *arg);
    17. extern void entry_task_trigger(void *arg);
    18.  
    19. const k_event_flag_t event_eeny = (k_event_flag_t)(1 << 0);
    20. const k_event_flag_t event_meeny = (k_event_flag_t)(1 << 1);
    21. const k_event_flag_t event_miny = (k_event_flag_t)(1 << 2);
    22. const k_event_flag_t event_moe = (k_event_flag_t)(1 << 3);
    23.  
    24. k_event_t event;
    25.  
    26. void entry_task_listener1(void *arg)
    27. {
    28. k_event_flag_t flag_match;
    29. k_err_t err;
    30.  
    31. while (K_TRUE) {
    32. // 此任务监听四个事件,因为使用了TOS_OPT_EVENT_PEND_ALL选项,因此必须是四个事件同时到达此任务才会被唤醒
    33. err = tos_event_pend(&event, event_eeny | event_meeny | event_miny | event_moe,
    34. &flag_match, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ALL | TOS_OPT_EVENT_PEND_CLR);
    35. if (err == K_ERR_NONE) {
    36. printf("entry_task_listener1:\n");
    37. printf("eeny, meeny, miny, moe, they all come\n");
    38. }
    39. }
    40. }
    41.  
    42. void entry_task_listener2(void *arg)
    43. {
    44. k_event_flag_t flag_match;
    45. k_err_t err;
    46.  
    47. while (K_TRUE) {
    48. // 此任务监听四个事件,因为使用了TOS_OPT_EVENT_PEND_ANY选项,因此四个事件任意一个到达(包括四个事件同时到达)任务就会被唤醒
    49. err = tos_event_pend(&event, event_eeny | event_meeny | event_miny | event_moe,
    50. &flag_match, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);
    51. if (err == K_ERR_NONE) {
    52. printf("entry_task_listener2:\n");
    53. // 有事件到达,判断具体是哪个事件
    54. if (flag_match == event_eeny) {
    55. printf("eeny comes\n");
    56. }
    57. if (flag_match == event_meeny) {
    58. printf("meeny comes\n");
    59. }
    60. if (flag_match == event_miny) {
    61. printf("miny comes\n");
    62. }
    63. if (flag_match == event_moe) {
    64. printf("moe comes\n");
    65. }
    66. if (flag_match == (event_eeny | event_meeny | event_miny | event_moe)) {
    67. printf("all come\n");
    68. }
    69. }
    70. }
    71. }
    72.  
    73. void entry_task_trigger(void *arg)
    74. {
    75. int i = 1;
    76.  
    77. while (K_TRUE) {
    78. if (i == 2) {
    79. printf("entry_task_trigger:\n");
    80. printf("eeny will come\n");
    81. // 发送eeny事件,task_listener2会被唤醒
    82. tos_event_post(&event, event_eeny);
    83. }
    84. if (i == 3) {
    85. printf("entry_task_trigger:\n");
    86. printf("meeny will come\n");
    87. // 发送eeny事件,task_listener2会被唤醒
    88. tos_event_post(&event, event_meeny);
    89. }
    90. if (i == 4) {
    91. printf("entry_task_trigger:\n");
    92. printf("miny will come\n");
    93. // 发送eeny事件,task_listener2会被唤醒
    94. tos_event_post(&event, event_miny);
    95. }
    96. if (i == 5) {
    97. printf("entry_task_trigger:\n");
    98. printf("moe will come\n");
    99. // 发送eeny事件,task_listener2会被唤醒
    100. tos_event_post(&event, event_moe);
    101. }
    102. if (i == 6) {
    103. printf("entry_task_trigger:\n");
    104. printf("all will come\n");
    105. // 同时发送四个事件,因为task_listener1的优先级高于task_listener2,因此这里task_listener1会被唤醒
    106. tos_event_post(&event, event_eeny | event_meeny | event_miny | event_moe);
    107. }
    108. tos_task_delay(1000);
    109. ++i;
    110. }
    111. }
    112.  
    113. int main(void)
    114. {
    115. board_init();
    116. tos_knl_init();
    117. tos_event_create(&event, (k_event_flag_t)0u);
    118. // 这里task_listener1的优先级比task_listener2高,因此在task_trigger发送所有事件时,task_listener1会被唤醒
    119. // 读者可以尝试将task_listener1优先级修改为5(比task_listener2低),此设置下,在task_trigger发送所有事件时,task_listener2将会被唤醒。
    120. (void)tos_task_create(&task_listener1, "listener1", entry_task_listener1, NULL,
    121. 3, stack_task_listener1, STK_SIZE_TASK_LISTENER, 0);
    122. (void)tos_task_create(&task_listener2, "listener2", entry_task_listener2, NULL,
    123. 4, stack_task_listener2, STK_SIZE_TASK_LISTENER, 0);
    124. (void)tos_task_create(&task_trigger, "trigger", entry_task_trigger, NULL,
    125. 4, stack_task_trigger, STK_SIZE_TASK_TRIGGER, 0);
    126. tos_knl_start();
    127. }
    运行效果

    entry_task_trigger:eeny will comeentry_task_listener2:eeny comesentry_task_trigger:meeny will comeentry_task_listener2:meeny comesentry_task_trigger:miny will comeentry_task_listener2:miny comesentry_task_trigger:moe will comeentry_task_listener2:moe comesentry_task_trigger:all will comeentry_task_listener1:eeny, meeny, miny, moe, they all come

    实例代码

    2.3.4 队列

    概述

    队列提供了一种任务间实现同步和数据传递的机制。事件只能用于任务间传递某类“事件”是否发生的信号,无法传递更为复杂的数据,队列弥补了事件的这一不足,可以在任务间传递不定长度的消息。

    API讲解
    编程实例

    1、在tos_config.h中,配置队列组件开关TOS_CFG_QUEUE_EN:

    #define TOS_CFG_QUEUE_EN 1u

    2、在tos_config.h中,配置消息队列组件开关TOS_CFG_MSG_EN:

    #define TOS_CFG_MSG_EN 1u

    3、编写main.c示例代码:

    1. #include "tos.h"
    2. #include "mcu_init.h"
    3.  
    4. #define STK_SIZE_TASK_RECEIVER 512
    5. #define STK_SIZE_TASK_SENDER 512
    6.  
    7. #define PRIO_TASK_RECEIVER_HIGHER_PRIO 4
    8. #define PRIO_TASK_RECEIVER_LOWER_PRIO (PRIO_TASK_RECEIVER_HIGHER_PRIO + 1)
    9.  
    10. k_stack_t stack_task_receiver_higher_prio[STK_SIZE_TASK_RECEIVER];
    11. k_stack_t stack_task_receiver_lower_prio[STK_SIZE_TASK_RECEIVER];
    12. k_stack_t stack_task_sender[STK_SIZE_TASK_SENDER];
    13.  
    14. k_task_t task_receiver_higher_prio;
    15. k_task_t task_receiver_lower_prio;
    16. k_task_t task_sender;
    17.  
    18. k_queue_t queue;
    19.  
    20. extern void entry_task_receiver_higher_prio(void *arg);
    21. extern void entry_task_receiver_lower_prio(void *arg);
    22. extern void entry_task_sender(void *arg);
    23.  
    24. void entry_task_receiver_higher_prio(void *arg)
    25. {
    26. k_err_t err;
    27. void *msg_received;
    28. size_t msg_size;
    29.  
    30. while (K_TRUE) {
    31. err = tos_queue_pend(&queue, &msg_received, &msg_size, TOS_TIME_FOREVER);
    32. if (err == K_ERR_NONE) {
    33. printf("entry_task_receiver_higher_prio:\n");
    34. printf("message body: %s\n", (char *)msg_received);
    35. printf("message size: %d\n", msg_size);
    36. }
    37. }
    38. }
    39.  
    40. void entry_task_receiver_lower_prio(void *arg)
    41. {
    42. k_err_t err;
    43. void *msg_received;
    44. size_t msg_size;
    45.  
    46. while (K_TRUE) {
    47. err = tos_queue_pend(&queue, &msg_received, &msg_size, TOS_TIME_FOREVER);
    48. if (err == K_ERR_NONE) {
    49. printf("entry_task_receiver_lower_prio:\n");
    50. printf("message body: %s\n", (char *)msg_received);
    51. printf("message size: %d\n", msg_size);
    52. }
    53. }
    54. }
    55.  
    56. void entry_task_sender(void *arg)
    57. {
    58. int i = 1;
    59. char *msg_to_one_receiver = "message for one receiver[with highest priority]";
    60. char *msg_to_all_receiver = "message for all receivers";
    61.  
    62. // 此任务不断通过队列queue发送消息
    63. while (K_TRUE) {
    64. if (i == 2) {
    65. printf("entry_task_sender:\n");
    66. printf("send a message to one receiver, and shoud be the highest priority one\n");
    67. // 发送消息并唤醒一个等待任务,唤醒的应该是等待任务中优先级最高的
    68. tos_queue_post(&queue, msg_to_one_receiver, strlen(msg_to_one_receiver));
    69. }
    70. if (i == 3) {
    71. printf("entry_task_sender:\n");
    72. printf("send a message to all recevier\n");
    73. // 发送消息并唤醒所有正在等待的任务
    74. tos_queue_post_all(&queue, msg_to_all_receiver, strlen(msg_to_all_receiver));
    75. }
    76. if (i == 4) {
    77. printf("entry_task_sender:\n");
    78. printf("send a message to one receiver, and shoud be the highest priority one\n");
    79. // 发送消息并唤醒一个等待任务,唤醒的应该是等待任务中优先级最高的
    80. tos_queue_post(&queue, msg_to_one_receiver, strlen(msg_to_one_receiver));
    81. }
    82. if (i == 5) {
    83. printf("entry_task_sender:\n");
    84. printf("send a message to all recevier\n");
    85. // 发送消息并唤醒所有正在等待的任务
    86. tos_queue_post_all(&queue, msg_to_all_receiver, strlen(msg_to_all_receiver));
    87. }
    88. tos_task_delay(1000);
    89. ++i;
    90. }
    91. }
    92.  
    93. int main(void)
    94. {
    95. board_init();
    96. tos_knl_init();
    97. tos_queue_create(&queue);
    98. // task_receiver_higher_prio任务的优先级较高
    99. (void)tos_task_create(&task_receiver_higher_prio, "receiver_higher_prio",
    100. entry_task_receiver_higher_prio, NULL, PRIO_TASK_RECEIVER_HIGHER_PRIO,
    101. stack_task_receiver_higher_prio, STK_SIZE_TASK_RECEIVER, 0);
    102. // task_receiver_lower_prio任务的优先级较低
    103. (void)tos_task_create(&task_receiver_lower_prio, "receiver_lower_prio",
    104. entry_task_receiver_lower_prio, NULL, PRIO_TASK_RECEIVER_LOWER_PRIO,
    105. stack_task_receiver_lower_prio, STK_SIZE_TASK_RECEIVER, 0);
    106. (void)tos_task_create(&task_sender, "sender", entry_task_sender, NULL,
    107. 4, stack_task_sender, STK_SIZE_TASK_SENDER, 0);
    108. tos_knl_start();
    109. }
    运行效果

    entry_task_trigger:send a message to one receiver, and shoud be the highest priority oneentry_task_receiver_higher_prio:message body: message for one receiver[with highest priority]message size: 47entry_task_trigger:send a message to all recevierentry_task_receiver_higher_prio:message body: message for all receiversmessage size: 25entry_task_receiver_lower_prio:message body: message for all receiversmessage size: 25entry_task_trigger:send a message to one receiver, and shoud be the highest priority oneentry_task_receiver_higher_prio:message body: message for one receiver[with highest priority]message size: 47entry_task_trigger:send a message to all recevierentry_task_receiver_higher_prio:message body: message for all receiversmessage size: 25entry_task_receiver_lower_prio:message body: message for all receiversmessage size: 25

    实例代码