AutoLeaders控制组——51单片机学习笔记(2)

一.模块化编程

各个模块的代码放在不同的.c文件里,在.h文件里提供各个模块的外部可调用函数的声明;其他从.c文件想使用其中的代码时,只需要在开头声明调用的头文件:#include“XXX.h”即可。
可极大的提高代码的可阅读性、可维护性、可移植性等。

#include后<>–>在安装目录中寻找头文件
后“ ”–>在程序目录中寻找头文件
#define pi 3.14–>定义pi,将pi替换为3.14
#ifdef:如果定义了__XXX_H_
#ifndef:如果没有定义__XXX_H_
#endif:与if形式语句匹配,组成括号;

.h文件格式

#ifndef __XXX_H__
#define __XXX_H__ //防止重复定义出错,对代码进行选择;
>
>//给出函数的定义(执行预编译)
>
>#endif

二.LCD1602

使用LCD1602液晶屏作为调试窗口,提供类似printf函数的功能,可以实时观察单片机内部数据的变化情况,便于调试和演示。

三.矩阵键盘

在这里插入图片描述
使用矩阵排布可减少I/O口的占用;
通过循环快速逐行(列)扫描实现所有按键同时检测;

51单片机P0~P3:弱上拉强下拉(0驱动强,1驱动弱)——在靠近Vcc处置有上拉电阻,I/O口另一端接地时由于电势降落显示低电平,否则显示高电平;

设置扫描按键取值代码

	unsigned char keynum=0;
	P1=0xFF;
	P1_3=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);keynum=1;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);keynum=5;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);keynum=9;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);keynum=13;}

按以上操作覆盖四列(行),最后返回keynum的值;
根据按键在LCD1602显示按键值:

while(1)
	{
		keynum=Matrixkey();//无按键按下时,返回值为0
		if(keynum)//只有按键按下时改变LCD的显示,否则显示会在循环中被刷新为0
		{
			LCD_ShowNum(2,1,keynum,2);
		}
	}

密码锁(矩阵键盘输入密码):

#include <REGX52.H>
#include "Delay.h"
#include "Matrixkey.h"
#include "LCD1602.h"
unsigned char keynum;
unsigned int password;
unsigned int key;
unsigned char cnt;
void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"PassWord?");
	while(cnt<4)//键盘输入四位数密码;
	{
		keynum=Matrixkey();
		if(keynum)
		{
			if(keynum<=10)
			{
				key*=10;
				key+=keynum%10;
				cnt++;
			}
		}	
	}
cnt=0;
	LCD_ShowString(2,1,"GUESS:");	
	while(1)
	{
		keynum=Matrixkey();
		if(keynum)
		{
			if(keynum<=10)
			{
				if(cnt<4)//是否能进入输入模式的判断
				{
					password*=10;
					password+=keynum%10;//按键输入10时置为0
					cnt++;
				}LCD_ShowNum(2,7,password,4);
			}
				if(keynum==11)
				{
					if(password==key)
					{
						LCD_ShowString(1,12,"   OK");
						Delay(1000);
						LCD_Init();//清屏
						Delay(500);
						LCD_ShowString(1,6,"Welcome");
						LCD_ShowString(2,5,"MY Master");//MVP结算画面
					
					}
					else
					{
						LCD_ShowString(1,12,"  ERR");
						Delay(500);
						LCD_ShowString(1,12,"Again");
						password=0;
						cnt=0;//刷新输入资格
						LCD_ShowNum(2,7,password,4);//重置
					}
				}
				if(keynum==12)
				{
					password=0;
						cnt=0;
						LCD_ShowNum(2,7,password,4);//重置
				}
				if(keynum==13)//提示
				{
					LCD_ShowNum(2,7,key,4);
					Delay(200);
					LCD_ShowNum(2,7,password,4);
				}
		}
	}
}

四.定时器

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c022ec7665024e53b836e09ee21e7617.png定时器:计时系统,实现软件计时或使程序隔一段时间完成一项操作,提高CPU运行效率和处理速度;

时钟系统(产生脉冲)——>计数单元(计数溢出)——>产生中断,执行中断任务;

SYSclk:系统时钟,即晶振周期,所使用开发板晶振为11.0592MHz
T0 Pin:计数器,由外部提供时钟;
通过CT选择计时还是计数;
+12:12分频;+6同理
TL0:time low,低8位;TH1:高8位;
TR0:控制定时器启动

GATE:非门,置反;GATE置0时输出1,经过与门输出1,TR0通过与门单独控制定时器启动;GATE置1时由INT0和TR0共同控制定时器启动
往右 或门:有1置1; 与门:有0记0;

TF0中断请求标志;

该模式定时器的中断源相关信息
在这里插入图片描述
中断查询次序即中断号;则该中断号为1;
在这里插入图片描述

该中断源下原理图
在这里插入图片描述
相关寄存器
在这里插入图片描述
定时器设置代码:

void Timer0Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;		//设置定时器模式(清除低四位,不影响高四位)
	TMOD |= 0x01;		//设置定时器模式(设置低四位)
	TL0 = 0x66;		//设置定时初值(软件生成1ms)
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0(溢出)标志,溢出后由硬件清零,只需清零一次
	TR0 = 1;		//定时器0开始计时
	PT0=0;//设置优先级
	EA=1;//打开中断总开关
	ET0=1;//打开该中断
}

中断函数:

void Time0_movement() interrupt 1
{
	static unsigned int t;//静态变量,不丢失初值
	t++;
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	while(t>=1000)//设置计时时长
	{
		t=0;
		//写出中断后的操作
	}
}

函数_crol_(unsigned char, unsigned char)头文件:<INTRINS.H>
作用:crol(a,n)—>a向左按位移n位,可刷新;cror();右移;

定时器应用:
时钟:

#include <REGX52.H>
#include "Timer.h"
#include "LCD1602.h"
unsigned char sec=55,min=59,hour=23; 
void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"CLOCK:");
	LCD_ShowChar(2,3,':');
	LCD_ShowChar(2,6,':');
	Timer0Init();
	while(1)
	{
		LCD_ShowNum(2,1,hour,2);
		LCD_ShowNum(2,4,min,2);
		LCD_ShowNum(2,7,sec,2);
	}
}
void Time0_movement() interrupt 1
{
	static unsigned int t;
	t++;
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	while(t>=1000)
	{
		t=0;
	  sec++;
		if(sec>=60){sec=0;min++;}
		if(min>=60){min=0;hour++;}
		if(hour>=24){hour=0;}
	}
}

五.串口通信

通信接口:UART;
通信方式:全双工、异步;
在这里插入图片描述

串口参数
波特率:串口通信的速率(发送和接受各数据位的间隔时间)
检验位:用于数据验证

奇校验:保证1个数为奇;
偶校验:保证1个数为偶;

停止位:用于数据帧间隔

串口模式图
在这里插入图片描述

SBUF:串口数据缓存寄存器,物理上为两个独立的寄存器,但占用相同的地址;写入时为发送寄存器,读入时为接送寄存器;
设置T1溢出时间来约定波特率
TI:发送控制器,发送数据后置1,触发中断;硬件不会自动复位,须用软件复位
RI:接收控制器,接收数据后置1,触发中断;硬件不会自动复位,须用软件复位
波特率计算:计数溢出所用时间相应频率经过16分频后的频率;

UART中断源相关资料
在这里插入图片描述
在这里插入图片描述
中断号为4
在这里插入图片描述
相关寄存器
在这里插入图片描述

可位寻址:可以对寄存器中单个单元操作
不可位寻址:只能对寄存器整体赋值;
SCON:设置TI,RI,SM0,SM1(决定模式),RED(决定是否能接受数据)
PCON:设置波特率;

初始化串口代码:

void UART_Init()		//4800bps@11.0592MHz
{
	PCON |= 0x80;		//使能波特率倍速位SMOD
	SCON = 0x50;		//8位数据,可变波特率
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xF4;		//设定定时初值
	TH1 = 0xF4;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1,以约定波特率发送/接收数据
	EA=1;  //打开中断总开关(发送时不用)
	ES=1;  //打开UART中断开关(发送时不用)
}

8位自动重装:将TH1的值重装进TL1中,TH1值不变,不需要重新赋值;计数个数为0~255;

发送数据代码:

void UART_SendByte(unsigned char Byte)
{
	SBUF=Byte;//发送数据
	while(TI==0);//若未发送则停留在此处
	TI=0;//软件置零
}

接受数据后中断操作代码:

void UART_Routine() interrupt 4
{
	if(RI==1)//判断数据是否接收
	{
		P2=SBUF;//用LED显示接受的数据
		UART_SendByte(SBUF);
		RI=0;//软件置零
	}
}	

文本模式:发送ASCII编码对应的符号

六.LED点阵屏

在这里插入图片描述
在这里插入图片描述
(使用时需将OE接到低电平GND处)
通过逐行(列)扫描显示所有LED;
74HC595模块串转并实现减少I/O口;

RCLK:寄存器时钟;上升沿沿将一个字节的数据锁存
SRCLK:串形时钟;上升沿将一个位的数据位移
SER:串行数据
QH;用于级联,扩展I/O口

写入数据代码:

sbit RCK=P3^5;//特殊位声明(取地址)
sbit SRCK=P3^6;
sbit SER=P3^4;
/**
   * @brief  读入选择行
   * @param  Byte 行数相应的代码
   * @retval 无
   */
void linectr(unsigned char Byte)
{
	unsigned char cnt=0;
	for(cnt=0;cnt<8;cnt++)
	{
		SER=Byte&(0x80>>cnt);//SER位赋值为有1赋1,从最高位开始读入
		SRCK=1;//位移
		SRCK=0;
	}
	RCK=1;//锁存
	RCK=0;
}

在LED屏上显示一列代码:

/**
   * @brief 点阵显示列
   * @param  column,line 分别确定行和列
   * @retval 无
   */
void MatrixLED_Showcolumn(unsigned char column,line)
{
	linectr(line);//1为选中
	P0=~(0x80>>column);//为0则选中
	Delay(1);//暂留效果
	P0=0xFF;//防止段选窜跑
}

若要显示画面只需在while中循环显示各列;
若要显示动画:

#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"

unsigned char code Animin[]={0x2C,0xFF,0x28,0x11,0x7F,0x11,0x11,0x00,
0x14,0x28,0x5F,0x28,0x0A,0x2F,0x28,0x08,
0x08,0x08,0x7E,0x08,0x08,0x7E,0x08,0x08,
0x20,0x7F,0x80,0x4C,0xC2,0x5F,0x60,0x4C,
0x00,0x00,0x82,0xBA,0x8A,0xF8,0x0B,0x0F,
0x00,0x00,0x00,0xFB,0xFB,0x00,0x00,0x00,
};//软件对动画取模,将数据储存在数组中;code:将数据置于flash中,节省空间,不能更改

void main()
{
	unsigned char i=0,offset=0,cnt=0;
	MatrixLED_Init();
	while(1)
	{
		for(i=0;i<8;i++)//循环读取列显示画面
		{
			MatrixLED_Showcolumn(i,Animin[i+offset]);
		}
		cnt++;
		while(cnt>=50)//决定画面暂留/换帧时间
		{
			offset+=8;//切换到下八位显示
			cnt=0;
			if(offset>40)offset=0;//重复显示
		}
	}
}

七.DS1302

在这里插入图片描述
DS1302:具有涓细电流充电能力的低功耗实时时钟芯片;
数据传输原理图:
在这里插入图片描述

CE:高电平有效;
SCLK:通过上升沿和下降沿来写入/读入数据
I/O:位数据暂存;输入写命令后写入数据,输入读命令字时读入数据;
从最低位写入数据;

相关寄存器:

在这里插入图片描述
0~3位为时间的个位,后4位为时间的十位;即输出时间为BCD码;
WP:写入保护,写入时需将该位置零。
数据写入代码:

void DS1302_WriteByte(unsigned char command,time)
{
	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=time&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;
}

读出数据:

#define DS1302_SEC		0x80
#define DS1302_HOUR		0x84
#define DS1302_MIN		0x82
#define DS1302_YEAR		0x8C
#define DS1302_MON		0x88
#define DS1302_DATE		0x86
#define DS1302_DAY		0x8A
#define DS1302_WP		0x8E//定义地址的名字
unsigned char DS1302_ReadByte(unsigned char command)
{
	DS1302_CE=1;//使能
	command|=0x01;//将最低位置1后上述写地址转变为读地址。
	ret=0x00;
	for(i=0;i<8;i++)
	{
		DS1302_IO=command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;//使命令字写入后为上升沿;使分块明显;
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;
		DS1302_SCLK=0;
		if(DS1302_IO){ret|=(0x01<<i);}//不影响其他位情况下读入当前位数据;
	}
	DS1302_CE=0;
	DS1302_IO=0;//防止出错
	return ret;
}

可调时钟(位选闪烁)代码:

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Timer.h"
#include "key.h"

unsigned char keynum,mode,tselect,flash;
void showtime()//刷新LCD显示
{
	DS1302_Timeread();
	LCD_ShowNum(1,1,Time[0],2);
	LCD_ShowNum(1,4,Time[1],2);
	LCD_ShowNum(1,7,Time[2],2);
	LCD_ShowNum(2,1,Time[3],2);
	LCD_ShowNum(2,4,Time[4],2);
	LCD_ShowNum(2,7,Time[5],2);		
}
void settime()//更改时间 模式
{
	keynum=key();
		if(keynum==2)//对时间进行位选
		{
			tselect++;
			tselect%=6;
		}
		if(keynum==3)
		{
			Time[tselect]++;
				if(Time[0]>99){Time[0]=0;}
			if(Time[1]>12){Time[1]=1;}
			if(Time[1]==1||Time[1]==3||Time[1]==5||Time[1]==7||Time[1]==8||Time[1]==10||Time[1]==12){if(Time[2]>31)Time[2]=1;}
				else if(Time[1]==4||Time[1]==6||Time[1]==9||Time[1]==11){if(Time[2]>30)Time[2]=1;}
					else if(Time[1]==2){if(Time[0]%4==0){if(Time[2]>29)Time[2]=1;}else{if(Time[2]>28)Time[2]=1;}}
			if(Time[3]>23){Time[3]=0;}
			if(Time[4]>59){Time[4]=0;}
			if(Time[5]>59){Time[5]=0;}//对时间溢出的判断
		}
		if(keynum==4)
		{
			Time[tselect]--;
			if(Time[0]<0){Time[0]=99;}
			if(Time[1]<1){Time[1]=12;}
			if(Time[1]==1||Time[1]==3||Time[1]==5||Time[1]==7||Time[1]==8||Time[1]==10||Time[1]==12){if(Time[2]<1)Time[2]=31;}
				else if(Time[1]==4||Time[1]==6||Time[1]==9||Time[1]==11){if(Time[2]<1)Time[2]=30;if(Time[2]>30)Time[2]=1;}
					else if(Time[1]==2){if(Time[0]%4==0){if(Time[2]<1)Time[2]=29;if(Time[2]>29)Time[2]=1;}else{if(Time[2]<1)Time[2]=28;if(Time[2]>28)Time[2]=1;}}
			if(Time[3]<0){Time[3]=23;}
			if(Time[4]<0){Time[4]=59;}
			if(Time[5]<0){Time[5]=59;}//对时间小于0/设置天数后更改月时天数溢出的处理
			
		}
if(tselect==0&&flash==1){LCD_ShowString(1,1,"  ");}
		else{LCD_ShowNum(1,1,Time[0],2);}
if(tselect==1&&flash==1){LCD_ShowString(1,4,"  ");}
		else{LCD_ShowNum(1,4,Time[1],2);}
if(tselect==2&&flash==1){LCD_ShowString(1,7,"  ");}
		else{LCD_ShowNum(1,7,Time[2],2);}
if(tselect==3&&flash==1){LCD_ShowString(2,1,"  ");}
		else{LCD_ShowNum(2,1,Time[3],2);}
if(tselect==4&&flash==1){LCD_ShowString(2,4,"  ");}
		else{LCD_ShowNum(2,4,Time[4],2);}
if(tselect==5&&flash==1){LCD_ShowString(2,7,"  ");}
		else{LCD_ShowNum(2,7,Time[5],2);}				
		LCD_ShowNum(2,10,tselect,2);//选中闪烁
}

void main()
{
	DS1302_Init();
	LCD_Init();
  Timer0Init();
  DS1302_Timeset();
	LCD_ShowString(1,1,"  -  -");
	LCD_ShowString(2,1,"  :  :");
	while(1)
	{
    keynum=key();
		
			if(keynum==1)
			{
				mode=!mode;
				if(!mode)DS1302_Timeset();//将更改的数据确认设置
			}
		if(!mode)//通过mode状态控制模式
		{
			showtime();
		}
		else{
			settime();
		}
	}
}

void Time0_movement() interrupt 1
{
	static unsigned int t;
	t++;
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	while(t>=500)
	{
		t=0;
		flash=!flash;//通过该变量控制闪烁
	}
}

DS1302模块代码:

#include <REGX52.H>

sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

unsigned char i=0,ret=0x00;

char Time[]={23,12,13,14,3,55,3};//初始化时间,依次为年月日时分秒

#define DS1302_SEC		0x80
#define DS1302_HOUR		0x84
#define DS1302_MIN		0x82
#define DS1302_YEAR		0x8C
#define DS1302_MON		0x88
#define DS1302_DATE		0x86
#define DS1302_DAY		0x8A
#define DS1302_WP		0x8E

void DS1302_Init()
{
	DS1302_SCLK=0;
	DS1302_CE=0;
	DS1302_IO=0;
}//初始化

void DS1302_WriteByte(unsigned char command,time)
{
	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=time&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;
}


unsigned char DS1302_ReadByte(unsigned char command)
{
	DS1302_CE=1;
	command|=0x01;
	ret=0x00;
	for(i=0;i<8;i++)
	{
		DS1302_IO=command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;
		DS1302_SCLK=0;
		if(DS1302_IO){ret|=(0x01<<i);}
	}
	DS1302_CE=0;
	DS1302_IO=0;
	return ret;
}


void DS1302_Timeset()//读入初始时间/读入修改后的时间
{
	DS1302_WriteByte(DS1302_WP,0x00);//解除写入保护
	DS1302_WriteByte(DS1302_YEAR,Time[0]/10*16+Time[0]%10);
	DS1302_WriteByte(DS1302_MON,Time[1]/10*16+Time[1]%10);
	DS1302_WriteByte(DS1302_DATE,Time[2]/10*16+Time[2]%10);
	DS1302_WriteByte(DS1302_HOUR,Time[3]/10*16+Time[3]%10);
	DS1302_WriteByte(DS1302_MIN,Time[4]/10*16+Time[4]%10);
	DS1302_WriteByte(DS1302_SEC,Time[5]/10*16+Time[5]%10);
	DS1302_WriteByte(DS1302_DAY,Time[6]/10*16+Time[6]%10);//将时间的BCD码写入
	DS1302_WriteByte(DS1302_WP,0x80);
}
unsigned char BCD(unsigned char Byte)
{
	unsigned char temp=0;
	temp=Byte/16*10+Byte%16;
	return temp;
}//BCD码转变为十进制数

void DS1302_Timeread()
{
	Time[0]=BCD(DS1302_ReadByte(DS1302_YEAR));
	Time[1]=BCD(DS1302_ReadByte(DS1302_MON));
	Time[2]=BCD(DS1302_ReadByte(DS1302_DATE));
	Time[3]=BCD(DS1302_ReadByte(DS1302_HOUR));
	Time[4]=BCD(DS1302_ReadByte(DS1302_MIN));
	Time[5]=BCD(DS1302_ReadByte(DS1302_SEC));
	Time[6]=BCD(DS1302_ReadByte(DS1302_DAY));
}



以上图片取自普中开发板原理图,STC89C52手册,DS1302原理图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BOKUSAIQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值