用serialport控件(C#)实现PC和多个传感器通信

ximengni 2014-11-21 04:19:52
初学串口通信,单个温度传感器的通信已经没问题,用timer自动1000ms发送一次,用DataReceived接收并且能显示温度和曲线,现在要同时跟三个传感器通信,每个传感器的发送命令字节数不同,接收数据的字节数也不同,想要发送一个收到一个之后再自动发送第二个命令接收第二条数据,然后第三个,轮一轮之后再从头开始,我在timer里这样写能做到自动轮流发送,但是收的时候就乱了,求高手帮忙!谢谢!
private void timer1_Tick(object sender, EventArgs e)
{
k++;
if (k > 3) k = 1;
switch (k)
{
case 1:
byte[] boutdata1 = { 0x01, 0x04, 0x01, 0xE3 };
serialPort1.Write(boutdata1, 0, 4);
break;
case 2:
byte[] boutdata2 = { 0x02, 0x03, 0x00, 0x01, 0x00, 0x01, 0xD5, 0xF9 };
serialPort1.Write(boutdata2, 0, 8);
break;
case 3:
byte[] boutdata3 = { 0x50, 0x03, 0x00, 0x03, 0x00, 0x01, 0x79, 0x8B };
serialPort1.Write(boutdata3, 0, 8);
break;
}
...全文
957 36 打赏 收藏 转发到动态 举报
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
ximengni 2014-12-01
  • 打赏
  • 举报
回复
解决了 感觉用了挺笨的方法 谢谢大家的帮助!

public partial class Form1 : Form
{
int k = 0;
int[] crc_data1 = { 0, 0 };
int[] crc_data2 = { 0, 0, 0, 0, 0 };
int[] crc_data3 = { 0, 0, 0, 0, 0 };
int x = 0;
short qtemp;
byte[] new_byte1 = { 0, 0, 0, 0, 0, 0, 0 };
SolidBrush bush1 = new SolidBrush(Color.Red);
SolidBrush bush2 = new SolidBrush(Color.Green);
public Form1()
{
InitializeComponent();
}

//串口初始化
private void Form1_Load(object sender, EventArgs e)
{
serialPort1.PortName = "COM3";
serialPort1.BaudRate = 9600;
serialPort1.DataBits = 8;
serialPort1.StopBits = System.IO.Ports.StopBits.One;
serialPort1.Parity = System.IO.Ports.Parity.None;
serialPort1.Open();
}

//向水浸传感器发送读指令
private void timer1_Tick(object sender, EventArgs e)
{
byte[] boutdata1 = { 0x01, 0x04, 0x01, 0xE3 };
serialPort1.Write(boutdata1, 0, 4);
}

//触发事件,读取温度传感器返回数据
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
System.Threading.Thread.Sleep(100);
int byte_num1 = serialPort1.Read(new_byte1, 0, 7);
this.Invoke(new EventHandler(DisplayText));
}

private void DisplayText(object sender, EventArgs e)
{
if (new_byte1[0] == 1) //水浸传感器
{
for (int i = 0; i < 2; i++)
crc_data1[i] = (int)new_byte1[i];
int[] crc1 = crc16(crc_data1);
if (crc1[0] == new_byte1[2] && crc1[1] == new_byte1[3]) //校验CRC
alarm1(); //显示,红灯报警,绿灯正常
else
MessageBox.Show("校验码错误,数据无效!", "");
byte[] boutdata2 = { 0x02, 0x03, 0x00, 0x01, 0x00, 0x01, 0xD5, 0xF9 }; //发送温度传感器数据
serialPort1.Write(boutdata2, 0, 8);
}
if (new_byte1[0] == 2 && new_byte1[1] == 3) //温度传感器
{
for (int i = 0; i < 5; i++)
crc_data2[i] = (int)new_byte1[i];
int[] crc2 = crc16(crc_data2);
if (crc2[0] == new_byte1[5] && crc2[1] == new_byte1[6])
DisplayText2(); //显示温度并画出温度曲线
else
MessageBox.Show("校验码错误,数据无效!", "");
byte[] boutdata3 = { 0x50, 0x03, 0x00, 0x03, 0x00, 0x01, 0x79, 0x8B }; //发送烟感传感器数据
serialPort1.Write(boutdata3, 0, 8);
}
if (new_byte1[0] == 80 && new_byte1[1] == 3) //烟感传感器
{
for (int i = 0; i < 5; i++)
crc_data3[i] = (int)new_byte1[i];
int[] crc3 = crc16(crc_data3);
if (crc3[0] == new_byte1[5] && crc3[1] == new_byte1[6])
alarm3(); //显示,红灯报警,绿灯正常
else
MessageBox.Show("校验码错误,数据无效!", "");
}
}

//关闭串口,退出程序
private void button1_Click(object sender, EventArgs e)
{
serialPort1.Close();
Close();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort1.IsOpen) serialPort1.Close();
}
}


运行结果
舞动的代码 2014-11-28
  • 打赏
  • 举报
回复
不知道怎么获取串口的名称呢
Sai950163 2014-11-28
  • 打赏
  • 举报
回复
收到数据都不进行校验的么
ximengni 2014-11-25
  • 打赏
  • 举报
回复
引用 29 楼 zh2305 的回复:
将serialPort1.Write(boutdata2, 0, 8)这些放入前面对应的RECIVE代码中
您说的是28楼的receive()函数吗?
ximengni 2014-11-25
  • 打赏
  • 举报
回复
引用 28 楼 junzhang4008 的回复:
[quote=引用 26 楼 u014059470 的回复:] [quote=引用 14 楼 zcg1041 的回复:] 1 不明白一个串口怎么可以连接三个串口; 2 如果三个串口的数据你可以同时接受到的话, 给三种数据加标识,根据标识进行处理就可以了 3 你的K 没有, 顺序发是你可以控制的; 但是什么时候那边处理完,再给你返回,就不一定是按顺序了
三个传感器的485线并联一起接到232转485上然后接到电脑[/quote] 既然是485并联,还是采用阻塞模式把。因为485 线路原因容易导致数据碰撞

//构建设备对象并放到数组中
while(true)
{
    for( i = 0 ; i < 设备数组个数;i++)
    {
     try
{
        设备[i].send(要数据命令);
        Thread.sleep(500);  //根据线路延迟可以设置一个较为合理的。超过时间没数据认为设备掉线
        Receive()
}
catch
{设备[i]通讯错误}
    }
}
[/quote] 谢谢!请问一下这段代码是放在timer_tick里面吗,那个true指的是timer.enabled?没有用过try catch,继续学习!
ximengni 2014-11-25
  • 打赏
  • 举报
回复
引用 19 楼 q3310017 的回复:
我这边是一个串口接收两个设备的数据,我这里是定义好数据格式的, 第一个那就是 A开头,第二个就是B。我觉得你可以看能不能沟通下,定义下数据格式。遇到程序问题很多时候可以有别的解决方案
这个是传感器的协议自带的数据收发格式,不知道怎么改哇
程序猿老曾 2014-11-25
  • 打赏
  • 举报
回复
貌似有吧!!有一种USB一托四的~~~~一个USB接口接在PC上,然后另一头是4个COM口的!! 但是不知道那日 本 人的系统是怎么做的,就是PCI和USB的卡互换,只要保证PC机上的COM端口号不变的话,他们的系统都能照常运行的!! 可能真的是开了多个线程分别去处理COM3、4、5、6的数据吧
James(Wood) 2014-11-25
  • 打赏
  • 举报
回复
将serialPort1.Write(boutdata2, 0, 8)这些放入前面对应的RECIVE代码中
  • 打赏
  • 举报
回复
引用 26 楼 u014059470 的回复:
[quote=引用 14 楼 zcg1041 的回复:] 1 不明白一个串口怎么可以连接三个串口; 2 如果三个串口的数据你可以同时接受到的话, 给三种数据加标识,根据标识进行处理就可以了 3 你的K 没有, 顺序发是你可以控制的; 但是什么时候那边处理完,再给你返回,就不一定是按顺序了
三个传感器的485线并联一起接到232转485上然后接到电脑[/quote] 既然是485并联,还是采用阻塞模式把。因为485 线路原因容易导致数据碰撞

//构建设备对象并放到数组中
while(true)
{
    for( i = 0 ; i < 设备数组个数;i++)
    {
     try
{
        设备[i].send(要数据命令);
        Thread.sleep(500);  //根据线路延迟可以设置一个较为合理的。超过时间没数据认为设备掉线
        Receive()
}
catch
{设备[i]通讯错误}
    }
}
ximengni 2014-11-24
  • 打赏
  • 举报
回复
引用 11 楼 wanghui0380 的回复:
我想你忽略了一个东西,你是怎么用串口把3个设备连起来滴 正常情况,他中间会用有一个多路采集卡或者多路集线器,而与这种设备东西他一般会用通道概念,你3个传感器才多路集线器里就是3个通道,所以你要做的就是对多路集线器发送带通道标识的命令,而集线器自己负责硬件信号转发
是把三个传感器的485线并在一起接到232转485上面再连的电脑,没有用到集线器吧
ximengni 2014-11-24
  • 打赏
  • 举报
回复
引用 10 楼 junzhang4008 的回复:
异步处理的话,防止沾包
我这种情况是不是只能用异步处理呀
ximengni 2014-11-24
  • 打赏
  • 举报
回复
引用 9 楼 diaodiaop 的回复:
LZ这样不对啊.. 3个东西走一个通道 那么 你如何区分 是哪个东西? 如果你可以设置第一个东西发送AA 第二个BB 第三个CC 不就好办了么.. 如果你不能的话你是没办法区分是哪个东西的.
发送的数据里面第一字节是地址,最后是校验码,都是按照传感器的协议发送,调试的时候发现数组里接收的数据也是对的,只是显示那个语句错后一步,另外如果我再加上一些校验CRC的语句就错后更多
luhr 2014-11-24
  • 打赏
  • 举报
回复
引用 19 楼 q3310017 的回复:
我这边是一个串口接收两个设备的数据,我这里是定义好数据格式的, 第一个那就是 A开头,第二个就是B。我觉得你可以看能不能沟通下,定义下数据格式。遇到程序问题很多时候可以有别的解决方案
而且发数据的设备改这些一般都比较容易
luhr 2014-11-24
  • 打赏
  • 举报
回复
我这边是一个串口接收两个设备的数据,我这里是定义好数据格式的, 第一个那就是 A开头,第二个就是B。我觉得你可以看能不能沟通下,定义下数据格式。遇到程序问题很多时候可以有别的解决方案
ximengni 2014-11-24
  • 打赏
  • 举报
回复
引用 8 楼 junzhang4008 的回复:
如果要想顺序执行,只能采用阻塞模式进行通讯。也就是: 要数据,等待回复,回复响应。 要下一数据。。。。。 你现在属于 要数据,要数据,要数据。由于线路原因,下位返回数据延迟不同,就乱套了。 解决方法: 1、采用阻塞模式单步发送 (通讯效率慢) 2、协议中增加类似于命令id 的字节。你发送的id是多少,回来的信息中就包含这个id。进行异步处理
谢谢,明白我的问题所在了,您说的单步发送是不是就不能用timer自动发了?第二个解决方法,协议是传感器固定的发送什么接收什么不知道改不改的了哇
ximengni 2014-11-24
  • 打赏
  • 举报
回复
引用 7 楼 ilovedxt 的回复:
[quote=引用 4 楼 u014059470 的回复:] [quote=引用 1 楼 ilovedxt 的回复:] 既然是收数据有问题,那你应该把收数据的代码也贴上来看看
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { switch (k) { case 1: int byte_num1 = serialPort1.Read(new_byte1, 0, 4); this.Invoke(new EventHandler(DisplayText1)); //显示红绿灯报警 break; case 2: int byte_num2 = serialPort1.Read(new_byte2, 0, 7); this.Invoke(new EventHandler(DisplayText2)); //显示温度 break; case 3: int byte_num3 = serialPort1.Read(new_byte3, 0, 7); this.Invoke(new EventHandler(DisplayText3)); //显示红绿灯报警 break; } 收的代码是这个,按步执行发现他是k=1执行第一组发送和接收,然后还没有执行显示就回去发送k=2,再回来显示第一个报警,这样都岔开了,第二轮的时候就会把第一个显示漏掉,试过发送完一个命令关闭timer然后接收一个再打开,这样是把接收和显示都能执行完,但是就只能执行一次,打开timer语句没有反应[/quote] 这个K是啥东西,而且你用一个串口收3个传感器,如果每个传感器回传的值没有一定的标志,你是区别不开是哪台设备的 ,所以你这样是肯定收数据时错误的 要实现你这个的功能,我觉得要定义传感器回传数据的格式, 比如 ,传感器1返回的数据格式是 AA 01 温度数据 EE 其中AA是数据开头,EE是数据结尾, 01 代表传感器1 ,温度数据才是你真正需要的数据,同理 传感器2应该返回 AA 02 温度数据 EE 这样在你收到一个数据帧的时候,可以根据这个数据的第二位判断是哪个传感器的数据,在做进一步处理[/quote] k就是发送里面那个k,为了让三个循环发的,k>3之后再等于1,三个返回数据格式是固定的,最前面都是地址,三个地址不同,最后两个字节是CRC校验
ximengni 2014-11-24
  • 打赏
  • 举报
回复
引用 15 楼 skywshing 的回复:
[quote=引用 7 楼 ilovedxt 的回复:] [quote=引用 4 楼 u014059470 的回复:] [quote=引用 1 楼 ilovedxt 的回复:] 既然是收数据有问题,那你应该把收数据的代码也贴上来看看
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { switch (k) { case 1: int byte_num1 = serialPort1.Read(new_byte1, 0, 4); this.Invoke(new EventHandler(DisplayText1)); //显示红绿灯报警 break; case 2: int byte_num2 = serialPort1.Read(new_byte2, 0, 7); this.Invoke(new EventHandler(DisplayText2)); //显示温度 break; case 3: int byte_num3 = serialPort1.Read(new_byte3, 0, 7); this.Invoke(new EventHandler(DisplayText3)); //显示红绿灯报警 break; } 收的代码是这个,按步执行发现他是k=1执行第一组发送和接收,然后还没有执行显示就回去发送k=2,再回来显示第一个报警,这样都岔开了,第二轮的时候就会把第一个显示漏掉,试过发送完一个命令关闭timer然后接收一个再打开,这样是把接收和显示都能执行完,但是就只能执行一次,打开timer语句没有反应[/quote] 看你问题的描述是不是在发送第二个命令之前Thread.Sleep一下可以避免,可以试试。 如果对每次检查的间隔要求不是很严格且设备返回数据较快的话,直接放在Timer的Tick事件中接受返回值就好了,发一个,接受一个,显示一个,最好先把timer的Enable false处理完再True 不一定准确,仅提供一个解决思路。[/quote] 谢谢!这个Thread.Sleep是不是让第二个数据等一下再发,这样跟把timer的发送频率调一下效果一样吧应该,另外我在发送完设置timer.enabled=false,然后处理完再true,就不再发送下一条了不知道怎么回事。
ximengni 2014-11-24
  • 打赏
  • 举报
回复
引用 14 楼 zcg1041 的回复:
1 不明白一个串口怎么可以连接三个串口; 2 如果三个串口的数据你可以同时接受到的话, 给三种数据加标识,根据标识进行处理就可以了 3 你的K 没有, 顺序发是你可以控制的; 但是什么时候那边处理完,再给你返回,就不一定是按顺序了
三个传感器的485线并联一起接到232转485上然后接到电脑
ximengni 2014-11-24
  • 打赏
  • 举报
回复
引用 13 楼 yalunwang123 的回复:
不能开3个线程来接收吗
不好意思,刚刚入门,还不太清楚线程是怎么回事,也不知道怎么开三个线程哇
skywshing 2014-11-23
  • 打赏
  • 举报
回复
引用 7 楼 ilovedxt 的回复:
[quote=引用 4 楼 u014059470 的回复:] [quote=引用 1 楼 ilovedxt 的回复:] 既然是收数据有问题,那你应该把收数据的代码也贴上来看看
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { switch (k) { case 1: int byte_num1 = serialPort1.Read(new_byte1, 0, 4); this.Invoke(new EventHandler(DisplayText1)); //显示红绿灯报警 break; case 2: int byte_num2 = serialPort1.Read(new_byte2, 0, 7); this.Invoke(new EventHandler(DisplayText2)); //显示温度 break; case 3: int byte_num3 = serialPort1.Read(new_byte3, 0, 7); this.Invoke(new EventHandler(DisplayText3)); //显示红绿灯报警 break; } 收的代码是这个,按步执行发现他是k=1执行第一组发送和接收,然后还没有执行显示就回去发送k=2,再回来显示第一个报警,这样都岔开了,第二轮的时候就会把第一个显示漏掉,试过发送完一个命令关闭timer然后接收一个再打开,这样是把接收和显示都能执行完,但是就只能执行一次,打开timer语句没有反应[/quote] 看你问题的描述是不是在发送第二个命令之前Thread.Sleep一下可以避免,可以试试。 如果对每次检查的间隔要求不是很严格且设备返回数据较快的话,直接放在Timer的Tick事件中接受返回值就好了,发一个,接受一个,显示一个,最好先把timer的Enable false处理完再True 不一定准确,仅提供一个解决思路。
加载更多回复(15)

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧