c#之串口通信
作者:互联网
文章目录
- 前言
- 背景
- 一、添加串口组件
- 二、搭建界面
- 三、可能会用到的代码
- 四、编写扫描端口函数
- 五、编写打开串口函数
- 六、编写发送函数
- 七、编写接收函数
- 八、测试
- 注意
- 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=============================分割线
标签:log,c#,Text,dateString,通信,Length,str,串口 来源: https://blog.csdn.net/lengyuefeng212/article/details/117394267