C#做一个简单的进行串口通信的上位机

C#做一个简单的进行串口通信的上位机

乱世中的单纯 发布于 1年前,共有 10 条评论

1、上位机与下位机

        上位机相当于一个软件系统,可以用于接收数据、控制数据。即可以对接收到的数据直接发送操控命令来操作数据。上位机可以接收下位机的信号。下位机是一个控制器,是直接控制设备获取设备状况的计算机。上位机发出的命令首先给下位机,下位机再根据此命令解释成相应时序信号直接控制相应设备。下位机不时读取设备状态数据(一般为模拟量),转换成数字信号反馈给上位机。上位机不可以单独使用,而下位机可以单独使用。

 

2、串口通信

        串口相当于硬件类型的接口。比如无线传感节点发送信号到汇聚节点,汇聚节点通过串口将数据传到计算机中的上位机中,上位机接收信息,并处理。

      串口是按位(bit)发送和接收字节。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。

    a,波特率:这是一个衡量符号传输速率的参数。

    b,数据位:这是衡量通信中实际数据位的参数。

    c,停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。

    d,奇偶校验位:在串口通信中一种简单的检错方式。

 

3、C#代码

[c#]  view plain  copy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Linq;  
  7. using System.Text;  
  8. using System.Threading.Tasks;  
  9. using System.Windows.Forms;  
  10. using System.IO.Ports;  
  11. using System.Diagnostics;  
  12. namespace serial2  
  13. {  
  14.     public partial class Form1 : Form  
  15.     {  
  16.         SerialPort s = new SerialPort();    //实例化一个串口对象,在前端控件中可以直接拖过来,但最好是在后端代码中写代码,这样复制到其他地方不会出错。s是一个串口的句柄  
  17.         public Form1()  
  18.         {  
  19.             InitializeComponent();  
  20.             Control.CheckForIllegalCrossThreadCalls = false;   //防止跨线程访问出错,好多地方会用到  
  21.             button1.Text = "打开串口";  
  22.             int[] item = { 9600,115200};    //定义一个Item数组,遍历item中每一个变量a,增加到comboBox2的列表中  
  23.             foreach (int a in item)  
  24.             {  
  25.                 comboBox2.Items.Add(a.ToString());  
  26.             }  
  27.              
  28.             comboBox2.SelectedItem = comboBox2.Items[1];    //默认为列表第二个变量  
  29.         }  
  30.         private void Form1_Load(object sender, EventArgs e)   //窗体事件要先配置端口信息。  
  31.         {  
  32.             string[] ports = SerialPort.GetPortNames();  
  33.             comboBox1.Items.AddRange(ports);  
  34.             comboBox1.SelectedItem=comboBox1.Items[0];  
  35.             //Array.Sort(ports);  
  36.               
  37.         }  
  38.         private void button1_Click(object sender, EventArgs e)   //下面讲解中差不多已经讲清楚了  
  39.         {  
  40.             try  
  41.             {  
  42.                 if (!s.IsOpen)  
  43.                 {  
  44.                     s.PortName = comboBox1.SelectedItem.ToString();  
  45.                     s.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString());  
  46.                     s.Open();  
  47.                     s.DataReceived += s_DataReceived;  
  48.                     button1.Text = "关闭串口";  
  49.                     //MessageBox.Show("串口已打开");  
  50.                 }  
  51.                 else  
  52.                 {  
  53.                     s.Close();  
  54.                     s.DataReceived -= s_DataReceived;  
  55.                     button1.Text = "打开串口";  
  56.                 }  
  57.             }  
  58.             catch (Exception ee)  
  59.             {  
  60.                 MessageBox.Show(ee.ToString());  
  61.             }  
  62.         }  
  63.         void s_DataReceived(object sender, SerialDataReceivedEventArgs e)   //数据接收事件,读到数据的长度赋值给count,如果是8位(节点内部编程规定好的),就申请一个byte类型的buff数组,s句柄来读数据  
  64.         {  
  65.             int count =s.BytesToRead;      
  66.             string str=null ;  
  67.             if (count == 8)  
  68.             {  
  69.                 byte[] buff = new byte[count];  
  70.                 s.Read(buff, 0, count);  
  71.                 foreach (byte item in buff)    //读取Buff中存的数据,转换成显示的十六进制数  
  72.                 {  
  73.                     str += item.ToString("X2")+" ";  
  74.                 }  
  75.                 richTextBox1.Text =System.DateTime.Now.ToString()+": "+ str + "\n" + richTextBox1.Text;      //这是跨线程访问richtextbox,原程序和DataReceived事件是两个不同的线程同时在执行  
  76.                 if (buff[0] == 0x04)   //如果节点是04发来的数据  
  77.                 {  
  78.                     ID.Text = buff[0].ToString();   //这下面是上位机右边那一段,用来显示处理好的数据的温度、湿度、光照、灰尘、ID信息的。buff【0】中存的是数据的ID信息,显示在ID的Label上面  
  79.                     switch (buff[2])   //判断数据类型  buff【0】和buff【1】代表ID的低位和高位,同理2和3代表数据类型的低位和高位,当2和3的值为1时,4和5代表温度,6和7代表湿度;  
  80.                       
  81.          {    
  82.                       case 0x01:       //当2和3的值为1,4和5是温度,6和7是湿度  
  83.           {  
  84.                                 Tem.Text = (buff[5] * 4 + buff[4] * 0.05 - 30).ToString();  
  85.                                 Hum.Text = (buff[6]  + buff[7]).ToString();  
  86.                                 break;  
  87.                             }  
  88.                         case 0x02://6和7是光照  
  89.                        {  
  90.                                 Light.Text = (buff[6] + buff[7]).ToString();  
  91.                                 break;  
  92.                             }  
  93.                         case 0x04://6和7是灰尘  
  94.                         {  
  95.                                 Dust.Text = (buff[6] + buff[7]).ToString();  
  96.                                 break;  
  97.                             }  
  98.                         default:  
  99.                             break;  
  100.                     }  
  101.                 }  
  102.               
  103.             }  
  104.               
  105.         }  
  106.         private void button3_Click(object sender, EventArgs e)   //每次发一个字节  
  107.    {  
  108.             string[] sendbuff = richTextBox2.Text.Split();  //分割输入的字符串,判断有多少个字节需要发送  
  109.         Debug.WriteLine("发送字节数:"+sendbuff.Length);  
  110.             foreach (string  item in sendbuff)  
  111.             {  
  112.                 int count = 1;  
  113.                 byte[] buff = new byte[count];  
  114.                 buff[0] = byte.Parse(item, System.Globalization.NumberStyles.HexNumber);//格式化字符串为十六进制数值  
  115.               s.Write(buff, 0, count);  
  116.             }  
  117.         }  
  118.         private void button2_Click(object sender, EventArgs e)//刷新右边的数值  
  119.       {  
  120.             int count = 1;  
  121.             byte[] buff = new byte[count];  
  122.             buff[0] = byte.Parse("04", System.Globalization.NumberStyles.HexNumber);//这里只显示04节点的信息  
  123.         s.Write(buff, 0, count);  
  124.         }  
  125.     }  
  126. }  

 (以上规则均是本实验室节点内部自定义规则,测试的,外面的相应要改)

4、结果

 

 

5、补充四点知识

  1)在程序可能会遇到错误的地方,用try+两个Tab键,将代码写入try中。比如本例子中的代码:

[c#]  view plain  copy
  1.  private void button1_Click(object sender, EventArgs e)  
  2.         {  
  3.             try  
  4.             {  
  5.                 if (!s.IsOpen)  
  6.                 {  
  7.                     s.PortName = comboBox1.SelectedItem.ToString();  
  8.                     s.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString());  
  9.                     s.Open();  
  10.                     s.DataReceived += s_DataReceived;  
  11.                     button1.Text = "关闭串口";  
  12.                     //MessageBox.Show("串口已打开");  
  13.                 }  
  14.                 else  
  15.                 {  
  16.                     s.Close();  
  17.                     s.DataReceived -= s_DataReceived;  
  18.                     button1.Text = "打开串口";  
  19.                 }  
  20.             }  
  21.             catch (Exception ee)  
  22.             {  
  23.                 MessageBox.Show(ee.ToString());  
  24.             }  
  25.         }  

如果代码没有写入try中,则可能出现的一种情况是比如有两个上位机,同时占用同一个串口,则就会冲突,会出错。程序就会终止,整个进程结束。而如果写入try中,并且把抛出异常的catch代码实例化,即捕获异常要实例化一个句柄,这样程序遇到error就不会终止,而会出现报错的原因。如下图,我的这个上位机和网上下载的一个上位机同时占用COM3串口(网上下载的先占用COM3),这时我的上位机在打开串口时会出现报错。

2)就我这个上位机而言,需要有打开串口和关闭串口两个button按钮,但是考虑到占地方,当然最重要的还是如果用两个按钮来表示,当你按下打开串口,如果忘了是否打开,则是看不出来是不是打开的,所以可以合并为一个button控件。(代码还是用上面那一段的代码)。(感觉很神奇啊)。在button1_Click事件中,先点击button,如果串口是关闭的,则打开串口,然后把button1.Text的值赋值为“关闭串口”,如果串口本来是关闭的,则点击按钮会把button1.Text的值赋值为“打开串口”,同时把接收的数据清空。感觉这个方法真的很不错!嘿嘿

3)当输入一个变量或方法什么的,它所有有的会自动出现在一个列表,这时,“正方体”代表“方法”,“小钳子”代表“变量”,“闪电”代表“事件”。

4) 产生对象的事件时

比如输入s.自动会出现DataReceived事件,再输入“+=”就会有如上图提示,按Tab键。然后又会如下图提示

再次按tab键,就会自动生成DataReceived事件处理函数。


  • 2
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值