假如你有两台蓝牙设备,如两能计算机并都蓝牙适配器,或PC机与多功能终端(手机,虚拟仪器等等)
假如你安装了蓝牙的服务程序, 如IVT的程序或它程序, 它有串口服务功能. 并且开起了串口服务.
那我们就可以采用蓝牙的虚拟串口来通信.
当然蓝牙服务程序默认也带有一些通信程序,如文件传输出、语音、网络、打印、图像传输等,如果有安装的其它插件可能更多。但是有没有灵活的操作呢,比如我们进行虚拟仪器的网络编程。那就试着用串口吧。串口不是先进的,但是它是成熟的,平民化的,是地球人都会用。
这里我就安装了IVT 6.4.249.
一台电脑,本机为EeePC: 远端为yiling-PC
另一台电脑: yiling-PC, 对于它来说,远端就是EeePC
两台电脑都开起了串口服务;
配对方法就不说了.
关于串口:有两个选项,
一是 对于本地蓝牙串口属性:
有一个"安全连接"属性,如果是安全连接属性,则连接对方串口时,需要对方的确认才能连接.
二是 对于远文蓝牙串口属性:
有一个"自动连接"属性,如果是自动连接属性,则本地打开远程串口时,则蓝牙连接自动连接(可能会不成功,有时需要手动连接,一般先手动连接,自已的程序再打开串口).
以上两点都是对同一台电脑来说的,另一台电脑最好也如同设置.
-----------
为了说明问题.我在yilingPC上又添加了多个蓝牙虚拟串口,如串口10.
注意的是,在本机查看的蓝牙设备中的远端中的串口号并不是远端真正的串口号,这可能是IVT的问题.
具体使用时,要看蓝牙正在连接的哪个串口号.
这可以从计算机的设置管理器中查看;
------------------------------------------
先说一说串口的接口:
Serial (PC 9)
PC端9线
(At the Computer)
9 PIN D-SUB MALE at the Computer.
Pin Name Dir Description
1 CD Carrier Detect
2 RXD Receive Data
3 TXD Transmit Data
4 DTR Data Terminal Ready
5 GND System Ground
6 DSR Data Set Ready
7 RTS Request to Send
8 CTS Clear to Send
9 RI Ring Indicator
Note: Direction is DTE (Computer) relative DCE (Modem).
对机线:
其中1线不一定接,像现在用的蓝牙虚拟串口连接就不接.
-----------------------------------------------------------------
先说检测线电平的方法.
计算机可控制的串口线电平的是:DTR和RTS.可检测的线电平是CD,DSR,CTS,RI.
由上述PC-PC接线方式可知,两机可检验的信号只有6线DSR(由远机DTR控制)和8线CTS(由远机RTS控制)
程序(在别人程序的基础上改的):
-----------
有全局变量:
HANDLE hComm; //给串行端口用的Handle
boolean DTRState,RTSState;
----
打开串口
void __fastcall TForm1::Button1Click(TObject *Sender)
{
char *ComNo;
DCB dcb;
String Temp;
//取得要打开的通信端口
// 取得要打开的通信端口
if(rdCOM->ItemIndex +1>9)
Temp = "\\\\.\\COM"+IntToStr(rdCOM->ItemIndex
+1);
else
Temp = "COM"+IntToStr(rdCOM->ItemIndex +1);
//转换至指针类型Char
ComNo = Temp.c_str();
hComm = CreateFile(ComNo,GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, 0);
if (hComm == INVALID_HANDLE_VALUE) // 如果COM 未打开
{
MessageBox(0, "打开通信端口错误!!","Comm Error",MB_OK);
return;
}
//将dcb地址传入,以取得通信参数
GetCommState(hComm,&dcb); // 得知目前COM 的状态
dcb.BaudRate =
CBR_9600; // 设置波特率为9600
dcb.ByteSize =
8; // 字节为 8 bit
dcb.Parity =
NOPARITY; // Parity 为 None
dcb.StopBits =
ONESTOPBIT; // 1 个Stop bit
//通信端口设置
if (!SetCommState(hComm, &dcb))
{ // 设置COM 的状态
MessageBox
(0, "通信端口设置错误!!!","Set Error",MB_OK);
CloseHandle(hComm);
return;
}
EscapeCommFunction( hComm, CLRDTR); //将DTR降为低电位
EscapeCommFunction( hComm, CLRRTS); //将RTS降为低电位
}
-------------------------------
其中关键的问题是,串口大于9时(1-9相同,10以上不同),打开串口的方法不同,
----
关闭串口
//------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if (hComm!=INVALID_HANDLE_VALUE) CloseHandle(hComm);
exit(EXIT_SUCCESS);
}
//------------------------------------------------------------------
程序用用一个时钟去查询输入线的电平信息.
void __fastcall TForm1::Timer1Timer(TObject
*Sender)
{
unsigned long lStatus;
if (hComm == INVALID_HANDLE_VALUE) return;
if (GetCommModemStatus(hComm,&lStatus))
{
//检查CTS状态
if (lStatus & MS_CTS_ON)
spCTS->Brush->Color =clRed;
else spCTS->Brush->Color
=clWhite;
//检查DSR状态
if (lStatus & MS_DSR_ON )
spDSR->Brush->Color =clRed;
else spDSR->Brush->Color
=clWhite;
//检查RI状态
if ( lStatus & MS_RING_ON )
spRI->Brush->Color =clRed;
else spRI->Brush->Color
=clWhite;
//检查CD状态
if ( lStatus & MS_RLSD_ON )
spCD->Brush->Color =clRed ;
else spCD->Brush->Color
=clWhite;
}
}
----------------------
设置DTR及RTS
//------------------------------------------------------------------
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
//若通信端口未打开,则不作动作,并跳出
if
(hComm==0)
{
MessageBox (0, "通信端口未打开!!!","Open Error",MB_OK);
return;
}
//判断DTRState值,输出状态后,将原值作转态
if
(DTRState)
{
//输出DTR状态为低电位
EscapeCommFunction( hComm, CLRDTR );
spDTR->Brush->Color=clWhite;
//更改信号灯的颜色
}
else
{
//输出DTR的状态为高电位
EscapeCommFunction( hComm, SETDTR );
spDTR->Brush->Color=clRed;
//更改信号灯的颜色
}
DTRState
=~DTRState; //将DTRState转态
}
//------------------------------------------------------------------
//------------------------------------------------------------------
void __fastcall TForm1::BitBtn2Click(TObject *Sender)
{
//若通信端口未打开,则不作动作,并跳出
if
(hComm==0)
{
MessageBox (0, "通信端口未打开!!!","Open Error",MB_OK);
return;
}
//判断RTSState值,输出状态后,将原值作转态
if
(RTSState)
{
//输出RTS状态为低电位
EscapeCommFunction( hComm, CLRRTS );
spRTS->Brush->Color=clWhite;
//更改信号灯的颜色
}
else
{
//输出RTS的状态为高电位
EscapeCommFunction( hComm, SETRTS );
spRTS->Brush->Color=clRed;
//更改信号灯的颜色
}
RTSState
=~RTSState; //将RTSState转态
}
//------------------------------------------------------------------
使用中一定要注意自已电脑中正在连接的串口号是多少?不是从远端电脑去查看本机连接的端口号.
在进行手动蓝牙串口连接时,如果都开起串口服务,那么从哪一方发起连接都可以,不同之处是端口号不同而已.
主动方使用的串口号为远机中的串口号,被动方为本地机中串口号.
如,我在EeePC上连接yilingPC上的串口.如下图.
上图显示了在EeePC上显示的远端yilingPC上的开起的所有服务,可见开起了四个串口,分别是5.6.8,9
下图显示了在yilingPC上查看的本地开起的服务,可邮开起的四个串口号为5,6,7,10.
串口号不同的原因是因为,在不同电脑上的资源的编号并不相同.
从上两个图,还可以看出,我从EeePC上打开(连接)yilingPC上的9号串口,则在yilingPC上查看,被打开的串口号是10号串口.
因此,这个连接,在EeePC上应该打开9号串号,在yilingPC上打开10号串口.
---
下图EeePC上的.
下图yilingPC上.
---------------------------------------------------
上面主要说串口号及电平检测问题.
下面进行串口字符通信:
程序中串口打开方式类似:
这个程序中采用我多线程的方法,开辟一个新的线程来读取数据:
线程头文件:
//---------------------------------------------------------------------------
#ifndef
Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include
//---------------------------------------------------------------------------
class TReadThread : public TThread
{
private:
void __fastcall
TReadThread::ReadData();
protected:
void __fastcall Execute();
public:
__fastcall TReadThread(bool CreateSuspended);
};
//---------------------------------------------------------------------------
#endif
线程单元文件:
//---------------------------------------------------------------------------
#include
#pragma
hdrstop
#include
"Unit2.h"
#include "Unit1.h"
#pragma package(smart_init)
extern HANDLE hComm;
//---------------------------------------------------------------------------
// Important: Methods and properties of objects in VCL can only
be
// used in a method called
using Synchronize, for example:
//
// Synchronize(UpdateCaption);
//
// where UpdateCaption could
look like:
//
// void __fastcall Unit2::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------
__fastcall
TReadThread::TReadThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TReadThread::Execute()
{
//---- Place thread code here ----
while (!
Terminated)
Synchronize(ReadData); }
//---------------------------------------------------------------------------
void __fastcall
TReadThread::ReadData()
{
String Temp;
char inbuff[1024];
DWORD nBytesRead, dwEvent, dwError;
COMSTAT cs;
if (hComm ==
INVALID_HANDLE_VALUE) return;
//
取得状态
ClearCommError(hComm,&dwError,&cs);
// 数据是否大于我们所准备的Buffer
if (cs.cbInQue
> sizeof(inbuff))
{
PurgeComm(hComm, PURGE_RXCLEAR); // 清除COM 数据
return;
}
ReadFile(hComm,
inbuff,cs.cbInQue,&nBytesRead,NULL); // 接收COM
的数据
// 转移数据到变量中
inbuff[cs.cbInQue]=
'\0';
// 将数据显示在Memo1上
Form1->mReceive->Text =
Form1->mReceive->Text +
inbuff;
}
主窗口单元文件:
//---------------------------------------------------------------------------
#include
#pragma
hdrstop
#include
"Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
HANDLE hComm; // 将给串行端口使用的Handle声明
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
char *ComNo;
DCB dcb;
String Temp;
// 取得要打开的通信端口
if(rdCOM->ItemIndex +1>9)
Temp = "\\\\.\\COM"+IntToStr(rdCOM->ItemIndex
+1);
else
Temp = "COM"+IntToStr(rdCOM->ItemIndex +1);
// 转换到指针类型的Char
ComNo = Temp.c_str();
hComm = CreateFile(ComNo,GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, 0);
if (hComm == INVALID_HANDLE_VALUE) // 如果COM未打开
{
MessageBox(0, "打开通信端口错误!!","Comm Error",MB_OK);
return;
}
// 将dcb地址传入,以取得通信参数
GetCommState(hComm,&dcb); // 得知目前COM的状态
dcb.BaudRate =
CBR_9600; // 设置波特率为9600
dcb.ByteSize =
8; // 字节为8 bit
dcb.Parity =
NOPARITY; // Parity 为 None
dcb.StopBits =
ONESTOPBIT; // 1个Stop bit
// 通信端口设置
if (!SetCommState(hComm, &dcb))
{ // 设置COM的状态
MessageBox
(0, "通信端口设置错误!!!","Set Error",MB_OK);
CloseHandle(hComm);
return;
}
Read232->Resume();
}
//---------------------------------------------------------------------------
void __fastcall
TForm1::mSendKeyPress(TObject *Sender, char
&Key)
{
String Temp;
char *SendData;
int ln;
unsigned long lrc,BS;
if (Key!=13) return;
if (hComm==0) return; // 检查Handle值
Temp = mSend->Text;// 取得传送的字符串
SendData = Temp.c_str(); // 字符串转换
// 取得传送的字符串长度
BS = Temp.Length();
//BS = StrLen(SendData); // 也可以使用此种方式取得字符串长度
// 实际的传送动作
WriteFile(hComm,SendData,BS, &lrc,NULL); //
送出数据
}
//---------------------------------------------------------------------------
void __fastcall
TForm1::FormCreate(TObject *Sender)
{
Read232 = new TReadThread(true);
Read232->FreeOnTerminate = true;
//Terminated时自行摧毁 }
//---------------------------------------------------------------------------
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
Read232->Terminate();
if (hComm!=INVALID_HANDLE_VALUE) CloseHandle(hComm);
exit(EXIT_SUCCESS);
}
//---------------------------------------------------------------------------
下图是EeePC
下图是yilingPC
到此结束!谢谢,请指正.