c#之串口通信
2021/5/30 12:23:00
本文主要是介绍c#之串口通信,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- 前言
- 背景
- 一、添加串口组件
- 二、搭建界面
- 三、可能会用到的代码
- 1.Form1
- 2.窗体初始化
- 3.调试追踪
- 4.字符串转化为十六进制字节数据
- 5.发送框文本改变事件
- 四、编写扫描端口函数
- 1.编写按键点击事件
- 2.编写扫描函数
- 五、编写打开串口函数
- 六、编写发送函数
- 七、编写接收函数
- 1.先注册一下事件
- 2.编写数据接收函数
- 八、测试
- 1.打开串口
- 2.接收数据
- 1.十六进制
- 2.字符串
- 3.发送数据
- 1.十六进制
- 2.字符串
- 注意
- END=============================分割线
前言
之前因为和学长一起参加一个活动,所以自学了一下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=============================分割线
这篇关于c#之串口通信的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-03-01沐雪多租宝商城源码从.NetCore3.1升级到.Net6的步骤
- 2024-05-08首个适配Visual Studio平台的国产智能编程助手CodeGeeX正式上线!C#程序员必备效率神器!
- 2024-03-30C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
- 2024-03-29c# datetime tryparse
- 2024-02-21list find index c#
- 2024-01-24convert toint32 c#
- 2024-01-24Advanced .Net Debugging 1:你必须知道的调试工具
- 2024-01-24.NET集成IdGenerator生成分布式全局唯一ID
- 2024-01-23用CI/CD工具Vela部署Elasticsearch + C# 如何使用
- 2024-01-23.NET开源的简单、快速、强大的前后端分离后台权限管理系统