POS打印机系列之 => 串口

近来工作有关多种方式Pos小票打印。此谨备用

串口是什么?

串行通信接口,COM接口:指数据一位一位地顺序传送。现有主要是9针串口。

怎样与串口通信

以C#代码演示,首先定义与串口通信的行为

/// <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);
    }
Interface

实现串口通信,用CreateFile的方式。(还有Stream的方式未使用)

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, "请先打开串口连接");
        }
    }
Implement Serial Port

实现串口打印

在实现串口通信后,编程通过串口打印只需要发送指令就OK了

    /// <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]));
        }
    }
Print with Serial Port

本人原创,欢迎转载,请声明原载

posted @ 2013-07-17 11:10  zzq417  阅读(901)  评论(0编辑  收藏  举报