学习马老师的第二版教材,按照状态机思想写了个时钟(DS1302+LCD1602)
买了马老师的第二版教材,正在入门中。参考马老师和论坛上的讨论,尝试按照状态机的思想写了个时钟,DS1302+LCD1602,在实验板上调试通过。
本人菜鸟一个,让大家见笑,请多指教!
一共使用了两个状态机:
1.按键扫描状态机:
*可处理多位独立按键(8位)
*可处理按键短按、长按(超过1s)、连续长按(超过1s后,每0.5s)
*本题中使用了3个按键
2.DS1302显示、时间设置的状态机
*这个逻辑简单,状态稍多,下面上图。
http://cache.amobbs.com/bbs_upload782111/files_46/ourdev_679154UXP9AU.png
(原文件名:DS1302_state_diagram.png) 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 = (time_dec + 1) % 24;
else if(key_temp == key2_l) //key2长按,+10
time_dec = (time_dec + 10) % 30;
if(time_dec > 24) time_dec = 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 = (time_dec + 1) % 60;
else if(key_temp == key2_l)
time_dec = (time_dec + 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 = (time_dec + 1) % 60;
else if(key_temp == key2_l)
time_dec = (time_dec + 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 = (date_dec + 1) % 100;
else if(key_temp == key2_l)
date_dec = (date_dec + 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 = (date_dec + 1) % 13;
else if(key_temp == key3_s)
{
update_1302();
clock_state = clock_normal;
}
break; 按键处理状态机:
#ifndef _KEY_H
#define _KEY_H
#include <avr/io.h>
#include <avr/iom16.h>
//PORTD7 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 没有通过审核居然不能修改帖子,只好在下面回复一下了。
等通过在修改到楼主位。
在整个程序设计中,根据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)
http://cache.amobbs.com/bbs_upload782111/files_46/ourdev_679228UF1E05.png
Code Blocks (原文件名:1.png) mark 回复【楼主位】ahit
-----------------------------------------------------------------------
谢谢楼主的分享~ mark. mark,谢谢楼主了呀!!1 显示用状态机!!这个新鲜!! 非常感谢LZ! mark{:lol:}{:lol:}{:lol:}{:lol:}{:lol:}{:lol:} 非常感谢LZ 请问楼主,那个图是在哪里画的啊
请问楼主,那个图是在哪里画的啊 不错,正在看,支持下! 马老师很久没出来指点了。 楼主这个贴子太及时了,正好参考一下,谢谢
页:
[1]