c#之串口通信

前言

之前因为和学长一起参加一个活动,所以自学了一下SQL Server数据库和c#编程。

之前做的是一个CPU卡的项目,我主要负责的是上位机这部分,经过短暂的学习,算是基本完成了这个项目。

那个项目就是用串口和下位机进行通信。

背景

那么今天我们要做的是什么呢?因为我们实验室有个热敏打印机,如果想要支持图片打印,那么肯定需要一个上位机去给这个下位机发送图片数据,然后去打印。最好的方法肯定是编写一个APP,通过手机加载图片,然后发送给下位机去打印,奈何自己能力不够,不会写APP,无奈只好选择用PC端开发一个上位机然后去选择图片。

既然用PC编写上位机,那么和下位机通信也有两种方式,有线和无线。无线的肯定要复杂一点,后续可以再整。怎么简单怎么来,那就先写一个用串口和下位机通信的代码吧!

一、添加串口组件

因为c#中有自带的串口组件,这就给我们编程提供了很多的便利。
在这里插入图片描述
像拖拽其他控件一样拖到窗体中。
会在下面显示这个控件。
在这里插入图片描述

二、搭建界面

我这里做的是一个串口助手,根据自己的实际应用搭建界面。
在这里插入图片描述

三、可能会用到的代码

1.Form1

   public Form1()
        {
            InitializeComponent();
            serialPort1.Encoding = Encoding.GetEncoding("GB2312");  // 设置串口的编码
            Control.CheckForIllegalCrossThreadCalls = false;  // 忽略多线程
        }

2.窗体初始化

      /* 窗体加载初始化 */
        private void Form1_Load(object sender, EventArgs e)
        {
            /* 可以在初始化的过程中添加波特率 */
            SearchCompleteSerial(serialPort1, cbb_Serial_Port);  // 自动扫描找到的端口号 
        }

3.调试追踪

/* 调试追踪
         
         */
        public void DebugTrack(string str, Color color)
        {
            if (this.log.InvokeRequired)
            {
                Action<string, Color> method = this.DebugTrack;
                this.log.Invoke(method, str, color);
            }
            else
            {
                /* 字符串的长度超过1000 自动清空接收显示界面 */
                if (this.log.TextLength > 1000)
                {
                    this.log.Clear(); // 清空
                }

                this.log.SelectionStart = this.log.Text.Length;
                this.log.SelectionColor = color;  // 选择打印的颜色
                
                /* 在接收数据前面显示时间 可选可不选 */
                if (chb_Show_Time.Checked) // 如果显示时间被选中 
                {
                    /* 在打印出来的信息的前面追加时间字符串 */
                    this.log.AppendText(DateTime.Now.ToString("【yyyy-MM-dd HH:mm:ss】  ") + str);
                }
                else
                {
                    this.log.AppendText(str); // 不添加时间 直接在后面追加字符串
                    // this.log.AppendText("aaa"+"\r\n"); // 不添加时间 直接在后面追加字符串 
                }

                this.log.ScrollToCaret();  // 将控件内容滚动到当前插入符号位置 不加,光标在最初的位置,加上后,光标滚动到插入位置的最后面
            }
        }/* 调试追踪 */

        /* 打印字符串 */
        public void log_printf(string str)
        {
            DebugTrack(str, Color.Black);  // 打印出这个字符串 黑色显示
        }

4.字符串转化为十六进制字节数据

    /* 字符串转化为十六进制的字节数据 
   * str:要转换的字符串
   */
        private byte[] strToHexBytes(string str)
        {
            str = str.Replace(" ", ""); // 把空格去掉
            if (str.Length % 2 != 0)
            {
                str = str.Insert(str.Length - 1, "0");
            }

            byte[] array = new byte[str.Length / 2];
            for (int i = 0; i < str.Length / 2; i++)
            {
                array[i] = Convert.ToByte(str.Substring(2 * i, 2), 16);
            }
            return array;
        }

5.发送框文本改变事件

当我们发送十六进制的时候,我们需要确保输入框中输入的字符必须是0-9,A-F a-f,空格或换行这些字符。
可以使用下面这个函数来限定。

/* 发送框文本改变事件  当文本发生改变产生事件 */
        private void rich_send_TextChanged(object sender, EventArgs e)
        {
            /* 判断当前是不是十六进制发送 如果是 需要判断 */
            if (chb_Hex_Tx.Checked)
            {
                char[] clist = rich_send.Text.ToCharArray(); 
                String newString = "";
                for(int i = 0; i< clist.Length; i++)
                {
                    int ascii = (int)clist[i]; // 把每一个字符都转化为ascii码

                    /* 如果这个值在A-F之间 0-9之间 可以用 空格是32
                     * 回车 13  换行 10
                     */
                    if ((ascii >= 48 && ascii < 59) || (ascii >= 97 && ascii < 103) || (ascii >= 65 && ascii < 71) || ascii == 32
                        || ascii == 10 || ascii == 13)
                    {  
                     
                    }else
                    {
                     clist[i] = '\0';
                    }
                    newString += clist[i].ToString();
                }
                rich_send.Text = newString; // 显示界面上 
            }

        } 

四、编写扫描端口函数

1.编写按键点击事件

  /* 扫描按键 点击监听事件 */
        private void btn_Scan_Click(object sender, EventArgs e)
        {
            /* 说明:serialPort1是添加一个组件自动生成的一个对象 */
            SearchCompleteSerial(serialPort1, cbb_Serial_Port);  // 自动扫描找到的端口号
        }

2.编写扫描函数

    /* 串口自动扫描端口号  */
        private void SearchCompleteSerial(SerialPort myport, ComboBox mybox)
        { 
          /* 先清空端口下拉列表 */
            mybox.Items.Clear();

            String[] mystring = SerialPort.GetPortNames(); // 获取计算机的端口名的数组
            for (int i = 0; i < mystring.Length; i++)
                mybox.Items.Add(mystring[i]); // 往下拉列表中添加端口号

            mybox.Text = mystring[0]; // 默认选中的是第一个端口(小的端口)
        }

五、编写打开串口函数

通过这个函数可以打开串口,关闭串口。

    /* 打开串口按键  点击监听事件 */
        private void btn_OpenSerial_Click(object sender, EventArgs e)
        {
            /* 先判断当前串口是打开状态还是关闭状态 */
            if (btn_OpenSerial.Text == "打开串口") // 串口还没有打开 需要打开串口 
            {
                try
                {

                    serialPort1.PortName = cbb_Serial_Port.Text; // 端口号
                    serialPort1.BaudRate = 115200; // 写死  串口通信的波特率
                    serialPort1.Open(); // 打开串口
                    btn_OpenSerial.Text = "关闭串口";
                    log_printf("打开串口");
                    lab_Device_Connect.Text = "设备已连接";

                }
                catch
                {
                    MessageBox.Show("端口打开错误,请检查串口", "错误");
                }
            }
            else  // 串口已经打开 需要关闭 
            {
                try
                {
                    serialPort1.Close(); // 关闭串口 
                    btn_OpenSerial.Text = "打开串口";
                    log_printf("关闭串口");
                    lab_Device_Connect.Text = "设备已断开";
                }
                catch
                {
                    MessageBox.Show("关闭串口错误");
                }
            }

        }/* 打开串口 按键监听*/

六、编写发送函数

    /* 发送按键 监听事件 */
        private void btn_SendData_Click(object sender, EventArgs e)
        {
            byte[] data = new byte[1]; // 定义1个字节
            if (serialPort1.IsOpen)  // 如果串口是打开的
            {

                if (rich_send.Text != "")  // 如果发送了数据
                {

                    String dateString  = rich_send.Text; // 获取要发送的字符串 
                    /* 判断是否发送新行 */
                    if(chb_Tx_newLine.Checked) // 发送新行 
                    {
                       dateString += "\r\n";
                    }


                    if (chb_Hex_Tx.Checked)  // 如果发送是十六进制模式 
                    {

                        /* 先对要发送的数据去除掉 空格 */
                        dateString = dateString.Replace(" ", ""); // 把空格去掉
                        ///* 把换行符也去掉 */
                        //dateString = dateString.Replace("\r\n","");
                        //dateString = dateString.Replace("\r", "");
                        //dateString = dateString.Replace("\n", "");
                        // 每两位数据是1个字节  还考虑了发送奇数个字符的情况

                        for (int i = 0; i < (dateString.Length - dateString.Length % 2) / 2; i++)
                        {
                            // 把发送文本框的数值两个两个的发送,并且转化为16进制表示
                            data[0] = Convert.ToByte(dateString.Substring(i * 2, 2), 16);
                            serialPort1.Write(data, 0, 1); // 从0开始写入1个字节
                        }
                        if (dateString.Length % 2 != 0)  // 剩下1位数据单独处理
                        {
                            data[0] = Convert.ToByte(dateString.Substring(dateString.Length - 1, 1), 16); // 单独发送1位
                            serialPort1.Write(data, 0, 1); // 写入到串口
                        }

                        txTotalCount += dateString.Length / 2; // 发送数据总长度 程序全局变量  

                        txt_Tx_Count.Text = Convert.ToString(dateString.Length / 2);  // 把发送缓冲区的数据的长度用发送长度文本框显示

                        txt_Tx_TotalCount.Text = Convert.ToString(txTotalCount); // 接收数据的总长度 界面显示  
                         
                    }
                    else  // 字符串
                    {
                        try
                        {
                            serialPort1.WriteLine(dateString); // 以字符模式写数据,把要发送的数据写到串口

                            txTotalCount += rich_send.Text.Length; // 发送数据总长度 程序全局变量  

                            txt_Tx_Count.Text = Convert.ToString(rich_send.Text.Length);  // 把发送缓冲区的数据的长度用发送长度文本框显示

                            txt_Tx_TotalCount.Text = Convert.ToString(txTotalCount); // 接收数据的总长度 界面显示  
                        }
                        catch
                        {
                            MessageBox.Show("串口数据写入错误");
                        }
                    }

                }
                else
                {
                    MessageBox.Show("请输入要发送的数据");
                }
            }
            else   // 串口没有打开 提示不能发送数据 
            {
                MessageBox.Show("请先打开串口");
            }
        }/* 监听按键事件 */

七、编写接收函数

1.先注册一下事件

点击一下串口组件
在这里插入图片描述
点击右边的事件,选择数据接收事件
在这里插入图片描述

2.编写数据接收函数

  /* 串口数据接收事件 
         数据接收事件  这个是创建一个串口数据接收事件  要在Designer.cs中注册一下 不然没法调用 
         */
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (chb_Hex_Rx.Checked)  // 如果接收模式为十六进制 
            {
                byte data;
                data = (byte)serialPort1.ReadByte();  // 读取一个数值
                string str = Convert.ToString(data, 16).ToUpper(); // 转化为大写的16进制字符串
                log_printf(str);
                // log_printf("0x" + (str.Length == 1 ? "0" + str : str) + " ");// 如果为1个字节,前面补0, 每个数值后面加空格

            }
            else  // 如果接收为字符串
            {

                string str = serialPort1.ReadExisting(); //以字符串方式读
                log_printf(str); // 往文本框中添加读出的文本数据
                // TebReceiveNum.AppendText(Convert.ToString(str.Length)); 
                /* 显示接收的数据长度 */

                txTotalCount += str.Length;// 接收数据的总长度 
                txt_Rx_Count.Text = Convert.ToString(str.Length);// 把接收到的数据的长度赋值给这个文本框显示
                txt_Rx_TotalCount.Text = Convert.ToString(txTotalCount); // 接收到的数据的总长度 界面显示 

            }
        }

八、测试

1.打开串口

我是使用两个串口助手进行通信,中间使用的是虚拟串口,大家使用的话可以去下载虚拟串口。
在这里插入图片描述
在这里插入图片描述

2.接收数据

1.十六进制

效果不好啊。

2.字符串

在这里插入图片描述

3.发送数据

1.十六进制

在这里插入图片描述

在这里插入图片描述

2.字符串

在这里插入图片描述
在这里插入图片描述

到此,测试基本完成。除了接收十六进制效果不好之外,其他的都可以正常使用。

注意

用这个编写的串口发送数据效率很低,如果下位机用帧中断来处理数据的话,会接收不到数据。建议用定时器来判断一帧数据是否接收完成。

END=============================分割线

  • 19
    点赞
  • 186
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值