51单片机学习笔记(零基础版)

引言

本笔记跟随B站经典课程江科大51单片机入门教程,对课程内容进行了一些细化,兼顾零基础的同学,并且按照理论结合实践的学习理念,学习就是一个发现问题,解决问题,获得技能的一个过程。力求成为一名嵌入式开发工程师,并且从这个课程中学习到在嵌入式开发的工作中需要用的知识以及技能,仅围绕51单片机展开,着重在原理图分析以及代码编写上下功夫,这两块需要细化。

学习目标:

  • 认识集成电路开发芯片
  • 对嵌入式系统开发进行初步的认知
  • 认识嵌入式开发的基本用词
  • MCU/RAM/ROM/IO引脚/中断器/定时器(集成在单片机开发板上的器件)
  • 看懂电路图,认识电路图元器件的符号
  • 熟练使用keil5以及stc-isp等开发工具
  • 会熟练使用C语言/汇编语言进行C51单片机程序编程

​​​​​​开发工具介绍

Keil5

软件介绍

997b1156411148f48558b929b3f06283.png

说明:

        Keil C51美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,它是一款嵌入式开发工具,主要用于开发和调试嵌入式系统的软件。Keil5集成了多个功能模块,包括代码编辑器、编译器、调试器等,使得嵌入式软件开发过程更加高效和方便。

主要特点和功能包括:

  1. 集成开发环境:Keil5提供了一个集成的开发环境,方便开发人员在一个界面中进行代码编写、编译、调试和测试。

  2. 支持多种芯片:Keil5支持多种不同厂家的嵌入式芯片,包括各种常见的ARM和Cortex系列处理器,使得开发人员可以在一个环境下集中对多种芯片进行开发。

  3. 实时调试功能:Keil5提供了实时调试功能,能够在硬件上实时监控程序运行状态,快速定位和解决问题。

  4. 支持多种编程语言:Keil5支持多种编程语言,包括C和汇编语言,开发人员可以根据需求选择适合的语言进行编程。

  5. 优秀的性能和稳定性:Keil5具有优秀的性能和稳定性,能够满足嵌入式软件开发的需求,提高开发效率。

         总的来说,Keil5是一款功能强大的嵌入式开发工具,被广泛应用于各种嵌入式系统的开发和调试过程中。它能够帮助开发人员快速、高效地开发出稳定可靠的嵌入式软件。

Stc-isp

软件介绍

76365e1312fd4639a73725fe2e556960.png

 说明:

          STC-ISPSTC系列单片机的编程工具,用于将编写好的程序下载到单片机中进行烧录和调试。它可以通过串口或USB接口与单片机连接,实现程序的下载和调试功能。

在单片机项目中,STC-ISP的作用主要包括以下几点:

  1. 程序下载:STC-ISP可以将编写好的程序烧录到单片机内部的Flash存储器中,让单片机能够执行程序逻辑。
  2. 调试功能:STC-ISP可以用于单片机程序的调试,例如单步调试、断点设定等操作,帮助开发人员快速定位和解决程序中的问题
  3. 擦除:STC-ISP也支持对单片机内部存储器的擦除操作,以便重新烧录新的程序。

在实际的单片机开发工作中:

      使用STC-ISP的频率取决于项目的具体需求和开发进程。一般来说,在项目的初期和开发阶段,开发人员会频繁使用STC-ISP进行程序的下载和调试。随着项目的进展和稳定性提高,对STC-ISP的使用会逐渐减少。然而,在需要更新程序、调试bug或验证新功能时,STC-ISP仍然是一个重要的工具。

       因此,虽然在实际的开发工作中可能不是每时每刻都使用STC-ISP,但它作为单片机项目中不可或缺的编程工具,仍然是开发过程中必不可少的一部分。

单片机介绍

51单片机(也称为8051单片机)是一种广泛使用的微控制器,主要用于嵌入式系统中。其任务和功能包括:

  1. 数据处理:就是里面的CPU芯片能够完成基本的数据运算和逻辑操作,包括加、减、乘、除、与、或、非等运算。

  2. 输入/输出控制:通过端口(变量名)对外部设备进行输入和输出控制,例如读取传感器数据或控制电机、LED等。

  3. 定时和计数:内置定时器可用于精确地执行任务、计时或事件计数。

  4. 中断处理:支持外部和内部中断,可以在特定事件发生时快速响应,提高系统的实时性。

  5. 串口通信:支持串行通信协议(串行通信:数据位一个接一个地按顺序传输。同一时间只传输一位数据,通常通过单一的数据通道进行。并行通信:多个数据位同时传输。使用多条数据线同时传输, 并行地传送几个数据位)可以与其他设备或者计算机进行数据传输。

  6. 简单控制系统:可以用于开发简单的控制系统,如温度控制、灯光控制、自动化设备等。

  7. 嵌入式应用:被广泛应用于家用电器、工业自动化、医疗设备、汽车电子等多个领域。

总之,51单片机的任务是处理和控制与其它设备的交互,执行编程指令,从而实现各种应用的目的。

单片机(Microcontroller)的结构组成通常包括以下几个主要部分:

  1. 中央处理器 (CPU)

    • 负责执行指令和处理数据。它是单片机的核心部件,通常包含算术逻辑单元(ALU)、控制单元和寄存器等。
  2. 内存

    • 程序存储器 (ROM):用于存储程序代码,通常是不可更改的只读内存(如EPROM、Flash)。
    • 数据存储器 (RAM):用于存储数据和临时变量,具有读写功能,通常是易失性内存。
  3. 输入/输出端口 (I/O Ports)

    • 用于与外部设备进行数据交换。可以连接传感器、执行器、显示器等硬件,通常有多个通用的I/O端口。
  4. 定时器/计数器

    • 用于生成时间延迟,计时,计数事件等功能,广泛用于控制和精确调度。
  5. 中断系统

    • 提供中断处理能力,使单片机能够快速响应外部事件。这包括外部中断和内部中断。
  6. 串行通信接口

    • 用于与其他设备进行串行数据传输,通常包括UART、SPI、I2C等协议的实现。
  7. 时钟电路

    • 提供时钟信号,为CPU和其他部件提供时序控制,通常由外部晶振或内部振荡器提供。
  8. 辅助模块

    • 这些模块的功能因单片机型号和厂家而异,可能包括模数转换器(ADC)、数模转换器(DAC)、PWM产生器等。

示例结构

      假设一个典型的8051单片机,它的组成部分可能类似于以下结构:

  • CPU:处理器核心
  • ROM:可编程只读存储器
  • RAM:静态随机存取存储器
  • I/O 口:多个可编程的I/O 端口
  • 定时器/计数器:通常有两个
  • 中断系统:支持多种中断类型
  • 串口通信:支持UART通信

以上组成部分共同工作,实现了单片机进行各种控制和处理任务的能力,适用于不同的嵌入式应用。

项目一

名称:LED灯点亮

电路图展示: 

 ed13ae482aad486db6d4a46ae11c26dd.png

LED模块电路图理解:

  1. 单片机端口:

    • P20/SDA: 连接到D1(绿色二极管)。
    • P21/SCL: 连接到D2(绿色二极管)。
    • P22: 连接到D3(绿色二极管)。
    • P23: 连接到D4(绿色二极管)。
    • P24: 连接到D5(绿色二极管)。
    • P25/WR: 连接到D6(绿色二极管)。
    • P26/RD: 连接到D7(绿色二极管)。
    • P27/LCDE: 连接到D8(绿色二极管)。
  2. 电阻器网络:

    • RP7 和 RP9 是两个电阻器网络,每个都有四个引脚:1K、2、3、4、5、6、7、8。它们可能用于为不同的信号路径提供合适的阻抗匹配或电流限制。
  3. 电源线:

    • VCC 表示正电压供应点,通常在电子电路中表示电源的正极。
  4. 其他符号:

    • 绿色三角形表示二极管,这里可能是用来指示信号的流向或者是保护二极管。
    • 蓝色线条代表导线或线路,连接着不同的元件。

 代码模块:

#include <REG51.H>  
int main() {
    P2 = 0xFE;
	}

代码解释:

#include <REG51.H>       

表示包含51单片机的寄存器定义,P2这个变量就是在这个头文件里面进行定义的,也就是说有了这个头文件,程序代码中的P2就可以用来设置数值来控制单片机,单片机再来控制小灯点亮了。

 P2 = 0xFE;

0x代表数据是16进制的,因为C语言不支持直接用二进制写出数据
然后十六进制易于转化,且很多指令集都是十六进制表示出来的,可以说这就是一个规范了
将P1端口的所有引脚设为低(0V),所有灯亮


单片机上面有一个端口叫P2是专门连接LED模块进行数据的输入输出的
由P2端口生成出了8个引脚,分别是P20,P21...P27.

 

点亮一个LED灯的原理就是设置这些引脚的高低电平,VCC正极提供了
5V的高电平,所以你设置的端口的值就是要低电平就能实现电路的流通,从而实现点亮灯
P1这个端口有8个引脚,设置每一个引脚的高低电平就能实现你想要的灯的亮与灭  
高电平用1表示,低电平用0表示即可

 

我们在写代码的时候是直接与单片机进行交互的,所以我们直接将端口作为变量进行数据的设置
也就是通过0和1是实现了对引脚电平的控制

物理原理:

点亮一个LED灯的电路原理可以简单概括为以下几点:

  1. 电源:LED灯需要一个适当的电源(如电池或直流电源)来提供电压和电流。

  2. 正向电流:LED是一种二极管,只允许电流在一个方向流动。连接时要确保其正极(阳极)接到电源的正极,负极(阴极)接到电源的负极。

  3. 限流电阻:为了保护LED,防止电流过大导致损坏,通常在电路中串联一个限流电阻。该电阻的值根据LED的工作电压和电流来选定。

  4. 电流通过:当电源接通,电流从电源流入LED,经过限流电阻后,形成闭合电路,LED被激活并发光。

  5. 呈现光效:电流通过LED时,LED内部的电子与空穴复合,释放出能量,以光的形式表现出来,从而点亮LED。

P27P26P25P24P23P22P21P20

 上面就是引脚示意图:

                     你在编程时就设置引脚的高低电平就行了,代码P2=0xFE的意思就是设置为二进制的P2=11111110,也就是P20引脚是低电平,只有那个引脚连接的LED灯会亮,就这么直白。 

考题:

1:编程实现LED灯都亮

2:编程实现LED灯隔一个就亮,并且第一个灯要亮 

答案: 

#include <REG51.H>  

int main() {
    P2 = 0x00;
	    }

 P2 = 0000 0000;

就是说所有引脚低,也就是都亮

#include <REG51.H>  
int main() {
    P2 = 0xAA;
    }

 P2 = 1010 1010;

也就是隔一个灯就亮

项目二

LED流水灯

 原理解释:

灯亮与灭交替进行就能形成闪烁的效果,但是由于两者之间相差的时间太短,需要加入延时函数进行时间的限制,从而明显实现闪烁的效果,闪烁了之后进行一次闪烁就能实现流水灯效果。

#include <REGX52.H>
void Delayxms(unsigned int xms)		
{
	unsigned char  j, k;
while(xms){	
	j = 2;
	k = 239;
		do
		{
			while (--k);
		} while (--j);
	xms--;
}
	}//延时函数的原理就是不断地进行数据的加减来占用CPU的资源
     //这将使得CPU将过一段时间再进行下一条指令的执行
int main(){
	int i;
	while(1){
	for(i=1;i<129;i*=2){
		P2=0xFF;
		Delayxms(300);
//灯灭,延时300毫秒,300毫秒时间内灯一直灭着
	  P2=P2-i;
//灯亮,延时300毫秒进行下一次灯亮,你在进行延时函数语句计算的时候,数据状态仍然处在上一句
//上一句语句就是灯亮,所以灯在这段时间一直亮着呗,等着下一条语句执行完成之后数据又被执行了P2=0xFF
//这时候灯又一次熄灭了
		Delayxms(300);
	}
    //根据下面的二进制数据图我们可以知道让不同的灯亮起再熄灭就是一个循环递增的过程
    //二进制中的数据0会不断地向左移动(其实你也可以设置向右,改数据而已)
    //所谓不断的向右其实就是十进制形式下的数据不断的进行-2^i的过程
}
}

 控制不同的灯的亮灭的二进制数据展示:

d5157f9037554bdca492ab1066cf40e5.png

 C51里面的数据类型与C语言规范有一些小的差距,因为单片机本身的算力和电脑CPU存在差距,所以设置数据的应当注意,下图是C51的数据类型展示:

e77637f73b084360ba1cab2c77406b35.png

考题:

一阶二阶三阶流水灯问题:

编程实现第一次灯是一个灯依次闪烁形成流水灯,第二次是两个灯依次闪烁形成流水灯,第三次是四个灯依次闪烁形成流水灯

 答案如下:

#include <REGX52.H>
void Delayxms(unsigned int xms)
{
	unsigned char  j, k;
while(xms){	
	j = 2;
	k = 239;
		do
		{
			while (--k);
		} while (--j);
	xms--;
}
	}
int main(){
	int i,a,b;
	while(1){
	for(i=1;i<129;i*=2){
		P2=0xFF;
		Delayxms(300);
	  P2=P2-i;
		Delayxms(300);
	}
	for(a=3;a<193;a*=4){
		P2=0xFF;
		Delayxms(300);
	  P2=P2-a;
		Delayxms(300);
}
	for(b=15;b<300;b*=16){
		P2=0xFF;
		Delayxms(300);
	  P2=P2-b;
		Delayxms(300);
}
	}
}

项目三

 按键控制LED灯的状态:

代码:

#include <REGX52.H>
void Delayxms(unsigned int xms)
{
	unsigned char  j, k;
while(xms){	
	j = 2;
	k = 239;
		do
		{
			while (--k);
		} while (--j);
	xms--;
}
	}
int main(){
	if(P3_1==0){
		Delayxms(20);
		while(P3_1==0);
		Delayxms(20);
		P2_0=~P2_0;
	}
}

按键实现LED灯控制原理解释:

K1按键按下的时候就是对应代码的P3_0这个变量变成了0

K2按键松开的时候就是对应的代码P3_0这个变量又变成了1

就是说它按下的时候就是低电平0,平时松开的时候就是高电平1,所以放在循环条件里面进行判断,当按键松开P3_1就变成了0退出循环,接着执行下一条语句。

写代码时,按键按下的时候,就给P2_0这个口赋值0,也就是低电平,前面我们说过,只要给端口赋值低电平,加上另一边提供的高电平就能实现点亮小灯

关于延时函数,按键在按下的一瞬间不会马上就直接进入低电平状态,他需要经过5到10毫秒的延时才能变成稳定的低电平,我们能把这段时间让单片机计算一个延时函数,等这个延时函数计算完成之后,现在按键的状态也就顺利的稳定在了低电平这个状态里面,然后我们紧接着进行while循环检测它是否在低电平,当我们松开的时候就被检测到按键的电平变成了1,也不是完全变成1,就是在抖动,但是肯定不为0了,就立刻退出循环,然后将延时函数写出,延时过后就稳定在了高电平1了,最后再对按键进行取反

关于取反说一下:其实按键初始的状态就是1,也就是高电平,现在进行取反就直接给P2_0变成了0,也就是低电平,现在LED灯就直接亮了,然后再次按下按钮的时候进行取反,P2_0取反之后就变成了1,现在连接LED灯的端口P2_0就是高电平(回到了初始状态),所以也就直接不亮了。

 两个按键控制LED左右移动亮灭:

代码:

#include <REGX52.H>
void Delayxms(unsigned int xms)
{
	unsigned char  j, k;
while(xms){	
	j = 2;
	k = 239;
		do
		{
			while (--k);
		} while (--j);
	xms--;
}
	}
unsigned char a=0;
int main() {
	P2=~(0x01);
	while(1){
	if(P3_1==0)
{
		Delayxms(20);
		while(P3_1==0);
		Delayxms(20);
		a++;
		if(a>=8)
			a=0;
		P2=~(0x01<<a);
		Delayxms(50);
}
		if(P3_0==0)
{
			Delayxms(20);
		while(P3_0==0);
		Delayxms(20);
		if(a==0)
			a=7;
		else
			a--;
		P2=~(0x01<<a);
}
	     }
	      }

代码解释:

每次进行了循环之后P2的值就会停留在上一次按下之后P2所改变的值,所以每次按下之后就相当于P2的值进行了更新,就变成了原来的0x01,这个就是实现循环的关键。

考题:实现静态数码管流水灯形式,依次循环输出1,2,3,...,8

答案代码:

#include <REGX52.H>

void Delay(unsigned int xms)
{
	unsigned char  j, k;
while(xms){	
	j = 2;
	k = 239;
		do
		{
			while (--k);
		} while (--j);
	xms--;
}
	}
int main(){
	while(1){
	P2_2=1;
	P2_3=1;
	P2_4=1;
	//前面三个数据就是控制哪一个数码管亮的
	P0=0x30;// 0011 0000
	Delay(500);
	//0111 1101
	//这个数据就是控制数码管亮几根的
	P2_2=0;
	P2_3=1;
	P2_4=1;
	//前面三个数据就是控制哪一个数码管亮的
	P0=0x5B;//0101 1011
	Delay(500);
	P2_2=1;
	P2_3=0;
	P2_4=1;
	//前面三个数据就是控制哪一个数码管亮的
	P0=0x4F;// 0011 0000
	Delay(500);
	P2_2=0;
	P2_3=0;
	P2_4=1;
	//前面三个数据就是控制哪一个数码管亮的
	P0=0x66;// 0011 0000
	Delay(500);
	P2_2=1;
	P2_3=1;
	P2_4=0;
	//前面三个数据就是控制哪一个数码管亮的
	P0=0x6D;// 0011 0000
	Delay(500);
	P2_2=0;
	P2_3=1;
	P2_4=0;
	//前面三个数据就是控制哪一个数码管亮的
	P0=0x7D;// 0011 0000
	Delay(500);
	P2_2=1;
	P2_3=0;
	P2_4=0;
	//前面三个数据就是控制哪一个数码管亮的
	P0=0x07;// 0011 0000
	Delay(500);
	P2_2=0;
	P2_3=0;
	P2_4=0;
	//前面三个数据就是控制哪一个数码管亮的
	P0=0x7F;// 0011 0000
	Delay(500);
}
}

 解释:就是简单的依次点亮不同的数码管。

项目四

矩阵键盘开关控制LCD屏幕显示

原理图解释:

 

单片机核心通过P10到P17口直接控制矩阵键盘,现在简单了,就是我们代码对这几个口进行数值的计算就能直接实现对LCD屏幕的控制。

P13P12P11P10
S1S2S3S4P17
S5S6S7S8P16
S9S10S11S12P15
S13S14S15S16P14

设置端口值:就直接按照上述表格设置就行了,你想设置哪个按键生效,就找到这个按键的行列信息,把他在第几行第几列的两个行列数值分别设置为0,也就是低电平,然后其他全是高电平就行了,这就是让按键生效,但是按下按键生效了如何让它在LCD显示屏上输出你想要的数字就直接调用LCD函数模块就行了。

代码展示:

矩阵按键的.c文件:

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

/**
  * @brief  矩阵键盘读取按键键码
  * @param  无
  * @retval KeyNumber 按下按键的键码值
			如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回0
  */
unsigned char MatrixKey()
{
	unsigned char KeyNumber=0;
	
	P1=0xFF;//设置所以P1端口为1就是所有的都是输出高电平
	P1_3=0;//又单独对P13这个端口设置为低电平,也就是让P13这一列先生效
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}
	//又单独对P17这个端口单独设置低电平,让P13这一列的P17行生效
	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;
}

考题:

用LED设置一个猜数字小游戏显示器,计算机保存了一个四位数字,用户进行输入,如果输入数字小于保存的数字,LCD显示Sa,如果输入数字过大就输出Mr,如果相等就输出y

答案如下:

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

unsigned char KeyNum;
unsigned int Password,Count;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"Password:");
	while(1)
	{
		KeyNum=MatrixKey();
		if(KeyNum)
		{
			if(KeyNum<=10)	//如果S1~S10按键按下,输入密码
			{
				if(Count<4)	//如果输入次数小于4
				{
					Password*=10;				//密码左移一位
					Password+=KeyNum%10;		//获取一位密码
					Count++;	//计次加一
				}
				LCD_ShowNum(2,1,Password,4);	//更新显示
			}
			if(KeyNum==11)	//如果S11按键按下,确认
			{
				if(Password<2345)	//如果密码等于正确密码
				{
					LCD_ShowString(1,14,"Mr ");	//显示OK
					Password=0;		//密码清零
					Count=0;		//计次清零
					LCD_ShowNum(2,1,Password,4);	//更新显示
				}
				else if(Password>2345)			//否则
				{
					LCD_ShowString(1,14,"Sm ");	//显示ERR
					Password=0;		//密码清零
					Count=0;		//计次清零
					LCD_ShowNum(2,1,Password,4);	//更新显示
				}
				else 
				{
					LCD_ShowString(1,14,"y ");	//显示ERR
					Password=0;		//密码清零
					Count=0;		//计次清零
//					LCD_ShowNum(2,1,Password,4);	//更新显示
				}
			}
			if(KeyNum==12)	//如果S12按键按下,取消
			{
				Password=0;		//密码清零
				Count=0;		//计次清零
				LCD_ShowNum(2,1,Password,4);	//更新显示
			}
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值