04.Development_Manual.md 84.0 KB
Newer Older
S
supowang 已提交
1 2 3 4
# TencentOS tiny开发指南

目录

T
tommytim0515 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
- [TencentOS tiny开发指南](#tencentos-tiny开发指南)
  - [1. 概述](#1-概述)
    - [1.1 基础内核组件](#11-基础内核组件)
  - [2. 基础内核](#2-基础内核)
    - [2.1 系统管理](#21-系统管理)
      - [概述](#概述)
      - [API讲解](#api讲解)
      - [编程实例](#编程实例)
      - [运行效果](#运行效果)
    - [2.2 任务管理](#22-任务管理)
      - [概述](#概述-1)
      - [API讲解](#api讲解-1)
      - [编程实例](#编程实例-1)
      - [运行效果](#运行效果-1)
    - [2.3 任务间通信](#23-任务间通信)
      - [2.3.1 互斥量](#231-互斥量)
        - [概述](#概述-2)
        - [API讲解](#api讲解-2)
        - [编程实例](#编程实例-2)
        - [运行效果](#运行效果-2)
      - [2.3.2 信号量](#232-信号量)
        - [概述](#概述-3)
        - [API讲解](#api讲解-3)
        - [编程实例](#编程实例-3)
        - [运行效果](#运行效果-3)
      - [2.3.3 事件](#233-事件)
        - [概述](#概述-4)
        - [API讲解](#api讲解-4)
        - [编程实例](#编程实例-4)
        - [运行效果](#运行效果-4)
      - [2.3.4 完成量](#234-完成量)
        - [概述](#概述-5)
        - [API讲解](#api讲解-5)
        - [编程实例](#编程实例-5)
        - [运行效果](#运行效果-5)
      - [2.3.5 计数锁](#235-计数锁)
        - [概述](#概述-6)
        - [API讲解](#api讲解-6)
        - [编程实例](#编程实例-6)
        - [运行效果](#运行效果-6)
      - [2.3.6 栅栏](#236-栅栏)
        - [概述](#概述-7)
        - [API讲解](#api讲解-7)
        - [编程实例](#编程实例-7)
        - [运行效果](#运行效果-7)
      - [2.3.7 消息队列](#237-消息队列)
        - [概述](#概述-8)
        - [API讲解](#api讲解-8)
        - [编程实例](#编程实例-8)
        - [运行效果](#运行效果-8)
      - [2.3.8 邮箱队列](#238-邮箱队列)
        - [概述](#概述-9)
        - [API讲解](#api讲解-9)
        - [编程实例](#编程实例-9)
        - [运行效果](#运行效果-9)
      - [2.3.9 优先级消息队列](#239-优先级消息队列)
        - [概述](#概述-10)
        - [API讲解](#api讲解-10)
        - [编程实例](#编程实例-10)
        - [运行效果](#运行效果-10)
      - [2.3.10 优先级邮箱队列](#2310-优先级邮箱队列)
        - [概述](#概述-11)
        - [API讲解](#api讲解-11)
        - [编程实例](#编程实例-11)
        - [运行效果](#运行效果-11)
    - [2.4 内存管理](#24-内存管理)
      - [2.4.1 动态内存](#241-动态内存)
        - [概述](#概述-12)
        - [API讲解](#api讲解-12)
        - [编程实例](#编程实例-12)
        - [运行效果](#运行效果-12)
      - [2.4.2 静态内存](#242-静态内存)
        - [概述](#概述-13)
        - [API讲解](#api讲解-13)
        - [编程实例](#编程实例-13)
        - [运行效果](#运行效果-13)
    - [2.5 时间管理](#25-时间管理)
      - [概述](#概述-14)
      - [API讲解](#api讲解-14)
      - [编程实例](#编程实例-14)
      - [运行效果](#运行效果-14)
    - [2.6 软件定时器](#26-软件定时器)
      - [概述](#概述-15)
      - [API讲解](#api讲解-15)
      - [编程实例](#编程实例-15)
      - [运行效果](#运行效果-15)
    - [2.7 时间片轮转机制](#27-时间片轮转机制)
      - [概述](#概述-16)
      - [API讲解](#api讲解-16)
      - [编程实例](#编程实例-16)
      - [运行效果](#运行效果-16)
    - [2.8 内核基础组件](#28-内核基础组件)
      - [2.8.1 环形队列](#281-环形队列)
        - [概述](#概述-17)
        - [API讲解](#api讲解-17)
        - [编程实例](#编程实例-17)
        - [运行效果](#运行效果-17)
      - [2.8.2 字符流先入先出队列](#282-字符流先入先出队列)
        - [概述](#概述-18)
        - [API讲解](#api讲解-18)
        - [编程实例](#编程实例-18)
        - [运行效果](#运行效果-18)
      - [2.8.3 二项堆](#283-二项堆)
        - [概述](#概述-19)
      - [2.8.4 优先级队列](#284-优先级队列)
        - [概述](#概述-20)
        - [API讲解](#api讲解-19)
        - [编程实例](#编程实例-19)
        - [运行效果](#运行效果-19)
    - [2.9 功耗管理](#29-功耗管理)
      - [2.9.1 低功耗](#291-低功耗)
        - [概述](#概述-21)
        - [API讲解](#api讲解-20)
        - [编程实例](#编程实例-20)
        - [运行效果](#运行效果-20)
      - [2.9.2 tickless](#292-tickless)
        - [概述](#概述-22)
        - [API讲解](#api讲解-21)
        - [编程实例](#编程实例-21)
        - [运行效果](#运行效果-21)
S
supowang 已提交
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279

## 1. 概述

TencentOS tiny是面向物联网(IOT)领域的操作系统,由一个实现精简的实时操作系统(RTOS)内核,以及丰富的物联网组件组成。

### 1.1 基础内核组件

- **系统管理**

系统管理模块,主要提供了内核的初始化、内核运行启动,中断进入/退出流程托管、系统调度锁定及解锁等功能。

- **任务管理**

提供了任务的创建、删除、睡眠、取消睡眠、挂起、恢复、优先级修改、主动放弃CPU等功能。

- **任务间通信**

提供互斥量、信号量、队列、事件等常用任务间通信机制。

- **内存管理**

提供了基于堆的动态内存管理,以及静态内存块管理机制。

- **时间管理**

提供了获取/设置系统时钟滴答数、系统时钟滴答数与墙上时钟时间转换、基于墙上时钟时间的任务睡眠等机制。

- **软件定时器**

提供了软件定时器的创建、删除、启动、停止等机制。

- **时间片轮转机制**

TencentOS tiny内核在提供可抢占式调度内核基础上,还提供了按时间片轮转的robin机制。

- **内核基础组件**

提供了消息队列、字符流先入先出队列等机制。

- **功耗管理**

提供了CPU低功耗运行模式设置、低功耗设备注册、板级唤醒闹钟设置等机制。

## 2. 基础内核

### 2.1 系统管理

#### 概述

系统管理模块提供了几个接口,用以初始化/启动TencentOS tiny内核、锁定/解锁系统调度等。

#### API讲解

`k_err_t tos_knl_init(void);`

初始化内核。

`k_err_t tos_knl_start(void);`

启动运行内核,开始第一个任务调度。

`int tos_knl_is_running(void);`

判断内核是否已启动运行。

`void tos_knl_irq_enter(void);`

此函数应该在中断调用函数的最前端被调用。

`void tos_knl_irq_leave(void);`

此函数应该在中断调用函数的尾端被调用。

`k_err_t tos_knl_sched_lock(void);`

锁定系统调度,此函数被调用并返回K_ERR_NONE时,系统调度会被锁定,系统调度器不再进行任务的切换。

`k_err_t tos_knl_sched_unlock(void);`

解锁系统调度,允许任务切换。

#### 编程实例

#### 运行效果

### 2.2 任务管理

#### 概述

TencentOS tiny内核是单地址空间的可抢占式实时内核,TencentOS tiny内核不提供进程模型,任务对应线程的概念,是最小的调度运行体,也是最小的资源持有单位。

任务的本质是一个拥有独立栈空间的可调度运行实体,用户可以在任务的入口函数中编写自己的业务逻辑;多个任务之间可以通过系统提供的任务间通信机制进行同步或者信息传递等操作;每个任务都有优先级,高优先级任务可以抢占低优先级任务的运行。

#### API讲解

创建任务的系统api接口为tos_task_create,接口原型如下:

```c
k_err_t tos_task_create(k_task_t *task,
                        char *name,
                        k_task_entry_t entry,
                        void *arg,
                        k_prio_t prio,
                        k_stack_t *stk_base,
                        size_t stk_size,
                        k_timeslice_t timeslice);
```

这里详细讲解此api参数意义:

- task

  这是一个k_task_t类型的指针,k_task_t是内核的任务结构体类型。注意:task指针,应该指向生命周期大于待创建任务体生命周期的k_task_t类型变量,如果该指针指向的变量生命周期比待创建的任务体生命周期短,譬如可能是一个生命周期极端的函数栈上变量,可能会出现任务体还在运行而k_task_t变量已被销毁,会导致系统调度出现不可预知问题。

- name

  指向任务名字符串的指针。注意:同task,该指针指向的字符串生命周期应该大于待创建的任务体生命周期,一般来说,传入字符串常量指针即可。

- entry

  任务体运行的函数入口。当任务创建完毕进入运行状态后,entry是任务执行的入口,用户可以在此函数中编写业务逻辑。

- arg

  传递给任务入口函数的参数。

- prio

  任务优先级。prio的数值越小,优先级越高。用户可以在tos_config.h中,通过TOS_CFG_TASK_PRIO_MAX来配置任务优先级的最大数值,在内核的实现中,idle任务的优先级会被分配为TOS_CFG_TASK_PRIO_MAX - 1,此优先级只能被idle任务使用。因此对于一个用户创建的任务来说,合理的优先级范围应该为[0, TOS_CFG_TASK_PRIO_MAX - 2]。另外TOS_CFG_TASK_PRIO_MAX的配置值必需大于等于8。

- stk_base

  任务在运行时使用的栈空间的起始地址。注意:同task,该指针指向的内存空间的生命周期应该大于待创建的任务体生命周期。stk_base是k_stack_t类型的数组起始地址。

- stk_size

  任务的栈空间大小。注意:因为stk_base是k_stack_t类型的数组指针,因此实际栈空间所占内存大小为stk_size * sizeof(k_stack_t)。

- timeslice

  时间片轮转机制下当前任务的时间片大小。当timeslice为0时,任务调度时间片会被设置为默认大小(TOS_CFG_CPU_TICK_PER_SECOND / 10),系统时钟滴答(systick)数 / 10。

#### 编程实例

1、在tos_config.h中,配置最大任务优先级TOS_CFG_TASK_PRIO_MAX:

`#define TOS_CFG_TASK_PRIO_MAX           10u`

2、配置每秒钟的系统滴答数TOS_CFG_CPU_TICK_PER_SECOND:

`#define TOS_CFG_CPU_TICK_PER_SECOND     1000u`

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

```c
D
daishengdong 已提交
280
#include "tos_k.h"		// 添加TencentOS tiny内核接口头文件
S
supowang 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
#include "mcu_init.h"	// 包含mcu初始化头文件,里面有board_init等板级启动代码函数原型声明

#define STK_SIZE_TASK_PRIO4      512	// 优先级为4的任务栈大小为512
#define STK_SIZE_TASK_PRIO5      1024	// 优先级为5的任务栈大小为1024

k_stack_t stack_task_prio4[STK_SIZE_TASK_PRIO4];	// 优先级为4的任务栈空间
k_stack_t stack_task_prio5[STK_SIZE_TASK_PRIO5];	// 优先级为5的任务栈空间

k_task_t task_prio4;	// 优先级为4的任务体
k_task_t task_prio5;	// 优先级为5的任务体

extern void entry_task_prio4(void *arg);	// 优先级为4的任务体入口函数
extern void entry_task_prio5(void *arg);	// 优先级为5的任务体入口函数

uint32_t arg_task_prio4_array[3] = {	// 优先级为4的任务体入口函数入参
    1, 2, 3,
};

char *arg_task_prio5_string = "arg for task_prio5";	// 优先级为5的任务体入口函数入参

static void dump_uint32_array(uint32_t *array, size_t len)
{
    size_t i = 0;

    for (i = 0; i < len; ++i) {
        printf("%d\t", array[i]);
    }
    printf("\n\n");

}

void entry_task_prio4(void *arg)
{
    uint32_t *array_from_main = (uint32_t *)arg;	// 捕获调用者传入的参数

    printf("array from main:\n");
    dump_uint32_array(array_from_main, 3);	// dump传入的参数(数组)

    while (K_TRUE) {
        printf("task_prio4 body\n");	// 任务运行体,不断打印这条信息
        tos_task_delay(1000);	// 睡眠1000个系统时钟滴答(以下记作systick),因为TOS_CFG_CPU_TICK_PER_SECOND为1000,也就是一秒钟会有1000个systick,因此睡眠1000个systick就是睡眠了1秒。
    }
}

void entry_task_prio5(void *arg)
{
    int i = 0;
    char *string_from_main = (char *)arg;
    printf("string from main:\n");
    printf("%s\n\n", string_from_main);	// 打印出调用者传入的字符串参数

    while (K_TRUE) {
        if (i == 2) {
            printf("i = %d\n", i);	// i为2时,挂起task_prio4,task_prio4停止运行
            tos_task_suspend(&task_prio4);
        } else if (i == 4) {
            printf("i = %d\n", i);	// i为4时,恢复task_prio4的运行
            tos_task_resume(&task_prio4);
        } else if (i == 6) {
            printf("i = %d\n", i);	// i为6时,删除task_prio4,task_prio4不再运行
            tos_task_destroy(&task_prio4);
        }
        printf("task_prio5 body\n");
        tos_task_delay(1000);
        ++i;
    }
}

int main(void)
{
    board_init();	// 执行板级初始化代码,初始化串口等外设。
    tos_knl_init();	// 初始化TencentOS tiny内核
    // 创建一个优先级为4的任务
    (void)tos_task_create(&task_prio4, "task_prio4", entry_task_prio4,
                          (void *)(&arg_task_prio4_array[0]), 4,
                          stack_task_prio4, STK_SIZE_TASK_PRIO4, 0);
    // 创建一个优先级为5的任务
    (void)tos_task_create(&task_prio5, "task_prio5", entry_task_prio5,
                          (void *)arg_task_prio5_string, 5,
                          stack_task_prio5, STK_SIZE_TASK_PRIO5, 0);
    // 开始内核调度
    tos_knl_start();
}
```

#### 运行效果

> array from main:
> 1	2	3	
>
> task_prio4 body
> string from main:
> arg for task_prio5
>
> task_prio5 body
> task_prio4 body
> task_prio5 body
> task_prio4 body
> i = 2
> task_prio5 body
> task_prio5 body
> i = 4
> task_prio4 body
> task_prio5 body
> task_prio4 body
> task_prio5 body
> task_prio4 body
> i = 6
> task_prio5 body
> task_prio5 body
> task_prio5 body
> task_prio5 body
> task_prio5 body
> task_prio5 body
> task_prio5 body

T
tommytim0515 已提交
397
[实例代码](code/2.2%20task%20manager/main.c)
S
supowang 已提交
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

### 2.3 任务间通信

#### 2.3.1 互斥量

##### 概述

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

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

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

##### API讲解

##### 编程实例

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

`#define TOS_CFG_MUTEX_EN		1u`

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

```c
D
daishengdong 已提交
422
#include "tos_k.h"
S
supowang 已提交
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
#include "mcu_init.h"

#define STK_SIZE_TASK_WRITER        512
#define STK_SIZE_TASK_READER        512

k_stack_t stack_task_writer[STK_SIZE_TASK_WRITER];
k_stack_t stack_task_reader[STK_SIZE_TASK_READER];

k_task_t task_writer;
k_task_t task_reader;

extern void entry_task_writer(void *arg);
extern void entry_task_reader(void *arg);

k_mutex_t critical_resource_locker;

// 一片临界区内存
static uint32_t critical_resource[3];

static void write_critical_resource(int salt)
{
    size_t i = 0;
	// 此函数每次向共享内存中按递增顺序写入三个无符号整数
    printf("writting critical resource:\n");
    for (i = 0; i < 3; ++i) {
        printf("%d\t", salt + i);
        critical_resource[i] = salt + i;
    }
    printf("\n");
}

void entry_task_writer(void *arg)
{
    size_t salt = 0;
    k_err_t err;

    while (K_TRUE) {
        // 在向临界区写入数据之前,先尝试获取临界区保护锁
        err = tos_mutex_pend(&critical_resource_locker);
        if (err == K_ERR_NONE) {
            // 成功获取锁之后,向临界区写入数据
            write_critical_resource(salt);
            // 写完数据后,释放互斥锁
            tos_mutex_post(&critical_resource_locker);
        }
        tos_task_delay(1000);
        ++salt;
    }
}

static void read_critical_resource(void)
{
    size_t i = 0;

    // 从临界区读取数据
    printf("reading critical resource:\n");
    for (i = 0; i < 3; ++i) {
        printf("%d\t", critical_resource[i]);
    }
    printf("\n");
}

void entry_task_reader(void *arg)
{
    k_err_t err;

    while (K_TRUE) {
        // 读取临界区数据之前,先尝试获取临界区保护锁
        err = tos_mutex_pend(&critical_resource_locker);
        if (err == K_ERR_NONE) {
            // 成功获取锁之后,从临界区读取数据
            read_critical_resource();
            // 读取数据完毕后,释放互斥锁
            tos_mutex_post(&critical_resource_locker);
        }
        tos_task_delay(1000);
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
    // 创建临界区保护互斥锁
    tos_mutex_create(&critical_resource_locker);
    (void)tos_task_create(&task_writer, "writer", entry_task_writer, NULL,
                            4, stack_task_writer, STK_SIZE_TASK_WRITER, 0);
    (void)tos_task_create(&task_reader, "reader", entry_task_reader, NULL,
                            4, stack_task_reader, STK_SIZE_TASK_READER, 0);
    tos_knl_start();
}
```

##### 运行效果

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

T
tommytim0515 已提交
551
[实例代码](code/2.3.1%20mutex/main.c)
S
supowang 已提交
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571

#### 2.3.2 信号量

##### 概述

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

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

##### API讲解

##### 编程实例

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

`#define TOS_CFG_SEM_EN		1u`

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

```c
D
daishengdong 已提交
572
#include "tos_k.h"
S
supowang 已提交
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
#include "mcu_init.h"

#define STK_SIZE_TASK_PRODUCER      512
#define STK_SIZE_TASK_CONSUMER      512

k_stack_t stack_task_producer[STK_SIZE_TASK_PRODUCER];
k_stack_t stack_task_consumer[STK_SIZE_TASK_CONSUMER];

k_task_t task_producer;
k_task_t task_consumer;

extern void entry_task_producer(void *arg);
extern void entry_task_consumer(void *arg);

k_mutex_t buffer_locker;
k_sem_t full;
k_sem_t empty;

#define RESOURCE_COUNT_MAX      3

struct resource_st {
    int cursor;
    uint32_t buffer[RESOURCE_COUNT_MAX];
} resource = { 0, {0} };

static void produce_item(int salt)
{
    printf("produce item:\n");

    printf("%d", salt);
    resource.buffer[resource.cursor++] = salt;
    printf("\n");
}

void entry_task_producer(void *arg)
{
    size_t salt = 0;
    k_err_t err;

    while (K_TRUE) {
        err = tos_sem_pend(&empty, TOS_TIME_FOREVER);
        if (err != K_ERR_NONE) {
            continue;
        }
        err = tos_mutex_pend(&buffer_locker);
        if (err != K_ERR_NONE) {
            continue;
        }

        produce_item(salt);

        tos_mutex_post(&buffer_locker);
        tos_sem_post(&full);
        tos_task_delay(1000);
        ++salt;
    }
}

static void consume_item(void)
{
    printf("cosume item:\n");
    printf("%d\t", resource.buffer[--resource.cursor]);
    printf("\n");
}

void entry_task_consumer(void *arg)
{
    k_err_t err;

    while (K_TRUE) {
        err = tos_sem_pend(&full, TOS_TIME_FOREVER);
        if (err != K_ERR_NONE) {
            continue;
        }
        tos_mutex_pend(&buffer_locker);
        if (err != K_ERR_NONE) {
            continue;
        }

        consume_item();

        tos_mutex_post(&buffer_locker);
        tos_sem_post(&empty);
        tos_task_delay(2000);
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
    tos_mutex_create(&buffer_locker);
    tos_sem_create(&full, 0);
    tos_sem_create(&empty, RESOURCE_COUNT_MAX);
    (void)tos_task_create(&task_producer, "producer", entry_task_producer, NULL,
                            4, stack_task_producer, STK_SIZE_TASK_PRODUCER, 0);
    (void)tos_task_create(&task_consumer, "consumer", entry_task_consumer, NULL,
                            4, stack_task_consumer, STK_SIZE_TASK_CONSUMER, 0);
    tos_knl_start();
}
```

##### 运行效果

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

T
tommytim0515 已提交
716
[实例代码](code/2.3.2%20semaphore/main.c)
S
supowang 已提交
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736

#### 2.3.3 事件

##### 概述

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

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

##### API讲解

##### 编程实例

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

`#define TOS_CFG_EVENT_EN		1u`

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

```c
D
daishengdong 已提交
737
#include "tos_k.h"
S
supowang 已提交
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
#include "mcu_init.h"

#define STK_SIZE_TASK_LISTENER      512
#define STK_SIZE_TASK_TRIGGER       512

k_stack_t stack_task_listener1[STK_SIZE_TASK_LISTENER];
k_stack_t stack_task_listener2[STK_SIZE_TASK_LISTENER];
k_stack_t stack_task_trigger[STK_SIZE_TASK_TRIGGER];

k_task_t task_listener1;
k_task_t task_listener2;
k_task_t task_trigger;

extern void entry_task_listener1(void *arg);
extern void entry_task_listener2(void *arg);
extern void entry_task_trigger(void *arg);

const k_event_flag_t event_eeny     = (k_event_flag_t)(1 << 0);
const k_event_flag_t event_meeny    = (k_event_flag_t)(1 << 1);
const k_event_flag_t event_miny     = (k_event_flag_t)(1 << 2);
const k_event_flag_t event_moe      = (k_event_flag_t)(1 << 3);

k_event_t event;

void entry_task_listener1(void *arg)
{
    k_event_flag_t flag_match;
    k_err_t err;

    while (K_TRUE) {
        // 此任务监听四个事件,因为使用了TOS_OPT_EVENT_PEND_ALL选项,因此必须是四个事件同时到达此任务才会被唤醒
        err = tos_event_pend(&event, event_eeny | event_meeny | event_miny | event_moe,
                                &flag_match, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ALL | TOS_OPT_EVENT_PEND_CLR);
        if (err == K_ERR_NONE) {
            printf("entry_task_listener1:\n");
            printf("eeny, meeny, miny, moe, they all come\n");
        }
    }
}

void entry_task_listener2(void *arg)
{
    k_event_flag_t flag_match;
    k_err_t err;

    while (K_TRUE) {
        // 此任务监听四个事件,因为使用了TOS_OPT_EVENT_PEND_ANY选项,因此四个事件任意一个到达(包括四个事件同时到达)任务就会被唤醒
        err = tos_event_pend(&event, event_eeny | event_meeny | event_miny | event_moe,
                                &flag_match, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);
        if (err == K_ERR_NONE) {
            printf("entry_task_listener2:\n");
            // 有事件到达,判断具体是哪个事件
            if (flag_match == event_eeny) {
                printf("eeny comes\n");
            }
            if (flag_match == event_meeny) {
                printf("meeny comes\n");    
            }
            if (flag_match == event_miny) {
                printf("miny comes\n");
            }
            if (flag_match == event_moe) {
                printf("moe comes\n");
            }
            if (flag_match == (event_eeny | event_meeny | event_miny | event_moe)) {
                printf("all come\n");
            }
        }
    }
}

void entry_task_trigger(void *arg)
{
    int i = 1;

    while (K_TRUE) {
        if (i == 2) {
            printf("entry_task_trigger:\n");
            printf("eeny will come\n");
            // 发送eeny事件,task_listener2会被唤醒
            tos_event_post(&event, event_eeny);
        }
        if (i == 3) {
            printf("entry_task_trigger:\n");
            printf("meeny will come\n");
            // 发送eeny事件,task_listener2会被唤醒
            tos_event_post(&event, event_meeny);
        }
        if (i == 4) {
            printf("entry_task_trigger:\n");
            printf("miny will come\n");
            // 发送eeny事件,task_listener2会被唤醒
            tos_event_post(&event, event_miny);
        }
        if (i == 5) {
            printf("entry_task_trigger:\n");
            printf("moe will come\n");
            // 发送eeny事件,task_listener2会被唤醒
            tos_event_post(&event, event_moe);
        }
        if (i == 6) {
            printf("entry_task_trigger:\n");
            printf("all will come\n");
            // 同时发送四个事件,因为task_listener1的优先级高于task_listener2,因此这里task_listener1会被唤醒
            tos_event_post(&event, event_eeny | event_meeny | event_miny | event_moe);
        }
        tos_task_delay(1000);
        ++i;
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
    tos_event_create(&event, (k_event_flag_t)0u);
    // 这里task_listener1的优先级比task_listener2高,因此在task_trigger发送所有事件时,task_listener1会被唤醒
    // 读者可以尝试将task_listener1优先级修改为5(比task_listener2低),此设置下,在task_trigger发送所有事件时,task_listener2将会被唤醒。
    (void)tos_task_create(&task_listener1, "listener1", entry_task_listener1, NULL,
                            3, stack_task_listener1, STK_SIZE_TASK_LISTENER, 0);
    (void)tos_task_create(&task_listener2, "listener2", entry_task_listener2, NULL,
                            4, stack_task_listener2, STK_SIZE_TASK_LISTENER, 0);
    (void)tos_task_create(&task_trigger, "trigger", entry_task_trigger, NULL,
                            4, stack_task_trigger, STK_SIZE_TASK_TRIGGER, 0);
    tos_knl_start();
}
```

##### 运行效果

> entry_task_trigger:
> eeny will come
> entry_task_listener2:
> eeny comes
> entry_task_trigger:
> meeny will come
> entry_task_listener2:
> meeny comes
> entry_task_trigger:
> miny will come
> entry_task_listener2:
> miny comes
> entry_task_trigger:
> moe will come
> entry_task_listener2:
> moe comes
> entry_task_trigger:
> all will come
> entry_task_listener1:
> eeny, meeny, miny, moe, they all come

T
tommytim0515 已提交
889
[实例代码](code/2.3.3%20event/main.c)
S
supowang 已提交
890

D
daishengdong 已提交
891
#### 2.3.4 完成量
S
supowang 已提交
892 893 894

##### 概述

D
daishengdong 已提交
895
完成量是一种简单的任务间通信机制,用以在任务间同步某一事件是否已“完成”的信息。
S
supowang 已提交
896 897 898 899 900

##### API讲解

##### 编程实例

D
daishengdong 已提交
901
1、编写main.c示例代码:
S
supowang 已提交
902

D
daishengdong 已提交
903 904 905 906
```c
/*
 这个例子里创建了两个任务,一个任务task_wait等待完成量完成,另一个任务负责触发完成量(让完成量完成)
 */
S
supowang 已提交
907

D
daishengdong 已提交
908
#include "tos_k.h"
D
daishengdong 已提交
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
#include "mcu_init.h"

#define STK_SIZE_TASK_WAIT      512
#define STK_SIZE_TASK_TRIGGER   512

k_stack_t stack_task_wait[STK_SIZE_TASK_WAIT];
k_stack_t stack_task_trigger[STK_SIZE_TASK_TRIGGER];

k_task_t task_wait;
k_task_t task_trigger;

k_completion_t completion;

void entry_task_wait(void *arg)
{
    printf("wait: I won't go further until someone do the trigger(make it 'complete')\n");
    tos_completion_pend(&completion);
    printf("wait: someone has made it complete, so I'm here\n");
}

void entry_task_trigger(void *arg)
{
    printf("trigger: I'm the one who make complete, anyone waitting for the complete won't go further until I do the trigger\n");
    tos_completion_post(&completion);
    printf("trigger: I have done the completion\n");
}

int main(void)
{
    board_init();
    tos_knl_init();
    tos_completion_create(&completion);
    (void)tos_task_create(&task_wait, "wait", entry_task_wait, NULL,
                            3, stack_task_wait, STK_SIZE_TASK_WAIT, 0);
    (void)tos_task_create(&task_trigger, "trigger", entry_task_trigger, NULL,
                            4, stack_task_trigger, STK_SIZE_TASK_TRIGGER, 0);
    tos_knl_start();
}
```

##### 运行效果

> wait: I won't go further until someone do the trigger(make it 'complete')
> trigger: I'm the one who make complete, anyone waitting for the complete won't go further until I do the trigger
> wait: someone make it complete, so I'm here
> trigger: I have done the completion

T
tommytim0515 已提交
956
[实例代码](code/2.3.4%20completion/main.c)
D
daishengdong 已提交
957 958 959 960 961 962 963 964 965 966 967

#### 2.3.5 计数锁

##### 概述

计数锁提供了一种“计数信息”同步的概念,计数锁创建的时候会指定一个计数值,每当有任务执行tos_countdownlatch_post时,该计数锁的计数值减一,直到计数锁的计数值为零时,等待此计数锁的任务才会被唤醒。

##### API讲解

##### 编程实例

968
1、编写main.c示例代码:
S
supowang 已提交
969 970

```c
D
daishengdong 已提交
971 972 973 974
/*
 假设有这样的业务场景,共有三个勇士,此三个勇士分头去寻找三个武器的碎片,只有这三个勇士都找到碎片后,法师才能将三个碎片合成为武器。用代码看具体如何使用计数锁来完成这个模型。
 */

D
daishengdong 已提交
975
#include "tos_k.h"
D
daishengdong 已提交
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
#include "mcu_init.h"

#define STK_SIZE_TASK_WIZARD    512
#define STK_SIZE_TASK_WARRIOR   512

k_stack_t stack_task_wizard[STK_SIZE_TASK_WIZARD];
k_stack_t stack_task_warrior_0[STK_SIZE_TASK_WARRIOR];
k_stack_t stack_task_warrior_1[STK_SIZE_TASK_WARRIOR];
k_stack_t stack_task_warrior_2[STK_SIZE_TASK_WARRIOR];

k_task_t task_wizard;
k_task_t task_warrior_0;
k_task_t task_warrior_1;
k_task_t task_warrior_2;

k_countdownlatch_t countdownlatch;

void entry_task_warrior_0(void *arg)
{
    printf("warrior 0: I have done my job\n");
    tos_countdownlatch_post(&countdownlatch);
}

void entry_task_warrior_1(void *arg)
{
    printf("warrior 1: I have done my job\n");
    tos_countdownlatch_post(&countdownlatch);
}

void entry_task_warrior_2(void *arg)
{
    printf("warrior 2: I have done my job\n");
    tos_countdownlatch_post(&countdownlatch);
}

void entry_task_wizard(void *arg)
{
    printf("wizard: I will set 3 warriors to find the fragments\n");
    tos_countdownlatch_create(&countdownlatch, 3);
    (void)tos_task_create(&task_warrior_0, "warrior_0", entry_task_warrior_0, NULL,
D
daishengdong 已提交
1016
                                4, stack_task_warrior_0, STK_SIZE_TASK_WARRIOR, 0);
D
daishengdong 已提交
1017
    (void)tos_task_create(&task_warrior_1, "warrior_1", entry_task_warrior_1, NULL,
D
daishengdong 已提交
1018
                                4, stack_task_warrior_1, STK_SIZE_TASK_WARRIOR, 0);
D
daishengdong 已提交
1019
    (void)tos_task_create(&task_warrior_2, "warrior_2", entry_task_warrior_2, NULL,
D
daishengdong 已提交
1020
                                4, stack_task_warrior_2, STK_SIZE_TASK_WARRIOR, 0);
D
daishengdong 已提交
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
    printf("wizard: now warriors are on their way, I will wait here until they all done the job\n");
    tos_countdownlatch_pend(&countdownlatch);
    printf("wizard: the warriors all have done their jobs, let's make the weapon\n");
}

int main(void)
{
    board_init();
    tos_knl_init();
    (void)tos_task_create(&task_wizard, "wizard", entry_task_wizard, NULL,
                            3, stack_task_wizard, STK_SIZE_TASK_WIZARD, 0);
    tos_knl_start();
}
```

##### 运行效果

> wizard: I will set 3 warriors to find the fragments
> wizard: now warriors are on their way, I will wait here until they all done the job
> warrior 0: I have done my job
> warrior 1: I have done my job
> warrior 2: I have done my job
> wizard: the warriors all have done their jobs, let's make the weapon

T
tommytim0515 已提交
1045
[实例代码](code/2.3.5%20countdownlatch/main.c)
D
daishengdong 已提交
1046

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
#### 2.3.6 栅栏

##### 概述

栅栏提供了一种设置任务阻塞屏障的机制,栅栏创建的时候会指定一个计数值,每当有任务执行tos_barrier_pend时,该计数锁的计数值减一,直到计数锁的计数值为零时,所有阻塞在tos_barrier_pend点上的任务才可以往下运行。

##### API讲解

##### 编程实例

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

```c
/*
 假设有这样的业务场景,共有三个勇士,此三个勇士分头去寻找三个武器的碎片,任意勇士找到自己的那块武器碎片时,都在原地等待,直到所有的小伙伴都找到了自己的武器碎片后,才各自采取下一步行动。用代码看具体如何使用栅栏来完成这个模型。
 */

#include "tos_k.h"
#include "mcu_init.h"

#define STK_SIZE_TASK_WARRIOR   512

k_stack_t stack_task_warrior_0[STK_SIZE_TASK_WARRIOR];
k_stack_t stack_task_warrior_1[STK_SIZE_TASK_WARRIOR];
k_stack_t stack_task_warrior_2[STK_SIZE_TASK_WARRIOR];

k_task_t task_warrior_0;
k_task_t task_warrior_1;
k_task_t task_warrior_2;

k_barrier_t barrier;

void entry_task_warrior_0(void *arg)
{
    printf("warrior 0: I'm searching the fragment\n");
    tos_task_delay(1000);
    printf("warrior 0: I have done my job, waitting other buddies done their job\n");
    tos_barrier_pend(&barrier);
    printf("warrior 0: all buddies find their fragment, do my next job\n");
}

void entry_task_warrior_1(void *arg)
{
    printf("warrior 1: I'm searching the fragment\n");
    tos_task_delay(1500);
    printf("warrior 1: I have done my job, waitting other buddies done their job\n");
    tos_barrier_pend(&barrier);
    printf("warrior 1: all buddies find their fragment, do my next job\n");
}

void entry_task_warrior_2(void *arg)
{
    printf("warrior 2: I'm searching the fragment\n");
    tos_task_delay(2000);
    printf("warrior 2: I have done my job, waitting other buddies done their job\n");
    tos_barrier_pend(&barrier);
    printf("warrior 2: all buddies find their fragment, do my next job\n");
}

int main(void)
{
    board_init();
    tos_knl_init();
    tos_barrier_create(&barrier, 3);

    (void)tos_task_create(&task_warrior_0, "warrior_0", entry_task_warrior_0, NULL,
D
daishengdong 已提交
1113
                                4, stack_task_warrior_0, STK_SIZE_TASK_WARRIOR, 0);
1114
    (void)tos_task_create(&task_warrior_1, "warrior_1", entry_task_warrior_1, NULL,
D
daishengdong 已提交
1115
                                4, stack_task_warrior_1, STK_SIZE_TASK_WARRIOR, 0);
1116
    (void)tos_task_create(&task_warrior_2, "warrior_2", entry_task_warrior_2, NULL,
D
daishengdong 已提交
1117
                                4, stack_task_warrior_2, STK_SIZE_TASK_WARRIOR, 0);
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
    tos_knl_start();
}
```

##### 运行效果

> warrior 0: I'm searching the fragment
> warrior 1: I'm searching the fragment
> warrior 2: I'm searching the fragment
> warrior 0: I have done my job, waitting other buddies done their job
> warrior 1: I have done my job, waitting other buddies done their job
> warrior 2: I have done my job, waitting other buddies done their job
> warrior 2: all buddies find their fragment, do my next job
> warrior 0: all buddies find their fragment, do my next job
> warrior 1: all buddies find their fragment, do my next job

T
tommytim0515 已提交
1134
[实例代码](code/2.3.6%20barrier/main.c)
1135 1136

#### 2.3.7 消息队列
D
daishengdong 已提交
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156

##### 概述

消息队列提供了任务间传递指针数据的机制,所谓的“消息“就是指针。消息本身如何解析使用,由传递消息的两个任务自行规定,消息队列不对消息本身做任何规定和限制,消息队列仅承担指针数据的传递义务。

##### API讲解

##### 编程实例

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

`#define TOS_CFG_MESSAGE_QUEUE_EN		1u`

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

```c
/*
 这里演示如何使用消息队列在sender和receiver任务之间传递消息(一个指针,此案例中这个指针信息指向的是一个字符串)
 */

D
daishengdong 已提交
1157
#include "tos_k.h"
S
supowang 已提交
1158 1159 1160 1161 1162 1163 1164 1165
#include "mcu_init.h"

#define STK_SIZE_TASK_RECEIVER      512
#define STK_SIZE_TASK_SENDER        512

#define PRIO_TASK_RECEIVER_HIGHER_PRIO      4
#define PRIO_TASK_RECEIVER_LOWER_PRIO       (PRIO_TASK_RECEIVER_HIGHER_PRIO + 1)

D
daishengdong 已提交
1166 1167
#define MESSAGE_MAX     10

S
supowang 已提交
1168 1169 1170 1171
k_stack_t stack_task_receiver_higher_prio[STK_SIZE_TASK_RECEIVER];
k_stack_t stack_task_receiver_lower_prio[STK_SIZE_TASK_RECEIVER];
k_stack_t stack_task_sender[STK_SIZE_TASK_SENDER];

D
daishengdong 已提交
1172 1173
uint8_t msg_pool[MESSAGE_MAX * sizeof(void *)];

S
supowang 已提交
1174 1175 1176 1177
k_task_t task_receiver_higher_prio;
k_task_t task_receiver_lower_prio;
k_task_t task_sender;

D
daishengdong 已提交
1178
k_msg_q_t msg_q;
S
supowang 已提交
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189

extern void entry_task_receiver_higher_prio(void *arg);
extern void entry_task_receiver_lower_prio(void *arg);
extern void entry_task_sender(void *arg);

void entry_task_receiver_higher_prio(void *arg)
{
    k_err_t err;
    void *msg_received;

    while (K_TRUE) {
D
daishengdong 已提交
1190
        err = tos_msg_q_pend(&msg_q, &msg_received, TOS_TIME_FOREVER);
S
supowang 已提交
1191
        if (err == K_ERR_NONE) {
D
daishengdong 已提交
1192
            printf("higher: msg incoming[%s]\n", (char *)msg_received);
S
supowang 已提交
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
        }
    }
}

void entry_task_receiver_lower_prio(void *arg)
{
    k_err_t err;
    void *msg_received;

    while (K_TRUE) {
D
daishengdong 已提交
1203
        err = tos_msg_q_pend(&msg_q, &msg_received, TOS_TIME_FOREVER);
S
supowang 已提交
1204
        if (err == K_ERR_NONE) {
D
daishengdong 已提交
1205
            printf("lower: msg incoming[%s]\n", (char *)msg_received);
S
supowang 已提交
1206 1207 1208 1209 1210 1211 1212
        }
    }
}

void entry_task_sender(void *arg)
{
    int i = 1;
D
daishengdong 已提交
1213
    char *msg_to_one_receiver = "message for one receiver(with highest priority)";
S
supowang 已提交
1214 1215 1216 1217
    char *msg_to_all_receiver = "message for all receivers";

    while (K_TRUE) {
        if (i == 2) {
D
daishengdong 已提交
1218 1219
            printf("sender: send a message to one receiver, and shoud be the highest priority one\n");
            tos_msg_q_post(&msg_q, msg_to_one_receiver);
S
supowang 已提交
1220 1221
        }
        if (i == 3) {
D
daishengdong 已提交
1222 1223
            printf("sender: send a message to all recevier\n");
            tos_msg_q_post_all(&msg_q, msg_to_all_receiver);
S
supowang 已提交
1224 1225
        }
        if (i == 4) {
D
daishengdong 已提交
1226 1227
            printf("sender: send a message to one receiver, and shoud be the highest priority one\n");
            tos_msg_q_post(&msg_q, msg_to_one_receiver);
S
supowang 已提交
1228 1229
        }
        if (i == 5) {
D
daishengdong 已提交
1230 1231
            printf("sender: send a message to all recevier\n");
            tos_msg_q_post_all(&msg_q, msg_to_all_receiver);
S
supowang 已提交
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
        }
        tos_task_delay(1000);
        ++i;
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
D
daishengdong 已提交
1242
    tos_msg_q_create(&msg_q, msg_pool, MESSAGE_MAX);
S
supowang 已提交
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
    (void)tos_task_create(&task_receiver_higher_prio, "receiver_higher_prio",
                            entry_task_receiver_higher_prio, NULL, PRIO_TASK_RECEIVER_HIGHER_PRIO,
                            stack_task_receiver_higher_prio, STK_SIZE_TASK_RECEIVER, 0);
    (void)tos_task_create(&task_receiver_lower_prio, "receiver_lower_prio",
                            entry_task_receiver_lower_prio, NULL, PRIO_TASK_RECEIVER_LOWER_PRIO,
                            stack_task_receiver_lower_prio, STK_SIZE_TASK_RECEIVER, 0);
    (void)tos_task_create(&task_sender, "sender", entry_task_sender, NULL,
                            4, stack_task_sender, STK_SIZE_TASK_SENDER, 0);
    tos_knl_start();
}
```

##### 运行效果

D
daishengdong 已提交
1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
> sender: send a message to one receiver, and shoud be the highest priority one
> higher: msg incoming[message for one receiver(with highest priority)]
> sender: send a message to all recevier
> higher: msg incoming[message for all receivers]
> lower: msg incoming[message for all receivers]
> sender: send a message to one receiver, and shoud be the highest priority one
> higher: msg incoming[message for one receiver(with highest priority)]
> sender: send a message to all recevier
> higher: msg incoming[message for all receivers]
> lower: msg incoming[message for all receivers]

T
tommytim0515 已提交
1268
[实例代码](code/2.3.7%20message%20queue/main.c)
D
daishengdong 已提交
1269

1270
#### 2.3.8 邮箱队列
S
supowang 已提交
1271

D
daishengdong 已提交
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
##### 概述

消息队列传递的是指针,邮箱队列传递的是大片的内存数据。

##### API讲解

##### 编程实例

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

`#define TOS_CFG_MAIL_QUEUE_EN		1u`

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

```c
/*
 这里演示如何使用邮箱队列在sender和receiver任务之间传递邮箱(此案例中邮件,也就是邮箱要传递的内存数据为一个mail_t类型的结构体,从此案例中可以看出来,邮箱队列相对消息队列来说,可以传递更为复杂的内存块数据)
 */
D
daishengdong 已提交
1290
#include "tos_k.h"
D
daishengdong 已提交
1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416
#include "mcu_init.h"

#define STK_SIZE_TASK_RECEIVER      512
#define STK_SIZE_TASK_SENDER        512

#define PRIO_TASK_RECEIVER_HIGHER_PRIO      4
#define PRIO_TASK_RECEIVER_LOWER_PRIO       (PRIO_TASK_RECEIVER_HIGHER_PRIO + 1)

#define MAIL_MAX    10

k_stack_t stack_task_receiver_higher_prio[STK_SIZE_TASK_RECEIVER];
k_stack_t stack_task_receiver_lower_prio[STK_SIZE_TASK_RECEIVER];
k_stack_t stack_task_sender[STK_SIZE_TASK_SENDER];

typedef struct mail_st {
    char   *message;
    int     payload;
} mail_t;

uint8_t mail_pool[MAIL_MAX * sizeof(mail_t)];

k_task_t task_receiver_higher_prio;
k_task_t task_receiver_lower_prio;
k_task_t task_sender;

k_mail_q_t mail_q;

extern void entry_task_receiver_higher_prio(void *arg);
extern void entry_task_receiver_lower_prio(void *arg);
extern void entry_task_sender(void *arg);

void entry_task_receiver_higher_prio(void *arg)
{
    k_err_t err;
    mail_t mail;
    size_t mail_size;

    while (K_TRUE) {
        err = tos_mail_q_pend(&mail_q, &mail, &mail_size, TOS_TIME_FOREVER);
        if (err == K_ERR_NONE) {
            TOS_ASSERT(mail_size == sizeof(mail_t));
            printf("higher: msg incoming[%s], payload[%d]\n", mail.message, mail.payload);
        }
    }
}

void entry_task_receiver_lower_prio(void *arg)
{
    k_err_t err;
    mail_t mail;
    size_t mail_size;

    while (K_TRUE) {
        err = tos_mail_q_pend(&mail_q, &mail, &mail_size, TOS_TIME_FOREVER);
        if (err == K_ERR_NONE) {
            TOS_ASSERT(mail_size == sizeof(mail_t));
            printf("lower: msg incoming[%s], payload[%d]\n", mail.message, mail.payload);
        }
    }
}

void entry_task_sender(void *arg)
{
    int i = 1;
    mail_t mail;

    while (K_TRUE) {
        if (i == 2) {
            printf("sender: send a mail to one receiver, and shoud be the highest priority one\n");
            mail.message = "1st time post";
            mail.payload = 1;
            tos_mail_q_post(&mail_q, &mail, sizeof(mail_t));
        }
        if (i == 3) {
            printf("sender: send a message to all recevier\n");
            mail.message = "2nd time post";
            mail.payload = 2;
            tos_mail_q_post_all(&mail_q, &mail, sizeof(mail_t));
        }
        if (i == 4) {
            printf("sender: send a message to one receiver, and shoud be the highest priority one\n");
            mail.message = "3rd time post";
            mail.payload = 3;
            tos_mail_q_post(&mail_q, &mail, sizeof(mail_t));
        }
        if (i == 5) {
            printf("sender: send a message to all recevier\n");
            mail.message = "4th time post";
            mail.payload = 4;
            tos_mail_q_post_all(&mail_q, &mail, sizeof(mail_t));
        }
        tos_task_delay(1000);
        ++i;
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
    tos_mail_q_create(&mail_q, mail_pool, MAIL_MAX, sizeof(mail_t));
    (void)tos_task_create(&task_receiver_higher_prio, "receiver_higher_prio",
                            entry_task_receiver_higher_prio, NULL, PRIO_TASK_RECEIVER_HIGHER_PRIO,
                            stack_task_receiver_higher_prio, STK_SIZE_TASK_RECEIVER, 0);
    (void)tos_task_create(&task_receiver_lower_prio, "receiver_lower_prio",
                            entry_task_receiver_lower_prio, NULL, PRIO_TASK_RECEIVER_LOWER_PRIO,
                            stack_task_receiver_lower_prio, STK_SIZE_TASK_RECEIVER, 0);
    (void)tos_task_create(&task_sender, "sender", entry_task_sender, NULL,
                            5, stack_task_sender, STK_SIZE_TASK_SENDER, 0);
    tos_knl_start();
}
```

##### 运行效果

> sender: send a mail to one receiver, and shoud be the highest priority one
> higher: msg incoming[1st time post], payload[1]
> sender: send a message to all recevier
> higher: msg incoming[2nd time post], payload[2]
> lower: msg incoming[2nd time post], payload[2]
> sender: send a message to one receiver, and shoud be the highest priority one
> higher: msg incoming[3rd time post], payload[3]
> sender: send a message to all recevier
> higher: msg incoming[4th time post], payload[4]
> lower: msg incoming[4th time post], payload[4]

T
tommytim0515 已提交
1417
[实例代码](code/2.3.8%20mail%20queue/main.c)
D
daishengdong 已提交
1418

1419
#### 2.3.9 优先级消息队列
D
daishengdong 已提交
1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435

##### 概述

优先级消息队列相对消息队列来说,给消息附加了一个优先级的概念,较高优先级的消息会比较低优先级的消息更快地被其他任务收到(本质上,消息队列的底层数据容器是环形队列,优先级消息队列的底层数据容器是优先级队列)。

##### API讲解

##### 编程实例

1、在tos_config.h中,配置优先级消息队列组件开关TOS_CFG_PRIORITY_MESSAGE_QUEUE_EN:

`#define TOS_CFG_PRIORITY_MESSAGE_QUEUE_EN		1u`

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

```c
D
daishengdong 已提交
1436
#include "tos_k.h"
D
daishengdong 已提交
1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507
#include "mcu_init.h"

#define STK_SIZE_TASK_RECEIVER      512
#define STK_SIZE_TASK_SENDER        512

#define MESSAGE_MAX     10

k_stack_t stack_task_receiver[STK_SIZE_TASK_RECEIVER];
k_stack_t stack_task_sender[STK_SIZE_TASK_SENDER];

uint8_t msg_pool[MESSAGE_MAX * sizeof(void *)];

k_task_t task_receiver;
k_task_t task_sender;

k_prio_msg_q_t prio_msg_q;

extern void entry_task_receiver(void *arg);
extern void entry_task_sender(void *arg);

void entry_task_receiver(void *arg)
{
    k_err_t err;
    void *msg_received;

    while (K_TRUE) {
        err = tos_prio_msg_q_pend(&prio_msg_q, &msg_received, TOS_TIME_FOREVER);
        if (err == K_ERR_NONE) {
            printf("receiver: msg incoming[%s]\n", (char *)msg_received);
        }
    }
}

void entry_task_sender(void *arg)
{
    char *msg_prio_0 = "msg with priority 0";
    char *msg_prio_1 = "msg with priority 1";
    char *msg_prio_2 = "msg with priority 2";

    printf("sender: post a message with priority 2\n");
    tos_prio_msg_q_post(&prio_msg_q, msg_prio_2, 2);

    printf("sender: post a message with priority 1\n");
    tos_prio_msg_q_post(&prio_msg_q, msg_prio_1, 1);

    printf("sender: post a message with priority 0\n");
    tos_prio_msg_q_post(&prio_msg_q, msg_prio_0, 0);
}

int main(void)
{
    board_init();
    tos_knl_init();
    tos_prio_msg_q_create(&prio_msg_q, msg_pool, MESSAGE_MAX);
    (void)tos_task_create(&task_receiver, "receiver", entry_task_receiver, NULL,
                            5, stack_task_receiver, STK_SIZE_TASK_RECEIVER, 0);
    (void)tos_task_create(&task_sender, "sender", entry_task_sender, NULL,
                            4, stack_task_sender, STK_SIZE_TASK_SENDER, 0);
    tos_knl_start();
}
```

##### 运行效果

> sender: post a message with priority 2
> sender: post a message with priority 1
> sender: post a message with priority 0
> receiver: msg incoming[msg with priority 0]
> receiver: msg incoming[msg with priority 1]
> receiver: msg incoming[msg with priority 2]

T
tommytim0515 已提交
1508
[实例代码](code/2.3.9%20priority%20message%20queue/main.c)
D
daishengdong 已提交
1509

1510
#### 2.3.10 优先级邮箱队列
D
daishengdong 已提交
1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529

##### 概述

优先级邮箱队列相对邮箱队列来说,给邮件附加了一个优先级的概念,较高优先级的邮件会比较低优先级的邮件更快地被其他任务收到(本质上,邮箱队列的底层数据容器是环形队列,优先级邮箱队列的底层数据容器是优先级队列)。

##### API讲解

##### 编程实例

1、在tos_config.h中,配置优先级邮箱队列组件开关TOS_CFG_PRIORITY_MAIL_QUEUE_EN:

`#define TOS_CFG_PRIORITY_MAIL_QUEUE_EN		1u`

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

```c
/*
 这里演示了优先级邮箱队列的使用,从sender任务中的逻辑可以看出来,依次post了三个mail,优先级按时间顺序依次为2、1、0(数值越高优先级越低)。如果是传统的邮箱队列,那个receiver应该是依次收到优先级为2、1、0的邮件;但这是优先级邮箱队列,因而receiver会按优先级顺序收到这三个邮件,也就是依次收到优先级为0、1、2的邮件。
 */
D
daishengdong 已提交
1530
#include "tos_k.h"
D
daishengdong 已提交
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612
#include "mcu_init.h"

#define STK_SIZE_TASK_RECEIVER      512
#define STK_SIZE_TASK_SENDER        512

#define MAIL_MAX    10

k_stack_t stack_task_receiver[STK_SIZE_TASK_RECEIVER];
k_stack_t stack_task_sender[STK_SIZE_TASK_SENDER];

typedef struct mail_st {
    char   *message;
    int     payload;
} mail_t;

uint8_t mail_pool[MAIL_MAX * sizeof(mail_t)];

k_task_t task_receiver;
k_task_t task_sender;

k_prio_mail_q_t prio_mail_q;

extern void entry_task_receiver(void *arg);
extern void entry_task_sender(void *arg);

void entry_task_receiver(void *arg)
{
    k_err_t err;
    mail_t mail;
    size_t mail_size;

    while (K_TRUE) {
        err = tos_prio_mail_q_pend(&prio_mail_q, &mail, &mail_size, TOS_TIME_FOREVER);
        if (err == K_ERR_NONE) {
            TOS_ASSERT(mail_size == sizeof(mail_t));
            printf("receiver: msg incoming[%s], payload[%d]\n", mail.message, mail.payload);
        }
    }
}

void entry_task_sender(void *arg)
{
    mail_t mail_0, mail_1, mail_2;

    printf("sender: post a mail with priority 2\n");
    mail_2.message = "priority 2";
    mail_2.payload = 2;
    tos_prio_mail_q_post(&prio_mail_q, &mail_2, sizeof(mail_t), 2);

    printf("sender: post a mail with priority 1\n");
    mail_1.message = "priority 1";
    mail_1.payload = 1;
    tos_prio_mail_q_post_all(&prio_mail_q, &mail_1, sizeof(mail_t), 1);

    printf("sender: post a mail with priority 0\n");
    mail_0.message = "priority 0";
    mail_0.payload = 0;
    tos_prio_mail_q_post(&prio_mail_q, &mail_0, sizeof(mail_t), 0);
}

int main(void)
{
    board_init();
    tos_knl_init();
    tos_prio_mail_q_create(&prio_mail_q, mail_pool, MAIL_MAX, sizeof(mail_t));
    (void)tos_task_create(&task_receiver, "receiver", entry_task_receiver, NULL,
                            6, stack_task_receiver, STK_SIZE_TASK_RECEIVER, 0);
    (void)tos_task_create(&task_sender, "sender", entry_task_sender, NULL,
                            5, stack_task_sender, STK_SIZE_TASK_SENDER, 0);
    tos_knl_start();
}
```

##### 运行效果

> sender: post a mail with priority 2
> sender: post a mail with priority 1
> sender: post a mail with priority 0
> receiver: msg incoming[priority 0], payload[0]
> receiver: msg incoming[priority 1], payload[1]
> receiver: msg incoming[priority 2], payload[2]

T
tommytim0515 已提交
1613
[实例代码](code/2.3.10%20priority%20mail%20queue/main.c)
S
supowang 已提交
1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637

### 2.4 内存管理

#### 2.4.1 动态内存

##### 概述

动态内存管理模块,提供了一套动态管理系统内存的机制,支持用户动态的申请、释放不定长内存块。

##### API讲解

##### 编程实例

1、在tos_config.h中,配置动态内存组件开关TOS_CFG_MMHEAP_EN:

`#define TOS_CFG_MMHEAP_EN		1u`

2、在tos_config.h中,配置动态内存池大小:

`#define TOS_CFG_MMHEAP_POOL_SIZE	0x2000`

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

```c
D
daishengdong 已提交
1638
#include "tos_k.h"
S
supowang 已提交
1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717
#include "mcu_init.h"

#define STK_SIZE_TASK_DEMO      512

k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];

k_task_t task_demo;

extern void entry_task_demo(void *arg);

void entry_task_demo(void *arg)
{
    void *p = K_NULL, *p_aligned = K_NULL;
    int i = 0;

    while (K_TRUE) {
        if (i == 1) {
            p = tos_mmheap_alloc(0x30);
            if (p) {
                printf("alloc: %x\n", (cpu_addr_t)p);
            }
        } else if (i == 2) {
            if (p) {
                printf("free: %x\n", p);
                tos_mmheap_free(p);
            }
        } else if (i == 3) {
            p = tos_mmheap_alloc(0x30);
            if (p) {
                printf("alloc: %x\n", (cpu_addr_t)p);
            }            
        } else if (i == 4) {
            p_aligned = tos_mmheap_aligned_alloc(0x50, 16);
            if (p_aligned) {
                printf("aligned alloc: %x\n", (cpu_addr_t)p_aligned);
                if ((cpu_addr_t)p_aligned % 16 == 0) {
                    printf("%x is 16 aligned\n", (cpu_addr_t)p_aligned);
                } else {
                    printf("should not happen\n");
                }
            }
        } else if (i == 5) {
            p = tos_mmheap_realloc(p, 0x40);
            if (p) {
                printf("realloc: %x\n", (cpu_addr_t)p);
            }
        } else if (i == 6) {
            if (p) {
                tos_mmheap_free(p);
            }
            if (p_aligned) {
                tos_mmheap_free(p_aligned);
            }
        }

        tos_task_delay(1000);
        ++i;
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
    (void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
                            4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
    tos_knl_start();
}
```

##### 运行效果

> alloc: 20000c8c
> free: 20000c8c
> alloc: 20000c8c
> aligned alloc: 20000cc0
> 20000cc0 is 16 aligned
> realloc: 20000d14

T
tommytim0515 已提交
1718
[实例代码](code/2.4.1%20mmheap/main.c)
S
supowang 已提交
1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753

#### 2.4.2 静态内存

##### 概述

静态内存管理模块,提供了一套管理静态内存块的机制,支持用户申请、释放定长的内存块。

##### API讲解

创建静态内存池接口:

```c
k_err_t tos_mmblk_pool_create(k_mmblk_pool_t *mbp, void *pool_start, size_t blk_num, size_t blk_size);
```

这里详细讲解此api参数意义:

- mbp

  静态内存池句柄。

- pool_start

  静态内存池起始地址。

- blk_num

  内存池将要划分的内存块个数。

- blk_size

  每个内存块的大小。

##### 编程实例

1754
1、编写main.c示例代码:
S
supowang 已提交
1755 1756

```c
D
daishengdong 已提交
1757
#include "tos_k.h"
S
supowang 已提交
1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842
#include "mcu_init.h"

#define STK_SIZE_TASK_DEMO      512

k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];

k_task_t task_demo;

#define MMBLK_BLK_NUM       5
#define MMBLK_BLK_SIZE      0x20

k_mmblk_pool_t mmblk_pool;

// 需要管理的静态内存池
uint8_t mmblk_pool_buffer[MMBLK_BLK_NUM * MMBLK_BLK_SIZE];

// 记录从内存池中分配到的地址
void *p[MMBLK_BLK_NUM] = { K_NULL };

extern void entry_task_demo(void *arg);

void entry_task_demo(void *arg)
{
    void *p_dummy = K_NULL;
    k_err_t err;
    int i = 0;

    printf("mmblk_pool has %d blocks, size of each block is 0x%x\n", 5, 0x20);
    // 从内存池中获取所有的block
    for (; i < MMBLK_BLK_NUM; ++i) {
        err = tos_mmblk_alloc(&mmblk_pool, &p[i]);
        if (err == K_ERR_NONE) {
            printf("%d block alloced: 0x%x\n", i, p[i]);
        } else {
            printf("should not happen\n");
        }
    }

    // 前文逻辑已经将所有可用block分配完毕,继续分配会返回K_ERR_MMBLK_POOL_EMPTY错误码
    err = tos_mmblk_alloc(&mmblk_pool, &p_dummy);
    if (err == K_ERR_MMBLK_POOL_EMPTY) {
        printf("blocks exhausted, all blocks is alloced\n");
    } else {
        printf("should not happen\n");
    }

    // 将前文分配得到的所有block归还到池中
    for (i = 0; i < MMBLK_BLK_NUM; ++i) {
        err = tos_mmblk_free(&mmblk_pool, p[i]);
        if (err != K_ERR_NONE) {
            printf("should not happen\n");
        }
    }
    // 前文的归还动作中已经将所有的block归还到池中,继续规范会返回K_ERR_MMBLK_POOL_FULL错误码
    err = tos_mmblk_free(&mmblk_pool, p[0]);
    if (err == K_ERR_MMBLK_POOL_FULL) {
        printf("pool is full\n");
    } else {
        printf("should not happen\n");
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
    // 创建静态内存池
    tos_mmblk_pool_create(&mmblk_pool, mmblk_pool_buffer, MMBLK_BLK_NUM, MMBLK_BLK_SIZE);
    (void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
                            4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
    tos_knl_start();
}
```

##### 运行效果

> mmblk_pool has 5 blocks, size of each block is 0x20
> 0 block alloced: 0x20000974
> 1 block alloced: 0x20000994
> 2 block alloced: 0x200009b4
> 3 block alloced: 0x200009d4
> 4 block alloced: 0x200009f4
> blocks exhausted, all blocks is alloced
> pool is full

T
tommytim0515 已提交
1843
[实例代码](code/2.4.2%20mmblk/main.c)
S
supowang 已提交
1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861

### 2.5 时间管理

#### 概述

时间管理,提供了一族与时间相关的函数,可以获取/设置系统时钟滴答数(systick)、systick与毫秒单位之间互相转化、按毫秒、墙上时钟等单位进行任务睡眠的功能。

#### API讲解

#### 编程实例

1、配置每秒钟的系统滴答数TOS_CFG_CPU_TICK_PER_SECOND:

`#define TOS_CFG_CPU_TICK_PER_SECOND     1000u`

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

```c
D
daishengdong 已提交
1862
#include "tos_k.h"
S
supowang 已提交
1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913
#include "mcu_init.h"

#define STK_SIZE_TASK_DEMO      512

k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];

k_task_t task_demo;

extern void entry_task_demo(void *arg);

void entry_task_demo(void *arg)
{
    k_time_t ms;
    k_tick_t systick, after_systick;

    // 因为TOS_CFG_CPU_TICK_PER_SECOND为1000,也就是一秒钟会有1000个systick,因此1000个systick等于1000毫秒。
    systick = tos_millisec2tick(2000);
    printf("%d millisec equals to %lld ticks\n", 2000, systick);

    ms = tos_tick2millisec(1000);
    printf("%lld ticks equals to %d millisec\n", (k_tick_t)1000, ms);

    systick = tos_systick_get();
    printf("before sleep, systick is %lld\n", systick);

    tos_msleep(2000);

    after_systick = tos_systick_get();
    printf("after sleep %d ms, systick is %lld\n", 2000, after_systick);

    printf("milliseconds sleep is about: %d\n", tos_ticks2millisec(after_systick - systick));
}

int main(void)
{
    board_init();
    tos_knl_init();
    (void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
                            4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
    tos_knl_start();
}
```

#### 运行效果

> 2000 millisec equals to 2000 ticks
> 1000 ticks equals to 1000 millisec
> before sleep, systick is 7
> after sleep 2000 ms, systick is 2009
> milliseconds sleep is about: 2002

T
tommytim0515 已提交
1914
[实例代码](code/2.5%20time/main.c)
S
supowang 已提交
1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973

### 2.6 软件定时器

#### 概述

软件定时器提供了一套从软件层次实现的定时器机制,相对应的概念是硬件定时器。用户可以创建一系列的软件定时器,并指定软件定时器到期的条件以及执行回调,当软件定时器到期时会执行注册的回调。

通常来说,用户注册的软件定时器回调中很可能包含延迟动作或同步等待操作,或者回调函数本身逻辑复杂执行耗时较长,因此系统将软件定时器管理逻辑设计成一个任务,在这个任务中扫描定时器是否过期并执行定时器回调。但是如你所知,创建一个任务是需要消耗系统内存资源的(任务的栈、任务句柄本身的内存空间等等),而如果用户注册的软件定时器回调中并不包含延迟动作也不包含同步等待操作,或者回调本身执行耗时很短,这种情况下软件定时器管理逻辑无需被设计成任务,而是可以被设计成时钟中断中被调用的一个函数。当软件定时器管理逻辑被设计成一个函数时,就可以节省创建任务所需的资源。

系统默认采用的实现是将定时器管理逻辑设计为任务,当用户的定时器回调都是耗时极短的操作时,用户可以通过将软件定时器管理逻辑配置为函数来节省内存资源。通过在tos_config.h中打开TOS_CFG_TIMER_AS_PROC开关来讲软件定时器管理逻辑配置为函数:

`#define TOS_CFG_TIMER_AS_PROC		1u`

#### API讲解

```c
k_err_t tos_timer_create(k_timer_t *tmr,
                         k_tick_t delay,
                         k_tick_t period,
                         k_timer_callback_t callback,
                         void *cb_arg,
                         k_opt_t opt)
```

这里详细讲解此api参数意义:

- tmr

  软件定时器句柄。

- delay

  该定时器延迟多久后执行。

- period

  一个定时器的执行周期。

- callback

  定时器到期后的执行回调。

- cb_arg

  执行回调的入参。

- opt

  此opt的传入主要是界定tmr的属性,如果传入的是TOS_OPT_TIMER_ONESHOT,表明此tmr是一次性的,当delay时间到期,tmr的执行回调调用完毕后此tmr生命周期就结束了;如果传入的是TOS_OPT_TIMER_PERIODIC,表明此tmr是周期性定时器,当tmr定时时间到期,tmr的执行回调调用完毕后,系统会重新按period参数为到期时间将tmr加入到定时队列,开启下一个周期。

#### 编程实例

1、在tos_config.h中,配置软件定时器组件开关TOS_CFG_TIMER_EN:

`#define TOS_CFG_TIMER_EN	1000u`

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

```c
D
daishengdong 已提交
1974
#include "tos_k.h"
S
supowang 已提交
1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033
#include "mcu_init.h"

#define STK_SIZE_TASK_DEMO      512

k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];

k_task_t task_demo;

extern void entry_task_demo(void *arg);

void oneshot_timer_cb(void *arg)
{
    printf("this is oneshot timer callback, current systick: %lld\n", tos_systick_get());
}

void periodic_timer_cb(void *arg)
{
    printf("this is periodic timer callback, current systick: %lld\n", tos_systick_get());
}

void entry_task_demo(void *arg)
{
    k_timer_t oneshot_tmr;
    k_timer_t periodic_tmr;

    // 这是一个一次性的timer,且超时时间是3000个tick之后
    tos_timer_create(&oneshot_tmr, 3000, 0, oneshot_timer_cb, K_NULL, TOS_OPT_TIMER_ONESHOT);
    // 这是一个周期性的timer,第一次超时时间是2000个tick之后,之后按3000个tick为周期执行回调
    tos_timer_create(&periodic_tmr, 2000, 3000, periodic_timer_cb, K_NULL, TOS_OPT_TIMER_PERIODIC);

    printf("current systick: %lld\n", tos_systick_get());
    tos_timer_start(&oneshot_tmr);
    tos_timer_start(&periodic_tmr);

    while (K_TRUE) {
        tos_task_delay(1000);
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
    (void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
                            4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
    tos_knl_start();
}
```

#### 运行效果

> current systick: 0
> this is periodic timer callback, current systick: 2001
> this is oneshot timer callback, current systick: 3001
> this is periodic timer callback, current systick: 5001
> this is periodic timer callback, current systick: 8001
> this is periodic timer callback, current systick: 11001
> this is periodic timer callback, current systick: 14001

T
tommytim0515 已提交
2034
[实例代码](code/2.6%20timer/main.c)
S
supowang 已提交
2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056

### 2.7 时间片轮转机制

#### 概述

TencentOS tiny操作系统内核是一个抢占式内核,抢占式内核的特点是,如果最高优先级的任务不放弃CPU(调用tos_task_delay、tos_task_yeild等主动放权,或者任务间同步通信机制的pend接口等),那么CPU将会一直被此任务独占。

假设这样一种场景:系统中包含多个同等优先级的任务,且这几个任务体中都没有放弃CPU的行为,则会出现的情况是,这几个任务始终只有第一个被得到调度的那个在运行,因为第一个得到调度的任务体中不会主动放弃CPU,而其他任务优先级上与其相等无法抢占。此种场景下,其他任务会因得不到CPU而陷入饥饿状态。

时间片轮转机制提供了按时间片占用调度的策略,可以解决上述场景下的任务饥饿问题。

#### API讲解

#### 编程实例

1、在tos_config.h中,配置时间片轮转组件开关TOS_CFG_ROUND_ROBIN_EN:

`#define TOS_CFG_ROUND_ROBIN_EN		1u`

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

```c
D
daishengdong 已提交
2057
#include "tos_k.h"
S
supowang 已提交
2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125
#include "mcu_init.h"

#define STK_SIZE_TASK_DEMO      512
#define STK_SIZE_TASK_SAMPLE    512

/*
此代码中创建了两个同等优先级(PRIO_TASK_DEMO)的任务task_demo1、task_demo2,两个任务体中做的操作是不断对各自的计数器(demo1_counter、demo2_counter)做自增操作(没有放弃CPU的操作),时间片分别为timeslice_demo1、timeslice_demo2。
同时创建了一个优先级比task_demo1、task_demo2较高的采样任务task_sample,此任务不间歇的对两个计数器进行采样。在开启时间片轮转的情况下,task_demo1、task_demo2得到运行的时间比例应该是timeslice_demo1与timeslice_demo2的比例,那么demo1_counter和demo2_counter值的比例应该也差不多是timeslice_demo1与timeslice_demo2的比例。
*/

// task_demo1和task_demo2的优先级
#define PRIO_TASK_DEMO          4
// 采样任务的优先级
#define PRIO_TASK_SAMPLE        (PRIO_TASK_DEMO - 1)

// task_demo1的时间片,在tos_task_create时传入
const k_timeslice_t timeslice_demo1 = 10;
// task_demo2的时间片,在tos_task_create时传入
const k_timeslice_t timeslice_demo2 = 20;

k_stack_t stack_task_demo1[STK_SIZE_TASK_DEMO];
k_stack_t stack_task_demo2[STK_SIZE_TASK_DEMO];
k_stack_t stack_task_sample[STK_SIZE_TASK_SAMPLE];

k_task_t task_demo1;
k_task_t task_demo2;
k_task_t task_sample;

extern void entry_task_demo1(void *arg);
extern void entry_task_demo2(void *arg);
extern void entry_task_sample(void *arg);

uint64_t demo1_counter = 0;
uint64_t demo2_counter = 0;

void entry_task_demo1(void *arg)
{
    while (K_TRUE) {
        ++demo1_counter;
    }
}

void entry_task_demo2(void *arg)
{
    while (K_TRUE) {
        ++demo2_counter;
    }
}

void entry_task_sample(void *arg)
{
    while (K_TRUE) {
        ++demo2_counter;
        printf("demo1_counter: %lld\n", demo1_counter);
        printf("demo2_counter: %lld\n", demo2_counter);
        printf("demo2_counter / demo1_counter = %f\n",
            (double)demo2_counter / demo1_counter);
        printf("should almost equals to:\n");
        printf("timeslice_demo2 / timeslice_demo1 = %f\n\n", (double)timeslice_demo2 / timeslice_demo1);
        tos_task_delay(1000);
    }
}

int main(void)
{
    board_init();
    tos_knl_init();
    // 配置robin机制参数
S
SheldonDai 已提交
2126
    tos_robin_default_timeslice_config((k_timeslice_t)500u);
S
supowang 已提交
2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207
    (void)tos_task_create(&task_demo1, "demo1", entry_task_demo1, NULL,
                            PRIO_TASK_DEMO, stack_task_demo1, STK_SIZE_TASK_DEMO,
                            timeslice_demo1);
    (void)tos_task_create(&task_demo2, "demo2", entry_task_demo2, NULL,
                            PRIO_TASK_DEMO, stack_task_demo2, STK_SIZE_TASK_DEMO,
                            timeslice_demo2);
    (void)tos_task_create(&task_sample, "sample", entry_task_sample, NULL,
                            PRIO_TASK_SAMPLE, stack_task_sample, STK_SIZE_TASK_SAMPLE,
                            0);
    tos_knl_start();
}
```

#### 运行效果

> demo1_counter: 0
> demo2_counter: 1
> demo2_counter / demo1_counter = 0.000000
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 1915369
> demo2_counter: 3720158
> demo2_counter / demo1_counter = 1.942267
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 3774985
> demo2_counter: 7493508
> demo2_counter / demo1_counter = 1.985043
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 5634601
> demo2_counter: 11266858
> demo2_counter / demo1_counter = 1.999584
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 7546896
> demo2_counter: 14987015
> demo2_counter / demo1_counter = 1.985852
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 9406512
> demo2_counter: 18759340
> demo2_counter / demo1_counter = 1.994293
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 11266128
> demo2_counter: 22531664
> demo2_counter / demo1_counter = 1.999947
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 13177398
> demo2_counter: 26251821
> demo2_counter / demo1_counter = 1.992185
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 15037014
> demo2_counter: 30023632
> demo2_counter / demo1_counter = 1.996649
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 16896630
> demo2_counter: 33795443
> demo2_counter / demo1_counter = 2.000129
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000
>
> demo1_counter: 18807900
> demo2_counter: 37515600
> demo2_counter / demo1_counter = 1.994672
> should almost equals to:
> timeslice_demo2 / timeslice_demo1 = 2.000000

T
tommytim0515 已提交
2208
[实例代码](code/2.7%20robin/main.c)
S
supowang 已提交
2209 2210 2211

### 2.8 内核基础组件

D
daishengdong 已提交
2212
#### 2.8.1 环形队列
S
supowang 已提交
2213 2214 2215

##### 概述

D
daishengdong 已提交
2216
环形队列本质上就是支持先入先出操作的环形buffer,是系统的一个基础组件,通常用来作为实现上层机制的底层数据容器。
S
supowang 已提交
2217 2218 2219 2220 2221

##### API讲解

##### 编程实例

D
daishengdong 已提交
2222
1、编写main.c示例代码:
S
supowang 已提交
2223 2224

```c
D
daishengdong 已提交
2225
#include "tos_k.h"
S
supowang 已提交
2226 2227
#include "mcu_init.h"

D
daishengdong 已提交
2228
#define STK_SIZE_TASK_DEMO      512
S
supowang 已提交
2229 2230 2231

#define PRIO_TASK_DEMO          4

D
daishengdong 已提交
2232
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
S
supowang 已提交
2233

D
daishengdong 已提交
2234
k_task_t task_demo;
S
supowang 已提交
2235

D
daishengdong 已提交
2236 2237 2238 2239 2240
typedef struct item_st {
    int a;
    int b;
    int c;
} item_t;
S
supowang 已提交
2241

D
daishengdong 已提交
2242 2243
#define RING_QUEUE_ITEM_MAX        5
uint8_t ring_q_buffer[RING_QUEUE_ITEM_MAX * sizeof(item_t)];
S
supowang 已提交
2244

D
daishengdong 已提交
2245
k_ring_q_t rinq_q;
S
supowang 已提交
2246

D
daishengdong 已提交
2247 2248
void entry_task_demo(void *arg)
{
S
supowang 已提交
2249 2250
    k_err_t err;
    int i = 0;
D
daishengdong 已提交
2251 2252 2253 2254 2255 2256 2257 2258 2259 2260
    item_t item;
    size_t item_size;

    tos_ring_q_create(&rinq_q, ring_q_buffer, RING_QUEUE_ITEM_MAX, sizeof(item_t));
    for (i = 0; i < RING_QUEUE_ITEM_MAX; ++i) {
        printf("enqueue: %d  %d  %d\n", i, i, i);
        item.a = i;
        item.b = i;
        item.c = i;
        err = tos_ring_q_enqueue(&rinq_q, &item, sizeof(item_t));
S
supowang 已提交
2261 2262 2263 2264 2265
        if (err != K_ERR_NONE) {
            printf("should never happen\n");
        }
    }

D
daishengdong 已提交
2266 2267 2268
    err = tos_ring_q_enqueue(&rinq_q, &item, sizeof(item_t));
    if (err == K_ERR_RING_Q_FULL) {
        printf("ring queue is full: %s\n", tos_ring_q_is_full(&rinq_q) ? "TRUE" : "FALSE");
S
supowang 已提交
2269 2270 2271 2272
    } else {
        printf("should never happen\n");
    }

D
daishengdong 已提交
2273 2274
    for (i = 0; i < RING_QUEUE_ITEM_MAX; ++i) {
        err = tos_ring_q_dequeue(&rinq_q, &item, &item_size);
S
supowang 已提交
2275
        if (err == K_ERR_NONE) {
D
daishengdong 已提交
2276
            printf("dequeue: %d %d %d\n", item.a, item.b, item.c);
S
supowang 已提交
2277 2278 2279 2280
        } else {
            printf("should never happen\n");
        }
    }
D
daishengdong 已提交
2281 2282 2283 2284

    err = tos_ring_q_dequeue(&rinq_q, &item, &item_size);
    if (err == K_ERR_RING_Q_EMPTY) {
        printf("ring queue is empty: %s\n", tos_ring_q_is_empty(&rinq_q) ? "TRUE" : "FALSE");
S
supowang 已提交
2285 2286 2287 2288 2289 2290 2291 2292 2293
    } else {
        printf("should never happen\n");
    }

}

int main(void)
{
    board_init();
D
daishengdong 已提交
2294 2295
    tos_knl_init();
    (void)tos_task_create(&task_demo, "demo", entry_task_demo, NULL,
S
supowang 已提交
2296
                            PRIO_TASK_DEMO, stack_task_demo, STK_SIZE_TASK_DEMO,
D
daishengdong 已提交
2297
                            0);
S
supowang 已提交
2298 2299 2300 2301 2302 2303
    tos_knl_start();
}
```

##### 运行效果

D
daishengdong 已提交
2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316
> enqueue: 0  0  0
> enqueue: 1  1  1
> enqueue: 2  2  2
> enqueue: 3  3  3
> enqueue: 4  4  4
> ring queue is full: TRUE
> dequeue: 0 0 0
> dequeue: 1 1 1
> dequeue: 2 2 2
> dequeue: 3 3 3
> dequeue: 4 4 4
> ring queue is empty: TRUE

T
tommytim0515 已提交
2317
[实例代码](code/2.8.1%20ring%20queue/main.c)
S
supowang 已提交
2318 2319 2320 2321 2322

#### 2.8.2 字符流先入先出队列

##### 概述

D
daishengdong 已提交
2323
字符流先入先出队列,提供的是一个面向字符操作的环形队列实现,提供了基本的字符流入队出队操作。本质上就是环形队列中元素为字符(单字节长度)时的特例,实际上字符流先出先出队列底层的实现就是环形队列。
S
supowang 已提交
2324 2325 2326 2327 2328 2329 2330 2331

##### API讲解

##### 编程实例

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

```c
D
daishengdong 已提交
2332
#include "tos_k.h"
S
supowang 已提交
2333 2334
#include "mcu_init.h"

D
daishengdong 已提交
2335
#define STK_SIZE_TASK_DEMO      512
S
supowang 已提交
2336 2337

#define PRIO_TASK_DEMO          4
D
daishengdong 已提交
2338 2339

k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
S
supowang 已提交
2340 2341 2342 2343 2344 2345

k_task_t task_demo;

#define FIFO_BUFFER_SIZE        5
uint8_t fifo_buffer[FIFO_BUFFER_SIZE];

D
daishengdong 已提交
2346
k_chr_fifo_t fifo;
S
supowang 已提交
2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358

extern void entry_task_demo(void *arg);

void char_push(void)
{
    k_err_t err;
    int i = 0;
    uint8_t data;

    // 往fifo中压入FIFO_BUFFER_SIZE个字符,分别是a、b、c、d、e
    for (i = 0; i < FIFO_BUFFER_SIZE; ++i) {
        printf("char pushed: %c\n", 'a' + i);
D
daishengdong 已提交
2359
        err = tos_chr_fifo_push(&fifo, 'a' + i);
S
supowang 已提交
2360 2361 2362 2363 2364 2365
        if (err != K_ERR_NONE) {
            printf("should never happen\n");
        }
    }

    // fifo最多包含FIFO_BUFFER_SIZE个字符,上文逻辑中已经压入了最大的字符量,此时继续压入字符会返回K_ERR_FIFO_FULL(fifo已满)
D
daishengdong 已提交
2366 2367 2368
    err = tos_chr_fifo_push(&fifo, 'z');
    if (err == K_ERR_RING_Q_FULL) {
        printf("fifo is full: %s\n", tos_chr_fifo_is_full(&fifo) ? "TRUE" : "FALSE");
S
supowang 已提交
2369 2370 2371 2372 2373 2374
    } else {
        printf("should never happen\n");
    }

    // 从fifo中把所有的字符都弹出来
    for (i = 0; i < FIFO_BUFFER_SIZE; ++i) {
D
daishengdong 已提交
2375
        err = tos_chr_fifo_pop(&fifo, &data);
S
supowang 已提交
2376 2377 2378 2379 2380 2381 2382
        if (err == K_ERR_NONE) {
            printf("%d pop: %c\n", i, data);
        } else {
            printf("should never happen\n");
        }
    }
    // 此时继续弹出字符,会返回K_ERR_FIFO_EMPTY(fifo已空)
D
daishengdong 已提交
2383 2384 2385
    err = tos_chr_fifo_pop(&fifo, &data);
    if (err == K_ERR_RING_Q_EMPTY) {
        printf("fifo is empty: %s\n", tos_chr_fifo_is_empty(&fifo) ? "TRUE" : "FALSE");
S
supowang 已提交
2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398
    } else {
        printf("should never happen\n");
    }
}

void stream_push(void)
{
    int count = 0, i = 0;
    uint8_t stream[FIFO_BUFFER_SIZE] = { 'a', 'b', 'c', 'd', 'e' };
    uint8_t stream_dummy[1] = { 'z' };
    uint8_t stream_pop[FIFO_BUFFER_SIZE];

    // 压入字符流,字符流的长度是5,不超过fifo的最大长度FIFO_BUFFER_SIZE,会压入成功,并返回压入字符流的长度5
D
daishengdong 已提交
2399
    count = tos_chr_fifo_push_stream(&fifo, &stream[0], FIFO_BUFFER_SIZE);
S
supowang 已提交
2400 2401 2402 2403 2404
    if (count != FIFO_BUFFER_SIZE) {
        printf("should never happen\n");
    }

    // 继续压入字符流(即使是长度为1的字符流),因fifo已满无法继续压入,返回长度0(压入失败)
D
daishengdong 已提交
2405
    count = tos_chr_fifo_push_stream(&fifo, &stream_dummy[0], 1);
S
supowang 已提交
2406
    if (count == 0) {
D
daishengdong 已提交
2407
        printf("fifo is full: %s\n", tos_chr_fifo_is_full(&fifo) ? "TRUE" : "FALSE");
S
supowang 已提交
2408 2409 2410 2411 2412
    } else {
        printf("should never happen\n");
    }

    // 将前文中压入的字符流全部弹出,返回前文压入的字符流长度5(弹出的字符流长度)
D
daishengdong 已提交
2413
    count = tos_chr_fifo_pop_stream(&fifo, &stream_pop[0], FIFO_BUFFER_SIZE);
S
supowang 已提交
2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424
    if (count == FIFO_BUFFER_SIZE) {
        printf("stream popped:\n");
        for (i = 0; i < FIFO_BUFFER_SIZE; ++i) {
            printf("%c", stream_pop[i]);
        }
        printf("\n");
    } else {
        printf("should never happen\n");
    }

    // 继续弹出,因fifo已空,返回0
D
daishengdong 已提交
2425
    count = tos_chr_fifo_pop_stream(&fifo, &stream_pop[0], 1);
S
supowang 已提交
2426
    if (count == 0) {
D
daishengdong 已提交
2427
        printf("fifo is empty: %s\n", tos_chr_fifo_is_empty(&fifo) ? "TRUE" : "FALSE");
S
supowang 已提交
2428 2429 2430 2431 2432 2433 2434 2435
    } else {
        printf("should never happen\n");
    }
}

void entry_task_demo(void *arg)
{
    // 创建了一个最多包含FIFO_BUFFER_SIZE个字符的fifo
D
daishengdong 已提交
2436
    tos_chr_fifo_create(&fifo, &fifo_buffer[0], FIFO_BUFFER_SIZE);
S
supowang 已提交
2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447

    printf("fifo, dealing with char\n");
    char_push();

    printf("fifo, dealing with stream\n");
    stream_push();
}

int main(void)
{
    board_init();
D
daishengdong 已提交
2448 2449
    tos_knl_init();
    (void)tos_task_create(&task_demo, "demo", entry_task_demo, NULL,
S
supowang 已提交
2450
                            PRIO_TASK_DEMO, stack_task_demo, STK_SIZE_TASK_DEMO,
D
daishengdong 已提交
2451
                            0);
S
supowang 已提交
2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476
    tos_knl_start();
}
```

##### 运行效果

> fifo, dealing with char
> char pushed: a
> char pushed: b
> char pushed: c
> char pushed: d
> char pushed: e
> fifo is full: TRUE
> 0 pop: a
> 1 pop: b
> 2 pop: c
> 3 pop: d
> 4 pop: e
> fifo is empty: TRUE
> fifo, dealing with stream
> fifo is full: TRUE
> stream popped:
> abcde
> fifo is empty: TRUE

T
tommytim0515 已提交
2477
[实例代码](code/2.8.2%20char%20fifo/main.c)
D
daishengdong 已提交
2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501

#### 2.8.3 二项堆

##### 概述

此组件用来内部实现优先级队列,不推荐用户使用。

#### 2.8.4 优先级队列

##### 概述

提供了基于优先级的队列管理。环形队列的入队出队规则是先入队的先出队(first in, first out),优先级队列的出队顺序是按照优先级来的,优先级较高的元素先出队。

##### API讲解

##### 编程实例

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

```c
/*
 此案例展示了优先级队列的出队规则:依次入队优先级为5、4、3、2、1的元素,出队时的顺序是按照优先级来的,也就是按优先级从高到低(数值越大,优先级越小),依次出队优先级为1、2、3、4、5元素。
 */

D
daishengdong 已提交
2502
#include "tos_k.h"
D
daishengdong 已提交
2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596
#include "mcu_init.h"

#define STK_SIZE_TASK_DEMO      512

#define PRIO_TASK_DEMO          4

k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];

k_task_t task_demo;

typedef struct item_st {
    int a;
    int b;
    int c;
} item_t;

#define PRIO_QUEUE_ITEM_MAX     5
uint8_t ring_q_buffer[PRIO_QUEUE_ITEM_MAX * sizeof(item_t)];
uint8_t mgr_pool[TOS_PRIO_Q_MGR_ARRAY_SIZE(PRIO_QUEUE_ITEM_MAX)];

k_prio_q_t prio_q;

void entry_task_demo(void *arg)
{
    k_err_t err;
    int i = 0;
    item_t item;
    k_prio_t prio;
    size_t item_size;

    tos_prio_q_create(&prio_q, mgr_pool, ring_q_buffer, PRIO_QUEUE_ITEM_MAX, sizeof(item_t));

    for (i = PRIO_QUEUE_ITEM_MAX; i > 0; --i) {
        printf("enqueue: %d  %d  %d\n", i, i, i);
        item.a = i;
        item.b = i;
        item.c = i;
        err = tos_prio_q_enqueue(&prio_q, &item, sizeof(item_t), i);
        if (err != K_ERR_NONE) {
            printf("should never happen\n");
        }
    }

    err = tos_prio_q_enqueue(&prio_q, &item, sizeof(item_t), i);
    if (err == K_ERR_PRIO_Q_FULL) {
        printf("priority queue is full: %s\n", tos_prio_q_is_full(&prio_q) ? "TRUE" : "FALSE");
    } else {
        printf("should never happen\n");
    }

    for (i = 0; i < PRIO_QUEUE_ITEM_MAX; ++i) {
        err = tos_prio_q_dequeue(&prio_q, &item, &item_size, &prio);
        if (err == K_ERR_NONE) {
            printf("dequeue: %d %d %d, prio: %d\n", item.a, item.b, item.c, prio);
        } else {
            printf("should never happen\n");
        }
    }

    err = tos_prio_q_dequeue(&prio_q, &item, &item_size, &prio);
    if (err == K_ERR_PRIO_Q_EMPTY) {
        printf("priority queue is empty: %s\n", tos_prio_q_is_empty(&prio_q) ? "TRUE" : "FALSE");
    } else {
        printf("should never happen\n");
    }

}

int main(void)
{
    board_init();
    tos_knl_init();
    (void)tos_task_create(&task_demo, "demo", entry_task_demo, NULL,
                            PRIO_TASK_DEMO, stack_task_demo, STK_SIZE_TASK_DEMO,
                            0);
    tos_knl_start();
}
```

##### 运行效果

> enqueue: 5  5  5
> enqueue: 4  4  4
> enqueue: 3  3  3
> enqueue: 2  2  2
> enqueue: 1  1  1
> priority queue is full: TRUE
> dequeue: 1 1 1, prio: 1
> dequeue: 2 2 2, prio: 2
> dequeue: 3 3 3, prio: 3
> dequeue: 4 4 4, prio: 4
> dequeue: 5 5 5, prio: 5
> priority queue is empty: TRUE

T
tommytim0515 已提交
2597
[实例代码](code/2.8.4%20priority%20queue/main.c)
S
supowang 已提交
2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702

### 2.9 功耗管理

#### 2.9.1 低功耗

##### 概述

TencentOS tiny提供了多级低功耗管理框架。初级低功耗的方案是,当系统处于“空闲”状态,也即进入idle任务时,系统调用处理器(目前支持的架构是arm v7m)低功耗接口进入短暂的睡眠模式。

##### API讲解

##### 编程实例

对于初级低功耗模式,无需用户编写任何代码,直接通过在tos_config.h打开TOS_CFG_PMR_MGR_EN开关即可:

`#define TOS_CFG_PWR_MGR_EN		1u`

##### 运行效果

#### 2.9.2 tickless

##### 概述

TencentOS tiny的tickless机制提供了一套非周期性时钟的方案,在系统无需systick驱动调度的情况下,停掉systick。

初级功耗管理方案下,因为还有系统systick的存在,因此系统进入idle任务后,并不会在睡眠模式下停留太久。要想进入到更极致的低功耗状态,需要暂停systick。

arm架构提供三级低功耗模式,sleep、stop、standby模式,三种模式运行功耗逐次降低,standby模式最低。TencentOS tiny的内核提供了简洁清晰的接口来管理各级模式。

##### API讲解

```c
void tos_tickless_wkup_alarm_install(k_cpu_lpwr_mode_t mode, k_tickless_wkup_alarm_t *wkup_alarm);
```

此接口用以安装各低功耗模式下的唤醒闹钟。当内核进入tickless模式下后,systick以及停止了,因此需要其他计时器来将CPU从低功耗模式下唤醒。

根据arm v7m的芯片规格,三种模式下的唤醒源分别为:

- sleep

  CPU进入sleep模式后,可以由systick、硬件timer、RTC时钟唤醒(wakeup/alarm中断)。

- stop

  CPU进入stop模式后,可以由RTC时钟(wakeup/alarm中断)唤醒。

- standby

  CPU进入standby模式后,只可由RTC时钟的alarm中断唤醒(还可以通过外部管脚唤醒,但这不属于TencentOS tiny内核机制设计的范畴)。

k_tickless_wkup_alarm_t定义如下:

```c
typedef struct k_tickless_wakeup_alarm_st {
    int         (*init)(void);
    int         (*setup)(k_time_t millisecond);
    int         (*dismiss)(void);
    k_time_t    (*max_delay)(void); /* in millisecond */
} k_tickless_wkup_alarm_t;
```

一个唤醒闹钟有四个成员方法:

- init

  闹钟初始化函数。

- setup

  闹钟设定函数,入参为闹钟到期时间(单位毫秒)。此闹钟在设定完毕后的millisecond毫秒时来中断。

- dismiss

  闹钟解除函数,执行完后闹钟中断不会再来。

- max_delay

  此闹钟最长的到期时间(单位为毫秒)。

```c
k_err_t tos_tickless_wkup_alarm_init(k_cpu_lpwr_mode_t mode);
```

此函数用来初始化特定模式下的唤醒闹钟(实际上调用的是tos_tickless_wkup_alarm_install接口中安装的k_tickless_wkup_alarm_t的init方法)。

```c
k_err_t tos_pm_cpu_lpwr_mode_set(k_cpu_lpwr_mode_t cpu_lpwr_mode);
```

设置内核在tickless模式下进入的CPU低功耗模式。

##### 编程实例

1、在tos_config.h中,配置低功耗组件开关TOS_CFG_PWR_MGR_EN:

`#define TOS_CFG_PWR_MGR_EN	1u`

2、在tos_config.h中,配置tickless组件开关TOS_CFG_TICKLESS_EN:

`#define TOS_CFG_TICKLESS_EN	1u`

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

```c
D
daishengdong 已提交
2703
#include "tos_k.h"
S
supowang 已提交
2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749
#include "mcu_init.h"

#define STK_SIZE_TASK_DEMO      512 

#define PRIO_TASK_DEMO          4
 
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO]; 

k_task_t task_demo;

extern void entry_task_demo(void *arg);

void timer_callback(void *arg)
{
    printf("timer callback: %lld\n", tos_systick_get());
}

void entry_task_demo(void *arg)
{
    k_timer_t tmr;

    // 创建一个软件定时器,每6000个tick触发一次
    tos_timer_create(&tmr, 0u, 6000u, timer_callback, K_NULL, TOS_OPT_TIMER_PERIODIC);
    tos_timer_start(&tmr);

    // 此任务体内每3000个tick运行一次
    while (K_TRUE) {
        printf("entry task demo: %lld\n", tos_systick_get());
        tos_task_delay(3000);
    }
}

int main(void)
{
    board_init();
    tos_knl_init(); 
    (void)tos_task_create(&task_demo, "demo1", entry_task_demo, NULL,
                            PRIO_TASK_DEMO, stack_task_demo, STK_SIZE_TASK_DEMO,
                            0); 
    tos_knl_start();
}
```

4、实现tos_bsp_tickless_setup回调(参考board\TOS_tiny_EVK_STM32L431CBT6\BSP\Src\tickless\bsp_pwr_mgr.c、board\TOS_tiny_EVK_STM32L431CBT6\BSP\Src\tickless\bsp_tickless_alarm.c):

```c
D
daishengdong 已提交
2750
#include "tos_k.h"
S
supowang 已提交
2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804
#include "tickless/bsp_pm_device.h"
#include "tickless/bsp_tickless_alarm.h"

int tos_bsp_tickless_setup(void)
{
#if TOS_CFG_TICKLESS_EN > 0u
    // sleep模式下的唤醒源,基本定时器
    tos_tickless_wkup_alarm_install(TOS_LOW_POWER_MODE_SLEEP, &tickless_wkup_alarm_tim);
    // 初始化唤醒源闹钟
    tos_tickless_wkup_alarm_init(TOS_LOW_POWER_MODE_SLEEP);
    // 设置tickless状态时进入sleep模式
    tos_pm_cpu_lpwr_mode_set(TOS_LOW_POWER_MODE_SLEEP);
#endif
}
```

5、为了观察在tickless时是否确实没有systick中断,在tos_sys.c的idle任务体内加一句调试代码:

```c
__STATIC__ void knl_idle_entry(void *arg)
{
    arg = arg; // make compiler happy

    while (K_TRUE) {
        // 这里在idle任务体内加上一句打印,如果systick正常开启,在没有用户任务运行时,此调试信息会不断打印;如果是tickless状态,此调试信息应该只会第一次进入idle任务时,或在用户任务等待到期,或用户的软件定时器到期时,才打印一次。
        printf("idle entry: %lld\n", tos_systick_get());
#if TOS_CFG_PWR_MGR_EN > 0u
        pm_power_manager();
#endif
    }
}
```

##### 运行效果

> entry task demo: 0
> idle entry: 2
> entry task demo: 3002
> idle entry: 3002
> timer callback: 6000
> idle entry: 6000
> entry task demo: 6002
> idle entry: 6002
> entry task demo: 9002
> idle entry: 9002
> timer callback: 12000
> idle entry: 12000
> entry task demo: 12002
> idle entry: 12002
> entry task demo: 15002
> idle entry: 15002
> timer callback: 18000
> idle entry: 18000
> entry task demo: 18002
D
daishengdong 已提交
2805
> idle entry: 18002