4.3串口通信功能设计
4.3.1 串口通信协议
串口通信协议采用CCESeries类实现,通过对此类的成员函数的调用,完成对串口的初始化、读写操作等,在不用串口的情况下,还可以关闭串口。
4.3.2 软件的实现
在Visual Studio的解决方案视图里添加类CCESeries,如图4-7所示。CCESeries类的成员函数如表4-2所示。编辑。
图4-7 在VS2005中建立CCESeries类
表4-2 串口操作类CCESeries的成员函数
函数作用
函数名称
打开串口
BOOL OpenPort(HWND pPortOwner, UINT portNo = 1, UINT baud= 9600,UINT parity= NOPARITY, UINT databits= 8,UINT stopbits= 0 );
关闭串口
Void ClosePort();
设置串口超时
BOOL SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts);
读串口线程
Static DWORD WINAPI ReadThreadFunc(LPVOID lparam);
写串口
BOOL WritePort(LPCTSTR lpString);
BOOL WritePort(char *buf,int nSize);
BOOL WritePort(char *buf);
关闭读线程
Void CloseReadThread();
表4-3 串口操作类CCESeries的成员变量
变量类型
变量名称
作用
HANDLE
m_hComm
已打开的串口句柄
HWND
m_hWnd
此串口类所属的窗口句柄
HANDLE
m_hReadThread
读串口线程句柄
HANDLE
m_hWriteThread
写串口线程句柄
DWORD
m_dwReadThreadID
读串口线程ID
DWORD
m_dwWriteThreadID
写串口线程ID
HANDLE
m_hReadCloseEvent
读串口线程退出事件
HANDLE
m_hWriteCloseEvent
写串口线程退出事件
4.2.2.1 打开串口与配置串口
在Windows CE中,驱动程序分为本地设备驱动和流设备驱动两种[6]。本地设备如键盘、触摸屏等,一般在设备生产后是不会再更换的,相应的驱动这些设备的驱动程序也是必需的。而流接口设备驱动程序可以由第三方提供,支持另外添加到系统中的设备。而串口就属于流接口设备之一。
流设备驱动在系统中是以三位字符串后跟一位数字表示的,对于串口而言,这三位字符是“COM”(不包括引号)。当引用一个流接口驱动程序时,除了三位字符后跟一位数字以外,还要紧跟一个冒号“:”( 英文 半角冒号),冒号是必需的,是为了区别桌面Windows的设备驱动程序的命名规则。
该函数的流程图如图4-8所示:
图4-8 OpenPort函数流程图
(1)串口的打开所有的流驱动程序都将采用CreateFile函数打开串口设备。在Windows CE下,系统不支持设备的重叠I/O,所以不能在参数dwFlagsAndAttributes中传递FILE_FLAG_OVERLAPPED标志,返回的句柄要么是已打开的串口端口的句柄,或者是INVAILID_HANDLE_VALUE。CreateFile与大多数Windows函数不一样,打开失败时,不返回0。
CreateFile函数的原型如下:
HANDLE CreateFile(
LPCTSTR lpFileName, // 对象路径名
DWORD dwDesiredAccess, //控制模式
DWORD dwShareMode, // 共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //安全属性(也即销毁方式)
DWORD dwCreationDisposition, // 创建的方式
DWORD dwFlagsAndAttributes, // 对象属性
HANDLE hTemplateFile // 一个文件模板句柄
);
在CCESeries类中, HANDLE类型全局变量m_hComm存储CreateFile函数打开串口成功时的返回值,凭借此句柄可以控制打开的串口对象;以读写的方式打开串口。
(2)串口的设置
打开串口后,还必须对端口配置好正确的波特率、字符长度、停止位等等。在OpenPort函数中,使用两个函数GetCommState和SetCommState来配置串口。
函数原型如下:
BOOL SetCommState(HANDLE hRle, LPDCB lpDCB);
BOOL GetCommState(HANDLE hFile, LPDCB lpDCB);
这两个函数都包含了同样的参数,即已打开的串口的句柄和指向DCB结构的指针。DCB结构的设置如表4-4所示。
表4-4 OpenPort函数中的串口DCB配置
DCB定义项目
设置值
备注
BaudRate
Baud
设置波特率为传入型参baud的值
fBinary
TRUE
二进制模式有效
fParity
TRUE
支持奇偶校验
ByteSize
Databits
设置数据位为传入型参databits,范围4~8
Parity
NOPARITY
校验模式有效
StopBits
Stopbits
设置停止位为传入型参stopbits
fOutxCtsFlow
TRUE
串口输出由端口的CTS线控制
fOutxDsrFlow
TRUE
串口输出由端口的DSR线控制
fDtrSensitivity
DTR_CONTROL_ENABLE
续表4-4 OpenPort函数中的串口DCB配置
DCB定义项目
设置值
备注
fDsrSensitivity
FALSE
fTXContinueOnXoff
TRUE
串口接收缓冲区已满时,且驱动程序已传送了XOFF字符,则驱动程序停止传送字符
fOutX
FALSE
XON/XOFF控制不用于控制串行输出
fInX
FALSE
XON/XOFF控制不为输入串行流使用
fErrorChar
FALSE
fNull
FALSE
串行驱动不忽略接收到的空字节
fRtsControl
RTS_CONTROL_ENABLE
fAbortOnError
FALSE
串口发生错误时,并不终止串口读写
(3)串口超时设置
设置好串口驱动操作的配置后,一个必要的步骤是,设置端口的超时值。所谓超时,是指Windows CE.net在读串口和写串口的自动返回前等待读或写操作的时间长度。在程序中,用以下两个函数控制串行超时。
BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);
BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);
这两个函数都包含了对应的串行设备的句柄和COMMTIMEOUTS结构的指针,COMMTIMEOUTS结构定义为:
typedef struct _COMMTIMEOUTS{
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWROD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
}COMMTIMEOUTS;
(4)其他的函数
设置串口的输入输出缓冲区。原型如下
BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue);
设置串口检测的事件集。
SetCommMask(HANDLE hFile,DWORD dwEvtMask);
通过这个函数,可以使线程等待如表4-5中的常见的串口驱动程序事件。
表4-5 串口驱动程序事件
事件标志
表达意义
EV_BREAK
检测到中断
EV_CTS
CTS更改状态
EV_DSR
Data Set Ready信号更改状态
EV_ERR
串行驱动程序检测到错误
EV_RKSD
RecEive Line Signal Detect 行更改状态
EV_RXCHAR
接收到一个字符
EV_RXFLAG
接收到一个事件字符
EV_TXEMPTY
传输缓冲区为空
4.2.2.2关闭串口
调用CloseHandle可以关闭一个串行端口,用关闭在打开串口时创建的那个句柄即可关闭串口了。方法如下
CloseHandle(m_hComm);
关闭线程函数:
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
其中,hThread为要关闭的线程的句柄。dwExitCode一般为0。若成功关闭,则返回一个非零值。关闭串口函数的工作流程如图4-9所示。
SHAPE \* MERGEFORMAT
图4-9 ClosePort函数流程图
4.2.2.3 串口读线程
读串口ReadFile函数原形:
BOOL ReadFile(
HANDLE hFile, //文件的句柄
LPVOID lpBuffer, //用于保存读入数据的一个缓冲区
DWORD nNumberOfBytesToRead, //要读入的字符数
LPDWORD lpNumberOfBytesRead, //从文件中实际读入的字符数
LPOVERLAPPED lpOverlapped //异步读取描述,在Windows CE中为NULL
);
在该线程中,若串口读完一条信息,则发送一个系统消息到系统窗体,该消息的名称为WM_RXCHAR_ALL,是一个用户自定义消息。发送给系统窗体的附带消息包含收到的消息的内容和消息的长度。
发送系统消息的API函数SendMessage的原型如下:
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
图4-10 串口读线程流程图
4.2.2.4 写串口
在CCESeries类中,一共编写了三个写串口的函数,为的是方便不同情况下的调用。如要向写已知长度的字符串,可以采用WritePort(char *buf,int nSize)函数。写未知长度字符串时,采用WritePort(char *buf)函数,字符串的长度将在函数内部进行 计算 。前者提高了写串口的时间效率。这三个函数中,采用了一个共同的API函数WriteFile(),它的函数原型如下:
BOOL WriteFile(
HANDLE hFile, // 文件句柄
LPCVOID lpBuffer, // 数据缓存区指针
DWORD nNumberOfBytesToWrite, // 要写的字节数
LPDWORD lpNumberOfBytesWritten, // 用于保存实际写入字节数的存储区域的指针
LPOVERLAPPED lpOverlapped // OVERLAPPED结构体指针,CE里设为NULL
);
4.2.2.5 串口类的调用
在主程序中,必须调用此CCESeries类,以达到操作串口的目的。
在定义智能家居的窗口行为的CSmartHomeDlg类的头文件CSmartHomeDlg.h文件中,添加CCESeries.h头文件。并且新建一个public公共型变量m_BTSerial(蓝牙串口)和m_IRSeries进行对CCESeries类的实例化。
CCESeries m_BTSerial;
CCESeries m_IRSerial;
在用于CSmartHomeDlg类成员函数的定义的文件CSmartHomeDlg.cpp文件中,采用形如m_BTSerial.OpenPort(this->m_hWnd,1,9600)的方式进行串口类成员函数的调用。
4.2.2.6 串口的监听
在读串口线程中,读完串口的一条信息后发送名为WM_RXCHAR_ALL的系统消息给系统窗体。在窗体程序中,必须对其进行响应。
在CSmartHomeDlg.cpp文件中,添加一条消息响应的声明:
ON_MESSAGE(WM_RXCHAR_ALL,OnComRecvMsg)
其中OnComRecvMsg为对其消息响应的函数,函数的声明如下:
afx_msg long OnComRecvMsg(WPARAM wParam, LPARAM lParam);
其中“afx_msg”说明该函数是一消息响应函数。
4.4 GSM通信模块
智能家居以GSM 网络 作为数据的无线传输网络,利用基于GSM网络上的短信息服务,完成智能家居的远程安全监控和无线报警的功能。
4.4.1 设计基本原理
4.4.1.1 PDU编码规则
自短信息技术被开发到现在,对短信息的控制方式包含了Block Mode、基于AT的Text Mode基于AT指令的PDU Mode。
使用Block模式需要厂家驱动的支持,随着技术的 发展 ,后两种模式将其取代。Text模式比较简单,但不能发送中文短信息。在智能家居的软件设计中,采用的是基于PDU模式,该模式支持各个国家语言,当然也包括中文。在智能家居中,采用中文报警和中文指令。
PDU数据包格式,是手机发送短信的一个数据单位[7]。其中的发送源手机号码、短消息服务中心号码(SMSC)、时间戳等都是用压缩BCD码表示,低位在前,高位在后。在PDU模式中,可采用Bit7、Bit8和UCS2编码方式对短消息发送内容进行编码。这三种编码方式可以发送的最大字符数分别为160,140和70。
其中Bit7编码发送普通ASCII字符,从0x00到0x7f,共可以表示128个字符。而短消息的报文是以8位字节传输的,所以就必须把8位码压缩成7位码。实现方法是: 将第二个字符的最后一位, 补充到第一个字符的前面第一位, 第三个字符的后两位, 补充到第二个字符的前两位, 依次类推, 就可得到相应的字符串。最后转换成十六进制。
Bit8编码通常用于发送任何二进制数据。
UCS2编码用于发送Unicode字符。它固定使用16位共两个字节来表示一个字符,共可以表示65535个字符。图4-11和图4-12分别是接收和发送短信的报文格式。
图4-11 接收短信的报文格式
图4-12 发送短消息的报文格式
实例一、发送短信时的PDU码:
08 91 68 31 08 20 01 05 F0 11 00 0B 91 31 58 81 27 64 F8 00 00 00 06 C8 32 9B FD 0E 01
表4-6 发送PDU包解析
分段
含义
说明
08
SMSC地址信息的长度
共8个八位字节(包括91)
91
SMSC地址格式(TON/NPI)
用国际格式号码(在前面加‘+’)
68 31 08 20 01 05 F0
SMSC地址
8613800250500,补‘F’凑成偶数个
11
基本参数(TP-MTI/VFP)
发送,TP-VF用相对格式
00
消息基准值(TP-MR)
0
0B
目标地址数字个数
共11位,不包括补足的‘F’
91
目标地址格式(TON/NPI)
用国际格式号码(在前面加‘+’)
68 31 18 48 31 46 F9
目标地址(TP-DA)
8613818413649,补‘F’凑成偶数个
00
协议标识(TP-PID)
是普通GSM类型,点到点方式
00
用户信息编码方式(TP-DCS)
7-bit编码
00
有效期(TP-VP)
5分钟
06
用户信息长度(TP-UDL)
实际长度6个字节
C8 32 9B FD 0E 01
用户信息(TP-UD)
“Hello!”
结合发送短信的报文格式,知道短消息中心号码是:+8613800210500,对方号码是:13818413649,消息内容是“Hello!”。
实例二、接收短信时的PDU码
08 91 68 31 08 20 01 05 F0 84 0D 91 68 31 58 81 27 64 F8 00 08 30 30 21 80 63 54 80 06 53 4E 4E 1C 00 21
表4-7 接收短消息PDU包解析
分段
含义
说明
08
SMSC地址信息的长度
共8个八位字节(包括91)
91
SMSC地址格式(TON/NPI)
用国际格式号码(在前面加‘+’)
68 31 08 20 05 05 F0
SMSC地址
8613800250500,补‘F’凑成偶数位
84
基本参数(TP-MTI/MMS/RP)
接收,无更多消息,有回复地址
0B
回复地址数字个数
共11个十进制数(不包括‘F’)
91
回复地址格式(TON/NPI)
用国际格式号码(在前面加‘+’)
68 31 58 81 27 64 F8
回复地址(TP-RA)
8613851872468,补‘F’凑成偶数位
00
协议标识(TP-PID)
是普通GSM类型,点到点方式
08
用户信息编码方(TP-DCS)
UCS2编码
30 30 21 80 63 54 80
时间戳(TP-SCTS)
2003-3-12 08:36:45 +8时区
06
用户信息长度(TP-UDL)
实际长度6个字节
53 4E 4E 1C 00 21
用户信息(TP-UD)
“华东!”
短消息中心号码是:+8613800210500,对方号码是13851872468,消息内容是“华东!”。
4.4.1.2 AT指令
在智能家居中,控制中心通过串口给MC35模块发送AT指令以达到控制它的目的。表4-8中是一些系统中采用的AT指令。
表4-8 系统采用的AT指令
AT指令
功能
AT+CMGF=0
选择短消息信息格式,等于0时为PDU格式,等于1时为文本模式,
AT+CNMI=1,1,0,0,1
设置为短信到达时自动提醒
AT+CMGS=?
发送短消息,发送成功则返回OK字符串
AT+CMGR=?
读取短消息,读取成功则返回+CMGR: 0,"",[id][content]
AT+CMGD=?
删除短消息









潘霜霜变身皇马足球宝
崔涩琪:韩国性感美女