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

一、模块化编程

简介

模块化程序设计是指在进行程序设计时将一个大程序按照功能划分为若干小程序模块,每个小程序模块完成一个确定的功能,并在这些模块之间建立必要的联系,通过模块的互相协作完成整个功能的程序设计方法。

传统的编程方式将所有的函数放在main函数之中,当时用的代码数量增多时会极大的影响编码者的效率和思路,为了提高效率和思路采用模块化编程的方式。

模块化编程便是将那些常使用且变化不大的函数整合打包放在<xxx.h>的头文件中,当我们需要使用某段代码时只需要声称include<xxx.h>即可。使用这样的方法可以让我们在编码中更简洁,高效,清晰的编写代码。
文件格式(可作为模板套用)

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

预编译的部分代码与作用

#include <REGX52.H> 把REGX52.H文件的内容搬到此处
#define PI 3.14 定义PI,将PI替换为3.14
#define ABC 定义ABC
#ifndef xx_H 如果没有定义__XX_H__
#endif 与**#ifndef,#if** 匹配,组成“括号”
此外还有#ifdef,#if,#else,#elif,#undef等

举例

例如,当我们想要对Delay函数进行模块化编程

void Delay(unsigned int xms)		
{  while(xms--)
	{
	unsigned char i, j;
	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
 }
}
#ifndef __Delay_H__
#define __Delay_H__

void Delay(unsigned int xms);

#endif

二、LCD1602

简介

它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。不同厂家生产的LCD1602芯片可能有所不同,但使用方法都是一样的。为了降低成本,绝大多数制造商都直接将裸片做到板子上。
LCD液晶屏有两行,十六列
(1). LCD1602液晶屏作为调试窗口,提供类似printf函数的功能 可实时观察单片机内部数据的变换情况,便于调试和演示。
(2) . 相比于数码管和串口 (数码管显示的内容太少并且需要不断扫描,容易闪烁。)(串口则需要使用电脑来不断打开。操作相对复杂笨重)

显示原理

点阵图形式液晶由M×N个显示单元组成,假设LCD显示屏有64行,每行有128列,每8列对应1字节的8位,即每行由16字节,共16×8=128个点组成。显示屏上64×16个显示单元与显示RAM区的1024字节相对应,每一字节的内容与显示屏上相应位置的亮暗对应。例如显示屏第一行的亮暗由RAM区的000H~00FH的16字节的内容决定,当(000H)=FFH时,屏幕左上角显示一条短亮线,长度为8个点;当(3FFH)=FFH时,屏幕右下角显示一条短亮线;当(000H)=FFH,(001H)=00H,(002H)=00H…,(00EH)=00H,(00FH)=00H时,在屏幕的顶部显示一条由8条亮线和8条暗线组成的虚线。这就是LCD显示的基本原理。

字符型液晶显示模块是一种专门用于显示字母、数字和符号等的点阵式LCD,常用16×1,16×2,20×2和40×2等的模块。一般的LCD1602字符型液晶显示器的内部控制器大部分为HD44780,能够显示英文字母、阿拉伯数字、日文片假名和一般性符号。

在这里插入图片描述

三、矩阵键盘

简介

矩阵键盘是单片机外部设备中所使用的排布类似于矩阵的键盘组。矩阵式结构的键盘显然比直接法要复杂一些,识别也要复杂一些,列线通过电阻接正电源,并将行线所接的单片机的I/O口作为输出端,而列线所接的I/O口则作为输入。
当键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,矩阵键盘由此而来。采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态。如下图:16个按键只需要8个I/O口
在这里插入图片描述
扫描:
数码管扫描(输出扫描)原理: 显示第1位一>显示第2位一>显示第3位然后快速循环这个过程,最终实现所有数码管同时显示的效果
矩阵键盘扫描(输入扫描原理:
读取第1行(列)一>读取第2行(列)一>读取第3行(列)然后快速循环这个过程,最终实现所有按键同时检测的效果
以上两种扫描方式的共性: 节省I/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;
}

由于I/O口为弱上拉模式,单片机检测低电平,故只有当某按键两端均为0时,才算按下此按键
因此由上述代码可知,逐列扫描时先将所有P1口赋为1,再分别将每一列对应的I/O口赋为0,接下来判断该列上的四个按键中哪一个按键的另一侧/O口为0,若为0则表示此按键被按下,然后进行按键消抖,判断按键两端是否恒为0,如果不松手程序则被卡死,松手后则进行接下来的扫描。

四、定时器

简介

1.简介
定时器介绍:51单片机的定时器不同于独立按键和矩阵键盘等外设,它属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。
定时器作用:

(1)用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作。
(2) 替代长时间的Delay,提高CPU的运行效率和处理速度。例如边进行流水灯边按键扫描
(3)操作系统任务进行切换

原理

定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号,每隔“一秒”,计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间”时,计数单元就会向中断系统发出中断申请,产生“响铃提醒”,使程序跳转到中断服务函数中执行

STC89C52定时器资源

定时器个数:3个(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源

注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的

定时器工作模式

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

中断系统

第6章中断系统
中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。
当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求。
当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果CPU能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中断嵌套功能的中断系统称为单级中断系统。

中断程序流程图

在这里插入图片描述

寄存器

寄存器是连接软硬件的媒介
在单片机中寄存器就是一段特殊的RAM存储器,一方面,寄存器可以存储和读取数据,另一方面,每一个寄存器背后都连接了一根导线,控制着电路的连接方式
寄存器相当于一个复杂机器的“操作按钮”
在这里插入图片描述
在这里插入图片描述

五、串口通信

简介

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
51单片机内部自带UART(Universal Asynchronous Receiver
Transmitter,通用异步收发器),可实现单片机的串口通信。

硬件电路

简单双向串口通信有两根通信线(发送端TXD和接收端RXD)
TXD与RXD要交叉连接
当只需单向的数据传输时,可以直接一根通信线
当电平标准不一致时,需要加电平转换芯片
在这里插入图片描述
TXD(Transmit Exchange Data)

RXD(receive external data)

交叉连接是因为TXD->RXD,延长线不需要交叉

如果两个设备各自都有供电,可以不接VCC

电平标准

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

TTL电平:+5V表示1,0V表示0
RS232电平:-3-15V表示1,+3+15V表示0
RS485电平:两线压差+2+6V表示1,-2-6V表示0(差分信号)

常见通信接口

相关术语

全双工:通信双方可以在同一时刻互相传输数据 半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
单工:通信只能有一方发送到另一方,不能反向传输

异步:通信双方各自约定通信速率 同步:通信双方靠一根时钟线来约定通信速率

总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)

单片机的UART

STC89C52有1个UART
STC89C52的UART有四种工作模式:
模式0:同步移位寄存器
模式1:8位UART,波特率可变(常用)
模式2:9位UART,波特率固定
模式3:9位UART,波特率可变在这里插入图片描述

初始化串口代码

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中断开关(发送时不用)
}

串口参数

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

波特率是用于描述数字通信中数据传输速率的一个重要参数。它表示每秒钟在通信线路上传输的二进制数据位数,受到通信线路的质量、设备的稳定性和调制方式等多种因素的影响。

检验位

校验位又称奇偶校验位(英语:parity bit)是一个表示给定位数的二进制数中1的个数是奇数还是偶数的二进制数。奇偶校验位是最简单的错误检测码。
中文名校验位

串口模式

在这里插入图片描述

SBUF:串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器

串口和中断模式图

在这里插入图片描述
在这里插入图片描述
数据通过总线传输,波特率由定时器T1控制,即在使用串口时需要配置定时器
发送数据:数据(八位)—> 缓存
接收数据:移位寄存器 —> 缓存

六LED点阵屏

简介

LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合,如汽车报站器、广告屏以及公告牌等
LED点阵屏分类
按颜色:单色、双色、全彩
按像素:88、1616等(大规模的LED点阵通常由很多个小点阵拼接而成)

原理

LED点阵屏的结构类似于数码管,只不过是数码管把每一列的像素以“8”字型排列而已
LED点阵屏与数码管一样,有共阴和共阳两种接法,不同的接法对应的电路结构不同
LED点阵屏需要进行逐行或逐列扫描,才能使所有LED同时显示

74HC595

74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展。
在这里插入图片描述

七、DS1302

简介

DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能
RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片
在这里插入图片描述
在这里插入图片描述
数据写入

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>
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        //写入保护
unsigned char DS1302_Time[]={23,12,9,22,25,55,6};
void DS1302_Init(void)
{
	DS1302_CE=0;           //初始化函数要把CE,SCLK置0
	DS1302_SCLK=0;
}
void DS1302_WriteByte(unsigned char Command,Data)
{
	unsigned char i=0;
	DS1302_CE=1;            //开始先把CE拉高到1,但前提是SCLK为0
	for(i=0;i<8;i++)          //先给命令语句指定要接下来存储的寄存器
	{
		DS1302_IO=Command&(0X01<<i); //利用for循环和左移     
		DS1302_SCLK=1;     //写入数据时上升沿有效,所以SCLK先置1,后置0 
		DS1302_SCLK=0;     //这里由于DS1302芯片处理的速度比单片机快,所以这里SCLK两个语句间不用加delay
	}
	for(i=0;i<8;i++)     //接下来写入内容数据
	{
		DS1302_IO=Data&(0X01<<i);//与0x01即提取出数据的第一位(&是有0则0,、|是有1则1)
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;          //结束时CE归0
	
}
unsigned char DS1302_ReadByte(unsigned char Command)   //读函数要返回值
{
	unsigned char i=0,Data=0x00;
	Command|=0x01;      //这里或0x01即把上一个写入函数中的命令的首位0换成1,这样就可以读函数的命令参数共用写函数的
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0X01<<i);
		DS1302_SCLK=0;    //读数据时下降沿有效,所以SCLK先置0后置1
		DS1302_SCLK=1;
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;    //由于读数据时的脉冲为15个比写时少一个周期,所以这里先给SCLK置1,卡住补回一个周期
		DS1302_SCLK=0;
		if(DS1302_IO){Data|=(0X01<<i);}  //这里是判断IO口收到的数据是否为1,为1则写入Data,为0则不变,达到接收数据效果
	}
	DS1302_CE=0;
	return Data;
}
unsigned char BCD(unsigned char DCE)  //DCE转BCD
{
	unsigned char i=0;
	i=DCE/10*16+DCE%10;
	return i;
}
unsigned char DCE(unsigned char BCD)  //BCD转DCE
{
	unsigned char i=0;
	i=BCD/16*10+BCD%16;
	return i;
}
void DS1302_SetTime(void)      //把数组中设置的时间写入交给DS1302
{
	DS1302_WriteByte(DS1302_WP,0x00);       //0x00解除写保护
	DS1302_WriteByte(DS1302_YEAR,BCD(DS1302_Time[0]));    //这里用写入函数传入参数要传16进制数
	DS1302_WriteByte(DS1302_MONTH,BCD(DS1302_Time[1]));
	DS1302_WriteByte(DS1302_DATE,BCD(DS1302_Time[2]));
	DS1302_WriteByte(DS1302_HOUR,BCD(DS1302_Time[3]));
	DS1302_WriteByte(DS1302_MINUTE,BCD(DS1302_Time[4]));
	DS1302_WriteByte(DS1302_SECOND,BCD(DS1302_Time[5]));
	DS1302_WriteByte(DS1302_DAY,BCD(DS1302_Time[6]));
	DS1302_WriteByte(DS1302_WP,0X80);       //0x80开启写保护
}
void DS1302_ReadTime(void)    //读出时间并转化成DCE存入数组中
{
	DS1302_Time[0]=DCE(DS1302_ReadByte(DS1302_YEAR));
	DS1302_Time[1]=DCE(DS1302_ReadByte(DS1302_MONTH));
	DS1302_Time[2]=DCE(DS1302_ReadByte(DS1302_DATE));
	DS1302_Time[3]=DCE(DS1302_ReadByte(DS1302_HOUR));
	DS1302_Time[4]=DCE(DS1302_ReadByte(DS1302_MINUTE));
	DS1302_Time[5]=DCE(DS1302_ReadByte(DS1302_SECOND));
	DS1302_Time[6]=DCE(DS1302_ReadByte(DS1302_DAY));
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值