上位机界面编程.docx
- 文档编号:9942942
- 上传时间:2023-02-07
- 格式:DOCX
- 页数:14
- 大小:28.25KB
上位机界面编程.docx
《上位机界面编程.docx》由会员分享,可在线阅读,更多相关《上位机界面编程.docx(14页珍藏版)》请在冰豆网上搜索。
上位机界面编程
首先,我们来大概的回忆一下单片机的串口通信。
8051单片机的串行接口由数据缓冲寄存器SBUF、移位寄存器、串行控制寄存器SCON组成。
8051单片机的串行接口是一个可编程的全双工通信接口,通过软件编程可以作为通用异步接收和发送器使用,也可作为同步移位寄存器,还可实现多机通信。
其帖格式有8位、10位和11位,通过T1或T2设置各种波特率。
1.1串行口工作原理
在发送和接收数据前,先对串行口进行初始化设置,要明确串行口的工作方式、波特率等。
1.发送数据
发送数据,由累加器A送入发送缓冲寄存器SBUF,在发送控制器控制下组成帧结构,并自动以串行方式从TXD输出,每发送完一帧TI置位,可以通过中断方式或查询方式来了解数据的发送情况。
值得注意的是TI只能用软件复位。
2.接收数据
单片机每接收完一帧数据,RI置位,通过中断或查询方式来了解数据的接收情况,然后用MOVA,SBUF指令,将接收缓冲寄存器(SBUF)的值送累加器A。
RI与TI一样,也只能用软件复位。
1.2串行口工作方式
8051单片机通过编程可选择4种串行通信工作方式。
1.方式0
在方式0下,串行口用作同步移位寄存器,以8位数据为1帧,先发送或接收最低位,每个机器周期发送或接收1位,其波特率为fosc/12。
串行数据由RXD端输入或输出,同步移位脉冲由TXD端送出。
方式0数据发送与接收是无起始位和停止位,先发送或接收最低位,数据格式为:
— D0 D1 D2 D3 D4 D5 D6 D7
2.方式1
在方式1下,串行口为10位通用异步接口,数据格式为:
—— 0 D0 D1 D2 D3 D4 D5 D6 D7 1 ——
发送数据:
当执行MOVSBUF,A指令,CPU将1字节的数据写入发送缓冲寄存器SBUF,数据从引脚TXD端输出,当发送完1帧数据后,TI标志置1,可用中断或查询方式来了解数据发送情况,TI只有通过软件复位。
接收数据:
接收时,先使REN置1,使串行口处于允许接收状态,RI标志为0,串行口采样到RXD由1到0时,确认是起始位0,就开始接收1帧数据。
当停止位到来时,RB8位置1,同时,中断标志位RI也置1,用中断或查询方式,通知CPU从SBUF取走接收到的数据。
3.方式2和方式3
方式2和方式3均为11位异步通信方式,只是波特率的设置方法不同,数据格式为:
— 0 D0 D1 D2 D3 D4 D5 D6 D7 D8 1 ——
发送数据:
发送前,先要根据能信协议由软件设置TB8,然后将要发送的数据写入SBUF即可启动发送器。
接收数据:
接收时,先使REN置1,使串行口处于允许接收状态,RI标志为0。
在满足这个条件的前提下,再根据SM2的状态和所接收到的RB8的状态,才能决定此串行口在信息到来后是否会使RI置1。
如果置1,在中断方式下将申请中断,接收数据。
当SM2=0时,不管RB8为0还是为1,RI都置1,此串行口将接收发来的信息。
当SM2=1,且RB8为1时,表示在多机能信情况下,接收的信息为地址帧,此时RI置1。
串行口将接收发来的地址。
当SM2=1,且RB8为0时,表示接收到的信息为数据帧,便不是发给本从机的,此时RI不置1,因而SBUF中所接收的数据帧将丢失。
4.多机通信
在方式2和方式3下,有一个专门用于多机通信的功能,这一功能使它可以方便地应用于集散分布系统中,这种系统采用一台主机和多台从机之间通信。
多机通信的实现,主要靠主、从机之间正确地设置与判断多机通信控制位SM2和发送或接收的第9位数据位。
在硬件上,所有从机的TXD接主机的RXD,所有从机的RXD接主机的TXD。
在编程序前,首先要给从机地址编号,如分别为00H、01H、02H等,主机设置在工作方式2或方式3,TB8=1,从机初始化时设置SM2=1,处于方式2或方式3的允许接收状态。
主机与从机通信前,主机先发送一个地址字节(地址字节和数据字节可用第9位数据位来区别,第9位为1表示发送的是地址)给从机,从机接收到主机发来的信息时,第9位RB8若为1,则置位中断标志位RI,并在中断后判断主机送来的地址与本机是否相同,若相同,则被寻址的从机设置成SM2=0,准备接收即将从主机送来的数据帧,未被选中的从机保持SM2=1的状态。
当主机发送数据时,应置TB8为0,此时,虽各从机处于接收状态,但由于TB8=0,所以只有SM2=0的从机才接收数据,其余从机保持SM2=1状态。
1.3串行口控制
串行口控制与串行口控制寄存器SCON、电源控制寄存器PCON、串行口发送/接收缓冲区SBUF中断允许寄存器IE、中断优先级、定时控制寄存器TCON及方式控制寄存器TDOM有关。
1.串行控制寄存器SCON
SCON是一个可位寻址的专用寄存器,地址为98H,用于串行数据通信的控制,位功能如下:
SM0 SM1 SM2 REN TB8 RB8 TI RI
SM0、SM1:
串行口工作方式选择位。
工作方式的选择如表8.1所示下。
表8.1串行口工作方式
SM0 SM1 工作方式 功能 波特率
0 0 0 同步移位寄存器 FOSC/12
0 1 1 8位格式 2SMOD/32×T1溢出率
1 0 2 9位格式 FOSC/32或FOSC/64
1 1 3 9位格式 2SMOD/32×T1溢出率
SM2:
多机通信控制位。
在方式2或方式3下,如果SM2=1,当RB8=1(RB8为收到的第9位数据),接收数据送SBUF,并产生中断请求(RI=1),否则丢失8位数据。
在方式2或方式3下,如果SM2=0,无论RB8=0或1,接收数据装入SBUF,并产生中断(RI=1)。
在方式1下,如果SM2=1,则只有接收到有效的停止位时,才激活RI;如果SM2=0,接收一帧数据,停止位进处RB8,数据进入SBUF,才激活RI。
在方式0下,SM2只能为0。
REN:
允许接收位。
由软件置位或清0。
REN=1,允许接收;REN=0,禁止接收。
TB8:
发送数据位。
在方式2或方式3下,将要发送的第9位数据放在TB8中。
可根据需要由软件置位或复位。
在多机通信中,TB8=0表示主机发送的是数据,TB8=1表示主机发送的是地址。
RB8:
接收数据位。
方式0不使用这位。
方式1下,如果SM2=0,RB8的内容是接收到的停止位。
在方式2或方式3下,存放接收到的第9位数据。
TI:
发送中断标志位。
在方式0下,发送完第8位数据时,TI=1。
在其它方式下,开始发送停止位时,TI=1。
在任何工作方式下TI必须由软件清0。
RI:
接收中断标志位。
在方式0下,接收完第8位数据时,RI=1。
在其它方式下,接收到停止位时,RI=1。
在任何工作方式下RI也必须由软件清0。
2.串行口波特率设置
串行口波特率设置与串行口工作方式及定时控制寄存器TCON、方式控制寄存器TDOM及电源控制寄存器有关。
(1).T溢出率的计算
在串行通信方式1和方式3下,使用定时器T作为波特率发生器。
T可以工作于方式0、方式1和方式2,由于定时器方式2是具有自动重装功能的8位定时器,因此常选用它。
溢出周期=(12/fosc)*(256--X) ;式中X为定时器初值,fosc为晶振频率。
溢出率=1/溢出周期
(2).波特率的计算
(1)方式0和方式2的波特率
方式0的波特率=fosc/12
方式2的波特率=(2SMOD/64)×fosc
(2)方式1和方式3的波特率
方式1和方式3的波特率=2SMOD×fosc/[32×12(256-X)]
(3).波特率的设置
TMOD:
工作在方式2
PCON:
只有工作在方式1、2、3时,如果PCON的SMOD为高电平,则通信整度加倍。
SMOD — — —
计算定时器初值:
定时器初值是由波特率=2SMOD×fosc/[32×12(256-X)]计算得到。
中断设置:
SETBES ;开串行中断
SETBEA ;中断启动
串行中断优先级由中断优先控制寄存器IP中的PS决定。
农民也学单片机
2009-05-0210:
12
好,串口通信的基本概念和通信规则我们已经大概的有一个了解了,相信大家都已经熟记在心了。
下面,我们开始编写上位机了。
先让我们来了解一下串口通信上位机编写的方法有哪些以及要用到哪些技术,其实到现在为止我自己也是一窍不通,也没有具体的书参考,先我们还是这样画葫芦画下去吧,本人对VC的研究也不是很透彻,还得去学好C++,不过一步一步慢慢来吧,告诉自己坚持到底,要坚持到底!
方法一:
使用VC++提供的串行通信控件MSComm首先,在对话框中创建通信控件,若Control工具栏中缺少该控件,可通过菜单Project-->AddtoProject-->ComponentsandControl插入即可,再将该控件从工具箱中拉到对话框中。
此时,你只需要关心控件提供的对Windows通讯驱动程序的API函数的接口。
换句话说,只需要设置和监视MSComm控件的属性和事件。
----在ClassWizard中为新创建的通信控件定义成员对象(CMSCommm_Serial),通过该对象便可以对串口属性进行设置,MSComm控件共有27个属性,这里只介绍其中几个常用属性:
----CommPort设置并返回通讯端口号,缺省为COM1。
----Settings以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位。
----PortOpen设置并返回通讯端口的状态,也可以打开和关闭端口。
----Input从接收缓冲区返回和删除字符。
----Output向发送缓冲区写一个字符串。
----InputLen设置每次Input读入的字符个数,缺省值为0,表明读取接收缓冲区中的全部内容。
----InBufferCount返回接收缓冲区中已接收到的字符数,将其置0可以清除接收缓冲区。
----InputMode定义Input属性获取数据的方式(为0:
文本方式;为1:
二进制方式)。
----RThreshold和SThreshold属性,表示在OnComm事件发生之前,接收缓冲区或发送缓冲区中可以接收的字符数。
----以下是通过设置控件属性对串口进行初始化的实例:
BOOL CSampleDlg:
:
PortOpen(){BOOL m_Opened;......m_Serial.SetCommPort
(2); // 指定串口号m_Serial.SetSettings("4800,N,8,1"); // 通信参数设置m_Serial.SetInBufferSize(1024); // 指定接收缓冲区大小m_Serial.SetInBufferCount(0); // 清空接收缓冲区m_Serial.InputMode
(1); // 设置数据获取方式m_Serial.SetInputLen(0); // 设置读取方式m_Opened=m_Serail.SetPortOpen
(1); // 打开指定的串口 return m_Opened; }
----打开所需串口后,需要考虑串口通信的时机。
在接收或发送数据过程中,可能需要监视并响应一些事件和错误,所以事件驱动是处理串行端口交互作用的一种非常有效的方法。
使用OnComm事件和CommEvent属性捕捉并检查通讯事件和错误的值。
发生通讯事件或错误时,将触发OnComm事件,CommEvent属性的值将被改变,应用程序检查CommEvent属性值并作出相应的反应。
在程序中用ClassWizard为CMSComm控件添加OnComm消息处理函数:
void CSampleDlg:
:
OnComm(){ ...... switch(m_Serial.GetCommEvent()) { case 2:
// 串行口数据接收,处理; }}
----方法二:
在单线程中实现自定义的串口通信类
----控件简单易用,但由于必须拿到对话框中使用,在一些需要在线程中实现通信的应用场合,控件的使用显得捉襟见肘。
此时,若能够按不同需要定制灵活的串口通信类将弥补控件的不足,以下将介绍如何在单线程中建立自定义的通信类。
----该通信类CSimpleComm需手动加入头文件与源文件,其基类为CObject,大致建立步骤如下:
----
(1)打开串口,获取串口资源句柄
----通信程序从CreateFile处指定串口设备及相关的操作属性。
再返回一个句柄,该句柄将被用于后续的通信操作,并贯穿整个通信过程。
CreateFile()函数中有几个值得注意的参数设置:
串口共享方式应设为0,串口为不可共享设备;创建方式必须为OPEN_EXISTING,即打开已有的串口。
对于dwFlagAndAttribute参数,对串口有意义的值是FILE_FLAG_OVERLAPPED,该标志表明串口采用异步通信模式,可进行重叠操作;若值为NULL,则为同步通信方式,在同步方式下,应用程序将始终控制程序流,直到程序结束,若遭遇通信故障等因素,将导致应用程序的永久等待,所以一般多采用异步通信。
----
(2)串口设置
----串口打开后,其属性被设置为默认值,根据具体需要,通过调用GetCommState(hComm,&dcb)读取当前串口设备控制块DCB(DeviceControlBlock)设置,修改后通过SetCommState(hComm,&dcb)将其写入。
再需注意异步读写的超时控制设置,通过COMMTIMEOUTS结构设置超时,调用SetCommTimeouts(hComm,&timeouts)将结果写入。
以下是温度监控程序中串口初始化成员函数:
BOOL CSimpleComm:
:
Open() { DCBdcb;m_hIDComDev=CreateFile("COM2",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVE RLAPPED,NULL); // 打开串口,异步操作if(m_hIDComDev==NULL)return(FALSE);dcb.DCBlength=sizeof(DCB);GetCommState(m_hIDComDev,&dcb); // 获得端口默认设置dcb.BaudRate=CBR_4800;dcb.ByteSize=8;dcb.Parity=NOPARITY;dcb.StopBits=(BYTE)ONESTOPBIT; ......}
----(3)串口读写操作
----主要运用ReadFile()与WriteFile()API函数,若为异步通信方式,两函数中最后一个参数为指向OVERLAPPED结构的非空指针,在读写函数返回值为FALSE的情况下,调用GetLastError()函数,返回值为ERROR_IO_PENDING,表明I/O操作悬挂,即操作转入后台继续执行。
此时,可以用WaitForSingleObject()来等待结束信号并设置最长等待时间,举例如下:
BOOL bReadStatus; bReadStatus=ReadFile(m_hIDComDev,buffer, dwBytesRead,&dwBytesRead, &m_OverlappedRead); if(!
bReadStatus) { if(GetLastError()==ERROR_IO_PENDING) { WaitForSingleObject(m_OverlappedRead.hEvent,1000); return((int)dwBytesRead); } return(0); } return((int)dwBytesRead);
----定义全局变量m_Serial作为新建通信类CSimpleComm的对象,通过调用类的成员函数即可实现所需串行通信功能。
与方法一相比,方法二赋予串行通信程序设计较大的灵活性,端口的读写可选择较简单的查询式,或通过设置与外设数据发送时间间隔TimeCycle相同的定时器:
SetTimer(1,TimeCycle,NULL),进行定时读取或发送。
CSampleView:
:
OnTimer(UINTnIDEvent) { char InputData[30]; m_Serial.ReadData(InputData,30); //数据处理 }
----若对端口数据的响应时间要求较严格,可采用事件驱动I/O读写,Windows定义了9种串口通信事件,较常用的有:
----EV_RXCHAR:
接收到一个字节,并放入输入缓冲区。
----EV_TXEMPTY:
输出缓冲区中的最后一个字符发送出去。
----EV_RXFLAG:
接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区。
----在用SetCommMask()指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事件的发生。
SetCommMask(hComm,0)可使WaitCommEvent()中止。
----方法三多线程下实现串行通信
----方法一,二适用于单线程通信。
在很多工业控制系统中,常通过扩展串口连接多个外设,各外设发送数据的重复频率不同,要求后台实时无差错捕捉,采集,处理,记录各端口数据,这就需要在自定义的串行通信类中创建端口监视线程,以便在指定的事件发生时向相关的窗口发送通知消息。
----线程的基本概念可详见VC++参考书目,Windows内部的抢先调度程序在活动的线程之间分配CPU时间,Win32区分两种不同类型的线程,一种是用户界面线程UI(UserInterfaceThread),它包含消息循环或消息泵,用于处理接收到的消息;另一种是工作线程(WorkThread),它没有消息循环,用于执行后台任务。
用于监视串口事件的线程即为工作线程。
----多线程通信类的编写在端口的配置,连接部分与单线程通信类相同,在端口配置完毕后,最重要的是根据实际情况,建立多线程之间的同步对象,如信号灯,临界区,事件等,相关细节可参考VC++中的同步类。
----一切就绪后即可启动工作线程:
CWinThrea*CommThread=AfxBeginThread(CommWatchThread, //线程函数名(LPVOID)m_pTTYInfo, //传递的参数THREAD_PRIORITY_ABOVE_NORMAL, //设置线程优先级 (UINT)0, // 最大堆栈大小 (DWORD)CREATE_SUSPENDED, // 创建标志(LPSECURITY_ATTRIBUTES)NULL); // 安全性标志
----同时,在串口事件监视线程中:
if(WaitCommEvent(pTTYInfo->idComDev,&dwEvtMask,NULL)) {if((dwEvtMask &pTTYInfo->dwEvtMask)==pTTYInfo->dwEvtMask){ WaitForSingleObject(pTTYInfo->hPostEvent,0xFFFFFFFF); ResetEvent(pTTYInfo->hPostEvent); //置同步事件对象为非信号态 :
:
PostMessage(CSampleView,ID_COM1_DATA,0,0); //发送通知消息 } }
----用PostMessage()向指定窗口的消息队列发送通知消息,相应地,需要在该窗口建立消息与成员函数间的映射,用ON_MESSAGE将消息与成员函数名关联。
BEGIN_MESSAGE_MAP(CSampleView,CView)//{{AFX_MSG_MAP(CSampleView)ON_MESSAGE(ID_COM1_DATA,OnProcessCom1Data) ON_MESSAGE(ID_COM2_DATA,OnProcessCom2Data) .....//}}AFX_MSG_MAPEND_MESSAGE_MAP()
----然后在各成员函数中完成对各串口数据的接收处理,但必须保证在下一次监测到有数据到来之前,能够完成所有的中间处理工作。
否则将造成数据的捕捉错误。
----多线程的实现可以使得各端口独立,准确地实现串行通信,使串口通信具有更广泛的灵活性与严格性,且充分利用了CPU时间。
但在具体的实时监控系统中如何协调多个线程,线程之间以何种方式实现同步也是在多线程串行通信程序实现的难点。
MSComm控件的属性
(必须要熟悉的几个——CommPort:
设置并返回通信端口号;Setting:
以字符串的形式设置并返回数据传输速率、奇偶校验、数据比特、停止比特;PortOpen:
设置并返回通信端口的状态,也可以打开和关闭端口;Input:
从接收缓冲区返回和删除字符;Output:
向传输缓冲区定一个字符串)
1)CommPort属性
voidSetCommPort(shortnNewValue);shortGetCommPort();
这一属性设置并返回连接的串行端口号,Windows将会利用该串口和外界通信。
在设计时,nNewValue可以设置成从1~16的任何数(默认值为1)。
但是如果用PortOpen属性打开一个并不存在的端口时,MSComm控件会产生错误68(设备无效)。
注意:
必须在打开端口之前设置CommPort属性。
2)Settings属性
voidSettings(LPCTSTRlpszNewValue);StringGetSettings();
该属性用于设置并返回数据传输速率、奇偶校验、数据比特、停止比特参数。
当端口打开时,如果valu
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 上位 机界面 编程