标题:
单片机音乐发生器 proteus仿真
[打印本页]
作者:
wuzi008
时间:
2021-3-26 13:34
标题:
单片机音乐发生器 proteus仿真
#include <reg51.h>
#include <string.h>
#define uchar unsigned char
#define uint unsigned int
sbit SDA=P1^0; //DS1302 数据线
sbit CLK=P1^1; //DSB1302 时钟线
sbit RST=P1^2; //DS1302 复位线
sbit RS=P2^0; //LCD 寄存器选择
sbit RW=P2^1; //LCD 读/写控制
sbit EN=P2^2; //LCD 启用
sbit K1=P3^4; //选择
sbit K2=P3^5; //加
sbit K3=P3^6; //减
sbit K4=P3^7; //确定
uchar tCount=0;
uchar dat; //定义参数
//一年中每个月的天数,二月的天数由年份决定
uchar MonthsDays[]={31,0,31,30,31,30,31,31,30,31,30,31};
//周日,周一到周六{0,1-6} [读取 DS1302 时分别是 1-7]
uchar *WEEK[]={"SUN","MON","TUS","WEN","THU","FRI","SAT"};
//LCD 显示缓冲
uchar LCD_DSY_BUFFER1[]={"0019 00-00-00 "};
uchar LCD_DSY_BUFFER2[]={"HWW 00:00:00 "};
uchar DateTime[7]; //所读取的日期时间
char Adjust_Index=-1;//当前调节的时间对象:秒,分,时,日,月,年(0,1,2,3,4,6)
uchar Change_Flag[]="-MHDM-Y";
//(分,时,日,月,年) (不调节秒周)
//延时,向 DS1302 写、读一字节以及从 DS1302 指定位置度、写数据的程序
//延时---------------------------------------------------
void DelayMS(uint x)
{
uchar i;
while(x--) for(i=0;i<120;i++);
}
//向 DS1302 写入一个字节
void Write_A_Byte_TO_DS1302(uchar x)
{
uchar i;
for(i=0;i<8;i++)
{
SDA=x&1;CLK=1; CLK=0; x>>=1;
}
}
//从 DA1302 读取一字节---------------------------------------
uchar Get_A_Byte_FROM_DS1302()
{
uchar i,b,t;
for(i=0;i<8;i++)
{
b>>=1; t=SDA;b|=t<<7;CLK=1;CLK=0;
}
//BCD 码转换
return b/16*10+b%16;
}
//从 DS1302 指定的位置读数据--------------------------------------
uchar Read_Data(uchar addr)
{
uchar dat;
RST=0;CLK=0;RST=1;
Write_A_Byte_TO_DS1302(addr);
dat=Get_A_Byte_FROM_DS1302();
CLK=1;RST=0;
return dat;
}
// 向 DS1302 某地址写入数据-------------------------------------------
void Write_DS1302(uchar addr,uchar dat)
{
CLK=0;RST=1;
Write_A_Byte_TO_DS1302(addr);
Write_A_Byte_TO_DS1302(dat);
CLK=0;RST=0;
}
//-------------------------------------------
//------设置时间-------------------------------------
void SET_DS1302()
{
uchar i;
Write_DS1302(0x8E,0x00); //写控制字,取消写保护
for(i=1;i<7;i++) //分时日月年依次写入
{
//分的起始地址 10000010(0x82),后面续依次是时.日.月.周.年.写入地址每次递增 2
Write_DS1302(0x80+ 2*i,(DateTime[i]/10<<4)|(DateTime[i]%10)); }
Write_DS1302(0x8E,0x80);//加保护
}
//---------------------------------------------------
//读取当前日期时间
//---------------------------------------------------
void GetTime()
{
uchar i;
for(i=0;i<7;i++)
{
DateTime[i]=Read_Data(0x81+2*i);
}
}
//---------------------------------------------------
//1602LCD 的若干显示控制代码
//uchar Read_LCD_State() 读 LCD 状态
uchar Read_LCD_State()
{
uchar state;
RS=0;RW=1;EN=1;DelayMS(1);state=P0;EN=0;DelayMS(1);
return state;
}
//void LCD_Busy_Wait() 忙等待
void LCD_Busy_Wait()
{
while((Read_LCD_State()&0x80)==0x80);
DelayMS(5);
}
//void Write_LCD_Data(uchar dat) 向 LCD 写数据
void Write_LCD_Data(uchar dat)
{
LCD_Busy_Wait();
RS=1;RW=0;EN=0;P0=dat;EN=1;DelayMS(1);EN=0;
}
//void Write_LCD_Command(uchar cmd) 写 LCD 指令
void Write_LCD_Command(uchar cmd)
{
LCD_Busy_Wait();
RS=0;RW=0;EN=0;P0=cmd;EN=1;DelayMS(1);EN=0;
}
// Void Init_LCD() LCD 初始化
void Init_LCD()
{
Write_LCD_Command(0x38);DelayMS(1);
Write_LCD_Command(0x01);DelayMS(1);
Write_LCD_Command(0x06);DelayMS(1);
Write_LCD_Command(0x0c);DelayMS(1);
}
//void Set_LCD_POS(uchar P) 设置液晶显示位置
void Set_LCD_POS(uchar p)
{
Write_LCD_Command(p|0x80);
}
//------------------------------------------------------
//在 LCD 上显示字符串
void Display_LCD_String(uchar p,uchar *s)
{
uchar i;
Set_LCD_POS(p);
for(i=0;i<16;i++)
{
Write_LCD_Data(s[i]); DelayMS(1);
}
}
//-----------------------------------------------------------
//日期与时间值转换为数字字符
void Format_DateTime(uchar d,uchar *a)
{
a[0]=d/10+'0';a[1]=d%10+'0';
}
//----------------------------------------------------------
//判断是否为闰年
uchar isLeapYear(uint y)
{
return(y%4==0&y%100!=0)||(y%400==0);
}
//-----------------------------------------------------------
//求自 2000.1.1 开始的任何一天是星期几
//函数没有通过,求出总天数后再求星期几
//因为求总天数可能会超越 uint 的范围
void RefreshWeekDay()
{
uint i,d,w=5;
//已知 1999.12.31 是周五
for(i=2000;i<2000+DateTime[6];i++)
{
d=isLeapYear(i)?366:365;
w=(w+d)%7;
}
d=0;
for(i=1;i<DateTime[4];i++) d+=MonthsDays[i];
d+=DateTime[3];
//保存星期,0~6 表示星期日至周六,为了与DS1302 的星期格式匹配,返回值需要加 1 DateTime[5]=(w+d)%7+1;
}
//----------------------------------------------------------
void DateTime_Adjust(char x)
{
switch(Adjust_Index)
{
case 6://年 00-99
if(x==1&DateTime[6]<99) DateTime[6]++;
if(x==-1&DateTime[6]>0) DateTime[6]--;
//获取 2 月天数
MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
//如果年份变化后当前月份的天数大于上限则设为上限
if(DateTime[3]>MonthsDays[DateTime[4]])
DateTime[3]=MonthsDays[DateTime[4]]; RefreshWeekDay();
//刷新星期
break;
case 4:
//月 01-12
if(x==1&DateTime[4]<12) DateTime[4]++;
if(x==-1&DateTime[4]>1) DateTime[4]--;
//获取 2 月天数
MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
//如果月份变化后当前月份的天数大于上限则设为上限
if(DateTime[3]>MonthsDays[DateTime[4]])
DateTime[3]=MonthsDays[DateTime[4]];
RefreshWeekDay();
//刷新星期
break;
case 3://日00-28/29/30/31;调节之前首先根据年份得出该年中2月的天数
MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
//根据当前月份决定调节日期的上限
if(x==1&DateTime[3]<MonthsDays[DateTime[4]]) DateTime[3]++;
if(x==-1&DateTime[3]>0) DateTime[3]--;
RefreshWeekDay();//刷新星期
break;
case 2://时
if(x==1&DateTime[2]<23) DateTime[2]++;
if(x==-1&DateTime[2]>0) DateTime[2]--;
break;
case 1://分
if(x==1&DateTime[1]<59) DateTime[1]++;
if(x==-1&DateTime[1]>0) DateTime[1]--;
break;
}
}
//---------------------------------------------------------
//定时器 0 每秒刷新 LCD 显示
void T0_INT() interrupt 1
{
TH0=-50000/256;
TL0=-50000%256;
if(++tCount!=2) return;
tCount=0;
//按指定的格式生成待显示的日期时间串
Format_DateTime(DateTime[6],LCD_DSY_BUFFER1+5);
Format_DateTime(DateTime[4],LCD_DSY_BUFFER1+8);
Format_DateTime(DateTime[3],LCD_DSY_BUFFER1+11);
//星期 strcpy(LCD_DSY_BUFFER1+13,WEEK[DateTime[5]-1]);
//时分秒
Format_DateTime(DateTime[2],LCD_DSY_BUFFER2+5);
Format_DateTime(DateTime[1],LCD_DSY_BUFFER2+8);
Format_DateTime(DateTime[0],LCD_DSY_BUFFER2+11);
//显示年月日,星期,时分秒
Display_LCD_String(0x00,LCD_DSY_BUFFER1);
Display_LCD_String(0x40,LCD_DSY_BUFFER2);
}
//---键盘中断(INT0)-----------------------------------------
void EX_INT0() interrupt 0
{
if(K1==0) //选择调整对象(Y M D H M)
{
while(K1==0);
if(Adjust_Index==-1||Adjust_Index==1) Adjust_Index=7;
Adjust_Index--;
if(Adjust_Index==5) Adjust_Index=4;//跳过对星期的调节
LCD_DSY_BUFFER2[13]='[';
LCD_DSY_BUFFER2[14]=Change_Flag[Adjust_Index];
LCD_DSY_BUFFER2[15]=']';
}
else if(K2==0)//加
{
while(K2==0); DateTime_Adjust(1);
}
else if(K3==0)//减
{
while(K3==0); DateTime_Adjust(-1);
}
else if(K4==0)//确定
{
while(K4==0); SET_DS1302(); //将调整后的时间写入 1302
LCD_DSY_BUFFER2[13]=' ';
LCD_DSY_BUFFER2[14]=' ';
LCD_DSY_BUFFER2[15]=' ';
Adjust_Index=-1;
//操作索引重设为-1,时间继续正常显示
}
}
//---------------------------------------------------------------
//主程序 void main()
void main()
{
Init_LCD();//液晶初始化
IE=0x83; //允许INT0,T0中断
IP=0x01;
IT0=0x01;
TMOD=0x01;
TH0=-50000/256;
TL0=-50000%256;
TR0=1;
while(1)
{
//如果为执行调整操作则正常读取当前时间
if(Adjust_Index==-1) GetTime();
}
}
复制代码
新建 WinRAR 压缩文件.rar
2021-3-26 13:33 上传
点击文件名下载附件
下载积分: 黑币 -5
14.79 KB, 下载次数: 5, 下载积分: 黑币 -5
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1