C#做一个简单的进行串口通信的上位机
乱世中的单纯 发布于 1年前,共有 10 条评论
1、上位机与下位机
上位机相当于一个软件系统,可以用于接收数据、控制数据。即可以对接收到的数据直接发送操控命令来操作数据。上位机可以接收下位机的信号。下位机是一个控制器,是直接控制设备获取设备状况的计算机。上位机发出的命令首先给下位机,下位机再根据此命令解释成相应时序信号直接控制相应设备。下位机不时读取设备状态数据(一般为模拟量),转换成数字信号反馈给上位机。上位机不可以单独使用,而下位机可以单独使用。
2、串口通信
串口相当于硬件类型的接口。比如无线传感节点发送信号到汇聚节点,汇聚节点通过串口将数据传到计算机中的上位机中,上位机接收信息,并处理。
串口是按位(bit)发送和接收字节。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。
a,波特率:这是一个衡量符号传输速率的参数。
b,数据位:这是衡量通信中实际数据位的参数。
c,停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。
d,奇偶校验位:在串口通信中一种简单的检错方式。
3、C#代码
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using System.IO.Ports;
- using System.Diagnostics;
- namespace serial2
- {
- public partial class Form1 : Form
- {
- SerialPort s = new SerialPort();
- public Form1()
- {
- InitializeComponent();
- Control.CheckForIllegalCrossThreadCalls = false;
- button1.Text = "打开串口";
- int[] item = { 9600,115200};
- foreach (int a in item)
- {
- comboBox2.Items.Add(a.ToString());
- }
-
- comboBox2.SelectedItem = comboBox2.Items[1];
- }
- private void Form1_Load(object sender, EventArgs e)
- {
- string[] ports = SerialPort.GetPortNames();
- comboBox1.Items.AddRange(ports);
- comboBox1.SelectedItem=comboBox1.Items[0];
-
-
- }
- private void button1_Click(object sender, EventArgs e)
- {
- try
- {
- if (!s.IsOpen)
- {
- s.PortName = comboBox1.SelectedItem.ToString();
- s.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString());
- s.Open();
- s.DataReceived += s_DataReceived;
- button1.Text = "关闭串口";
-
- }
- else
- {
- s.Close();
- s.DataReceived -= s_DataReceived;
- button1.Text = "打开串口";
- }
- }
- catch (Exception ee)
- {
- MessageBox.Show(ee.ToString());
- }
- }
- void s_DataReceived(object sender, SerialDataReceivedEventArgs e)
- {
- int count =s.BytesToRead;
- string str=null ;
- if (count == 8)
- {
- byte[] buff = new byte[count];
- s.Read(buff, 0, count);
- foreach (byte item in buff)
- {
- str += item.ToString("X2")+" ";
- }
- richTextBox1.Text =System.DateTime.Now.ToString()+": "+ str + "\n" + richTextBox1.Text;
- if (buff[0] == 0x04)
- {
- ID.Text = buff[0].ToString();
- switch (buff[2])
-
- {
- case 0x01:
- {
- Tem.Text = (buff[5] * 4 + buff[4] * 0.05 - 30).ToString();
- Hum.Text = (buff[6] + buff[7]).ToString();
- break;
- }
- case 0x02:
- {
- Light.Text = (buff[6] + buff[7]).ToString();
- break;
- }
- case 0x04:
- {
- Dust.Text = (buff[6] + buff[7]).ToString();
- break;
- }
- default:
- break;
- }
- }
-
- }
-
- }
- private void button3_Click(object sender, EventArgs e)
- {
- string[] sendbuff = richTextBox2.Text.Split();
- Debug.WriteLine("发送字节数:"+sendbuff.Length);
- foreach (string item in sendbuff)
- {
- int count = 1;
- byte[] buff = new byte[count];
- buff[0] = byte.Parse(item, System.Globalization.NumberStyles.HexNumber);
- s.Write(buff, 0, count);
- }
- }
- private void button2_Click(object sender, EventArgs e)
- {
- int count = 1;
- byte[] buff = new byte[count];
- buff[0] = byte.Parse("04", System.Globalization.NumberStyles.HexNumber);
- s.Write(buff, 0, count);
- }
- }
- }
(以上规则均是本实验室节点内部自定义规则,测试的,外面的相应要改)
4、结果
5、补充四点知识
1)在程序可能会遇到错误的地方,用try+两个Tab键,将代码写入try中。比如本例子中的代码:
- private void button1_Click(object sender, EventArgs e)
- {
- try
- {
- if (!s.IsOpen)
- {
- s.PortName = comboBox1.SelectedItem.ToString();
- s.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString());
- s.Open();
- s.DataReceived += s_DataReceived;
- button1.Text = "关闭串口";
-
- }
- else
- {
- s.Close();
- s.DataReceived -= s_DataReceived;
- button1.Text = "打开串口";
- }
- }
- catch (Exception ee)
- {
- MessageBox.Show(ee.ToString());
- }
- }
如果代码没有写入try中,则可能出现的一种情况是比如有两个上位机,同时占用同一个串口,则就会冲突,会出错。程序就会终止,整个进程结束。而如果写入try中,并且把抛出异常的catch代码实例化,即捕获异常要实例化一个句柄,这样程序遇到error就不会终止,而会出现报错的原因。如下图,我的这个上位机和网上下载的一个上位机同时占用COM3串口(网上下载的先占用COM3),这时我的上位机在打开串口时会出现报错。
2)就我这个上位机而言,需要有打开串口和关闭串口两个button按钮,但是考虑到占地方,当然最重要的还是如果用两个按钮来表示,当你按下打开串口,如果忘了是否打开,则是看不出来是不是打开的,所以可以合并为一个button控件。(代码还是用上面那一段的代码)。(感觉很神奇啊)。在button1_Click事件中,先点击button,如果串口是关闭的,则打开串口,然后把button1.Text的值赋值为“关闭串口”,如果串口本来是关闭的,则点击按钮会把button1.Text的值赋值为“打开串口”,同时把接收的数据清空。感觉这个方法真的很不错!嘿嘿
3)当输入一个变量或方法什么的,它所有有的会自动出现在一个列表,这时,“正方体”代表“方法”,“小钳子”代表“变量”,“闪电”代表“事件”。
4) 产生对象的事件时
比如输入s.自动会出现DataReceived事件,再输入“+=”就会有如上图提示,按Tab键。然后又会如下图提示
再次按tab键,就会自动生成DataReceived事件处理函数。