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

模块化编程

a.将实现不同功能的代码块单独提出作为函数,降低开发难度,提高移植性。
keil内需要将子程序.c和.h文件放入project文件所在文件夹。

b.定义.h文件常用格式:

#ifndef __XXX_H__
#def __XXX_H__
//函数声明
#endif

LCD1602

功能更强大的显示屏,功能实现易于模块化,便于不了解原理而直接使用现成代码。江科大提供如下函数:

LCD_Init();
	
	LCD_ShowString(1,1,"hello");
	LCD_ShowChar(1,6,'A');
	LCD_ShowNum(1,7,123,3);
	LCD_ShowHexNum(1,10,0xA8,2);
	LCD_ShowBinNum(2,1,0xAA,8);

矩阵键盘

a.数码管扫描为输出扫描,而类似于数码管扫描,矩阵键盘扫描为输入扫描,分为按行扫描和按列扫描,如此可节约I/O口。
按列扫描时,需要扫描的列置0,其他置1,某行按下会被下拉至0.

b.STC89C52的P15口在逐行扫描会出问题,与蜂鸣器有关,建议按列。
在这里插入图片描述
c.检测函数如下:

unsigned char Matrixkey()
{
	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;}	
	//以下省略
	return keynum;
	
}

定时器

51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。
STC89C52有3个定时器(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是STC89C52型号单片机新增。

STC89C52的T0,T1均有四种工作模式:
模式0:13位定时器/计数器
模式1:16位定时器/计数器(常用)
模式2:8位自动重装模式
模式3:两个8位计数器
在这里插入图片描述

中断

配合计数器,可跳出循环执行额外操作。

中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3)
中断优先级个数:4个

以下为该型号的中断资源:
在这里插入图片描述

相关寄存器

在这里插入图片描述
在这里插入图片描述
手册里关于各寄存器有详细解释。

定时器应用

定时器初始化代码可用stc-isp辅助生成,如下:

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;
}

定时器时钟

#include <STC89C5xRC.H>
#include "Delay.h"
#include "LCD1602.h"
#include "Timer0.h"


unsigned char second = 0;
unsigned char minute = 0;
unsigned char hour = 0;
void main()
{
    Timer0_Init();
    LCD_Init();
    LCD_ShowString(1, 1, "Clock:");
    while(1)
    {
        LCD_ShowNum(2, 1, hour, 2);
        LCD_ShowString(2, 3, ":");
        LCD_ShowNum(2, 4, minute, 2);
        LCD_ShowString(2, 6, ":");
        LCD_ShowNum(2, 7, second, 2);
    }
}


void Timer0_Isr(void) interrupt 1
{
    static unsigned int T0_count = 0;

	TL0 = 0x66;
	TH0 = 0xFC;	

    T0_count++;
    if(T0_count >= 1000)
    {
        T0_count = 0;
        second++;
        if(second == 60)
        {
            second = 0;
            minute++;
            if(minute == 60)
            {
                minute = 0;
                hour++;
                if(hour == 24)
                {
                    hour = 0;
                }
            }
            
        }
    }
}

串口通信

a.串口可用于实现两个设备之间的互相通信。

TTL电平:+5V表示1,0V表示0(单片机用的)
RS232电平:-3-15V表示1,+3+15V表示0(一般用在电脑等高电压的传输)
RS485电平:两线压差+2+6V表示1,-2-6V表示0

通信接口

在这里插入图片描述
b.51应用UART接口,其初始化代码可借由stc-isp生成:

void UART_Init(void)	//4800bps@11.0592MHz
{
	PCON |= 0x80;		//使能波特率倍速位SMOD
	SCON = 0x50;		//8位数据,可变波特率,这里0x40是ren置0,只能接收;若要能接受发送需要置1,即0x50
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xF4;			//设置定时初始值
	TH1 = 0xF4;			//设置定时重载值
	ET1 = 0;			//禁止定时器中断
	TR1 = 1;			//定时器1开始计时
	EA=1;               //打开总中断
	ES=1;               //打开串口中断
}
void UART_SendByte(unsigned char Byte)    //向电脑发送数据
{
	SBUF=Byte;
	while(TI==0);        //发送
	TI=0;                //软件复位
}

LED点阵屏

a.类似为8组数码管,行列式连接,每行为共阴/共阳。
点阵屏的图像显示也是通过不断扫描来实现的,分逐行/逐列扫描。

b.74HC595
在这里插入图片描述

点阵屏应用

a.点亮点阵屏,用函数简单控制动画帧。
b.用数组储存动画数据,如下:

unsigned char code Animation[]={       //这里加code后数组在flash存放,且不能更改,不加code的话存放在ram里
	0x70,0xF8,0xFC,0x7F,0x7F,0xFC,0xF8,0x70,  //
	0x30,0x78,0x7C,0x3E,0x3E,0x7C,0x78,0x30,
	0x00,0x00,0x30,0x38,0x38,0x30,0x00,0x00,
	0x30,0x78,0x7C,0x3E,0x3E,0x7C,0x78,0x30,
	}

模块部分如下:

#include <REGX52.H>
#include "delay.h"
#define MatriLED_PORT    P0    //不加分号
sbit RCK=P3^5;    //RCLK
sbit SCK=P3^6;    //SERCLK
sbit SER=P3^4;    //SER
void Matrix_Init()
{
	SCK=0;
	RCK=0;
}
void _74HC595_WriteByte(unsigned char Byte)
{
	unsigned char i;       //74HC595写入一个字节Byte
	for(i=0;i<8;i++)       
	{
		SER=Byte&(0x80>>i);
		SCK=1;            //上升沿移位写入数据时上升沿有效
		SCK=0;            //复位
	}
	RCK=1;               //打开上升沿锁存,数据移出
	RCK=0;
}
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
	_74HC595_WriteByte(Data);  //高位在上低位在下,1亮0灭,控制点阵屏的每一列
	MatriLED_PORT=~(0x80>>Column);
	Delay(1);              //消影
	MatriLED_PORT=0xff;    //对于点阵屏的每一列来说1灭0亮,所以复位用0xff
}

DS1302

a.内置时钟,自带进位逻辑,但任需代码判断。

b.芯片电路

在这里插入图片描述

CE:芯片使能,类似中介开关,置1才能输入移位
SLCK:串行时钟,0是下降沿(单片机读取DS1302数据),1是上升沿(向DS1302输入数据)
I/O:输入输出(一次一位)
(SLCK与IO配合实现输入移位)
VCC,GND:电源与电源地,左侧VCC是主电源,右侧VCC是备用电源
X1,X2:晶振,利用晶振计数

BCD码

用四位数表示十进制数的一位。

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

可调时钟

DS1302模块化(此略)后,主函数代码主要关注越界判断和利用定时器实现闪烁。

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"

unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;

void TimeShow(void)//时间显示功能
{
	DS1302_ReadTime();//读取时间
	LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
	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(void)//时间设置功能
{
	if(KeyNum==2)//按键2按下
	{
		TimeSetSelect++;//设置选择位加1
		TimeSetSelect%=6;//越界清零
	}
	if(KeyNum==3)//按键3按下
	{
		DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
		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;}//大月
		}
		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)
			{
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
			}
			else
			{
				if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
			}
		}
		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)//按键3按下
	{
		DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
		if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判断
		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){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;}//闰年2月
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
			}
			else
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
				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;}//秒越界判断
	}
	//更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
	if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}
	else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
	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();
	Timer0Init();
	LCD_ShowString(1,1,"  -  -  ");//静态字符初始化显示
	LCD_ShowString(2,1,"  :  :  ");
	
	DS1302_SetTime();//设置时间
	
	while(1)
	{
		KeyNum=Key();//读取键码
		if(KeyNum==1)//按键1按下
		{
			if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
			else if(MODE==1){MODE=0;DS1302_SetTime();}
		}
		switch(MODE)//根据不同的功能执行不同的函数
		{
			case 0:TimeShow();break;
			case 1:TimeSet();break;
		}
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=500)//每500ms进入一次
	{
		T0Count=0;
		TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
	}
}

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值