POS打印机系列之 => 串口
近来工作有关多种方式Pos小票打印。此谨备用
串口是什么?
串行通信接口,COM接口:指数据一位一位地顺序传送。现有主要是9针串口。
怎样与串口通信
以C#代码演示,首先定义与串口通信的行为
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 接口通信,包括:串口、并口、USB、网口 /// </summary> public interface IPortContact { void Close(); int Write(string data); int Write(byte[] data); bool Opened { get; set; } } /// <summary> /// 串口通信 /// </summary> public interface ISerialContact : IPortContact { ActionResult Open(string port, int BaudRate, char cParity, byte ByteSize, byte StopBits); }
实现串口通信,用CreateFile的方式。(还有Stream的方式未使用)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class SerialContact : ISerialContact { private int hComm = -1; public bool Opened { get; set; } //win32 api constants private const uint GENERIC_READ = 0x80000000; private const uint GENERIC_WRITE = 0x40000000; private const int OPEN_EXISTING = 3; private const int INVALID_HANDLE_VALUE = -1; private const byte NOPARITY = 0; private const byte ODDPARITY = 1; private const byte EVENPARITY = 2; private const byte MARKPARITY = 3; private const byte SPACEPARITY = 4; private const byte ONESTOPBIT = 0; private const byte ONE5STOPBITS = 1; private const byte TWOSTOPBITS = 2; [StructLayout(LayoutKind.Sequential)] public struct DCB { //taken from c struct in platform sdk public int DCBlength; // sizeof(DCB) public int BaudRate; // current baud rate public uint flags; public ushort wReserved; // not currently used public ushort XonLim; // transmit XON threshold public ushort XoffLim; // transmit XOFF threshold public byte ByteSize; // number of bits/byte, 4-8 public byte Parity; // 0-4=no,odd,even,mark,space public byte StopBits; // 0,1,2 = 1, 1.5, 2 public char XonChar; // Tx and Rx XON character public char XoffChar; // Tx and Rx XOFF character public char ErrorChar; // error replacement character public char EofChar; // end of input character public char EvtChar; // received event character public ushort wReserved1; // reserved; do not use } [StructLayout(LayoutKind.Sequential)] private struct COMMTIMEOUTS { public int ReadIntervalTimeout; public int ReadTotalTimeoutMultiplier; public int ReadTotalTimeoutConstant; public int WriteTotalTimeoutMultiplier; public int WriteTotalTimeoutConstant; } [StructLayout(LayoutKind.Sequential)] private struct OVERLAPPED { public int Internal; public int InternalHigh; public int Offset; public int OffsetHigh; public int hEvent; } [DllImport("kernel32.dll")] private static extern int CreateFile( string lpFileName, // file name uint dwDesiredAccess, // access mode int dwShareMode, // share mode int lpSecurityAttributes, // SD int dwCreationDisposition, // how to create int dwFlagsAndAttributes, // file attributes int hTemplateFile // handle to template file ); [DllImport("kernel32.dll")] private static extern bool SetCommTimeouts( int hFile, // handle to comm device ref COMMTIMEOUTS lpCommTimeouts // time-out values ); [DllImport("kernel32.dll")] private static extern bool ReadFile( int hFile, // handle to file byte[] lpBuffer, // data buffer int nNumberOfBytesToRead, // number of bytes to read ref int lpNumberOfBytesRead, // number of bytes read ref OVERLAPPED lpOverlapped // overlapped buffer ); [DllImport("kernel32.dll")] private static extern bool WriteFile( int hFile, // handle to file byte[] lpBuffer, // data buffer int nNumberOfBytesToWrite, // number of bytes to write ref int lpNumberOfBytesWritten, // number of bytes written ref OVERLAPPED lpOverlapped // overlapped buffer ); [DllImport("kernel32.dll")] private static extern bool CloseHandle( int hObject // handle to object ); [DllImport("kernel32.dll")] private static extern bool SetCommTimeouts( int hFile, // handle to comm device ref COMMTIMEOUTS lpCommTimeouts // time-out values ); [DllImport("kernel32.dll")] private static extern uint GetLastError(); /// <summary> /// Opens the specified port num.打开指定的串口 /// </summary> /// <param name="port">The port num.端口</param> /// <param name="baudRate">The baud rate.波特率</param> /// <param name="cParity">The c parity.奇偶校验</param> /// <param name="byteSize">Size of the byte.</param> /// <param name="stopBits">The stop bits.停止位</param> /// <returns></returns> public ActionResult Open(string port, int baudRate, char cParity, byte byteSize, byte stopBits) { DCB dcbCommPort = new DCB(); // OPEN THE COMM PORT. hComm = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); // IF THE PORT CANNOT BE OPENED, BAIL OUT. if (hComm == INVALID_HANDLE_VALUE) { return new ActionResult(false, "打开端口[" + port + "]失败!"); } // SET BAUD RATE, PARITY, WORD SIZE, AND STOP BITS. if (!GetCommState(hComm, ref dcbCommPort)) { uint ErrorNum = GetLastError(); Close(); return new ActionResult(false, "打开端口[" + port + "]时执行GetCommState()失败!\r\n" + "错误号: " + ErrorNum.ToString(), ErrorNum.ToString()); } dcbCommPort.BaudRate = baudRate; dcbCommPort.ByteSize = byteSize; switch (cParity) { case 'N': case 'n': dcbCommPort.Parity = NOPARITY; // 不校验 break; case 'O': case 'o': dcbCommPort.Parity = ODDPARITY; // 奇校验 break; case 'E': case 'e': dcbCommPort.Parity = EVENPARITY; // 偶校验 break; case 'M': case 'm': dcbCommPort.Parity = MARKPARITY; // MARK方式 break; case 'S': case 's': dcbCommPort.Parity = SPACEPARITY; // SPACE方式 break; } // 设置停止位方式 switch (stopBits) { case 1: dcbCommPort.StopBits = ONESTOPBIT; // 停止位为1位 break; case 2: dcbCommPort.StopBits = TWOSTOPBITS; // 停止位为2位 break; } if (!SetCommState(hComm, ref dcbCommPort)) { uint ErrorNum = GetLastError(); Close(); return new ActionResult(false, "打开端口[" + port + "]执行SetCommState()失败!\r\n" + "错误号: " + ErrorNum.ToString(), ErrorNum.ToString()); } Opened = true; return new ActionResult(true); } /// <summary> /// Closes this instance.关闭端口句柄 /// </summary> public void Close() { if (hComm != INVALID_HANDLE_VALUE) { CloseHandle(hComm); Opened = false; } } /// <summary> /// Writes the specified data.写入文本数据给打印机输出 /// </summary> /// <param name="data">The data.</param> /// <returns></returns> public int Write(string data) { return this.Write(Encoding.Default.GetBytes(data)); } /// <summary> /// Writes the specified data.写入byte数据给打印机输出 /// </summary> /// <param name="data">The data.</param> /// <returns></returns> public int Write(byte[] data) { int BytesWritten = 0; if (hComm != INVALID_HANDLE_VALUE) { OVERLAPPED ovlCommPort = new OVERLAPPED(); WriteFile(hComm, data, data.Length, ref BytesWritten, ref ovlCommPort); } return BytesWritten; } /// <summary> /// Reads the specified num bytes.读取打印机返回的信息 /// </summary> /// <param name="NumBytes">The num bytes.要读取的数据量</param> /// <returns>null: 没打开连接或者读取数据异常</returns> public byte[] Read(int length) { byte[] OutBytes = null; byte[] BufBytes = new byte[length]; if (hComm != INVALID_HANDLE_VALUE) { OVERLAPPED ovlCommPort = new OVERLAPPED(); int BytesRead = 0; try { ReadFile(hComm, BufBytes, length, ref BytesRead, ref ovlCommPort); } catch(Exception ex) { return OutBytes; } OutBytes = new byte[BytesRead]; Array.Copy(BufBytes, OutBytes, BytesRead); } return OutBytes; } /// <summary> /// Sets the time out. /// </summary> /// <param name="read">The read.读取串口返回信息的超时时间,毫秒</param> /// <param name="write">The write.信息写入串口的超时时间,毫秒</param> /// <returns></returns> public ActionResult SetTimeOut(int read, int write) { COMMTIMEOUTS timeouts = new COMMTIMEOUTS(); timeouts.ReadIntervalTimeout = read; timeouts.ReadTotalTimeoutConstant = read; timeouts.ReadTotalTimeoutMultiplier = read; timeouts.WriteTotalTimeoutConstant = write; timeouts.WriteTotalTimeoutMultiplier = write; if (hComm != INVALID_HANDLE_VALUE) { if (!SetCommTimeouts(hComm, ref timeouts)) return new ActionResult(false, "设置超时失败"); return new ActionResult(true); } return new ActionResult(false, "请先打开串口连接"); } }
实现串口打印
在实现串口通信后,编程通过串口打印只需要发送指令就OK了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 接口打印,包括:串口、并口、网口 /// </summary> public interface IPortPrint:IPrintUtil { /// <summary> /// 接口的通信 /// </summary> IPortContact Contact { get; set; } /// <summary> /// Sets the font.设置打印字体的大小模式和颜色 /// </summary> /// <param name="colorCmd">The color CMD.</param> /// <param name="sizeCmd">The size CMD.正常模式:null; sizeCmd[0]开启字体模式; sizeCmd[1]关闭字体模式</param> void SetFont(string colorCmd, string[] sizeCmd); /// <summary> /// Prints the photo.打印图片,只支持热敏打印机 /// </summary> /// <param name="imgPath">The img path.</param> void PrintPhoto(string paddingText, System.Drawing.Image img, string[] lineSpaceCmd); } /// <summary> /// 接口方式打印的公共实现。字体设置、图片打印 /// </summary> public class PortPrint : IPortPrint { private string _colorCmd = string.Empty; private string[] _sizeCmd = null; /// <summary> /// 接口的通信 /// </summary> public IPortContact Contact { get; set; } public PortPrint(IPortContact contact) { Contact = contact; } /// <summary> /// Sets the font.设置打印字体的大小模式和颜色 /// </summary> /// <param name="colorCmd">The color CMD.</param> /// <param name="sizeCmd">The size CMD.正常模式:null; sizeCmd[0]开启字体模式; sizeCmd[1]关闭字体模式</param> public void SetFont(string colorCmd, string[] sizeCmd) { if (!_colorCmd.Equals(colorCmd)) { Contact.Write(ConvertToByte(colorCmd)); _colorCmd = colorCmd; } //打印机之前的字体模式不一样。倍高、倍宽、倍高与倍宽 if (_sizeCmd != null && sizeCmd != null && !_sizeCmd[0].Equals(sizeCmd[0])) { //关闭之前的打印机字体模式 Contact.Write(ConvertToByte(_sizeCmd[1])); //开启新的打印机字体模式 Contact.Write(ConvertToByte(sizeCmd[0])); } else if (_sizeCmd == null && sizeCmd != null) Contact.Write(ConvertToByte(sizeCmd[0])); else if (sizeCmd == null && _sizeCmd != null) Contact.Write(ConvertToByte(_sizeCmd[1])); _sizeCmd = sizeCmd; } private byte[] ConvertToByte(string cmd) { return Array.ConvertAll<string, byte>(cmd.Split(','), delegate(string s) { return Convert.ToByte(s); }); } /// <summary> /// Prints the photo.打印图片 /// </summary> /// <param name="imgPath">The img path.</param> public void PrintPhoto(string paddingText, System.Drawing.Image img, string[] lineSpaceCmd) { //设行间距为0 Contact.Write(ConvertToByte(lineSpaceCmd[1])); //首先将图片转换为黑白图片,再进行打印,不支持彩色图片打印 ColorMatrix cm = new ColorMatrix(); System.Drawing.Bitmap bmp = cm.ConvertBlackAndWhite(new System.Drawing.Bitmap(img), 180); byte[] data = new byte[] { 0x00, 0x00, 0x00 }; System.Drawing.Color pixelColor; // ESC * m nL nH 点阵图 byte[] escBmp = new byte[] { 0x1B, 0x2A, 0x00, 0x00, 0x00 }; escBmp[2] = (byte)'\x21'; //nL, nH escBmp[3] = (byte)(bmp.Width % 256); escBmp[4] = (byte)(bmp.Width / 256); // data for (int i = 0; i < (bmp.Height / 24) + 1; i++) { Contact.Write(paddingText); Contact.Write(escBmp); for (int j = 0; j < bmp.Width; j++) { for (int k = 0; k < 24; k++) { if (((i * 24) + k) < bmp.Height) // if within the BMP size { pixelColor = bmp.GetPixel(j, (i * 24) + k); if (pixelColor.R == 0) { data[k / 8] += (byte)(128 >> (k % 8)); } } } Contact.Write(data); data[0] = (byte)'\x00'; data[1] = (byte)'\x00'; data[2] = (byte)'\x00'; // Clear to Zero. } if (i < (bmp.Height / 24)) Contact.Write("\n"); } //回复默认行间距 Contact.Write(ConvertToByte(lineSpaceCmd[0])); } }
本人原创,欢迎转载,请声明原载
现代足球中场兼进球,兼防守扫荡。软件开发也要求产品意识,质量测试。