单片机很好玩6,制作一个闹钟

单片机常被称作 MCU,MCU 的全称是 Micro Control Unit,就是微型控制器的意思。顾名思义,单片机常被用于控制一些器件工作,因此物联网的终端,或者其他一些智能机器,都是离不开单片机的。

小到手机,大到汽车飞机,现在几乎只要是电子器件,就有单片机的身影。

精确计时的重要性

传感器能够感知外界环境,可以说是一切智能机器的基础。实际工程中,常常使用单片机或者控制传感器工作,或者采集传感器的数据。但是不管是控制还是采集,单片机都需要与传感器通信,这样才能将控制命令发送到传感器,或者将传感数据接收到单片机内部。

为了实现器件与器件之间的通信,人们制定了一些通信协议。通信协议其实就是一系列约定,比如约定总线先输出低电平 10us,再输出高电平 30us 表示 0;总线先输出低电平 10us,再输出高电平 100us 表示 1。

可以看出,如果单片机要解析通信协议,就只需要处理电信号与时间的关系就可以了

假设单片机在与某个传感器通信时,需要拉低总线 50us,这就需要一个精确的定时器。在读传感器数据时,需要判断总线究竟被传感器拉高了 30us 还是 100us,这就需要一个精确的计时器。

不精确的定时器

那么单片机怎样才能精确的定时和计时呢?本节就以 51 单片机为例,来说一下这个问题。

第三节制作呼吸灯时,用到的延时函数C语言代码是如下定义的:

void delay(unsigned int n)
{
    unsigned int x;
    while(n--){
        x = 50;
        while(x--);
    }
}

[video width="544" height="960" mp4="https://blog.popkx.com/usr/uploads/2019/01/1-1.mp4"][/video]

但这只是粗略的定时,因为软件每次执行需要花费的时间都有所差异。所以上面的 delay() 函数,只能用在对时间精确度要求不高的“呼吸灯”小项目中。

精确的定时器

相当一部分单片机内部都有计数器资源。计数器内部有一个寄存器,这个寄存器的值每经过一个机器周期就会自动加 1,而机器周期仅与单片机的晶振有关。

我使用的这款 51 单片机有两个计数器,它的晶振固定为 11.0592MHz,一个机器周期等于 12 个时钟周期。所以,计数器每加1,就表示时间过去了 n 秒,n 的计算公式如下:

n = 12 * ( 1/11.0592MHz )

这款单片机计数器的寄存器宽为 16 位,因此最大能够表示到 0xffff 即 65535。计数器计满(溢出)一次,就会将寄存器 TFx 置 1,所以检测 TFx 寄存器就能够知道计数器是否计满。

如此一来,设计精确的定时器思路就有了,请看如下 C语言代码:

static unsigned int timer_cnt = 0;

void set_timer0(unsigned int tus)
{   
    timer_cnt = (unsigned int)((float)tus * 11.0592 / 12.0);
    timer_cnt = 65535 - timer_cnt;

    TH0 = (timer_cnt>>8) & 0xff;
    TL0 = timer_cnt & 0xff;
    TMOD |= 0x01;
}


假设定时器计数 timer_cnt 次消耗 tus 微秒,那么让计数器计数 65535-timer_cnt 次就溢出,我们就可以检测 TF0 寄存器的值判断是否已经过去 tus 微秒。请看如下 C语言代码:

void start_timer0()
{
    TF0 = 0;
    TR0 = 1;
}
void wait_timer0()
{
    while(!TF0);
    TR0 = 0;
    TH0 = (timer_cnt>>8) & 0xff;
    TL0 = timer_cnt & 0xff;
}


TR0 寄存器为高电平时,计数器才开始计数。如此一来,可以定义精确的延时函数,它的C语言代码如下:

set_timer0(10);
void delay_10us(unsigned int n)
{
    while(n--){
        start_timer0();
        wait_timer0();
    }
}

现在写如下控制程序,测试我们实现的精确定时器,请看如下C语言代码:

void main()
{
    init_uart(9600);
    set_timer0(10); // 10us

    prints("program start...\n");

    while(1){   
        delay_10us(50000);delay_10us(50000);    
        prints("1s past ...\n");
    }
}

两句delay_10us(50000);表示延时 100万微秒(即 1秒)。编译程序并烧写到单片机,在电脑端打开串口调试工具,发现的确每隔 1秒打印一次 "1s past ...":

精确计时器

思路与设计精确定时器是一样的。因为暂时不方便测试,所以放入下一节再讨论。下一节将介绍一款温度、湿度传感器,并使用单片机采集之,发送到电脑端。这样一来,就可以点击鼠标知道室内的温度和湿度了。敬请关注!!!

阅读更多:   单片机
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif