mixer 结构分析[uavcan为例]mixer指令为系统的app命令,位置在Firmware/src/systemcmds/mixer目录下面,其功能是装载mix文件中的有效内容到具体的设备中,然后由具体的设备中的MixerGroups来解析这些定义.本例是以uvacan为例, 系统运行后,设备的名称为:/dev/uavcan/esc.uavcan的定义中有MixerGroup实例,Output实例.MIXER的种类一共有三个:NullMixer, SimpleMixer 和MultirotorMixer.NullMixer:用来为未分组的输出通道点位;SimpleMixer:0或多个输入融合成一个输出;MultirotorMixer:将输入量(ROLL,PITCH,RAW,Thrusttle)融合成一组基于 预先定义的geometry的输出量.读取mix文件的函数位于Firmware/src/modules/systemlib/mixwr/mixer_load.c中的函数:int load_mixer_file(const char *fname, char *buf, unsigned maxlen) 登录后复制参数fname为mix文件在系统中的位置,buf为读取文件数据存放的缓冲区,maxlen为buf的最大长度。该函数会剔除符合下面任何一条的行:1.行长度小于2个字符的行2.行的首字符不是大写字母的行3.第二个字符不是':'的行剔除这些非法内容的数据后,剩余的全部为格式化的内容,会被全部存入buf缓冲区中。所以这要求在写mix文件时要遵循mix和格式。这些格式化的mix内容被读取缓冲区后,就会通过函数int ret = ioctl(dev, MIXERIOCLOADBUF, (unsigned long)buf); 登录后复制来交给具体的设备处理。相关结构的定义:/** simple channel scaler */ struct mixer_scaler_s {floatnegative_scale;//负向缩放, MIX文件中 O: 后面的第1个整数/10000.0ffloatpositive_scale;//正向缩放, MIX文件中 O: 后面的第2个整数/10000.0ffloatoffset; //偏移 , MIX文件中 O: 后面的第3个整数/10000.0ffloatmin_output;//最小输出值 , MIX文件中 O: 后面的第4个整数/10000.0ffloatmax_output;//最大输出值 , MIX文件中 O: 后面的第5个整数/10000.0f};//该结构定义了单个控制量的结构/** mixer input */struct mixer_control_s {uint8_tcontrol_group;/**uint8_tcontrol_index;/**struct mixer_scaler_s scaler;/**};//定义输入量的结构/** simple mixer */struct mixer_simple_s {uint8_tcontrol_count;/**struct mixer_scaler_soutput_scaler;/**struct mixer_control_scontrols[0];/**};//定义了一个控制实体的控制体,包括输入的信号数量,输入信号控制集,输出信号控制。 //因为一个mixer只有一个输出,可以有0到多个输入,所以control_count指明了这个mixer所需要的输入信号数量,而具体的信号都存放在数组controls[0]中。 //输出则由output_scaler来控制. //从这些结构体的定义,可以对照起来mix文件语法的定义. 登录后复制uavcan_main.cpp:该文件中有解析上面提到的缓冲数据int UavcanNode::ioctl(file *filp, int cmd, unsigned long arg) { ... case MIXERIOCLOADBUF: { const char *buf = (const char *)arg; unsigned buflen = strnlen(buf, 1024); if (_mixers == nullptr) { _mixers = new MixerGroup(control_callback, (uintptr_t)_controls); } if (_mixers == nullptr) { _groups_required = 0; ret = -ENOMEM; } else { ret = _mixers->load_from_buf(buf, buflen);//这里开始解析数据 if (ret != 0) { warnx("mixer load failed with %d", ret); delete _mixers; _mixers = nullptr; _groups_required = 0; ret = -EINVAL; } else { _mixers->groups_required(_groups_required); } } break; }... } int MixerGroup::load_from_buf(const char *buf, unsigned &buflen){int ret = -1;const char *end = buf + buflen;/* * Loop until either we have emptied the buffer, or we have failed to * allocate something when we expected to. */while (buflen > 0) {Mixer *m = nullptr;const char *p = end - buflen;unsigned resid = buflen;/* * Use the next character as a hint to decide which mixer class to construct. */switch (*p) {//首先看该行的第一个字母,来确定数据的类别.case 'Z':m = NullMixer::from_text(p, resid);break;case 'M':m = SimpleMixer::from_text(_control_cb, _cb_handle, p, resid);break;case 'R':m = MultirotorMixer::from_text(_control_cb, _cb_handle, p, resid);break;default:/* it's probably junk or whitespace, skip a byte and retry */buflen--;continue;}/* * If we constructed something, add it to the group. */if (m != nullptr) {add_mixer(m);/* we constructed something */ret = 0;/* only adjust buflen if parsing was successful */buflen = resid;debug("SUCCESS - buflen: %d", buflen);} else {/* * There is data in the buffer that we expected to parse, but it didn't, * so give up for now. */break;}}/* nothing more in the buffer for us now */return ret;} 登录后复制下面这个函数用来 处理 M: 开头的定义, 格式规定该字符后面只能有一个数字,用来指明input信号源的数量,即S类型数量的数量,联系到结构体的定义,则为 struct mixer_control_s 的数量. SimpleMixer * SimpleMixer::from_text(Mixer::ControlCallback control_cb, uintptr_t cb_handle, const char *buf, unsigned &buflen){SimpleMixer *sm = nullptr;mixer_simple_s *mixinfo = nullptr;unsigned inputs;int used;const char *end = buf + buflen;/* get the base info for the mixer */if (sscanf(buf, "M: %u%n", &inputs, &used) != 1) {debug("simple parse failed on '%s'", buf);goto out;}//复制M:后面第一个数值到无符号整型数据到变量inputs中,并将已经处理的字条数目赋值给usedbuf = skipline(buf, buflen);//让buf指定下一行if (buf == nullptr) {debug("no line ending, line is incomplete");goto out;}mixinfo = (mixer_simple_s *)malloc(MIXER_SIMPLE_SIZE(inputs)); //M:后面的数字为struct mixer_control_s 结构的数量.MIXER_SIMPLE_SIZE的字义为sizeof(mixer_simple_s) + inputs*sizeof(mixer_control_s), //即一个完整的mixer_simple_s的定义,controls[0]一共有inputs个. if (mixinfo == nullptr) {debug("could not allocate memory for mixer info");goto out;}mixinfo->control_count = inputs;//input 信号的数量if (parse_output_scaler(end - buflen, buflen, mixinfo->output_scaler)) {debug("simple mixer parser failed parsing out scaler tag, ret: '%s'", buf);goto out;}//该函数解析输出域,并将期填充到mixinfo的output_scaler字段中.int SimpleMixer::parse_output_scaler(const char *buf, unsigned &buflen, mixer_scaler_s &scaler){int ret;int s[5];int n = -1;buf = findtag(buf, buflen, 'O');//寻找"O:"这样的控制符,返回指针指向输出格式域定义的首字符'O'.登录后复制if ((buf == nullptr) || (buflen debug("output parser failed finding tag, ret: '%s'", buf);return -1;}//12,表示O:这行的定义至少有12个字符(O:和五个1位长的整数),例如最短的定义为: O: 0 0 0 0 0if ((ret = sscanf(buf, "O: %d %d %d %d %d %n",//O:后面必须有5个整数,且整数间用至少一个空格分开,此处是取出O:后面的5个整数值. &s[0], &s[1], &s[2], &s[3], &s[4], &n)) != 5) {debug("out scaler parse failed on '%s' (got %d, consumed %d)", buf, ret, n);return -1;}buf = skipline(buf, buflen);if (buf == nullptr) {debug("no line ending, line is incomplete");return -1;} //从下面的赋值操作可以得出 O:后面5个数值的字义.,分别为 [negative_scale] [positive_scale] [offset] [min_output] [max_output] //并且每个这都做了除10000的操作,所以MIX格式定义中说这些值都是被放大10000倍后的数值. scaler.negative_scale= s[0] / 10000.0f;scaler.positive_scale= s[1] / 10000.0f;scaler.offset= s[2] / 10000.0f;scaler.min_output= s[3] / 10000.0f;scaler.max_output= s[4] / 10000.0f;return 0;} 登录后复制 //上面解析了MIXER的输出量,下面开始解析输入量,因为我们已经读取了输入信号的数量("M: n"中n定义的数值),所以要循环n次. //首先记住parse_control_scaler函数输入的参数 for (unsigned i = 0; i if (parse_control_scaler(end - buflen, buflen, mixinfo->controls[i].scaler, mixinfo->controls[i].control_group, mixinfo->controls[i].control_index)) {debug("simple mixer parser failed parsing ctrl scaler tag, ret: '%s'", buf);goto out;}}int SimpleMixer::parse_control_scaler(const char *buf, unsigned &buflen, mixer_scaler_s &scaler, uint8_t &control_group, uint8_t &control_index){unsigned u[2];int s[5];buf = findtag(buf, buflen, 'S');//找到剩余缓冲区中的第一个'S',并让buf指向该行的行首; // //16表示该S:行至少有16个字符,即至少有7个整数(因为整数间至少有1个空格分隔)if ((buf == nullptr) || (buflen debug("control parser failed finding tag, ret: '%s'", buf);return -1;} //读取S:后面的7个整数.if (sscanf(buf, "S: %u %u %d %d %d %d %d", &u[0], &u[1], &s[0], &s[1], &s[2], &s[3], &s[4]) != 7) {debug("control parse failed on '%s'", buf);return -1;}buf = skipline(buf, buflen);if (buf == nullptr) {debug("no line ending, line is incomplete");return -1;} //从下面的赋值可以看出MIXER文件S:定义的格式,S:后面的整数分别为 // [control_group] [ontrol_index] [negative_scale] [positive_scale] [offset] [min_output] [max_output] // 可以看出,输入信号的定义比输入出信号的定义多了两个整数,用来表示当前输入信号所在的组和组内的序号. 第1和第2个整就是用来 // 说明组号和组内序号.而后面5个整数的定义和输入信号的定义一样,且也要除以10000. control_group= u[0];control_index= u[1];scaler.negative_scale= s[0] / 10000.0f;scaler.positive_scale= s[1] / 10000.0f;scaler.offset= s[2] / 10000.0f;scaler.min_output= s[3] / 10000.0f;scaler.max_output= s[4] / 10000.0f;return 0;} 登录后复制 sm = new SimpleMixer(control_cb, cb_handle, mixinfo);if (sm != nullptr) {mixinfo = nullptr;debug("loaded mixer with %d input(s)", inputs);} else {debug("could not allocate memory for mixer");}out:if (mixinfo != nullptr) {free(mixinfo);}return sm;} 09-15 02:11