单片机实验(三)
前言
实验一:利用定时器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
原理图到没什么需要更改的,主要是不同频率的声音应该怎么发出来。

拓展
其实主要还是代码部分,原理图不需要修改,需要先找一份简谱,然后需要知道怎么看音乐乐谱。
实验需要把握的就是“音的高低”和“音的长短”。
在目前的代码中不考虑倍低音和低音,自己低音、中音、高音对应的代码。
音的高低部分需要知道,在基本音符上方加一个“.”,表示该音升高一个八度,称为高音;在基本音符下方加一个“.”,表示该音降低一个八度,称为低音。这里只需要知道区分低中高音即可。
知道了音的高低接下来就是音的长短部分,其实就是表示那个音延迟的时间,这里就只需要记住在基本音符下方加记一条短横线,表示缩短原音符时值的一半。
这两个问题都解决了就可以直接编写程序了,其实也不需要很复杂的乐理知识,甚至基本音符是什么都可以不用知道,就是看着简谱修改成自己对应的即可,对于特殊的符号还需要自己查资料看表示什么。
基本音符
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| Do | Re | Mi | Fa | Sol | La | Si |
《平凡之路》的部分简谱

缺点对应倍低音的地方无法编写程序,因为这只有低音、中音、高音频率的代码,然后音也有点不准吧,但是调子感觉也差不多,可以听出来大致的调子。
#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的显示以及时钟的进位,这里需要考虑的是根据不同的年份以及月份,他的天数是不一致的,所以这个需要考虑进去,经过在程序中这里也没有体现到,可以通过设置时间的节点来去验证他是否满足要求。
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
| 80 | y | y | y | y | . | M | M | . | d | d | w | w | ||||
| C0 | T | I | M | E | h | h | : | m | m | : | s | s |
我这里犯了一个很严重的错误就是对于年份在用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;
}
为了更好的表示按钮的含义这里用到了写文字的模块,如果直接用中文命名器件容易导致程序崩溃。



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


总结
认真想下来其实还是很有意思的,但是也是很耗时间,有时间再来尝试一些新的功能。
相关文章:
单片机实验(三)
前言 实验一:利用定时器T1的中断控制P1.7引脚输出音频信号,启动蜂鸣器发出一段熟悉的与众不同的具有10个音节的音乐音频。 实验二:使用定时器/计数器来实现一个LCD显示年、月、日、星期 、时、分、秒的电子表,要求时和分可以方便…...
Python 2进制按位取反
根据一checksum算法需要将一些参数按位取反 例:参数 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表格文件,主要是为每个用户创建一个pubkey,并输出C Sharp C#和嵌入式 Keil的工程文件 pub…...
<Linux>(极简关键、省时省力)《Linux操作系统原理分析之存储管理(2)》(15)
[TOC](《Linux操作系统原理分析之存储管理(2)》(15) 5 存储管理5.4 分页存储管理5.4.1 纯分页存储管理a.页(页面)和物理块(帧)b. 页面大小c. 逻辑地址结构 5.5 存储扩充技术5.5.2 交…...
jdk8、jdk9中,接口的新特性
接口的老特性: 没有构造方法成员变量只能定义常量,默认三个关键字public static final只能是抽象方法,默认两个关键字public abstract 接口的新特性: jdk8 1.接口允许定义非抽象方法,需加入default关键字。为了解决…...
第一题-字符串拼接【第六届传智杯程序设计挑战赛解题分析详解复盘】(C/C++实现)
🚀 欢迎来到 ACM 算法题库专栏 🚀 在ACM算法题库专栏,热情推崇算法之美,精心整理了各类比赛题目的详细解法,包括但不限于ICPC、CCPC、蓝桥杯、LeetCode周赛、传智杯等等。无论您是刚刚踏入算法领域,还是经验丰富的竞赛选手,这里都是提升技能和知识的理想之地。 ✨ 经典…...
简谈oracle数据库的归档模式
一、oracle数据库归档模式简介 Oracle数据库归档模式是一种数据备份和恢复策略,它允许数据库记录所有数据库的更改操作(包括已提交和未提交的事务)并将其存储在归档日志中。这些归档日志可以用于在数据库发生故障时进行恢复,并提供点时间恢复(PITR)的能力。 在Oracle数…...
FLASK博客系列9——你想成为我的新用户吗?
距离上次发文好久好久了。 先说声抱歉,拖更的毛病我会改掉的。 上次我们教大家如何用后台去管理用户和新增文章,但始终都是单机操作,怎么让你的朋友也来加入你的小站呢?今天我们来为我们的网站增添一个新功能,实现用户…...
用通俗的方法讲解:大模型微调训练详细说明(附理论+实践代码)
本文内容如下 介绍了大模型训练的微调方法,包括prompt tuning、prefix tuning、LoRA、p-tuning和AdaLoRA等。 介绍了使用deepspeed和LoRA进行大模型训练的相关代码。 给出了petals的介绍,它可以将模型划分为多个块,每个用户的机器负责其中一…...
现代化工安全保障迎来巡查无人机新时代
当今现代化工企业呈现出规模不断扩大,设备逐渐趋向大型化的局面,由此导致化工安全生产面临日益严峻的挑战。然而,随着巡查无人机技术的成熟,这种新的高效手段正在提高化工安全检测的工作效率。 一、传统化工安全巡检存在弊端 化工…...
关于web前端通过js获取后端mysql数据库数据的一个方法
关于web前端通过js获取后端mysql数据库数据的一个方法 问题引入 关于html的教程很多,关于mysql的教程也很多,那么怎么让html展示mysql的数据呢? 一言以蔽之 前端通过js向后端发起一个http请求,后端响应这个请求并返回数据 实…...
如何下载IEEE出版社的Journal/Conference/Magazine的LaTeX/Word模板
当你准备撰写一篇学术论文或会议论文时,使用IEEE(电气和电子工程师协会)的LaTeX或Word模板是一种非常有效的方式,它可以帮助你确保你的文稿符合IEEE出版的要求。无论你是一名研究生生或一名资深学者,本教程将向你介绍如…...
京东数据运营-京东数据开放平台-鲸参谋10月粮油调味市场品牌店铺销售数据分析
鲸参谋监测的京东平台10月份料油调味市场销售数据已出炉! 根据鲸参谋数据显示,今年10月份,京东平台粮油调味市场的销量将近4600万,环比增长约10%,同比降低约20%;销售额将近19亿,环比增长约4%&am…...
ThermalLabel SDK for .NET 13.0.23.1113 Crack
ThermalLabel SDK for .NET 是一个 .NET 典型类库,它允许用户和开发人员创建非常创新的条码标签并将其发布在 zebra ZPL、EPL、EPSON ESC、POS 以及 Honeywell intermec 指纹中通过在 VB.NET 或 C# 上编写 .NET 纯代码来实现热敏打印机,以实现项目框架的…...
[Java学习日记]网络编程
目录 一.常见的软件架构、网络编程三要素、IP 二.利用UDP发送与接收数据 三.改聊天室 四.组播案例 五.TCP通信案例 一.常见的软件架构、网络编程三要素、IP 网络编程:在网络通信协议下,不同的计算机上运行的程序进行的数据传输 在Java中可以使用java…...
spring boot mybatis TypeHandler 看源码如何初始化及调用
目录 概述使用TypeHandler使用方式在 select | update | insert 中加入 配置文件中指定 源码分析配置文件指定Mapper 执行query如何转换 结束 概述 阅读此文 可以达到 spring boot mybatis TypeHandler 源码如何初始化及如何调用的。 spring boot 版本为 2.7.17,my…...
数据结构基础(带头节点的双向循环链表)
完整代码 DLinkList.hDLinkList.ctest.c DLinkList.h #pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h>typedef int ElemType;// SList - 单链表 // DList - 双链表 // 带头节点的双向循环链表 - 最优链表结构,任意位置…...
STM32CubeMx+MATLAB Simulink点灯程序
STM32CubeMxMATLAB点灯程序 ✨要想实现在MATLAB Simulink环境下使用STM32,前提是已经搭建好MATLAB环境并且安装了必要的Simulink插件,以及对应的STM32支持包。 🌿需要准备一块所安装支持包支持的STM32开发板. 🔖具体支持包详情页…...
【深度学习】gan网络原理生成对抗网络
【深度学习】gan网络原理生成对抗网络 GAN的基本思想源自博弈论你的二人零和博弈,由一个生成器和一个判别器构成,通过对抗学习的方式训练,目的是估测数据样本的潜在分布并生成新的数据样本。 1.下载数据并对数据进行规范 transform tran…...
springboot参数汇总
multipart multipart.enabled 开启上传支持(默认:true) multipart.file-size-threshold: 大于该值的文件会被写到磁盘上 multipart.location 上传文件存放位置 multipart.max-file-size最大文件大小 multipart.max-request-size 最大请求…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
