计数器与串口通信的实例

        

目录

一、利用中断控制发出1kHz的音频信号

二、利用LED数码管制作秒表

三、使用定时器实现一个LCD显示时钟

四、甲乙两个单片机串口通信

五、利用串口通信发送“Hello C51”

六、总结


        本文主要讲述了利用中断、计数器和串口通信等知识进行一些单片机实例的应用。

一、利用中断控制发出1kHz的音频信号

        要求:利用T1的中断控制P1.7引脚输出频率为1kHz方波音频信号,驱动蜂鸣器发声。系统时钟为12MHz。方波音频信号周期1ms,因此T1的定时中断时间为0.5 ms,进入中断服务程序后,对P1.7求反。

​        先计算T1初值,系统时钟为12MHz,则机器周期为1µs。1kHz音频信号周期为1ms,要定时计数的脉冲数为a。
​        则T1初值:TH1=(65 536 −a) /256; TL1=(65 536 −a) %256。​    实现代码如下所示。

#include<reg51.h>
sbit sound=P2^7;                      //将sound位定义为P1.7脚
#define f1(a) (65536-a)/256            //定义装入定时器高8位时间常数
#define f2(a) (65536-a)%256            //定义装入定时器低8位时间常数

unsigned int i=500;
unsigned int j=0;

void main(void)
{
    EA=1;                          //开总中断.
    ET1=1;                        //允许定时器T1中断         .
    TMOD=0x10;                     //TMOD=0001 000B,使用T1的方式1定时        TH1=f1(i);                  
                                //给T1高8位赋初值.
    TL1=f2(i);                  //给T1低8位赋初值.
    TR1=1;                         //启动T1
    while(1)  
    {                              //循环等待
        i=460; 
        while(j<2000);
        j=0;
        i=360; 
        while(j<2000);
        j=0;
    }
}

void T2(void) interrupt 3 using 0    //定时器T1中断函数
{
        TR1= 0;                     //关闭T1
        sound=~sound;                 //P1.7输出求反
        TH1=f1(i);                   //T1的高8位重新赋初值.
        TL1=f2(i);                   //T1的低8位重新赋初值.
        j++;                
        TR1=1;                         //启动定时器T1
}

        在proteus中的模拟图如下所示。

        最后可以在普中开发板上进行实验,可以观察到蜂鸣器发生鸣叫。

二、利用LED数码管制作秒表

​        要求:用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。​        实现代码如下所示。
 

#include<reg51.h>              //头文件
unsigned char code discode1[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};    
                                 //数码管显示0~9的段码表, 带小数点
unsigned char code discode2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};    
                                //数码管显示0~9的段码表,不带小数点
unsigned char timer=0;            //timer记录中断次数
unsigned char second;            //second储存秒
unsigned char key=0;            //key记录按键次数

main()                //主函数
{
    TMOD=0x01;            //定时器T0方式1定时
    ET0=1;                       //允许定时器T0中断
    EA=1;                            //总中断允许
    second=0;                        //设初始值
    P0=discode1[second/10];           //显示秒位0
    P2=discode2[second%10];           //显示0.1s位0
    while(1)                 //循环
    {    
        if((P3&0x80)==0x00)        //当按键被按下时
        {     
            key++;            //按键次数加1
            switch(key)        //根据按键次数分三种情况
            {
                case 1:        //第一次按下为启动秒表计时
                TH0=0xee;     //向TH0写入初值的高8位
                TL0=0x00;           //向TL0写入初值的低8位,定时5ms
                TR0=1;                 //启动定时器T0
                break;
                case 2:                //按下两次暂定秒表
                TR0=0;                 //关闭定时器T0
                break;
                case 3:                    //按下3次秒表清0
                key=0;                     //按键次数清
                second=0;                  //秒表清0
                P0=discode1[second/10];       //显示秒位0                   
                P2=discode2[second%10];      //显示0.1s位0
                break;
            }
            while((P3&0x80)==0x00);             //如果按键时间过长在此循环
        }
    }
}

void int_T0() interrupt 1  using 0         //定时器T0中断函数
{                        
    TR0=0;             //停止计时,执行以下操作(会带来计时误差)
    TH0=0xee;          //向TH0写入初值的高8位
    TL0=0x00;           //向TL0写入初值的低8位,定时5ms
    timer++;              //记录中断次数
    if (timer==20)           //中断20次,共计时20*5ms=100ms=0.1s
    {
        timer=0;                //中断次数清0
        second++;               //加0.1s
        P0=discode1[second/10];     //根据计时,即时显示秒位        
        P2=discode2[second%10];     //根据计时,即时显示0.1s位     
    }
    if(second==99)          //当计时到9.9s时
    {
        TR0=0;            //停止计时
        second=0;        //秒数清0        
        key=2;              //按键数置2,当再次按下按键时,                    //key++,即key=3,秒表清0复原    
    }                           
    else                //计时不到9.9s时
    {
        TR0=1;            //启动定时器继续计时
    }
}

        在proteus中的模拟图如下所示。

        最后可以在普中开发板上进行实验,可以观察到秒表的操作。

三、使用定时器实现一个LCD显示时钟

        要求:使用定时器实现一个LCD显示时钟,采用LCD1602。

        对于时间的定义,可将T0定时时间定为50ms,采用中断方式进行溢出次数累计,满20次,则秒计数变量second加1;若秒计满60,则分计数变量minute加1,同时将秒计数变量second清0;若分钟计满60,则小时计数变量hour加1;若小时计数变量满24,则将小时计数变量hour清0。        

        实现代码如下所示。

#include<reg51.h>
#include<lcd1602.h>
#define uchar unsigned char
#define uint unsigned int
uchar int_time;                //定义中断次数计数变量
uchar second;                //秒计数变量
uchar minute;                //分钟计数变量
uchar hour;                //小时计数变量
uchar code date[]="  H.I.T. CHINA  ";    //LCD第1行显示的内容
uchar code time[]=" TIME  23:59:55 ";    //LCD第2行显示的内容
uchar second=55,minute=59,hour=23;

void clock_init()
{
    uchar i,j;
    for(i=0;i<16;i++)
    {
        write_data(date[i]);
    }
    write_com(0x80+0x40);
    for(j=0;j<16;j++)
    {
        write_data(time[j]);
    }
}

void clock_write( uint s, uint m, uint h)
{
    write_sfm(0x47,h);
    write_sfm(0x4a,m);
    write_sfm(0x4d,s);
}

void main()
{
    init1602();        //LCD初始化
    clock_init();        //时钟初始化
    TMOD=0x01;             //设置定时器T0为方式1定时
    EA=1;                        // 总中断开 
    ET0=1;                 // 允许T0中断 
    TH0=(65536-46483)/256;    //给T0装初值
    TL0=(65536-46483)%256;
    TR0=1;
    int_time=0;            //中断次数、秒、分、时单元清0
    second=55;
    minute=59;
    hour=23;
    while(1)
    {
        clock_write(second ,minute, hour);
    }
}
void  T0_interserve(void)  interrupt 1  using 1     //T0中断服务子程序
{    
    int_time++;                //中断次数加1
     if(int_time==20)             //若中断次数计满20次
     { 
        int_time=0;             //中断次数变量清0
         second++;            //秒计数变量加 1
     }
     if(second==60)            //若计满60s
     { 
        second=0;                 //秒计数变量清0
        minute ++;                //分计数变量加 1
     }
    if(minute==60)            //若计满60分
    {     
        minute=0;        //分计数变量清0
        hour ++;        //小时计数变量加1
    }
    if(hour==24)
    {     
        hour=0;            //小时计数计满24,将小时计数变量清0
    }
    TH0=(65536-46083)/256;        //定时器T0重新赋值
    TL0=(65536-46083)%256;
}

        在proteus中的模拟图如下所示。

四、甲乙两个单片机串口通信

        要求:甲机把控制8个流水灯点亮的数据发送给乙机并点亮其P1口的8个LED。方式3比方式1多了1个可编程位TB8,该位一般作奇偶校验位。乙机接收到的8位二进制数据有可能出错,需进行奇偶校验,其方法是将乙机的RB8和PSW的奇偶校验位P进行比较,如果相同,接收数据;否则拒绝接收。        本例使用了一个虚拟终端来观察甲机串口发出的数据。实现代码如下所示。
        首先是发送机。

#include <reg51.h>
sbit T=PSW^0;        //P位为PSW寄存器的第0位,即奇偶校验位
unsigned char Tab[8]= {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf,  0xbf, 0x7f};   
                    //控制流水灯显示数据数组,为全局变量

void Send(unsigned char dat)        // 发送1字节数据的函数
{        
    TB8=T;                 // 将偶校验位作为第9位数据发送
    SBUF=dat;
    while(TI==0);             //检测TI,TI=0,未发送完
    ;                    // 空操作
    TI=0;                 // 1字节发送完,TI清0
}

void delay (void)         // 延时约200ms的函数
{    
    unsigned char m,n;
    for(m=0;m<250;m++)
        for(n=0;n<250;n++);
}

void main(void)                 //主函数
{    
    unsigned char i;
    TMOD=0x20;                //设置定时器T1为方式2
    SCON=0xc0;                //设置串口为方式3
    PCON=0x00;                  //SMOD=0     
    TH1=0xfd;                //给T1赋初值,波特率设置为9600
    TL1=0xfd;
    TR1=1;                //启动定时器T1
    while(1)
    {    
        for(i=0;i<8;i++)
        {
            Send(Tab[i]);
            delay( );        //大约200ms发送一次数据
        }
    }
}

        然后是接收机。

#include <reg51.h>
sbit T=PSW^0;        // P位为PSW 寄存器的第0位,即奇偶校验位

unsigned char Receive(void)        //接收1字节数据的函数
{    
    unsigned char dat;
    while(RI==0);         //检测RI,RI=0,未接收完,则循环等待
    ;
    RI=0;            //已接收一帧数据,将RI清0
    ACC=SBUF;            //将接收缓冲器的数据存于ACC
    if(RB8==T)             //只有偶校验成功才能往下执行,接收数据
    {    
        dat=ACC;        //将接收缓冲器的数据存于dat
        return dat;        //将接收的数据返回
    }
}

void main(void)         //主函数
{    
    TMOD=0x20;        //设置定时器T1为方式2
    SCON=0xd0;        //设置串口为方式3,允许接收REN=1
    PCON=0x00;          // SMOD=0  
    TH1=0xfd;        //给定时器T1赋初值,波特率为9600
    TL1=0xfd;
    TR1=1;            //接通定时器T1
    REN=1;             //允许接收
    while(1)
    {    
        P1= Receive( );            //将接收到的数据送P1口显示
    }
}

        在proteus中的模拟图如下所示。

五、利用串口通信发送“Hello C51”

        要求:将单片机串口与笔记本电脑串口模块相连,单片机每隔2秒发送“Hello C51”,笔记本电脑用串口助手软件接收。 如果串口助手发送字符“0" 给单片机,则单片机停止发送; 如果单片机收到“1”,则继续每隔2秒发送“Hello C51”。

​    实现代码如下所示。

#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uint p=0;//默认设置状态0
void delay_2s()//延时函数
{
    int i,j; 
    for(i=0;i<21800;i++)
     {
        for(j=10;j>0;j--);
       }
}
void uart_sendbyte(uchar byte)//发送字符
{
    SBUF=byte;
    while(TI==0);
    TI=0;
}
void helloc51()
{
            uart_sendbyte('h');
            uart_sendbyte('e');
            uart_sendbyte('l');
            uart_sendbyte('l');
            uart_sendbyte('o');
            uart_sendbyte('c');
            uart_sendbyte('5');
            uart_sendbyte('1');
            uart_sendbyte(' ');
}
void uart_init(uchar baud)//uart初始化
{
    TMOD|=0X20;
    SCON=0X50; 
    PCON=0X80; 
    TH1=baud; 
    TL1=baud;
    ES=1;
    EA=1;
    TR1=1;
}
void uart() interrupt 4
{
    if(RI==1)//读取串口发送的信息
    {
        uchar rec_data;
        rec_data=SBUF;
        RI=0;
        if(rec_data==0)//发送0,设置为模式0
        {
            p=0;
        }
        else if(rec_data==1)//发送1,设置为模式1
        {
            p=1;
        }
    }
}
void main()
{
    uart_init(0XFA);
    while(1)
    {
        if(p==0)//状态0,不做操作
        {
            while(p==0)
            {
        
            }
        }
        else if(p==1)//状态1,循环输出hello c51并且延时2s
        {
            while(p==1)
            {
                helloc51();
                delay_2s();
            }
        }
    }
}

        进入串口调试助手后可以看到如下结果。

六、总结

​    本文中我利用中断、计数器和串口通信等知识完成了一些实例的应用,对于单片机的应用有了进一步的认识,但是关于这些内容仍不熟练,还需多加练习。对于文中出现的错误,也希望各位大佬指出错误。

  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值