搜索
bottom↓
回复: 16

学习马老师的第二版教材,按照状态机思想写了个时钟(DS1302+LCD1602)

[复制链接]

出0入0汤圆

发表于 2011-9-24 10:46:32 | 显示全部楼层 |阅读模式
买了马老师的第二版教材,正在入门中。
参考马老师和论坛上的讨论,尝试按照状态机的思想写了个时钟,DS1302+LCD1602,在实验板上调试通过。
本人菜鸟一个,让大家见笑,请多指教!

一共使用了两个状态机:
1.按键扫描状态机:
  *可处理多位独立按键(8位)
  *可处理按键短按、长按(超过1s)、连续长按(超过1s后,每0.5s)
  *本题中使用了3个按键
2.DS1302显示、时间设置的状态机
  *这个逻辑简单,状态稍多,下面上图。


(原文件名:DS1302_state_diagram.png)

出0入0汤圆

 楼主| 发表于 2011-9-24 10:53:48 | 显示全部楼层
if(bit_time_10ms_ok)                //每10ms
        {
            bit_time_10ms_ok = 0;           //10ms ok
            key_temp = key_read();          //每10ms读键盘

            if(key_temp)
            {   return_time = 0;            //如果有键按下,强制返回时间清零
            }
            //TRACE_CHAR(clock_state);
            //TRACE_CHAR(key_temp);
            //时钟状态机,处理菜单及各种响应
            switch(clock_state)
            {
            case clock_normal:
                ds1302_data_2_dis();
                if(key_temp == key1_s)              //key1短按,(选择)
                    clock_state = menu_set_time;
                if(key_temp == key3_s)              //key3短按,(确定)
                    bit_alarm_beep_on = 0;
                break;
            case menu_set_time:
                if(key_temp == key3_s)
                    clock_state = c_set_hour;
                else if(key_temp == key1_s)
                    clock_state = menu_set_alarm;
                break;
            case menu_set_alarm:
                ds1302_alarm_2_dis();
                if(key_temp == key1_s)
                    clock_state = clock_normal;
                else if(key_temp == key3_s)
                    clock_state = a1_set_hour;
                break;
                //set time
            case c_set_hour:
                if(key_temp == key1_s)
                    clock_state = c_set_min;
                else if(key_temp == key1_l)         //key1长按,放弃更改
                    clock_state = clock_normal;
                else if(key_temp == key2_s)         //key2短按,+1
                    time_dec[2] = (time_dec[2] + 1) % 24;
                else if(key_temp == key2_l)         //key2长按,+10
                    time_dec[2] = (time_dec[2] + 10) % 30;
                if(time_dec[2] > 24) time_dec[2] = 0;
                else if(key_temp == key3_s)         //key3短按,接受更改
                {
                    update_1302();                  //确定后,更新写1302
                    clock_state = clock_normal;
                }
                break;
            case c_set_min:
                if(key_temp == key1_s)
                    clock_state = c_set_sec;
                else if(key_temp == key1_l)
                    clock_state = clock_normal;
                else if(key_temp == key2_s)
                    time_dec[1] = (time_dec[1] + 1) % 60;
                else if(key_temp == key2_l)
                    time_dec[1] = (time_dec[1] + 10) % 60;
                else if(key_temp == key3_s)
                {
                    update_1302();
                    clock_state = clock_normal;
                }
                break;
            case c_set_sec:
                if(key_temp == key1_s)
                    clock_state = c_set_week;
                else if(key_temp == key1_l)
                    clock_state = clock_normal;
                else if(key_temp == key2_s)
                    time_dec[0] = (time_dec[0] + 1) % 60;
                else if(key_temp == key2_l)
                    time_dec[0] = (time_dec[0] + 10) % 60;
                else if(key_temp == key3_s)
                {
                    update_1302();
                    clock_state = clock_normal;
                }
                break;
            case c_set_week:
                if(key_temp == key1_s)
                    clock_state = c_set_year;
                else if(key_temp == key1_l)
                    clock_state = clock_normal;
                else if((key_temp == key2_s) || (key_temp == key2_l))
                    week_bcd_dec = (week_bcd_dec + 1) % 8;
                else if(key_temp == key3_s)
                {
                    update_1302();
                    clock_state = clock_normal;
                }
                break;
            case c_set_year:
                if(key_temp == key1_s)
                    clock_state = c_set_month;
                else if(key_temp == key1_l)
                    clock_state = clock_normal;
                else if(key_temp == key2_s)
                    date_dec[2] = (date_dec[2] + 1) % 100;
                else if(key_temp == key2_l)
                    date_dec[2] = (date_dec[2] + 10) % 100;
                else if(key_temp == key3_s)
                {
                    update_1302();
                    clock_state = clock_normal;
                }
                break;
            case c_set_month:
                if(key_temp == key1_s)
                    clock_state = c_set_date;
                else if(key_temp == key1_l)
                    clock_state = clock_normal;
                else if((key_temp == key2_s) || (key_temp == key2_l))
                    date_dec[1] = (date_dec[1] + 1) % 13;
                else if(key_temp == key3_s)
                {
                    update_1302();
                    clock_state = clock_normal;
                }
                break;

出0入0汤圆

 楼主| 发表于 2011-9-24 11:01:09 | 显示全部楼层
按键处理状态机:

#ifndef _KEY_H
#define _KEY_H

#include <avr/io.h>
#include <avr/iom16.h>



//PORTD  7     6    5    4     3    2    X    X高四位接按键,低电平(按下)
//                 key4 key3 key2 key1
//根据使用的端口不同,修改key_mask,及计算键值部分
//按键返回值:
//0:无按键
//1:短按 <0.5s
//2:长按 >1s为长按,以后每0.5s返回一个长按信号
#define key_input   PIND
#define key_mask    0x3c
#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3

#define key_no      0
#define key1_s      1
#define key1_l      2
#define key2_s      3
#define key2_l      4
#define key3_s      5
#define key3_l      6
#define key4_s      7
#define key4_l      8


//10ms调用一次key_read()
uchar key_read(void)
{
    static uchar key_state = 0, key_old, key_time;
    uchar key_press, key_temp, key_code = 0, key_return = key_no;
    key_press = key_input & key_mask;    //read IO
    switch(key_state)
    {
    case key_state_0:
        if(key_press != key_mask)
        {
            key_old = key_press;
            key_state = key_state_1;    //有键按下,转入状态1确认按键
        }
        break;
    case key_state_1:
        if(key_press == key_old)
        {
            key_state = key_state_2;    //已确认,进入2
            key_time = 0;               //清零计时
        }
        else
            key_state = key_state_0;    //为干扰,回到0
        break;
    case key_state_2:


        if(key_press == key_mask)
        {
            key_temp = (key_old ^ key_mask) >> 2; //key_old高四位取反,然后右移4位
            while(key_temp)
            {
                key_temp >>= 1;
                key_code++;                  //得到键值(1,2,3,4)
            }
            key_state = key_state_0;        //键已释放,返回状态0
            key_return = key_code * 2 - 1;  //返回键的短按值
        }


        else if(++key_time >= 100)          //按下时间>1s,进入状态2计时
        {
            key_temp = (key_old ^ key_mask) >> 2; //长按时,用当前按键输入计算键值
            while(key_temp)
            {
                key_temp >>= 1;
                key_code++;                 //得到键值(1,2,3,4)
            }
            key_state = key_state_3;        //进入状态3计时
            key_time = 0;                   //清零计时
            key_return = key_code * 2;      //返回按键的长按值
        }

        break;
    case key_state_3:
        if(key_press == key_mask)
            key_state = key_state_0;        //按键已释放,回到状态0
        else
        {   key_temp = (key_old ^ key_mask) >> 2; //长按时,用当前按键输入计算键值
            while(key_temp)
            {
                key_temp >>= 1;
                key_code++;                 //得到键值(1,2,3,4)
            }
            if(++key_time >= 50)            //按下时间>0.5s
            {
                key_time = 0;
                key_return = key_code * 2;
            }
        }
        break;
    default:
        key_state = key_state_0;
    }
    return key_return;
}


#endif

出0入0汤圆

 楼主| 发表于 2011-9-24 11:34:58 | 显示全部楼层
没有通过审核居然不能修改帖子,只好在下面回复一下了。
等通过在修改到楼主位。

在整个程序设计中,根据T0 2ms中断配合计数,得到时间序列,再由时间序列安排主程序的循环过程。
* 每10ms检测按键(状态机1),检测时钟状态(状态机2)
* 每10ms,在时间显示状态(clock_normal)下,从1302读取实时时钟
* 每6ms显示LCD
* 每1s,在非“闹钟设置状态”下,从1302中读出闹钟时间。(因为此处同时是显示缓冲区,“闹钟设置”状态下每1s读取,会导致设置闹钟时间不正确)
* 每1s,决定闹钟是否响铃。(此处在每分钟的第0秒进行闹钟判断,解决闹钟不停的问题)
* 显示程序中,通过处理当前时钟状态,将同类状态进行合并,方便显示。比如:时钟正常运行和时间设置状态(小时、分、秒等)可以共用显示。
* 程序设置了4个闹钟
* LCD1602中没有busy处理,所以RW要接地
* trace.h用来串口调试,删掉即可

问题:
* 程序十分臃肿,本人写的程序很少,基础太差。编译后M16的Program居然用了34%,data用了12%,,请指教,谢谢!
* 本来想使用结构体,枚举,但是不知道这样会不会减少体积。
* 是不是在程序中大量的参数传递、赋值使得程序臃肿?怎么才能好点呢?采用地址/指针传递会好些么?


另外说一下:
我使用Code Blocks写的程序,参考论坛里和网上其他地方简单设置了一下,真的很好用。
有代码折叠、自动完成、代码格式化功能,还能自动生成makefile,调用GCC编译,无缝开发。

程序代码ourdev_679224P64HF3.zip(文件大小:10K) (原文件名:DS1302.zip)

Code Blocks (原文件名:1.png)

出0入0汤圆

发表于 2012-2-16 20:25:13 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-2-16 20:26:21 | 显示全部楼层
回复【楼主位】ahit
-----------------------------------------------------------------------

谢谢楼主的分享~

出0入0汤圆

发表于 2012-2-16 20:33:05 | 显示全部楼层
mark.

出0入0汤圆

发表于 2012-4-2 08:31:37 | 显示全部楼层
mark,谢谢楼主了呀!!1

出0入0汤圆

发表于 2012-4-7 22:31:36 | 显示全部楼层
显示用状态机!!这个新鲜!!

出0入0汤圆

发表于 2012-6-5 10:09:52 | 显示全部楼层
非常感谢LZ!

出0入0汤圆

发表于 2012-6-26 10:28:46 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-8-23 13:03:33 | 显示全部楼层
非常感谢LZ

出0入0汤圆

发表于 2013-3-21 23:56:06 | 显示全部楼层
请问楼主,那个图是在哪里画的啊

出0入0汤圆

发表于 2015-3-19 13:55:22 | 显示全部楼层

请问楼主,那个图是在哪里画的啊

出0入0汤圆

发表于 2015-3-19 14:39:53 | 显示全部楼层
不错,正在看,支持下!

出0入0汤圆

发表于 2015-3-19 17:58:49 | 显示全部楼层
马老师很久没出来指点了。

出0入0汤圆

发表于 2015-3-19 23:26:35 | 显示全部楼层
楼主这个贴子太及时了,正好参考一下,谢谢
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 12:16

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表