ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

BODAS运行时函数指令集实战:从调用约定到CAN、EEPROM避坑指南

BODAS运行时函数指令集实战:从调用约定到CAN、EEPROM避坑指南

简介:这份文档面向力士乐行走机械控制器的编程初学者与调试人员,系统整理了BODAS运行时系统中常用指令的说明,帮助读者理解控制器程序的编写逻辑与函数调用方式。内容按注解、输入、输出、系统函数等模块组织,涵盖in_initAna、in_initDig、in_initFreq等初始化函数,in_getPoti系列电位器读取函数,以及in()、in_setVirtual、in_doFreqStop等输入处理接口,并针对MC6、RC2-1、RC6-9、RC12-18等不同目标系统标注了适用差异与参数限制。资源包共1个doc文件,约1.53MB,为英文指令集手册,适合作为编程查阅与函数速查的案头资料。目前已有1522人学习,读者可借助它快速定位各函数的功能、适用控制器型号及调用注意事项,配合运行时系统说明书排查输入输出配置问题,提升控制程序的编写效率与调试准确性。

1. 一份被低估的“函数字典”:BODAS 指令集到底解决什么问题

手上拿到一份力士乐行走机械控制器 BODAS 编程指令集(英文版),第一反应往往不是“终于有资料了”,而是“这玩意儿怎么用”。它不像教程那样从点亮第一个 LED 讲起,而是直接把运行时系统(Runtime System)里几十个函数按输入、输出、系统、BODEM/BB-3、伺服、EEPROM、CAN 七大类摊开,每个函数给声明、参数、目标系统适用范围和返回值。真正干过行走机械电控的人都知道,这类文档的价值不在“读”,而在“查”——写代码卡在某个通道号怎么填、某个函数在 RC2-1 上到底能不能用的时候,翻它比翻论坛快得多。

这份资料覆盖 MC6、32RC2-1、12/20RC6-9、10RC12-18、10BODAS/10 等多个目标系统,版本标注到 03.02,属于典型的运行时系统说明书。它解决的核心问题是:让你在 IEC 61131-3 环境下调用 C 写的底层运行时函数时,知道参数怎么传、哪些函数挑板子、返回值怎么判。适合已经上手 BODAS 开发环境、能建工程能编译,但在具体外设读写和系统控制上频繁翻车的初学和中级开发者。如果你还在纠结怎么装环境,这份文档帮不上忙;但如果你已经能编译出一个空工程,接下来要接电位器、读频率、发 CAN 帧,它就是案头那本随时要翻的册子。

2. 运行时函数的调用约定:void 参数、void 返回值和双卡 ECU 的坑

BODAS 的运行时系统例程是用 C 代码写的,而 C 允许定义不带参数的函数,也允许函数没有返回值。问题在于 IEC 61131-3 不允许这两件事。于是文档在开头就用两小节专门交代了这个“标准适配”的细节,很多人扫目录时直接跳过,结果第一次调用SYS_getLoadedPages就编译报错,回头再找原因,浪费半小时。

2.1 void 参数不是“不用传”,而是传一个 FALSE

按文档说明,为了在符合 IEC 61131-3 的前提下调用无参函数,参数括号里要填一个 BOOL 类型的变量void。这个变量在BODAS_xxx.lib里已经声明并初始化为 FALSE。也就是说,你看到声明是SYS_getLoadedPages( void ),实际写的时候不能留空括号,得把void这个变量名填进去。

(* 错误写法:IEC 61131-3 下空括号编译不过 *) nPages := SYS_getLoadedPages(); (* 正确写法:传入库中已声明的 void 变量 *) nPages := SYS_getLoadedPages( void );

逻辑说明:void在这里不是关键字占位,而是一个真实存在的 BOOL 变量,值为 FALSE。参数说明上它没有任何业务含义,纯粹是为了满足标准对函数签名的要求。我一般会在工程全局变量里确认BODAS_xxx.lib已经被引用,否则这个void符号找不到,报的还是“未定义标识符”,很容易误判成库没加载。

同理,带 void 返回值的函数在标准里也不允许,所以运行时系统给它们定义了一个 BOOL 返回值。调用这类函数时,你可以不接收返回值,但要知道它返回的 BOOL 并不代表业务结果,别拿它做判断。

2.2 双卡 ECU 的 Board A / Board B 由接线决定

文档 1.1.4 节提到,MC6/E 32 和 RC12-18 是 2 卡电子控制单元,连接配置决定哪块卡被识别为 Board A、哪块为 Board B。这句话短,但影响后面所有带通道号的函数——比如in_initAna里 RC2-1 的通道号是ANA_A1 .. 2,而双卡系统上你填的通道号落在哪块板,取决于硬件接线。

常见做法是:先在硬件说明书里确认本项目的板卡拓扑,把 A/B 板的输入输出清单列成表,再对照指令集里的通道命名去填。我见过有人把 RC12-18 的通道号按单卡习惯填,编译通过、下载也正常,但读上来的值一直是 0,查了两天才发现信号接在 B 板而代码按 A 板寻址。这类问题文档不会替你兜底,它只告诉你“连接配置决定”,剩下的靠你对硬件拓扑的掌握。

提示:凡是函数声明里带通道号参数的,先在纸上画一遍板卡与通道的对应关系,再动手写初始化,比事后用调试器逐通道试要快得多。

3. 输入通道初始化与电位器读取:in_init 系列和 in_getPoti 的配合

输入部分是这份指令集里最厚的一块,从in_initAna、in_initDig、in_initFreq到in_getPoti系列,再到按目标系统分版本的in()和in_setVirtual、in_doFreqStop。新手最容易犯的错是跳过初始化直接调in()读值,结果读出来全是默认值或者乱跳。运行时系统的设计逻辑是:先按输入类型做初始化配置,再用读入函数取值,两步不能省。

3.1 in_initAna 的模式参数:iMode1 管参考电压,iMode2 只管 ANA_A2

以仅适用于 RC2-1 的in_initAna为例,声明是INT in_initAna( iChnl:INT; iMode1:INT; iMode2:INT )。通道号在 RC2-1 上是ANA_A1 .. 2,iMode1选参考电压或分流电阻接法,iMode2选电压范围。文档明确写了两个注意点:iMode1的 SHUNT 模式对两个输入都适用,而iMode2只对ANA_A2生效。

(* RC2-1 模拟输入初始化示例 *) (* ANA_A1:参考电压 0V,普通 0~5V 量程 *) in_initAna( ANA_A1, ANA_MODE1_PV, ANA_MODE2_NORMAL ); (* ANA_A2:2.5V 参考,-10~+10V 量程,用于双向信号 *) in_initAna( ANA_A2, ANA_MODE1_PVR, ANA_MODE2_SPS );

逻辑说明:iMode1取ANA_MODE1_PV表示参考电压 0V,取ANA_MODE1_PVR表示 2.5V,取ANA_MODE1_SHUNT则接分流电阻做 0~20mA 测量。iMode2取ANA_MODE2_NORMAL是 0~5V 读入范围,取ANA_MODE2_SPS是 -10~+10V。参数说明里那句“SHUNT 模式适用于两种输入”意味着如果你用电流型传感器,两个通道都得按 SHUNT 配,别只配一个。而iMode2只对 A2 有效,给 A1 传 SPS 不会报错,但也不会有预期效果,属于静默失效,排查时容易怀疑到传感器头上。

3.2 in_getPoti 系列:校准值从哪来,状态位怎么看

电位器读取不是简单调一个in_getPoti就完事。文档说明,校准电位器值的读入程序引用的是校准值,这些值可以通过 BODAS 中的可视化或者 BODEM/BB-3 读入。也就是说,in_getPoti返回的是经过校准的模拟值,而校准数据本身要先通过工具写进去。

配套的几个函数各有分工:in_getPotiMin返回编码的最小校准值,in_getPotiMax返回编码的最大校准值,in_getPotiDir返回校准的中位模拟值,in_getPotiStatus返回电位器功能状态用于错误检测。实际写代码时,我一般先用 Min/Max 确认校准范围是否合理,再用 Status 判断有没有断线或超范围,最后才用in_getPoti取业务值。

(* 电位器读取与状态检查 *) nRaw := in_getPoti( ANA_A1 ); nMin := in_getPotiMin( ANA_A1 ); nMax := in_getPotiMax( ANA_A1 ); nMid := in_getPotiDir( ANA_A1 ); nStat := in_getPotiStatus( ANA_A1 ); (* 状态非正常时置故障标志,不直接使用原始值 *) IF nStat <> 0 THEN bPotiFault := TRUE; ELSE bPotiFault := FALSE; nPercent := ( nRaw - nMin ) * 100 / ( nMax - nMin ); END_IF

逻辑说明:in_getPotiStatus的返回值具体编码文档在对应小节给出,使用时按目标系统查表,不要假设 0 一定代表正常——不同固件版本的状态定义可能有差异,这一点文档没有统一表格,得逐函数看。参数上通道号要和初始化时一致,ANA_A1初始化成什么模式,读取时就按那个模式的量纲解释。常见翻车点是校准值没写入就调in_getPoti,返回值看着像模像样,实际和物理位置对不上,这时候先查 Min/Max 是否等于默认值。

3.3 in() 按目标系统分版本:别拿 MC6 的写法套 RC2-1

文档把in()拆成了三个版本:适用于 MC6、适用于 RC2-1、适用于 RC6-9 和 RC12-18。三个版本都叫“读入开关、频率和模拟值”,但参数和通道定义不同。这意味着同一段读输入代码,换控制器型号就得改。

我的做法是给每个目标系统单独建一个输入映射功能块,把in()的调用封在里面,上层业务只认统一的结构体。这样换型时只改功能块内部,不动业务逻辑。另外in_setVirtual明确标注不适用于 RC2-1,in_doFreqStop用于在开关或计时器输入上停止频率测量——后者在需要复用引脚做频率和开关两种用途时很有用,但要注意停止后重新使能的时序,文档只给了函数说明,时序得自己在台架上试。

注意:in_initDig标注“不适用于 RC2-1”,in_initAna标注“仅适用于 RC2-1”,这两个限定词在选型阶段就要确认,别等代码写完才发现目标板根本不支持。

4. 输出、系统控制与 CAN 通讯:out_、sys_、can_ 三组函数的落地要点

输入之后是输出和系统级控制。输出组有out_initProp、out_initPropFreq、out、out_setProp、out_getProp、out_getStatus;系统组有供电读取、周期设置、访问掩码、密码、上下电、时间、错误 LED、内存变更检测、RTS 版本等;CAN 组从can_init到can_sendData、can_sendDatabox一整套。这三组函数共同点是:初始化函数和运行函数分离,且不少函数带目标系统限定。

4.1 比例输出初始化与状态回读

out_initProp配置比例输出,out_initPropFreq针对频率输出,out是通用输出写值,out_setProp和out_getProp分别设值和回读,out_getStatus取输出状态。和输入侧一样,先初始化再写值。比例输出的初始化通常要指定通道和输出模式,具体参数文档在对应小节列出,这里不臆造。

(* 比例输出初始化与写值 *) out_initProp( OUT_CH1, OUT_MODE_CURRENT ); out_setProp( OUT_CH1, nTargetCurrent ); nActual := out_getProp( OUT_CH1 ); nStat := out_getStatus( OUT_CH1 ); IF nStat <> 0 THEN bOutFault := TRUE; END_IF

逻辑说明:out_setProp写入目标值,out_getProp回读实际值,两者在闭环或诊断场景下配合使用。out_getStatus的状态编码按目标系统查文档,别跨型号套用。参数上通道号要和初始化一致,模式选错会导致输出量纲不对——比如电流模式写成电压值,执行器动作幅度明显偏小,这种“看起来能动但不对”的问题最难查,因为不报错。

4.2 sys_ 系统函数:周期、上下电和错误 LED 的边界

系统函数里几个高频使用的是sys_getSupply(读供电)、sys_setCycle(设周期)、sys_setPowOff/sys_setPowOn(下电/上电)、sys_getTime(读时间)、sys_setErrorLed(错误 LED,仅 RC6-9 和 RC12-18)、sys_getRTSRevision(读运行时系统版本)。

sys_setCycle影响任务调度周期,改它之前要确认所有外设的采样和输出刷新能跟上,否则会出现 CAN 帧发送间隔抖动或者输入采样丢点。sys_setPowOffExt和sys_setPowOnExt仅适用于 RC12-18,做扩展电源管理时用,普通上下电用不带 Ext 的版本。sys_setErrorLed的限定词很明确,MC6 上没有这个函数,写了也编译不过。

(* 系统信息读取与周期设置 *) nSupply := sys_getSupply(); sys_setCycle( 10 ); (* 10ms 任务周期,按实际负载调整 *) sVersion := sys_getRTSRevision(); (* 记录 RTS 版本,便于现场对账 *)

逻辑说明:sys_getSupply返回供电电压相关值,用于低压诊断;sys_setCycle的参数单位按文档约定,改完要重新评估任务最坏执行时间;sys_getRTSRevision在排查“同一份代码在不同批次控制器上行为不一致”时特别有用,先把 RTS 版本记下来,再对比差异。sys_isMemChanged用于检测内存是否变更,配合 EEPROM 保存逻辑使用,避免频繁写页。

4.3 CAN 初始化、收发与中断控制

CAN 组函数覆盖了从初始化到收发的完整链路:can_init初始化控制器,can_initTraDatabox和can_initRecDatabox分别初始化发送和接收数据盒,can_getStatus取状态,can_doDisableInterrupt/can_doEnableInterrupt控制中断,can_sendData和can_sendDatabox发送数据。

(* CAN 初始化与发送示例 *) can_init( CAN_CH1, 250 ); (* 通道 1,250kbps *) can_initTraDatabox( CAN_CH1, nBoxId, 8 ); (* 发送数据盒,8 字节 *) can_sendDatabox( CAN_CH1, nBoxId ); (* 按数据盒发送 *) (* 接收侧 *) can_initRecDatabox( CAN_CH1, nRecBoxId, 8 ); nCanStat := can_getStatus( CAN_CH1 );

逻辑说明:can_init的波特率参数要和总线上的其他节点一致,改之前先确认整车网络规划;can_initTraDatabox的数据长度参数决定该数据盒承载的字节数,超过 8 字节在经典 CAN 上不合法;can_sendDatabox是按预配置的数据盒发送,适合周期帧,can_sendData更适合事件触发帧。中断的禁用和使能要成对出现,禁用后忘了使能,表现为“发得出去收不进来”,查总线负载和终端电阻都正常,最后发现是中断没开——这种坑我踩过一次,之后每次动中断控制都在代码里加注释标记配对位置。

提示:CAN 相关函数的目标系统适用范围文档没有逐条标注“仅适用于”,默认按全系统可用处理,但数据盒数量和通道数受硬件限制,以硬件说明书为准。

5. EEPROM 与 BODEM/BB-3:参数保存、错误管理和伺服函数的实操边界

EEPROM 组和 BODEM/BB-3 组是这份指令集里最容易被跳过的部分,因为看起来“不常用”。但行走机械的参数保存、故障记录和诊断恰恰绕不开它们。EEPROM 组从ee_getPage、ee_getPageStatus、ee_getStatus(仅 RC2-1)、ee_doSavePage、ee_doSaveMem、ee_doSaveArray到ree_getMem、ree_getArray、ree_setMem、ree_setArray,覆盖了页分配、保存和读写。BODEM/BB-3 组则围绕过程变量、参数和错误消息,提供bb3_setName、bb3_getVar、bb3_setError、bb3_getActiveErrors、bb3_delActiveError、bb3_getSavedErrors、bb3_delSavedErrors。

5.1 EEPROM 保存:先看页状态,再决定写不写

文档在 EEPROM 小节开头就讲了“载入新程序的参数”和“保存参数”,然后才是“分配内存页面”。这个顺序暗示了实际使用逻辑:参数保存不是随手调一个ee_doSaveMem就完事,要先确认页状态和内存变更标志。

(* EEPROM 保存流程 *) nPageStat := ee_getPageStatus( nPage ); IF nPageStat = PAGE_STATUS_OK AND sys_isMemChanged() THEN ee_doSavePage( nPage ); END_IF (* 数组保存 *) ee_doSaveArray( nPage, nArrayAddr, nCount );

逻辑说明:ee_getPageStatus返回页状态,状态不正常时写页可能失败或损坏数据;sys_isMemChanged判断内存是否有变更,没有变更就不写,减少擦写次数——EEPROM 寿命有限,频繁无脑保存是血泪教训。ee_doSaveArray适合批量参数,参数是数组起始地址和元素个数,地址要落在已分配的页内。ree_getMem/ree_setMem是直接内存读写,ree_getArray/ree_setArray是数组版本,用的时候注意地址对齐和长度边界,越界写不会报错但会踩坏相邻数据。

5.2 BODEM/BB-3 错误管理:活动错误和已保存错误分开处理

bb3_setError置错误,bb3_getActiveErrors取当前活动错误,bb3_delActiveError删活动错误,bb3_getSavedErrors取已保存错误,bb3_delSavedErrors删已保存错误。活动错误和已保存错误是两套记录,前者反映当前状态,后者是历史留档。诊断界面一般先显示活动错误,用户确认后再清除;历史错误用于售后分析,不能随便删。

(* 错误上报与查询 *) bb3_setError( nErrCode, nErrArg ); nActiveCount := bb3_getActiveErrors( aErrBuf, nBufSize ); IF bUserAck THEN bb3_delActiveError( nErrCode ); END_IF

逻辑说明:bb3_setError的参数是错误码和附加参数,错误码定义在文档的错误消息章节,按字母序和错误号升序各有一份表,查的时候按手头信息选表。bb3_getActiveErrors把活动错误填进缓冲区,缓冲区大小要够,否则截断。删除活动错误前要确认故障源已消失,否则删了又立刻报,日志里全是重复记录。

5.3 伺服函数仅限 MC6:stm_ 系列的初始化顺序

伺服马达函数明确标注仅适用于 MC6,包括stm_init、stm_getStat、stm_getIst、stm_setSoll、stm_doStop,文档还给了一个示例程序。stm_init做初始化,stm_getStat取状态,stm_getIst取实际值,stm_setSoll设目标值,stm_doStop停止。顺序上必须先 init 再 setSoll,停止用 doStop 而不是直接把目标值写 0,后者在部分工况下不满足停机时序要求。

6. 避坑与排查:五条从台架和现场攒下来的记录

6.1 编译报“未定义标识符 void”

现象:调用SYS_getLoadedPages( void )时编译失败,提示 void 未定义。原因:工程没有正确引用BODAS_xxx.lib,或者引用的是不匹配的库版本。解决:检查库引用列表,确认库文件与目标系统一致,重新编译;仍不行就查库的声明段是否包含 void 变量。

6.2 模拟输入读数恒为 0 或满量程

现象:in_getPoti返回值不随物理动作变化。原因:初始化模式与传感器类型不匹配,或通道号落在未接线的板卡上。解决:先核对in_initAna的iMode1/iMode2与传感器输出类型,再用万用表量实际引脚电压,最后确认双卡系统的 A/B 板寻址。

6.3 CAN 能发不能收

现象:发送帧总线上能看到,接收数据盒始终无数据。原因:接收数据盒未初始化,或中断被禁用后未重新使能。解决:确认can_initRecDatabox在接收前调用,检查can_doEnableInterrupt与can_doDisableInterrupt是否配对,用can_getStatus看控制器状态位。

6.4 EEPROM 保存后参数丢失

现象:下电重启后参数回到默认值。原因:保存前未检查页状态,或保存的是内存区而读取的是页区,地址不匹配。解决:保存前用ee_getPageStatus确认页可用,保存和读取用同一套页/地址定义,保存后回读校验一次。

6.5 换控制器型号后代码行为异常

现象:同一份代码在 MC6 上正常,换 RC12-18 后输入输出错乱。原因:in()、out_系列和部分 sys 函数按目标系统分版本,通道定义和可用函数不同。解决:按目标系统重建输入输出映射层,逐函数核对文档中的“仅适用于”标注,用sys_getRTSRevision记录版本做对比。

7. 把指令集变成可检索的工程资产:我的函数速查表做法

这份文档是英文的,函数名和参数说明混排,直接翻 PDF 效率不高。我的习惯是把它拆成一张可检索的速查表,按“函数名 / 所属组 / 目标系统限定 / 参数要点 / 返回值含义 / 踩坑备注”六列组织,用表格软件维护,写代码时第二屏常开。下面是我实际在用的表结构片段,列出来供参考:

函数名组别目标系统限定参数要点返回值备注
in_initAna输入仅 RC2-1iChnl=ANA_A1..2;iMode1 管参考电压/分流;iMode2 仅 A2 有效INTSHUNT 对两通道都适用
in_getPotiStatus输入全系统通道号与初始化一致状态编码按目标系统查编码表
sys_setErrorLed系统仅 RC6-9 / RC12-18按文档参数BOOLMC6 无此函数
ee_doSaveArrayEEPROM全系统页号、数组地址、元素数按文档地址须在已分配页内
can_sendDataboxCAN全系统通道、数据盒 ID按文档数据盒须先初始化

建表时有个细节值得说:目标系统限定这一列不要只写“是/否”,要写清楚限定在哪个型号,因为同一组函数里不同函数的限定可能不一样,比如in_initAna仅 RC2-1 而in_initDig不适用于 RC2-1,混在一起记必然出错。参数要点列只记“会填错的地方”,不抄完整声明,抄完整声明不如直接看原文。

验证方法上,我一般做两轮:第一轮在台架上用最小工程逐个调用初始化函数,用调试器看返回值和状态位是否符合预期;第二轮把业务代码里的每个运行时函数调用点加上注释,标注文档章节号,后期维护时按章节号回查,比凭记忆靠谱。这套做法不复杂,但坚持下来之后,因为“记错参数”导致的返工明显少了。

从那以后我每次拿到新的控制器型号,第一件事不是写业务代码,而是先把这份指令集里该型号的限定函数筛一遍,建好速查表再动手。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表