SerialPort接收函数正确完全接收的问题

jiangnanxiong 2012-07-10 01:48:03
刚刚那个帖子我说的不清楚,现在我新开一个帖子,把我的问题说清楚了,希望大侠们能够很详细的回答我的问题,谢谢;

问题一:
问题如下:
我想把从SerialPort接收到的数放入Textbox中,但总是收不全(比如共10个字节,却总是收了5、6个,后面的就丢了)
我是这么写的(抄了修改的):
void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int n = comm.BytesToRead;
byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
comm.Read(buf, 0, n);//读取缓冲数据
builder.Length = 0;//清除字符串构造器的内容
//因为要访问ui资源,所以需要使用invoke方式同步ui。
this.Invoke((EventHandler)(delegate
{
//判断是否是显示为16禁止
if (checkBoxhexreceive.Checked)
{
//依次的拼接出16进制字符串
foreach (byte b in buf)
{
builder.Append(b.ToString("X2") + " ");
}
}
else
{
//直接按ASCII规则转换成字符串
builder.Append(Encoding.ASCII.GetString(buf));
}
//追加的形式添加到文本框末端,并滚动到最后。
textBoxreceive.Text += builder.ToString() + "\r\n";
}
}

采用RS485自发自收,每次发送相同的命令,收到在textBoxreceive中的值却不一样,(假如说全的时候是10个字节),有些时候收全了,有些时候只收了8个字节,有些时候只收了6个字节等等等等。。。。

我敢肯定是由于代码的第一行“int n = comm.BytesToRead;”在作怪,应为这里的n就决定了我要读多少字节数。

于是乎我请教了一个人,他告诉我一个方法:
采用ModBUS协议的通讯,可以这样做(比如说收到的数据将是12 34 01 78 90 AB CD):先收第一个数据(判断其是不是12),在收第二个数据(判断其是不是34),在收第三个数据(此处为01);
重点来了:根据ModBus协议来讲,第三位(此处为01)即代表了将要收到的数据时多少位(01:代表了数据为2个字节“78 90”,CRC为2个字节“AB CD”),这样我就知道了在收了第三个数据后还要再收多少个字节了,这样我就不可能丢数据了,这样问题就解决了

上面的思想是好的,但是用代码我根本不会写啊,如果用代码来实现,应该怎么写呢(上面的“先收到第三个数据,判断还要收多少位,从而保证收到的数据是完整的”),应该怎么写呢?

请尽量给代码,谢谢,如果有更好的方法,请指出,谢谢
我已经想了2天了,没有答案;


问题二:
我定义了一个串口接收函数
void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
。。。。。。
}

我想在别的地方调用它,怎么做呢?

比如是在一个控件触发的时候调用它:
private void button1_Click_1(object sender, EventArgs e)
{
“调用DataReceived”函数
}

怎么做啊??
或者说这个不行,换成DataRead(串口读函数),怎么做呢??

请教了!


请高手们不要一笔带过,我是个新手,尽量将详细一点,尽量给代码

谢谢了
...全文
553 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
love_521_qian 2013-11-18
  • 打赏
  • 举报
回复
private void SerialPort_DataReceived( object sender , SerialDataReceivedEventArgs e ) { byte rev = (byte)PortHelper.SerialPort.ReadByte (); switch ( PortHelper.RecFlag ) { case 0: { if ( 0x7E == rev ) { Net.RecLen = 0; Net.DataReceiveBuffer[Net.RecLen++] = rev; PortHelper.RecFlag = 1; } else { PortHelper.RecFlag = 0; } break; } case 1: { if ( 0x7E == rev ) { Net.DataReceiveBuffer[Net.RecLen++] = rev; PortHelper.RecFlag = 2; } else { PortHelper.RecFlag = 0; } break; } case 2: { Net.DataReceiveBuffer[Net.RecLen++] = rev; if ( 13 == Net.RecLen ) { Len = (UInt16)( ( Net.DataReceiveBuffer[11] & 0x0F * 0x100 ) + ( Net.DataReceiveBuffer[12] ) ); PortHelper.RecFlag = 3; } break; } case 3: { Net.DataReceiveBuffer[Net.RecLen++] = rev; if ( Len + 17 == Net.RecLen ) { GlobalVariable.TaskFlag = 0x40; PortHelper.RecFlag = 0; } break; } } 这就是方法!不过用的协议不一样!其中包头两个字节,数据长度用的是12位的,也就是Net.DataReceiveBuffer[11] & 0x0F * 0x100 ) + ( Net.DataReceiveBuffer[12] 然后再去判断是否接受完了!再去校验什么的!
love_521_qian 2013-11-18
  • 打赏
  • 举报
回复
private void SerialPort_DataReceived( object sender , SerialDataReceivedEventArgs e ) { byte rev = (byte)PortHelper.SerialPort.ReadByte (); switch ( PortHelper.RecFlag ) { case 0: { if ( 0x7E == rev ) { Net.RecLen = 0; Net.DataReceiveBuffer[Net.RecLen++] = rev; PortHelper.RecFlag = 1; } else { PortHelper.RecFlag = 0; } break; } case 1: { if ( 0x7E == rev ) { Net.DataReceiveBuffer[Net.RecLen++] = rev; PortHelper.RecFlag = 2; } else { PortHelper.RecFlag = 0; } break; } case 2: { Net.DataReceiveBuffer[Net.RecLen++] = rev; if ( 13 == Net.RecLen ) { Len = (UInt16)( ( Net.DataReceiveBuffer[11] & 0x0F * 0x100 ) + ( Net.DataReceiveBuffer[12] ) ); PortHelper.RecFlag = 3; } break; } case 3: { Net.DataReceiveBuffer[Net.RecLen++] = rev; if ( Len + 17 == Net.RecLen ) { GlobalVariable.TaskFlag = 0x40; PortHelper.RecFlag = 0; } break; } }
xiaomingauk 2012-08-08
  • 打赏
  • 举报
回复 1
嘎嘎,串口收数据收不全要等待下再接收,可以用Thread.sleep
jiangnanxiong 2012-07-10
  • 打赏
  • 举报
回复
我试了comm_DataReceived(null,null)
发现如下:
我在原DataReceive函数里定义如下:
void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
comm.Read(buf, 0, n);//读取缓冲数据
}
注:其中的buf是一个全局变量;

调用时用comm_DataReceived(null,null)

我用中断模式调试,发现转去执行DataReceive函数时,buf数组为有数据为正确的;
但是执行完DataReceive函数跳回来时,buf数组就自动清空了,数据全没了!!

这是为何??
缭绕飘渺 2012-07-10
  • 打赏
  • 举报
回复
那你把参数类型改为SerialDataReceivedEventArgs不就完了吗
或者直接comm_DataReceived(null,null)试试
serialport 有个属性ReceivedBytesThreshold = 1;就是串口有1个字节
就触发接收事件进行数据读取,可以根据你的需要设置
jiangnanxiong 2012-07-10
  • 打赏
  • 举报
回复
我说的就是SerialPort的DataReceive事件!!!!!!

我就是不知道怎么能够去断定串口对象要达到多少字节才能触发该事件,应为每次来的数据字节数都是不一样的,要定义的话在哪里定义???

我觉得还是要靠上述的Modbus协议的某位数据来判断串口的接收字节数;可是该怎么写???

另外
我根据mervyn807的回复,用comm_DataReceived(this,EventArgs.Empty)调用该接收函数,爆出错误“无法从System.EventArgs转换为System.IO.Ports.SerialDataReceivedEventArgs)
这是为什么啊?

bdmh 2012-07-10
  • 打赏
  • 举报
回复
seralport就有DataReceived事件啊,你可以动态绑定,也可以在设计期就制定好
缭绕飘渺 2012-07-10
  • 打赏
  • 举报
回复
comm_DataReceived如果你这个是serialport的接收事件的话
他是会自动触发的,要看你的串口对象设置的多少字节触发该事件
你这样读也可以说没有问题,不过串口数据的处理一般是缓存,分析
第二个问题
调用comm_DataReceived(this,EventArgs.empty)
串行通信主要用了C# 中的SerialPort类 开发语言:C# 程序运行环境:.NetFrameWork 2.0以上版本 开发工具:Visual Studio 2008 C# System.IO.Ports命名空间包含了控制串口重要的SerialPort类,该类提供了同步 I/O 和事件驱动的 I/O、对管脚和中断状态的访问以 及对串行驱动程序属性的访问,所以在程序代码起始位置需加入Using System.IO.Ports。 串口的通讯参数 a.通讯端口号 [PortName]属性获取或设置通信端口,包括但不限于所有可用的 COM 端口,请注意该属性返回类型为String。通常情况下,PortName正 常返回的值为CO1、COM2……,SerialPort类最大支持的端口数突破了CommPort控件中CommPort属性不能超过16的限止,大大方便了用户 串口设备的配置。 b. 通讯格式 SerialPort类对分别用[BaudRate]、[Parity]、[DataBits]、[StopBits]属性设置通讯格式中的波特率、数据位、停止位和校验位,其 中[Parity]和[StopBits]分别是枚举类型Parity、StopBits,Parity类型中枚举了Odd(奇)、Even(偶)、Mark、None、Space,Parity枚 举了None、One、OnePointFive、Two。 SerialPort类提供了七个重载的构造函数,既可以对已经实例化的SerialPort对象设置上述相关属性的值,也可以使用指定的端口名称 、波特率和奇偶校验位数据位和停止位直接初始化 SerialPort 类的新实例。 3.串口的打开和关闭 SerialPort类是调用类的Open()和Close()方法。 4. 数据的发送和读取 Serial类调用重载的Write和WriteLine方法发送数据,其中WriteLine可发送字符串并在字符串末尾加入换行符,读取串口缓冲区的方法 有许多,其中除了ReadExisting和ReadTo,其余的方法都是同步调用,线程被阻塞直到缓冲区有相应的数据或大于ReadTimeOut属性设定 的时间值后,引发ReadExisting异常。 5.DataReceived事件 该事件类似于MSComm控件中的OnComm事件,DataReceived事件在接收到了[ReceivedBytesThreshold]设置的字符个数或接收到了文件结 束字符并将其放入了输入缓冲区时被触发。其中[ReceivedBytesThreshold]相当于MSComm控件的[Rthreshold]属性,该事件的用法与 MsComm控件的OnComm事件在CommEvent为comEvSend和comEvEof时是一致的
/*=============================================================================================================================================================================================================*/ /*======================================================================================================== 如何使用? 1.引入DLL文件,引入头本文件,就已声明了导入函数 //要引入DLL的方法 //1、把SERIALPORTDLL1.dll和SERIALPORTDLL1.lib拷贝到工程目录下 //2、然后在 工程->设置->连接->分类->常规->对象/库模块输入:SERIALPORTDLL1.lib 2.调用Init初始化串口 3.调用SetReceiveFuntion(&ReveveChar);指定接收和处理数据的函数 4.实现void ReveveChar(WPARAM data, LPARAM port)这个函数 5.调用SendData发送数据 6.ClosePort(); ========================================================================================================*/ /*======================================================================================================= 函 数 名: Init 功 能: 打开并初始化串口 参数说明: UINT port //端口号 UINT baud //波特率 char parity = 'N' //校验位,默认为无校验位 UINT databits = 8 //数据位,一个字节的位数,默认为8位 UINT stopbit = 1 //停止位,默认为1位 使用例子: Init(2,9600); //打开串口2,波特率9600,其它值为默认值(无奇偶校验、数据位为8、停止位为1) Init(4,4800,'N',10);//打开串口4,波特率为4800,无奇偶校验,数据位为10,(停止位默认为1) ... ... ======================================================================================================*/ extern "C" __declspec(dllimport) void Init( UINT port, //端口号 UINT baud, //波特率 char parity = 'N', //校验位 UINT databits=8 , //数据位 UINT stopbit=1 //停止位 ); /*========================================================================================================== 函 数 名: SendData 功 能: 向串口发送数据,使用之前必须先调用Init函数初始化串口 参数说明: char data[] //要发送的数据 int datalen //发送数据的长度 使用例子: 例1: char a[] = {0x01,0x03,0x00,0x03,0x00,0x02,0x34,0x0B};//准备要发送的十六进制:01 03 00 03 00 02 34 0B SendData(a,8); //向串口发送十六进制数:01 03 00 03 00 02 34 0B 例2: char a[] = "$01001000020110*" //准备要发送的字符串$01001000020110* SendData(a,16); //向串口发送:$01001000020110* ============================================================================================================*/ extern "C" __declspec(dllimport) void SendData( char data[], int datalen ); /*=================================================================================================================== 函 数 名: SetReceiveFuntion 功 能: 指定接收数据的函数,指定好后,串口接收缓冲区每有一个字节的数据时会自动触发和调用所指定的函数 参数说明: a、参数需要一个函数指针, b、该指针指向一个用于接收数据的函数(该函数由用户自己声明、定义和实现), c、用户定义该函数时必须要满足一下条件: 1、该函数为全局函数 2、返回值为void 3、形参为两个,类型为:形参为UINT,LONG 使用例子: 1、void ReveveChar(WPARAM data, LPARAM port);//声明一个用于接收数据的全局函数,本头文件已默认声明这个函数,用户无需再次声明 2、 CString strTemp; void ReveveChar(WPARAM data, LPARAM port)//实现这个接收数据并处理数据的函数 { static int RecevCount = 0; //用于接收字节数的计数 char str[512] = ""; sprintf(str,"%02x",data); //接收十六进制数,并格式化为字符形式 strTemp += str; RecevCount++; if (RecevCount >= 8) //接收到数据长度等于了协议长度,则开始进行协议的处理 { RecevCount = 0; strTemp = "接收到得数据协议为" + strTemp; AfxMessageBox(strTemp); strTemp = ""; } } 3、SetReceiveFuntion(&ReveveChar);//指定void ReveveChar(WPARAM data, LPARAM port);该函数接收和处理数据 =========================================================================================================================*/ extern "C" __declspec(dllimport) void SetReceiveFuntion(void (*pfCallBack)(WPARAM data, LPARAM port)); /*================================================= 函 数 名: ClosePort 功 能: 关闭由Init函数打开的串口 参数说明: 无需参数 调用例子: ClosePort(); =================================================*/ extern "C" __declspec(dllimport) void ClosePort(); /*=============================================================================================================== 函 数 名: ReveveChar 功 能: 每当串口接收到一个字符(字节)时就会自动触发和调用这个函数, 用户只需实现不必也不能去亲自调用, 注意:打开串口后,必须要调用SetReceiveFuntion(ReveveChar) 参数说明: data //接收到的是串口中的一个字节数据 port //端口号,表明是哪个端口接收到的数据 调用例子: 只要实现,无需自己调用。 实现处理数据可参考如下简单的例子,思想是边接收数据边处理 void ReveveChar(WPARAM data, LPARAM port) { static int RecevCount = 0; //用于接收字节数的计数 char str[512] = ""; sprintf(str,"%02x",data); //接收十六进制数,并格式化为字符形式 strTemp += str; RecevCount++; if (RecevCount >= 8) //接收到数据长度等于了协议长度,则开始进行协议的处理 { RecevCount = 0; strTemp = "接收到得数据协议为" + strTemp; AfxMessageBox(strTemp); strTemp = ""; } } 说明:本函数并非为导出函数,写在这里仅仅是起到声明一个函数的作用,用户也可以删除掉,自己声明和实现! ===============================================================================================================*/ void ReveveChar(WPARAM data, LPARAM port);
呈上头文件部分信息 注释的也很详细的 COPYRIGHT NOTICE Copyright c 2009 华中科技大学tickTick Group (版权声明) All rights reserved @file SerialPort h @brief 串口通信类头文件 本文件完成串口通信类的声明 @version 1 0 @author 卢俊 @E mail:lujun hust@gmail com @date 2010 03 19 修订说明: #ifndef SERIALPORT H #define SERIALPORT H #include <Windows h> 串口通信类 本类实现了对串口的基本操作 例如监听发到指定串口的数据 发送指定数据到串口 class CSerialPort { public: CSerialPort void ; CSerialPort void ; public: 初始化串口函数 @param: UINT portNo 串口编号 默认值为1 即COM1 注意 尽量不要大于9 @param: UINT baud 波特率 默认为9600 @param: char parity 是否进行奇偶校验 "Y"表示需要奇偶校验 "N"表示不需要奇偶校验 @param: UINT databits 数据位的个数 默认值为8个数据位 @param: UINT stopsbits 停止位使用格式 默认值为1 @param: DWORD dwCommEvents 默认为EV RXCHAR 即只要收发任意一个字符 则产生一个事件 @return: bool 初始化是否成功 @note: 在使用其他本类提供的函数前 请先调用本函数进行串口的初始化       n本函数提供了一些常用的串口参数设置 若需要自行设置详细的DCB参数 可使用重载函数 n本串口类析构时会自动关闭串口 无需额外执行关闭串口 @see: bool InitPort UINT portNo 1 UINT baud CBR 9600 char parity "N" UINT databits 8 UINT stopsbits 1 DWORD dwCommEvents EV RXCHAR ; 串口初始化函数函数提供直接根据DCB参数设置串口参数 @param: UINT portNo @param: const LPDCB & plDCB @return: bool 初始化是否成功 @note: 本函数提供用户自定义地串口初始化参数 @see: bool InitPort UINT portNo const LPDCB& plDCB ; 开启监听线程 本监听线程完成对串口数据的监听 并将接收到的数据打印到屏幕输出 @return: bool 操作是否成功 @note: 当线程已经处于开启状态时 返回flase @see: bool OpenListenThread ; 关闭监听线程 @return: bool 操作是否成功 @note: 调用本函数后 监听串口的线程将会被关闭 @see: bool CloseListenTread ; 向串口写数据 将缓冲区中的数据写入到串口 @param: unsigned char pData 指向需要写入串口的数据缓冲区 @param: unsigned int length 需要写入的数据长度 @return: bool 操作是否成功 @note: length不要大于pData所指向缓冲区的大小 @see: bool WriteData unsigned char pData unsigned int length ; 获取串口缓冲区中的字节数 @return: UINT 操作是否成功 @note: 当串口缓冲区中无数据时 返回0 @see: UINT GetBytesInCOM ; 读取串口接收缓冲区中一个字节的数据 @param: char & cRecved 存放读取数据的字符变量 @return: bool 读取是否成功 @note: @see: bool ReadChar char &cRecved ; private: 打开串口 @param: UINT portNo 串口设备号 @return: bool 打开是否成功 @note: @see: bool openPort UINT portNo ; 关闭串口 @return: void 操作是否成功 @note: @see: void ClosePort ; 串口监听线程 监听来自串口的数据和信息 @param: void pParam 线程参数 @return: UINT WINAPI 线程返回值 @note: @see: static UINT WINAPI ListenThread void pParam ; private: 串口句柄 HANDLE m hComm; 线程退出标志变量 static bool s bExit; 线程句柄 volatile HANDLE m hListenThread; 同步互斥 临界区保护 CRITICAL SECTION m csCommunicationSync; < 互斥操作串口 }; #endif SERIALPORT H ">呈上头文件部分信息 注释的也很详细的 COPYRIGHT NOTICE Copyright c 2009 华中科技大学tickTick Group (版权声明) All rights reserved @file SerialPort h @brief [更多]

110,566

社区成员

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

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

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