☰
BHT-BASIC 4.0手持终端编译链部署与刷机避坑指南
2026/10/27 10:46:33 网站建设 项目流程

简介:这份资源是DENSO BHT-BASIC 4.0开发套件,主要面向使用DENSO BHT系列手持终端进行库存管理、条码扫描与数据采集应用开发的工程师,也适合刚接触BHT-BASIC的初学者。内容围绕BHT-BASIC 4.0的开发工具链展开,涵盖源代码编辑、编译与调试环境,并提供条码、RFID读取解析以及串口通信、小型数据库支持等应用开发所需的基础能力,其中R1.15开发包的说明与远程调试资料,可以帮助读者把程序编译、部署到真机上验证。压缩包共24个文件,约23.31MB,以13个gif安装与界面截图、4个PDF手册(含远程调试启动指南及Vista补充说明)、3个exe安装程序为主体,另有README说明文本和HTM索引页,便于按装、查手册、排错。目前已有392人学习。读者可据此搭建完整的开发环境,理清从源码编写到上机测试的流程,并借助官方示例与截图快速定位常见问题,减少摸索成本。

1. 手持终端的 BASIC 编译链:为什么 4.0 版本还有人翻出来

仓库盘点的场景里,一线作业员手里那台东西通常只有四行屏、十几个数字键、一个红外窗口,没有操作系统,也没有任何 App 生态。它跑的逻辑来自 ROM 里的 BASIC 解释器,而 PC 这一侧需要一个能把源码变成目标文件再刷进设备的工具,BHT-BASIC 4.0 就是这条链路上的编译器加配套工具集。它解决的问题很具体:在内存以 KB 计、屏幕以字符计、通信靠串口的工业手持机上,把扫码采集、数量累加、越限告警这套业务逻辑稳定跑起来。现在还在用它的人,基本是旧设备还在服役、原厂已经不维护工具链的场合,或者做仓储设备二次开发、需要离线批量刷机的从业者。它对新手不算友好,但链路短、行为确定,摸清之后反而比很多现代框架更好排查。

2. 装环境:在 64 位 Windows 上把编译链跑起来

这套工具集的安装包是上个时代的产物,安装器本身是 32 位甚至 16 位程序,直接在现在的 64 位系统上双击,最常见的结局是进度条走到一半静默退出,或者装完之后点图标毫无反应。血泪经验是:别在主力机上反复试,先准备一台干净的虚拟机。我一般会开一台 32 位的 Windows 虚拟机专门放这套东西,快照随时能回滚,编译环境和现场拷回来的工程文件也隔离得干净。如果实在只能用 64 位主机,退而求其次的办法是手动解包安装器,把文件铺到目录里再注册依赖,但这条路不保证每条命令都能走到最后。

2.1 安装目录与运行时依赖

安装完成后,先不要急着点图形界面,先看一眼目录结构。不同版本的组织方式有差别,但大体跳不出下面这张表里的几块:

子目录内容是否必须
BIN编译器主程序、下载工具、模拟器必须
LIB运行时库、常量定义、机型配置文件必须
SAMPLE各机型的示例工程建议保留
DOC语句手册、错误码表必须

依赖这一块是翻车重灾区。老工具链基本都依赖两个东西:一个是早期的 Visual Basic 运行库,另一个是串口通信控件。前者在安装器里通常自带,后者经常需要手动注册。以管理员身份打开命令行,进到BIN目录执行:

# 以管理员身份执行,注册串口通信控件 # 路径按实际安装目录替换,注意是 32 位版本的 regsvr32 cd C:\BHTBASIC\BIN C:\Windows\SysWOW64\regsvr32.exe /s MSCOMM32.OCX # 顺手校验关键文件是否齐全 dir /b C:\BHTBASIC\BIN

第一行的SysWOW64不能写成System32,64 位系统上这两个目录的名字跟实际位数是反的,写错了也不报错,只是注册不生效,表现为下载工具里串口下拉框永远是空的。/s是静默参数,去掉它能看到弹窗提示。第二条dir /b是裸列表,用来快速核对编译器可执行文件、下载工具、模拟器是不是都在,少一个就说明安装器中途失败了,别浪费时间往下走。

环境变量方面,建议把BIN目录加进PATH,这样后面写批处理做批量编译时不用每次都写全路径。加完之后新开一个命令行窗口验证:

# 验证编译器是否在 PATH 里 where bhtcomp

where返回路径就说明通了,返回「找不到文件」就是PATH没生效或者可执行文件名跟你想的不一样。不同版本的可执行文件名可能有出入,以BIN目录里实际列出来的为准,别硬套。

2.2 工程目录结构与配置文件

这套工具对工程目录的组织方式比较敏感,最好不要随手在桌面新建一个文件夹就开始写。比较稳的做法是每个业务一个独立目录,源码、字段定义、编译输出分开放:

ZAIKO\ ZAIKO.PD4 源码主文件 ZAIKO.FIL 数据文件字段定义 ZAIKO.PRJ 工程描述,记录机型与库路径 OUT\ 编译输出目录 ZAIKO.OBJ 目标文件,最终刷进设备的就是它 BUILD.LOG 编译日志

PRJ文件是这个工程的黑匣子,里面记录了三件关键信息:目标机型代号、运行时库的搜索路径、编译选项。机型代号选错是最常见的低级错误——不同机型的屏幕尺寸、按键数量、可用内存都不一样,选错了编译能过,下载也能过,跑起来就是花屏或者按键错位。我一般会在建工程的第一步就把机型号写进工程名,比如ZAIKO_DT930,省得后面同时维护多个机型时搞混。

编译选项里有一个容易被忽略的开关:是否把运行时库静态链接进目标文件。静态链接的好处是刷机时不用额外下发库文件,坏处是目标文件体积会大出几 KB,在 32KB 程序区里这是要命的。旧机型上我一般关掉静态链接,把库单独刷一次;新一点的机型容量宽裕,开着更省事。

2.3 版本对齐与许可

编译器和设备固件之间的版本对齐,是这套工具链里最容易被跳过的一步。工具集的版本号是 4.0,不代表所有跑 4.0 的设备都能收 4.0 编译出来的目标文件。实际的做法是先确认设备固件版本,再回来看工具集DOC目录里的兼容表。常见情况是:编译器版本比固件新,编译出来的目标文件用了新语句,下载时能通过校验,运行时直接死机;编译器比固件旧,某些语句在设备上根本不存在,编译阶段就会报未定义。

许可是另一块。这套工具集当年大多是随设备捆绑或者按席位授权,安装后可能需要在某个界面里填序列号,也可能是一个放在安装目录下的授权文件。如果拿到的是一份已经装好的目录拷贝,先翻一下根目录有没有.LIC、.KEY之类的文件;没有的话,界面里大概率会有一个登记入口。这里没有什么技巧可言,缺什么补什么,别去猜格式。

3. 从源码到目标文件:一份能编译通过的最小工程

环境通了之后,第一件事不是急着写业务逻辑,而是先让一个最小工程完整走一遍「源码 → 编译 → 目标文件」的流程。这一步跑通了,后面所有报错才有对照基线。我见过太多人一上来就写几百行扫码逻辑,结果编译报错三十条,分不清是环境问题还是语法问题,最后整包推倒重来。

3.1 字段定义文件与数据记录结构

这套工具集里,数据文件不是靠代码运行时动态建的,而是先写一份字段定义文件,由工具链在编译阶段生成访问结构。字段定义文件决定了记录长度、字段顺序和字段类型,改一次就要重新编译所有引用它的程序,所以前期设计要一次性想清楚。

' ZAIKO.FIL 数据文件字段定义 ' 语法:FIELD = 序号, 字段名, 长度, 类型 ' 类型:A = 字符型,N = 数值型 FILENAME = ZAIKO RECORD = 1 FIELD = 1, HINBAN, 13, A ' 品番,13 位字符,对应常见条码长度 FIELD = 2, SUU, 5, N ' 数量,5 位数值,最大 99999 FIELD = 3, TIME, 6, A ' 时间戳,HHMMSS

三个字段加起来记录长度是 24 字节,RECORD = 1表示只定义一个记录格式。这里有两个硬约束:单条记录长度不能超过 255 字节,字段数量一般不超过几十个。超过之后工具链会直接拒绝,报错信息通常只给一句「定义非法」,不会告诉你具体超在哪,所以要自己拿计算器加一遍。

字符型字段的长度建议按实际条码长度取整,不要拍脑袋写 50 或者 100。设备上的数据区是按记录数 × 记录长度静态分配的,写大了直接吃掉可用记录条数。我一般的做法是先统计现场条码的最长长度,再加两三位余量,比如 13 位的 EAN 码就写 13,需要存前缀就写 15。

3.2 主程序骨架与语句边界

源码主文件的后缀在不同版本里有差异,常见的是.PD4。下面这份骨架是我一般用来验证链路的最小结构,功能是扫码、查重、累加、越限告警,正好覆盖了数组、循环、条件分支、屏幕输出、蜂鸣器几类语句:

' =========================================== ' 程序名 : ZAIKO.PD4 ' 机型 : 手持终端 型号X ' 功能 : 扫码入库,同品番累加,超上限告警 ' =========================================== ' ---- 常量 ---- CONST MAX_REC = 100 ' 最大记录数,受数据区容量限制 CONST LIMIT = 999 ' 单品行数量上限 ' ---- 变量 ---- DIM HIN$(MAX_REC) ' 品番数组 DIM CNT%(MAX_REC) ' 数量数组 DIM PTR% ' 当前已用记录指针 DIM I% ' 循环变量 DIM BAR$ ' 扫码结果 DIM FOUND% ' 查重命中标志 ' ---- 初始化 ---- PTR% = 0 CLS LOCATE 0, 0 PRINT "PLEASE SCAN" BEEP 1 ' ---- 主循环 ---- DO ' 等待一次扫码输入 KEY 1, BAR$ IF BAR$ = "" THEN ' 空输入直接跳过,不做任何处理 CONTINUE DO END IF ' 在已有记录里查重 FOUND% = 0 FOR I% = 0 TO PTR% - 1 IF HIN$(I%) = BAR$ THEN FOUND% = 1 EXIT FOR END IF NEXT I% IF FOUND% = 1 THEN ' 命中,累加并判断上限 CNT%(I%) = CNT%(I%) + 1 IF CNT%(I%) > LIMIT THEN BEEP 3 LOCATE 0, 2 PRINT "OVER LIMIT" END IF ELSE ' 未命中,新增一条 IF PTR% >= MAX_REC THEN BEEP 3 LOCATE 0, 2 PRINT "MEM FULL" ELSE HIN$(PTR%) = BAR$ CNT%(PTR%) = 1 PTR% = PTR% + 1 END IF END IF ' 刷新屏幕计数 LOCATE 0, 3 PRINT "CNT="; PTR% LOOP

这份骨架里有几处是必须按设备手册核对的:KEY语句的写法在不同固件上可能是别的名字,有的机型用专门的扫码读取语句,参数含义也不完全一样;BEEP后面跟的数字表示响几声或者响多长,各机型定义不同;CONTINUE DO这类循环控制语句不是所有版本都支持,老版本要改成GOTO加标号。骨架的作用是把程序结构固定下来,具体语句名在编译报错时按手册替换即可,不要因为一句报错就推翻整个结构。

数组维度MAX_REC = 100不是随便写的。两个数组,一个 13 字节的字符串数组加一个整型数组,粗略算下来要吃掉两 KB 左右的数据区,再加上程序本身和运行时开销,32KB 的机型上已经过半。真实现场如果记录数要求更多,就得改用文件方式落盘,而不是全塞内存。

3.3 触发编译与读日志

图形界面里有编译按钮,但我更推荐先跑一遍命令行编译,因为日志是完整的,出错时能直接定位。常见的调用形式大致是这样:

# 进入工程目录后执行 cd /d C:\WORK\ZAIKO # 全量编译,输出目标文件与详细日志 bhtcomp ZAIKO.PD4 /O OUT\ZAIKO.OBJ /L OUT\BUILD.LOG # 编译结束后看日志尾部 type OUT\BUILD.LOG

参数含义:第一个位置参数是源码主文件;/O指定目标文件输出路径,一定要带上,不带的话默认输出到当前目录,很容易跟源码混在一起;/L指定日志文件,不加这个参数时详细报错不落盘,出了错只能靠界面上一闪而过的提示。可执行文件名和参数开关在不同版本里可能不同,用bhtcomp /?看一次帮助,把实际开关记下来存成批处理。

日志读法上有个习惯值得养成:先看最后一行,再看第一条错误。编译器一般会把汇总信息放最后,比如「N errors」,而第一条错误往往是根因,后面的错误经常是它引发的连锁反应。有一次我改了一个字段长度,后面报了十七条错误,逐条看下去全是引用处的类型不匹配,其实根因只有第一行那条「字段定义与引用不一致」。

4. 避坑清单:编译报错、容量超限与字符集的 5 个现场

这套工具链的报错信息质量参差不齐,有些能精确到行号和列号,有些只给一句笼统的提示。下面这五条是这些年反复撞上的,按「现象 → 原因 → 解决」记下来,能省掉不少瞎猜的时间。

4.1 编译期报错:报错码和源码对不上

现象一:报错行号指向注释行或者空行。编译输出里给出的行号,落在一行纯注释或者一个空行上,那行代码本身看不出任何问题。翻了半天才发现,真正的问题在下一行或者上一行。原因基本只有一个:源码里混进了全角字符。中文输入法状态下打出来的空格、括号、逗号,在编辑器里跟半角长得几乎一样,编译器按单字节解析时就会整体偏移,行号自然对不上。解决办法是把编辑器字体换成等宽字体并打开「显示空白字符」,全角空格会显示成一个方块。更彻底的做法是保存前跑一次检查,把所有非 ASCII 字符列出来:

# 列出源码里所有非 ASCII 字符及其行号,全角字符会在这里现形 findstr /n /r "[^ -~]" ZAIKO.PD4

这条命令的正则含义是「不在空格到波浪号这个可打印 ASCII 区间内的字符」,中文注释也会被列出来,所以看结果时把注释行过滤掉,剩下的就是嫌疑对象。

现象二:改了代码,编译也过了,设备行为没变。最常见的解释是编译产物没更新。工具链在增量编译时依赖文件时间戳,如果系统时间被改过、或者工程目录是从别的机器拷贝过来的,时间戳可能比源码还新,编译器就认为不需要重编,直接用旧的OBJ。表现就是明明改了逻辑,刷进去跑的还是老行为,查半天以为是设备缓存。解决很简单:每次出包前删掉输出目录做全量编译,并且刷机前核对OBJ的修改时间和文件大小。

4.2 链接与容量:目标文件超限

现象三:编译阶段零错误,链接阶段报容量溢出。这种情况特别迷惑人,因为源码层面看不出任何问题。原因在于编译只检查语法,链接时才把运行时库、常量表、字符串池全部算进去,总量超过程序区上限就溢出。手持机的程序区通常只有 32KB 这个量级,随便几个没用到的库函数就能吃掉两三 KB。解决分三步:第一步用日志里的容量明细,看清楚是代码段还是数据段超了;第二步检查有没有把整个运行时库静态链接进来,关掉这个选项通常能省下可观的体积;第三步把大数组从内存改成文件存储,数组是数据段膨胀的头号原因。

现象四:数量字段累加到某个值之后突然归零。这不是编译问题,是字段类型选错。数值型字段在设备上是按定长存储的,5 位数值最大到 99999,超过之后不会报错,直接溢出回绕。现象是作业员反馈「数着数着就变 0 了」,但代码里看不出任何清零逻辑。解决办法是在累加前加越限判断,或者把字段长度加大。这类问题在测试阶段很难复现,因为要跑到边界值才触发,只能靠设计阶段就把字段长度算清楚。

4.3 字符集与显示:宽字符变方块

现象五:模拟器里显示正常,刷到真机后中文全变成方块。模拟器用的是 PC 上的系统字体,设备用的是固化在 ROM 里的点阵字模,两边的字符集范围不一样。源码文件如果存成 UTF-8,编译器在解析字符串常量时按单字节切分,到了设备上自然对不上字模。解决方法是把源码文件另存为设备字库对应的单字节编码,并且在编辑器里关掉「自动检测编码」,强制指定。保存之后重新编译,别指望在旧的目标文件上做任何补救。

提示:这一条对所有老式工业设备的工具链都适用。源码编码、编辑器编码、设备字模三者必须一致,任何一环用了默认值都可能翻车。

5. 下发与回读:把程序刷进手持机的正确姿势

编译出目标文件只是半程,把文件真正灌进设备是另一条独立的链路,而且这条链路的失败率高得多——串口、红外对准、开机按键时序、电池电量,任何一个环节不对都会卡住。很多人在这一步耗掉的时间比写代码还多。

5.1 通信参数与串口配置

下载工具走的是串口,早期用有线 cradle,后期机型多用红外或者光通信。不管哪种,参数基本固定:波特率 9600 或者 19200,数据位 8,无校验,停止位 1,无流控。这几个值在下载工具的设置界面里能改,但设备那一侧是不可改的,所以必须按设备手册里的值来配,配错了表现为连接超时或者收到的全是乱码。

串口号的选择有个坑:USB 转串口线在 Windows 上每次插拔可能换号,下载工具里配好的 COM3 下次开机变成 COM5,点下载就提示打不开端口。稳妥的做法是在设备管理器里把 USB 转串口线的端口号手工固定下来,固定之后每次插都还是同一个号。红外通信还要额外注意物理对准,两个窗口之间的夹角超过十几度就基本收不到,现场我一般会让作业员把设备顶住 cradle 的卡槽,别用手悬着。

连接建立之后的第一次握手最重要。下载工具一般会先发一个探测包,设备回应之后界面上的状态灯会变。如果一直停在「等待设备」,先检查设备是不是已经开机进了传输模式,再检查串口线是不是插在正确的口上,最后才怀疑线材本身。

5.2 下载/上传的时序与开机按键

这套设备的传输模式不是常驻的,需要在开机时按住特定按键组合才能进入。不同机型的组合不一样,常见的是按住某个功能键再按电源键,屏幕会显示一行提示文字。这里有两个必须记住的时序细节:

第一,按键要在按电源键之前按住,并且保持到屏幕出现提示为止。松早了设备会走正常启动流程,直接进应用界面,这时候再插线也没用,必须关机重来。第二,传输模式有超时,一般几十秒内没有开始传输就会自动退出,所以下载工具的配置要提前弄好,进了传输模式直接点开始。

上传方向同样重要。设备上跑了一段时间的采集数据,需要通过同一个工具回读到 PC,回读的格式和编译时的字段定义是对应的,字段定义改过之后,旧数据回读出来会错位。我一般的做法是:每次更新程序前先把设备里的数据回读干净并存档,再刷新程序,避免数据格式对不上导致现场数据丢失。

# 下载/上传类操作不建议写批处理自动化 # 老工具的串口握手不稳定,人工点按钮反而更容易判断卡在哪一步 # 唯一值得脚本化的是刷机前后的文件校验 certutil -hashfile OUT\ZAIKO.OBJ MD5

最后这条命令算目标文件的哈希,用来确认刷进去的就是刚编译出来的那个文件。多人协作或者一次刷几十台设备时,这个校验能挡住「拿错版本」这类事故。

5.3 模拟器先跑一遍再上真机

工具集里带的模拟器是个被严重低估的东西。它能在 PC 上把设备屏幕、按键、扫码输入都模拟出来,逻辑错误在这一层就能抓掉大半,不用每改一行就刷一次真机。用模拟器的正确姿势是:先在模拟器里把主流程跑通,包括边界情况(空输入、满容量、超上限),确认逻辑没问题,再上真机验证硬件相关的部分。

但模拟器有个不能信的边界:它不模拟内存上限,也不模拟真实的时序。模拟器里数组开多大都能跑,真机上超了就是死机;模拟器里循环跑一万次瞬间完成,真机上可能要等好几秒,作业员会以为死机了然后长按电源键。所以模拟器只验证逻辑,容量和性能必须按真机来核。

6. 内存表与提速:把 32KB 塞满也不崩的做法

设备端的资源账要提前算,不能等链接报错了再回头改代码。下面这张表是我一般用来做容量预估的基线,具体数值按机型手册调整,但比例关系基本一致:

区域典型容量主要占用者可控手段
程序区32KB编译后的代码、字符串常量关静态链接、拆分模块
数据区64KB数组、文件记录、运行时栈改数组为文件、缩短字段
显示缓冲固定屏幕字符不可控
字模区固定ASCII + 点阵汉字按需裁剪字库

字符串常量是最容易被忽视的吞金兽。一段几十行的提示文字,编译进程序区就是实打实的字节,而且这些常量在各个分支里重复出现时编译器不一定合并。我的习惯是把所有提示文字集中到一个CONST块里定义,用的时候只引用常量名,这样至少能保证同一句话在二进制里只出现一次:

' 提示语集中定义,便于统计占用和统一修改 CONST MSG_SCAN = "PLEASE SCAN" CONST MSG_FULL = "MEM FULL" CONST MSG_LIMIT = "OVER LIMIT" ' 使用处只引用常量 LOCATE 0, 2 PRINT MSG_FULL

循环里做屏幕输出是老设备上最大的性能杀手。字符屏的刷新走的是慢速总线,一次PRINT的开销可能是几十条算术指令的量级。如果在扫码循环里每读一条就刷一次屏,作业员会明显感觉到按键响应变迟钝。做法是把屏幕刷新收敛到状态变化时,比如数量每变化十次刷一次,或者只在开始和结束各刷一次,中间过程让蜂鸣器代替提示。

还有一个反直觉的优化:查重用的线性遍历,在记录数少于一百条时,比排序加二分更快。因为排序本身要消耗数据区的临时空间,还会带来额外的交换开销,而手持机的记录规模一般就几十条,遍历的实际耗时在毫秒级,作业员完全感知不到。等到记录数上千条再考虑索引结构,那时候瓶颈早就不是遍历而是数据区容量了。

从那以后我每次接这类老设备项目,第一步都是先把容量表画出来贴在工位上,任何新功能先在上面划一块地方,划不下就先改设计再写代码。这个习惯救过我很多次——有一次需求临时要加一个「按品番查询历史」的功能,按老思路直接加数组,算了算数据区只剩三百字节,当场改成用文件顺序扫描,虽然慢一点,但一次都没崩过。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询