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.0f
- floatpositive_scale;//正向縮放, MIX文件中 O: 後面的第2個整數/10000.0f
- floatoffset; //偏移 , MIX文件中 O: 後面的第3個整數/10000.0f
- floatmin_output;//最小輸出值 , MIX文件中 O: 後面的第4個整數/10000.0f
- floatmax_output;//最大輸出值 , MIX文件中 O: 後面的第5個整數/10000.0f
- };//該結構定義了單個控制量的結構
- /** mixer input */
- struct mixer_control_s {
- uint8_tcontrol_group;/**< group from which the input reads */
- uint8_tcontrol_index;/**< index within the control group */
- struct mixer_scaler_s scaler;/**< scaling applied to the input before use */
- };//定義輸入量的結構
- /** simple mixer */
- struct mixer_simple_s {
- uint8_tcontrol_count;/**< number of inputs */
- struct mixer_scaler_soutput_scaler;/**< scaling for the output */
- struct mixer_control_scontrols[0];/**< actual size of the array is set by control_count */
- };//定義了一個控制實體的控制體,包括輸入的信號數量,輸入信號控制集,輸出信號控制。
- //因為一個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中,並將已經處理的字條數目賦值給used
- buf = 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 < 12)) {
- debug("output parser failed finding tag, ret: '%s'", buf);
- return -1;
- }//12,表示O:這行的定義至少有12個字符(O:和五個1位長的整數),例如最短的定義為: O: 0 0 0 0 0
- if ((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 < inputs; 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 < 16)) {
- 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;
- }