前言
编程软件:keil
烧录软件:普中isp
开发板:普中A2
若干所需元器件:面包板 杜邦线 引脚式的LED灯 5V电源 220Ω的电阻
单片机:STC89c52rc
本文需要有一定的c语言基础(入门即可) 和 对电子元器件有基础认识(知道什么是电阻就行了)还有一些电路知识(知道电流方向 知道电子元器件的正负极 )
还有本文大部分图片都来自网络
关于单片机
在初学单片机时,没必要深学原理,倒不是原理不重要,只是微机原理在很多课程老师都讲不清楚,入门学时应该重视实践,但也需要知道其中部分工作原理以及简单理论,像是了解心脏一样,知道其基本功能是泵血,通过收缩和舒张推动血液循环,向身体各部位输送氧气和营养物质,同时清除代谢废物,就可以了,至于他的内部为四个腔室:左心房、左心室、右心房和右心室,哪一个区域负责什么样的工作,在深入学前不应该探究,这一部分留给更专业的书籍和老师。
什么是单片机?
单片机是一块集成芯片集成了CPU、RAM、ROM、IO口输入输出设备、定时器、中断系统、通信接口等,由一系列构成了一台极小的计算机,好那么什么是单片机……
这条四四方方的蜈蚣是单片机,有好多好多个脚
这是开发板有好多好多个模块但缺少一个控制者,他是stc89c52rc单片机的坐骑
为什么叫51不叫61
51单片机是对所有兼容Intel8031指令系统的单片机的统称。有很多公司 做单片机 有很多型号:
Intel(英特尔)的180C31、i80C51、i87C51,i80C32、80C52、i87C52等;
ATMEL(艾德梅尔)的:AT89C51、AT89C52、AT89C2051,AT89S51(RC),AT89S52 (RC)
Philips(6利浦)、华邦、Dallas(达拉斯)、Siemens(西门子等公司的许多产品;
STC(国产宏晶)单片机:STC89C51、STC89C52、STC89C516、STC90C516等众多品牌。
我们使用的是stc89c52rc单片机
stc89家族的命名方式如下:
该系列单片机的命名规则通常遵循以上格式,所以由图可知:
STC
:表示该单片机由STC公司生产。89
:这个数字表明单片机是兼容8051指令集的,即属于51系列单片机。C
:表示单片机的工作电压范围,例如5.5V至3.3V。52
:表示单片机的程序空间大小为8KB。RC
:表示单片机的RAM容量为512B。40
:表示单片机的最高工作频率为40MHz。I
:表示单片机能在工业级温度范围内工作(-40℃至85℃)。PDIP
:表示单片机的封装类型为DIP(双列直插封装),后面的数字表示管脚数量。
性能可谓是拉跨,但实在入门首选,主要是便宜耐造.
不知道是不是记忆错乱,我怎么记得stc89c52rc的rc是带有内部rc振荡电路的意思,但图片有别的解释,不过也不影响我们也用不到
针脚以及功能介绍
上面也说到了这款STC89C52RC单片机一共有40个针脚简要功能概述功能如下:
电源和时钟引脚:
- VCC(引脚40):连接正电源电压,通常为5V。
- GND(引脚20):接地引脚。
- XTAL1/XTAL2(引脚18和19):用于连接外部晶振,形成时钟信号。
硬件控制引脚:
- RST(引脚9):复位引脚,高电平有效,用于重置单片机。
- EA(引脚31):程序存储器选择引脚,决定是否启用外部程序存储器。
- PSEN(引脚30):外部程序存储器使能信号输出引脚。
- ALE(引脚29):地址锁存允许信号输出引脚。
在初步学习阶段,知道有,干什么用就好了
通用输入输出(I/O)引脚: - P0、P1、P2、P3(引脚0-7、10-17、21-28、32-39):分别为四组I/O端口,其中P0为漏极开路输出,P1、P2、P3为准双向口/弱上拉。
接下来来看一下普中A2开发板带的原理图
哇 好多脚啊 不过我们目前知道除去一些功能性引脚外,剩下带数字的引脚大致可分为P0、P1、P2、P3这几个是通用I/O引脚,这些引脚与外界相连即可,这便是成为点灯大师的第一步
最小应用系统
点亮一个led灯只需要电源电阻led灯相互连接就好了如下
连接一个十分有十二分的简单 但如果要连接多个还要实现控制呢。那单纯使用电路系统便十分麻烦,哎 —— 这时候我便可以把单片机的最小应用系统引用进来,
古书有云:“若是要讲单片机,那便不能只讲单片机,还要讲时钟电路、复位电路、电源电路”
这张图是AT89S51单片机的 参考一些外围电路就行了
其中最基础的便是 单片机芯片:作为系统的核心,负责执行程序和控制整个系统的运作。
其次是时钟电路:由晶振(晶体振荡器)和电容组成,提供单片机所需的时钟信号。他能提供单片机内部所有操作的时序基础, 确保了指令的正确执行和数据处理的同步性。没有稳定的时钟信号,单片机将无法正常执行程序和控制外设。
复位电路:确保单片机在上电或复位信号作用下能够正确初始化。 开发板上单片机旁边有一个红色的按钮按一下烧录在单片机里面的程序就开始重新执行了
电源电路:为单片机及其外围电路提供稳定的工作电压。这位才是重中之重,这才是没他啥都跑不了
这些组成部分共同构成了51单片机的最小硬件环境,使单片机能够执行基本的功能和任务在开发板上的实现
我们先来说一下时钟电路的实现
上面在介绍引脚的时候也说了 XTAL1/XTAL2(引脚18和19):用于连接外部晶振,形成时钟信号
其中
- GND是接地
- Y2便是晶振元件 旁边那个12MHz是晶振频率意味着该晶振能够以12兆赫兹(MHz)的频率振荡。这个频率是晶振的固有属性,决定了单片机能以多快的速度执行指令和处理数据。
C12和C13是电容器
接下来是复位电路
复位电路接接在stc89c52rc上的RST引脚上 提供了一个按钮连接到RST低电平有效的。这意味着,当RST引脚检测到低电平时,单片机会被置于复位状态。在复位电路中,通常会通过一个上拉电阻和一个按键(或开关)配合电容来实现复位逻辑,确保在上电或手动按下复位按钮时,RST引脚能够被拉至低电平,从而触发复位过程.
c14在VCC和GND中间提供了一个10uf的去耦电容,用于滤除电源线上的噪声,确保单片机稳定供电。
剩下那个电源电路就不讲了
点亮一个51单片机 LED灯
在点亮一个电容之前我们先来点亮一个LED灯,在点亮一个LED灯之前我们先来看一下普中A2开发板的LED模块原理图
在以上图片我们看到VCC是被连接到一起的共阳极,而负极是被分别连接到P20~P23、P24 ~ P27的
所以我们要点亮某一个LED应该要让指定的端口为低电平也就是要让指定的端口置零,因为共阳极所以低电平有效
不过……
在亮灯之前我们先来看看单片机核心模块与LED相连的端口
在图中我们可以看到与LED模块相连的P20~P23、P24 ~ P27端口 ,我们只需要控制指定端口置零便能控制指定端口亮灭
我们来试试看(哎呀 有种翻牌的快感 让我想想要决定要让哪一个灯亮
我们来亮个D1举个例子
参考上一篇教程创建一个keil项目51单片机教程前导
#include <REGX52.H>
void main()
{
P2_0 = 0;
while(1);
}
不出意外的话,第一个灯应该已经亮起了
关于烧录软件的话我选择普中的ISP关于具体的所以方法51单片机教程前导也有介绍到
那么一个灯可以的话两个三个呢,要不我们让他隔空亮吧
#include <REGX52.H>
void main()
{
P2_0 = 0;
P2_2 = 0;
P2_4 = 0;
P2_6 = 0;
while(1);
}
如图可以实现,可能你到现在还有点摸不着头脑,但你一定以及发现了代码中的P2_0对应着单片机中的P20接口依此类推P2_6对应着单片机中的P26接口.
我们来看看代码是如何实现的……
我们在keil里面右键打开<REGX52.H>头文件,这是一个针对特定微控制器架构的宏定义和寄存器常量定义文件。在51系列单片机的编程中,这个头文件可以提供便利的编程接口,打开后往下话找到
/*------------------------------------------------
P2 Bit Registers
------------------------------------------------*/
sbit P2_0 = 0xA0;
sbit P2_1 = 0xA1;
sbit P2_2 = 0xA2;
sbit P2_3 = 0xA3;
sbit P2_4 = 0xA4;
sbit P2_5 = 0xA5;
sbit P2_6 = 0xA6;
sbit P2_7 = 0xA7;
这是P2位寄存器不难发现代码与单片机端口的对应关系,我们再往上翻一翻还能看到一下内容
/*------------------------------------------------
Byte Registers
------------------------------------------------*/
sfr P0 = 0x80;
sfr SP = 0x81;
sfr DPL = 0x82;
sfr DPH = 0x83;
sfr PCON = 0x87;
sfr TCON = 0x88;
sfr TMOD = 0x89;
sfr TL0 = 0x8A;
sfr TL1 = 0x8B;
sfr TH0 = 0x8C;
sfr TH1 = 0x8D;
sfr P1 = 0x90;
sfr SCON = 0x98;
sfr SBUF = 0x99;
sfr P2 = 0xA0;
sfr IE = 0xA8;
sfr P3 = 0xB0;
sfr IP = 0xB8;
sfr T2CON = 0xC8;
sfr T2MOD = 0xC9;
sfr RCAP2L = 0xCA;
sfr RCAP2H = 0xCB;
sfr TL2 = 0xCC;
sfr TH2 = 0xCD;
sfr PSW = 0xD0;
sfr ACC = 0xE0;
sfr B = 0xF0;
这是字节寄存器,在其中也能发现P2的身影,这个P2也能用来操控LED模块吗?
答案是可以的我们给他赋值个1看看会有什么效果。
#include <REGX52.H>
void main()
{
P2 = 1;
while(1);
}
嘢诶,能亮说明能提供P2来操控LED模块。想必你已经发现,P2通常代表整个P2端口的寄存器,而P2_后面跟着的数字(如P2_0、P2_1等)代表P2端口中的单个引脚。
那如何通过P2来控制指定的LED灯呢
#include <REGX52.H>
void main()
{
P2 = 0xfe;
while(1);
}
还可以给P2赋的值不止0和1,我们可以通过十六进制(前缀是0x)
0xfe的二进制数是 1111 1110 他是倒着读的所以是第一个亮
所以我们要是要让其他灯亮只需要让指定的Bit 位置零就好了,我们来试试看让他隔空亮,所以他的二进制数应该是1010 1010把他转换成十六进制就是0xAA(怎么算的呢 当然是计算机啦
我们来试试看
#include <REGX52.H>
void main()
{
P2 = 0xAA;
while(1);
}
不出意外的话,效果应该和上面的隔空亮一样
它亮太久了 想让他休息一下 让他暗下来呢
根据二极管思维既然是低电平亮那么高电平就是暗,试试看
先把P24端口输出低电平让第五个LED灯亮吧
#include <REGX52.H>
void main()
{
P2_4 = 0;
while(1);
}
再次代码上我们再给把他放到while循环里反复运行,继续赋值1输出高电平 让他在亮后灭 实现 闪
#include <REGX52.H>
void main()
{
while(1){
P2_4 = 0;
P2_4 = 1;
};
}
这时把他编译后烧录到单片机里会看到他还是一直亮的 理论通 可是为什么呢
延时
人类还是太弱了 居然连这速度都跟不上
以上代码实现了普中A2开发板上的LED模块闪,但是速度太快了视觉残留 ,看起来就像是一直亮着,所以我们只需要想办法让他的运行间隙时间长一些就好了。
程序指令的执行需要时间,可以使用一些无效的循环来浪费它
如下:
#include <REGX52.H>
void Dy(int x);
void main()
{
while(1){
Dy(10000);
P2_4 = 1;
Dy(10000);
P2_4 = 0;
Dy(10000);
};
}
void Dy(int x){
while(x){
x--;
}
}
定义了一个while
循环接受外部传入的整型变量 在每循环一次便将变量减1知道传入的值变成0便跳出该循环再继续执行其他代码,这便实现了基础的延时执行,不过仍然剩下许多问题,比如延时时间不够精确、硬件开销大等问题
我们下面介绍两种单片机的延时方法
分别是基于循环计数的软件延时,还有是基于的定时器的延时
不过在讲延时之前需要来补充点微机原理
时钟周期 = 震荡周期 = 1/晶振频率
我的这个开发板上的晶振频率是11.0592Mhz
所以是1 / 11.0592Mhz
机器周期是12 * 时钟周期 = 12 * 1/11.0592Mhz
这个12是针对于这款芯片的
基于循环计数的软件延时
如果你手头有stcisp这个软件里面有一个延时函数生成他是基于循环计数的软件延时,我们来一起看一下他生成的c语言代码。
先把他的系统频率设置成为和我们开发板上晶振一样的,我的是11.0592Mhz,然后设置他的定时长度为1毫秒,以及他的8051指令集设置为STC - Y1,如图
我们来解析一下代码
这段代码定义了一个名为Delay1ms
的函数,其目的是在STC系列单片机上产生大约1毫秒的延时。让我们逐行解析这个函数:
void Delay1ms() //@11.0592MHz
{
unsigned char i, j;
void Delay1ms()
声明了一个不返回任何值的函数Delay1ms
。unsigned char i, j;
声明了两个无符号字符型变量i
和j
。在8位单片机中,unsigned char
类型通常用来做计数器,因为它提供了0到255的范围,足以满足大部分计数需求,同时占用较小的内存。
_nop_();
i = 2;
j = 199;
_nop_()
是一个空操作指令,通常用于微控制器编程中,用于产生延时或用于调试。在这个函数中,_nop_()
可能用于确保i
和j
的初始化在指令流水线上正确执行。i = 2;
和j = 199;
初始化了计数变量i
和j
,i
被设置为2,j
被设置为199。
do
{
while (--j);
} while (--i);
}
do {...} while (--i);
是循环结构的一部分,它将至少执行一次循环体,然后检查i
的值是否非零,如果非零则继续循环。i
在每次循环开始时递减。while (--j);
是一个内部循环,它会递减j
的值直到j
变为0,j
的值在每次循环开始时递减。这个循环是主要的延时生成部分。
整个Delay1ms
函数通过嵌套的while
循环实现延时,内部循环j
的递减次数和外部循环i
的递减次数共同决定了延时的长短。这种延时方法是基于循环计数实现的,其延时精度受到单片机时钟频率和指令执行时间的影响。在11.0592MHz晶振下,通过调整i
和j
的值,可以大致实现1毫秒的延时。但是,这种方法的延时精度较低,容易受到系统负载变化的影响,不适合需要高精度延时的场合。
在这里要注意的是在对于无符号的char类进行–操作的话,在减到0后面不是负数而是2^8-1 == 255
还有在_nop();时应该声明其头文件
#include <intrins.h>
还有还有函数调用时需要消耗大概4个机器周期
但这个延时函数是写死的,我们可以通过传参来控制延时时间,如下:
void delay_ms(unsigned int t){ //延时函数
unsigned int x, y;
for(x=t; x>0; x--){
for(y=110; y>0; y--){}
}
}
你别玩得太花
差不多到这里告一段落 ,关于定时器我会再另写一篇博客
现在你A2开发板中LED模块的神了,
你可以控制亮灭,指定那个亮那个灭,甚至你还领悟到到时间属性可以不精确的控制时间,但也够用了
以及 我新建立的订阅号(E.F的杂笔)劳烦关注