单片机很好玩8,制作一个温度警报器

第二节和第三节介绍了如何使用C语言编程单片机,制作 LED 闪烁小灯,以及 LED “呼吸灯”。上一节介绍了利用 DHT11 温湿度传感器测量室内温度和湿度的方法,本节将把它们结合,制作一些更加“有意思”的小玩意儿。

温湿度传感器的很多实际应用的场景都是检测一个“阈值”——例如,如果环境温度在 40 摄氏度以下则一切正常,一旦环境温度超过 40 摄氏度,就会有报警信息,通常以“声光”的形式(铃声、警报灯等)给出。

参照实际应用,我们可以使用C语言编程单片机,用缓和的 LED “呼吸灯”表示正常温湿度情况。而一旦出现温度超过阈值的情况,则可以用急促的 LED 闪烁小灯表示。

先来看看上一节获取室内温湿度的单片机 C语言程序代码:

void main()
{
    char dat[5] = {0};
    int cks;

    init_uart(9600);
    set_timer0(10); // 10us

    prints("init...\r\n");
    delay_10us(50000);delay_10us(50000);
    delay_10us(50000);delay_10us(50000);

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

    while(1){

        dht11_start();
        dht11_read_once(dat);

        cks = 0;
        cks += dat[0]; cks += dat[1];
        cks += dat[2]; cks += dat[3];
        if((char)cks == dat[4]){
            prints("RH: ");printn(dat[0]);prints(".");printn(dat[1]);prints("    ");
            prints("TM: ");printn(dat[2]);prints(".");printn(dat[3]);prints("\r\n");
        }else{
            prints("capture failed\r\n");
        }

        delay_10us(50000);delay_10us(50000);
    }
}


乍一看,要实现上面的设计好像很简单,只需要判断一下温度值,再决定使用哪一套 LED 小灯的控制程序(“呼吸灯”程序,或者“闪烁”程序)就可以了。

但是 DHT11 传感器需要的 delay_10us(50000); 语句会破坏 LED 小灯的控制程序,因为在这 500ms 期间,单片机处于等待状态,无法控制 LED 小灯闪烁或者“呼吸”。那么,没有办法解决这个问题吗?也不是,请看 LED 小灯“闪烁”的核心C语言程序:

while(1){
        P20 = 0;
        delay(10);
        P20 = 1;
        delay(10);
    }

以及 LED 的“呼吸灯”的核心C语言程序:

void twinkle_once(unsigned char darkTime)
{
    P20 = 0;
    delay(100-darkTime);
    P20 = 1;
    delay(darkTime);
}

仔细思考一下,应该能明白无论是 LED “闪烁”程序,还是“呼吸”程序,其实都依赖“延时”。那思路就有了,直接使用 LED 小灯的控制程序代替 delay_10us(50000); 不就可以了吗?的确如此,请继续往下看。

先在上一节 C语言代码的基础上,实现更大尺度的 5ms 延时函数:

void delay_5ms(unsigned int n)
{
    while(n--)
        delay_10us(5);
}

使用 delay_5m() 函数替换之前定义的“不精确”延时函数 delay() 函数,这样我们就能知道较为精确的 LED 小灯的控制程序执行时间,也就能更加方便的替换 delay_10us(50000); 语句。

现在写出“呼吸”1 秒的 LED “呼吸灯”C语言程序就不难了,请看:

void led_breath_1s()
{
    static int cnt = 0;
    static char dark_time = 0, dir = 1  ;
    while(1){
        twinkle_once(dark_time);
        if( 0==((cnt++)%4) ){
            if(dir)
                dark_time += 1;
            else
                dark_time -= 1;
            if(dark_time >= 100)
                dir = 0;
            if(dark_time <= 60)
                dir = 1;
        }
        if(cnt > 200){
            cnt = 0;
            break;
        }
    }
}


twinkle_once() 执行一次需要 5ms 的时间,cnt 计数到 200 次就恰好是 1 秒,另外,if( 0==((cnt++)%4) ) 语句仍然能够控制 LED 小灯的“呼吸”频率。其他部分的代码与之前介绍的就没什么不同了。

类似的,闪烁 1 秒的 LED “闪烁”C语言程序可以如下实现:

void led_twinkle_1s()
{
    char cnt = 5;
    while(cnt--){
        P21 = 1;
        delay_10us(10000);
        P21 = 0;
        delay_10us(10000);
    }
}

闪烁一次需要 200 ms 的时间,那么闪烁 5 次就是 1秒了。现在使用 led_breath_1s() 和 led_twinkle_1s() 函数替换delay_10us(50000); 语句就可以了。请看:

void main()
{
    char dat[5] = {0};
    int cks;

    init_uart(9600);
    set_timer0(10); // 10us

    prints("init...\r\n");
//  delay_10us(50000);delay_10us(50000);
//  delay_10us(50000);delay_10us(50000);
    led_breath_1s();
    led_breath_1s();    
    prints("program start...\r\n");

    while(1){

        dht11_start();
        dht11_read_once(dat);

        cks = 0;
        cks += dat[0]; cks += dat[1];
        cks += dat[2]; cks += dat[3];
        if((char)cks == dat[4]){
            prints("RH: ");printn(dat[0]);prints(".");printn(dat[1]);prints("    ");
            prints("TM: ");printn(dat[2]);prints(".");printn(dat[3]);prints("\r\n");
        }else{
            prints("capture failed\r\n");
        }

        if(dat[2] > 15)
            led_twinkle_1s();
        else
            led_breath_1s();
        //delay_10us(50000);delay_10us(50000);
    }
}


初始化时,使用了 led_breath_1s(); 代替了 DHT11 传感器需要的延时,每次获取温湿度值的时候,则判断了温度值,如果温度大于 15 摄氏度,使用LED的“闪烁”程序代替延时 1秒,否则使用 LED 的“呼吸灯”程序代替。

现在编译C语言程序,烧写到单片机,可以得到如下结果:

一开始,DHT11 探测到的温度较低,所以LED指示灯是“呼吸”状态的,然后使用手捏住 DHT11,温度升高超过“阈值”后,LED 小灯就变成“闪烁”状态了。

还有问题吗?

上面的控制程序还不是很完美,相信细心的朋友应该看出来了,LED小灯处于“呼吸”状态时,总是会有一次闪烁。这是因为我们采集 DHT11 传回来的温湿度信息时,也有几十毫秒的延时,在这段时间内,单片机是无法控制 LED 小灯的,所以小灯会一直处于最近一次的亮暗状态,查看 led_twinkle_1s() 函数的C语言代码就知道,LED 小灯最后的状态是亮的状态,这就解释了我们遇到的问题。

这其实也是单片机编程的一个特点,若单片机内部没有运行操作系统,则编写并行的程序几乎是不可能的,所以串行的阻塞延时应该尽量避免,否则就会出现类似上面的问题。那么,上面这种问题不借助于操作系统也能解决吗?当然可以,借助于单片机的中断系统就很好解决,限于篇幅,以后再说了。

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

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