> For the complete documentation index, see [llms.txt](https://libooogo.gitbook.io/esplane/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://libooogo.gitbook.io/esplane/developer-guide/12esplane-2.0-crazyfile-bian-liang-guan-li-fang-an-yi-zhi.md).

# 12 Variable Unified Management

## 一、Table of content (TOC)

简单的类比,TOC是一个存储了集团所有客户电话号码的号码薄,不同的分公司使用统一的注册机制,将客户电话写到该电话簿上,集团商务部门可以以一定频率通过遍历号码薄,与所有客户逐个通信,为客户推送信息或接受客户反馈.

在该工程中TOC是一张烧录在flash连续空间上的地址表,地址表内存储了来自不同源文件需要监控的变量名字、类型和地址(组成一个结构体).Crazyflie有两个TOC,PARAM 和 LOG,用于承载不同的功能.上位机通过获取TOC,获取全部已经注册的变量(名字、类型和地址),使用CRTP协议通过该变量地址实时查看或修改对应变量值.

The aim of the logging and parameter framework is to easily be able to log data from the Crazyflie and to set variables during runtime. [Logging and parameter frameworks](https://www.bitcraze.io/docs/crazyflie-firmware/master/logparam/)

## 二、 PARAM\_GROUP

1. **把变量加入PARAM\_GROUP,方便从外部进行修改(支持设置读写权限),大大加速调试过程(该机制仅写到ram,重启后变成默认值).**
2. **支持使用{组名.变量名}访问变量**:if you would like to set the effect variable in the ring group it's accessed using ring.effect.
3. **可以在任意时刻访问和修改变量** The reading or writing of a parameter can be done at any time once you are connected to the Crazyflie.
4. **读写没有线程安全保护**:There's no thread protection on reading/writing. Since the architecture is 32bit and the largest parameter you can have is 32bit it's safe to write one variable. But if you write a group of variables that should be used together (like PID parameters) you might end up in trouble.
5. **在访问运行期变化的变量时,推荐使用LOG\_GROUP**:Only use the parameter framework to read variables that are set during start-up. If variables change during runtime then use the logging framework.

### 1. 宏定义

```
/* Macros */
#define PARAM_ADD(TYPE, NAME, ADDRESS) \
   { .type = TYPE, .name = #NAME, .address = (void*)(ADDRESS), },

#define PARAM_ADD_GROUP(TYPE, NAME, ADDRESS) \
   { \
  .type = TYPE, .name = #NAME, .address = (void*)(ADDRESS), },

#define PARAM_GROUP_START(NAME)  \
  static const struct param_s __params_##NAME[] __attribute__((section(".param." #NAME), used)) = { \
  PARAM_ADD_GROUP(PARAM_GROUP | PARAM_START, NAME, 0x0)

#define PARAM_GROUP_STOP(NAME) \
  PARAM_ADD_GROUP(PARAM_GROUP | PARAM_STOP, stop_##NAME, 0x0) \
  };
```

### 2. 源文件中,将全局变量添加到PARAM TOC的方法

```
PARAM_GROUP_START(stabilizer)
PARAM_ADD(PARAM_UINT8, estimator, &estimatorType)
PARAM_ADD(PARAM_UINT8, controller, &controllerType)
PARAM_GROUP_STOP(stabilizer)
```

**按照宏定义展开:**

```
static const struct param_s __params_stabilizer[] __attribute__((section(".param.stabilizer"), used)) = {
   { .type = PARAM_GROUP | PARAM_START, .name = “stabilizer”, .address = (void*)(0x0), },
   { .type = PARAM_UINT8, .name = “estimator”, .address = (void*)(&estimatorType), },
   { .type = PARAM_UINT8, .name = “controller”, .address = (void*)(&controllerType), },
   {.type = PARAM_GROUP | PARAM_STOP, .name =“stop_stabilizer”, .address = (void*)(0x0), }, 
};
```

> 相当于创建了一个结构体(param\_s)数组,该数组包含4个成员,其中第1个和第4个用于标记该组变量的头和尾,每个结构体有三个成员(type、name、address)
>
> 使用`__attribute__((section(".param.stabilizer"), used))` 将该数组放在指定名为`".param.stabilizer"`的段.

### 3. 把源文件".param."##NAME 段映射到地址连续区域

#### 3.1 esp\_idf链接脚本生成器

esp\_idf从v3.3开始,支持在component层级控制最后的链接阶段,使用[Linker Script Generation](https://docs.espressif.com/projects/esp-idf/en/v3.3/api-guides/linker-script-generation.html)机制,在component里添加一个`xx.lf`文件.

**在编译系统中使用`xx.lf`**

```
COMPONENT_ADD_LDFRAGMENTS += "xx.lf"
```

**按照规定的语法填写内容:**

```
\\示例:
[sections:text]
    .text+
    .literal+

[sections:iram]
    .iram1+

[scheme:default]
entries:
    text -> flash_text
    iram -> iram0_text

[scheme:noflash]
entries:
    text -> iram0_text

[mapping:freertos]
archive: libfreertos.a
entries:
    * (noflash)
```

**脚本生成器将`xx.lf`转换成gcc任何的链接片段:** 该链接片段将被添加到链接文件,形成当前工程的链接规则.

```
\\示例:
.iram0.text :
{
    /* Code marked as runnning out of IRAM */
    _iram_text_start = ABSOLUTE(.);

    /* Placement rules generated from the processed fragments, placed where the marker was in the template */
    *(.iram1 .iram1.*)
    *libfreertos.a:(.literal .text .literal.* .text.*)

    _iram_text_end = ABSOLUTE(.);
} > iram0_0_seg
```

#### 3.2 直接修改框架的链接脚本

ESPlane2.0版本基于release/v3.3分支编写,该分支下链接脚本生成机制 1. 不支持添加一个地址的引用(添加一个入口)？ 2. 不支持添加对齐指令？ 因此临时跳过了使用链接脚本工具生成链接脚本,而是直接修改默认的链接脚本(不推荐,需要后期调整),(位置在/components/esp32/ld/esp32.project.ld.in)修改如下:

```
.flash.rodata :
{
/*省--------------------------------
*略
*已
*有
*若
*干
*行---------------------------------*/

    /* Parameters and log system datas */
    _param_start = .;
    KEEP(*(.param))
    KEEP(*(.param.*))
    _param_stop = .;
    . = ALIGN(4);
    _log_start = .;
    KEEP(*(.log))
    KEEP(*(.log.*))
    _log_stop = .;
    . = ALIGN(4);

} >drom0_0_seg
```

> `KEEP(*(.param))` `KEEP(*(.param.*))`可以将所有带有.param的分段,统一映射到drom0\_0\_seg的`.flash.rodata`段

#### 3.3 链接脚本修改效果

![map](https://img-blog.csdnimg.cn/20191021190642548.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIwNTE1NDYx,size_16,color_FFFFFF,t_70)

查看编译输出的`.map`文件可以找到`.param.stabilizer`,为第2部分宏定义添加的结构体数组,该数组包含4个元素,每个元素大小为12,占用大小为0x30字节

#### 3.4 程序如何遍历该变量地址表

读取:

```
extern struct param_s _param_start; //添加声明,定义在3.2的ld文件中
extern struct param_s _param_stop; //添加声明,定义在3.2的ld文件中
static struct param_s * params;

params = &_param_start;//参数地址表入口
paramsLen = &_param_stop - &_param_start; //参数总个数
  for (int i=0; i<paramsLen; i++)//遍历
  {
      params[i].type;
      params[i].name;
      params[i].address; 
  }
```

写入:写入到ram,掉电不保存

```
    switch (params[id].type & PARAM_BYTES_MASK)
    {
    case PARAM_1BYTE:
        *(uint8_t*)params[id].address = *(uint8_t*)valptr;
      break;
      case PARAM_2BYTES:
        *(uint16_t*)params[id].address = *(uint16_t*)valptr;
        break;
    case PARAM_4BYTES:
        *(uint32_t*)params[id].address = *(uint32_t*)valptr;
        break;
    case PARAM_8BYTES:
        *(uint64_t*)params[id].address = *(uint64_t*)valptr;
        break;
    }
```

## 三、 LOG\_GROUP

1. **把变量加入LOG\_GROUP,可以从上位机监控变量的变化**:The logging framework is used to log variables from the Crazyflie at a specific interval.
2. **上位机下载TOC并完成设置,将收到Crazyflie主动push的数据**:Instead of triggering a reading of the variables at certain intervals, the framework is used to set up a logging configuration to that will push data from the Crazyflie to the host.
3. The interval for a logging configuration is specified in 10th of milliseconds.

### 1. 源文件中,将全局变量添加到LOG TOC的方法

```
LOG_GROUP_START(stabilizer)
LOG_ADD(LOG_FLOAT, roll, &eulerRollActual)
LOG_ADD(LOG_FLOAT, pitch, &eulerPitchActual)
LOG_ADD(LOG_FLOAT, yaw, &eulerYawActual)
LOG_ADD(LOG_UINT16, thrust, &actuatorThrust)
LOG_GROUP_STOP(stabilizer)
```

## 四、 通过上位机读取TOC并监视变量

![获取TOC](https://img-blog.csdnimg.cn/20191021194027794.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIwNTE1NDYx,size_16,color_FFFFFF,t_70)

![监视变量](https://img-blog.csdnimg.cn/20191021194047889.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIwNTE1NDYx,size_16,color_FFFFFF,t_70)
