分享好友 百科头条首页 百科头条分类 切换频道

如何用API函数实现WINDOWS下的串口写程

2023-07-27 23:14810网络

用API函数实现Windows下的串行通讯

冯华亮 2002年4月 四川·电子科大

以往的DOS系统是通过DOS中断和BIOS中断向用户提供串行接口的通讯能力。在Windows环境下,C++的开发工具既没有提供象DOS和BIOS

中那样专门的串行通讯控制方法,也不允许用户直接控制串口的中断。

为了保证资源共享,Windows系统完全接管了各种硬件资源,使用中断来控制端口将破坏系统的多任务性,使系统的稳定性受到影响。

但Windows同时也提供了功能强大的API函数使用户能间接的控制串行通讯。

1、实现串行通讯的相关API函数

API函数不仅提供了打开和读写通讯端口的操作方法,还提供了名目繁多的函数以支持对串行通讯的各种操作。常用函数及作用如表5-1所示。

表5-1 常用串行通讯API函数及其作用

函数名 作用

CreateFile 打开串口

GetCommState 检测串口设置

SetCommState 设置串口

BuilderCommDCB 用字符串中的值来填充设备控制块

GetCommTimeouts 检测通信超时设置

SetCommTimeouts 设置通信超时参数

SetCommMask 设定被监控事件

WaitCommEvent 等待被监控事件发生

WaitForMultipleObjects 等待多个被监测对象的结果

WriteFile 发送数据

ReadFile 接收数据

GetOverlappedResult 返回最后重叠(异步)操作结果

PurgeComm 清空串口缓冲区,退出所有相关操作

ClearCommError 更新串口状态结构体,并清除所有串口硬件错误

CloseHandle 关闭串行口

2、打开串口

函数CreateFile原本用于打开文件,但它同样可用于打开一个通信端口。与系统中其他对象一样,通信端口也是用句柄来标识的。

CreateFile函数返回被操作的通信端口句柄,其调用方法如下:

HANDLE CreateFile (

LPCTSTR lpFileName, //指向文件名字符串的指针

DWORD dwDesireAccess, //操作模式

DWORD dwShareMode,  //共享方式

LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针

DWORD dwCreationDistribution, //文件建立方式

DWORD dwFlagsAndAttributes //文件属性

HANDLE hTemplateFile //模板文件句柄

)

lpFileName:指向一个以NULL结束的字符串,该串指定了要创建、打开或截断的文件、管道、通信源、磁盘设备或控制台的名字。

当用CreateFile打开串口时,这个参数可用“COM1”指定串口1,用“COM2”指定串口2,依此类推。

dwDesireAccess: 指定对文件访问的类型,该参数可以为GENERIC_READ(指定对该文件的读访问权)

或ENERIC_WRITE(指定该文件的写访问权)两个值之一或同时为为这两个值。用ENERIC_READ|GENERIC_WRITE则指定可对串口进行读写

dwShareMode:指定此文件可以怎样被共享。因为串行口不支持任何共享模式,所以dwShareMode必须设为0

lpSecurityAttributes定义安全属性,一般不用,可设为NULL。Win 9x下该参数被忽略;

dwCreationDistribution定义文件创建方式, 对串口必须设为OPEN_EXISTING,表示打开已经存在的文件;

dwFlagsAndAttributes为该文件指定定义文件属性和标志,这个程序中设为FILE_FLAG_OVERLAPPED,表示异步通信方式;

hTemplateFile 指向一个模板文件的句柄,串口无模板可言,设为NULL。在 Windows 9x下该参数必须为NULL。

用异步读写方式打开串口1的函数调用如下:

m_hComm = CreateFile(“COM1”,//打开串口1

GENERIC_READ | GENERIC_WRITE, //读写方式

0, //不能共享

NULL, //不用安全结构

OPEN_EXISTING, //打开已存在的设备

FILE_FLAG_OVERLAPPED,//异步方式

0) //无模板

串口被成功打开时,返回其句柄,否则返回INVALID_HANDLE_VALUE(0XFFFFFFFF)。

3、串口设置

第一次打开串口时,串口设置为系统默认值,函数GetCommState和SetCommState可用于检索和设定端口设置的DCB(设备控制块)结构,

该结构中BaudRate、ByteSize、StopBits和Parity字段含有串口波特率、数据位数、停止位和奇偶校验控制等信息。

程序中可先用GetCommState检索端口的当前设置,修改其中的部分字段后再用SetCommState进行端口设定。这样可不必构造一个完整的DCB结构。

下面介绍几个主要的函数和结构体:

(1)GetCommState

BOOL GetCommState( hCommDev, lpdcb);

参数hCommDev标识通信设备,应使用CreateFile返回的句柄。Lpdcb是指向DCB结构的指针,

函数调用后当前串口配置信息将被保存在这个结构内。如果函数成功返回值为TRUE;否则返回值为FALSE。

SetCommState用法与GetCommState相似,在此不再重复。DCB结构定义如下(只介绍主要的几项):

typedef struct _ DCB{

……

DWORD BardRate //波特率的设置

BYTE ByteSize; //数据位的个数

BYTE Parity//是否有奇偶校验位

BYTE StopBits //停止位的个数

……

}DCB;

(2)SetCommTimeouts

BOOL SetCommTimeouts( hCommDev, lpctmo );

Lpctmo指向包含新的超时参数的COMMTIMEOUTS结构。COMMTIMEOUTS结构定义如下:

typedef struct _ COMMTIMEOUTS{

DWORD ReadIntervalTimeout

DWORD ReadTotalTimeoutMultiplier

DWORD ReadTotalTimeoutconstant

DWORD WriteTotalTimeoutMultiplier

DWORD WriteTotalTimeoutconstant

}COMMTIMEOUTS, LPCOMMTIMEOUTS

ReadIntervalTimeout: 以毫秒为单位指定通信线上两个字符到达之间的最大时间。在ReadFile操作其间,

收到第一个字符时开始计算时间。若任意两个字符到达之间的间隔超过这个最大值,ReadFile操作完成,

返回缓冲数据。0值表示不用间隔限时。若该成员为MAXDWORD,且ReadTotalTimeoutconstant和

ReadTotalTimeoutMultiplier成员为零,则指出读操作要立即返回已接收到的字符,即使未收到字符,

读操作也要返回。

ReadTotalTimeoutMultiplier:以毫秒为单位指定一个乘数,该乘数用来计算读操作的总限时时间。每个读操作的总限时时间等于读操作所需的字节数与该值的乘积。

ReadTotalTimeoutConstant:以毫秒为单位指定一个常数,用于计算读操作的总限时时间。每个操作的总限时时间等于ReadTotalTimeoutMultiplier成员乘以读操作所需字节数再加上该值的和。ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant成员的值为0表示读操作不使用限时时间。

WriteTotalTimeoutMultiplier和WriteTotalTimeoutconstant的意义和作用分别与ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant相似,不再重复。

(3)BuilderCommDCB

BOOL BuilderCommDCB(lpszDef,lpdcb)

这个函数按lpszDef字符串所指定的格式来配置串口的DCB。

LpszDef:指向一个以NULL结束的字符串,该字符串指定串口的控制信息。比如,“1200,N,8,1”指定波特率为1200,无奇偶校验位,有8个数据位和1个停止位。

lpdcb:指向被填充的DCB结构。

(4)SetCommMask

BOOL SetCommMask(hCommDev,fdwEvtMask)

fdwEvtMask指向一个32位的屏蔽码,如果指定为EV_RXCHAR | EV_CTS,表示程序监控串口的收、发事件。

下面以简单的例子说明串口设置的步骤:

m_CommTimeouts.ReadIntervalTimeout = 1000

m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000

m_CommTimeouts.ReadTotalTimeoutConstant = 1000

m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000

m_CommTimeouts.WriteTotalTimeoutConstant = 1000

if (SetCommTimeouts(m_hComm, &m_CommTimeouts))

// 串口超时参数设置

if (SetCommMask(m_hComm, dwCommEvents))

// 设置串口事件掩码

if (GetCommState(m_hComm, &m_dcb))

// 获取串口当前状态

if (BuildCommDCB(“1200,N,8,1”, &m_dcb))

// 建立串口设备控制块

if (SetCommState(m_hComm, &m_dcb))

// 设置串口参数

……

以上任何一个if语句的判断条件为假时都将调用GetLastError函数获取错误信息,进行错误处理。

4、读写串口数据

C# 调用文件

计算机系统中,通常CPU执行两种不同性质的程序:一种是操作系统内核程序;另一种是用户自编程序或系统外层的应用程序。对操作系统而言,这两种程序的作

用不同,前者是后者的管理者,因此“管理程序”要执行一些特权指令,而“被管理程序”出于安全考虑不能执行这些指令。所谓特权指令,是指计算机中不允许用

户直接使用的指令,如I/O指令、

置中断指令,存取用于内存保护的寄存器、送程序状态字到程序状态字寄存器等指令。操作系统在具体实现上划分了用户态(目态)和核心态(管态),以严格区分

两类程序。

在软件工程思想和结构程序设计方法的影响下诞生的现代操作系统,几乎都是层次式的结构。操作系统的各项功能分别被设置在不同的层次上。一些与硬件关联较紧

密的模块,诸如时钟管理、中断处理、设备驱动等处于最底层。其次是运行频率较髙的程序,诸如进程管理、存储器管理和设备管理等。这两部分内容构成了操作系

统的内核。这部分内容的指令操作工作在核心态。

内核是计算机上配置的底层软件,是计算机功能的延伸。不同系统对内核的定义稍有区别,大多数操作系统内核包括四个方面的内容。

1) 时钟管理

在计算机的各种部件中,时钟是最关键的设备。时钟的第一功能是计时,操作系统需要通过时钟管理,向用户提供标准的系统时间。另外,通过时钟中断的管理,可

以实现进程的切换。诸如,在分时操作系统中,釆用时间片轮转调度的实现;在实时系统中,按截止时间控制运行的实现;在批处理系统中,通过时钟管理来衡量一

个作业的运行程度等。因此,系统管理的方方面面无不依赖于时钟。

2) 中断机制

引入中断技术的初衷是提高多道程序运行环境中CPU的利用率,而且主要是针对外部设备的。后来逐步得到发展,形成了多种类型,成为操作系统各项操作的基

础。例如,键盘或鼠标信息的输入、进程的管理和调度、系统功能的调用、设备驱动、文件访问等,无不依赖于中断机制。可以说,现代操作系统是靠中断驱动的软

件。

中断机制中,只有一小部分功能属于内核,负责保护和恢复中断现场的信息,转移控制权到相关的处理程序。这样可以减少中断的处理时间,提高系统的并行处理能力。

3) 原语

按层次结构设计的操作系统,底层必然是一些可被调用的公用小程序,它们各自完成一个规定的操作。其特点是:

它们处于操作系统的最底层,是最接近硬件的部分。

这些程序的运行具有原子性——其操作只能一气呵成(这主要是从系统的安全性和便于管理考虑的)。

这些程序的运行时间都较短,而且调用频繁。

通常把具有这些特点的程序称为原语(Atomic Operation)。定义原语的直接方法是关闭中断,让它的所有动作不可分割地进行完再打开中断。

系统中的设备驱动、CPU切换、进程通信等功能中的部分操作都可以定义为原语,使它们成为内核的组成部分。

4) 系统控制的数据结构及处理

系统中用来登记状态信息的数据结构很多,比如作业控制块、进程控制块(PCB)、设备控制块、各类链表、消息队列、缓冲区、空闲区登记表、内存分配表等。为了实现有效的管理,系统需要一些基本的操作,常见的操作有以下三种:

进程管理:进程状态管理、进程调度和分派、创建与撤销进程控制块等。

存储器管理:存储器的空间分配和回收、内存信息保护程序、代码对换程序等。

设备管理:缓冲区管理、设备分配和回收等。

从上述内容可以了解,核心态指令实际上包括系统调用类指令和一些针对时钟、中断和原语的操作指令。

怎样获取串口地址

C# 读取GPS基类

using System

using System.Runtime.InteropServices

using System.Text

namespace baseStationPDA

{

class GPS

{

public string PortNum

public int BaudRate

public byte ByteSize

public byte Parity// 0-4=no,odd,even,mark,space

public byte StopBits// 0,1,2 = 1, 1.5, 2

public int ReadTimeout

//comm port win32 file handle

private int hComm = -1

public bool Opened = false

//win32 api constants

private const uint GENERIC_READ = 0x80000000

private const uint GENERIC_WRITE = 0x40000000

private const int OPEN_EXISTING = 3

private const int INVALID_HANDLE_VALUE = -1

[StructLayout(LayoutKind.Sequential)]

public struct DCB

{

//taken from c struct in platform sdk

public int DCBlength// sizeof(DCB)

public int BaudRate// 指定当前波特率 current baud rate

// these are the c struct bit fields, bit twiddle flag to set

public int fBinary// 指定是否允许二进制模式,在windows95中必须主TRUE binary mode, no EOF check

public int fParity// 指定是否允许奇偶校验 enable parity checking

public int fOutxCtsFlow// 指定CTS是否用于检测发送控制,当为TRUE是CTS为OFF,发送将被挂起。 CTS output flow control

public int fOutxDsrFlow// 指定CTS是否用于检测发送控制 DSR output flow control

public int fDtrControl// DTR_CONTROL_DISABLE值将DTR置为OFF, DTR_CONTROL_ENABLE值将DTR置为ON, DTR_CONTROL_HANDSHAKE允许DTR"握手" DTR flow control type

public int fDsrSensitivity// 当该值为TRUE时DSR为OFF时接收的字节被忽略 DSR sensitivity

public int fTXContinueOnXoff// 指定当接收缓冲区已满,并且驱动程序已经发送出XoffChar字符时发送是否停止。TRUE时,在接收缓冲区接收到缓冲区已满的字节XoffLim且驱动程序已经发送出XoffChar字符中止接收字节之后,发送继续进行。FALSE时,在接收缓冲区接收到代表缓冲区已空的字节XonChar且驱动程序已经发送出恢复发送的XonChar之后,发送继续进行。XOFF continues Tx

public int fOutX// TRUE时,接收到XoffChar之后便停止发送接收到XonChar之后将重新开始 XON/XOFF out flow control

public int fInX// TRUE时,接收缓冲区接收到代表缓冲区满的XoffLim之后,XoffChar发送出去接收缓冲区接收到代表缓冲区空的XonLim之后,XonChar发送出去 XON/XOFF in flow control

public int fErrorChar// 该值为TRUE且fParity为TRUE时,用ErrorChar 成员指定的字符代替奇偶校验错误的接收字符 enable error replacement

public int fNull// eTRUE时,接收时去掉空(0值)字节 enable null stripping

public int fRtsControl// RTS flow control

public int fAbortOnError// TRUE时,有错误发生时中止读和写操作 abort on error

public int fDummy2// 未使用 reserved

public uint flags

public ushort wReserved// 未使用,必须为0 not currently used

public ushort XonLim// 指定在XON字符发送这前接收缓冲区中可允许的最小字节数 transmit XON threshold

public ushort XoffLim// 指定在XOFF字符发送这前接收缓冲区中可允许的最小字节数 transmit XOFF threshold

public byte ByteSize// 指定端口当前使用的数据位 number of bits/byte, 4-8

public byte Parity// 指定端口当前使用的奇偶校验方法,可能为:EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY 0-4=no,odd,even,mark,space

public byte StopBits// 指定端口当前使用的停止位数,可能为:ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS 0,1,2 = 1, 1.5, 2

public char XonChar// 指定用于发送和接收字符XON的值 Tx and Rx XON character

public char XoffChar// 指定用于发送和接收字符XOFF值 Tx and Rx XOFF character

public char ErrorChar// 本字符用来代替接收到的奇偶校验发生错误时的值 error replacement character

public char EofChar// 当没有使用二进制模式时,本字符可用来指示数据的结束 end of input character

public char EvtChar// 当接收到此字符时,会产生一个事件 received event character

public ushort wReserved1// 未使用 reserveddo not use

}

[StructLayout(LayoutKind.Sequential)]

private struct COMMTIMEOUTS

{

public int ReadIntervalTimeout

public int ReadTotalTimeoutMultiplier

public int ReadTotalTimeoutConstant

public int WriteTotalTimeoutMultiplier

public int WriteTotalTimeoutConstant

}

[StructLayout(LayoutKind.Sequential)]

private struct OVERLAPPED

{

public int Internal

public int InternalHigh

public int Offset

public int OffsetHigh

public int hEvent

}

[Dllimport("coredll.dll")]

private static extern int CreateFile(

string lpFileName, // 要打开的串口名称

uint dwDesiredAccess, // 指定串口的访问方式,一般设置为可读可写方式

int dwShareMode, // 指定串口的共享模式,串口不能共享,所以设置为0

int lpSecurityAttributes, // 设置串口的安全属性,WIN9X下不支持,应设为NULL

int dwCreationDisposition, // 对于串口通信,创建方式只能为OPEN_EXISTING

int dwFlagsAndAttributes, // 指定串口属性与标志,设置为FILE_FLAG_OVERLAPPED(重叠I/O操作),指定串口以异步方式通信

int hTemplateFile // 对于串口通信必须设置为NULL

)

[Dllimport("coredll.dll")]

private static extern bool GetCommState(

int hFile, //通信设备句柄

ref DCB lpDCB // 设备控制块DCB

)

[Dllimport("coredll.dll")]

private static extern bool BuildCommDCB(

string lpDef, // 设备控制字符串

ref DCB lpDCB // 设备控制块

)

[Dllimport("coredll.dll")]

private static extern bool SetCommState(

int hFile, // 通信设备句柄

ref DCB lpDCB // 设备控制块

)

[Dllimport("coredll.dll")]

private static extern bool GetCommTimeouts(

int hFile, // 通信设备句柄 handle to comm device

ref COMMTIMEOUTS lpCommTimeouts // 超时时间 time-out values

)

[Dllimport("coredll.dll")]

private static extern bool SetCommTimeouts(

int hFile, // 通信设备句柄 handle to comm device

ref COMMTIMEOUTS lpCommTimeouts // 超时时间 time-out values

)

[Dllimport("coredll.dll")]

private static extern bool ReadFile(

int hFile, // 通信设备句柄 handle to file

byte[] lpBuffer, // 数据缓冲区 data buffer

int nNumberOfBytesToRead, // 多少字节等待读取 number of bytes to read

ref int lpNumberOfBytesRead, // 读取多少字节 number of bytes read

ref OVERLAPPED lpOverlapped // 溢出缓冲区 overlapped buffer

)

[Dllimport("coredll.dll")]

private static extern bool WriteFile(

int hFile, // 通信设备句柄 handle to file

byte[] lpBuffer, // 数据缓冲区 data buffer

int nNumberOfBytesToWrite, // 多少字节等待写入 number of bytes to write

ref int lpNumberOfBytesWritten, // 已经写入多少字节 number of bytes written

ref OVERLAPPED lpOverlapped // 溢出缓冲区 overlapped buffer

)

[Dllimport("coredll.dll")]

private static extern bool CloseHandle(

int hObject // handle to object

)

[Dllimport("coredll.dll")]

private static extern uint GetLastError()

public void Open()

{

DCB dcbCommPort = new DCB()

COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS()

// 打开串口 OPEN THE COMM PORT.

hComm = CreateFile(PortNum ,GENERIC_READ | GENERIC_WRITE,0, 0,OPEN_EXISTING,0,0)

// 如果串口没有打开,就打开 IF THE PORT CANNOT BE OPENED, BAIL OUT.

if(hComm == INVALID_HANDLE_VALUE)

{

throw(new ApplicationException("非法操作,不能打开串口!"))

}

// 设置通信超时时间 SET THE COMM TIMEOUTS.

GetCommTimeouts(hComm,ref ctoCommPort)

ctoCommPort.ReadTotalTimeoutConstant = ReadTimeout

ctoCommPort.ReadTotalTimeoutMultiplier = 0

ctoCommPort.WriteTotalTimeoutMultiplier = 0

ctoCommPort.WriteTotalTimeoutConstant = 0

SetCommTimeouts(hComm,ref ctoCommPort)

// 设置串口 SET BAUD RATE, PARITY, WORD SIZE, AND STOP BITS.

GetCommState(hComm, ref dcbCommPort)

dcbCommPort.BaudRate=BaudRate

dcbCommPort.flags=0

//dcb.fBinary=1

dcbCommPort.flags|=1

if (Parity>0)

{

//dcb.fParity=1

dcbCommPort.flags|=2

}

dcbCommPort.Parity=Parity

dcbCommPort.ByteSize=ByteSize

dcbCommPort.StopBits=StopBits

if (!SetCommState(hComm, ref dcbCommPort))

{

//uint ErrorNum=GetLastError()

throw(new ApplicationException("非法操作,不能打开串口!"))

}

//unremark to see if setting took correctly

//DCB dcbCommPort2 = new DCB()

//GetCommState(hComm, ref dcbCommPort2)

Opened = true

}

public void Close()

{

if (hComm!=INVALID_HANDLE_VALUE)

{

CloseHandle(hComm)

}

}

public byte[] Read(int NumBytes)

{

byte[] BufBytes

byte[] OutBytes

BufBytes = new byte[NumBytes]

if (hComm!=INVALID_HANDLE_VALUE)

{

OVERLAPPED ovlCommPort = new OVERLAPPED()

int BytesRead=0

ReadFile(hComm,BufBytes,NumBytes,ref BytesRead,ref ovlCommPort)

try

{

OutBytes = new byte[BytesRead]

Array.Copy(BufBytes,0,OutBytes,0,BytesRead)

}

catch

{

return BufBytes

}

}

else

{

throw(new ApplicationException("串口未打开!"))

}

return OutBytes

// return BufBytes

}

public void Write(byte[] WriteBytes)

{

if (hComm!=INVALID_HANDLE_VALUE)

{

OVERLAPPED ovlCommPort = new OVERLAPPED()

int BytesWritten = 0

WriteFile(hComm,WriteBytes,WriteBytes.Length,ref BytesWritten,ref ovlCommPort)

}

else

{

throw(new ApplicationException("串口未打开!"))

}

}

public string GetGPS(string strGPS,string strFind)

{

///从GPS中读取的数据中,找出想要的数据

///GPSstring原始字符串,

///strFind要查找的内容,X:经度,Y:纬度,T:时间,V:速度,是数字从1开始,即以“,”分隔的位置

///返回查找到指定位置的字符串

string handerStr="$GPRMC"//GPS串头

int findHander=strGPS.IndexOf(handerStr)//看是否含有GPS串头

if (findHander<0)

{

return "-1"

}

else

{

strGPS=strGPS.Substring(findHander,strGPS.Length-findHander)

string[] ArryTmp=strGPS.Split(",".ToCharArray())

try

{

if(ArryTmp[2]=="V")

{

return "V"//没有信号

}

else

{

switch(strFind)

{

case "X":

return DM2DD(ArryTmp[5])

case "Y":

return DM2DD(ArryTmp[3])

case "T":

return T2Time(ArryTmp[9],ArryTmp[1])

case "V":

return Convert.ToString(Convert.ToDouble(ArryTmp[7])* 1.852)

default:

return "V"

}

}

}

catch

{

return "V"

}

}

}

public string T2Time(string strDate,string strTime)

{

string dT="20"+strDate.Substring(4,2)+"-"+strDate.Substring(2,2)+"-"+strDate.Substring(0,2)

string TT=Convert.ToString(Convert.ToInt32(strTime.Substring(0,2)))+":"+strTime.Substring(2,2)+":"+strTime.Substring(4,2)

DateTime T=Convert.ToDateTime(dT+" "+TT)

T=T.AddHours(8)

return T.ToString()

}

public string DM2DD(string DegreeMinutes)

{

//转换NMEA协议的“度分”格式为十进制“度度”格式

string sDegree

string sMinute

string sReturn=""

if(DegreeMinutes.IndexOf(".")==4)

{

//DegreeMinutes = Replace(DegreeMinutes, ".", "")

//DM2DD = CDbl(Left(DegreeMinutes, 2)) + CDbl(Left(CStr(CDbl(Right(DegreeMinutes, Len(DegreeMinutes) - 2)) / 60), 8)) / 10000

DegreeMinutes=DegreeMinutes.Replace(".","")

double sDegree1=Convert.ToDouble(DegreeMinutes.Substring(0,2))

double sDegree2=Convert.ToDouble(DegreeMinutes.Substring(2,DegreeMinutes.Length-2))

string sTmp=Convert.ToString(sDegree2/60)

sDegree2=Convert.ToDouble(sTmp.Substring(0,sTmp.Length))

sDegree2=sDegree2/10000

sDegree=Convert.ToString(sDegree1+sDegree2)

if(sDegree.Length>11)

sDegree=sDegree.Substring(0,11)

sReturn=sDegree

}

else if(DegreeMinutes.IndexOf(".")==5)

{

//DegreeMinutes = Replace(DegreeMinutes, ".", "")

//DM2DD = CDbl(Left(DegreeMinutes, 2)) + CDbl(Left(CStr(CDbl(Right(DegreeMinutes, Len(DegreeMinutes) - 2)) / 60), 8)) / 10000

DegreeMinutes=DegreeMinutes.Replace(".","")

double sMinute1=Convert.ToDouble(DegreeMinutes.Substring(0,3))

double sMinute2=Convert.ToDouble(DegreeMinutes.Substring(3,DegreeMinutes.Length-2))

string sTmp=Convert.ToString(sMinute2/60)

sMinute2=Convert.ToDouble(sTmp.Substring(0,sTmp.Length))

sMinute2=sMinute2/10000

sMinute=Convert.ToString(sMinute1+sMinute2)

if(sMinute.Length>10)

sMinute=sMinute.Substring(0,10)

sReturn=sMinute

}

return sReturn

}

public bool ScanPort()

{

try

{

if (Opened)

{

Close()

Open()

}

else

{

Open()//打开串口

}

byte[] bytRead=Read(512)

Close()

if(Encoding.ASCII.GetString(bytRead,0,bytRead.Length).IndexOf("$GP")>=0)

return true

else

return false

}

catch

{

return false

}

}

}

class HexCon

{

// 把十六进制字符串转换成字节型和把字节型转换成十六进制字符串 converter hex string to byte and byte to hex string

public static string ByteToString(byte[] InBytes)

{

string StringOut=""

foreach (byte InByte in InBytes)

{

StringOut=StringOut + String.Format("{0:X2} ",InByte)

}

return StringOut

}

public static byte[] StringToByte(string InString)

{

string[] ByteStrings

ByteStrings = InString.Split(" ".ToCharArray())

byte[] ByteOut

ByteOut = new byte[ByteStrings.Length-1]

for (int i = 0i==ByteStrings.Length-1i++)

{

ByteOut[i] = Convert.ToByte(("0x" + ByteStrings[i]))

}

return ByteOut

}

}

}

在别的class中调用时如Frmlogoin(是通过一个时间控件来循环的)

public class Frmlogin : System.Windows.Forms.Form

{

private GPS ss_port=new GPS()

}

#region 读取GPS

private void opengps(string ComPoint)

{

ss_port.PortNum = ComPoint

ss_port.BaudRate = 4800

ss_port.ByteSize = 8

ss_port.Parity = 0

ss_port.StopBits = 1

ss_port.ReadTimeout = 1000

try

{

if (ss_port.Opened)

{

ss_port.Close()

ss_port.Open()

timer1.Enabled=true

}

else

{

ss_port.Open()//打开串口

timer1.Enabled=true

}

}

catch

{

// MessageBox.Show("读取GPS错误!" ,"系统提示")

}

}

private void timer1_Tick(object sender, System.EventArgs e)

{

if (ss_port.Opened)

gpsread()

else

ss_port.Open()//打开串口

}

private void gpsread()

{

byte[] aa=ss_port.Read(512)

string gpsinfo =System.Text.Encoding.ASCII.GetString(aa,0,aa.Length)

GetParam.GpsLongitude=ss_port.GetGPS(gpsinfo,"X")

GetParam.GpsLatitude=ss_port.GetGPS(gpsinfo,"Y")

GetParam.GpsSpeed=ss_port.GetGPS(gpsinfo,"V")

GetParam.GpsTime=ss_port.GetGPS(gpsinfo,"T")

if(GetParam.GpsLongitude=="-1")

GetParam.GpsState="0"

if(GetParam.GpsLongitude=="V" &&GetParam.GpsLatitude=="V")

GetParam.GpsState="0"

if(GetParam.GpsLongitude!="-1" &&GetParam.GpsLongitude!="V")

GetParam.GpsState="1"

GetParam.GpsLongitude=(GetParam.GpsLongitude=="V") ? "0" : GetParam.GpsLongitude

GetParam.GpsLatitude=(GetParam.GpsLatitude=="V") ? "0" : GetParam.GpsLatitude

GetParam.GpsSpeed=(GetParam.GpsSpeed=="V") ? "0" : GetParam.GpsSpeed

GetParam.GpsTime=(GetParam.GpsTime=="V") ? "0" :GetParam.GpsTime

}

private void GpsClose()

{

timer1.Enabled=false

if (ss_port.Opened)

ss_port.Close()

}

#endregion

急!请教RS485串口通讯的问题

如果是在操作系统中,你是不需要知道串口地址的。直接用设备名打开就可以。系统内的设备名可以用devs查看,一般是/tyCo/0和/tyCo/1

如果是在bootrom中,可以用BSP提供的函数,一般在sysSerial.c中,sysChanGet(i)。设置中断或查询方式后然后输出

你用vb还是vc?

看OnComm事件应该是vb吧。

(参照出处http://www.gjwtech.com/serialcomm.htm):

MSComm控件使用详解

摘要:本文详细介绍了MSComm控件在串口编程中使用。

目 次

MSComm控件两种处理通讯的方式

CommPort属性

RThreshold 属性

CTSHolding 属性

SThreshold 属性

CDHolding 属性

DSRHolding 属性

Settings 属性

InputLen 属性

EOFEnable 属性

Handshake 常数

onComm 常数

InputMode 常数

错误消息

MSComm 控件通过串行端口传输和接收数据,为应用程序提供串行通讯功能。MSComm控件在串口编程时非常方便,程序员不必去花时间去了解较为复杂的API函数,而且在VC、VB、Delphi等语言中均可使用。 Microsoft Communications Control(以下简称MSComm)是Microsoft公司提供的简化Windows下串行通信编程的ActiveX控件,它为应用程序提供了通过串行接口收发数据的简便方法。具体的来说,它提供了两种处理通信问题的方法:一是事件驱动(Event-driven)方法,一是查询法。

1.MSComm控件两种处理通讯的方式

MSComm控件提供下列两种处理通讯的方式:事件驱动方式和查询方式。

1.1 事件驱动方式

事件驱动通讯是处理串行端口交互作用的一种非常有效的方法。在许多情况下,在事件发生时需要得到通知,例如,在串口接收缓冲区中有字符,或者 Carrier Detect (CD) 或 Request To Send (RTS) 线上一个字符到达或一个变化发生时。在这些情况下,可以利用 MSComm 控件的 onComm 事件捕获并处理这些通讯事件。onComm 事件还可以检查和处理通讯错误。所有通讯事件和通讯错误的列表,参阅 CommEvent 属性。在编程过程中,就可以在OnComm事件处理函数中加入自己的处理代码。这种方法的优点是程序响应及时,可靠性高。每个MSComm 控件对应着一个串行端口。如果应用程序需要访问多个串行端口,必须使用多个 MSComm 控件。

1.2 查询方式

查询方式实质上还是事件驱动,但在有些情况下,这种方式显得更为便捷。在程序的每个关键功能之后,可以通过检查 CommEvent 属性的值来查询事件和错误。如果应用程序较小,并且是自保持的,这种方法可能是更可取的。例如,如果写一个简单的电话拨号程序,则没有必要对每接收一个字符都产生事件,因为唯一等待接收的字符是调制解调器的“确定”响应。

2.MSComm 控件的常用属性

MSComm 控件有很多重要的属性,但首先必须熟悉几个属性。

CommPort 设置并返回通讯端口号。

Settings 以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位。

PortOpen 设置并返回通讯端口的状态。也可以打开和关闭端口。

Input 从接收缓冲区返回和删除字符。

Output 向传输缓冲区写一个字符串。

下面分别描述:

CommPort属性 设置并返回通讯端口号。

语法 object.CommPort[value ] (value 一整型值,说明端口号。)

说明 在设计时,value 可以设置成从 1 到 16 的任何数(缺省值为 1)。但是如果用 PortOpen 属性打开一个并不存在的端口时,MSComm 控件会产生错误 68(设备无效)。

注意:必须在打开端口之前设置 CommPort 属性。

RThreshold 属性:在 MSComm 控件设置 CommEvent 属性为 comEvReceive 并产生 onComm 之前,设置并返回的要接收的字符数。

语法 object.Rthreshold [ = value ](value 整型表达式,说明在产生 onComm 事件之前要接收的字符数。 )

说明 当接收字符后,若 Rthreshold 属性设置为 0(缺省值)则不产生 onComm 事件。例如,设置 Rthreshold 为 1,接收缓冲区收到每一个字符都会使 MSComm 控件产生 onComm 事件。

CTSHolding 属性:确定是否可通过查询 Clear To Send (CTS) 线的状态发送数据。Clear To Send 是调制解调器发送到相联计算机的信号,指示传输可以进行。该属性在设计时无效,在运行时为只读。

语法: object.CTSHolding(Boolean)

Mscomm 控件的 CTSHolding 属性设置值:

True Clear To Send 线为高电平。

False Clear To Send 线为低电平。

说明:如果 Clear To Send 线为低电平 (CTSHolding = False) 并且超时时,MSComm 控件设置 CommEvent 属性为 comEventCTSTO (Clear To Send Timeout) 并产生 onComm 事件。

Clear To Send 线用于 RTS/CTS (Request To Send/Clear To Send) 硬件握手。如果需要确定 Clear To Send 线的状态,CTSHolding 属性给出一种手工查询的方法。

详细信息 有关握手协议,请参阅 Handshaking 属性。

SThreshold 属性: MSComm 控件设置 CommEvent 属性为 comEvSend 并产生 onComm 事件之前,设置并返回传输缓冲区中允许的最小字符数。

语法 object.SThreshold [ = value ]

value 整形表达式,代表在 onComm 事件产生之前在传输缓冲区中的最小字符数。

说明:若设置 Sthreshold 属性为 0(缺省值),数据传输事件不会产生 onComm 事件。若设置 Sthreshold 属性为 1,当传输缓冲区完全空时,MSComm 控件产生 onComm 事件。如果在传输缓冲区中的字符数小于 value,CommEvent 属性设置为 comEvSend,并产生 onComm 事件。comEvSend 事件仅当字符数与 Sthreshold 交叉时被激活一次。例如,如果 Sthreshold 等于 5,仅当在输出队列中字符数从 5 降到 4 时,comEvSend 才发生。如果在输出队列中从没有比 Sthreshold 多的字符,comEvSend 事件将绝不会发生。

Handshake 常数

常数 值 描述

comNone 0 无握手。

comXonXoff 1 XOn/Xoff 握手。

comRTS 2 Request-to-send/clear-to-send 握手。

comRTSXonXOff 3 Request-to-send 和 clear-to-send 握手皆可。

onComm 常数

常数 值 描述

comEvSend 1 发送事件。

comEvReceive 2 接收事件。

comEvCTS 3 clear-to-send 线变化。

comEvDSR 4 data-set ready 线变化。

comEvCD 5 carrier detect 线变化。

comEvRing 6 振铃检测。

comEvEOF 7 文件结束。

Error 常数

常数 值 描述

comEventBreak 1001 接收到中断信号

comEventCTSTO 1002 Clear-to-send 超时

comEventDSRTO 1003 Data-set ready 超时

comEventframe 1004 帧错误

comEventOverrun 1006 端口超速

comEventCDTO 1007 Carrier detect 超时

comEventRxOver 1008 接收缓冲区溢出

comEventRxParity 1009 Parity 错误

comEventTxFull 1010 传输缓冲区满

comEventDCB 1011 检索端口 设备控制块 (DCB) 时的意外错误

InputMode 常数

常数 值 描述

comInputModeText 0 (缺省)通过 Input 属性以文本方式取回数据。

comInputModeBinary 1 通过 Input 属性以二进制方式检取回数据。

CDHolding 属性:通过查询 Carrier Detect (CD) 线的状态确定当前是否有传输。Carrier Detect 是从调制解调器发送到相联计算机的一个信号,指示调制解调器正在联机。该属性在设计时无效,在运行时为只读。

语法 object.CDHolding

设置值:CDHolding 属性的设置值为:

设置 描述

True Carrier Detect 线为高电平

False Carrier Detect 线为低电平

说明:注意当 Carrier Detect 线为高电平 (CDHolding = True) 且超时时,MSComm 控件设置CommEvent 属性为 comEventCDTO(Carrier Detect 超时错误),并产生 onComm 事件。

注意 在主机应用程序中捕获一个丢失的传输是特别重要的,例如一个公告板,因为呼叫者可以随时挂起(放弃传输)。

Carrier Detect 也被称为 Receive Line Signal Detect (RLSD)。

数据类型 Boolean

DSRHolding 属性:确定 Data Set Ready (DSR) 线的状态。Data Set Ready 信号由调制解调器发送到相连计算机,指示作好操作准备。该属性在设计时无效,在运行时为只读。

语法:object.DSRHolding

object 所在处表示对象表达式,其值是“应用于”列表中的对象。

DSRHolding 属性返回以下值:

值 描述

True Data Set Ready 线高

False Data Set Ready 线低

说明:当 Data Set Ready 线为高电平 (DSRHolding = True) 且超时时,MSComm 控件设置 CommEvent 属性为 comEventDSRTO(数据准备超时)并产生 onComm 事件。

当为 Data Terminal Equipment (DTE) 机器写 Data Set Ready/Data Terminal Ready 握手例程时该属性是十分有用的。

数据类型:Boolean

Settings 属性: 设置并返回波特率、奇偶校验、数据位、停止位参数。

语法: object.Settings[ = value]

说明:当端口打开时,如果 value 非法,则 MSComm 控件产生错误 380(非法属性值)。

Value 由四个设置值组成,有如下的格式:

"BBBB,P,D,S"

BBBB 为波特率,P 为奇偶校验,D 为数据位数,S 为停止位数。value 的缺省值是:

"9600,N,8,1"

InputLen 属性:设置并返回 Input 属性从接收缓冲区读取的字符数。

语法 object.InputLen [ = value]

InputLen 属性语法包括下列部分:

value 整型表达式,说明 Input 属性从接收缓冲区中读取的字符数。

说明:InputLen 属性的缺省值是 0。设置 InputLen 为 0 时,使用 Input 将使 MSComm 控件读取接收缓冲区中全部的内容。

若接收缓冲区中 InputLen 字符无效,Input 属性返回一个零长度字符串 ("")。在使用 Input 前,用户可以选择检查 InBufferCount 属性来确定缓冲区中是否已有需要数目的字符。该属性在从输出格式为定长数据的机器读取数据时非常有用。

EOFEnable 属性:确定在输入过程中 MSComm 控件是否寻找文件结尾 (EOF) 字符。如果找到 EOF 字符,将停止输入并激活 onComm 事件,此时 CommEvent 属性设置为 comEvEOF,

语法:object.EOFEnable [ = value ]

EOFEnable 属性语法包括下列部分:

value 布尔表达式,确定当找到 EOF 字符时,onComm 事件是否被激活,如“设置值”中所描述。

value 的设置值:

True 当 EOF 字符找到时 onComm 事件被激活。

False (缺省)当 EOF 字符找到时 onComm 事件不被激活。

说明:当 EOFEnable 属性设置为 False,onComm 控件将不在输入流中寻找 EOF 字符。

错误消息(MS Comm 控件)

下表列出 MSComm 控件可以捕获的错误:

值 描述

380 无效属性值 comInvalidPropertyValue

383 属性为只读 comSetNotSupported

394 属性为只读 comGetNotSupported

8000 端口打开时操作不合法 comPortOpen

8001 超时值必须大于 0

8002 无效端口号 comPortInvalid

8003 属性只在运行时有效

8004 属性在运行时为只读

8005 端口已经打开 comPortAlreadyOpen

8006 设备标识符无效或不支持该标识符

8007 不支持设备的波特率

8008 指定的字节大小无效

8009 缺省参数错误

8010 硬件不可用(被其它设备锁定)

8011 函数不能分配队列

8012 设备没有打开 comNoOpen

8013 设备已经打开

8014 不能使用 comm 通知

8015 不能设置 comm 状态 comSetCommStateFailed

8016 不能设置 comm 事件屏蔽

8018 仅当端口打开时操作才有效 comPortNotOpen

8019 设备忙

8020 读 comm 设备错误 comReadError

8021 为该端口检索设备控制块时的内部错误 comDCBError

///-----------------------------------------------------

串口数据接收方式

1、 在onComm 事件中接收数据:

这种方式能充分MSCOMM控件的特性。onComm 事件还可以检查和处理通讯错误;可以通过检查 CommEvent 属性的值来查询事件和错误;对于不定长数据以及对数据进行处理比较复杂的情况,此法不是很方便。

Private Sub MSComm_onComm ()

Select Case MSComm1.CommEvent

' 错误

Case comEventBreak ' 收到 Break。

Case comEventCDTO ' CD (RLSD) 超时。

Case comEventCTSTO ' CTS Timeout。

Case comEventDSRTO ' DSR Timeout。

Case comEventframe ' Framing Error

Case comEventOverrun '数据丢失。

Case comEventRxOver'接收缓冲区溢出。

Case comEventRxParity' Parity 错误。

Case comEventTxFull '传输缓冲区已满。

Case comEventDCB '获取 DCB] 时意外错误

' 事件

Case comEvCD ' CD 线状态变化。

Case comEvCTS ' CTS 线状态变化。

Case comEvDSR ' DSR 线状态变化。

Case comEvRing ' Ring Indicator 变化。

Case comEvReceive ' 收到 RThreshold # of chars.

Case comEvSend ' 传输缓冲区有 Sthreshold 个字符 '

Case comEvEof ' 输入数据流中发现 EOF 字符

End Select

End Sub

2.轮循法采集数据:

A、定时器轮循法

对于数据包方式收发数据以及不需即时响应情况,用轮循法更好些。实际上轮循法最大的好处在于集中处理数据而且不太占用CPU。轮循法要注意定时采集的时间片段大小;这里用二进制收发模式;使属性RThreshold、SThreshold为0,屏蔽ONCOMM事件。

InputMode = comInputModeBinary

RThreshold = 0

SThreshold = 0

Private Sub TmrComm_Timer()

'采用轮循法采集数据

Dim Rx_buff() As Byte

Dim okstring As String

Dim ReceivedLen As Integer

On Error GoTo ErrorHandler

TmrComm.Enabled = False '关闭定时器

If commport.InBufferCount >0 Then

ReceivedLen = commport.InBufferCount

Rx_buff = commport.Input

okstring = StrConv(tempbyte, vbUnicode)

If ReceivedLen = 6 Then

If Chr(tempbyte(0)) = ":" And tempbyte(3) = &h0a Then

....

End If

If Instr(okstring ,":@END*",vbBinaryCompare) Then

....

End If

End If

TmrComm.Enabled = True '打开定时器

End Sub

B、直接轮循法

此法用于接收少量控制命令字;

' 保存输入子串的缓冲区

Dim Instring As String

' 使用 COM1。

MSComm1.CommPort = 1

' 9600 波特,无奇偶校验,8 位数据,一个停止位。

MSComm1.Settings = "9600,N,8,1"

' 当输入占用时,

' 告诉控件读入整个缓冲区。

MSComm1.InputLen = 0

' 打开端口。

MSComm1.PortOpen = True

' 将 attention 命令送到调制解调器。

MSComm1.Output = "ATV1Q0" &Chr$(13)

' 确保

' 调制解调器以"OK"响应。

' 等待数据返回到串行端口。

Do

DoEvents

Buffer$ = Buffer$ &MSComm1.Input

Loop Until InStr(Buffer$, "OK" &vbCRLF)

' 从串行端口读 "OK" 响应。

' 关闭串行端口。

MSComm1.PortOpen = False

如何处理不定长数据的接收

在处理串口通讯时,经常会遇到不定长数据的接收。由于通讯任务不同及编程要求的差异所以采用的方法也有所不同。本文就此问题进行探讨。不定长数据从数据格式上分,可分为有格式和无格式。

一、无格式不定长数据的接收

这种格式在实际串口通讯中用得不多,一般只用传送字符串数据。问题在于怎么判断接收结束。一般用时间延迟的方法解决。

A、对于非握手式通讯,可用一个定时器定时轮循接收,并假定每个轮循接收完成。用ONCOMM事件接收也可,只是不如定时器定时轮循接收简便。

B、对于握手方式通讯,可用直接轮循法提高接收的准确性。下面是实现此法的函数:

Function sComm(sCommand As String, comReceive As MSComm) As String

Dim nReceiveCount As Integer

If comReceive.PortOpen = False Then

comReceive.PortOpen = True

End If

comReceive.Output = sCommand

Do

nReceiveCount = comReceive.InBufferCount

sleep (2) 'API 函数,挂起当前进程一段时间

Loop Until comReceive.InBufferCount = nReceiveCount

If comReceive.PortOpen = True Then

sComm = comReceive.Input

End If

End Function

注:此函数参照了xth一文。

此法一般是能确保数据接收的正确,但由于WINDOWS是多任务操作系统,当有耗时的进程运行时会丢失数据。如果系统会出现这种情况,可增大函数sleep()的参数值。

二、不定长格式数据的接收

对于不定长数据接收最好的方法是制定通讯协议,比如定义开始字符和结束字符。由于单片机系统通讯一般不太复杂,没必要去制定一套象通用计算机间通讯的协议,而根据单片机系统的大小和性能要求制定通讯协议。实际上为便于交流、维护以及一致性,可制定一套可伸缩的通讯协议。定义了开始字符和结束字符就容易实现不定长格式数据通讯,但在实际通讯编程还是容易出现一些比较隐蔽的通讯错误。下面就常用方法分别进行分析。

A、定时器轮循法。

假定每个轮循期数据接收完毕,并在每个轮循期处理数据,由于有开始字符和结束字符很容易确定接收数据的完整性。好象合理设定轮循时间值就万无一失了,但被动接收数据时无论如何也找不合适的轮循时间值,因为启动定时器和数据到来基本不同步,这就会出现一次发送的数据被分在两个轮循期接收,所以被动接收数据时不能假定每个轮循期数据接收完毕。在接收到结束字符后才确定一次数据接收完毕就可解决此问题。

B、OnComm事件法。

方法和定时器轮循法基本相同,因为每次OnCommg事件也只能接收到一部分数据。在VB的在线帮助中这样注解“设置 Rthreshold 为 1,接收缓冲区收到每一个字符都会使 MSComm 控件产生 onComm 事件。”。但实际上OnComm事件并不是每收到一个字符便触发一次 onComm 事件。OnComm事件是在缓冲区收到几个甚至几十个字节数据后才被触发的。版主认为这是WINDOWS多任务使操作系统不能实时响应造成的。如果要在每次OnComm事件接收一个字符似乎可设INPUTLEN属性为1,但实际行不通。VB在线帮助中“有该属性在从输出格式为定长数据的机器读取数据时非常有用”的注解,好象在说对定长字符有效,但版主发现INPUTLEN设为16,接收16个字符定长数据时却被当作两次接收了,一次12个,一次4个。建议在OnComm事件中接收数据要定义通讯协议并检测数据的完整性。 对于不定长格式数据的接收程序员更喜欢定时器轮循法,也许OnComm事件不好控制吧。

对于不定长数据的接收,最佳方法可能是在OnComm事件中启动定时器轮循接收,并同时停止OnComm事件的触发,接收完毕后或超时开启OnComm事件。

用字符方式收发码值大于127的字符数据

VB的通讯控件友好、功能强大,编程速度快是众人皆知的。加上VB的易学、易用,快速开发等特点,数据通讯量不是很大时,在单片机通讯领域广泛地使用VB开发PC上层通讯软件。实际开发时会有不少问题,这里就用字符方式收发码值大127的字符数据进行讨论。

在实际开发中经常遇到通讯只是用来发送一些控制字符命令和少量数据。在VB的中文在线帮助中有“若数据只用 ANSI 字符集,则用 comInputModeText”的表述。 ANSI字符集是0-127这容易使人误解为&h88也可用“INPUTMIDE=comInputModeText”方式收发。我刚开始用VB编通讯模块时就为此迷惑过,网上不少网友也时常问及这种问题。实际上在VB中0-127是可以正常收发的,大于127即&H7F的只有&H80和&HFF能够收发,其余ANSI字符都被过滤为0。由于串口通讯是以字节收发的,数据如以comInputModeText模式收发则非字符串数据会被过滤。在VB中用“INPUTMIDE = comInputModeBinary” 就可以解决这个问题,只是收发都必须用动态数组来完成。用comInputModeBinary模式编程稍有点复杂,调试也不直观,对于初学者不易掌握。另外软件完成后,在实际应用时会增加工程维护难度,因为对于二进制代码不是易于理解的。比如下端机发送现场统计数据233,comInputModeBinary模式下串口监测到“:A &H233",它代表A探针的温度。一般串口监测软件要么用ASCII方式显示,要么用二进制方式显示。用ASCII方式则不能看到&H233,而二进制方式则示不好理解,如果显示58 65 233 59,我想没有人喜欢这种方式(如果有更好的方式的话)。但如果显示“:A 2 3 3 ;”不就解决问题了!用comInputModeText方式就可完成任务了,只是多了一段数据分离程序。对于一般通讯要求这种方法不为是一种好方法。由于通讯任务是多种多样的,有时候这种方法就有点力不从心了,如传送较多的的数据时,这会显著地增加通讯量,通讯变得复杂了,对于单片机系统就不太合适了;还有一些特殊要求,如数据包的识别符也不适此法,但能确定传送数据码值范围也可用此法。下面介绍另一种方法,此法适用比较广,传送二进制数据通讯量增加也不大。

这种方法实际上很简单,实际运用中有不少采用此法。原理是一码分为二码。如设7E为临界字符,对于7E则分为7E和0两个ASCII码,依此类推,8F分为7E和11。接收合并时遇到7E则将7E和后一个ASCII码相加为下字符。下面给出C语言函数,VB转换一下便可。

由于C语言不能返回两个参数,所以用数组指针。

void Filt(char code[],char c)

{

if(c=='F')

{

if(code[0]>=0X7E)

{

code[1]=code[0]-0X7E

code[0]=0X7E

}

else

{

code[1]=0XFF

}

}

else if(c=='H')

{

if(code[0]!=0X7E)

{

code[1]=0xFE

}

else

{

if(code[1]==0XFE)

{

code[1]=0XFF

}

else

{

code[0]=code[0]+code[1]

code[1]=0XFE

}

}

}

发送时:

char SendChar[2]

....

SendChar[0]=c

Filt(SendChar,'F')

if(SendChar(1)==0XFF)

{

.....

}

else

{

......

}

接收时:

char ReceiveChar[2]

.....

ReceiveChar[0]=c0

Filt(ReceiveChar,'H')

if(ReceiveChar[1]==0xFF)

{

ReceiveChar[1]=c1

Filt(ReceiveChar,'H)

}

else if(ReceiveChar[1]==0xFE)

{

......

}

以上代码仅提供一种思路,实际情况视编程需要而定。

串口通讯问答录

1、Q:各位vb高手:我有一个问题想请教一下。我从COM口用BIN方式接收到数据(一串汉字),存入一BYTE数组,但无法还原为一串汉字,我认为是ANSI和UNICODE的转换,请问如何转换。

例:字符串“我”,按BIN方式接收成一BYTE数组,其值为“206,210”,如用“CHR(206)+CHR(210)”却无法得到“我”,实际上“我”=CHR(-12860)请问如何能实现BYTE数组(206,210)与字符串“我”之间的转换?万分感谢!!!

JY

1999.10

A:经CHR(206)+CHR(210)转换后实际上变成了两个UNICODE字符,四个字节了。汉字的收发必须用BINARY方式。下面的程序能实现汉字收发。

发:

Dim ytemp() As Byte

Dim stemp As String

stemp = "你好!"

ytemp = StrConv(stemp, vbFromUnicode)

Debug.Print UBound(ytemp)

MSComm1.Output = ytemp

收:

Private Sub mscTest_onComm()

'中文收发

Dim yTemp() As Byte

Dim stemp As String

Dim i As Integer

If mscTest.InBufferCount >0 Then

i = mscTest.InBufferCount

yTemp = mscTest.Input

stemp = StrConv(yTemp, vbUnicode)

txtTest1.Text = stemp

End If

End Sub

Deson

1999-10-16

--------------------------------------------------------------------------------

2、Q:各位大侠,在下被两个问题困扰多时,实在无法找到答案,请各位多多指教。

1、在用MSCOMM控件设计通讯程序时,我始终无法将ASC码大于127的值发送出去,查阅了VB论坛以前的文章,按部就班也不行,部分 VB 程序如下,请指教:

Private Sub OkBtn_Click()

Dim Data() as byte

Dim Temp as variant

redim data(10)

For i = 0 to 10

Data(i) = int(rnd()*256)

next

temp = data

mscomm.output = temp

end Sub

A:接收方式使用了文本方式,用二进制方式即可。

以上就是关于如何用API函数实现WINDOWS下的串口写程全部的内容,如果了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

免责声明:
1.本站部份内容系网友自发上传与转载,不代表本网赞同其观点;
2.秉承互联网开放、包容的精神,福步贸易网欢迎各方(自)媒体、机构转载、引用我们原创内容,但要严格注明来源:福步贸易网
3.我们倡导尊重与保护知识产权,如发现本站文章存在版权问题,烦请将版权疑问、授权证明、版权证明、联系方式等,发邮件至service@fobmy.com,我们将第一时间核实、处理,谢谢。

举报
收藏 0
打赏 0
评论 0