首页 > 其他分享 >51单片机学习第四课---B站UP主江协科技

51单片机学习第四课---B站UP主江协科技

时间:2024-09-21 09:54:45浏览次数:3  
标签:第四课 H1 主江协 ds1302 --- M7 time include define

DS1302实时时钟

1、基本知识讲解

2、用DS1302做时钟

main.c

 #include <regx52.h>
 #include<delay.h>
 #include<DS1302.h>
 #include<LCD1602.h>


 void main()
 {
  LCD_Init();
  ds1302_init();
  LCD_ShowString(1,1,"  -  -");
   LCD_ShowString(2,1,"  :  :  :  ");

 //如果读出时间为一个大于59并且不动的数则芯片有可能是处于写保护状态,在此处加上DS1302 WriteByte(0x8E,0x00)即可解除芯片写保护
 //ds1302_writebyte(0x8e,0x00);
  ds1302_set_time();

  while(1)
  {
   ds1302_read_time();
   LCD_ShowNum(1,1,ds1302_time[0],2); //在DS1302中数据存储是以BCD码形式存储的,0001 0000在BCD码中是9	,如果以十进制显示就是16。
   //BCD码用4位二进制数来表示1位十进制数	,从0~9,而十六进制从0~F,0001 1100 十六进制是1C,在BCD码中不合法。

   LCD_ShowNum(1,4,ds1302_time[1],2);
    LCD_ShowNum(1,7,ds1302_time[2],2);
	  LCD_ShowNum(2,1,ds1302_time[3],2);
	    LCD_ShowNum(2,4,ds1302_time[4],2);
		  LCD_ShowNum(2,7,ds1302_time[5],2);
		    LCD_ShowNum(2,10,ds1302_time[6],2);


  }
 }


		

ds1302.c

#include<REGX52.h>
sbit DS1302_SCLK =P3^6;
sbit DS1302_IO =P3^4;	
sbit DS1302_CE=P3^5;
#define ds1302_write_second  0x80  //注意define后面不用加分号
#define ds1302_write_minute  0x82  //对位地址重新定义,这样方便调用,直接调用0x80如果不看手册不知道对应的是什么
#define ds1302_write_hour  0x84
#define ds1302_write_date  0x86
#define ds1302_write_month  0x888
#define ds1302_write_day  0x8a
#define ds1302_write_year  0x8c
#define ds1302_write_wp  0x8e

unsigned char ds1302_time[]={19,11,16,12,59,55,6}; //年 月 日 小时 分钟 秒 星期    定义初值
void  ds1302_init()
{
  	DS1302_SCLK =0;
  	
  	DS1302_CE=0;//上电默认高电平,初始化给0 	
}

void ds1302_writebyte(unsigned char command,Data)
{
   unsigned char i;
  DS1302_CE=1;
  for(i=0;i<8;i++)
  {
   	DS1302_IO=command&(0x01<<i);
	DS1302_SCLK =1;
	DS1302_SCLK =0;

  }
  for(i=0;i<8;i++)
  {
   	DS1302_IO=(Data&(0x01<<i));
	DS1302_SCLK =1;
	DS1302_SCLK =0;
  }
  DS1302_CE=0;
 
}
unsigned char ds1302_readbyte(unsigned char command)
{
unsigned char i,Data=0x00; //注意这里的data存放的是一个字节,有8位数据
DS1302_CE=1; 
command|=0x01;	//这一步必加,不然全部显示65.因为读命令最低位是1,所以这一步的作用就是先把最低位置1
 for(i=0;i<8;i++)
  {
   	DS1302_IO=(command&(0x01<<i)); //io口从最低位开始读取读命令地址,直到8位读取完毕
	DS1302_SCLK =0;
	DS1302_SCLK =1;
  }
 for(i=0;i<8;i++)
  {
   
	DS1302_SCLK =1;
	DS1302_SCLK =0;						 //假设写入的0011 0101
	if(DS1302_IO){Data=(Data|0x01<<i);}// 当DS1302_IO=1时,才执行,则data=0000 0000|0000 0001=0000 0001
										//第二位是0,不执行,此时i=1;第三位是1,执行if里的语句,i=2,则data=0000 0001|0000 0100=0000 0101
										//第四位是0,不执行,此时i=3,第五位是1,执行,i=4,则data=0000 0101|0001 0000=0001 0101,以此类推
  }
   DS1302_CE=0;
   DS1302_IO=0;//读取后将IO设置为0,否则读出的数据会出错
   return Data;
   }
void ds1302_set_time()
{
 ds1302_writebyte(ds1302_write_wp,0x00);//解除写保护
 ds1302_writebyte(ds1302_write_year,ds1302_time[0]/10*16+ds1302_time[0]%10); //十进制转BCD码写入
 ds1302_writebyte(ds1302_write_month,ds1302_time[1]/10*16+ds1302_time[1]%10);
 ds1302_writebyte(ds1302_write_date,ds1302_time[2]/10*16+ds1302_time[2]%10);
 ds1302_writebyte(ds1302_write_hour,ds1302_time[3]/10*16+ds1302_time[3]%10);
 ds1302_writebyte(ds1302_write_minute,ds1302_time[4]/10*16+ds1302_time[4]%10);
 ds1302_writebyte(ds1302_write_second,ds1302_time[5]/10*16+ds1302_time[5]%10);
 ds1302_writebyte(ds1302_write_day,ds1302_time[6]/10*16+ds1302_time[6]%10);
 ds1302_writebyte(ds1302_write_wp,0x80);//恢复写保护

}

void ds1302_read_time()
{
 unsigned char temp;//定义一个变量temp,用来存放年月日时分秒星期的地址

 temp= ds1302_readbyte(ds1302_write_year); //调用年地址,再读年的数据
 ds1302_time[0]=temp/16*10+temp%16;//BCD码转十进制
 temp= ds1302_readbyte(ds1302_write_month);
 ds1302_time[1]=temp/16*10+temp%16;
 temp=ds1302_readbyte(ds1302_write_date);
 ds1302_time[2]=temp/16*10+temp%16;
 temp=ds1302_readbyte(ds1302_write_hour);
 ds1302_time[3]=temp/16*10+temp%16;
 temp=ds1302_readbyte(ds1302_write_minute);
 ds1302_time[4]=temp/16*10+temp%16;
 temp=ds1302_readbyte(ds1302_write_second);
 ds1302_time[5]=temp/16*10+temp%16;
 temp=ds1302_readbyte(ds1302_write_day);
 ds1302_time[6]=temp/16*10+temp%16;
}
  

效果:

3、用DS1302做可调时钟 

 #include <regx52.h>
 #include<delay.h>
 #include<DS1302.h>
 #include<LCD1602.h>
 #include<Key.h>
 #include<timer0.h>
 unsigned char keynum,mode,timesetselect,timesetflashflag;

void timeshow()
{
    ds1302_read_time();
   LCD_ShowNum(1,1,ds1302_time[0],2); //在DS1302中数据存储是以BCD码形式存储的,0001 0000在BCD码中是9	,如果以十进制显示就是16。
   //BCD码用4位二进制数来表示1位十进制数	,从0~9,而十六进制从0~F,0001 1100 十六进制是1C,在BCD码中不合法。

   LCD_ShowNum(1,4,ds1302_time[1],2);
    LCD_ShowNum(1,7,ds1302_time[2],2);
	  LCD_ShowNum(2,1,ds1302_time[3],2);
	    LCD_ShowNum(2,4,ds1302_time[4],2);
		  LCD_ShowNum(2,7,ds1302_time[5],2);
		  

}
void timeset()
{
  if (keynum==2)
  {
  	timesetselect++; //按k2实现对年月日 时分秒的切换
	timesetselect%=6;	//用取余来实现循环复位,
  }
  if(keynum==3)//k3按下开始加
  {
   	ds1302_time[timesetselect]++;
	if(ds1302_time[0]>99){ds1302_time[0]=0;}//越上界年复位
	if(ds1302_time[1]>12){ds1302_time[1]=1;}//越上界月复位
	if(ds1302_time[1]==1||ds1302_time[1]==3||ds1302_time[1]==5||ds1302_time[1]==7||ds1302_time[1]==8||ds1302_time[1]==10||ds1302_time[1]==12)
	 {
	   if(ds1302_time[2]>31)
	   ds1302_time[2]=1; //越上界日复位	,同时区分31日和30天
	 }
	 else if (ds1302_time[1]==4||ds1302_time[1]==6||ds1302_time[1]==9||ds1302_time[1]==11)
	 {
	    if(ds1302_time[2]>30)
		 ds1302_time[2]=1;
	 }
	 else if(ds1302_time[1]==2)
	 {
	 	 if(ds1302_time[0]%4==0)  //通过平年和闰年来判断2月是28天还是29天
		 {
		   if(ds1302_time[2]>29)
		   	 ds1302_time[2]=1;
		 }
	 else
	 {
		if(ds1302_time[2]>28)
		   	 ds1302_time[2]=1;
	 }
	 }
	if(ds1302_time[3]>23)ds1302_time[3]=0;
	if(ds1302_time[4]>59)ds1302_time[4]=0;
	if(ds1302_time[5]>59)ds1302_time[5]=0;	//时分秒 越上界复位
  }
  if(keynum==4)//k4按下开始减,同理越下界复位
  {
   	ds1302_time[timesetselect]--;
	if(ds1302_time[0]<0)ds1302_time[0]=99;//注意这里是有符号函数才能写<0;因为无符号函数从0~255,<0会回到255报错
	if(ds1302_time[1]<1)ds1302_time[1]=12;
	if(ds1302_time[1]==1||ds1302_time[1]==3||ds1302_time[1]==5||ds1302_time[1]==7||ds1302_time[1]==8||ds1302_time[1]==10||ds1302_time[1]==12)
	 {
	   if(ds1302_time[2]<1)
	   ds1302_time[2]=31;
	     if(ds1302_time[2]>31)
	   ds1302_time[2]=1;
	 }
	 else if (ds1302_time[1]==4||ds1302_time[1]==6||ds1302_time[1]==9||ds1302_time[1]==11)
	 {
	    if(ds1302_time[2]<1)
		 ds1302_time[2]=30;
		if(ds1302_time[2]>30) //这一步是为了解决bug,因为当12月有31天,此时我对月份进行减,到11月份,则会变成11:31日,这是错的,因为11月没有31号
		 ds1302_time[2]=1;//所以增加一个上位越界的判断
	 }
	 else if(ds1302_time[1]==2)
	 {
	 	 if(ds1302_time[0]%4==0)
		 {
		   if(ds1302_time[2]<1)
		   	 ds1302_time[2]=29;
		    if(ds1302_time[2]>29)
		   	 ds1302_time[2]=1;
		 }
	 else
	 {
		if(ds1302_time[2]<1)
		   	 ds1302_time[2]=28;
			 	if(ds1302_time[2]>28)
		   	 ds1302_time[2]=1;
	 }
	 }
	if(ds1302_time[3]<0)ds1302_time[3]=23;
	if(ds1302_time[4]<0)ds1302_time[4]=59;
	if(ds1302_time[5]<0)ds1302_time[5]=59;

  }
           if(timesetselect==0&&timesetflashflag==1)
	 {LCD_ShowString(1,1,"  ");}
	  else{LCD_ShowNum(1,1,ds1302_time[0],2); }//在DS1302中数据存储是以BCD码形式存储的,0001 0000在BCD码中是9	,如果以十进制显示就是16。
   //BCD码用4位二进制数来表示1位十进制数	,从0~9,而十六进制从0~F,0001 1100 十六进制是1C,在BCD码中不合法。
           if(timesetselect==1&&timesetflashflag==1)
	  {LCD_ShowString(1,4,"  ");}
	  else{LCD_ShowNum(1,4,ds1302_time[1],2); }
	       if(timesetselect==2&&timesetflashflag==1)
	       {LCD_ShowString(1,7,"  ");}
	  else{LCD_ShowNum(1,7,ds1302_time[2],2); }
	       if(timesetselect==3&&timesetflashflag==1)
	       {LCD_ShowString(2,1,"  ");}
	  else{LCD_ShowNum(2,1,ds1302_time[3],2); }
	       if(timesetselect==4&&timesetflashflag==1)
	       {LCD_ShowString(2,4,"  ");}
	  else{LCD_ShowNum(2,4,ds1302_time[4],2); }
	       if(timesetselect==5&&timesetflashflag==1)
	       {LCD_ShowString(2,7,"  ");}
	  else{ LCD_ShowNum(2,7,ds1302_time[5],2); }
	
}
 void main()
 {
  LCD_Init();
  ds1302_init();
  Timer0_Init();
  LCD_ShowString(1,1,"  -  -");
   LCD_ShowString(2,1,"  :  :  ");
   ds1302_set_time(); //这个是数据原本写入的初始值,如果没有这不,LCD1602将不显示数值
 //如果读出时间为一个大于59并且不动的数则芯片有可能是处于写保护状态,在此处加上DS1302 WriteByte(0x8E,0x00)即可解除芯片写保护
 //ds1302_writebyte(0x8e,0x00);
 

  while(1)
  {

  keynum=key();
  if(keynum==1)
  {
   if (mode==0) {mode=1;timesetflashflag=0; } //实现按下按键实现来回切换功能
   else if (mode==1){mode=0; ds1302_set_time();} //当timeset()切换回yimeshow()之前,对ds1302_set_time()进行写入,这样数据才能得到保存
 
  }
  switch (mode)
 {  
   case 0: timeshow();break;
   case 1: timeset();break;
 }

  }
 }
void Timer0_Routine()        interrupt 1
{
	static unsigned  int count;	//
   	TL0 = 0x18;				
	TH0 = 0xFC;
	count++;
if(count>=500)	//定时1s的代码 ,可直接复制
{
  count=0;
  timesetflashflag=!timesetflashflag;

}



}

		

注释:按键1实现LCD1602屏幕显示和设置(年月日时分秒)模式

按键2实现年月日时分秒之间切换,按一下,对年设置,再按对月进行设置

按键3实现对年月日时分秒的加

按键4实现对年月日时分秒的减

这里了解的不够透彻,后续要回顾

蜂鸣器

1、基本知识讲解

 

 

 

 

 2、按下按键蜂鸣器响

main.c

 #include <regx52.h>
 #include<delay.h>
 #include "nixie.h"
 #include<LCD1602.h>
 #include<Key.h>
#include"buzzer.h"
unsigned char keynum;
 void main ()
 {

 nixie(1,0);
 	while(1)
   {  
    keynum =key();
	if(keynum)
	  {
	  buzzer_time(100);
	  nixie(1,keynum);  //数码管显示第二个位置,显示数值是keynum    

	  }

      
	}
		
}

buzzer.c

#include <REGX52.H>
#include<intrins.h>
sbit buzzer=P2^5;
 void buzzer_Delay500us()	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	i = 227;
	while (--i);
}
unsigned buzzer_time(unsigned int ms)
{
unsigned int i=0;
  for(i=0;i<ms*2;i++)//乘以2是因为一个for循环是0.5ms,而我们定义的是1ms
  {
    buzzer=!buzzer;	 //蜂鸣器要低电平才响,P2^5上电默认是高电平
	buzzer_Delay500us(); //每500us翻转一次,周期为1000us,频率为1000HZ
  }

}

3、蜂鸣器播放音乐--天空之城

机器周期 = 12 / 晶振频率

 #include <regx52.h>
 #include"delay.h"
 #include "timer0.h"
 sbit buzzer=P2^5; //定义蜂鸣器端口
#define P   0 //定义音符的索引号
#define L1  1
#define L1_ 2
#define L2  3
#define L2_ 4
#define L3  5
#define L4  6
#define L4_ 7
#define L5  8
#define L5_ 9
#define L6  10
#define L6_ 11
#define L7  12
#define M1	13
#define M1_	14
#define M2	15
#define M2_	16
#define M3	17
#define M4	18
#define M4_	19
#define M5	20
#define M5_	21
#define M6	22
#define M6_	23
#define M7	24
#define H1	25
#define H1_	26
#define H2	27
#define H2_	28
#define H3	29
#define H4	30
#define H4_	31
#define H5	32
#define H5_	33
#define H6	34
#define H6_	35
#define H7	36
 #define speed   500  //定义一个4分音符的时间500ms

 //c调音符频率对应定时器重装值
 unsigned int code freqtable[]= {

 0,63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283
};

 //天空之城乐谱
 unsigned char  code music[]= {
 //音符,时值
 //1
 P,4,
 P,4,
 P,4,
 M6,2,
 M7,2,

 H1,6,
 M7,2,
 H1,4,
 H3,4,

 M7,12,
 M3,2,
 M3,2,
 //2
 M6,6,
 M5,2,
 M6,4,
 H1,4,

 M5,12,
 M3,4,

 M4,6,
 M3,2,
 M4,4,
 H1,4,
 //3
 M3,8,
 P,2,
 H1,2,
 H1,2,
 H1,2,

 M7,6,
 M4_,2,
 M4,2,
 M7,4,

 M7,8,
 P,4,
 M6,2,
 M7,2,
//4
 H1,6,
 M7,2,
 H1,4,
 M3,4,
 
 M7,12,
 M3,2,
 M3,2,

 M6,6,
 M5,2,
 M6,4,
 H1,4,
//5
 M5,12,
 M2,2,
 M3,2,

 M4,4,
 H1,2,
 M7,4,
 H1,6,

 H2,2,
 H2,2,
 H3,2,
 H1,2+4+4,
 //6
 H1,2,
 M7,2,
 M6,2,
 M6,2,
 M7,4,
 M5_,4,

M6,12,
H1,2,
H2,2,

H3,6,
H2,2,
H3,4,
H5,4,
//7
H2,12,
M5,2,
M5,2,

H1,6,
M7,2,
H1,4,
H3,4,


H3,16,


//8
M6,2,
M7,2,
H1,4,
M7,4,
H2,2,
H2,2,

H1,6,
M5,2+4+4,

H4,4,
H3,4,
H2,4,
H1,4,

//9
H3,12,
H3,4,

H6,8,
H5,4,
H5,4,

H3,2,
H2,2,
H1,8,
P,2,
H1,2,

//10
H2,4,
H1,2,
H2,2,
H2,4,
H5,4,

H3,12,
H3,4,

H6,8,
H5,8,

//11
H3,2,
H2,2,
H1,8,
P,2,
H1,2,

H2,4,
H1,2,
H2,2+4,
M7,4,

M6,12,
M6,2,
M7,2,
0xff //停止位
};

 unsigned char select,musicselect;
 void main ()
 {
   Timer0_Init();
 	while(1)
   { 
	  if(music[musicselect]!=0xff)	//当播放完了,设置一个停止位
	  {
	  select=music[musicselect];
      musicselect++;
	  Delay(speed/4*music[musicselect]);
	  musicselect++;
	  TR0 = 0;//定时器关闭,这样两个相同音符才不会连音
	  Delay(5);
	  TR0 = 1;
	  }
	  else
	 { 
	 	TR0=0;
		while(1);
	 }
   }

      

		
}
void Timer0_Routine()        interrupt 1
{
 	if(freqtable[musicselect]) //如果不是休止符
   	{
	TL0 = freqtable[select]%256;//定时不同时间溢出				
	TH0 = freqtable[select]/256;
	buzzer=!buzzer;	//翻转蜂鸣器IO口

	}


}

ps:播放一开始会有杂音,乐谱一开始有3个休止符,但程序是休止符不进行中断,所以这个时候就乱了。我的单片机晶振是11.0592mhz,但程序实际是以12mhz晶振举例的。但是我把老师的代码搬过来,前面那三个休止符又没乱,所以应该还是代码有点问题。目前这部分对晶振周期、时钟周期,及音谱的频率怎么计算,或者换成D调音高对应频率是多少,以及晶振频率是11.0592mhz又改如何修改还有疑问,后续再学习。

标签:第四课,H1,主江协,ds1302,---,M7,time,include,define
From: https://blog.csdn.net/m0_51664996/article/details/142234084

相关文章

  • DataX--Web:图形化界面简化大数据任务管理
            在处理大数据任务时,频繁地修改配置文件或编写脚本可能会变得繁琐且容易出错。DataXWeb提供了一个图形化界面,旨在简化这些操作,让用户通过直观的界面管理数据同步任务。DataXWeb简介        DataXWeb是一个开源项目,它允许用户通过Web界面来配置和管......
  • YOLOv8改进 - 注意力篇 - 引入ECA注意力机制
    一、本文介绍作为入门性第一篇,这里介绍了ECA注意力在YOLOv8中的使用。包含ECA原理分析,ECA的代码、ECA的使用方法、以及添加以后的yaml文件及运行记录。二、ECA原理分析ECA官方论文地址:ECA文章ECA的pytorch版代码:ECA的pytorch代码ECA注意力机制:深度卷积神经网络的高效通......
  • 图片工具类 - C#小函数类推荐
          此文记录的是图像操作的工具类。/***图片工具类AustinLiu刘恒辉ProjectManagerandSoftwareDesignerE-Mail:[email protected]:http://lzhdim.cnblogs.comDate:2024-01-1515:18:00使用方法例子:Bitm......
  • HarmonyOs DevEco Studio小技巧17 -- 如何设置主题
    系统自带的主题不喜欢,然后自己又不想一个个去设置怎么办?机缘巧合之下,我发现了IntelliJ的IDE的主题也能在DevEcoStudio上用 这里是下载主题的网址ThemesforIntelliJ-basedIDEs|JetBrainsMarketplace这里下载分两种情况 虽然不兼容但是可以用  下载完......
  • HarmonyOs DevEco Studio小技巧19 --函数表达式与箭头函数
    在JavaScript中,函数表达式和箭头函数是定义函数的两种常见方式。函数表达式:函数表达式是将一个函数赋值给一个变量的方式函数表达式的一般形式是:letfunctionName=function([parameters]){//函数体[returnstatement]};简单的函数表达式的示例letadd=fun......
  • HarmonyOs DevEco Studio小技巧18--JavaScript 变量声明与作用域
    在JavaScript中,变量声明和作用域是非常重要的概念。变量声明:var:使用 var 声明的变量,其作用域在函数内,如果在函数外声明,则为全局变量。存在变量提升现象,即在变量声明之前使用该变量不会报错,但值为 undefined。functionexample(){console.log(a);//undefine......
  • Netty笔记09-网络协议设计与解析
    文章目录前言一、协议设计1.数据格式2.消息长度3.编码方式4.错误处理5.安全性二、协议解析1.消息分隔2.粘包与半包处理3.校验机制三、为什么需要协议?四、redis协议五、HTTP协议六、自定义协议要素编解码器......
  • Shiro-721—漏洞分析(CVE-2019-12422)
    目录PaddingOracleAttack原理PKCS5填充怎么爆破攻击漏洞原理源码分析漏洞复现本文基于shiro550漏洞基础上分析,建议先看上期内容:https://blog.csdn.net/weixin_60521036/article/details/142373353PaddingOracleAttack原理网上看了很多,感觉写的对我们这种对密码学不敏......
  • FastGPT一站式解决方案[2-应用篇]:轻松实现RAG-智能问答系统,AI工作流、核心模块讲解
    FastGPT一站式解决方案[2-应用篇]:轻松实现RAG-智能问答系统,AI工作流、核心模块讲解1.FastGPT快速使用:基本设置、核心模块讲解1.1知识库设置首先我们需要创建一个知识库。知识库创建完之后我们需要上传一点内容。上传内容这里有四种模式:手动输入:手动输入问答对,是最......
  • 数学建模 第二讲 - 初等建模
    绪论主要内容:介绍以下几个初等模型,椅子问题、席位分配问题、行走步长问题、实物交换模型。主要目的:体会数学建模的形式多样性与方法多样性,了解建模思想,着重理解由现实问题向数学问题的转化过程。一、椅子问题问题四条腿长度相等的方椅子放在不平的地面上,四条腿能否同......