当前位置: 首页 > news >正文

单片机实验(三)

前言

实验一:利用定时器T1的中断控制P1.7引脚输出音频信号,启动蜂鸣器发出一段熟悉的与众不同的具有10个音节的音乐音频。

实验二:使用定时器/计数器来实现一个LCD显示年、月、日、星期 、时、分、秒的电子表,要求时和分可以方便设置。液晶显示器采用LCD 1602.

参考链接

LED数码管的静态显示与动态显示(Keil+Proteus)-CSDN博客

定时器/计数器的应用-CSDN博客

基于51单片机的7键电子琴音乐播放器proteus_c51电子琴程序_BT-BOX的博客-CSDN博客​​​​​​

笔记:C51单片机——音乐播放,模拟钢琴键。_c51音乐_c-tion的博客-CSDN博客

51单片机学习--蜂鸣器播放音乐_51单片机蜂鸣器音乐代码-CSDN博客

闰年(历法中的名词)_百度百科 (baidu.com)

蜂鸣器播放音乐_哔哩哔哩_bilibili

【51单片机】蜂鸣器播放音乐 - 知乎 (zhihu.com)【51单片机】蜂鸣器播放音乐 - 知乎 (zhihu.com)

独立键盘接口设计(Keil+Proteus)-CSDN博客

如何看懂音乐简谱_简谱怎么看-CSDN博客

【蓝桥杯——单片机学习笔记】十六.蜂鸣器播放音乐(STC15F2K60S2)_有源蜂鸣器可以播放音乐吗-CSDN博客

简谱_百度百科 (baidu.com)

[11-2] 蜂鸣器播放提示音&音乐_哔哩哔哩_bilibili

实验一

Keil

这个找了半天,不是付费的就是代码不全的,然后是从视频上面找到一个简单的敲了一下,还是没怎么弄懂。

#include<reg51.h>
#define uchar unsigned char
#define uint unsigned intsbit speaker=P1^7;//生日快乐歌的音符频率表,不同频率由不同的延时来决定
uchar code SONG_TONE[]={212,212,190,212,159,169,212,212,190,212,142,159,212,212,106,126,159,169,190,119,119,126,159,142,159,0};//生日快乐歌节奏节拍表,节拍决定每个音符的演奏长短
uchar code SONG_LONG[]={9,3,12,12,12,24,9,3,12,12,24,9,3,12,12,12,12,12,9,3,12,12,12,24,0};//延时函数
void delay(uint x){uchar t;while(x--)for(t=0;t<120;t++);
}
//播放音乐
void PlayMusic(){uint i=0,j,k;for(i=0;i<26;i++){for(j=0;j<SONG_LONG[i]*20;j++){speaker=~speaker;for(k=0;k<SONG_TONE[i]/3;k++);}delay(100);}
}void main(){while(1){PlayMusic();}
}

Proteus

原理图到没什么需要更改的,主要是不同频率的声音应该怎么发出来。

拓展 

其实主要还是代码部分,原理图不需要修改,需要先找一份简谱,然后需要知道怎么看音乐乐谱。

实验需要把握的就是“音的高低”“音的长短”。

在目前的代码中不考虑倍低音和低音,自己低音、中音、高音对应的代码。

音的高低部分需要知道,在基本音符上方加一个“.”,表示该音升高一个八度,称为高音;在基本音符下方加一个“.”,表示该音降低一个八度,称为低音。这里只需要知道区分低中高音即可。

知道了音的高低接下来就是音的长短部分,其实就是表示那个音延迟的时间,这里就只需要记住在基本音符下方加记一条短横线,表示缩短原音符时值的一半。

这两个问题都解决了就可以直接编写程序了,其实也不需要很复杂的乐理知识,甚至基本音符是什么都可以不用知道,就是看着简谱修改成自己对应的即可,对于特殊的符号还需要自己查资料看表示什么。

基本音符 

1234567
DoReMiFaSolLaSi

 《平凡之路》的部分简谱

 

 缺点对应倍低音的地方无法编写程序,因为这只有低音、中音、高音频率的代码,然后音也有点不准吧,但是调子感觉也差不多,可以听出来大致的调子。

#include<reg51.h>
#define uchar unsigned char#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 ClockSpeed 12000000 //时钟频率,Hz
#define SongSpeed 330       //ms,八分音符
sbit beepIO = P1^7;         //定义蜂鸣器IO口unsigned char freq_select;//音阶频率表
unsigned int code freq_table[]={0,61714 ,61928 ,62131 ,62322 ,62502 ,62673 ,62833 ,62985 ,63128 ,63263 ,63391 ,63511, //低音63628 ,63731 ,63835 ,63928 ,64021 ,64103 ,64185 ,64260 ,64331 ,64400 ,64463 ,64524, //中音64580 ,64633 ,64684 ,64732 ,64777 ,64820 ,64860 ,64898 ,64934 ,64968 ,65000 ,65030 //高音};				//平凡之路
uchar code song[]={M1,2, M3,1, M1,1, L7,1, M1,1, M2,1, L5,1, 0,1, M3,1, M3,1, M3,1, M6,1, M6,1, M6,1, M1,1, M2,1, M3,1, M3,1, 0,2, 0,2, 0,2,0,1, M3,1, M3,1, M6,1, M6,1, M6,3, M5,1, M5,2, M4,1, M3,3, 0,2, 0,2, 0,1, M3,1, M3,1, M6,1, M6,1, M1,1, M2,1, M3,1,M3,2, 0,2, 0,2, 0,2, 0,1, M3,1, M3,1, M1,1, M4,1, M4,1, M4,1, M4,1, M3,1, M1,2, 0,2, 0,2,  40};void timer0_initial()
{beepIO = 0;TH0   = 0xFD;	TL0   = 0x09;TMOD  = 0x01;  //选择定时器0,工作方式1ET0   = 1;     //允许定时器0中断EA    = 1;     //CPU开放中断TF0   = 0;     //溢出标志位清0TR0   = 1;     //开启定时器0
}void BeepTimer0() interrupt 1	//中断函数
{beepIO = !beepIO;   //蜂鸣器IO口高低电平转换TH0 = freq_table[freq_select]/256 ;TL0 = freq_table[freq_select]%256 ;
}void delay_ms(unsigned int x) //延时函数
{unsigned char t;while(x--) for(t=0;t<120;t++);
}void main()
{unsigned char select;timer0_initial();while(song[select]!= 40)        //判断歌曲是否结束{freq_select=song[select];		if(freq_select)         //判断是否是休止符0{select++;delay_ms(song[select]*SongSpeed);TR0 = 0;   //关闭蜂鸣器一段时间再打开,模拟按键抬手动作。delay_ms(10);TR0 = 1;select++;}else{			TR0 = 0;select++;delay_ms(song[select]*SongSpeed);TR0 = 1;select++;}}
}

然后就是此次的作业《倔强》

 这个写我还是比较满意,就是中间会断掉然后重新开始,推测是因为储存不了这么多的数据,不能完整播放整首歌,但是也还不错,还是比较有成就感的。

#include<reg51.h>
#define uchar unsigned char#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 ClockSpeed 12000000 //时钟频率,Hz
#define SongSpeed 330       //ms,八分音符
sbit beepIO = P1^7;         //定义蜂鸣器IO口uchar freq_select;//音阶频率表
unsigned int code freq_table[]={0,61714 ,61928 ,62131 ,62322 ,62502 ,62673 ,62833 ,62985 ,63128 ,63263 ,63391 ,63511, //低音63628 ,63731 ,63835 ,63928 ,64021 ,64103 ,64185 ,64260 ,64331 ,64400 ,64463 ,64524, //中音64580 ,64633 ,64684 ,64732 ,64777 ,64820 ,64860 ,64898 ,64934 ,64968 ,65000 ,65030 //高音};				//倔强
uchar code song[]={M1,3, M1,1, M1,1, L7,1, M1,1, M2,1, M2,1, L7,1,M1,3, M1,1, M1,1, L7,1, M1,1, M2,1, M2,1, M2,1, M3,1, M1,1,M1,3, M1,1, M1,1, M1,1, L5,1, M1,1, M1,1, M2,1, M4,1, M3,1, M2,1, M1,1, M3,1, M2,1, M2,2,M1,3, M1,1, M1,1, L7,1, M1,1, M2,1, M2,1, L7,1,M1,3, M1,1, M1,1, L7,1, M1,1, M2,1, M2,1, M2,1, M3,1, M1,1,M1,3, M1,1, M1,1, M1,1, L5,1, M1,1, M1,1, M2,1, M4,1, M3,1, M2,1, M1,1, M3,1, M2,1, M2,1, 0,1,M1,1, L7,1, M1,1, M1,1, L7,1, M1,1, M1,1, 0,2, M1,1, L7,1, M1,1, M1,1, M2,1, M3,1, M3,2, M1,1, L7,1, M1,1, M1,1, M2,1, M4,1, M4,1, M3,1, M2,1, M1,1, M1,1, L7,1, M1,1, M3,1, M6,1, M5,1, M5,1,M3,1, M2_,1, M3,1, M3,1, M4,1, M5,1, M5,1, M4,1, M3,1, M3,1, M2,1, M1,1, L7,1, M1,1, M1,1, M2,1, M3,1, M3,1, M2,1, M1,1, L7,1,M1,1, L7,1, M1,1, M1,1, M6,1, M7,1, M7,1, M6,1, M5,1, M5,1, M3,1, M5,1, M6,1, M1,1, M1,1, M1,3, M6,1, M7,1, M3,1, M5,1, M5,1, 0,1,M3,1, M3,1, M3,1, M3,1, M4,1, M5,1, M5,1, M4,1, M3,1, M3,1, M3,1, M2,1, M1,1, M1,1, M1,1, M1,1, M2,1, M3,1, M2,1, M1,1, L7,1, M1,1, L6,1, M1,1, M1,1, M6,1, M7,1, M6,1, M5,1, M3,1, M6,1, M6,1, M1,1, M1,1, M1,3, M6,1, M7,1, M6,1, M5,1, M5,1, L6,1, M1,1, M1,2, 40
};void timer0_initial()
{beepIO = 0;TH0   = 0xFD;	TL0   = 0x09;TMOD  = 0x01;  //选择定时器0,工作方式1ET0   = 1;     //允许定时器0中断EA    = 1;     //CPU开放中断TF0   = 0;     //溢出标志位清0TR0   = 1;     //开启定时器0
}void BeepTimer0() interrupt 1	//中断函数
{beepIO = !beepIO;   //蜂鸣器IO口高低电平转换TH0 = freq_table[freq_select]/256 ;TL0 = freq_table[freq_select]%256 ;
}void delay_ms(unsigned int x) //延时函数
{uchar t;while(x--) for(t=0;t<120;t++);
}void main()
{uchar select;timer0_initial();while(song[select]!= 40)        //判断歌曲是否结束{freq_select=song[select];		if(freq_select)         //判断是否是休止符0{select++;delay_ms(song[select]*SongSpeed);TR0 = 0;   //关闭蜂鸣器一段时间再打开,模拟按键抬手动作。delay_ms(10);TR0 = 1;select++;}else{			TR0 = 0;select++;delay_ms(song[select]*SongSpeed);TR0 = 1;select++;}}
}

实验二

Keil

代码还是和之前的差不多,然后就是需要增加年月日星期的处理,星期常见的就是用三个字母来表示,年月日都是用数字,有了前面的基础就很容易确定LCD 1602的显示以及时钟的进位,这里需要考虑的是根据不同的年份以及月份,他的天数是不一致的,所以这个需要考虑进去,经过在程序中这里也没有体现到,可以通过设置时间的节点来去验证他是否满足要求。

0123456789ABCDEF
80yyyy.MM.ddww
C0TIMEhh:mm:ss

我这里犯了一个很严重的错误就是对于年份在用LCD输出的时候用到取余和整除,年份的类型是字符型,这就导致输出的数不对,后面还是通过控制变量法来缩小他的范围最后是想到uchar最大是到255,这个年份都是4位数的,所以会导致越界计算出来的数值就不正确,后面是改成了整型。 

#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;//202140200126
uint int_time;//定义中断次数变量(最多到200)
uchar code date[]=" 2022.12.31 SAT ";//LCD 第1行显示的内容
uchar code time[]=" TIME  23:59:55 ";//第2行显示的内容
uchar second=55,minute=59,hour=23;//秒,分钟,小时
uint year=2022;//年
uchar month=12,day=31,week=6;//月,日,星期
void delay(uint i);//延时函数
void write_com(uchar com);//写入命令数据到LCD
void write_data(uchar date);//写入字符显示数据到LCD
void init1602();//LCD1602初始化设定
void write_sfm(uchar add,uchar date);//向指定地址写入数据
void write_sfm_year(uchar add,uint year);//写年
void write_sfm_week(uchar add,uchar week);//写星期
void clock_init();//对时钟进行初始化
void clock_write(uint s,uint m,uint h,uint d,uint w,uint M,uint y);//写数据
int isleap(uint year);//判断是否是闰年void main(){int_time=0;//中断次数、秒、分钟、小时,年,月,日,星期变量进行清零second=55;minute=59;hour=23;year=2022;month=12;day=31;week=6;init1602();//lcd的初始化clock_init();//时钟的初始化TMOD=0x01;//设置定时器T0为方式1定时EA=1;//总中断开ET0=1;//允许T0中断TH0=0xEE;//对T0进行初始化TH1=0x00;TR0=1;while(1){clock_write(second,minute,hour,day,week,month,year);}
}void delay(uint j){//延时函数uchar i=110;for(;j>0;j--){while(i--);i=110;}
}void write_com(uchar com){//写入命令数据到LCDRW=0;RS=0;P0=com;delay(5);E=1;delay(5);E=0;
}void write_data(uchar date){//写入字符显示数据到LCDRW=0;RS=1;P0=date;delay(5);E=1;delay(5);E=0;
}void init1602(){//LCD1602初始化设定RW=0;E=0;write_com(0x3C);write_com(0x0C);//开整体显示,光标关,无闪烁write_com(0x06);//光标右移,写入一个字符后地址指针加1write_com(0x01);//清屏write_com(0x80);//字符输入地址,字符的第一位
}void write_sfm(uchar add,uchar date){//向指定地址写入数据uchar shi,ge;shi=date/10;//十位ge=date%10;//个位write_com(add);write_data(0x30+shi);//0x30表示48,让数字变成字符write_data(0x30+ge);
}//这里头脑不清醒了一下,uchar是一个字节,最大是127,而年份是四位数的,所以需要用uint类型才可以uint是两个字节有(65535)
void write_sfm_year(uchar add,uint date){//向指定地址写入数据(年份是四位的,所以单独处理)uchar ge,shi,bai,qian;qian=date/1000;//千位bai=date/100%10;//百位shi=date/10%10;//十位ge=date%10;//个位write_com(add);write_data(0x30+qian);//0x30表示48,让数字变成字符write_data(0x30+bai);//0x30表示48,让数字变成字符write_data(0x30+shi);//0x30表示48,让数字变成字符write_data(0x30+ge);//0x30表示48,让数字变成字符
}void write_sfm_week(uchar add,uchar date){//向指定地址写入数据(星期是用三个字母来表示)write_com(add);switch(date){case 0:write_data('S');write_data('U');write_data('N');break;//周日case 1:write_data('M');write_data('O');write_data('N');break;//周一case 2:write_data('T');write_data('U');write_data('E');break;//周二case 3:write_data('W');write_data('E');write_data('D');break;//周三case 4:write_data('T');write_data('H');write_data('U');break;//周四case 5:write_data('F');write_data('R');write_data('I');break;//周五case 6:write_data('S');write_data('A');write_data('T');break;//周六}
}void clock_init(){//对时钟进行初始化uchar i,j;//写第一行write_com(0x80);for(i=0;i<16;i++){write_data(date[i]);}//写第二行write_com(0xC0);for(j=0;j<16;j++){write_data(time[j]);}
}void clock_write(uint s,uint m,uint h,uint d,uint w,uint M,uint y){//向指定位置写数据//写年write_sfm_year(0x81,y);//写月write_sfm(0x86,M);//写星期write_sfm_week(0x8C,w);//写日write_sfm(0x89,d);//写小时write_sfm(0xC7,h);//写分钟write_sfm(0xCa,m);//写秒write_sfm(0xCd,s);
}//判断是否是闰年
int isleap(uint year){//能被400整除或者能被4整除且不能被100整除为闰年if((year%400==0)||(year%4==0 && year %100!=0)){return 1;}else{return 0;}
}//中断服务函数
void timer0(void) interrupt 1{uchar leap=isleap(year);//判断今年是否为闰年TR0=0;//停止计时,避免给计时造成误差(需要有这个,不然就一直在中断,导致时间不准确)TH0=0xEE;//对T0重新赋初值TH1=0x00;int_time++;//记录中断次数if(int_time==200){//中断次数满200次5ms*200=1sint_time=0;//中断次数变量清零second++;//加1秒}if(second==60){//60秒为一分钟second=0;minute++;}if(minute==60){//60分钟为一小时minute=0;hour++;}if(hour==24){//一天是24小时hour=0;week++;day++;}if(week==7){//0表示周日,week=7表示新的一周开始了week=0;}if(month==2){//是闰年(2月有29天)if(leap==1){if(day>29){day=0;month++;}}else{//不是闰年(2月有28天)if(day>28){day=0;month++;}}}else if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12){if(day>31){day=0;month++;}}else if(month==4 || month==6 || month==9 || month==11){if(day>30){day=0;month++;}}if(month==13){month=0;year++;}TR0=1;
}

Proteus

原理图和之前是保持一致的。

 在线过年,我们一起包饺子!!!

 

拓展

 

咱就说这个应不应该希望。这里大致的思路就是添加几个按钮,来控制增加或者减少年月日时分, 秒就不需要添加了,时钟一直在动这个调也不好调。

这里我一直在想这个调分钟这个进位怎么调,主要开始对于每月的天数,我突然想到我以前的电子表就是每个位置是独立的,调分钟不会影响其他的,就是分钟从0-59变化,抱着这样的想法,时钟就可以很快确定了,不就是求余吗,大家都会。

为了简单我又做了一个假设,当设置的时候每个月都是31天,而且分开设置的话,天数和星期就不是保持同步的了。

目前就是在之前的基础上增加了一个独立键盘的扫描,以及对应的求余功能,对于设置直接导致进位和借位的功能还没实现,目前先将简单实现的完成。扫描的时候尽量不和RS,RW,E用一个口,会导致不好查询,程序我不知道怎么编写,如果影响了那三根引脚的输出会导致LCD一直在查询发出警告。

这个我昨天想到了一个东西,我感觉突然就悟了一样,就是对于时间小于0前面要退一位吗,因为都是无符号数所以不好来设置,然后我就想到了一个方法。假设是要判断分钟minute,可以先给分钟加上一个60,然后整除60,如果他的结果是0,就表示之前的值是小于60的,如果他的值是等于1,就表示他的值在[0,60)之间,如果他的值等于2,就表示他大于等于60了,因为数值是实时更新的,所以这里就排除了数是一个很大的正数,或者是一个很小的负数的情况,主要是考虑-1,和60这两种情况怎么让小时的位置上面的数发生相应的变化。

#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;//分别对应12个按钮
sbit S1=P1^0;
sbit S2=P1^1;
sbit S3=P1^2;
sbit S4=P1^3;
sbit S5=P1^4;
sbit S6=P1^5;
sbit S7=P1^6;
sbit S8=P1^7;
sbit S9=P3^04;
sbit S10=P3^5;
sbit S11=P3^6;
sbit S12=P3^7;uint int_time;//定义中断次数变量(最多到200)
uchar code date[]=" 2022.12.31 SAT ";//LCD 第1行显示的内容
uchar code time[]=" TIME  23:59:55 ";//第2行显示的内容
uint second=55,minute=59,hour=23;//秒,分钟,小时
uint year=2022;//年
uchar month=12,day=31,week=6;//月,日,星期
uchar keyval;//定义键盘储存变量单元void delay(uint i);//延时函数
void write_com(uint com);//写入命令数据到LCD
void write_data(uint date);//写入字符显示数据到LCD
void init1602();//LCD1602初始化设定
void write_sfm(uint add,uint date);//向指定地址写入数据
void write_sfm_year(uint add,uint year);//写年
void write_sfm_week(uint add,uint week);//写星期
void clock_init();//对时钟进行初始化
void clock_write(uint s,uint m,uint h,uint d,uint w,uint M,uint y);//写数据
int isleap(uint year);//判断是否是闰年
void key_scan(void);//扫描键盘void main(){int_time=0;//中断次数、秒、分钟、小时,年,月,日,星期变量进行清零second=55;minute=59;hour=23;year=2022;month=12;day=31;week=6;keyval=0;//键值初始化为0init1602();//lcd的初始化clock_init();//时钟的初始化TMOD=0x01;//设置定时器T0为方式1定时EA=1;//总中断开ET0=1;//允许T0中断TH0=0xEE;//对T0进行初始化TH1=0x00;TR0=1;while(1){key_scan();switch(keyval){case 1://每年year++;year=(year+10000)%10000;break;case 2:year--;year=(year+10000)%10000;break;case 3://每月month++;month=(month+11)%12+1;break;//要让0表示12则需要先减1求余之后加1case 4:month--;month=(month+11)%12+1;break;//要让0表示12则需要先减1求余之后加1case 5://每周week++;week=(week+7)%7;break;case 6:week--;week=(week+7)%7;break;case 7://天数day++;day=(day+30)%31+1;break;case 8:day--;day=(day+30)%31+1;break;case 9://小时hour++;hour=(hour+60)%60;break;case 10:hour--;hour=(hour+60)%60;break;case 11://分钟minute++;minute=(minute+60)%60;break;case 12:minute--;minute=(minute+60)%60;break;}clock_write(second,minute,hour,day,week,month,year);keyval=0;}
}//延时函数
void delay(uint j){uchar i=110;for(;j>0;j--){while(i--);i=110;}
}//写入命令数据到LCD
void write_com(uint com){RW=0;RS=0;P0=com;delay(5);E=1;delay(5);E=0;
}//写入字符显示数据到LCD
void write_data(uint date){RW=0;RS=1;P0=date;delay(5);E=1;delay(5);E=0;
}//LCD1602初始化设定
void init1602(){RW=0;E=0;write_com(0x3C);write_com(0x0C);//开整体显示,光标关,无闪烁write_com(0x06);//光标右移,写入一个字符后地址指针加1write_com(0x01);//清屏write_com(0x80);//字符输入地址,字符的第一位
}//向指定地址写入数据
void write_sfm(uint add,uint date){uchar shi,ge;shi=date/10;//十位ge=date%10;//个位write_com(add);write_data(0x30+shi);//0x30表示48,让数字变成字符write_data(0x30+ge);
}//这里头脑不清醒了一下,uchar是一个字节,最大是127,而年份是四位数的,所以需要用uint类型才可以uint是两个字节有(65535)
void write_sfm_year(uint add,uint date){//向指定地址写入数据(年份是四位的,所以单独处理)uchar ge,shi,bai,qian;qian=date/1000;//千位bai=date/100%10;//百位shi=date/10%10;//十位ge=date%10;//个位write_com(add);write_data(0x30+qian);//0x30表示48,让数字变成字符write_data(0x30+bai);//0x30表示48,让数字变成字符write_data(0x30+shi);//0x30表示48,让数字变成字符write_data(0x30+ge);//0x30表示48,让数字变成字符
}void write_sfm_week(uint add,uint date){//向指定地址写入数据(星期是用三个字母来表示)write_com(add);switch(date){case 0:write_data('S');write_data('U');write_data('N');break;//周日case 1:write_data('M');write_data('O');write_data('N');break;//周一case 2:write_data('T');write_data('U');write_data('E');break;//周二case 3:write_data('W');write_data('E');write_data('D');break;//周三case 4:write_data('T');write_data('H');write_data('U');break;//周四case 5:write_data('F');write_data('R');write_data('I');break;//周五case 6:write_data('S');write_data('A');write_data('T');break;//周六}
}//对时钟进行初始化
void clock_init(){uchar i,j;//写第一行write_com(0x80);for(i=0;i<16;i++){write_data(date[i]);}//写第二行write_com(0xC0);for(j=0;j<16;j++){write_data(time[j]);}
}//向指定位置写数据
void clock_write(uint s,uint m,uint h,uint d,uint w,uint M,uint y){//写年write_sfm_year(0x81,y);//写月write_sfm(0x86,M);//写星期write_sfm_week(0x8C,w);//写日write_sfm(0x89,d);//写小时write_sfm(0xC7,h);//写分钟write_sfm(0xCa,m);//写秒write_sfm(0xCd,s);
}//判断是否是闰年
int isleap(uint year){//能被400整除或者能被4整除且不能被100整除为闰年if((year%400==0)||(year%4==0 && year %100!=0)){return 1;}else{return 0;}
}//键盘扫描
void key_scan(void){P1=0xFF;P3=0xFF;//不能给P2赋值,不然会导致LCD一直警告if((P1&0xFF)!=0xFF||(P3&0xF0)!=0xF0){delay(10);//适当延迟,怕到时候误判if(S1==0)keyval=1;if(S2==0)keyval=2;if(S3==0)keyval=3;if(S4==0)keyval=4;if(S5==0)keyval=5;if(S6==0)keyval=6;if(S7==0)keyval=7;if(S8==0)keyval=8;if(S9==0)keyval=9;if(S10==0)keyval=10;if(S11==0)keyval=11;if(S12==0)keyval=12;}
}//中断服务函数
void timer0(void) interrupt 1{uchar leap=isleap(year);//判断今年是否为闰年TR0=0;//停止计时,避免给计时造成误差(需要有这个,不然就一直在中断,导致时间不准确)TH0=0xEE;//对T0重新赋初值TH1=0x00;int_time++;//记录中断次数if(int_time==200){//中断次数满200次5ms*200=1sint_time=0;//中断次数变量清零second++;//加1秒}if(second==60){//60秒为一分钟second=0;minute++;}if(minute==60){//60分钟为一小时minute=0;hour++;}if(hour==24){//一天是24小时hour=0;week++;day++;}if(week==7){//0表示周日,week=7表示新的一周开始了week=0;}if(month==2){//是闰年(2月有29天)if(leap==1){if(day>29){day=0;month++;}}else{//不是闰年(2月有28天)if(day>28){day=0;month++;}}}else if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12){if(day>31){day=0;month++;}}else if(month==4 || month==6 || month==9 || month==11){if(day>30){day=0;month++;}}if(month==13){month=0;year++;}TR0=1;
}

为了更好的表示按钮的含义这里用到了写文字的模块,如果直接用中文命名器件容易导致程序崩溃。

综合 

怎么好看怎么来吧,就是有点小卡。

总结

认真想下来其实还是很有意思的,但是也是很耗时间,有时间再来尝试一些新的功能。

相关文章:

单片机实验(三)

前言 实验一&#xff1a;利用定时器T1的中断控制P1.7引脚输出音频信号&#xff0c;启动蜂鸣器发出一段熟悉的与众不同的具有10个音节的音乐音频。 实验二&#xff1a;使用定时器/计数器来实现一个LCD显示年、月、日、星期 、时、分、秒的电子表&#xff0c;要求时和分可以方便…...

Python 2进制按位取反

根据一checksum算法需要将一些参数按位取反 例&#xff1a;参数 13 数字13二进制为1101 [((x)) for x in str(bin(13))] [0, b, 1, 1, 0, 1] 除去0b字符串然后按位取反得到0010 [(1^int(x)) for x in str(bin(13)).replace(0b,)] [0, 0, 1, 0]然后将得到的2进制转换成十进制…...

【用Python根据用户名和手机号码生成Hash值并创建.cs .h和xlsx文件】

用Python根据用户名和手机号码生成Hash值并创建C Sharp .cs、嵌入式.h和xlsx表格文件 用Python根据用户名和手机号码生成Hash值并创建C Sharp .cs、嵌入式.h和xlsx表格文件&#xff0c;主要是为每个用户创建一个pubkey&#xff0c;并输出C Sharp C#和嵌入式 Keil的工程文件 pub…...

<Linux>(极简关键、省时省力)《Linux操作系统原理分析之存储管理(2)》(15)

[TOC](《Linux操作系统原理分析之存储管理&#xff08;2&#xff09;》&#xff08;15&#xff09; 5 存储管理5.4 分页存储管理5.4.1 纯分页存储管理a.页&#xff08;页面&#xff09;和物理块&#xff08;帧&#xff09;b. 页面大小c. 逻辑地址结构 5.5 存储扩充技术5.5.2 交…...

jdk8、jdk9中,接口的新特性

接口的老特性&#xff1a; 没有构造方法成员变量只能定义常量&#xff0c;默认三个关键字public static final只能是抽象方法&#xff0c;默认两个关键字public abstract 接口的新特性&#xff1a; jdk8 1.接口允许定义非抽象方法&#xff0c;需加入default关键字。为了解决…...

第一题-字符串拼接【第六届传智杯程序设计挑战赛解题分析详解复盘】(C/C++实现)

🚀 欢迎来到 ACM 算法题库专栏 🚀 在ACM算法题库专栏,热情推崇算法之美,精心整理了各类比赛题目的详细解法,包括但不限于ICPC、CCPC、蓝桥杯、LeetCode周赛、传智杯等等。无论您是刚刚踏入算法领域,还是经验丰富的竞赛选手,这里都是提升技能和知识的理想之地。 ✨ 经典…...

简谈oracle数据库的归档模式

一、oracle数据库归档模式简介 Oracle数据库归档模式是一种数据备份和恢复策略,它允许数据库记录所有数据库的更改操作(包括已提交和未提交的事务)并将其存储在归档日志中。这些归档日志可以用于在数据库发生故障时进行恢复,并提供点时间恢复(PITR)的能力。 在Oracle数…...

FLASK博客系列9——你想成为我的新用户吗?

距离上次发文好久好久了。 先说声抱歉&#xff0c;拖更的毛病我会改掉的。 上次我们教大家如何用后台去管理用户和新增文章&#xff0c;但始终都是单机操作&#xff0c;怎么让你的朋友也来加入你的小站呢&#xff1f;今天我们来为我们的网站增添一个新功能&#xff0c;实现用户…...

用通俗的方法讲解:大模型微调训练详细说明(附理论+实践代码)

本文内容如下 介绍了大模型训练的微调方法&#xff0c;包括prompt tuning、prefix tuning、LoRA、p-tuning和AdaLoRA等。 介绍了使用deepspeed和LoRA进行大模型训练的相关代码。 给出了petals的介绍&#xff0c;它可以将模型划分为多个块&#xff0c;每个用户的机器负责其中一…...

现代化工安全保障迎来巡查无人机新时代

当今现代化工企业呈现出规模不断扩大&#xff0c;设备逐渐趋向大型化的局面&#xff0c;由此导致化工安全生产面临日益严峻的挑战。然而&#xff0c;随着巡查无人机技术的成熟&#xff0c;这种新的高效手段正在提高化工安全检测的工作效率。 一、传统化工安全巡检存在弊端 化工…...

关于web前端通过js获取后端mysql数据库数据的一个方法

关于web前端通过js获取后端mysql数据库数据的一个方法 问题引入 关于html的教程很多&#xff0c;关于mysql的教程也很多&#xff0c;那么怎么让html展示mysql的数据呢&#xff1f; 一言以蔽之 前端通过js向后端发起一个http请求&#xff0c;后端响应这个请求并返回数据 实…...

如何下载IEEE出版社的Journal/Conference/Magazine的LaTeX/Word模板

当你准备撰写一篇学术论文或会议论文时&#xff0c;使用IEEE&#xff08;电气和电子工程师协会&#xff09;的LaTeX或Word模板是一种非常有效的方式&#xff0c;它可以帮助你确保你的文稿符合IEEE出版的要求。无论你是一名研究生生或一名资深学者&#xff0c;本教程将向你介绍如…...

京东数据运营-京东数据开放平台-鲸参谋10月粮油调味市场品牌店铺销售数据分析

鲸参谋监测的京东平台10月份料油调味市场销售数据已出炉&#xff01; 根据鲸参谋数据显示&#xff0c;今年10月份&#xff0c;京东平台粮油调味市场的销量将近4600万&#xff0c;环比增长约10%&#xff0c;同比降低约20%&#xff1b;销售额将近19亿&#xff0c;环比增长约4%&am…...

ThermalLabel SDK for .NET 13.0.23.1113 Crack

ThermalLabel SDK for .NET 是一个 .NET 典型类库&#xff0c;它允许用户和开发人员创建非常创新的条码标签并将其发布在 zebra ZPL、EPL、EPSON ESC、POS 以及 Honeywell intermec 指纹中通过在 VB.NET 或 C# 上编写 .NET 纯代码来实现热敏打印机&#xff0c;以实现项目框架的…...

[Java学习日记]网络编程

目录 一.常见的软件架构、网络编程三要素、IP 二.利用UDP发送与接收数据 三.改聊天室 四.组播案例 五.TCP通信案例 一.常见的软件架构、网络编程三要素、IP 网络编程&#xff1a;在网络通信协议下&#xff0c;不同的计算机上运行的程序进行的数据传输 在Java中可以使用java…...

spring boot mybatis TypeHandler 看源码如何初始化及调用

目录 概述使用TypeHandler使用方式在 select | update | insert 中加入 配置文件中指定 源码分析配置文件指定Mapper 执行query如何转换 结束 概述 阅读此文 可以达到 spring boot mybatis TypeHandler 源码如何初始化及如何调用的。 spring boot 版本为 2.7.17&#xff0c;my…...

数据结构基础(带头节点的双向循环链表)

完整代码 DLinkList.hDLinkList.ctest.c DLinkList.h #pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h>typedef int ElemType;// SList - 单链表 // DList - 双链表 // 带头节点的双向循环链表 - 最优链表结构&#xff0c;任意位置…...

STM32CubeMx+MATLAB Simulink点灯程序

STM32CubeMxMATLAB点灯程序 ✨要想实现在MATLAB Simulink环境下使用STM32&#xff0c;前提是已经搭建好MATLAB环境并且安装了必要的Simulink插件&#xff0c;以及对应的STM32支持包。 &#x1f33f;需要准备一块所安装支持包支持的STM32开发板. &#x1f516;具体支持包详情页…...

【深度学习】gan网络原理生成对抗网络

【深度学习】gan网络原理生成对抗网络 GAN的基本思想源自博弈论你的二人零和博弈&#xff0c;由一个生成器和一个判别器构成&#xff0c;通过对抗学习的方式训练&#xff0c;目的是估测数据样本的潜在分布并生成新的数据样本。 1.下载数据并对数据进行规范 transform tran…...

springboot参数汇总

multipart multipart.enabled 开启上传支持&#xff08;默认&#xff1a;true&#xff09; multipart.file-size-threshold: 大于该值的文件会被写到磁盘上 multipart.location 上传文件存放位置 multipart.max-file-size最大文件大小 multipart.max-request-size 最大请求…...

【算法刷题】Day9

文章目录 611. 有效三角形的个数题干&#xff1a;题解&#xff1a;代码&#xff1a; LCR 179. 查找总价格为目标值的两个商品题干&#xff1a;题解&#xff1a;代码&#xff1a; 1137. 第 N 个泰波那契数题干&#xff1a;原理&#xff1a;1、状态表示&#xff08;dp表里面的值所…...

LangChain的函数,工具和代理(三):LangChain中轻松实现OpenAI函数调用

在我之前写的两篇博客中:OpenAI的函数调用,LangChain的表达式语言(LCEL)中介绍了如何利用openai的api来实现函数调用功能&#xff0c;以及在langchain中如何实现openai的函数调用功能&#xff0c;在这两篇博客中&#xff0c;我们都需要手动去创建一个结构比较复杂的函数描述变量…...

WiFi概念介绍

WiFi概念介绍 1. 什么是WLAN2. 什么是Wi-Fi3. Wi-Fi联盟4. WLAN定义范围5. WiFi协议体系6. 协议架构7. WiFi技术的发展7.1 IEEE802.117.2 802.11标准和补充 8. 术语 1. 什么是WLAN Wireless Local Area Network&#xff0c;采用802.11无线技术进行互连的一组计算机和相关设备。…...

如何优雅的进行业务分层

1.什么是应用分层 说起应用分层&#xff0c;大部分人都会认为这个不是很简单嘛 就controller&#xff0c;service, mapper三层。 看起来简单&#xff0c;很多人其实并没有把他们职责划分开&#xff0c;在很多代码中&#xff0c;controller做的逻辑比service还多,service往往当…...

C++的std命名空间

总以为自己懂了&#xff0c;可是仔细想想&#xff0c;多问自己几个问题&#xff0c;发现好像又不是很清楚 命名空间&#xff08;Namespace&#xff09;是C中一种用于解决命名冲突问题的机制&#xff0c;它能够将全局作用域划分为若干个不同的区域&#xff0c;每个区域内可以有…...

unity学习笔记

一、射线检测 如何让鼠标点击某个位置&#xff0c;游戏角色就能移动到该位置&#xff1f; 实现的原理分析&#xff1a;我们能看见游戏的东西就是摄像机拍摄到的东西&#xff0c;所以摄像机的镜平面就是当前能看到的了。 那接下来我们可以让摄像机发射一条射线&#xff0c;鼠标…...

使用SpringBoot和ZXing实现二维码生成与解析

一、ZXing简介 ZXing是一个开源的&#xff0c;用Java实现的多种格式的1D/2D条码图像处理库。它包含了用于解析多种格式的1D/2D条形码的工具类&#xff0c;目标是能够对QR编码&#xff0c;Data Matrix, UPC的1D条形码进行解码。在二维码编制上&#xff0c;ZXing巧妙地利用构成计…...

C++模板—函数模板、类模板

目录 一、函数模板 1、概念 2、格式 3、实例化 4、模板参数的匹配 二、类模板 1、定义格式 2、实例化 交换两个变量的值&#xff0c;针对不同类型&#xff0c;我们可以使用函数重载实现。 void Swap(double& left, double& right) {double tmp left;left ri…...

Monkey

一、Monkey的概念 “猴子测试”是指没有测试经验的人甚至对计算机根本不了解的人&#xff08;就像猴子一样&#xff09;不需要知道程序的任何用户交互方面的知识&#xff0c;如果给他一个程序&#xff0c;他就会针对他看到的界面进行操作&#xff0c;其操作是无目的的、乱点乱按…...

SQL中left join、right join、inner join等的区别

一张图可以简洁明了的理解出left join、right join、join、inner join的区别&#xff1a; 1、left join 就是“左连接”&#xff0c;表1左连接表2&#xff0c;以左为主&#xff0c;表示以表1为主&#xff0c;关联上表2的数据&#xff0c;查出来的结果显示左边的所有数据&#…...