基于多线程方式的串行通信接口数据接收案例
广东职业技术技术学院 欧浩源
1、案例背景
在本博客的《【CC2530入门教程-06】CC2530的ADC工作原理与应用》中实现了电压数据采集的程序设计,传感器模块以每1秒发送一帧数据的形式通过串口向上位机发送电压数据。其数据帧由4个字节组成:一个帧头和一个帧尾,中间两个字节为电压数据,其格式如下:
帧头(0xAF) 电压数据高8位 电压数据低8位 帧尾(0xFA)
在篇博文中,将讲述如何通过多线程的方式,从串口接收传感器发送过来的数据帧,并将数据帧和换算后的实际电压值显示出来。
2、界面设计
3、引入命令空间
使用多线程的方式,需要引入命名空间:System.Threading;
使用串行通信接口,需要引入命名空间:System.IO.Ports;
4、初始化工作
首先,定义一个串行接口的对象和一个线程的字段,
然后,给主窗体添加窗体装载事件(即Load事件),在该事件中对各个控件的属性进行初始化工作。
SerialPort com = new SerialPort(); //实例化一个串口对象 Thread t; //定义一个线程字段 private void Form1_Load(object sender, EventArgs e) { textBox1.ReadOnly = true; //文本框只读 textBox1.ScrollBars = ScrollBars.Vertical; //文本框支持垂直滚动条 comboBox1.Items.Add("COM1"); comboBox1.Items.Add("COM2"); comboBox1.Items.Add("COM3"); comboBox1.Items.Add("COM4"); comboBox1.Items.Add("COM5"); comboBox1.SelectedIndex = 2; comboBox2.Items.Add("4800"); comboBox2.Items.Add("9600"); comboBox2.Items.Add("19200"); comboBox2.Items.Add("57600"); comboBox2.Items.Add("115200"); comboBox2.SelectedIndex = 1; comboBox3.Items.Add("6"); comboBox3.Items.Add("7"); comboBox3.Items.Add("8"); comboBox3.SelectedIndex = 2; comboBox4.Items.Add("1"); comboBox4.Items.Add("1.5"); comboBox4.Items.Add("2"); comboBox4.SelectedIndex = 0; }
5、设置串行接口
在进行串口通信的时候,一般的流程是:先设置通信的端口号、波特率、数据位、停止位和校验位,然后打开串口,接着发送数据和接收数据,最后要关闭串口。
在本案例中,对串口的各个参数配置完成后,打开串口,然后启动串口数据接收的线程,开始以轮询的方式接收数据。
private void button1_Click(object sender, EventArgs e) { if (button1.Text == "打开串口") { com.PortName = comboBox1.Text; //选择串口号 com.BaudRate = int.Parse(comboBox2.Text); //选择波特率 com.DataBits = int.Parse(comboBox3.Text); //选择数据位数 com.StopBits = (StopBits)int.Parse(comboBox4.Text); //选择停止位数 com.Parity = Parity.None; //选择是否奇偶校验 try { if (com.IsOpen) //判断该串口是否已打开 { com.Close(); com.Open(); } else { com.Open(); } t = new Thread(com_DataReceived); //创建并启用数据接收线程 t.Start(); } catch(Exception ex) { MessageBox.ReferenceEquals("错误:" + ex.Message, "串口通信"); } button1.Text = "关闭串口"; } else if (button1.Text == "关闭串口") { com.Close(); //关闭串口 t.Abort(); //终止线程 button1.Text = "打开串口"; } }
6、串口数据接收线程方法
在该方法中,只有在串口打开并连接成功的情况下在去读取串口缓冲区中的数据。
首先通过读取BytesToRead属性,获取串口接收缓冲区中的数据字节数,然后根据这个属性实例化一个byte类型的数据,在通过Read方法将数据从缓冲区中读取到数组中。
将数据帧以十六进制的形式显示到窗体的文本框中。
通过计算获得实际的电压值,显示到对应的Lable标签中。
private void com_DataReceived() { while (com.IsOpen) //在串口已打开情况下读取数据 { string strRcv = ""; int count = com.BytesToRead; //获取串口缓冲器的字节数 byte[] readBuffer = new byte[count]; //实例化接收串口数据的数组 if (count != 0) //如果串口接收到数据 { strRcv = count.ToString() + " "; com.Read(readBuffer, 0, count); //从串口缓冲区读出数据到数组 for (int i = 0; i < readBuffer.Length; i++) { strRcv += readBuffer[i].ToString("X2") + " "; //16进制显示 } textBox1.Text += strRcv + " "; if (readBuffer[0] == 0xAF && readBuffer[3] == 0xFA) //判断数据的帧头和帧尾 { Int32 ad = readBuffer[1]; double advalue; ad <<= 8; ad |= readBuffer[2]; //从数据帧中将电压数据取出 advalue = ad; advalue = (advalue * 3.3) / 32768; //将数据换算为实际的电压值 label6.Text = advalue.ToString("F2") + " V"; } } Thread.Sleep(500); }
7、程序运行结果
8、结语
串行接口通信是物联网技术应用中常用的数据交互方式。
利用多线程的手段以轮询的方式读取串口缓冲区的数据是一种最基础的方法,然而并不是唯一的方法。采用DadaReceived事件触发的方法来接收数据也很常用,当程序设计程度稍微复杂一点,后面再慢慢讲述。