ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TSC标签打印机TSPL指令详解:C#实现二维码打印的工程实践

TSC标签打印机TSPL指令详解:C#实现二维码打印的工程实践 简介这是一套使用C#编写的TSC标签打印机二维码标签打印源码面向新手及有一定经验的C#开发人员解决驱动TSC系列打印机输出二维码标签的实际需求。压缩包共79个文件大小仅2.12MB结构清晰23个cs源码文件实现窗体逻辑与打印核心11个dll库文件包含打印机驱动或第三方组件7个resources/resx资源文件存界面布局5个config配置文件用于参数设置。目前已有1218人学习下载内容获得一致认可。源码包含完整的ShenMaPrint项目涉及多个窗体、参数读取工具、日志组件等模块可直观学习二维码生成、标签尺寸控制、打印命令调用等关键环节同时提供配置参数外部化与日志跟踪机制方便排查与维护。代码注释清晰目录结构紧凑适合作为实战参考或二次开发基础便于快速集成到进销存、物流等标签打印应用场景。1. TSC标签打印机打印二维码C#程序从命令下发开始多数人接到“TSC标签打印机打印二维码”这个需求第一反应是“在Windows里生成一张二维码图片再调打印机驱动把它打出来”。真到了产线上这套思路很容易在分辨率、坐标和耗材上翻车。我做了几套产线标签程序之后固定改用TSPL指令直接往打印机下发命令让打印机自己画二维码程序只负责内容组织、坐标计算和参数控制。这套方案稳定、速度快、也方便维护适合做上位机和产线集成的开发者参考。下面把C#程序源码的工程思路、坐标换算、参数搭配和踩坑记录一次讲清楚。2. 从驱动打印切换到TSPL指令C#程序的最小工程先跑通2.1 为什么在TSC标签打印机上优先用TSPL指令而不是Windows驱动TSC打印机有两条常见打印路径。一条是安装Windows驱动后把它当成普通打印机程序生成图片后交给GDI打印。另一条是走TSPL指令程序把一串类似SIZE 50 mm,30 mm、QRCODE 20,20,...的文本指令通过串口、USB虚拟串口或网口发给打印机指令里已经写清楚纸张尺寸、内容位置和打印份数。驱动打印在单机演示场景下没问题但到产线上会遇到几个现实麻烦坐标计算不直观同一份内容在不同DPI驱动下的点阵解释有差异无法直接读取打印机缺纸、开盖、碳带耗尽的状态批量多线程打印时容易出现任务排队卡顿。TSPL指令则相反每一条指令对应一个明确的动作打印机状态也可以主动查询。TSPL在TSC全系标签机上都通用换机型不用重写整个程序改改端口和DPI参数就能接上。TSPL方案也有限制不适合打高精度彩色图片处理复杂矢量图形的能力弱。标签打印绝大多数是黑白条码、二维码、文字和简单图形所以这个限制在项目里基本可以忽略。另外如果你拿到的是TSC的老机型建议先用TSC官方诊断工具DiagTool查一下固件版本确认它支持哪些TSPL指令后面二维码命令的参数格式也要以固件支持为准。2.2 打开串口并发送TSPL指令SerialPort的最小代码TSC标签机一般提供串口、并口、USB和网口四种接口工业项目里最常用的是串口或USB虚拟串口。C#里用System.IO.Ports.SerialPort就能直接收发不需要额外安装驱动。下面是一个最小可用的打开串口并发送指令的代码using System; using System.IO.Ports; using System.Text; public class TscPrinter { private SerialPort _port; public bool Open(string comName, int baudRate 9600) { _port new SerialPort(comName, baudRate, Parity.None, 8, StopBits.One) { WriteTimeout 3000, ReadTimeout 1000 }; try { _port.Open(); return _port.IsOpen; } catch (Exception ex) { Console.WriteLine($打开串口失败: {ex.Message}); return false; } } public void SendCommand(string tsplCommand) { if (_port null || !_port.IsOpen) throw new InvalidOperationException(串口未打开); // TSPL 指令以 CRLF 结束缺了换行打印机不解析 _port.Write(tsplCommand \r\n); } public void Close() { if (_port ! null _port.IsOpen) _port.Close(); } }串口参数的几个关键点波特率不一定总是9600TSC新机型有些出厂默认115200老机型常见9600最终以打印机自检页为准。获取自检页的方法是关机状态下按住PAUSE键再开机打印机会输出一页配置信息上面有接口参数和固件日期。WriteTimeout设短一点是防止打印机离线时程序一直卡在写操作上产线上每张标签都有节拍要求不能因为一台掉线的打印机拖死整个工位。发送时注意\r\n是必须的。有人习惯把指令放进SerialPort.NewLine然后用WriteLine发送但不同版本.Net对NewLine处理有差异我一般直接用Write手动拼\r\n排查起来更简单。打印机解析不了指令时会亮红灯后面第5章会专门讲这类故障。2.3 二维码指令怎么下发QRCoder负责内容打印机负责画码TSPL指令里二维码有原生命令不需要在C#端把二维码渲染成图片再发。程序只需要把二维码内容按约定格式塞进QRCODE指令。TSPL-EZ固件近几年通用的二维码指令格式是QRCODE x,y,ECC等级,单元尺寸,旋转方向,掩码,打印模式,数据实际拼接代码一般是这样的public string BuildQrCommand(string data, int x 20, int y 20) { var sb new StringBuilder(); // SIZE 的单位是 mmGAP 是标签纸间隙 sb.AppendLine(SIZE 50 mm,30 mm); sb.AppendLine(GAP 2 mm,0 mm); // CLS 清除图像缓冲区每次打印前都要发 sb.AppendLine(CLS); // ECC 用 Q 级工业场景推荐单元尺寸 4旋转 A 表示 0 度 QString qr $QRCODE {x},{y},Q,4,A,0,M2,\{data}\; sb.AppendLine(qr); // PRINT 后面的数字是打印份数 sb.AppendLine(PRINT 1); return sb.ToString(); }这段代码里最容易被忽略的是CLS。TSC打印机有一块图像缓冲区上一张标签的内容不会自动清掉如果这次打印内容和上次不同不清缓冲区就会出现“重影”——上一次的内容残留在同一张纸上。PRINT 1是打印份数批量生产时这里传入订单数量即可。二维码内容如果本身包含双引号、逗号必须做转义否则指令解析会提前结束报错。最简单的做法是内容统一走Base64或把双引号替换成全角引号。在写进指令之前先用DiagTool的指令测试窗口贴进去试一次确认这条命令在你的机型上原样能出码再进程序集成。不少TSC新固件还支持在QRCODE指令里加打印模式字段老固件没有这个字段打印时报红灯就把M2,这一段删掉再看。3. 标签坐标换算与参数设置C#里的毫米转点位逻辑3.1 点、DPI与毫米的关系为什么203dpi的标签机最容易算错TSPL指令里所有坐标都以“点”为单位而标签纸尺寸通常以毫米为单位。点与毫米的换算取决于打印机物理分辨率常见TSC机型是203dpi或300dpi。203dpi意味着每英寸203个点换算到毫米是每毫米8个点300dpi则是每毫米11.81个点。这个换算做错二维码就会偏出标签或者靠边被剥纸机构压掉。打印机分辨率每毫米点数20mm标签宽度对应点数50mm标签宽度对应点数203 dpi8160400300 dpi11.81236591开发时我一般不在代码里写死8或者11.81而是用变量记录DPI因为同一套程序经常要适配多个工厂的机型。C#里的换算函数这样写public static int MmToDot(int mm, int dpi) { return (int)Math.Round(mm * dpi / 25.4); }注意最后取整方式。四舍五入还是向上取整在标签打印里影响不大单点误差肉眼基本不可见但有一个隐藏问题如果用向上取整左右两个对称元素可能出现1点的不对称虽然看不出来强迫症会很难受。我的习惯是统一用Math.Round让误差均匀分布到两侧。3.2 SIZE、GAP、SPEED、DENSITY开工前的四个必调参数TSPL指令里打印一张标签前至少要定四个参数顺序写错直接亮红灯。第一个是SIZE 标签宽,标签高单位是mm部分固件接受SIZE 50 mm,30 mm这种带单位写法但保守起见全用不带单位的数字。第二个是GAP 间隙宽度,偏移量对应标签纸底纸间隙常见热敏不干胶间隙是2mm到3mm。如果用的是黑标纸而不是间隙纸要把GAP换成BLINE。第三个是SPEED打印速度单位是英寸每秒一般设2到6。二维码标签我建议速度不要超过4速度太快碳带和打印头接触时间变短灰阶不稳定二维码边缘容易出现拉丝。第四个是DENSITY打印浓度范围0到15数值越大颜色越深。很多人一上来就把浓度拉到最大结果二维码黑成一块饼扫码枪根本读不出来。指令作用推荐起始值SIZE标签宽和高单位mm按实际标签纸写单位不要带GAP间隙标签间隙单位mm2,0BLINE黑标标签黑标长度按实际黑标长度SPEED打印速度2到4之间DENSITY打印浓度8到12之间这几个参数一旦确定就不要再让现场操作工随便调最稳的做法是写进配置文件。3.3 内容自动居中二维码和文本按标签宽度算起点标签内容不是永远贴左上角打的实际项目里经常要求二维码、条码和文字在标签上水平居中。二维码的物理宽度等于单元尺寸乘以模块数量QRCode模块总数由版本决定版本1是21×21版本4是33×33。假设你指定单元尺寸是4点版本4的二维码就是132点宽换成203dpi标签机大约是16.5mm。居中起点计算函数public static int CenterStart(int labelWidthMm, int contentWidthDot, int dpi) { int labelWidthDot MmToDot(labelWidthMm, dpi); return (labelWidthDot - contentWidthDot) / 2; }调用时传入二维码实际点宽比如CenterStart(50, 132, 203)得到的是(400 - 132)/2等于134这就是二维码左上角的x坐标。这里除以2之后直接取整就行不需要四舍五入因为左右两边相差的最多1点肉眼不可见。如果标签上同时有二维码和文字建议先把文字行高也算成点再统一按同一个x起点排避免一行偏左一行偏右。另外提醒一下二维码不像普通文本周边必须保留静区也就是二维码图案四周至少4个模块宽的空白不能被边框线或文字压住。所以居中的时候不要把二维码算满整个标签宽度左右各留出至少2mm空白再计算。4. 让二维码一次过检纠错等级、放大倍数和打印浓度的搭配4.1 QRCODE指令参数再拆一遍ECC、单元尺寸、旋转和掩码TSPL的QRCODE指令在不同机型上字段数量不完全一样但核心参数是固定的。x和y是起点坐标ECC等级控制纠错能力单元尺寸决定每个小黑块占几个点旋转方向用A到D表示0度到270度掩码可以让二维码的图案分布更均匀打印模式在部分固件里用于精细打印。QRCODE 20,20,Q,4,A,0,M2,ABC123这段指令里Q是纠错等级4是单元尺寸A是旋转0度0是掩码M2是打印模式。其中单元尺寸非常敏感设为1二维码每个模块只有1个点203dpi下就是0.125mm打印机稍有偏差就糊掉设为10二维码又大又粗一张50mm宽的标签只能放很小内容量。实际项目里我一般固定4到6之间先打一张用扫码枪验证再决定要不要降到3去节省标签空间。掩码参数m0到m7的意义是把QR码的模块图案做某种变换使得黑色模块分布更均匀避免大块连续黑点影响扫描。TSPL指令里掩码设为0通常是打印机自动选择掩码手工指定反而可能出问题。这个参数我不建议现场去调默认0就好。4.2 纠错等级为什么选Q而不是L磨损与污损的产线场景QRCode的纠错等级从低到高是L、M、Q、H能恢复的码字比例分别约7%、15%、25%、30%。选L级会让二维码密度更低、更容易打印但产线标签要面对的是碳带蹭花、贴胶带、机油滴落这些现实场景尤其是贴在周转箱和产品外壳上的标签运输过程中磨损是很常见的事。L级二维码一旦缺了几块就是直接报废返工成本远大于那一点打印空间。我一般用Q级H级只在内容极短、标签足够大时才考虑。纠错等级升高后同样的内容会多出更多纠错码字二维码版本会自动升高模块变多这时候要同步增大单元尺寸否则打印出来依然是一团密集的网格。二维码内容、纠错等级、单元尺寸三者是联动的不要单独调其中一个。供应链和仓储环节还有一个现实约束很多扫码枪对QR码的宽容度并不高过小的码在传送带上快速扫过时摄像头曝光时间不够照样丢码。这也是我更偏向Q级而不是H级的原因——H级让模块太密扫读反而变慢。4.3 打印浓度与速度的边界糊码、断针和耗材脾气打印浓度在TSPL里用DENSITY指令控制范围0到15默认通常是8。浓度太低二维码的黑色模块发灰扫码枪对比度不够浓度太高碳带熔化过度相邻模块之间粘连二维码变成一个整体黑斑。这里有点玄学同样浓度的碳带在铜版纸上和在合成纸上的表现完全不同每次换耗材批次都得重新试。我的做法是准备一个固定测试内容包含二维码、密集条码和细线在同一标签纸上打三张浓度分别取8、10、12然后用扫码枪连续扫十次挑出误码率最低的那组。速度的影响在打印头老化后尤为明显打印头磨损初期低速高浓度还能勉强撑住一旦速度提上去二维码边缘就开始出现锯齿。所以新项目调试时先固定速度4只调浓度浓度确定后再做一次提速验证。批量打印时还有一个容易忽略的点同一张标签上如果同时有二维码和大面积黑色块打印头局部发热严重二维码区域的浓度会随着连续打印而逐渐升高打了几百张之后居然扫不出来了。这种间歇性问题最坑人解决方法是把浓度往回调两档或者在大黑块和二维码之间留出空白缓冲带。4.4 打出来先别急着贴用扫码枪做闭环验证二维码打印完之后程序层面的验证不是看标签图片多漂亮而是确认扫码枪能稳定读出内容。产线上最好固定一把USB扫码枪把扫码结果回传回程序和原始内容做比对不一致就报警。手机扫码只能做应急辅助工业扫码枪对印刷精度的敏感度比手机高得多。验证指标建议用连续扫描成功率而不是单次成功。我一般扫十次有一次失败就要调整。标签贴到深色或反光表面上时扫码枪的补光会形成反光也会干扰识别验证时要模拟真实贴标环境不要放在白纸上扫了就算过。这个习惯能帮你提前发现很多藏在碳带和纸张组合里的隐患。5. TSC打印二维码常见问题排查5个现场翻车点的原因与处理5.1 打印机亮红灯不打印先查QRCODE指令里的逗号和引号现象是程序发送指令后打印机红灯闪烁标签一点没出来。这个故障在TSPL调试期出现频率极高。原因多半是QRCODE指令参数个数不对最常见的是二维码内容里带了英文双引号或逗号把指令后半段截断了也有的是指令末尾只发了\n没发\r\n打印机不认。解决办法是先把程序拼接出来的完整指令字符串复制到记事本里人眼看一遍重点数QRCODE里逗号的数量。数据里的双引号要做转义我的习惯是把内容里的双引号统一换成单引号因为TSPL解析器对单引号不敏感。换行统一用\r\n代码里不要依赖Environment.NewLine尽量写死。5.2 二维码内容扫出来乱码TEXT打印中文成方块二维码本身扫得出来但内容变了或者TEXT指令打印中文时直接变成方块这是编码和字库两个问题叠加。TSC固件对TSPL指令解析用的是单字节字符流常见中文字符集是GBK或GB2312如果你程序里用的是UTF-8发过去就是乱码。C#端要先把内容转码成GBK再拼进指令byte[] gbkBytes Encoding.GetEncoding(GBK).GetBytes(data); string gbkString Encoding.GetEncoding(GBK).GetString(gbkBytes);TEXT指令打不出中文是因为打印机内部没有中文字库。TSPL的TEXT命令只能调用打印机内置点阵字体老机型只有ASCII字体中文就需要先DOWNLOAD一个中文字库文件然后在TEXT命令里指定该字体ID。如果手头没有字库文件最简单的方案是放弃TEXT打中文把中文字段和二维码一起用BITMAP位图方案输出码和字都是图片不依赖打印机字库。二维码内容的编码还要看目标扫码枪的解析能力。部分扫码枪默认按UTF-8解析打印机按GBK编码出来的内容它读了也会乱。最佳做法是产线联调时直接用扫码枪把打印出来的码读一遍别假设两端编码一致。5.3 二维码打出来扫码枪不识别先看尺寸再看浓度现象是手机扫一下能出内容工业扫码枪扫半天没反应或者标签贴出去第二天就扫不出来了。典型原因有三个。第一二维码模块物理尺寸太小单元尺寸设成了1或2203dpi下每个模块只有0.125到0.25mm很多扫码枪的镜头解析不了第二浓度过高黑色模块粘连QR码的定位图案变形第三标签贴在曲面或反光表面上扫码枪补光直接镜面反射回来。排查顺序要固定先用卡尺量二维码实际宽度小于12mm就直接放大单元尺寸宽度够了再看边缘是不是发毛发毛就降浓度、降速度表面问题只能靠贴标试验。工业扫码枪对印刷对比度的最低要求是PCS值不低于0.6肉眼看着黑不溜秋的码反而经常不达标。5.4 两台电脑同时给一台打印机发指令串口占用和响应丢失现象是程序偶尔报“串口被占用”或者打印机漏打了一张流水线上的标签数量对不上。串口是独占设备同一时间只能有一个进程持有它。另一台电脑接同一个串口时后打开的进程会失败。TSPL指令本身是无应答的打印机收到就执行程序不知道它到底打了没有。解决分三层。程序里用SemaphoreSlim给发送操作加锁保证同一进程内不并发写串口。打印机端打开AUTOSTATUS自动状态响应或定时发送STATUS指令查询状态这样能在打印前判断打印机是否在线、是否缺纸。产线上如果多台工位共享一台打印机不要用串口并联走网络口或加一个串口服务器让它自己排队处理程序只负责提交任务。5.5 换了一卷标签纸后坐标全部跑偏GAP校准和纸张参数现象是标签纸用完了换上一卷新纸再打印时内容要么往上偏要么往左偏打两张之后位置还会漂移。原因九成是标签间隙传感器没有识别到底纸间隙。标签纸每卷的底纸透明度、间隙宽度甚至纸张厚度都有差异传感器要重新学习。换纸后的标准动作是先发一次GAP 2,0定义间隙再按FEED键让打印机走一张纸它会把间隙脉冲记录下来。程序里如果用的间隙纸却忘了发GAP指令打印机默认按连续纸处理位置自然对不上。还有一点SIZE指令里的标签高度必须和实际标签一致差1mm就会导致内容逐渐错位这不是玄学是每张纸补偿偏差累积出来的结果。6. 进阶老固件不支持QRCODE指令时用BITMAP把二维码打出来6.1 什么时候需要走位图方案TSC老机型固件不支持原生QRCODE指令或者指令带M2字段就报错这时候只能绕道。通用做法是在C#里生成二维码位图再用TSPL的BITMAP指令逐点下发。BITMAP指令格式是BITMAP x,y,width,height,hex字节流其中width和height是像素尺寸字节流把每8个点的黑白状态打包成一个字节转成十六进制字符串发送。这个方案的优点是打印机兼容性最好缺点是指令长度大一屏50mm宽的二维码大约几百字节串口9600波特率下有一点点延迟批量打印时影响不大。6.2 QRCoder生成BitMatrix与字节打包QRCoder是C#里很常用的二维码生成库。先用它生成二维码对象再拿到位图像素打包成TSPL的BITMAP数据段using QRCoder; public string BuildQrBitmapCommand(string content, int x, int y) { var generator new QRCodeGenerator(); var qrData generator.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q); using var qrBitmap new QRCode(qrData).GetGraphic(4); int width qrBitmap.Width; int height qrBitmap.Height; int bytesPerRow (width 7) / 8; var bytes new byte[bytesPerRow * height]; for (int row 0; row height; row) { for (int col 0; col width; col) { Color pixel qrBitmap.GetPixel(col, row); bool isBlack pixel.GetBrightness() 0.5f; if (isBlack) bytes[row * bytesPerRow col / 8] | (byte)(0x80 (col % 8)); } } string hexData Convert.ToHexString(bytes); return $BITMAP {x},{y},{width},{height},{hexData}; }这段代码的核心是把每行像素按8位一组打包。0x80 (col % 8)表示从高位到低位摆放每个字节的高位对应左侧像素。GetBrightness小于0.5判定为黑色防锯齿的边缘灰度色也会被二值化成黑或白。Convert.ToHexString要求.Net 5以上如果还在.Net Framework上跑改成手动BitConverter.ToString(bytes).Replace(-, )即可。6.3 别忘了加静区用BITMAP方案时很多人直接用QRCoder的默认输出结果打出来的二维码贴到纸边上静区完全被切掉。静区是二维码识别的基本前提至少要有4个模块宽的空白环。QRCoder的GetGraphic方法可以传静区参数或者干脆把二维码画到一张更大的Bitmap上四周留白后再打包。我用的是后一种因为留白顺便解决了居中计算二维码位图的宽度已经包含了静区再做居中时直接拿这个宽度参与计算不用单独估。批量打印时把生成的指令字符串写入日志文件哪张标签出问题直接回放指令就能复现。我做这套TSC打印方案磨合下来的最大教训就是先把碳带、标签纸和浓度速度的组合试稳定再回来动程序打印物理过程决定了二维码能不能被稳定扫读程序只是把验证过的参数可靠地复现出来。希望帮到你。本文还有配套的精品资源点击获取
返回列表