如何用VBA读取串口数据

如何用VBA读取串口数据,第1张

MSComm控件提供了两种处理通信的方式:一种为事件驱动方式,该方式相当于一般程序设计中的中断方式。当串口发生事件或错误时,MSComm控件会产生OnComm事件,用户程序可以捕获该事件进行相应处理。另一种为查询方式,在用户程序中设计定时或不定时查询MSComm控件的某些属性是否发生变化,从而确定相应处理。在程序空闲时间较多时可以采用该方式。 常用属性和方法 利用MSComm控件实现计算机通信的关键是理解并正确设置MSComm控件众多属性和方法。以下是MSComm控件的常用属性和方法:

●Commport:设置或返回串口号。

●Settings:以字符串的形式设置或返回串口通信参数。

●Portopen:设置或返回串口状态。

●InputMode:设置或返回接收数据的类型。

●Inputlen:设置或返回一次从接收缓冲区中读取字节数。

●InBufferSize:设置或返回接收缓冲区的大小,缺省值为1024字节。

●InBufferCount:设置或返回接收缓冲区中等待计算机接收的字符数。

●Input:从接收缓冲区中读取数据并清空该缓冲区,该属性设计时无效,运行时只读。

●OutBufferSize:设置或返回发送缓冲区的大小,缺省值为512字节。

●OutBufferCount:设置或返回发送缓冲区中等待计算机发送的字符数。

●Output:向发送缓冲区发送数据,该属性设计时无效,运行时只读。

●Rthreshold:该属性为一阀值。当接收缓冲区中字符数达到该值时,MSComm控件设置Commevent属性为ComEvReceive,并产生OnComm事件。用户可在OnComm事件处理程序中进行相应处理。若Rthreshold属性设置为0,则不产生OnComm事件。例如用户希望接收缓冲区中达到一个字符就接收一个字符,可将Rthreshold设置为1。这样接收缓冲区中接收到一个字符,就产生一次OnComm事件。

●Sthreshold:该属性亦为一阀值。当发送缓冲区中字符数小于该值时,MSComm控件设置Commevent属性为ComEvSend,并产生OnComm事件。若Sthreshold属性设置为0,则不产生OnComm事件。要特别注意的是仅当发送缓冲区中字符数小于该值的瞬间才产生OnComm事件,其后就不再产生OnComm事件。例如Sthreshold设置为3,仅当发送缓冲区中字符数从3降为2时,MSComm控件设置Commevent属性为ComEvSend,同时产生OnComm事件,如发送缓冲区中字符始终为2,则不会再产生OnComm事件。这就避免了发送缓冲区中数据未发送完就反复发生OnComm事件。

●CommEvent:这是一个非常重要的属性。该属性设计时无效,运行时只读。一旦串口发生通信事件或产生错误,依据产生的事件和错误,MSComm控件为CommEvent属性赋不同的代码,同时产生OnComm事件。用户程序就可在OnComm事件处理程序中针对不同的代码,进行相应的处理。 CommEvent通信事件代码 常数 含义

1 ComEvReceive 接受到Rthreshold个字符。该事件将持续产生,直到用Input属性从接受缓冲区中读取并删除字符。

2 ComEvSend 发送缓冲区中数据少于Sthreshold个,说明串口已经发送了一些数据,程序可以用Output属性继续发送数据。

3 ComEvCTS Clear To Send信号线状态发生变化。

4 ComEvDSR Data Set Ready信号线状态从1变到0。

5 ComEvCD Carrier Detect信号线状态发生变化。

6 ComEvRing 检测到振铃信号。

7 ComEvEOF 接受到文件结束符。

CommEvent通信错误代码 常数 含义

1001 ComEvntBreak 接受到一个中断信号。

1002 ComEvntCTSTO Clear To Send信号超时。

1003 ComEvntDSRTO Data Set Ready信号超时。

1004 ComEvntFrame 帧错误。

1006 ComEvntOverrun 串口超速。

1007 ComEvntCDTO 载波检测超时。

1008 ComEvntRxOver 接受缓冲区溢出,缓冲区中已没有空间。

1009 ComEvntRxParity 奇偶校验错。

1010 ComEvntTxFull 发送缓冲区溢出,缓冲区中已没有空间。

1011 ComEvntDCB 检索串口的设备控制块时发生错误。

一、文件下载

文件下载地址:

也可以下载我上传到网盘上的:

二、文件内容介绍

1下载到的文件为qextserialport-12win-alpha ,解压并打开后其内容如下。

(点击可以查看清晰大图)

下面分别介绍:

(1)doc文件夹中的文件内容是QextSerialPort类和QextBaseType的简单的说明,我们可以使用记事本程序将它们打开。

(2)examples文件夹中是几个例子程序,可以看一下它的源码,不过想运行它们好像会出很多问题啊。

(3)html文件夹中是QextSerialPort类的使用文档。

(4)然后就是剩下的几个文件了。其中qextserialenumeratorcpp及qextserialenumeratorh文件中定

义的QextSerialEnumerator类是用来获取平台上可用的串口信息的。不过,这个类好像并不怎么好用,而且它不是我们关注的重点,所以下面

就不再介绍它了。

(5)qextserialbasecpp和qextserialbaseh文件定义了一个QextSerialBase

类,win_qextserialportcpp和win_qextserialporth文件定义了一个Win_QextSerialPort

类,posix_qextserialportcpp和posix_qextserialporth文件定义了一个

Posix_QextSerialPort类,qextserialportcpp和qextserialporth文件定义了一个

QextSerialPort类。这个QextSerialPort类就是我们上面所说的那个,它是所有这些类的子类,是最高的抽象,它屏蔽了平台特征,

使得在任何平台上都可以使用它。

2几个类的简单介绍。

下面是这几个类的关系图。

可以看到它们都继承自QIODevice类,所以该类的一些函数我们也可以直接来使用。图中还有一个QextBaseType类,其实它只是一个标

识,没有具体的内容,它用来表示Win_QextSerialPort或Posix_QextSerialPort

中的一个类,因为在QextSerialPort类中使用了条件编译,所以QextSerialPort类既可以继承自

Win_QextSerialPort类,也可以继承自Posix_QextSerialPort类,所以使用了QextBaseType来表示。这一点

我们可以在qextserialporth文件中看到。再说QextSerialPort类,其实它只是为了方便程序的跨平台编译,使用它可以在不同的

平台上,根据不同的条件编译继承不同的类。所以它只是一个抽象,提供了几个构造函数而已,并没有具体的内容。在qextserialporth文件中的

条件编译内容如下:

#ifdef_TTY_POSIX_

#include“posix_qextserialporth”

#define QextBaseTypePosix_QextSerialPort

#else

#include“win_qextserialporth”

#define QextBaseTypeWin_QextSerialPort

#endif

所以,其实我们没有必要使用这个类,直接使用Win_QextSerialPort或Posix_QextSerialPort就可以了。当然如果

你想使用这个类,实现同样的源程序可以直接在Windows和Linux下编译运行,那么一定要注意在Linux下这里需要添加

#define _TTY_POSIX_ 。而我们这里为了使得程序更明了,所以没有使用该类,下面也就不再介绍它了。

QextSerialBase类继承自QIODevice类,它提供了 *** 作串口所必需的一些变量和函数等,而

Win_QextSerialPort和Posix_QextSerialPort均继承自QextSerialBase

类,Win_QextSerialPort类添加了Windows平台下 *** 作串口的一些功能,Posix_QextSerialPort类添加了

Linux平台下 *** 作串口的一些功能。所以说,在Windows下我们使用Win_QextSerialPort类,在Linux下我们使用

Posix_QextSerialPort类。

3在QextSerialBase类中还涉及到了一个枚举变量QueryMode。

它有两个值Polling和EventDriven

。QueryMode指的是读取串口的方式,下面我们称为查询模式,我们将Polling称为查询方式Polling,将EventDriven称为事件驱动方式。

事件驱动方式EventDriven就是使用事件处理串口的读取,一旦有数据到来,就会发出readyRead()信号,我们可以关联该信号来读取串口的数据。在事件驱动的方式下,串口的读写是异步的,调用读写函数会立即返回,它们不会冻结调用线程。

而查询方式Polling则不同,读写函数是同步执行的,信号不能工作在这种模式下,而且有些功能也无法实现。但是这种模式下的开销较小。我们需要自己建立定时器来读取串口的数据。

在Windows下支持以上两种模式,而在Linux下只支持Polling模式。

三、小结。

这里讲了这么多,最后要说的只是,我们在Qt中使用这个类编写串口程序,根据平台的不同只需要分别使用四个文件。

#include <stdioh>

#include <dosh>

#include <timeh>

#define LSB 0

#define MSB 1

#define RXR 0

#define TXR 0

#define IER 1

#define IIR 2

#define LCR 3

#define MCR 4

#define LSR 5

#define MSR 6

#define Com1_base 0x3f8

#define uchar unsigned char

volatile uchar inputData;

volatile uchar onInput = 0x00;

volatile uchar onOutput = 0x00;

volatile uchar inputdata[256]; /设设置接收缓冲区大小/

volatile unsigned int count; /中断接收的数据数目/

int datacount;/当前已发送数据条数/

void interrupt (OldVect)(); /函数是用来获取中断处理程序的入口地址的/

void interrupt SerialISR();

void InitCom()

{

uchar inttemp;

/设置波特率什么的/

outportb(Com1_base+LCR,0x80); /使LCR的高位为1,以便读取其它寄存器/

outportb(Com1_base+LSB,0x0c); /除数锁存器(低8位)DLL/

outportb(Com1_base+MSB,0x00); /除数锁存器(高8位)DLH 产生2400波特率/

outportb(Com1_base+LCR,0x03); /8位数据,1位停止位,无校验/

outportb(Com1_base+IER,0x01); /接收采用中断方式/

/设置中断向量/

OldVect = getvect(0x0c); /函数是用来获取中断处理程序的入口地址的/

disable();

inttemp = inportb(0x21)&0xef;

outportb(0x21,inttemp);

setvect(0x0c,SerialISR); /设置SerialISR的中断入口地址为0X0C/

enable();

}

void CloseCom()

{

disable();

outportb(Com1_base+IER,0x00); 禁止中断/

outportb(Com1_base,0x00);

outportb(0x21,inportb(0x21)|~(0xef));

setvect(0x0c,OldVect);

}

void interrupt SerialISR()

{

/串口中断服务代码/

inputData = inportb(Com1_base+RXR);

onInput = 0x01;

inputdata[count]=inputData;

count++;

/服务代码结束/

outportb(0x20,0x20); /中断结束的代码/

}

void SendChar(uchar key)

{

while( ((inportb(Com1_base + LSR)) & 0x40) == 0);

outportb(Com1_base + TXR,key);

}

/延时函数/

void delay(unsigned int n)

{

unsigned int i,k;

for(k=0;k<n;k++)

{

for(i=1;i<1142;i++)

;

}

}

/CDMA命令发送函数/

void CDMA_CommandSend(char p,unsigned int n)

{

int i,k,m,l;

int flag=0;

int selse_flag=0; int selse_flag_1=0;

int selse_flag1=0;int selse_flag1_1=0;

time_t start,end;

double dif=0;

char a;

uchar bExit_flag = 0x00;

count=0;

while(!bExit_flag)

{

count=0;

for(i=0;i<n;i++)

{SendChar(p);delay(9000);p++;}

/等待2S/

time (&start);

dif=0;

while(dif<2)

{

delay(9000);

time (&end);

dif = difftime (end,start);

}

if(count>14)

{

time (&start);

dif=0;

while(dif<3)

{

delay(9000);

time (&end);

dif = difftime (end,start);

}

}

/判断发送命令是否成功/

for(m=0;m<count;m++)

{

if(count>10)

{

for(l=0;l<count;l++)

{

if(inputdata[l-1]=='N'&&inputdata[l-2]=='E'&&inputdata[l-3]=='P'&&inputdata[l-4]=='O'&&inputdata[l-5]=='P'&&inputdata[l-6]=='P'&&inputdata[l-7]=='P'&&inputdata[l-8]=='V') { selse_flag_1=1;}

if(inputdata[l-1]=='N'&&inputdata[l-2]=='E'&&inputdata[l-3]=='P'&&inputdata[l-4]=='O'&&inputdata[l-5]=='P'&&inputdata[l-6]=='C'&&inputdata[l-7]=='T'&&inputdata[l-8]=='V') { selse_flag1_1=1;}

}

if(selse_flag_1==1){if(inputdata[m-1]=='3'&&inputdata[m-2]=='3'&&inputdata[m-3]==':'&&inputdata[m-4]=='T') selse_flag=1;}

if(selse_flag1_1==1){if(inputdata[m-1]=='0'&&inputdata[m-2]==','&&inputdata[m-3]=='1'&&inputdata[m-4]==':'&&inputdata[m-5]=='N') selse_flag1=1;}

if(selse_flag_1==1||selse_flag1_1==1)

{

if(selse_flag==1) {if(inputdata[m-1]=='0'&&inputdata[m-2]==':'&&inputdata[m-3]=='N'&&inputdata[m-4]=='E') flag=1;}

if(selse_flag1==1) {if(inputdata[m-1]=='0'&&inputdata[m-2]==','&&inputdata[m-3]=='1'&&inputdata[m-4]==':'&&inputdata[m-5]=='S') flag=1;}

}

else {if(inputdata[m-1]=='O'&&inputdata[m]=='K') {flag=1; }}

}

else {if(inputdata[m-1]=='O'&&inputdata[m]=='K') flag=1; }

}

if(flag==1){bExit_flag = 0x01;}

else {p=p-n; }

for(i=0;i<count;i++) printf("%c",inputdata[i]);

/按空格停止测试/

if(kbhit()){a=getch(); if(a==' '){CloseCom(); exit(1);}}

}

}

/数据测试发送任务/

void Data_Send(char pcomd,int n,char pdata,int m)

{

int i,k,l;time_t start,end;

double dif=0;

count=0;

for(i=0;i<n;i++)

{

SendChar(pcomd);delay(9000);pcomd++;

}

time (&start);

dif=0;

while(dif<02)

{

delay(9000);

time (&end);

dif = difftime (end,start);

}

for(l=0;l<m;l++)

{

SendChar(pdata);pdata++;

}

printf("The data num:%d",datacount);

datacount++;

}

void main()

{int i;char a;

/模块测试命令/

uchar CDMA_COMMAND_1[]={"at\r"};

uchar CDMA_COMMAND_2[]={"atz\r"};

uchar CDMA_COMMAND_3[]={"ate1v1\r"};

uchar CDMA_COMMAND_4[]={"AT+CRM=1;+CPS=33;+CMUX=1;+CTA=0\r"};

uchar CDMA_COMMAND_5[]={"AT+VPPPOPEN\r"};

uchar CDMA_COMMAND_6[]={"AT+VTCPOPEN=1,\"606342129\",1234\r"};

uchar CDMA_COMMAND_7[]={"AT+VTCPSEND=1,20\r"};

uchar CDMA_DATA[]={"ABCDEFGHIJKLMNOPQRST"};

uchar CDMA_COMMAND_9[]={"AT+VPPPCLOSE\r"};

uchar CDMA_COMMAND_8[]={"AT+VTCPCLOSE=1\r"};

uchar bExit_flag1 = 0x00;

time_t start,end;

double dif=0;

InitCom();/初始化端口1/

inputData = inportb(Com1_base+RXR);

while(!bExit_flag1)

{

//

if(kbhit()) /按任意键开始测试CDMA/

{

/ /

/发送TCP断开命令/

CDMA_CommandSend(CDMA_COMMAND_8,strlen(CDMA_COMMAND_8));

/发送PPP断开命令/

CDMA_CommandSend(CDMA_COMMAND_9,strlen(CDMA_COMMAND_9));

/发送AT命令/

CDMA_CommandSend(CDMA_COMMAND_1,strlen(CDMA_COMMAND_1));

/发送ATZ命令/

CDMA_CommandSend(CDMA_COMMAND_2,strlen(CDMA_COMMAND_2));

/发送ATE1V1命令/

CDMA_CommandSend(CDMA_COMMAND_3,strlen(CDMA_COMMAND_3));

/发送AT初始化命令/

CDMA_CommandSend(CDMA_COMMAND_4,strlen(CDMA_COMMAND_4));

/发送PPP拔号命令/

CDMA_CommandSend(CDMA_COMMAND_5,strlen(CDMA_COMMAND_5));

/发送网络连接命令/

CDMA_CommandSend(CDMA_COMMAND_6,strlen(CDMA_COMMAND_6));

/发送数据/

datacount=0;

while(1)

{

Data_Send(CDMA_COMMAND_7,strlen(CDMA_COMMAND_7),CDMA_DATA,strlen(CDMA_DATA));

time (&start);

dif=0;

while(dif<02)

{

delay(9000);

time (&end);

dif = difftime (end,start);

}

if(kbhit()){a=getch(); if(a==' '){bExit_flag1 = 0x01; break;}}

}

/发送TCP断开命令/

for(i=0;i<3;i++)

CDMA_CommandSend(CDMA_COMMAND_8,strlen(CDMA_COMMAND_8));

/发送PPP断开命令/

for(i=0;i<3;i++)

CDMA_CommandSend(CDMA_COMMAND_9,strlen(CDMA_COMMAND_9));

bExit_flag1 = 0x01;

}

//

}

CloseCom(); /关闭端口1/

printf("Bye~\n");

}

以上就是关于如何用VBA读取串口数据全部的内容,包括:如何用VBA读取串口数据、如何在QT中读取串口数据、怎么用C语言中断方式实现串口的读写等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zz/9409865.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-28
下一篇2023-04-28

发表评论

登录后才能评论

评论列表(0条)

    保存