初学串口通信,单个温度传感器的通信已经没问题,用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;
}
—- 5分
—- 10分
你可以按序发送,但你不能预计传感器那个先回传数据啊。
你想实现这个效果,就是先给第一个传感器发送数据,等待数据返回后,
再给第二个传感器发送,等待返回数据后,再给第三个发送。
—-
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语句没有反应
—-
拖3个serialport控件吗?可是只有一个串口,三个传感器都通过一个串口发送接收数据
—-
我也是这样想的,想发送一个然后关掉timer接收完第一个再打开继续发送,但是调试的时候发现发送完数据timer可以关掉但是接受完打不开了就
—-
这个K是啥东西,而且你用一个串口收3个传感器,如果每个传感器回传的值没有一定的标志,你是区别不开是哪台设备的 ,所以你这样是肯定收数据时错误的
要实现你这个的功能,我觉得要定义传感器回传数据的格式, 比如 ,传感器1返回的数据格式是 AA 01 温度数据 EE 其中AA是数据开头,EE是数据结尾, 01 代表传感器1 ,温度数据才是你真正需要的数据,同理 传感器2应该返回 AA 02 温度数据 EE
这样在你收到一个数据帧的时候,可以根据这个数据的第二位判断是哪个传感器的数据,在做进一步处理
—- 10分
要数据,等待回复,回复响应。 要下一数据。。。。。
你现在属于
要数据,要数据,要数据。由于线路原因,下位返回数据延迟不同,就乱套了。
解决方法:
1、采用阻塞模式单步发送 (通讯效率慢)
2、协议中增加类似于命令id 的字节。你发送的id是多少,回来的信息中就包含这个id。进行异步处理
—-
3个东西走一个通道 那么 你如何区分 是哪个东西?
如果你可以设置第一个东西发送AA 第二个BB 第三个CC 不就好办了么..
如果你不能的话你是没办法区分是哪个东西的.
—-
—-
正常情况,他中间会用有一个多路采集卡或者多路集线器,而与这种设备东西他一般会用通道概念,你3个传感器才多路集线器里就是3个通道,所以你要做的就是对多路集线器发送带通道标识的命令,而集线器自己负责硬件信号转发
—-
—-
—-
2 如果三个串口的数据你可以同时接受到的话,
给三种数据加标识,根据标识进行处理就可以了
3 你的K 没有, 顺序发是你可以控制的;
但是什么时候那边处理完,再给你返回,就不一定是按顺序了
—- 5分
既然是收数据有问题,那你应该把收数据的代码也贴上来看看
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语句没有反应
看你问题的描述是不是在发送第二个命令之前Thread.Sleep一下可以避免,可以试试。
如果对每次检查的间隔要求不是很严格且设备返回数据较快的话,直接放在Timer的Tick事件中接受返回值就好了,发一个,接受一个,显示一个,最好先把timer的Enable false处理完再True不一定准确,仅提供一个解决思路。
—-
—-
既然是收数据有问题,那你应该把收数据的代码也贴上来看看
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语句没有反应
这个K是啥东西,而且你用一个串口收3个传感器,如果每个传感器回传的值没有一定的标志,你是区别不开是哪台设备的 ,所以你这样是肯定收数据时错误的
要实现你这个的功能,我觉得要定义传感器回传数据的格式, 比如 ,传感器1返回的数据格式是 AA 01 温度数据 EE 其中AA是数据开头,EE是数据结尾, 01 代表传感器1 ,温度数据才是你真正需要的数据,同理 传感器2应该返回 AA 02 温度数据 EE
这样在你收到一个数据帧的时候,可以根据这个数据的第二位判断是哪个传感器的数据,在做进一步处理
k就是发送里面那个k,为了让三个循环发的,k>3之后再等于1,三个返回数据格式是固定的,最前面都是地址,三个地址不同,最后两个字节是CRC校验
—-
如果要想顺序执行,只能采用阻塞模式进行通讯。也就是:
要数据,等待回复,回复响应。 要下一数据。。。。。
你现在属于
要数据,要数据,要数据。由于线路原因,下位返回数据延迟不同,就乱套了。
解决方法:
1、采用阻塞模式单步发送 (通讯效率慢)
2、协议中增加类似于命令id 的字节。你发送的id是多少,回来的信息中就包含这个id。进行异步处理
谢谢,明白我的问题所在了,您说的单步发送是不是就不能用timer自动发了?第二个解决方法,协议是传感器固定的发送什么接收什么不知道改不改的了哇
—-
第一个那就是 A开头,第二个就是B。我觉得你可以看能不能沟通下,定义下数据格式。遇到程序问题很多时候可以有别的解决方案
—-
我这边是一个串口接收两个设备的数据,我这里是定义好数据格式的,
第一个那就是 A开头,第二个就是B。我觉得你可以看能不能沟通下,定义下数据格式。遇到程序问题很多时候可以有别的解决方案
而且发数据的设备改这些一般都比较容易
—-
LZ这样不对啊..
3个东西走一个通道 那么 你如何区分 是哪个东西?
如果你可以设置第一个东西发送AA 第二个BB 第三个CC 不就好办了么..
如果你不能的话你是没办法区分是哪个东西的.
发送的数据里面第一字节是地址,最后是校验码,都是按照传感器的协议发送,调试的时候发现数组里接收的数据也是对的,只是显示那个语句错后一步,另外如果我再加上一些校验CRC的语句就错后更多
—-
异步处理的话,防止沾包
我这种情况是不是只能用异步处理呀
—-
我想你忽略了一个东西,你是怎么用串口把3个设备连起来滴
正常情况,他中间会用有一个多路采集卡或者多路集线器,而与这种设备东西他一般会用通道概念,你3个传感器才多路集线器里就是3个通道,所以你要做的就是对多路集线器发送带通道标识的命令,而集线器自己负责硬件信号转发
是把三个传感器的485线并在一起接到232转485上面再连的电脑,没有用到集线器吧
—-
—-
不能开3个线程来接收吗
不好意思,刚刚入门,还不太清楚线程是怎么回事,也不知道怎么开三个线程哇
—-
1 不明白一个串口怎么可以连接三个串口;
2 如果三个串口的数据你可以同时接受到的话,
给三种数据加标识,根据标识进行处理就可以了
3 你的K 没有, 顺序发是你可以控制的;
但是什么时候那边处理完,再给你返回,就不一定是按顺序了
三个传感器的485线并联一起接到232转485上然后接到电脑
—-
既然是收数据有问题,那你应该把收数据的代码也贴上来看看
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语句没有反应
看你问题的描述是不是在发送第二个命令之前Thread.Sleep一下可以避免,可以试试。
如果对每次检查的间隔要求不是很严格且设备返回数据较快的话,直接放在Timer的Tick事件中接受返回值就好了,发一个,接受一个,显示一个,最好先把timer的Enable false处理完再True不一定准确,仅提供一个解决思路。
谢谢!这个Thread.Sleep是不是让第二个数据等一下再发,这样跟把timer的发送频率调一下效果一样吧应该,另外我在发送完设置timer.enabled=false,然后处理完再true,就不再发送下一条了不知道怎么回事。
—-
1 不明白一个串口怎么可以连接三个串口;
2 如果三个串口的数据你可以同时接受到的话,
给三种数据加标识,根据标识进行处理就可以了
3 你的K 没有, 顺序发是你可以控制的;
但是什么时候那边处理完,再给你返回,就不一定是按顺序了三个传感器的485线并联一起接到232转485上然后接到电脑
既然是485并联,还是采用阻塞模式把。因为485 线路原因容易导致数据碰撞
//构建设备对象并放到数组中 while(true) { for( i = 0 ; i < 设备数组个数;i++) { try { 设备[i].send(要数据命令); Thread.sleep(500); //根据线路延迟可以设置一个较为合理的。超过时间没数据认为设备掉线 Receive() } catch {设备[i]通讯错误} } }
—- 5分
—-
但是不知道那日 本 人的系统是怎么做的,就是PCI和USB的卡互换,只要保证PC机上的COM端口号不变的话,他们的系统都能照常运行的!!
可能真的是开了多个线程分别去处理COM3、4、5、6的数据吧
—-
我这边是一个串口接收两个设备的数据,我这里是定义好数据格式的,
第一个那就是 A开头,第二个就是B。我觉得你可以看能不能沟通下,定义下数据格式。遇到程序问题很多时候可以有别的解决方案
这个是传感器的协议自带的数据收发格式,不知道怎么改哇
—-
1 不明白一个串口怎么可以连接三个串口;
2 如果三个串口的数据你可以同时接受到的话,
给三种数据加标识,根据标识进行处理就可以了
3 你的K 没有, 顺序发是你可以控制的;
但是什么时候那边处理完,再给你返回,就不一定是按顺序了三个传感器的485线并联一起接到232转485上然后接到电脑
既然是485并联,还是采用阻塞模式把。因为485 线路原因容易导致数据碰撞
//构建设备对象并放到数组中 while(true) { for( i = 0 ; i < 设备数组个数;i++) { try { 设备[i].send(要数据命令); Thread.sleep(500); //根据线路延迟可以设置一个较为合理的。超过时间没数据认为设备掉线 Receive() } catch {设备[i]通讯错误} } }
谢谢!请问一下这段代码是放在timer_tick里面吗,那个true指的是timer.enabled?没有用过try catch,继续学习!
—-
将serialPort1.Write(boutdata2, 0, 8)这些放入前面对应的RECIVE代码中
您说的是28楼的receive()函数吗?
—-
—-
—-
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(); } }
运行结果