C#实现简单串口通信
作者:RunnerDNA 发布时间:2023-05-14 08:15:27
标签:C#,串口通信
串口通信(Serial Communications)是指外设和计算机间通过数据信号线、地线等按位(bit)进行传输数据的一种通信方式,属于串行通信方式,能够实现远距离通信,长度可达1200米。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。
串口通信的接口标准有很多,有 RS-232C、RS-232、RS-422A、RS-485 等。常用的就是 RS-232 和 RS-485。串口通信使用的大多都是 DB9 接口,如下图。
1 载波检测(DCD) 2 接受数据(RXD) 3 发出数据(TXD) 4 数据终端准备好(DTR) 5 信号地线(SG) 6 数据准备好(DSR) 7 请求发送(RTS) 8 清除发送(CTS) 9 振铃指示(RI)
这里我们以 RS-232 接口进行演示。
1、数据包格式定为(10bytes):
帧头(0xAA,0x55),命令字(1byte),地址位(2bytes),数据位(2bytes),校验位(1byte,和校验),帧尾(0xFE,0xFE)
地址位和数据位都是高位在前。
数据封装方法:
//数据打包
private byte[] DataPackage(byte cmd, int addr, int data)
{
byte[] package = new byte[10];
package[0] = 0xAA;//帧头
package[1] = 0x55;
package[2] = cmd;//命令字
byte[] dataaddr = IntToByteArray(addr);
package[3] = dataaddr[0];//地址高字节
package[4] = dataaddr[1];//地址低字节
byte[] value = IntToByteArray(data);
package[5] = value[0];//数据高字节
package[6] = value[1];//数据低字节
package[7] = CheckSum(package);//校验位
package[8] = 0xFE;//帧尾
package[9] = 0xFE;
return package;
}
//将int转换成2位数组
private static byte[] IntToByteArray(int value)
{
int hvalue = (value >> 8) & 0xFF;
int lValue = value & 0xFF;
byte[] arr = new byte[] { (byte)hvalue, (byte)lValue };
return arr;
}
//得到和校验码
private byte CheckSum(byte[] package)
{
byte checksum = 0;
for (int i = 0; i < package.Length; i++)
{
checksum += package[i];
}
return checksum;
}
2、串口调用封装类CommHelper.cs
internal class CommHelper
{
//委托
public delegate void EventHandle(byte[] readBuffer);
public event EventHandle DataReceived;
public SerialPort serialPort;
private Thread thread;
volatile bool _keepReading;
public CommHelper()
{
serialPort = new SerialPort();
thread = null;
_keepReading = false;
serialPort.ReadTimeout = -1;
serialPort.WriteTimeout = 1000;
}
//获取串口打开状态
public bool IsOpen
{
get
{
return serialPort.IsOpen;
}
}
//开始读取
private void StartReading()
{
if (!_keepReading)
{
_keepReading = true;
thread = new Thread(new ThreadStart(ReadPort));
thread.Start();
}
}
//停止读取
private void StopReading()
{
if (_keepReading)
{
_keepReading = false;
thread.Join();
thread = null;
}
}
//读数据
private void ReadPort()
{
while (_keepReading)
{
if (serialPort.IsOpen)
{
int count = serialPort.BytesToRead;
if (count > 0)
{
byte[] readBuffer = new byte[count];
try
{
Application.DoEvents();
serialPort.Read(readBuffer, 0, count);
DataReceived?.Invoke(readBuffer);
Thread.Sleep(100);
}
catch (TimeoutException)
{
}
}
}
}
}
//写数据
public void WritePort(byte[] send, int offSet, int count)
{
if (IsOpen)
{
serialPort.Write(send, offSet, count);
}
}
//打开
public void Open()
{
Close();
try
{
serialPort.Open();
serialPort.RtsEnable = true;
if (serialPort.IsOpen)
{
StartReading();
}
else
{
MessageBox.Show("串口打开失败!");
}
}
catch
{
}
}
//关闭
public void Close()
{
StopReading();
serialPort.Close();
}
}
3、调用(模拟测试读写硬件版本号)
//地址存储信息定义
private static int HARD_VERSION = 0; //硬件版本号
//命令定义
private static byte RD_HV = 0; //读硬件版本
private static byte WR_HV = 1; //写硬件版本
private CommHelper comm = new CommHelper();
private bool GetCorrect = false; //用来标识是否已经接收到返回值
private List<byte> buffer = new List<byte>(1024);//默认分配1M内存,限制不能超过
private byte[] binary_data = new byte[10];//指定格式的某个完整数据
//串口初始化
private void InitComm()
{
comm.serialPort.PortName = "COM1";
comm.serialPort.BaudRate = 19200;
try
{
comm.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
if (comm.IsOpen)
{
comm.DataReceived += new CommHelper.EventHandle(comm_DataReceived);
}
}
//数据接收后处理
private void comm_DataReceived(byte[] readBuffer)
{
try
{
buffer.AddRange(readBuffer);//将数据放到缓存中
//完整性判断
while (buffer.Count >= 10)
{
//查找数据头
if (buffer[0] == 0xAA && buffer[1] == 0x55)
{
//和校验
byte checksum = 0;
for (int i = 0; i < 7; i++)
{
checksum += buffer[i];
}
if (checksum != buffer[7])
{
buffer.RemoveRange(0, 10);//如果校验失败,从缓存中删除这一包数据
continue;
}
buffer.CopyTo(0, binary_data, 0, 10);//复制一条完整的数据到具体的数据缓存
//读取硬件版本号
if (binary_data[2] == RD_HV && ByteArrayToInt(binary_data[3], binary_data[4]) == HARD_VERSION)//命令字为读RD_HV,地址HARD_VERSION
{
GetCorrect = true;
string data = ByteArrayToString(binary_data[5], binary_data[6]);
//这里可以处理数据更新界面展示
}
buffer.RemoveRange(0, 10);
}
else
{
GetCorrect = false;
buffer.RemoveAt(0);//如果数据开始不是头,则删除数据
}
}
}
catch
{
}
}
//将2位数组转换成字符串
private static string ByteArrayToString(byte arr1, byte arr2)
{
int value = ByteArrayToInt(arr1, arr2);
string str = Convert.ToString(value, 10);
return str;
}
//将2位数组转换成10进制数
private static int ByteArrayToInt(byte arr1, byte arr2)
{
byte[] arr = new byte[] { arr1, arr2 };
int value = (int)((arr[0] & 0xFF) << 8)
| (arr[1] & 0xFF);
return value;
}
//读取硬件版本号
private void ReadHV()
{
GetCorrect = false;
try
{
comm.WritePort(DataPackage(RD_HV, HARD_VERSION, 0), 0, 10);//发送RD_E2P命令,地址HARD_VERSION
Delay(100);
if (GetCorrect == true)
{
MessageBox.Show("硬件版本号读取成功!");
}
else
{
MessageBox.Show("硬件版本号读取失败!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
//写入硬件版本号
private void WriteHV()
{
if (tbVersion.Text.Length == 4 && isLegal(tbVersion.Text))//限定只能写入4个字符且输入合法
{
try
{
int buf = Convert.ToInt32(tbVersion.Text);
GetCorrect = false;
comm.WritePort(DataPackage(WR_HV, HARD_VERSION, buf), 0, 10);//发送WR_HV命令,地址HARD_VERSION
Delay(100);
if (GetCorrect == true)
{
MessageBox.Show("硬件版本号写入成功!");
}
else
{
MessageBox.Show("硬件版本号写入失败!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else
{
MessageBox.Show("硬件版本号数据格式错误!请重新写入!");
}
}
//判断输入是否合法,必须是数字
private const string PATTERN = @"^[0-9]*$";
private bool isLegal(string hex)
{
return System.Text.RegularExpressions.Regex.IsMatch(hex, PATTERN);
}
//延时函数
[DllImport("kernel32.dll")]
static extern uint GetTickCount();
static void Delay(uint ms)
{
uint start = GetTickCount();
while (GetTickCount() - start < ms)
{
Application.DoEvents();
}
}
来源:https://blog.csdn.net/dnazhd/article/details/88327160


猜你喜欢
- 一、简单介绍翻看Spring的源码时,发现@Bean注解的源码上标注了Since: 3.0,也就是说,@Bean注解是Spring从3.0版
- 本文为大家分享了类似微信朋友圈,点击+号图片,可以加图片功能,供大家参考,具体内容如下xml:<?xml version="
- 一.什么是maven?Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一
- 目录IntroSampleWhat insideMoreReferenceIntroC# 9 中引入了 record,record 是一个特
- 绘制圆形锯齿问题普通绘制圆形形状时可以看到图形边缘会有明显锯齿现象并不像真实圆形形状一样圆润边缘平滑。在glsl中这种情况是常见情况,这里是
- 首先struts上传最大大小由两个地方决定. · struts.multipart.maxSize决定整个post的form最大是
- 卫星菜单 ArcMenu 相信大家接触安卓,从新手到入门的过渡,就应该会了解到卫星菜单、抽屉、Xutils、Coolmenu、一些大神封装好
- Java for循环打印菱形Java代码输出菱形的方法和思路有很多,在此分享一个稍带模块化拆分思想的解决方案,将需要输出的菱形拆分成8个模块
- 配置文件-yaml在spring Boot开发中推荐使用yaml来作为配置文件。基本语法:key: value;kv之间有空格大小写敏感使用
- 1.使用的是maven项目,添加依赖<!-- mybatis-plus begin --> <depend
- 介绍本文实现的功能有:1、播放音乐2、自定义流星数量、飞行速度、光晕大小、流星大小3、自定义表白话语 运用到的知识点有:GUI:j
- 简介基于springboot,mybatis plus集成了一套多数据源的解决方案,在使用时引入相应的插件dynamic-datasourc
- cookie机制和session机制的区别具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持
- C# 匿名函数、lambda表达式、Linq查询一、匿名函数的使用匿名函数是一个“内联”语句或表达式
- Java 获取文件大小今天写代码时需要实现获取文件大小的功能,目前有两种实现方法,一种是使用File的length()方法;另外
- 启用@DS实现数据源切换POM内添加核心jar包 <depe
- 步骤,如图所示:1.添加异步任务业务类package top.ytheng.demo.task;import java.util.concu
- Java9 集合类扩展of方法package com.jd.collections;import org.junit.Test;import
- 前言本篇文章 中写到的是 flutter 调用了Android 原生的 TextView 案例添加原生组件的流程基本上可以描述为:1 and
- 最近客户更新系统发现,以前的项目在调用相机的时候,闪退掉了,很奇怪,后来查阅后发现,Android 6.0以后需要程序授权相机权限,默认会给