第三次任务 单片机学习笔记2

1 模块化编程

1.1 什么是模块化编程

模块化编程是指将大型、笨拙的编程任务分解为单独的、更小更易于管理的子任务或模块的过程。然后可以像构建块一样拼凑单个模块以创建更大的应用程序。

1.2 模块化编程的好处

1.模块化代码更易于理解、调试和维护。
2.模块化代码可以提高代码的可重用性和可维护性。
3.模块化代码可以提高代码的可扩展性和可升级性。
4.模块化代码可以提高代码的可靠性和稳定性。

1.3 如何实现模块化编程

首先构建一个c函数文件,一个头文件
在c函数文件中写出函数原型,在头文件中声明

#ifndef __min_H__//这里写函数名(加如是min函数)
#define __min_H__

int min();//这里声明函数


#endif
#inclue "min.h"//如果在main函数文件夹中用双引号,其他最好用<>,放在软件的根目录等地外要添加地址

2 矩阵按键

2.1 什么是矩阵按键

在这里插入图片描述
以上便是矩阵键盘的原理图
P1_0到3是1到4列的IO口,P1_4到7是1到4行的IO口,如果是列扫描P1_0给0,s4按键按下,P1_7检测到0,然后就能执行下一步。通过这种方式检测节约了管脚,使得只需8个管脚便能控制16个按键。

2.2 按键读取方式

方式一
逐行扫描:我们可以通过高四位轮流输出低电平来对矩阵键盘进行逐行扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,然后通过接收到的数据是哪一位为0来判断是哪一个按键被按下。
方法二:
行列扫描:我们可以通过高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下,然后再反过来,高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的值判断是那一行有按键按下,这样就能够确定是哪一个按键按下了。

2.3 驱动设计

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

3 定时器

3.1 CPU时序的有关知识

振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)
状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期。
机器周期:1个机器周期含6个状态周期,12个振荡周期。
指令周期:完成1条指令所占用的全部时间,它以机器周期为单位。
下面给个例子
例如:外接晶振为12MHz时,51单片机相关周期的具体值为:

振荡周期=1/12us;
状态周期=1/6us;
机器周期=1us;
指令周期=1~4us;

3.2 定时器工作原理

定时/计数器实质上是一个加1计数器。它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自动加1,,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置1,向CPU发出中断请求(定时/计数器中断允许时)。如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。
内部结构图
在这里插入图片描述
下面两个灰矩形是TCON是控制寄存器,控制T0、T1的启动和停止及设置溢出标志。
下面是其原理图
在这里插入图片描述
首先可以看到其字节地址为89H,低四位用于T0,高四位用于T1

  1. GATE是门控位, GATE=0时,用于控制定时器的启动是否受外部中断源信号的影响。GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作。
  2. C/T表示置1为计数器,置0为计时器
  3. M1M0:工作方式设置位。定时/计数器有四种工作方式。看下图详解,一般我们用的是01模式
    在这里插入图片描述
    模式01
    在这里插入图片描述
    这里我也想介绍一下与或非门
    与门:有0置0,只有输入1,1会输出1
    或门:有1置1,只有输入0,0会输出0
    非门:非门的逻辑功能是对输入信号进行取反,即当输入为1时,输出为0,输入为0时,输出为1。
    于是我们可以这样写代码保证某个定时器正常运转
TMOD &= 0xF0;        //设置定时器模式(低4位清0,高四位不变)
TMOD |= 0x01;        //设置定时器模式(最低位置1,高四位不变)

下面是中断结构,中断信号从定时器发出后到T0口,开始配置中断优先级
在这里插入图片描述
所以ET0要置1,EA置1,PT0置0或1自行选择,置1为高优先级,置0为低优先级(上面的图连错了,最后应该连到下面)

3.3 总的定时器初始化代码

void Timer0Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;         //其实上面的可以用STC来生成,我们只需配置下面代码即可
	EA=1;
	PT0=0;
}

使用STC生成定时器初始化代码
在这里插入图片描述

4 串口

4.1 什么是串口

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。
通信方式
单工是指数据传输仅能沿一个方向,不能实现反向传输。
半双工是指数据传输可以沿两个方向,但需要分时进行。
全双工是指数据可以同时进行双向传输。

4.2 工作原理

在这里插入图片描述
VCC意思是正极电源电压,GND意思是地线或0线

  1. 简单双向串口通信有两根通信线(发送端TXD和接收端RXD)
  2. TXD与RXD交叉连接
    配置寄存器
    在这里插入图片描述
    在这里插入图片描述
    串行口工作之前,应对其进行初始化,主要是设置产
    生波特率的定时器1、串行口控制和中断控制。具体
    步骤如下:
    确定T1的工作方式(编程TMOD寄存器);

TI:发送中断标志位。在方式 0 时,当串行发送第 8 位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使 TI 置 1,向 CPU 发中断申请。 在中断服务程序中,必须用软件将其清 0,取消此中断申请.

计算T1的初值,装载TH1、TL1;
启动T1(编程TCON中的TR1位);
确定串行口控制(编程SCON寄存器);
串行口在中断方式工作时,要进行中断设置(编程IE、
IP寄存器)。

4.3 接收,输出函数

初始化函数

void UART_Init()
{
	SCON=0x50;
	PCON |= 0x80;
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xF3;		//设定定时初值
	TH1 = 0xF3;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
	EA=1;
	ES=1;
}

发送函数

void UART_SendByte(unsigned char Byte)
{
	SBUF=Byte;
	while(TI==0);
	TI=0;
}

串口中断函数模板

void UART_Routine() interrupt 4
{
	if(RI==1)
	{
		
		RI=0;
	}
}

尝试

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

void main()
{
	UART_Init();		//串口初始化
	while(1)
	{
		
	}
}

void UART_Routine() interrupt 4
{
	if(RI==1)					//如果接收标志位为1,接收到了数据
	{
		P2=~SBUF;				//读取数据,取反后输出到LED
		UART_SendByte(SBUF);	//将受到的数据发回串口
		RI=0;					//接收标志位清0
	}
}

5 LED点阵屏

5.1 什么是LED点阵屏

在这里插入图片描述
某一行置 1 电平,某一列置 0 电平,则相应的二极管就亮,通过此种方式使得某一个二极管点亮

5.2 工作原理

首先LED点阵屏那么多I/O口,不可能全都接到CPU上,那么我们是怎么解决的呢?
在这里插入图片描述
上面的74HC595 P3_4到6连接到了CPU
同时分别连接到了RCLK,SRCLK,SER口
在这里插入图片描述
左侧矩形为移位寄存器,SER输出1时,当SERCLK达到上升沿时(即1),向下推移一个数字,当八个数字储存完毕,RCLK上升沿锁位打开,八位数字同时进入右侧矩形。
然后控制LED点阵屏

5.3 代码实例

LED显示图形

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

sbit RCK=P3^5;		//RCLK
sbit SCK=P3^6;		//SRCLK
sbit SER=P3^4;		//SER

#define MATRIX_LED_PORT		P0

/**
  * @brief  74HC595写入一个字节
  * @param  Byte 要写入的字节
  * @retval 无
  */
void _74HC595_WriteByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		SER=Byte&(0x80>>i);
		SCK=1;
		SCK=0;
	}
	RCK=1;
	RCK=0;
}

/**
  * @brief  LED点阵屏显示一列数据
  * @param  Column 要选择的列,范围:0~7,0在最左边
  * @param  Data 选择列显示的数据,高位在上,1为亮,0为灭
  * @retval 无
  */
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
	_74HC595_WriteByte(Data);
	MATRIX_LED_PORT=~(0x80>>Column);
	Delay(1);
	MATRIX_LED_PORT=0xFF;
}

void main()
{
	SCK=0;
	RCK=0;
	while(1)
	{
		MatrixLED_ShowColumn(0,0x3C);
		MatrixLED_ShowColumn(1,0x42);
		MatrixLED_ShowColumn(2,0xA9);
		MatrixLED_ShowColumn(3,0x85);
		MatrixLED_ShowColumn(4,0x85);
		MatrixLED_ShowColumn(5,0xA9);
		MatrixLED_ShowColumn(6,0x42);
		MatrixLED_ShowColumn(7,0x3C);
	}
}

LED点阵屏显示动画

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

//动画数据
unsigned char code Animation[]={
	0x3C,0x42,0xA9,0x85,0x85,0xA9,0x42,0x3C,
	0x3C,0x42,0xA1,0x85,0x85,0xA1,0x42,0x3C,
	0x3C,0x42,0xA5,0x89,0x89,0xA5,0x42,0x3C,
};

void main()
{
	unsigned char i,Offset=0,Count=0;
	MatrixLED_Init();
	while(1)
	{
		for(i=0;i<8;i++)	//循环8次,显示8列数据
		{
			MatrixLED_ShowColumn(i,Animation[i+Offset]);
		}
		Count++;			//计次延时
		if(Count>15)
		{
			Count=0;
			Offset+=8;		//偏移+8,切换下一帧画面
			if(Offset>16)
			{
				Offset=0;
			}
		}
	}
}

6 DS1302时钟

6.1 什么是DS1302时钟

DS1302是一款由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它能够对年、月、日、周、时、分、秒进行计时,并且具有闰年补偿等多种功能。DS1302采用串行数据传输,并采用32.768kHz晶振

6.2 工作原理

下面是其控制的寄存器第0位为读写,0为写,1为读
第1到6位为时间寄存器地址,第七位不用管
在这里插入图片描述
下图为其时分秒…寄存器地址
在这里插入图片描述
BCD码
在这里插入图片描述
DS1302中的时间是以16进制储存的,所以要记得把它转化为10进制,
转换方式为:(16进制数)a/16*10+a%16

知道寄存器后那么读写原理是什么呢?
看下图
在这里插入图片描述
首先要初始化,使能CE置0,SCLK置0
接着看IO口上,前八位是控制寄存器的16进制数,而后8位为数据

  1. 单字节写入:在CE为高电平后,SCLK会输出周期脉冲,每一个上升沿,IO线的数据就会进入控制寄存器,当控制寄存器配置完成(为写入数据,并且地址已给),紧接着的脉冲IO线的数据就会在上升沿进入对应地址的寄存器;(上升沿读入)
    写入函数
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;
}
  1. 单字节读出:在CE为高电平后,SCLK会输出周期脉冲,每一个上升沿,IO线的数据就会进入控制寄存器,当控制寄存器配置完成(为读出数据,并且地址已给),紧接着对应地址的寄存器的数据就会在下降沿进入IO线;(下降沿读出)(注意读出的只有15个震荡,所以第8此读出时应保持SCLK为高电平,然后读出时才高低电平利用下降沿读出,否则会出现读出数据错误)
    读出函数
/**
  * @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;
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;
		DS1302_SCLK=0;
		if(DS1302_IO){Data|=(0x01<<i);}
	}
	DS1302_CE=0;
	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
	return Data;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lisabier

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

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

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

打赏作者

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

抵扣说明:

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

余额充值