把数控系统想象成一栋办公楼。
你写的每个 # 号,本质上都是在挑一间房间去存东西。搞清楚每间房的脾气,你才能不乱放、不撞车。
先给一张"户口本",一表看懂四大家族:
| 家族 | 编号范围 | 断电后 | 谁能用 | 典型用途 |
|---|---|---|---|---|
| 空变量 | #0 | —(永远空) | 全局只读 | 判断"这参数传没传" |
| 局部变量 | #1~#33 | 清零 | 仅当前宏 | 参数传递、临时计算 |
| 公共变量(A区) | #100~#199 | 清零(可选掉电保持,看参数6001#6) | 全局 | 跨程序临时通信 |
| 公共变量(B区) | #500~#999 | 保持不丢 | 全局 | 重要数据、状态保持 |
| 系统变量 | #1000+ | 系统定义 | 全局 | 读写坐标/刀具/模态/报警 |
别被"全局"吓到:公共变量和系统变量是所有宏程序共享的"公共空间",局部变量才是每个宏自己的"私有空间"。
#0 是全局最特殊的一个变量。它不是数值 0,而是"空"——表示"这个数根本不存在"。
它是只读的,你不能写 #0 = 5。
行为 1:地址字遇空,直接忽略。
#1 = 100 (#1 有值)
#10 = #0 (#10 是空的)
G90 X100 Y#1 Z#10 (实际执行:G90 X100 Y100 —— Z 被忽略!)
因为 #10 是空的,程序段里的 Z#10 连同 Z 地址一起被扔掉了,只执行 X100 Y100。
行为 2:判断里,空 ≠ 0。
#1 = 0
IF [#1 EQ #0] GOTO 100 (这里不会跳!因为 0 ≠ 空)
在条件判断里,空变量既不等于 0,也不等于任何数(它"谁都不等于")。想判断"这个参数传没传",就要用 IF [#x EQ #0],这是宏编程检查参数是否缺失的唯一正确姿势。
#1~#33 是局部变量,规矩很明确:
常见约定(非强制,但行业习惯如此):
#1 ~ #26 (接收 G65 传入的自变量,对应 A~Z)
#30 ~ #33 (宏内部临时变量,随便用,别覆盖 #1~#26)
公共变量是全局的,任何宏程序都能读写同一个 #100。这既是便利,也是事故源头。
| 区段 | 断电后 | 用途倾向 |
|---|---|---|
#100~#199 | 清零(除非改参数6001#6让它保持) | 临时的全局交换、计数器 |
#500~#999 | 掉电保持 | 要长期保存的数据,如刀具基准直径、工件原点偏移 |
典型用途:
(程序 A 结束时,把当前加工号记进公共变量)
#500 = 101 (掉电也不丢,下次开机还能读)
(程序 B 开机检查:上次加工到哪个号了?)
IF [#500 NE #0] THEN #501 = #500
⚠️ 但请看这段的坑:程序 A 刚写 #500,程序 B 就指望读它——如果 A 和 B 不同时在线、或 B 先于 A 运行,#500 读到的可能是上一次运行残留的旧值,甚至 #0。共享变量最大的敌人是"时序错乱"。
系统变量映射 CNC 内部真实状态,是宏编程真正"高级"的地方:
#5001~#5004 工件坐标位置、#5021~ 机床位置、#5061~ 跳转位置#2001~ 刀具补偿值#4001~ 当前 G 模态、#4102~ 辅助信息#3000 宏报警、#3001/#3002 计时器、#3006 程序停、#3011/#3012 日期时间#1000~ 读 PMC 输入信号、#1100~ 写接口给 PMC系统变量大多是只读的,少数可写(如 #3000 触发报警、#1100 输出信号)。乱写千万别试,改错坐标系统变量可能让刀直接怼上工件。
系统变量的详细拆解,是第 05、06 篇的重头戏。这一篇你只需记住:它是机床"现在真实状态"的窗口。
最常见的写法,直接写变量号:
#100 = 3
#102 = #100 + #100 * 10 (#102 = 3 + 30 = 33)
想把"变量号"也做成变量,用 #[#xxx]。当 #xxx = *** 时,#[#xxx] 就等同于 #***:
#1 = 100 (#1 里存的是"变量号"100)
#[#1] = 5 (等同于 #100 = 5)
#7 = #2 (#7 等于 #2 的值 —— 这是直接指定,别跟上面搞混)
规则:#1 存数值 N 时,#[#1] 就是变量 #N。 例:#10 = 500 → #[#10] ≡ #500;#3 = 2001 → #[#3] ≡ #2001。
方括号里不一定是单独的变量,也可以是变量号表达式:#[2000 + #n]。当 #n=5 时它就是 #2005 —— 这是动态访问刀补的标准写法,后面第 05 篇会用到。
间接寻址特别适合做循环:用 #1 当计数器(依次存 100、101、102...),就能批量访问 #[#1] → #100、#101、#102...。
成组系统变量(刀补、位置、PMC 接口等)除了用变量号直接写,还可以用官方预定义的名称加 [n] 标号访问,n 为组内序号:
#31 = 5
#32 = [#_OFS[#31]] (名称标号:刀补组第 5 个 → 就是 #2005)
#33 = [#_ABSMT[1]] (名称标号:第 1 轴机械坐标 → 就是 #5021)
⚠️ 千万别把 ② 和 ③ 当成一回事:
#[#31](间接寻址)当 #31=5 时 → 是 #5[#_OFS[#31]](名称标号)当 #31=5 时 → 是 #2005间接寻址取的是"变量号=#31的值"那个变量;名称标号取的是"刀补组第 n 个"。注意:官方语法里不存在 #2001[#31] 这种"数字变量号后面挂下标"的写法,想动态访问第 n 号刀补,请用表达式间接寻址 #[2000+#n] 或名称标号 [#_OFS[#n]]。学到第 05 篇刀补时会用到。
坑 1:拿公共变量当局部变量用,结果"撞车"。
两个宏程序都自作主张用 #100 存临时数据,先后调用时后写的把先写的覆盖掉,整个程序乱套。规矩:公共变量 #100~#199 一定只存"真正要跨程序共享"的数据,临时计算全部丢给局部变量 #30+。
坑 2:断电清零的变量,开机后是 #0 空值,不是 0。
很多新手误以为"没存东西就是 0"。错!没存过就是空 #0。所以读到公共变量后,判断要用 IF [#x EQ #0] 检查"到底有没有值",而不是想当然地直接拿去算。空值参与运算会整段忽略,意外就来了。
现在你认识了变量江湖的四个家族:
概念到位了,但光说不练假把式。下一篇我们拿局部变量真刀真枪地写一个会"记数"的程序:
👉 第 03 篇《局部变量实战:让程序会"记数"》——用 #1~#33 接收参数、做循环计数、按惯例分层使用,全程实战代码。
我们下一篇见!