所需要使用的元器件:
代码:(使用的是keil5)
#include <reg52.h> //调用单片机头文件
#define uchar unsigned char //无符号字符型 宏定义 变量范围0~255
#define uint unsigned int //无符号整型 宏定义 变量范围0~65535
//数码管段选定义 0 1 2 3 4 5 6 7 8 9
uchar code smg_du[]={0xa0,0xbb,0x62,0x2a,0x39,0x2c,0x24,0xBa,0x20,0x28,
0x30,0x25,0xe4,0x23,0x64,0x74,0xff}; //断码
//数码管位选定义
uchar code smg_we[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//数码管位选定义
uchar dis_smg[8] = {0};
bit flag_200ms ;
bit flag_100ms ;
sbit beep = P3^7; //蜂鸣器定义
bit flag_beep_en;
uint clock_value; //用作闹钟用的
uchar flag_s = 0x03;//控制数码管闪烁的变量
sbit clk = P1^3; //ds1302时钟线定义
sbit io = P1^4; //数据线
sbit rst = P1^5; //复位线
//秒 分 时 日 月 年 星期
uchar code write_add[]={0x80,0x82,0x84,0x86,0x88,0x8c,0x8a}; //写地址
uchar code read_add[] ={0x81,0x83,0x85,0x87,0x89,0x8d,0x8b}; //读地址
uchar miao,fen,shi,ri,yue,week,nian;
uchar i;
uchar fen1=3,shi1=8; //闹钟变量的定义
uchar open1;
uchar menu_1,menu_2;
sbit key1 = P3^6; //按键IO口定义
sbit key2 = P3^5; //按键IO口定义
sbit key3 = P3^4; //按键IO口定义
sbit key4 = P3^3; //按键IO口定义
/*************写一个数据到对应的地址里***************/
void write_ds1302(uchar add,uchar dat)
{
rst = 1; //把复位线拿高
for(i=0;i<8;i++)
{ //低位在前
clk = 0; //时钟线拿低开始写数据
io = add & 0x01;
add >>= 1; //把地址右移一位
clk = 1; //时钟线拿高
}
for(i=0;i<8;i++)
{
clk = 0; //时钟线拿低开始写数据
io = dat & 0x01;
dat >>= 1; //把数据右移一位
clk = 1; //时钟线拿高
}
rst = 0; //复位线合低
}
/*************从对应的地址读一个数据出来***************/
uchar read_ds1302(uchar add)
{
uchar value,i;
rst = 1; //把复位线拿高
for(i=0;i<8;i++)
{ //低位在前
clk = 0; //时钟线拿低开始写数据
io = add & 0x01;
add >>= 1; //把地址右移一位
clk = 1; //时钟线拿高
}
for(i=0;i<8;i++)
{
clk = 0; //时钟线拿低开始读数据
value >>= 1;
if(io == 1)
value |= 0x80;
clk = 1; //时钟线拿高
}
rst = 0; //复位线合低
return value; //返回读出来的数据
}
/*************把要的时间 年月日 都读出来***************/
void read_time()
{
miao = read_ds1302(read_add[0]); //读秒
fen = read_ds1302(read_add[1]); //读分
shi = read_ds1302(read_add[2]); //读时
ri = read_ds1302(read_add[3]); //读日
yue = read_ds1302(read_add[4]); //读月
nian = read_ds1302(read_add[5]); //读年
week = read_ds1302(read_add[6]); //读星期
}
/*************把要写的时间 年月日 都写入ds1302里***************/
void write_time() //把时间写进去
{
write_ds1302(0x8e,0x00); //打开写保护
write_ds1302(write_add[0],miao); //写秒
write_ds1302(write_add[1],fen); //写分
write_ds1302(write_add[2],shi); //写时
write_ds1302(write_add[3],ri); //写日
write_ds1302(write_add[4],yue); //写月
write_ds1302(write_add[5],nian); //写星期
write_ds1302(write_add[6],week); //写年
write_ds1302(0x8e,0x80); //关闭写保护
}
/***********************1ms延时函数*****************************/
void delay_1ms(uint q)
{
uint i,j;
for(i=0;i<q;i++)
for(j=0;j<120;j++);
}
/********************独立按键程序*****************/
uchar key_can; //按键值
void key() //独立按键程序
{
uchar key_new; //key_new 这个变量的功能是做按键松手检测的
key_can = 0; //按键值还原成0
if(key1 == 0 || key2 == 0 || key3 == 0 || key4 == 0) //有按键按下
{
delay_1ms(1); //按键延时消抖动
if(key_new == 1)
{
key_new = 0; //key_new = 0 说明按键已按下
if(key1 == 0) //确认是按键按下
key_can = 1; //得到按键值
if(key2 == 0) //确认是按键按下
key_can = 2; //得到按键值
if(key3 == 0) //确认是按键按下
key_can = 3; //得到按键值
if(key4 == 0) //确认是按键按下
key_can = 4; //得到按键值
}
}
else
key_new = 1; //key_new = 1 说明按键已经松开了
}
/**********************设置函数************************/
void key_with()
{
if(key_can == 1) //设置键
{
menu_1++;
menu_2 = 1;
if(menu_1 == 1) //设置时间
flag_s = 0x03;
if(menu_1 == 2) //设置年 月 日
flag_s = 0x03;
if(menu_1 == 3) //设置星期 闹钟的开和关
flag_s = 0x03;
if(menu_1 == 4) 设置 闹钟
flag_s = 0x0c;
if(menu_1 > 4) //回到正常显示
{
menu_1 = 0;
}
}
if(key_can == 2) //选择键
{
flag_200ms = 1;
if((menu_1 == 1) || (menu_1 == 2)) //设置时间
{
menu_2 ++;
if(menu_2 > 3)
menu_2 = 1;
if(menu_2 == 1)
flag_s = 0x03;
if(menu_2 == 2)
flag_s = 0x18;
if(menu_2 == 3)
flag_s = 0xc0;
}
if(menu_1 == 3) //设置星期 闹钟的开和关
{
menu_2 ++;
if(menu_2 > 2)
menu_2 = 1;
if(menu_2 == 1)
flag_s = 0x03;
if(menu_2 == 2)
flag_s = 0xf8;
}
if(menu_1 == 4) //设置 闹钟
{
menu_2 ++;
if(menu_2 > 2)
menu_2 = 1;
if(menu_2 == 1)
flag_s = 0x0c;
if(menu_2 == 2)
flag_s = 0x60;
}
}
if(menu_1 == 1) //
{
if(menu_2 == 1) //设置秒
{
if(key_can == 3) //加键
{
miao+=0x01; //设置秒钟加1
if((miao & 0x0f) >= 0x0a)
miao = (miao & 0xf0) + 0x10;
if(miao >= 0x60)
miao = 0;
}
if(key_can == 4) //减键
{
if(miao == 0x00)
miao = 0x5a;
if((miao & 0x0f) == 0x00)
miao = (miao | 0x0a) - 0x10;
miao -- ; //设置秒减1
}
}
if(menu_2 == 2) //设置分
{
if(key_can == 3) //加键
{
fen+=0x01; //设置分钟加1
if((fen & 0x0f) >= 0x0a)
fen = (fen & 0xf0) + 0x10;
if(fen >= 0x60)
fen = 0;
}
if(key_can == 4) //减键
{
if(fen == 0x00)
fen = 0x5a;
if((fen & 0x0f) == 0x00)
fen = (fen | 0x0a) - 0x10;
fen -- ; //设置分钟减1
}
}
if(menu_2 == 3) //设置时
{
if(key_can == 3) //加键
{
shi+=0x01; //设置时钟加1
if((shi & 0x0f) >= 0x0a)
shi = (shi & 0xf0) + 0x10;
if(shi >= 0x24)
shi = 0;
}
if(key_can == 4) //减键
{
if(shi == 0x00)
shi = 0x24;
if((shi & 0x0f) == 0x00)
shi = (shi | 0x0a) - 0x10;
shi -- ; //设置时钟减1
}
}
write_time(); //把时间写进去
}
if(menu_1 == 2) // 设置年月日
{
if(menu_2 == 1) //设置日
{
if(key_can == 3) //加键
{
ri+=0x01; //设置日加1
if((ri & 0x0f) >= 0x0a)
ri = (ri & 0xf0) + 0x10;
if(ri >= 0x32)
ri = 0;
}
if(key_can == 4) //减键
{
if(ri == 0x01)
ri = 0x32;
if((ri & 0x0f) == 0x00)
ri = (ri | 0x0a) - 0x10;
ri -- ; //设置日减1
}
}
if(menu_2 == 2) //设置月
{
if(key_can == 3) //加键
{
yue+=0x01; //设置月加1
if((yue & 0x0f) >= 0x0a)
yue = (yue & 0xf0) + 0x10;
if(yue >= 0x13)
yue = 1;
}
if(key_can == 4) //减键
{
if(yue == 0x01)
yue = 0x13;
if((yue & 0x0f) == 0x00)
yue = (yue | 0x0a) - 0x10;
yue -- ; //设置月减1
}
}
if(menu_2 == 3) //设置年
{
if(key_can == 3) //加键
{
nian+=0x01; //设置年加1
if((nian & 0x0f) >= 0x0a)
nian = (nian & 0xf0) + 0x10;
if(nian >= 0x9a)
nian = 1;
}
if(key_can == 4) //减键
{
if(nian == 0x01)
nian = 0x9a;
if((nian & 0x0f) == 0x00)
nian = (nian | 0x0a) - 0x10;
nian -- ; //设置年减1
}
}
write_time(); //把时间写进去
}
if(menu_1 == 3) // 设置星期 闹钟的开和关
{
if(menu_2 == 1) //设置星期
{
if(key_can == 3) //加键
{
week+=0x01; //设置星期加1
if((week & 0x0f) >= 0x0a)
week = (week & 0xf0) + 0x10;
if(week >= 0x08)
week = 1;
}
if(key_can == 4) //减键
{
if(week == 0x01)
week = 0x08;
if((week & 0x0f) == 0x00)
week = (week | 0x0a) - 0x10;
week -- ; //设置星期减1
}
}
if(menu_2 == 2) //设置闹钟的开和关
{
if((key_can == 4) || (key_can == 3)) //加
open1 ++;
if(open1 >= 2)
open1 = 0;
}
write_time(); //把时间写进去
}
if(menu_1 == 4) // 设置 闹钟
{
if(menu_2 == 1) //设置分
{
if(key_can == 3) //加键
{
fen1+=0x01; //设置闹钟分加1
if((fen1 & 0x0f) >= 0x0a)
fen1 = (fen1 & 0xf0) + 0x10;
if(fen1 >= 0x60)
fen1 = 0;
}
if(key_can == 4) //减键
{
if(fen1 == 0x00)
fen1 = 0x5a;
if((fen1 & 0x0f) == 0x00)
fen1 = (fen1 | 0x0a) - 0x10;
fen1 -- ; //设置闹钟分减1
}
}
if(menu_2 == 2) //设置时
{
if(key_can == 3) //加键
{
shi1+=0x01; //设置闹钟时加1
if((shi1 & 0x0f) >= 0x0a)
shi1 = (shi1 & 0xf0) + 0x10;
if(shi1 >= 0x24)
shi1 = 0;
}
if(key_can == 4) //减键
{
if(shi1 == 0x00)
shi1 = 0x5a;
if((shi1 & 0x0f) == 0x00)
shi1 = (shi1 | 0x0a) - 0x10;
shi1 -- ; //设置闹钟时减1
}
}
write_time(); //把时间写进去
}
}
/************菜单处理函数****************/
void menu_dis()
{
if((menu_1 == 1)) //选设置时 分 秒
{
dis_smg[0] = smg_du[miao % 16]; //显示秒
dis_smg[1] = smg_du[miao / 16]; //
dis_smg[2] = 0x7f;
dis_smg[3] = smg_du[fen % 16]; //显示分
dis_smg[4] = smg_du[fen / 16]; //
dis_smg[5] = 0x7f;
dis_smg[6] = smg_du[shi % 16]; //显示秒
dis_smg[7] = smg_du[shi / 16]; //
}
if((menu_1 == 2)) //选设置年 月 日
{
dis_smg[0] = smg_du[ri % 16]; //显示日
dis_smg[1] = smg_du[ri / 16]; //
dis_smg[2] = 0xfe;
dis_smg[3] = smg_du[yue % 16]; //显示月
dis_smg[4] = smg_du[yue / 16]; //
dis_smg[5] = 0xfe;
dis_smg[6] = smg_du[nian % 16]; //显示年
dis_smg[7] = smg_du[nian / 16]; //
}
if((menu_1 == 3)) //设置星期 和 闹钟的开和关
{
dis_smg[0] = smg_du[week % 16]; //显示星期
dis_smg[1] = smg_du[week / 16]; //
dis_smg[2] = 0x7f;
if(open1 == 1) //开闹钟
{
dis_smg[3] = 0xb0; //OPEN
dis_smg[4] = 0x64;
dis_smg[5] = 0x70;
dis_smg[6] = 0xa0;
}else
{
dis_smg[3] = 0x74; //OFF
dis_smg[4] = 0x74;
dis_smg[5] = 0xa0;
dis_smg[6] = 0xFF;
}
}
if(menu_1 == 4) //选设置时 分 秒
{
dis_smg[0] = 0xff; //
dis_smg[1] = 0xff; //
dis_smg[2] = smg_du[fen1 % 16]; //显示分
dis_smg[3] = smg_du[fen1 / 16]; //
dis_smg[4] = 0x7f;
dis_smg[5] = smg_du[shi1 % 16]; //显示秒
dis_smg[6] = smg_du[shi1 / 16]; //
dis_smg[7] = 0xff;
}
}
/*************闹钟报警函数***************/
void clock_dis()
{
if(flag_100ms == 1) //100ms执行一次
{
flag_100ms = 0;
if(open1 == 1) //如果闹钟打开
{
if((miao == 0) && (fen == fen1) && (shi == shi1))
{
flag_beep_en = 1; //有报警 打开蜂鸣器响的标志位
}
if(flag_beep_en == 1) //闹钟以被打开
{
beep = ~beep; //蜂鸣器叫3秒
}
if((miao == 0) && (fen == fen1+1) && (shi == shi1))
{
flag_beep_en = 0; //1分钟后关闭闹钟
}
}
}
}
/*************定时器0初始化程序***************/
void time_init()
{
EA = 1; //开总中断
TMOD = 0X01; //定时器0、工作方式1
ET0 = 1; //开定时器0中断
TR0 = 1; //允许定时器0定时
}
/*************时钟显示***************/
void xianshi_ds1302()
{
uchar value;
value ++;
if(value <= 5*2) //2秒
{
dis_smg[0] = smg_du[miao % 16]; //显示秒
dis_smg[1] = smg_du[miao / 16]; //
dis_smg[2] = 0x7f;
dis_smg[3] = smg_du[fen % 16]; //显示分
dis_smg[4] = smg_du[fen / 16]; //
dis_smg[5] = 0x7f;
dis_smg[6] = smg_du[shi % 16]; //显示秒
dis_smg[7] = smg_du[shi / 16]; //
}
else if(value <= 5*4) //2秒
{
dis_smg[0] = smg_du[ri % 16]; //显示日
dis_smg[1] = smg_du[ri / 16]; //
dis_smg[2] = 0x7f;
dis_smg[3] = smg_du[yue % 16]; //显示月
dis_smg[4] = smg_du[yue / 16]; //
dis_smg[5] = 0x7f;
dis_smg[6] = smg_du[nian % 16]; //显示年
dis_smg[7] = smg_du[nian / 16]; //
}
else if(value <= 5*6) //2秒
{
dis_smg[0] = smg_du[week % 16]; //显示星期
dis_smg[1] = smg_du[week / 16]; //
}
else
value = 0;
}
/*****************主函数********************/
void main()
{
uchar ii;
beep = 0; //开机叫一声
delay_1ms(150);
P0 = P1 = P2 = P3 = 0xff; //单片机IO口初始化为1
time_init(); //定时器初始化
while(1)
{
key(); //按键程序
if(flag_beep_en == 0) //只有闹钟关了的时候才能进入设置
key_with();
else
{
flag_beep_en = 0; //按下任意键可关闭闹钟
beep = 1;
}
if(flag_200ms == 1)
{
flag_200ms = 0;
read_time(); //读时间
xianshi_ds1302(); //显示时钟
}
menu_dis(); //设置的对应的显示处理
clock_dis(); //闹钟报警函数
P0 = 0xff; //消隐
P2 = smg_we[ii]; //位选
P0 = dis_smg[ii]; //段选
ii++;
if(ii >= 8)
ii = 0;
delay_1ms(1);
}
}
/*************定时器0中断服务程序***************/
void time0_int() interrupt 1
{
uint value;
TH0 = 0xF8;
TL0 = 0x30; // 2ms 12M
value ++;
if((value % 50) == 0) //100ms
flag_100ms = 1;
if(value % 100 == 0)
{
flag_200ms = 1;
}
}
仿真图(proteus)
仿真图(CAD)
PCB图:
红线所接的是跳线
常见问题:
数码管采用的是什么扫描方式?
一位数码管的设计就是采用静态扫描的方式,因为一位数码管是8个段选1个位选,如果采用动态,那就是得用9个IO口,而且程序也比较麻烦,如果选用静态那么位选接电源或地(共阳接电源,共阴接地),段选接IO口,就可以控制显示了,这样只用8个IO口就ok,而且程序比较简单。多位一体的数码管只能用动态扫描的方式,因为硬件本身就将每个位的段都接到一起了,所以只能动态控制了。
蜂鸣器或继电器的驱动三极管为什么选用pnp型的(9012、8550),而不是npn型的(9013、8050)?
因为单片机刚一上电的时候所有的IO口会有一个短暂的高电平。如果选用npn型的,即使程序上将IO口拉低,蜂鸣器或继电器也会响一小下或吸合一下,为了避免这种情况发生,就选用pnp型的。因为我们想控制蜂鸣器或继电器工作单片机的IO口要低电平,这样就避免了,因为我们不可能刚一通电就让蜂鸣器响或继电器吸合。避免了不必要的麻烦。
液晶三脚接的电阻是而不是可调电阻?
经过查阅资料得知(买液晶时给的资料),液晶3脚是灰度调节引脚,灰度正常时是0.5~1V左右,用可调电阻其他就是电阻分压的原理得到的电压,而我们直接用的是电阻也是可以得到正常显示的电压的。
为什么继电器吸合或风扇转动时,液晶屏幕会变暗?
从问题5中可以了解大概,就是液晶的灰度是电压控制的,当继电器吸合或风扇转动时,需要的电流较大,而我们采用的电源线或电池盒供电会有一定的压降。这样液晶的3脚采集的电压就高了。所以灰度就不合适了。解决的办法是,电源尽量用好一点的,或换粗一点的电源线供电(主要的压降都在电源线上)。
超声波测距模块的工作原理?
一个控制口发一个10US以上的高电平,就可以在接收口等待高电平输出.一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,方可算出距离.如此不断的周期测,就可以达到你移动测量的值了。
你的程序是怎么下载进去的?
STC单片机程序下载:
这个STC_ISP软件是串口下载的,加载程序文件夹中的.hex文件链接好下载器就可以下载了(首先下载器的驱动得装好)
AT的单片机怎么下载
AT单片机要用专门的下载器才能下载
有些电阻的阻值是怎么算出来的?
比如是LED串联的分压电阻,计算方法是:R=U/I
Led工作电压是3V左右,那么电阻的电压就是(供电电压-3v)=2v
Led点亮的电流是4~20ma,那么电阻的电流也是4~20ma
这样电阻就是2除以0.004~0.02=100~500Ω
但是实际使用的时候用100~500Ω,led就太亮了,很容易烧坏,所以就适当的加大了电阻。
晶振为什么选用12M?
12M是比较常用的晶,51单片机是12分频的,如果选用12M晶振,如果是单指令周期的语句,刚好是1us,其他语句正好是1us的整数倍。很轻松算出每个语句用了多长时间。
晶振为什么选用11.0592M?
11.0592比较常应用在串口通信和红外遥控电路中。
11.0592M是因为在进行通信时,12M频率进行串行通信不容易实现标准的波特率,比如9600,4800,而11.0592M计算时正好可以得到,因此在有通信接口的单片机中,一般选11.0592M计算一下就知道了。如我们要得到9600 的波特率,晶振为11.0592M 和12M,定时器1为模式2,SMOD 设为1,分别看看那所要求的TH1 为何值。代入公式:11.0592M 9600=(2÷32)×((11.0592M/12)/(256-TH1)) TH1=25012M 9600=(2÷32)×((12M/12)/(256-TH1))TH1≈249.49上面的计算可以看出使用12M 晶体的时候计算出来的TH1 不为整数,而TH1 的值只能取整数,这样它就会有一定的误差存在不能产生精确的9600 波特率。
比如做GSM的设计时,用12M就是不可行的,就得用11.0592M。
定时器的初值是怎么算的?
我们一般采用的是50ms的定时,那样20个50ms就是1s。至于初值怎么算,课本上刚学的时候就交了,可以自己看下,如果看了会发现我们的初值好像和课本上的不一样,那是因为我们是用软件算的。计算初值有好多软件,可以找度娘。软件很方便,动动手就可以了哦。
仿真图好像和实物图有差别呢?文章来源:https://www.toymoban.com/news/detail-729323.html
仿真图就是一个模拟用的,和实际效果是有点差别的,仿真中没有晶振电路和复位电路都是可以工作的,焊接是按照原理图来的而不是仿真。文章来源地址https://www.toymoban.com/news/detail-729323.html
到了这里,关于基于51单片机的电子时钟(原理图,代码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!