PIC16系列单片机与PC机 串行通信的软硬件实现 西安第四军医大学生物医学工程系 电子学教研室(710032) 焦 纯 陈马丁 李洪义 摘 要 介绍一种运用PIC16F84单片机实现与PC机串行通信的方法,并给出其硬件接口电路及通信源程序。 关键词 异步串行通信 发送与接收 VB4 Win95 串口查询法 | |
1 前言 美国Microchip公司的PIC16系列单片机是一种新型的CMOS工艺的8位单片机。其中,PIC16FXX单片机的程序存储器为电可擦除闪速存储器(flash),可多次修改程序,甚至可以在线编程。PIC16F83和PIC16F84片内数据存储器除RAM外,还有64字节的EEPROM,可以当作一般的或非易失性的数据存储器使用,简单方便。它还具有片内上电复位、延时电路、看门狗电路等。另外,PIC16系列单片机功耗极低,因而是一种非常适合在各种便携式设备中使用的高性价比的单片机,并已经得到了越来越广泛的应用。 2 硬件实现方法与电路 PIC16F84的程序存储器由1K×14的闪速(flash)存储器构成,它只有13条I/O口,1个定时器,为了尽量节省单片机的软硬件资源,采用下述异步串行通信的实现方法。 | |
由于PIC16F84本身没有专门的串行口,这里用其I/O口来模拟串行口的功能。 图1 接口原理图 3 PIC16F84单片机内通信程序的设计
| 图2 接收子程序框图 图3发送子程序框图 |
图2和图3分别是串行发送和接收的子程序流程框图。发送时,通过使数据发送端DX为低电平并保持B秒(9600波特率时为104μs)来发送起始位。随后每B秒钟通过置位或清零DX端把数据发送出去。这里的B是指一位所持续的时间(B=1/波特率)。接收时,数据接收端DR大约要每B/2秒(9600波特率时为52μs)查询一次以检测起始位,如果检测到起始位,则在大约1.5B秒(9600波特率时为156μs)后检测第一位数据位,随后每B秒钟检测一次其它的数据位。
相应的源程序如下:
clrwdt
;清片内看门狗btfsc
RA,DR;检测起始位goto
Rcvr ;未检测到起始位movlw
8;检测到起始位movwf
R_CNT;8位数据位call
Delay;延时B/2秒bcf
STATUS,C;清进位标志rrf
RevReg;LSB在先btfsc
RA,DR;该位为0;还是为1
bsf
RcvReg,MSB;为1call
Delay;延时B/2秒decfsz
R_CNTgoto
Rnextretlw0 | ;返回 |
clrwdt
;清片内看门狗movlw
8;发送位数为8movwf
X_CNTbcf
RA,DX;发送起始位call
Delay;延时B/2秒call
Delay;延时B/2秒rrf
XmtReg;LSB在先btfsc
STATUS,C;检测将要发送;的数据位
bsf
RA,DX;数据位为1btfss
STATUS,Cbcf
RA,DX;数据位为0decfsz
X_CNT;位计数为0则goto | Xnext | ;位计数不为0 |
;则发下一位数据位 | ||
Xstop | ||
call | Delay | ;延时B/2秒 |
call | Delay | ;延时B/2秒 |
Bsf | RA,DX | ;发送停止位 |
Retlw | 0 | ;返回 |
;延时子程序 | ||
Delay |
movlw | 12 | ;52μS延时 |
movwf | DCNT | ; |
Dnext | ||
decfsz | DCNT |
goto
retlw 0
;4 Win95的串行通信机制及串口查询法的原理
常用的DOS系统主要是工作在响应中断方式。PC机串行通信程序大多利用其BIOS块的INT14H中断,以查询串口的方式完成异步串行通信。
Windows系统函数即包含了通信支持中断功能。Win95系统为每个通信设备开辟了用户定义的输入输出缓冲区(即读/写缓冲区),数据进出通信口均由系统后台来完成。应用程序只需完成对输入输出缓冲区操作就可以了。实际过程是每接收一个字符就产生一个低级硬件中断,Win95系统中的串行驱动程序就取得了控制权,并将接收到的字符放入输入数据缓冲区。然后将控制权返还正在运行的应用程序。如果输入缓冲区数据已满,串行驱动程序用当前定义的流控制机制通知发送方停止发送数据。队列中的数据按“先进先出”的次序处理。
(1) 按协议的设置初始化并打开串口,这样做就是通知Windows本应用程序需要这个串口,并封锁其他应用程序使它们不能使用此串口。
(2) 配置这个串口。
(3) 在串口上往返地传输数据,并在传输过程中进行校验。
(4) 不需要此串口时,关闭串口。即释放串口以供其它应用程序使用。
在这四个步骤中,主要的程序代码集中在第(3)步。
串口查询法是一种主要工作在查询方式下的实现方法。当通信程序工作在“查询”方式时,可以不考虑Win95的进程和线程的问题。仅在串口有数据时,去读串口缓冲区就可以了,如图4所示。这种方法下确定串口读取的时机、握手协议及软件纠错的实现是程序员应考虑的主要问题。
图4 读取接收缓冲区数据流程图
| 图5 串口初始化流程 |
由于这种方法主要工作在查询方式。程序员必须完成相当一部分通信状态的检测工作,许多细节(甚至包括通信过程中的字符属性的转换)也必须通过程序代码完成。这种查询方法对通信双方协议的依赖性尤其大。双方通信协议的约定对程序实现的难易程度影响很大。 串口查询法中,一般串口初始化的流程如图5。 值得注意的一点是,此方法下协议的约定必须满足以下条件:即甲方发送时,乙方必须在甲方发送动作之前进入循环接收状态,直到接收到字符后通过对串口读取函数ReadFile返回值的判断跳出循环状态。 | |
同时,一般为了不使系统因循环等待接收而进入“死循环”状态,可以人为设置读取串口的循环次数,一般1000~10000次即可。 5 PC机内通信程序的实例 现约定甲方是PC机,乙方是单片机系统(如读卡器)。通信格式设置为2400波特率,8位数据位,1位停止位,无奇偶校验。 | 图6 通信协议流程 |
Private Function OpenThePort(cPort as String,cBaud as String,cParity
as String,cData as String,tStops asString)As Boolean ’ 打开串口的子过程
Dim lResult as Long
Dim lHandle as Long
Dim DCB_COMM as DCB
Dim cDCBConfig as String
lHandle = CreateFile(cPort,GENERIC_READ Or GENERIC_WRITE,
0&,0&,OPEN_EXISTING,0&,0&)
If lHandle = -1 Then ’打开串口失败
OpenThePort = False
MsgBox “串口可能正被其他应用程序占用!”
lResult = CloseHandle(lHandle) ’先关闭串口后再打开
If lResult = 0 Then
OpenThePort
Exit Function
End If
End If
cDCBConfig.band = 2400 ’设置DCB
cDCBConfig.parity = None
cDCBConfig.data = 8
cDCBConfig.stop = 1
lResult = BuildCommDCB(cDCBConfig,DCB_COMM) ’按用户设定配置一个DCB结构
If lResult = 0 Then
OpenThePort = False
MsgBox “无法建立DCB设备控制块”
Exit Function
End If
lResult = SetCommState(lHandle,DCB_Comm) ’实际设置一个串口的DCB
If lResult = 0 Then
OpenThePort = False
MsgBox “无法建立DCB设备控制块”
Exit Function
End If
OpenThePort = True
End Function
Private Sub SendHand ( ) ’发送握手信号的子过程
Dim Nchars As Long
Static Readbuff As String * 1
Static Writebuff As String * 1
Dim lpDCB As DCB
Dim lRet As Long
Dim lHandle As Long
Dim lpOverlapped As OVERLAPPED
Dim RNum As Integer
MsgBox “请把读卡器插在串口2上!”,
48,“提示窗口”
lHandle = OpenThePort(COMM1,2400,None,8,1)
lRet = PurgeComm( lHandle,1 ) ’清输出缓冲区
lRet = PurgeComm( lHandle,0 ) ’清输入缓冲区
lRet = GetCommState ( lHandle,lpDCB ) ’获得通讯口的状态
Shand:
Writebuff$= Chr$(&H8F)
lRet = WriteFile (lHandle,Writebuff$,1,Nchars,lpOverlapped )
’送握手信号入串口缓冲区
If lRet <= 0 Then
MsgBox “发送操作出错,卡握手信号未发送成功”, 16
GoTo Shand ’不成功则重发
Else
GoTo Qtest
End If
GoTo Shand
Qtest:
Readbuff$ =“ ” ’清除缓冲区为空
Do While lHandle ’循环查询串口
RNum = 0 ’设置读串口次数的指针为0
ReadAgain:
lRet = ReadFile( lHandle, Readbuff$,1,Nchars,lpOverlapped )
If lRet < 0 Then
MsgBox “读取应答信号时出错”, 16
End If
If lRet = 0 Then
If RNum > 1000 Then ’只读1000次串口,以免陷入死循环
MsgBox
"卡没有插接好或卡没有接在串口上!"
GoTo CloseP
End If
RNum = RNum + 1
GoTo ReadAgain
End If
If Hex$(Asc(Readbuff)) <> Hex$(&HFF) Then GoTo Shand
’回送码不正确则返回继续发送握手信号
Else
Label1.Caption = “握手信号是:”
+Hex$(Asc(Readbuff$))
Msgbox “握手信号正确,已正确联机”
GoTo CloseP
End If
Loop
CloseP:lRet = CloseHandle( lHandle )
If lRet = 0 Then
MsgBox “串行通讯口关闭成功”,
48,“提示窗口”
End If
End Sub
这里要注意的是:当PC机与单片机系统通信时,单片机数据存储区( RAM )内的数据是十六进制,在信号线上传输的是十六进制数的ASCII码的二进制形式;而Windows系统下使用的是ANSI码,ANSI码仅在前126个与ASCII码相同。即在Win95下接收到的是十六进制数的ASCII码的字符串,可先转换为ANSI码后再在Win95下还原为十六进制数。
具体为:Code$=Hex$(Asc ( Readbuff$ ) )
另外,由于32位API函数参数的数据类型的变化,所有整形参数都被换为长整型(Long)以支持32位的处理,这一点在设置返回值时尤其如此。
6 结束语
以上的软硬件在我们的实践中达到了较为理想的效果。通过软件节省了硬件的开销,并通过在PIC16F84单片机系统和PC机双方的通信软件内增加握手信号,达到了软件数据校验的目的,获得了较高的通信可靠性。
文章评论(0条评论)
登录后参与讨论