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

 

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();    //实例化一个串口对象,在前端控件中可以直接拖过来,但最好是在后端代码中写代码,这样复制到其他地方不会出错。s是一个串口的句柄
        public Form1()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;   //防止跨线程访问出错,好多地方会用到
            button1.Text = "打开串口";
            int[] item = { 9600,115200};    //定义一个Item数组,遍历item中每一个变量a,增加到comboBox2的列表中
            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];
            //Array.Sort(ports);

        }
        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 = "关闭串口";
                    //MessageBox.Show("串口已打开");
                }
                else
                {
                    s.Close();
                    s.DataReceived -= s_DataReceived;
                    button1.Text = "打开串口";
                }
            }
            catch (Exception ee)
            {
                MessageBox.Show(ee.ToString());
            }
        }
        void s_DataReceived(object sender, SerialDataReceivedEventArgs e)   //数据接收事件,读到数据的长度赋值给count,如果是8位(节点内部编程规定好的),就申请一个byte类型的buff数组,s句柄来读数据
        {
            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)    //读取Buff中存的数据,转换成显示的十六进制数
                {
                    str += item.ToString("X2")+" ";
                }
                richTextBox1.Text =System.DateTime.Now.ToString()+": "+ str + "\n" + richTextBox1.Text;      //这是跨线程访问richtextbox,原程序和DataReceived事件是两个不同的线程同时在执行
                if (buff[0] == 0x04)   //如果节点是04发来的数据
                {
                    ID.Text = buff[0].ToString();   //这下面是上位机右边那一段,用来显示处理好的数据的温度、湿度、光照、灰尘、ID信息的。buff【0】中存的是数据的ID信息,显示在ID的Label上面
                    switch (buff[2])   //判断数据类型  buff【0】和buff【1】代表ID的低位和高位,同理2和3代表数据类型的低位和高位,当2和3的值为1时,4和5代表温度,6和7代表湿度;

         {
                      case 0x01:       //当2和3的值为1,4和5是温度,6和7是湿度
          {
                                Tem.Text = (buff[5] * 4 + buff[4] * 0.05 - 30).ToString();
                                Hum.Text = (buff[6]  + buff[7]).ToString();
                                break;
                            }
                        case 0x02://6和7是光照
                       {
                                Light.Text = (buff[6] + buff[7]).ToString();
                                break;
                            }
                        case 0x04://6和7是灰尘
                        {
                                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);//这里只显示04节点的信息
        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 = "关闭串口";
                    //MessageBox.Show("串口已打开");
                }
                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事件处理函数。

C#做一个简单的进行串口通信的上位机的更多相关文章

  1. 基于Arduino和python的串口通信和上位机控制

    引言 经常的时候我们要实现两个代码之间的通信,比如说两个不同不同人写的代码要对接,例如将python指令控制Arduino控件的开关,此处使用串口通信是非常方便的,下面笔者将结合自己踩过的坑来讲述下自 ...

  2. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截

    程序猿修仙之路--数据结构之你是否真的懂数组?   数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少. ...

  3. 【Bugly干货分享】一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎

    Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言 好吧,说是“粒子引擎”还是大言不 ...

  4. 使用React并做一个简单的to-do-list

    1. 前言 说到React,我从一年之前就开始试着了解并且看了相关的入门教程,而且还买过一本<React:引领未来的用户界面开发框架 >拜读.React的轻量组件化的思想及其virtual ...

  5. 【 D3.js 入门系列 --- 3 】 做一个简单的图表!

    前面说了几节,都是对文字进行处理,这一节中将用 D3.js 做一个简单的柱形图. 做柱形图有很多种方法,比如用 HTML 的 div 标签,或用 svg . 推荐用 SVG 来做各种图形.SVG 意为 ...

  6. 一起用HTML5 canvas做一个简单又骚气的粒子引擎

    前言 好吧,说是"粒子引擎"还是大言不惭而标题党了,离真正的粒子引擎还有点远.废话少说,先看demo 本文将教会你做一个简单的canvas粒子制造器(下称引擎). 世界观 这个简单 ...

  7. Jmeter初步使用二--使用jmeter做一个简单的性能测试

    经过上一次的初步使用,我们懂得了Jmeter的安装与初步使用的方法.现在,我们使用Jmeter做一个简单的性能测试.该次测试,提交的参数不做参数化处理,Jmeter各元件使用将在介绍在下一博文开始介绍 ...

  8. 用EF DataBase First做一个简单的MVC3报名页面

    使用EF DataBase First做一个简单的MVC3报名网站 ORM(Object Relational Mapping)是面向对象语言中的一种数据访问技术,在ASP.NET中,可以通过ADO. ...

  9. 【 D3.js 入门系列 — 3 】 做一个简单的图表!

    图1. 柱形图 1. 柱形图 前几章的例子,都是对文字进行处理.本章中将用 D3 做一个简单的柱形图.制作柱形图有很多种方法,比如用 HTML 的 <div> 标签,或在 SVG 上绘制 ...

随机推荐

  1. Oracle数据库拼接字符串

    Oracle数据库中字符串拼接的实现 select count() from THT_HTFKJL where ht=1: 假如结果为:31.例如上面例子想要给结果拼接字符串有二种实现方法,第一种使用 ...

  2. 慕课网 深入浅出javascript 笔记

    javascript 数据类型 5种简单数据类型:Number.String.Boolean.Undefined.Null 1种复杂数据类型:Object =    表示赋值: ==  表示比较,但是 ...

  3. Ubuntu下彻底卸载wine

    简介: wine是linux下模拟windows的一个东西,可以用来安装exe程序,但是对于wine的卸载确 实非常麻烦的,这里是彻底卸载wine的一个教程. ##首先卸载wine sudo apt- ...

  4. vue-cli中的webpack配置

    编辑模式下显示正常,打开的时候不知道为啥排版有问题.segementfalut链接在这里 版本号 vue-cli 2.8.1 (终端通过vue -V 可查看) vue 2.2.2 webpack 2. ...

  5. CSS 内外边距 float positio属性

    一.外边距和内边 margin:            用于控制元素与元素之间的距离 外边距:margin的最基本用途就是控制元素周围空间的间隔,从视觉角度上达到相互隔开的目的. padding:   ...

  6. CCSUOJ评测系统——第一次scrum冲刺

    1.第一次冲刺任务安排 对Github上的HUSTOJ开源项目进行Fork,搭建基本环境 2.用户需求 ①基本功能显示在首页 ②能够提交题目并判题,并对自己所提交的题目正确性进行反馈,能够查看自己提交 ...

  7. 从.net角度分析 异步和多线程

    线程 进程 CPU目前都是多核心的,相当于一个大脑几块可以同时工作. 超线程CPU是指在一块CPU中,用虚拟方法将一个物理核心模拟成多个核心(如:一个单物理核心,模拟成二个核心,即所谓的二线程.)只有 ...

  8. JBoss服务器优化

    JBOSS参数调优 配置deploy/jboss-web.deployer/server.xml文件 . <Connector port="8080" address=&qu ...

  9. .net 下使用Quartz.Net

    Quartz.net是作业调度框架 1. 项目中添加quartz.net的引用(这里使用nuget管理) 新建一个类TimingJob,该类主要用于实现任务逻辑 using Quartz; using ...

  10. Linux setenforce命令详解[SeLinux操作]

    SELinux(Security-Enhanced Linux) 是美国国家安全局(NSA)对于强制访问控制的实现,是 Linux历史上最杰出的新安全子系统. 关闭SELinux 临时生效: 命令临时 ...