28

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

 4 years ago
source link: https://blog.popkx.com/%E5%8D%95%E7%89%87%E6%9C%BA%E5%BE%88%E5%A5%BD%E7%8E%A96-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E9%97%B9%E9%92%9F/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

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

945ef898acbddc6222e5049a3057b181.png

精确计时的重要性

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

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

3dcdb3f8f5f0b900ac7be4991d65fe19.png

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

假设单片机在与某个传感器通信时,需要拉低总线 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 寄存器就能够知道计数器是否计满。

a79906c11a96b41c22030b5d24735cf8.png

如此一来,设计精确的定时器思路就有了,请看如下 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;
}
9b1e9e079873746e0b17db5367788b69.png

假设定时器计数 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;
}
62d9e649e8b24ddc134a592f49c58b47.png

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 ...":

12121212.gif

精确计时器

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


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK