AutoLeaders控制组-刘凡

模块化编程

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

模块化编程框图

也就是说在Delay.c中写实现功能的代码,然后借助Delay.h实现一个预编译和中介作用,这里不要忘了在void函数后面加上分号,最后在main.c中直接调用Delay函数即可,注意头文件的形式是#include “Delay.h”

LCD1602调试工具

有两行十六列,可以显示数字或字符(串)方便调试

矩阵键盘

采用输入扫描:读取第一行(列),读取第二行(列)......快速循环,最终实现所有按键同时检测的效果。因为按行扫描会影响蜂鸣器。

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

/**
  * @brief 矩阵键盘读取按键键码
  * @param  无
  * @retval KeyNumber 按下按键的键码值
  如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回0
  */


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

	P1=0xFF;
	P1_2=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}
	
	P1=0xFF;
	P1_1=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}
	
	P1=0xFF;
	P1_0=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}
	
	return KeyNumber;
}

也就是说按键按下并松开后,返回键码

//在main.c中
unsigned char KeyNum=0;
void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"MatrixKey");
	while(1)
	{
		KeyNum=MatrixKey();//将返回键值传给一个新变量
		if(KeyNum)//对返回键值进行判断
		{
			LCD_ShowNum(2,1,KeyNum,2);//显示相应的数字
		}
	}
}

定时器

属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。

用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作;替代长时间的Delay,提高CPU的运行效率和处理速度。

STC89C52定时器/计数器资源有三个(T0、T1、T2),使用单片机要多看手册查看内部资源

定时器0模式1学习

T0和T1都分别有模式 1、2、3、4,模式1(最常用)对应到手册即将M1置0,M0置1,定时器0模式1框图如下:分为时钟(左)、计数(中间)、中断(右)

//配置TMOD不可位寻址
TMOD=0x01;    //0000 0001 前四位配置定时器1,后四位配置定时器0
//配置TCON可位寻址
TF0=0;    //中断溢出标志位清零,防止立刻产生中断
TR0=1;    //TimerReady定时器开始工作

时钟

SYSclk(内部):系统时钟即晶振周期,给0即Timer定时器。

T0Pin(外部):由外部引脚提供时钟时,单片机的定时器相当于计数器,给1即Counter计数器。

计数

TL0(低字节TimerLow)TH0(高字节TimerHigh)十六位计数系统能计65535个数,左边每来一个脉冲,计数系统加一。65535加一溢出置标志位TF0(TimerFlag)。

//配置定时器0模式1后
TH0=64535/256;    //获取高位
TL0=64535%256+1;    //获取低位

中断

计到TF0申请中断

另外,STC89C52的中断资源包括8个中断源(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3)、4个中断优先级、中断号

//中断号:中断查询次序号就是中断号,例如:
void Int0_Rountine(void)      interrupt0;    //外部中断
void Timer0_Rountine(void)    interrupt1;    //定时器中断
void Int1_Rountine(void)      interrupt2;
void Timer1_Rountine(void)    interrupt3;
void UART_Rountine(void)      interrupt4;    //串口中断
void Timer2_Rountine(void)    interrupt5;
void Int2_Rountine(void)      interrupt6;
void Int3_Rountine(void)      interrupt7;

//配置中断
ET0=1;
EA=1;
PT0=0;    //默认低优先级
unsigned int T0Count;
void Timer0_Routine() interrupt 1	//中断子函数
{
	//每次中断都要重新赋初值
	TH0=64535/256;
	TL0=64535%256;
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		//要执行的程序
	}
}

改进

由于TMOD是不可位寻址,为了不影响定时器1和定时器0的同时使用有以下改进:

TMOD&=0xF0;    //把TMOD的低四位清零,高四位保持不变
TMOD|=0x01;    //把TMOD的最低位置1,高四位保持不变

定时器的使用

常用的模板

可以将其模块化,方便使用。

按键检测

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

/**
  * @brief 	获取独立按键键码  
  * @param  无
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */

unsigned char Key()
{
	unsigned char KeyNumber=0;
	if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
	if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
	if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
	if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
	
	return KeyNumber;
}

定时器0的配置

#include <REGX52.H>

/**
  * @brief  定时器0初始化
  * @param  无
  * @retval 无
  */

void Timer0_Init(void)	//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x66;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

延时

void Delay(unsigned char xms)	//@11.0592MHz
{
	unsigned char data i, j;

	while(xms--)
	{
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
	}
}

按键控制LED流水灯模式

新函数库#include <INTRINS.H>

unsigned char a=0x01;
a=_crol_(a,1);	//a=0x02

实现流水灯

#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
#include "Delay.h"


unsigned char KeyNum=0,LEDMode=0;
void main()
{
	P2=0xFE;
	Timer0_Init();
	while(1)
	{
		KeyNum=Key();
		if(KeyNum)
		{
			if(KeyNum==1)
			{
				LEDMode++;
				if(LEDMode>=2)LEDMode=0;
			}
		}
	}
}

void Timer0_Routine() interrupt 1	//中断子函数
{
	static unsigned int T0Count=0;
	//每次中断都要重新赋初值
	TH0=0x18;
	TL0=0xFC;
	T0Count++;
	if(T0Count>=500)
	{
		T0Count=0;
		if(LEDMode==0)
			P2=_crol_(P2,1);
		if(LEDMode==1)
			P2=_cror_(P2,1);
	}
}

定时器时钟

就是一个多重循环判断,需要用到LCD1602进行显示、定时器

串口

应用广泛的通讯接口,可实现两个设备的互相通信

电平标准

TTL电平:+5V表示1,0V表示0

RS232电平:-3~-15V表示1,+3V~15V表示0

RS485电平:两线压差+2~+6表示1,-2~-6表示0(差分信号)

接口及引脚定义

51单片机的UART

有四种工作模式,常用的是模式1:8位UART,波特率可变

串口模式图

需要用到定时器,还有中断系统

串口相关寄存器

LED点阵屏

显示原理

LED点阵屏结构类似于数码管,且有共阴和共阳两种接法,进行逐行或逐列的扫描进行显示。

引脚和相关寄存器

74HC595是8位串行输入/输出、并行输出移位寄存器

SER口输入串行的数据,当SRCLK有上升沿移位信号时,数据就往下移一位,当数据占满8位时,给RCLK上升沿锁存信号时,8位数据就锁存到QA~QH,QH’连接到下一片74HC595,继续拓展16位、32位…

DS1302实时时钟

是RTC时钟芯片的一种,内含有一个实时时钟/日历和 31 字节静态 RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整。

应用电路

引脚名:

VCC2是主电源,VCC是备用电源,X1、X232.768Hz晶振(稳定提供1Hz脉冲)

CE芯片使能,IO数据输入/输出,SCLK串行时钟

内部结构框图

读写(寄存器定义)

地址和命令字

最高位7必须是1;

第6位给1操作RAM,给0操作时钟;

5~1地址位,给00000操作秒,写秒为80h(bcd码高字节表示第一位,低字节表示第二位,1000 0000)
第0位为0则为写,为1则为读.

时序图

整个操作过程中CE给高电平,写入操作才是有效的,结束后给0

SCLK上升沿写入,下降沿读出

写:CE置1,先发RW最低位,上升沿,时钟置0,IO次低位A0放线上,上升沿......

读:同上

BCD码:用4位二进制数来表示1位十进制数

例:0001 0011表示13,1000 0101表示85,0001 1010不合法

在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法

BCD码转十进制:DEC=BCD/16*10+BCD%16;(2位BCD)

十进制转BCD码:BCD=DEC/10*16+DEC%10;(2位BCD)

#include <REGX52.H>

//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

//寄存器写入地址/指令定义,也提高可读性,方便写代码
#define DS1302_SECOND		0x80
#define DS1302_MINUTE		0x82
#define DS1302_HOUR			0x84
#define DS1302_DATE			0x86
#define DS1302_MONTH		0x88
#define DS1302_DAY			0x8A
#define DS1302_YEAR			0x8C
#define DS1302_WP			0x8E
/**
  * @brief  DS1302初始化
  * @param  无
  * @retval 无
  */

void DS1302_Init(void)
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @retval 无
  */
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;
}

/**
  * @brief  DS1302读一个字节
  * @param  Command 命令字/地址
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
	unsigned char i,Data=0x00;
	Command|=0x01;	//将指令转换为读指令
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;	//先给0再给1,卡一个脉冲
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;	//重复置1,去掉一个脉冲
		DS1302_SCLK=0;
		if(DS1302_IO){Data|=(0x01<<i);}
	}
	DS1302_CE=1;
	DS1302_IO=0;	//读取后将IO设置为0,不然会出错
	return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  * @param  无
  * @retval 无
  */
void DS1302_SetTime(void)
{
	DS1302_WriteByte(DS1302_WP,0x00);
	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);	//十进制转BCD码后写入
	DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
	DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
	DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
	DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
	DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
	DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
	DS1302_WriteByte(DS1302_WP,0x80);
}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
void DS1302_ReadTime(void)
{
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);//BCD码转十进制后读取
	DS1302_Time[0]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MONTH);
	DS1302_Time[1]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DATE);
	DS1302_Time[2]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_HOUR);
	DS1302_Time[3]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MINUTE);
	DS1302_Time[4]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_SECOND);
	DS1302_Time[5]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DAY);
	DS1302_Time[6]=Temp/16*10+Temp%16;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值