发表于:2012/2/23 14:23:30
#0楼
附件:
[本地下载]1\利用中断与PC机通讯,
2\当PC机发送的适当的命令时做指定的工作并回传
出现的问题:
1\多次测试,得用串口测试工具,发送51 01 00 00 00 01到单片机时,由单片机的显示灯,显示只收到1个数(每次均是如此)
2\为验证相关的数据,将数据发送回PC机,一个是时间中断的次数及接收中断的两个时间间隔,不可能为0的,
3\每次发回的数据应是三个,但在串口助手中却有6到9个数回来不等
我已检查多次了,真没发现问题的原因,请各位大大帮忙检查一下?
谢谢
;串口1与PC联通
;适用于是STC stc89C51
;在伟福6000中用仿真器POD-8051(8031)编译
auxr equ 08eh ;串口2的辅助寄存器,用于控制brt的开关,是否翻倍,及计数的模式
;用50 ~59 放置PC发回来的6(8)个数字要对数字测试防止错误数据回来
PCdatasit equ 5AH ;数据放置开始位的前一位如开始放在60H由此数为5FH
PCallNo equ 5BH ;按协议该PC应收回的数据的个数
PCdataNo equ 5CH ;记录PC收回来的顺序数,
PCanswer equ 39H ;对PC数据检查后的回应值方便程序一次性将数据发回PC
PCdataXRL equ 3AH ;放置异或的结果,检测时的异或与发送时的异或同在一个中断中,故共用
;====================================================
tempL equ 37H ;放置回收温度数据的低位
tempH equ 38H ;放置温度数据的高位,用于计算最高值
sendPCsit equ 36H ;发送数据的开始位
FirsTime equ 45H ;记录时间中断的次数用于控制接收数据时的时间间隔
NextTime Equ 46H ;与上面的一同使用
;50H~58H用于记录收到的PC数据,
;====================================================
MyControl equ P2.5 ;检测控制位用于确定PC程序的设置
CallSheet bit P1.3 ;控制某个磁吸吸合
;====================================================
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
ORG 0000H
LJMP MAIN
org 000BH ;T0溢出产生的中断,会较串口中断高级,先执行
LJMP T0_ISR ;中断程序名
ORG 0023H
LJMP comreti ;串口中断
ORG 0050H
MAIN:
mov sendPCsit,#37H ;发送数据的开始位
mov PCdatasit,#4FH ;equ 5AH ;数据放置开始位的前一位如开始放在60H由此数为5FH
mov PCallNo,#06H ;equ 5BH ;按协议该PC应收回的数据的个数
mov PCdataNo,#00H ;equ 5CH ;记录PC收回来的顺序数,
clr ea ;关闭中断总开关
MOV SP,#5FH ;设置堆栈指针,即堆栈放在60H后面
MOV TH0,#00H ;定时器0以FFFF为一次溢出,用于计时
MOV TL0,#00H
mov tmod, #21H ;T0的工作方式1(16位),T1为自动重装的方式
;没有这个中断的次数算出的总时间会错误!
;串口1初始化,与PC进行通讯,
lcall UARTINIT ;stc串口1的初始化代码用于与USB模块通讯
setb ea ;CPU开中断
setb et0 ;计时器0可中断
SETb TR0 ;启用定时器0用于记录单片机运行的时间长度
clr P0.0
Ajmp $
ret
;**********************************************
;;主循环形成
;**********************************************
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;此为STC自己的代码,初始化波特率发生器
;请用实际行动支持宏晶STC大陆本土MCU统一全球市场差0.16%
UARTINIT: ;9600bps@12MHz
ANL PCON,#7FH ;波特率不倍速
MOV SCON,#50H ;8位数据,可变波特率
ORL AUXR,#40H ;定时器1时钟为Fosc,即1T
ANL AUXR,#0FEH ;串口1选择定时器1为波特率发生器
ANL TMOD,#0FH ;清除定时器1模式位
ORL TMOD,#20H ;设定定时器1为8位自动重装方式
MOV TL1,#0D9H ;设定定时初值
MOV TH1,#0D9H ;设定定时器重装值
CLR ET1 ;禁止定时器1中断
SETB TR1 ;启动定时器1
setb ES ;允许串行中断
RET
;**************************************************
;T0定时器中断;
T0_ISR:
;记录中断的次数,用于比较两个接收周期的间隔,确定两个接收周期
inc 45H ;45H记录中断的次数加一
RETI ;中断返回
;****************************************************
chk4546: ;用于将两个收数时间的判断,两者间差300MS 则为新的一周期
;已测试,利用自动借位因时间是在前进的,得出结果,并自动改变收数的起始位
;4个或以上的表示4*70Ms=280Ms
MOV A,firstime
clr c ;否则会产生不知道的结果
subb a,nexttime ;自动借位
cjne A,#05H,noequ04 ;再将A与2比较不同的转
cpl P0.5 ;=5
;mov r6,#5
Ajmp ithi
noequ04:
jc exitchk4546 ;itequ >1的转移(即不够减);A<02作当前一个
ithi:
mov PCdataNo,#00H ;时间足够,已是下一个了重新收数吧
exitchk4546:
ret
;将串口的数据变量化!希望以后的通用化
;用50 ~59 放置PC发回来的6(8)个数字要对数字测试防止错误数据回来
;PCdatasit equ 5AH ;数据放置开始位的前一位如开始放在50H由此数为4FH
;PCallNo equ 5BH ;按协议该PC应收回的数据的个数
;PCdataNo equ 5CH ;记录PC收回来的顺序数,
;====================================================
;为什么一个周期只中断一次?
comreti:
clr ES ;不允许串行中断
jnb ri,exitcom1 ;ri=1表示为接收中断
;产生接收中断
push acc
push psw
mov 4FH,r0 ;因为其它中断会用到R0,怕出错,先送出
push 4FH
;以时间来衡量是否下一个开始,如果是则将记录指针回0
;lcall chk4546 ;检查否一段时间内的开始数据,如果是,已将指针回到PCdataNo 00
;防止如接口问题不断有数回来的情况一旦FF以后,不再理会
mov A,PCdataNo ;中断回来的次数有关!
clr c
add A,#1
jc sriexit ;超过FFH,不处理
;记录每个周期收数的次数并判定是否大于指定的数量
inc PCdataNo ;用于记录第几次收到数!
mov A,PCallNo ;应该回来的总数
cpl A ;求反即#FFH-PCallNo
CLR C
ADD A, PCdataNo ;有没有可能 不断发来
JC sriexit ;超过PCallNo个就不再处理直接退出
;即收到的6个数据按顺序放在50H~55H中,
mov a, PCdatasit ;指定的放置数据的初始位置
add a, PCdataNo ;开始地址+1也是新的放置地址,前面有了加1操作
mov r0,a ;将新的地址装入R0用于新的寻址方式
mov @r0,sbuf ;放入相对的地址中
mov A,PCdataNo ;准备对比已收到的数量
cjne A,PCallNo,sriexit ;查是否收到了所有的指定数量的数据
;cjne A,#01H,sriexit ;查是否收到了所有的指定数量的数据
;已是6位数接着检查数据是不是合理,不管是否合理也会发送数,只是不全时发的数不一样
lcall chkPCdata ;检查数据是否合理并发送
mov PCdataNo,#00H ;因为关了中断,不会再有中断了,不用担心有干扰!
sriexit: ;其它的收数不理会!并清空标收数标志
mov NextTime,FirsTime ;用46H记录上次的接收时间
pop 4FH
mov r0,4FH
pop psw
pop acc
exitcom1:
lcall chkNoFromPC ;查到底收了几个数
clr ri
SETB ES ;允许串断
reti
;**************************************************
chkNoFromPC: ;此为测试程序,用于测试,到底接收了多少个数据
mov A,PCdataNo
cjne A,#01H,PC1
cpl P0.1
ajmp exitCHKNoFromPC
Pc1:
cjne A,#02H,PC2
cpl P0.2
ajmp exitCHKNoFromPC
Pc2:
cjne A,#03H,PC3
cpl P0.3
ajmp exitCHKNoFromPC
Pc3:
cjne A,#04H,PC4
cpl P0.4
ajmp exitCHKNoFromPC
Pc4:
cjne A,#05H,PC5
cpl P0.5
ajmp exitCHKNoFromPC
Pc5:
cjne A,#06H,PC6
cpl P0.6
ajmp exitCHKNoFromPC
Pc6:
cpl P0.0
exitCHKNoFromPC :
ret
;================================================
chkPCdata: ;检查PC发来的数据放在50H~5AH间50H为指针
;pc会发来6位数,最前数据号,四位为数据,最后一个为异或验证码
;要在发送数据之后回传
;51A必须是51H作为命令前导
;第一位数据,01表示手动降温,
;02表示重设保温时间,按保温执行后面三位是新的保温时间
;没有数据来根本没有不会触发!
mov A,PCdataNo ;前面已检查过回来的数字,但为了程序的严谨性加入
cjne A,#00H,PCSENDED ;不为0表示PC有数据发送过来
mov PCanswer,#00H ;在发回PC的数据中记录
Ajmp errfromPC ;没有数据来也要发回去
;在中断里产生会对各个R有影响,所以不用!除中断时用POP了的R0
PCSENDED:
mov A,PCdatasit
add a,#01H
mov R0,A
mov A,@R0 ;第一个前导数
cjne A,#51H,errfromPC ;第一个数不是前导数51H的为错误的数据
;对四个数据进行异或操作XRL A,direct 按位‘异或',结果送入A 中
mov A,PCdatasit
add a,#02H
mov R0,A
mov A,@R0 ;第一个真正的数据
mov PCdataXRL,A
mov A,PCdatasit
add a,#03H
mov R0,A
mov A,@R0 ;第二个真正的数据
XRL PCdataXRL,A
mov A,PCdatasit
add a,#04H
mov R0,A
mov A,@R0 ;第三个真正的数据
XRL PCdataXRL,A
mov A,PCdatasit
add a,#05H
mov R0,A
mov A,@R0 ;第四个真正的数据
XRL PCdataXRL,A
mov A,PCdatasit
add a,#06H
mov R0,A
mov A,@R0 ;第PC发来的校验码
cjne A,PCdataXRL,errfromPC ;异或验证不成功
;已收到合格的数据
mov A,PCdatasit
add a,#02H
mov R0,A
mov A,@R0 ;第一个真正的数据
cjne a,#01H,notCHKControl ;要求测量是否有控制的要求
mov A,MyControl ;将我的控制位回到A中
cjne A,#00H ,notcontrol ;脚为低电平
;低电平,为闭合,要求不要切割
;发送00回去PC
mov templ,#00H
ajmp exitchkpc
notcontrol:
;高电平,可以切割了
;发送01回PC
mov templ,#01H
ajmp exitchkpc
notCHKControl:
mov A,@R0 ;第一个真正的数据
cjne a,#02H,errfromPC ;要求打开开关
;打开callsheet ;300MS按下
lcall callmysheet
;发送02回PC
mov templ,#02H
ajmp exitchkpc
errfromPC:
mov templ,#04H
;发送04回PC
exitchkpc:
lcall sendTempToPc ;;只要收到数就应向PC发回,不论对与否
ret
;================================================
send1bit: ;单个发送数据回PC机
clr ti
MOV SBUF,A
;如何加入时间?防止进入死循环
mov r6,#05H ;这里约为0.3S
sendloop33:
MOV R5,#0ffH
sendLOOP23:
MOV R4,#0ffH
sendLOOP13:
jb ti ,exitsendtemp3 ;0.5S内完成的都算完成
DJNZ R4,sendLOOP13 ;两周期
DJNZ R5,sendLOOP23
Djnz r6,sendloop33
exitsendtemp3:
clr ti
; 每个字间隔点点时间
MOV R4,#10
DJNZ R4,$
ret
;利用 正常 串口1将37H(tempL),38H(tempH) 39H 发送到PC
sendTempToPc:
;测试是否进入了此处,并将相关的数据发送到PC机中用于检测
cpl P0.7
mov A,firstime
lcall send1bit
mov A,nexttime
lcall send1bit
mov A,tempL
lcall send1bit
ajmp exitsendtempret ;退出
;后面的发送为正常程序时用到
mov PCanswer,#00H
mov tempH,#00H
;只用了templ作回答地址
mov templ ,45H
mov temph,46H
mov pcanswer ,52H
mov A,TempL
XRL A,TempH
XRL A,PCanswer
mov PCdataXRL,A ;将发送数据的异或结果送到3ah用于PC机验证
mov r0,#37H ;从37H开始发送这个没有办法的用变量?
mov r3,#4
clr ti
myrese:
mov A, @R0 ;将R0地址中的数转到A
MOV SBUF,A
;如何加入时间?防止进入死循环
mov r6,#05H ;这里约为0.3S
sendloop3:
MOV R5,#0ffH
sendLOOP2:
MOV R4,#0ffH
sendLOOP1:
jb ti ,exitsendtemp ;0.5S内完成的都算完成
DJNZ R4,sendLOOP1 ;两周期
DJNZ R5,sendLOOP2
Djnz r6,sendloop3
exitsendtemp:
clr ti
; 每个字间隔点点时间
MOV R4,#250
DJNZ R4,$
inc r0
djnz r3,myrese
exitsendtempret:
ret
;============================
;****************************************************
;====================================================
; 延时子程序同样用到R456延时太长没必要!可以
;====================================================
usbDELAY: ;为了方便这个用在外面 12c5a60S2太快,约高了3倍?只是0.3S
;mov R6,#5 ;(((3*255+3)+2)*255+2)*5/1000000=0.98176S
usbloop3: MOV R5,#0ffH
usbLOOP2: MOV R4,#0ffH
usbLOOP1: NOP ;一个周期
DJNZ R4,usbLOOP1 ;两周期
DJNZ R5,usbLOOP2
djnz r6,usbloop3
RET
;****************************************************
callmysheet:
setb callsheet ;高电平
;setb P0.3
;clr P0.3 ;亮个灯看看?
clr callsheet ;低电平有效
mov R6,#05H
lcall usbdelay ;延时0.3S后高电平
setb callsheet ;高电平
;setb P0.3
ret
END