本帖最后由 破解project 于 2018-5-21 09:50 编辑
我很想吐槽京东搜索“单片机”之后搜索的结果。https://search.jd.com/Search?keyword=%E5%8D%95%E7%89%87%E6%9C%BA&enc=utf-8
排在这么前的是这么一项
https://item.jd.com/11967011.html
这么个不上不下的东西。单片机的配件也不足,书的内容也少(本来就薄,然后还有一大半是做综合项目勾引着我去买额外配件的)。放在这么前的位置干嘛。
不过也是我一时脑热。星期五在电脑课上看到了橱柜中的开发板,一时兴起就下了单。
做了两天才发现板载配件只能够做秒表时钟之类的东西。不过秒表在书本的项目十二做过了(而且做得还不够好),所以我就选择做了时钟。
学习过程总体还是轻松愉快的,不过(中断/定时计数器/串口)相关的东西卡了我一天的时间。但编程过程很痛苦(Keil C51是给人用的吗?!还好意思要授权收费?!)
本来想做个头文件,像网上描述的Arduino那样的(我没用过Arduino,讲真在之前我也只是听说过单片机这个词而已)用自然名称接口编程。不过发现一些代码结构没法包装,只能够包装一些针脚定义。而且在寸土寸金的单片机中这样做加大的开销无法忽略,遂放弃。
星期六日学习,星期日晚上写代码调试,星期一的今天用早自修回来到现在在补注释。没有进行很精心的测试,如果有Bug欢迎指出。
代码如下:
[C] 纯文本查看 复制代码 /**-----------------------------------------
用51单片机(STC89C52,亚博智能BST-M51教材定制版开发板)制作电子时钟
作者:破解project [url=home.php?mod=space&uid=44767]@52pojie.cn[/url] 2018-05-21
功能简述:1.小时/分钟显示和设置。2.每秒闪光一次,可以以此读秒。3.有每小时响铃一次的功能。
使用说明:
1.LED灯:D1作为秒针指示,每秒发出50毫秒的闪光。D5显示每小时响铃模式状态,变亮标示此功能开启。其他灯留作每小时响铃的辅助提示。
2.有源蜂鸣器:如果每小时响铃功能开启,整点过去时发出持续1秒的响铃。反之持续静默。
3.按键:K2切换(显示时间/设置小时/设置分钟)模式,从设置分钟切回显示时间模式时秒针置零,可以以此精确校时。
K3和K4在设置时间模式中负责加减当前设置的项目。K5切换每小时响铃模式状态。
4.LED数码管:显示时间模式下显示(小时.分钟)。中间的点一秒钟切换一次(亮/按)状态。在设置时间模式,只有被设置项以1Hz的频率在闪烁。
开源协议:WTFPL
------------------------------------------*/
//STC89C52用头文件
#include <reg52.h>
//数码管段选锁存器
#define lsd_seg P0
//LED阵列
#define ledx P1
//数码管位选锁存器
#define lsd_pos P2
//按键key2到key5对应实体标号K2到K5
sbit key2=P3^4;
sbit key3=P3^5;
sbit key4=P3^6;
sbit key5=P3^7;
//秒针指示,闪一下代表+1s
sbit led1=P1^0;
//每小时响铃指示:开启则亮
sbit led5=P1^4;
//有源蜂鸣器:用于指示每小时响铃
sbit beep=P2^3;
//其他变量:50ms计数,1s记录(钟摆),闪光(秒针指示),设置模式,每小时响铃模式
int fiftyms_count=0,pendulum,flash,set_mode,hour_alarm;
//时间存储:时分秒
int hour,min,sec;
//led数码管(LED Segment Displays)从右至左位列表
unsigned char code lsd_list[]= {0x8f,0x4f,0x2f,0x1f};
//字符集码表:0123456789
unsigned char code charset[]= {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//CPU时间占用延迟(ms)
void delay(int i)
{
int j,k;
for(j=i; j>0; j--)
for(k=125; k>0; k--);
}
//显示一位数字
void lsd_display(int which,int body)
{
lsd_seg=0x00;//段选置0防止残影
lsd_pos|=0xf0;//P2^4~P2^7置1
lsd_pos=lsd_pos&lsd_list[which];//要位置1其他清0
/*
设置模式:
0:时间显示
1:小时设置
2:分钟设置
*/
switch(set_mode)
{
case 0:
//钟摆决定显示分割用的点,在第三个数码管上
if(which==2&&pendulum)
//0x80 点
lsd_seg=charset[body]+0x80;
else
lsd_seg=charset[body];
break;
case 1:
//23:小时闪烁,其余数字消失
if((which==2||which==3)&&pendulum)
lsd_seg=charset[body];
else
lsd_seg=0x00;
break;
case 2:
//01:分钟闪烁,其余数字消失
if((which==0||which==1)&&pendulum)
lsd_seg=charset[body];
else
lsd_seg=0x00;
}
//延迟保证显示亮度和视觉暂留作用
delay(1);
}
//显示所有4位数字,参数排列顺序按照开发板正面数码管位置
void lsd_show_all(int a,int b,int c,int d)
{
lsd_display(3,a);
lsd_display(2,b);
lsd_display(1,c);
lsd_display(0,d);
}
//设置定时器初值参数
void set_timer_para()
{
/*晶振:11.0592MHz
TH:(65536-ms*1000*11.0592/12)/256
TL:(65536-ms*1000*11.0592/12)%256
50ms,注意不要溢出*/
TH0=76;
TL0=0;
}
//初始化串行端口
void serial_init()
{
//0101 0000 方式1,允许接收串行
SCON=0x50;
//0001 0100 T1 方式二 计数,保留定时器工作方式
TMOD|=0x20;
//SMOD=0
PCON=0x00;
//9600波特率:查表11-6得出
TH1=0xfd;
TL1=0xfd;
//接收中断打开
ES=1;
//计数器打开
TR1=1;
}
//主函数
void main()
{
//总中断打开
EA=1;
//0000 0001 T0 定时 方式一
TMOD=0x01;
//初始化定时器初值和串行
set_timer_para();
serial_init();
//设置初始时间:11:59:57
hour=11;
min=59;
sec=57;
//定时器T0中断开启
ET0=1;
//T0开启
TR0=1;
//轮询
while(1)
{
//小时十位数,小时个位数,分钟十位数,分钟个位数
lsd_show_all(hour/10,hour%10,min/10,min%10);
//key2监听
if(key2==0)
{
//消抖
delay(10);
//再次监听
if(key2==0)
{
//设置模式循环:0 1 2
set_mode++;
if(set_mode==3)
{
set_mode=0;
//秒置0方便校时
sec=0;
}
}
//暂停,放开才继续可视化操作
while(key2==0);
}
//key3监听
if(key3==0)
{
delay(10);
if(key3==0)
{
//设置数值增加模式
switch(set_mode)
{
//小时
case 1:
hour++;
//满24清零
if(hour==24)
hour=0;
break;
//分钟
case 2:
min++;
//满60清零
if(min==60)
min=0;
}
}
while(key3==0);
}
//key4监听
if(key4==0)
{
delay(10);
if(key4==0)
{
//设置数值减少模式
switch(set_mode)
{
case 1:
hour--;
if(hour==-1)
hour=23;
break;
case 2:
min--;
if(min==-1)
min=59;
}
}
while(key4==0);
}
//key5监听
if(key5==0)
{
delay(10);
if(key5==0)
{
//在不符合整小时情况下才能设置,
//防止在整小时情况下改为了开启报时后在一秒后无法重置状态的问题
if(min!=0||(sec!=0&&sec!=1))
{
//反转每小时响铃模式设置
hour_alarm=!hour_alarm;
//注意LED置0才能亮
led5=!hour_alarm;
}
while(key5==0);
}
}
}
}
//T0中断 50ms
void timer0() interrupt 1
{
//防止计数溢出,所以要重置初值
set_timer_para();
//50ms计数+1
fiftyms_count++;
//50ms*20=1000ms=1s 以下if范围中事件每秒执行一次
if(fiftyms_count==20)
{
//秒+1
sec++;
//钟摆反转(摆动一下)
pendulum=!pendulum;
//重置50ms计数
fiftyms_count=0;
//60s=1m
if(sec==60)
{
min++;
sec=0;
}
//60m=1h
if(min==60)
{
hour++;
min=0;
}
//h loop 0~23
if(hour==24)
hour=0;
//如果小时响铃开启并且不在设置模式进入,防止在调时间时受到惊吓
if(hour_alarm&&!set_mode)
//分钟为零,秒为0或1(1用来重置状态)
if(min==0&&(sec==0||sec==1))
{
//反转响铃和LED阵列
beep=~beep;
ledx=~ledx;
/*0:响铃,LED阵列除led5之外全部发光
1:关闭响铃,led恢复*/
}
}
//1s结束
//如果秒针指示发光,则关闭之
if(led1==0)
led1=1;
//钟摆是否和闪光同步
if(pendulum!=flash)
{
//不同步反转led1
/*
保证在1s只反转一次,并且反转发光在下一个50ms后被上个判断关闭秒针指示
*/
led1=~led1;
flash=pendulum;
}
}
//串口中断,调试信息通过串行端口输出,选择0~6输出变量(波特率9600)
void debuginfo_sender() interrupt 4
{
//取出串行数据缓冲寄存器的数据至dat,只允许读写一个char
unsigned char dat;
dat=SBUF;
//复位接收中断标志
RI=0;
//调试:根据条件,存回缓冲器相应变量信息
switch(dat)
{
case 0:
SBUF=hour;
break;
case 1:
SBUF=min;
break;
case 2:
SBUF=sec;
break;
case 3:
SBUF=pendulum;
break;
case 4:
SBUF=flash;
break;
case 5:
SBUF=set_mode;
break;
case 6:
SBUF=hour_alarm;
break;
//未知指令接收
default:
SBUF='?';
}
while(!TI);//等待发送数据完成
//复位发送中断标志
TI=0;
}
|