VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)

VB爱好者乐园(VBGood)

 找回密码
 立即注册
搜索
楼主: xuhuaping

[原创] 边学边写之——51单片机软硬件全实践学习笔记(新手教程系列)

[复制链接]
 楼主| 发表于 2008-12-31 00:59:54 | 显示全部楼层
51编程第四课(2)
接着说定时/计数器  
上次说了定时/计数器的工作原理
今天来说具体的操作方法

定时/计数器是个两用的设备,即能定时,又能计数,
因此称做“定时/计数器”,简称“C/T”

定时/计数器需要有开关,需要能控制“定时”和“计数”功能转换,还需要能设置定时时间和计数初值,  
这些功能是通过一些叫做“控制寄存器”的东西实现的

定时器是一直工作吗?
你只有告诉秘书说:“下午3点叫我开会”  
她才需要工作  
如果你不需要它,就不应该让它叫你
所以定时器需要有开关
  
定时/计数器是两用的  
你需要它定时,它就定时,需要它计数,它就计数  
就是说“定时”和“计数”需要选择模式  
所以需要“模式控制”开关  

定时器不是随便多长时间闹都可以  
而是根据你需要的时间闹铃
所以需要设置定时初值  

比如这些功能是通过一些“控制寄存器” 实现的,
每个控制寄存器有8位(1个字节),
相当于一个有8个开关的“开关柜”,
每个开关实现一定的功能,比如“开关定时器”或“改变工作模式”等。

为了方便使用,
我们会给不同的“开关柜”和里面的每个“开关”都取个名字
比如:“一号配电柜”,“主发电机开关”

同样,我们也给每个控制寄存器取名字,
用于控制定时/计数器的寄存器一共有两个:  
一个叫“TCON”,也就是“定时/计数器控制寄存器”  
一个叫“TMOD”,也就是“定时/计数器工作模式控制寄存器”

“定时/计数器工作模式控制”
也就是控制它以“定时器”还是“计数器”方式工作。
  
而“定时/计数器控制”,主要用来控制定时/计数器的“启动”和“停止”等功能。

“模式控制”是用来控制工作方式的,而“控制”是用来控制开关的  

先看“定时/计数器控制寄存器”也就是“TCON”:

TCON 从高到低,一共一个字节,8个位(第7位到第0位)
其功能定义如下:  

7      6      5      4      3      2      1     0
空     TR1    空     TR0    空     空     空    空  

可见 TCON 只使用了第 4 位和第 6 位,  
其他的位都没用到。  

第 6 位是“TR1”,是定时/计数器 1 的开关  
第 4 位是“TR0”,是定时/计数器 0 的开关
  
为 1 表示开,为 0 表示关  

例如我们要启动 定时/计数器 1  
写成程序就是
   
        TR1 =1;  

如果要关闭 定时/计数器 0,就是

        TR0=0;  

当然也可以一次操作整个寄存器,例如要同时开两个定时/计数器,则

        TCON = 0x50;     // 0x50 = 0101 0000 ,可见第4和6位都是1,即TR0=1,TR1=1
  
如果要全关了,则:
  
        TCON = 0x00;     // 0x00 = 0000 0000 ,TR0和TR1都是0

再看“定时/计数器工作模式控制寄存器”,也就是 TMOD 寄存器:

TMOD寄存器也是一个字节,共8位,4 位一组分成两个完全相同的部分:
高 4 位用来控制定时/计数器 1,
低 4 位用来控制定时/计数器 0  

TMOD寄存器各位的定义如下:

   7      6      5      4       3      2      1      0
   GATE   T/C    M1     M0      GATE   T/C    M1    M0

GATE是“门控”,用于设置是否需要触发信号,
GATE为1的时候,光打开开关TR1或TR0还不工作  
要有另外两个开门的引脚也为1才工作  


C/T 是“定时/计数”功能选择  
为 1 表示计数器,为 0 表示定时器  
例如我要让定时器1定时,定时器0计数,可以用这条语句:

       TMOD=0x40;   

解释一下:0x40 = 0100 0000 ,
可见第6位(C/T 1)为1,表示定时/计数器1为定时模式
而第2位(C/T 0)为0,表示定时/计数器0为计数模式


M1 和 M0 是工作方式,一共4种方式
第5、4位和第1、0位为两组工作方式设置位,
分别控制定时/计数器 1 和 0 的工作模式  

M1 M0 共有4种组合,分别表示4种工作方式:
  
M1 M0
0  0    13位定时/计数器,高8位在TH里,低5位在TL里
0  1    16位定时/计数器,高位在TH,低位在TL
1  0    自动重设初值的8位定时/计数器
1  1    这个只能用在定时器0,定时器1没有这个模式,把定时器0当成两个8位定时/计数器

M1 M0 为 1 0 这种模式的工作方式解释一下:
假如你有个闹钟,它每次定时都响一次,然后定的时间就丢了。
如果你想让它再响一次,还得重设初值,很不方便。
而当M1 M0 为 1 0 时,每次闹铃后则不需要重设初值,它会自动装入上一次定时/计数的初值。
比如你设置了 2 小时闹铃,闹完以后,它自动又设成了2小时,
这样每隔2小时都会闹一次铃。


最后说一下定时/计数初值的设置和计数结果的提取。

51单片机的定时/计数器最多能保存16位二进制值  
就是两个字节,表示10进制的范围为0~65535  
每个定时/计数器的定时/计数值分别存放在两个8位寄存器里,
分别叫 TL 和 TH,TL 用来存放低字节,TH 用来存放高字节

定时/计数器 0 的数据寄存器叫 TL0 和 TH0
定时/计数器 1 的数据寄存器叫 TL1 和 TH1

当M1M0模式设置为8位定时/计数器时,
只使用TL存放数据,表示十进制范围为0~255

而当M1M0模式为13或16位定时/计数器时,
使用TL存放低8位,TH存放高8位

要说明一点:定时器的工作方式是“溢出”方式
就是说只有定时器到了上限(8位为256,16位为65536)时
才会“闹铃”(即产生计数中断事件),
因此定时器的初值是用定时器最大值减去需要定时的周期数计算的,

例如我们要定时 50000 个周期,采用16位定时器,则定时器初值需设置为:

65536 - 50000 = 15536

就是说,定时器需要以 15536 为初值开始工作,
才能在经过50000个定时周期后“闹铃”。

由于这个初值要分别存放在两个8位寄存器 TL和TH中,
因此我们还需要把一个16位初值拆分成两个8位数值(低8位和高8位)
才能“设置”到TL和TH里。

举例来说:

一个89S51,采用12M晶振,
由于12个振荡周期为一个指令周期,
因此每秒可执行1M条指令。
每个指令周期定时器加1,
因此定时器周期等于指令周期
等于  1秒/1M = 1微秒

我们让它定时50毫秒

50毫秒/1微秒=50000个周期

即定时50毫秒需要定时器需计时50000次

那么我们采用16位定时器,
计时范围从0~65535,
要定时50000次,则初值为:

65536-50000=15536 次

先把15536转换成16进制。。。
15536=0x3CB0
高位为3C,低位为B0

那么我们把高位 3C放进TH0,低位B0放进TL0

就相当于设置了初值15536

TH0=0x3C;
TL0=0xB0;


课后实验:

采用12M晶振的80S51,利用定时器产生1秒的信号控制闪灯。

我们来写下这个试验的程序

#include <reg51.h>

sbit LED = P1^0;

unsigned char t;

void timer0(void) interrupt 1 using 1
{
        TH0 = 0x3C;   //计数初值重载
        TL0 = 0xAF;

        t = t + 1;

        if(t >= 20)
        {
                t=0;               
                LED=!LED;     //P1.0取反
        }
}

void main(void)
{
        t = 0;
        LED = 1;

        TMOD = 0x01;  //T/C0工作在定时器方式1
       

        TH0 = 0x3C;  //预置计数初值
        TL0 = 0xAF;

        EA = 1;     //CPU开中断
        ET0 = 1;    //T/C0开中断
        TR0 = 1;    //启动T/C0开始定时
       
        while(1); //主循环
}
我来逐句的解释下。。
sbit LED = P1^0;  sbit 是C51的数据类型 表示"特殊的位"
P1 ^ 0 表示到 P1 口的第 0 位 。
表示 定义一个 "特殊位" 类型的变量  LED 并令它表示 P1 口的第 0位
unsigned char t;定义一个无符号变量t
我们先看主函数那里的程序
        t = 0;
        LED = 1;
t=0和LED=1设置t和LED的初始值
        TMOD = 0x01;  //T/C0工作在定时器方式1
TMOD寄存器各位的定义如下:

   7      6      5      4       3      2      1      0
   GATE   T/C    M1     M0      GATE   T/C    M1    M0
GATE是“门控”,用于设置是否需要触发信号,
这里不需要触发信号,因此GATE为0

C/T 是“定时/计数”功能选择,为 1 表示计数器,为 0 表示定时器  
这里我们用定时/计数器0做为定时器,因此第2位T/C取0

M1 和 M0 是工作方式,这里取M1 M0为0 1,
表示定时/计数器0作为16位定时/计数器,高位在TH,低位在TL

综合起来,
TMOD=0x01=0000 0001

下面来计算一下这个定时器的初值:

由于采用12M晶振,
12个振荡周期为一个指令周期,
因此每秒可执行1M条指令。
每个指令周期定时器加1,
因此定时器周期等于指令周期
等于  1秒/1M = 1微秒

我们让它定时50毫秒

50毫秒/1微秒=50000个周期

即定时50毫秒需要定时器需计时50000次

那么我们采用16位定时器,
计时范围从0~65535,
要定时50000次,则初值为:

65536-50000=15536 次

先把15536转换成16进制。。。
15536=0x3CAF
高位为3C,低位为B0

那么我们把高位 3C放进TH0,低位B0放进TL0

就相当于设置了初值15536
        EA = 1;     //CPU开中断
        ET0 = 1;    //T/C0开中断
        TR0 = 1;    //启动T/C0开始定时

        while(1); //主循环

这个是等待计时器中断并一直循环。。

好我们来说中断
void timer0(void) interrupt 1 using 1
{
        TH0 = 0x3C;   //计数初值重载
        TL0 = 0xAF;

        t = t + 1;

        if(t >= 20)
        {
                t=0;               
                LED=!LED;     //P1.0取反
        }
}

void timer0(void) interrupt 1 using 1
interrupt 1是设置中断号为1的中断,即定时器0的中断
timer0 是这个中断处理函数的名字,可以随便取个名字,好记就行

        TH0 = 0x3C;   //计数初值重载
        TL0 = 0xAF;

为什么这里有重新设置了计数的初值呢。。
因为这我们M1 M0的工作方式选择了16位定时/计数器,高位在TH,低位在TL
它是用了一次后就丢失了。。
所以我们需要在中断里重新在装载初值,
以保证每次中断都刚好是50000个周期(即50毫秒)。

        t = t + 1;

这里的t=t+1就是中断一次t就+1

        if(t >= 20)
        {
                t=0;               
                LED=!LED;     //P1.0取反
        }

这句的意思说当t大于或等于20时就把t归0
20 * 50毫秒=1秒
这样就相当于每1秒钟LED这个值就取反,
也就是P1.0口取反。。。
于是接到P1.0口上的发光二极管就亮1秒,灭一秒地闪烁了。。。

[ 本帖最后由 DreamonII 于 2008-12-31 01:12 编辑 ]

评分

参与人数 1威望 +6 金钱 +4 人气 +3 收起 理由
DreamonII + 6 + 4 + 3 精品文章

查看全部评分

回复 支持 反对

使用道具 举报

发表于 2009-1-13 17:37:45 | 显示全部楼层
void main(void)
{
        t = 0;
        LED = 1;

        TMOD = 0x01;  //T/C0工作在定时器方式1
        

        TH0 = 0x3C;  //预置计数初值
        TL0 = 0xAF;

        EA = 1;     //CPU开中断
        ET0 = 1;    //T/C0开中断
        TR0 = 1;    //启动T/C0开始定时
        
        while(1); //主循环
}


1,这段程序没错么?WHILE(1)一直执行空指令

是不是WHLIE应该是总循环才对?

即:while(1){
        t = 0;
        LED = 1;

        TMOD = 0x01;  //T/C0工作在定时器方式1
        

        TH0 = 0x3C;  //预置计数初值
        TL0 = 0xAF;

        EA = 1;     //CPU开中断
        ET0 = 1;    //T/C0开中断
        TR0 = 1;    //启动T/C0开始定时
}

2,还有想问,TIME0函数一直未被调用,P1.0怎么会闪烁呢,是执行EA=1;ET0=1;TR0=1;时,这个中断函数自动执行么?

3,EA=1和ET0=1是什么意思,是TR0=1的必要条件么?

我新手望指点一二,辛苦了

[ 本帖最后由 我是菜鸟哦 于 2009-1-13 17:55 编辑 ]
回复 支持 反对

使用道具 举报

发表于 2009-1-22 17:10:21 | 显示全部楼层
确实很有恒心,值得我学习
回复 支持 反对

使用道具 举报

发表于 2009-1-22 17:12:17 | 显示全部楼层
原帖由 我是菜鸟哦 于 2009-1-13 17:37 发表
void main(void)
{
        t = 0;
        LED = 1;

        TMOD = 0x01;  //T/C0工作在定时器方式1
        

        TH0 = 0x3C;  //预置计数初值
        TL0 = 0xAF;

        EA = 1;     ...



这位仁兄,看来你的电子知识要好好补补了,最后的while(1)是等待中断,也就是所有的事情都是在中断中处理的,while(1)不能加在前面.否则出错.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-2-5 15:20:37 | 显示全部楼层
C51 新手编程(第五课)
串行口
今天介绍单片机怎样通过串行口与计算机通信。
1.        概述
通信实质就是以计算机为中心,通过某些通信线路与设备,对二进制编码的字母、数字、符号化及数字化声音、图像信息进行的传输、交换和处理。由于计算机的信息是以字节(8位)或字(1个或几个字节)为单位进行处理,所以以字或字节为传输单位比较合适,今天重点介绍使用串行口进行通信。
所谓串行通信就是所传送数据的各位按顺序一位一位地发送或接收。在串行通信系统中,传输数据的各位只在一条传输线上传送,因此在长距离传送数据时,比较经济,缺点是传输速度较慢。
2.        串行通信种类
串行通信分为同步通信和异步通信两种方式。
(1)        同步串行通信:要求在发送设备的时钟频率与接收设备的时钟频率一致的条件下,发送设备先发出一个(或几个)同步字符,随之发送一组数据。接收设备一旦检测到符合规定的同步字符,便连续按顺序接收所有数据。优点:速度较快。缺点:对设备的硬件结构要求较高。
(2)        异步串行通信:发送设备和接收设备的时钟是不一致的。数据以一个字符一个字符地顺序按帧传送。帧格式示意图如图2-1:
0        0/1        0/1        0/1        0/1        0/1        0/1        0/1        0/1        1
起始位        D0        D1        D2        D3        D4        D5        D6        奇偶校
验位        停止位
        七位数据               
图2-1异步通信信号传输格式
解释一下:开头一个起始位“0”,接着是5~8位数据位,规定低位在前,高位在后(图上是7位数据位),然后是一个奇偶校偶位(可以省略),最后加上一个停止位“1”表示字符的结束。
由于在技术实现上使用发送设备与接收设备的时钟频率长期严格保持一致有一定的困难,目前实际使用较多的还是串行异步通信方式。
3.        串行口中数据的双工传送
在串行通信中,机器的通信接口只能发送或接收,这种是单向传输的,称为单工传送,如果两机之间能够进行双向传送,这种方式就称为双工传送。
在双工传送中,如果接收和发送不能同时进行,只能分时接收和发送,这种传送就叫半双工传送,若两机的发送和接收均可同时进行,则为全双工传送。
4.        串行通信速率
波特率是在单片机异步通信中对数据传送速率的规定,即每秒钟传送信息的二进制位数。
5.        串行接口
从本质上讲,所有的串行接口都是以并行数据形式与CPU接口,而以串行数据形式与外部逻辑单元接口。它们的基本功能是从外部逻辑单元接收串行数据,转换成并行数据后传送给CPU;或者从CPU发出并行数据,转换成串行数据后输出给外部逻辑单元。
串行接口至少包含一个接收器和一个发送器。
6.        51串行接口
51单片机内部有一个功能很强的全双工的串行接口电路单元UART(即通用异步接收器/发送器,Universal Asynchronous Receiver/Transmitter),发送时数据由TXD端送出,接收时数据由RXD端输入,有两个缓冲器SBUF,一个作发送缓冲器,另一个作嫌收缓冲器。该串行口有4种工作方式,波特率可用软件设置,由片内的定时/计数器产生。串行口接收或发送数据均可触发中断系统,使用十分方便。
6.1.        串行口的组成
6.1.1.        串行口控制寄存器SCON,其格式如下:
D7        D6        D5        D4        D3        D2        D1        D0
SM0        SM1        SM2        REN        TB8        RB8        TI        RI
(1)        SM0,SM1:串行口工作方式控制位,具体工作方式见表6-1:



表6-1 串行口工作方式控制
SM0        SM1        工作方式        说明        波特率
0        0        方式0        同步移位寄存器        fOSC/12
0        1        方式1        10位异步收发        由定时器控制
1        0        方式2        11位异步收发        fOSC/32或fOSC/64
1        1        方式3        11位异步收发        由定时器控制
(2)        SM2:多机通信控制位(方式2,3)
1->只有接收到第9位(RB8)为1,RI才置位;
0->接收到字符,RI就置位。
(3)        REN:串行口接收允许位
1->允许串行口接收;
0->禁止串行口接收。
(4)        TB8:方式2和方式3时,为发送的第9位数据,也可以作奇偶校验位。
(5)        RB8:方式2和方式3时,为接收到的第9位数据;方式1时,为接收到的停止位。
(6)        TI:发送中断标志
由硬件置位,必须由软件清零
(7)        RI:接收中断标志
由硬件置位,必须由软件清零
6.1.2.        电源控制寄存器PCON:
PCON的第7位SMOD是与串行口的波特率设置有关的选择位。
D7        D6        D5        D4        D3        D2        D1        D0
SMOD                                                       
(1)        SMOD:串行口波特率加倍位
1->方式1和方式3时,波特率=定时器1溢出率/16;方式2波特率= fOSC/32;
0->方式1和方式3时,波特率=定时器1溢出率/32;方式2波特率= fOSC/64;
通常情况下,使用单片机的串行口时,选用的晶振比较固定,一般为6MHz,12MHz和11.0592MHz,常用于与微机的通信;选用的波特率也相对固定。串行口常用的波特率及相应的设置见表6-2:
表6-2 串行口常用波特率
串行口工作方式        波特率        fOSC = 6 MHz        fOSC = 12 MHz        fOSC = 11.0592 MHz
                SMOD        TMOD        TH1        SMOD        TMOD        TH1        SMOD        TMOD        TH1
方式0        1M                                ×        ×        ×                       
方式2        375K                                1        ×        ×                       
        187.5K        1        ×        ×        0        ×        ×                       




方式1






方式3        62.5K                                1        20        FFH                       
        19.2K                                                        1        20        FDH
        9.6K                                                        0        20        FDH
        4.8K                                1        20        F3H        0        20        FAH
        2.4K        1        20        F3H        0        20        F3H        0        20        F4H
        1.2K        1        20        E6H        0        20        E6H        0        20        E8H
        600        1        20        CCH        0        20        CCH        0        20        D0H
        300        0        20        CCH        0        20        98H        0        20        A0H
        137.5        1        20        1DH        0        20        1DH        0        20        2EH
        110        0        20        72H        0        20        FEEBH        0        10        FEFFH
7.        实例
单片机串行口发送/接收程序,每接收到字节即刻发送出去;和微机相接后微机键入的字符回显在屏幕上。代码如下:

/******************************************************
* Title:       串口接收发送程序(字母)
* Description:  
* Copyright:   
* Company:      
* @author       Enron
*******************************************************/
#include <reg51.h>

#define uchar unsigned char

/******************************************************
                       主函数
*******************************************************/
void main()
{
        uchar a;

        TMOD=0x20;
        TL1=0xfd;   // 采用11.0592MHz的晶振,波特率9600bps
        TH1=0xfd;
        SCON=0xd8;
        PCON=0x00;
        TR1=1;

        while(1)
        {
                while(RI==0);
                RI=0;
                a=SBUF;
                SBUF=a;
                while(TI==0);
                TI=0;
        }
}

解释一下:TMOD = 0x20 即
D7        D6        D5        D4        D3        D2        D1        D0
GATE        T/C 1        M1        M0        GATE        T/C 0        M1        M0
0        0        1        0        0        0        0        0
看出来,我们使用定时器 1 的工作方式1,即定时器 1 为自动重设初值的8位定时/计数器。

        TH1 = 0xFd;                //在11.0592MHz下,设置串行口波特率为9600,方式3
        TL1 = 0xFd;
串行口工作方式        波特率        fOSC = 6 MHz        fOSC = 12 MHz        fOSC = 11.0592 MHz
                SMOD        TMOD        TH1        SMOD        TMOD        TH1        SMOD        TMOD        TH1
方式0        1M                                ×        ×        ×                       
方式2        375K                                1        ×        ×                       
        187.5K        1        ×        ×        0        ×        ×                       




方式1






方式3        62.5K                                1        20        FFH                       
        19.2K                                                        1        20        FDH
        9.6K                                                        0        20        FDH
        4.8K                                1        20        F3H        0        20        FAH
        2.4K        1        20        F3H        0        20        F3H        0        20        F4H
        1.2K        1        20        E6H        0        20        E6H        0        20        E8H
        600        1        20        CCH        0        20        CCH        0        20        D0H
        300        0        20        CCH        0        20        98H        0        20        A0H
        137.5        1        20        1DH        0        20        1DH        0        20        2EH
        110        0        20        72H        0        20        FEEBH        0        10        FEFFH
按照表来看我们的在11.0592MHZ下,串口的波特率为9.6K也就是9600,那么TH1 TL1设置为0xFD
SCON = 0xd8;   //串口控制
D7        D6        D5        D4        D3        D2        D1        D0
SM0        SM1        SM2        REN        TB8        RB8        TI        RI
1        1        0        1        1        0        0        0
表6-1 串行口工作方式控制
SM0        SM1        工作方式        说明        波特率
0        0        方式0        同步移位寄存器        fOSC/12
0        1        方式1        10位异步收发        由定时器控制
1        0        方式2        11位异步收发        fOSC/32或fOSC/64
1        1        方式3        11位异步收发        由定时器控制
也就是说串行口的工作方式3,11位异步收发波特率由定时器控制的。REN:1->允许串行口接收。TB8:方式2和方式3时,为发送的第9位数据,也可以作奇偶校验位。RB8:方式2和方式3时,为接收到的第9位数据;方式1时。TI:发送中断标志,由硬件置位,必须由软件清零。RI:接收中断标志,由硬件置位,必须由软件清零。

PCON = 0x00;
D7        D6        D5        D4        D3        D2        D1        D0
SMOD                                                       
0        0        0        0        0        0        0        0
1->方式1和方式3时,波特率=定时器1溢出率/16;方式2波特率= fOSC/32;
0->方式1和方式3时,波特率=定时器1溢出率/32;方式2波特率= fOSC/64
TR1 = 1;   //启动定时器T1
也就是启动定时器
while (1)
                while(RI==0);  //串口接收
                RI = 0;
                c = SBUF;
                SBUF = c;
                while(TI==0);  //串口发送
                TI=0;
while(RI==0);  RI为0则等待(不为0就执行下一句)
RI = 0;   执行这句说明RI不再为0(接收结束),这里强制RI为0
c = SBUF;  接收字符
SBUF = c;  发送字符
while(TI==0);  TI为0则等待,否则执行下一句
TI=0;   执行到这句说明TI不为0(即发送结束),强制令TI为0
这里的意思是说判断RI是否为0当RI==0时就一直循环判断,当RI不等与0时就执行下面的语句。RI=0是重置。然后就接收数据SBUF就是串口数据寄存地方。当C=SBUF时就收到了串口数据然后在吧原来的数据在发到出去SBUF=C,while(TI==0)是说当TI为0是就一直等待。不为0时就执行TI=0重置TI。
将前面的程序编译后写入单片机,并连好串口线。通过串口调试助手向单片机发送字符,会发现发送的内容被原样发送回来。

[ 本帖最后由 xuhuaping 于 2009-2-5 15:23 编辑 ]

串口.rar

17 KB, 下载次数: 421

评分

参与人数 1威望 +10 金钱 +10 人气 +3 收起 理由
DreamonII + 10 + 10 + 3 精品文章

查看全部评分

回复 支持 反对

使用道具 举报

发表于 2009-2-5 15:35:19 | 显示全部楼层
加油!!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-2-7 21:09:58 | 显示全部楼层
试验(串口通讯)
在坛子里正好有坛友提到串口通讯这一块的内容,特将这个举例出来当串口通讯实例。
地址:《定时切换装置》
要求:
1:单片机由上位机控制
2:能够控制20台左右的设备
3:每台设备能够由上位机控制开和关
[控制协议]:
    这次采用1字节的控制协议,规定如下:

     1、最高位表示开关状态,0表示关,1表示开;
     2、低5位表示通道号;
     3、第5,6位空保留未用;
     4、用0xff(二进制 1111 1111)表示打开全部通道(输出全1);
     5、用0x7f(二进制 0111 1111)表示关闭全部通道(输出全0);
     6、操作正常完成时向主机返回原始命令;
     7、操作失败后向主机返回出错码0x3f(二进制 0011 1111);
     8、有效通道号 0~23,共24个通道,每8通道一组,分别占用P0,P1和P2口(P3用于控制和串行通信等功能)。

[使用方法]:
    例如要打开第12通道(通道号从0~23,因此第12通道的通道号是11)
    在VB中利用MSComm控件向单片机发送: &H8B (即二进制的 1000 1011)即可。
程序:
#include <reg51.h>
#define uchar unsigned char

void main(void)
{
        P0=0x00;
        P1=0x00;
        P2=0x00;
uchar a,b,c;
        TMOD=0x20;
        TL1=0xfd;   
        TH1=0xfd;
        SCON=0xd8;
        PCON=0x00;
        TR1=1;

while(1)
{
    while(RI==0);
    RI=0;
    c=SBUF;
      
    if(c == 0xff)
    {
        P0=0xff;
        P1=0xff;
        P2=0xff;
    }
    else if(c==0x7f)
    {
        P0=0x00;
        P1=0x00;
        P2=0x00;
    }
    else
    {
        b = c >> 7;  //取出最高位   
        a = c & 0x1f; //取出通道号

        if(a < 8)
         {
             if(b==1)
                 P0 |= (1<<a);
             else
                 P0 &= ~(1<<a);
        }
        else if(a < 16)
        {
            a=a-8;
            if(b==1)
                P1 |= (1<<a);
            else
                P1 &= ~(1<<a);
        }
        else if(a<24)
        {
            a=a-16;
            if(b==1)
                P2 |= (1<<a);
            else
                P2 &= ~(1<<a);
        }
        else
        {
            c=0x3F;  //用3F表示出错
        }
    }

    SBUF = c;  //发送返回值
    while(TI==0);
    TI=0;
}
}
下面我来解释下程序
#include <reg51.h>
#define uchar unsigned char

void main(void)
{
uchar a,b,c;
        TMOD=0x20;
        TL1=0xfd;   
        TH1=0xfd;
        SCON=0xd8;
        PCON=0x00;
        TR1=1;

while(1)
{
        P0=0x00;
        P1=0x00;
        P2=0x00;   //初始化
    while(RI==0);
    RI=0;
    c=SBUF;
这段不用说了吧,在第五课我已经讲到了。
    if(c == 0xff)
    {
        P0=0xff;
        P1=0xff;
        P2=0xff;
    }
当C=0Xff时P0,P1,P2端口全部打开也就是24个通道全部打开
    else if(c==0x7f)
    {
        P0=0x00;
        P1=0x00;
        P2=0x00;
    }
当C=0X7f时P0,P1,P2端口全部打开也就是24个通道全部关闭
        b = c >> 7;  //取出最高位   
        a = c & 0x1f; //取出通道号
b=c>>7 意思说C向右移动7位打个比方吧,1000 0011 向右移动七位后就变成了0000 0001也就是说b=0000 00001 那么也就是b=1。
a = c & 0x1f,假如C=0x81=1000 0001
那么a=1000 0001 &(与) 0001 1111 =0000 0001
1 & 0 =0
0 & 1 =0
0 & 0 =0
1 & 1 =1

        if(a < 8)
         {
             if(b==1)
                 P0 |= (1<<a);
             else
                 P0 &= ~(1<<a);
        }
当a < 8时那么a的范围在0 ~ 7
只要a等于0到7之间的任意一位数时就执行下面的程序
if(b==1)就是说当b=1时候就执行p0 |= (1<<a)
P0 |=(1<<a)
0 | 0 = 0
1 | 0 = 1
0 | 1 = 1
1 | 0 = 1
P0 |= (1<<a)反之就执行P0 &= ~(1<<a);
P0 |= (1<<a)就是将P0的第a位置为1(开)
P0 &= ~(1<<a)就是将P0的第a位清0(关)
这里整句语句的意思是当a=0~7中的任意一位数字时就执行下面的语句然后在判断b是否等于1当b等于1时那么就执行
P0 |= (1<<a)反之就执行P0 &= ~(1<<a);
P0 |= (1<<a)就是将P0的第a位置为1(开)
P0 &= ~(1<<a)就是将P0的第a位清0(关)
c=0x3F; 这里说当收到的数据不符合要求时就发回3f这个数据。也就时提示出错
SBUF = c; 就是说发送返回的数据也就是发过来的数据。

评分

参与人数 1威望 +3 人气 +3 收起 理由
DreamonII + 3 + 3 我很赞同(完全没分了)

查看全部评分

回复 支持 反对

使用道具 举报

发表于 2009-2-17 10:51:16 | 显示全部楼层
最近涉及到相关的内容,拜读了!
回复 支持 反对

使用道具 举报

发表于 2009-2-19 10:26:30 | 显示全部楼层
辛苦了!感谢楼主
回复 支持 反对

使用道具 举报

发表于 2009-3-1 00:46:25 | 显示全部楼层
写的不错
支持下
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

文字版|手机版|小黑屋|VBGood  

GMT+8, 2022-1-20 20:15

VB爱好者乐园(VBGood)
快速回复 返回顶部 返回列表