-
C#串口通讯
2018-06-14 17:48:46用C#做的上位机 用于串口通讯,方便的与下位机进行通讯 -
C# 串口通讯
2011-10-10 20:46:02本人做的一个C#串口上位机程序,最近有空就写了点感悟,见笑大方了。 一,软件概述 本上位机采用VisualC#2010编写,用于与单片机通信,发送并接收固定格式的数据包。 上位机每次点击“发送”按钮后将发送18字节...本人做的一个C#串口上位机程序,最近有空就写了点感悟,见笑大方了。
一,软件概述
本上位机采用Visual C# 2010编写,用于与单片机通信,发送并接收固定格式的数据包。
上位机每次点击“发送”按钮后将发送18字节给下位机,发送包的格式为:
包头:0xAA;命令号:0x01;帧长:0x0D;帧数据13字节,由界面上的选项决定(如发射频率6MHz,代表0x000600三字节),和校验1字节,包尾:0xa5;共18(3+13+2)字节。此18字节将在发送区中显示。具体的包格式就不说了,都是自己定义的。
下位机接收到数据后,将发送5字节的数据,上位机接收到数据后可以判定数据是否正确,校验是否正确等。下位机发送的数据由上位机接收后将在接收区中显示
二,具体实现核心代码
1)。运行软件后,将自动显示本机的串口(如果有的话),并打开。FormLoad代码为:
//列出本机所有串口
string[] ports = SerialPort.GetPortNames();
Array.Sort(ports);
comboBoxPortSelect.Items.AddRange(ports);
//默认选中第一个
comboBoxPortSelect.SelectedIndex = 0;
//打开本计算机的串口
if (mycomm.IsOpen) {
mycomm.Close();
}
mycomm.PortName = comboBoxPortSelect.Text;
mycomm.ReadTimeout = 32;
mycomm.BaudRate = 9600;
try {
mycomm.Open();
btnOpen.Text = "关闭串口";//按钮打开
lblToolStripStatus.Text = "串口打开成功!";//状态栏
}catch{
btnOpen.Text = "打开串口";
MessageBox.Show("没有发现串口或串口已被占用!");
lblToolStripStatus.Text = "串口打开失败!";
}
//添加事件注册
mycomm.DataReceived += comm_DataReceived;
2)。点击发送按钮,发送18字节数据,代码为:
//存放待发送的一包数据(包括帧头,命令号,帧长,帧数据,校验,帧尾)
byte[] package = new byte[18]; int j = 0; int k = 3;
package[0] = 0xAA;//帧头
package[1] = 0x01;//命令号
package[2] = 0x0D;//帧长
byte parity = 0x01+0x0d;//和校验:(命令号+帧长+帧数据)&7f。
//存放帧数据(13字节)
byte[] realData = new byte[13];
string data = getMode().Append(getTxFre(txtTxFre.Text).Append(getDuoPuLe(txtDuoFre.Text)).Append( getBand()).Append(getTxPower()).Append(getRxFre(txtRxFre.Text)).Append(getRxBandAndDelay()).Append(getAGC())).ToString();//待发送的二进制数据,略。。。
for (int i = 0; i < data.Length; i += 8)
{
//每八位截取,转为十进制的byte,再放入byte数组中
realData[j++] = (byte)Convert.ToInt32(data.Substring(i, 8), 2);
}
foreach (byte b in realData) {
parity += b;//校验码计算
package[k++] = b; }
parity &= 0x7F; //校验码计算完成
package[16] = parity;//和校验(1字节)
package[17] = 0xa5;//帧尾
if (!mycomm.IsOpen)
{
MessageBox.Show("串口没有打开,请打开串口!");
return;
}
mycomm.Write(package, 0, 18);//向串口发送一包(18字节)的数据
3)。 接收数据的事件代码:
private void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int n = mycomm.BytesToRead;
byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
mycomm.Read(buf, 0, n);//读取缓冲数据
builder.Clear();//清除字符串构造器的内容
//因为要访问ui资源,所以需要使用invoke方式同步ui。
this.Invoke((EventHandler)(delegate
{ //依次的拼接出16进制字符串
foreach (byte b in buf)
{
builder.Append(b.ToString("X2") + " ");
}
//直接按ASCII规则转换成字符串
//builder.Append(Encoding.ASCII.GetString(buf));
txtGet.Text = "";
txtGet.AppendText(builder.ToString());
}));
//判断返回正确与否
if (buf[3] == 0x00){
lblToolStripRxStatus.Text = "返回值正确!";
}
else if(buf[3] == 0x01){
lblToolStripRxStatus.Text = "返回值错误!";
}
//判断校验是否正确
byte rxParity;//接收校验和:(命令号+帧长+帧数据)&7f。
rxParity = 0x01+0x01;
rxParity += buf[3];
rxParity &= 0x7F;
if (rxParity != buf[4]) {
lblToolStripRxStatus.Text += "校验错误!";
}
}
三,总结
总体上说,C#串口通信比较简单,发送数据用:
byte[] package = new byte[18]{0};
mycomm.Write(package, 0, 18);
接收数据在串口的
private void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int n = mycomm.BytesToRead;
byte[] buf = new byte[n];
comm.Read(buf, 0, n);
}事件中。主要是Read/Write两个方法。
需要注意的地方是每次接收到的数据未必是一个完整的包(尤其是包较长时),例如刚开始本人本机调试(本地收发)时收到的18字节经常是先收到9字节,再收到9字节,后者先2,再8,再8字节。导致接手区的数据不对(没有18字节,只有最开始的9或2字节),此时可以用一个缓冲数组保存接收到的数据,直到接收的长度正确或到包尾了再进行处理。
以前好像忘记上传了,最近重写个。
工程源码地址: http://download.csdn.net/detail/a379039233/8864605 ,不需要分。
CSDN设置了分数,我也没办法。设置了共享链接:
链接:https://pan.baidu.com/s/1dg_i5m4mV_ciXw_RlQLLKA
提取码:ly88 -
c#串口通讯
2020-01-13 14:59:23} } /// /// 打开串口 /// /// 端口 /// 波特率 /// 奇偶校验检查协议 /// 每个字节的标准停止位数 public static SerialPort ConnectSerialPort(string portName, int baudRate, Parity parity, StopBits stopBits...using System; using System.IO.Ports; using System.Text; public class SerialPortHelper { public delegate void HanderInterfaceUpdataDelegate(string sendData); private static SerialPort _port; private static readonly object lockHelper = new object(); public static SerialPort SerialPortIns { get { if (_port == null) { lock (lockHelper) { _port = new SerialPort(); } } return _port; } } /// <summary> /// 打开串口 /// </summary> /// <param name="portName">端口</param> /// <param name="baudRate">波特率</param> /// <param name="parity">奇偶校验检查协议</param> /// <param name="stopBits">每个字节的标准停止位数</param> public static SerialPort ConnectSerialPort(string portName, int baudRate, Parity parity, StopBits stopBits,int dataBits, Handshake handShake) { try { _port = new SerialPort(); //端口 _port.PortName = portName; //波特率 _port.BaudRate = baudRate; //奇偶校验检查协议 _port.Parity = parity; //每个字节的标准停止位数 _port.StopBits = stopBits; //数据位 _port.DataBits = dataBits; //握手协议 _port.Handshake = handShake; //打开串口 if (!_port.IsOpen) { _port.Open(); } return _port; } catch (System.Exception ex) { return null; } } /// <summary> /// 关闭串口 /// </summary> public static void CloseSerialPort(SerialPort port) { if (port.IsOpen) { port.Close(); } _port = null; } /// <summary> /// 接收数据 /// </summary> /// <param name="port"></param> public static string ReceiveData(SerialPort port) { //Thread.Sleep(1000); var i = port.BytesToRead; byte[] bytes = new byte[i]; port.Read(bytes, 0, i); string str = Encoding.Default.GetString(bytes); return str; } public static string ReceiveDataByLine(SerialPort port) { return port.ReadTo("\r\n"); } /// <summary> /// 发送数据 /// </summary> /// <param name="port"></param> /// <param name="text"></param> public static void SendBytesData(SerialPort port, string text) { port.Write(text); } }
-
c# 串口通讯和TCP通讯
2018-08-15 11:21:40c# 串口通讯和TCP通讯。用途是根据串口通讯获得一起的准确数据,然后根据tcp发布出去,已经验证 -
C#串口通讯DEMO
2019-07-16 17:03:49C#串口通讯demo,引用system.IO.Ports,SerialPort类,用ASCII发送接收,可接收汉字 -
C#串口通讯代码
2018-09-10 10:28:49C#串口通讯代码 1)实时发送接收 2)自动获取端口 3)记录已发送命令,方便再次发送 -
C#串口通讯类
2017-11-28 16:32:23C#串口通讯类,基于System.IO.SerialPort类。内部默认处理了对串口操作中涉及的串口同步读取,按照协议解析数据等 -
C#串口通讯设计方法
2020-08-20 14:41:08C# 串口通讯设计流程,如何进行通讯的设计,数据读写,解析,方法不重要重要的是思路,通讯协议数据部分只有字或字节的方式接收,所以最后读取截取的数据只能是字或字节的集合。 -
C#串口通讯实例
2020-12-11 11:49:16文章目录C#串口通讯实例一、串口属性界面二、主界面特殊情况总结 C#串口通讯实例 本文参考《C#网络通信程序设计》(张晓明 编著) 程序界面如下图: 一、串口属性界面 参数设置界面代码如下: using System; using ...
C#串口通讯实例
本文参考《C#网络通信程序设计》(张晓明 编著)
程序界面如下图:
一、串口属性界面
参数设置界面代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO.Ports; namespace ComDemo { public partial class ComSet : Form { public ComSet() { InitializeComponent(); } private void ComSet_Load(object sender, EventArgs e) { //串口 string[] ports = SerialPort.GetPortNames(); foreach (string port in ports) { cmbPort.Items.Add(port); } cmbPort.SelectedIndex = 0; //波特率 cmbBaudRate.Items.Add("110"); cmbBaudRate.Items.Add("300"); cmbBaudRate.Items.Add("1200"); cmbBaudRate.Items.Add("2400"); cmbBaudRate.Items.Add("4800"); cmbBaudRate.Items.Add("9600"); cmbBaudRate.Items.Add("19200"); cmbBaudRate.Items.Add("38400"); cmbBaudRate.Items.Add("57600"); cmbBaudRate.Items.Add("115200"); cmbBaudRate.Items.Add("230400"); cmbBaudRate.Items.Add("460800"); cmbBaudRate.Items.Add("921600"); cmbBaudRate.SelectedIndex = 5; //数据位 cmbDataBits.Items.Add("5"); cmbDataBits.Items.Add("6"); cmbDataBits.Items.Add("7"); cmbDataBits.Items.Add("8"); cmbDataBits.SelectedIndex = 3; //停止位 cmbStopBit.Items.Add("1"); cmbStopBit.SelectedIndex = 0; //佼验位 cmbParity.Items.Add("无"); cmbParity.SelectedIndex = 0; } private void bntOK_Click(object sender, EventArgs e) { //以下4个参数都是从窗体MainForm传入的 MainForm.strProtName = cmbPort.Text; MainForm.strBaudRate = cmbBaudRate.Text; MainForm.strDataBits = cmbDataBits.Text; MainForm.strStopBits = cmbStopBit.Text; DialogResult = DialogResult.OK; } private void bntCancel_Click(object sender, EventArgs e) { DialogResult = DialogResult.Cancel; } } }
二、主界面
主界面代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO.Ports; using System.IO; using System.Threading; namespace ComDemo { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private Thread getRecevice; protected Boolean stop = false; protected Boolean conState = false; private StreamReader sRead; string strRecieve; bool bAccpet = false; SerialPort sp = new SerialPort();//实例化串口通讯类 //以下定义4个公有变量,用于参数传递 public static string strProtName = ""; public static string strBaudRate = ""; public static string strDataBits = ""; public static string strStopBits = ""; private void MainForm_Load(object sender, EventArgs e) { groupBox1.Enabled = false; groupBox2.Enabled = false; this.toolStripStatusLabel1.Text = "端口号:端口未打开 | "; this.toolStripStatusLabel2.Text = "波特率:端口未打开 | "; this.toolStripStatusLabel3.Text = "数据位:端口未打开 | "; this.toolStripStatusLabel4.Text = "停止位:端口未打开 | "; this.toolStripStatusLabel5.Text = ""; } //串口设计 private void btnSetSP_Click(object sender, EventArgs e) { timer1.Enabled = false; sp.Close(); ComSet dlg = new ComSet(); if (dlg.ShowDialog() == DialogResult.OK) { sp.PortName = strProtName;//串口号 sp.BaudRate = int.Parse(strBaudRate);//波特率 sp.DataBits = int.Parse(strDataBits);//数据位 sp.StopBits = (StopBits)int.Parse(strStopBits);//停止位 sp.ReadTimeout = 500;//读取数据的超时时间,引发ReadExisting异常 } } //打开/关闭串口 private void bntSwitchSP_Click(object sender, EventArgs e) { if (bntSwitchSP.Text == "打开串口") { if (strProtName != "" && strBaudRate != "" && strDataBits != "" && strStopBits != "") { try { if (sp.IsOpen) { sp.Close(); sp.Open();//打开串口 } else { sp.Open();//打开串口 } bntSwitchSP.Text = "关闭串口"; groupBox1.Enabled = true; groupBox2.Enabled = true; this.toolStripStatusLabel1.Text = "端口号:" + sp.PortName + " | "; this.toolStripStatusLabel2.Text = "波特率:" + sp.BaudRate + " | "; this.toolStripStatusLabel3.Text = "数据位:" + sp.DataBits + " | "; this.toolStripStatusLabel4.Text = "停止位:" + sp.StopBits + " | "; this.toolStripStatusLabel5.Text = ""; } catch (Exception ex) { MessageBox.Show("错误:" + ex.Message, "C#串口通信"); } } else { MessageBox.Show("请先设置串口!", "RS232串口通信"); } } else { timer1.Enabled = false; timer2.Enabled = false; bntSwitchSP.Text = "打开串口"; if (sp.IsOpen) sp.Close(); groupBox1.Enabled = false; groupBox2.Enabled = false; this.toolStripStatusLabel1.Text = "端口号:端口未打开 | "; this.toolStripStatusLabel2.Text = "波特率:端口未打开 | "; this.toolStripStatusLabel3.Text = "数据位:端口未打开 | "; this.toolStripStatusLabel4.Text = "停止位:端口未打开 | "; this.toolStripStatusLabel5.Text = ""; } } //发送数据 private void bntSendData_Click(object sender, EventArgs e) { if (sp.IsOpen) { try { sp.Encoding = System.Text.Encoding.GetEncoding("GB2312"); sp.Write(txtSend.Text);//发送数据 } catch (Exception ex) { MessageBox.Show("错误:" + ex.Message); } } else { MessageBox.Show("请先打开串口!"); } } //选择文件 private void btnOpenFile_Click(object sender, EventArgs e) { OpenFileDialog open = new OpenFileDialog(); open.InitialDirectory = "c\\"; open.RestoreDirectory = true; open.FilterIndex = 1; open.Filter = "txt文件(*.txt)|*.txt"; if (open.ShowDialog() == DialogResult.OK) { try { if (open.OpenFile() != null) { txtFileName.Text = open.FileName; } } catch (Exception err1) { MessageBox.Show("文件打开错误! " + err1.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } //发送文件内容 private void bntSendFile_Click(object sender, EventArgs e) { string fileName = txtFileName.Text.Trim(); if (fileName == "") { MessageBox.Show("请选择要发送的文件!", "Error"); return; } else { //sRead = new StreamReader(fileName); sRead = new StreamReader(fileName,Encoding.Default);//解决中文乱码问题 } timer1.Start(); } //发送文件时钟 private void timer1_Tick(object sender, EventArgs e) { string str1; str1 = sRead.ReadLine(); if (str1 == null) { timer1.Stop(); sRead.Close(); MessageBox.Show("文件发送成功!", "C#串口通讯"); this.toolStripStatusLabel5.Text = ""; return; } byte[] data = Encoding.Default.GetBytes(str1); sp.Write(data, 0, data.Length); this.toolStripStatusLabel5.Text = " 文件发送中..."; } //接收数据 private void btnReceiveData_Click(object sender, EventArgs e) { if (btnReceiveData.Text == "接收数据") { sp.Encoding = Encoding.GetEncoding("GB2312"); if (sp.IsOpen) { //timer2.Enabled = true; //使用主线程进行 //使用委托以及多线程进行 bAccpet = true; getRecevice = new Thread(new ThreadStart(testDelegate)); //getRecevice.IsBackground = true; getRecevice.Start(); btnReceiveData.Text = "停止接收"; } else { MessageBox.Show("请先打开串口"); } } else { //timer2.Enabled = false; bAccpet = false; try { //停止主监听线程 if (null != getRecevice) { if (getRecevice.IsAlive) { if (!getRecevice.Join(100)) { //关闭线程 getRecevice.Abort(); } } getRecevice = null; } } catch { } btnReceiveData.Text = "接收数据"; } } private void testDelegate() { reaction r = new reaction(fun); r(); } //用于接收数据的定时时钟 private void timer2_Tick(object sender, EventArgs e) { string str = sp.ReadExisting(); string str2 = str.Replace("\r", "\r\n"); txtReceiveData.AppendText(str2); txtReceiveData.ScrollToCaret(); } //下面用到了接收信息的代理功能,此为设计的要点之一 delegate void DelegateAcceptData(); void fun() { while (bAccpet) { AcceptData(); } } delegate void reaction(); void AcceptData() { if (txtReceiveData.InvokeRequired) { try { DelegateAcceptData ddd = new DelegateAcceptData(AcceptData); this.Invoke(ddd, new object[] { }); } catch { } } else { try { strRecieve = sp.ReadExisting(); txtReceiveData.AppendText(strRecieve); } catch (Exception ex) { } } } private void bntClear_Click(object sender, EventArgs e) { txtReceiveData.Text = ""; } private void button3_Click(object sender, EventArgs e) { try { string path = Directory.GetCurrentDirectory() + @"\output.txt"; string content = this.txtReceiveData.Text; FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write); StreamWriter write = new StreamWriter(fs); write.Write(content); write.Flush(); write.Close(); fs.Close(); MessageBox.Show("接收信息导出在:" + path); } catch (Exception ex) { MessageBox.Show(ex.Message); } } } }
特殊情况
如果处于接收数据的时候,直接关闭程序,会有残余线程。可不可以把线程设置为background线程,或者在private override OnClosing里进行一下线程结束确认?我改成用BackgroundWorker了。
private void bwReceive_DoWork(object obj, DoWorkEventArgs e) { while (true) { if(bwReceive.CancellationPending) { e.Cancel = true; return; } Thread.Sleep(100); bwReceive.ReportProgress(0); } } private void bwReceive_ProgressChanged(object obj, ProgressChangedEventArgs e) { strReceive = sp.ReadExisting(); txtReceiveData.AppendText(strReceive); } private void bwReceive_RunWorkerCompleted(object obj, RunWorkerCompletedEventArgs e) { if(e.Error!=null) { MessageBox.Show("Error: " + e.Error.Message, "C#通讯示例"); } else if(e.Cancelled) { MessageBox.Show("停止接收数据!", "C#通讯示例"); } else { MessageBox.Show("Task Completed!"); } }
总结
提示:以上全是整合资源。
-
串口通讯模块C# 串口通讯模块C#
2010-11-28 18:24:52串口通讯模块C串口串口通讯模块C#通串口通讯模块C#讯模块C##串口通讯模块C# -
C# 串口通讯Demo(VS2010)
2015-07-03 10:24:23一个简单的C# 串口通讯Demo,可收发数据。 -
c#串口通讯,同惠电子TH3312数字功率计
2020-10-16 09:10:15C#串口通讯,项目实战分享,此文件主要是使用C#语言通过串口485与设备通讯控制;后期继续更新与优化。
-
工程制图 AutoCAD 2012 从二维到三维
-
通过约束弹性网正则化进行稳健的图学习
-
FileOutputStream的使用
-
华为存储资料及模拟器百度网盘.txt
-
musiccenter
-
MySQL 主从复制 Replication 详解(Linux 和 W
-
2021-02-26
-
OpenCVForUnity.rar
-
wps的vba(宏,vb编辑器)
-
C++生成-1到1之间的随机浮点数
-
vgaVDMA.rar
-
音效整理该资源为各种音效的集合.rar
-
poi.jar包.rar
-
Galera 高可用 MySQL 集群(PXC v5.6 + Ngin
-
金融云徐敏:云计算将给互联网金融带来怎样的新浪潮?
-
创建函数/存储过程报错解决冲突!慢日志查询和函数冲突
-
【Python-随到随学】 FLask第一周
-
Unity ILRuntime框架设计
-
基于Qt的LibVLC开发教程
-
spark大数据分析与实战