• 2023年信息院学生科协第二次硬件培训


    前言

    信息科学与工程学院科协第二次培训的内容围绕51单片机展开,先简单介绍了单片机,搭建起了一个初步的认识,然后通过例程详细讲解了GPIO、中断、定时器以及串口,带领大家入门了51单片机。

    一、51单片机简介

    1、什么是单片机

    单片机是一种集成电路芯片,它采用超大规模集成电路技术,将具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能,可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路,集成到一块硅片上,构成一个微型计算机系统。可以说,一块芯片就成了一台计算机。

    2、主流单片机及其编程语言

    51单片机、STM32单片机、TI系列单片机、ESP32单片机等。主流的单片机编程语言包括C/C++、Python、汇编等。还有一种设计单片机芯片的芯片——FPGA,使用Verilog等硬件语言编程。

    3、单片机的应用

    单片机(Microcontroller,MCU)作为一种集成了中央处理器、存储器和各种输入/输出接口的芯片,由于其体积小、功耗低、成本低、易于编程和开发等特点广泛应用于各个领域,包括智能家居、工业制造、医疗、农业等。
    智能家居
    工业制造
    医疗

    农业

    4、单片机开发软件

    Keil(全称Keil µVision IDE)是一款集成开发环境(IDE),主要用于嵌入式系统的开发。它由德国Keil公司开发,现在已经被ARM公司收购,并与其MDK-ARM软件包合并成为MDK-ARM Keil软件包。Keil支持多种编程语言,包括C、C++、ASM等,可以对多种单片机进行编译、调试和仿真。

    Keil作为嵌入式系统开发工具,具有丰富的功能和优秀的性能,可帮助开发人员在较短的时间内完成从编译到调试和部署的所有过程。Keil提供了一个友好的用户界面,包括源代码编辑器、编译器、调试器和仿真器等组件,使得开发人员可以方便地编写和调试嵌入式应用程序。

    Keil不仅支持多种编程语言和单片机体系结构,还提供了丰富的API和库函数,可以方便地访问硬件资源,并通过模拟器和仿真器等工具来测试和验证代码的正确性。此外,Keil还支持多种调试接口和外围设备,如JTAG、SWD、UART等,可适用于各种开发需求和场景。

    STC-ISP 是一款单片机下载编程烧录软件,是针对STC系列单片机而设计的,可下载STC89系列、12C2052系列和12C5410等系列的STC单片机,使用简便。
    Keil5

    STC-ISP

    二、GPIO(点亮LED)

    1、GPIO简介

    单片机引脚类型包括电源引脚(VCC\GND),晶振引脚(XTAL1/2),复位引脚(RST)下载引脚(RXD、TXD),GPIO引脚。GPIO(general purpose input output)是通用输入输出端口的简称,可以通过软件来控制其输入和输出。

    2、LED简介

    LED(Light Emitting Diode),即发光二极管,是一种半导体固体发光器件,它是利用固体半导体芯片作为发光材料,当两端加上正向电压,半导体中的载流子发生复合引起光子发射而产生光。LED可以直接发出红、黄、蓝、绿、青、橙、紫、白色的光。
    在这里插入图片描述

    在这里插入图片描述

    3、硬件设计

    在这里插入图片描述
    在这里插入图片描述
    这些就是LED在单片机上的原理图,分别是是A2-A4系列的板子的原理图和A5-A7系列的板子的原理图,前面一个已经用网络标签和单片机连到一起了,比较方便但是只能是单片机固定的引脚控制固定的LED,后面那个把引脚连在了排线上,比较灵活但需要外接杜邦线。

    4、软件设计

    首先设计了较为常用的延时函数,其原理是占用CPU进行计算操作从而进行延时,是一种较为简单的延时方式。

    void delay_10us(u16 ten_us)
    {
    	while(ten_us--);	
    }
    
    • 1
    • 2
    • 3
    • 4

    根据原理图,将P2.0管脚定义为LED1,便于代码的编写。
    完整代码如下:

    #include "REGX52.h"
    
    typedef unsigned int u16;	//对系统默认数据类型进行重定义
    typedef unsigned char u8;
    
    sbit LED1=P2^0;	//将P2.0管脚定义为LED1
    
    void delay_10us(u16 ten_us)
    {
    	while(ten_us--);	
    }
    void main()
    {	
    	while(1)
    	{
    		LED1=0;	//点亮
    		delay_10us(50000); //大约延时450ms
    		LED1=1;	//熄灭
    		delay_10us(50000); 	
    	}		
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    三、GPIO(独立按键)

    1、按键简介

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    如独立按键的实物图所示,初始状态是1-3导通2-4导通,当按键按下的时候1-2导通3-4导通,这样就达到了开关的目的。假设在我们的1号引脚接上单片机的IO口,2端接地,这样在按键按下的时候单片机的io口就会检测到低电平,就像理想波形一样。但是按键会在我们摁下的时候出现机械抖动,所以真正的波形会有杂波,所以我们需要用delay函数进行延时消抖。

    2、硬件设计

    在这里插入图片描述
    在这里插入图片描述
    这些是按键的原理图,上下两幅图的区别和LED一样,所以第一幅图只能通过K3引脚来触发外部中断0,用K4来触发外部中断1;K1,K2分别可以用来控制LED或其他外设。

    3、软件设计

    要实现通过开发板上的独立按键K1控制D1指示灯亮灭的功能。在主函数中使用轮询的方式检测哪个按键被按下,从而进行相应的操作。
    按键扫描函数:

    typedef unsigned int u16;	//对系统默认数据类型进行重定义
    typedef unsigned char u8;
    
    //定义LED1控制脚
    sbit LED1=P2^0;
    
    //定义独立按键控制脚
    sbit KEY1=P3^1;
    sbit KEY2=P3^0;
    sbit KEY3=P3^2;
    sbit KEY4=P3^3;
    
    //使用宏定义独立按键按下的键值
    #define KEY1_PRESS	1
    #define KEY2_PRESS	2
    #define KEY3_PRESS	3
    #define KEY4_PRESS	4
    #define KEY_UNPRESS	0	
    
    u8 key_scan(u8 mode)
    {
    	static u8 key=1;
    	if(mode)key=1;//连续扫描按键
    	if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))//任意按键按下
    	{
    		delay_10us(1000);//消抖
    		key=0;
    		if(KEY1==0) return KEY1_PRESS;
    		else if(KEY2==0) return KEY2_PRESS;
    		else if(KEY3==0) return KEY3_PRESS;
    		else if(KEY4==0) return KEY4_PRESS;	
    	}
    	else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)	//无按键按下
    		key=1;			
    	return KEY_UNPRESS;		
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    当mode=1时支持连按,当mode=0时不支持连按。

    main函数设计:

    void main()
    {	
    	u8 key=0;
    	while(1)
    	{
    	   	key=key_scan(1);
    		if(key==KEY1_PRESS)//检测按键K1是否按下
    			LED1=!LED1;//LED1状态翻转	
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    通过key_scan的返回值判断哪个按键被按下,从而进行相应的操作。

    四、中断系统

    1、中断介绍

    中断是为了使单片机具有对内外部随机发生的事件的实时处理能力而设置的。中断功能的存在很大程度上提高了单片机处理内外部事件的能力。它也是单片机最重要的功能之一,是学习单片机必须掌握的。
    在这里插入图片描述
    在这里插入图片描述
    中断的优点有:分时操作、实时响应、可靠性高等。

    2、中断结构

    在这里插入图片描述
    只有当图上所有相应的开关都闭合时,相应的中断才能被响应。

    3、中断相关寄存器

    理解寄存器的相关功能需要多多阅读参考手册。

    (1)中断允许控制IE

    在这里插入图片描述

    (2)中断请求标志TCON

    在这里插入图片描述

    (3)中断优先级

    在这里插入图片描述

    (4)中断号

    在这里插入图片描述

    (5)中断响应条件

    1、中断源有中断请求
    2、中断源的中断允许位为1
    3、CPU开中断(即EA=1)
    仅当以上条件都满足,CPU才会响应中断。

    五、外部中断实验

    1、外部中断简介

    STC89C5x系列单片机提供了4个外部中断资源:外部中断0(INT0)、外部中断1(INT1)、外部中断2(INT2)、外部中断3(INT3)。其中数字越小优先级越高。

    2、外部中断配置

    根据中断响应条件配置相关寄存器。
    在这里插入图片描述
    在这里插入图片描述

    3、硬件设计

    该实验的硬件部分为LED和按键,硬件设计可参考上文。

    4、软件设计

    使用独立按键K3控制LED亮灭。

    INT0初始化:

    void exti0_init(void)
    {
    	IT0=1;//跳变沿触发方式(下降沿)
    	EX0=1;//打开INT0的中断允许
    	EA=1;//打开总中断
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    INT0中断服务函数:

    //定义LED1管脚
    sbit LED1=P2^0;
    
    //定义独立按键K3控制脚
    sbit KEY3=P3^2;
    
    void exti0() interrupt 0 //外部中断0中断函数
    {
    	delay_10us(1000);//消抖
    	if(KEY3==0)//再次判断K3键是否按下
    		LED1=!LED1;//LED1状态翻转					
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    main函数:

    void main()
    {	
    	exti0_init();//外部中断0配置
    	while(1);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    因为主函数的循环里没有进行任何操作,因此LED亮灭的控制是CPU响应中断的结果。

    六、定时器中断

    1、定时器简介

    定时器是由单片机自身提供的一个非常稳定的计数器,这个稳定的计数器就是单片机上连接的晶振部件,晶振经过12分频后提供给单片机固定频率的稳定脉冲。晶振的频率非常准确,因此单片机计数脉冲之间的时间间隔也是非常准确的。

    STC89C5x系列有三个定时器(T0,T1,T2),T0和T1与传统的51单片机兼容,T2是该型号增加的资源。

    2、定时器的功能

    定时器可以用于以微秒、毫秒或秒为单位计时,对外部事件的脉冲计数,在寄存计数器溢出的时候产生中断,生成PWM波等。

    3、定时器的计数原理

    以外接12MHz晶振为例,晶振的振荡周期为1/12us。而一个机器周期=6状态周期=12振荡周期=1us。
    (与微处理器架构有关,微处理器的工作过程中,执行一条机器指令通常需要多个步骤,每个步骤都对应一个振荡周期。这些步骤包括:
    取指令周期:处理器从内存中取出下一条指令,这需要3个振荡周期。
    执行指令周期:指令的执行,这也需要3个振荡周期。
    存储周期:将执行指令的结果存储回内存或寄存器,同样需要3个振荡周期。
    其他周期:还有额外的周期用于中断处理等。
    这些步骤合在一起,通常需要12个振荡周期。)
    计数部分由高八位和低八位两个寄存器THx和TLx组成。每当经过一个机器周期或每当检测到一个外部信号时,寄存器的计数值加一。当寄存器所有位都为1时,再输入一个脉冲就使计数器归零,且相应的中断标志位置为1,向CPU发起中断请求。
    可见溢出时计数器的值减去初值才是计数器的计数值。

    4、寄存器简介

    寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快。
    在这里插入图片描述
    在这里插入图片描述

    5、定时器中断的寄存器

    (1)中断允许控制IE

    在这里插入图片描述

    (2)中断请求控制TCON

    在这里插入图片描述
    TF0/1:中断请求标志位
    TR0/1:运行控制位,=0,停止工作;=1,开始工作。

    (3)TMOD工作方式寄存器

    在这里插入图片描述
    低四位用于T0,高四位用于T1
    C/T:=0,定时模式;=1,计数模式。
    M1,M0:工作方式
    在这里插入图片描述

    6、软件设计

    利用T0中断控制LED每隔500ms闪烁。
    以12MHz晶振为例,一个机器周期=1us,计数1000次就是1ms。计数器初值=65535-1000+1=FC18

    T0初始化函数:

    typedef unsigned int u32;
    
    void T0Init(void)
    {
    	TMOD&=0xF0;
    	TMOD|=0x01;
    	TH0=0xFC;
    	TL0=0x18;			//赋初始值使定时时间为1ms
    	TF0=0;
    	TR0=1;
    	ET0=1;
    	EA=1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    T0中断服务函数:

    sbit LED=P2^0;
    
    void Timer0_Routine() interrupt 1
    {
    	static u32 T0Count;
    	TL0 = 0x18;		
    	TH0 = 0xFC;		
    	T0Count++;		
    	if(T0Count>=500)	//累计500次,500ms
    	{
    		T0Count=0;
    		LED=~LED;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    main函数:

    void main()
    {
    	T0Init();
    	while(1);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    因为主函数的循环里没有进行任何操作,因此出现LED闪烁的现象是CPU响应中断的结果。

    七、串口通信

    1、身边的通信

    在这里插入图片描述
    在这里插入图片描述
    有线通信

    在这里插入图片描述
    在这里插入图片描述
    无线通信

    2、串口通信简介

    串口通信是通信中最常用的通信手段之一。设备采用串行通信协议(serial communication)在一条信号线上将数据逐个bit进行传输的通信模式。
    串口通信的方式有多种,有UART、SPI、IIC等。
    UART是通用异步收/发器,属于全双工通信方式。

    3、专有词汇解释

    在这里插入图片描述
    串行通信:数据逐位按顺序发送。
    并行通信:数据的各位同时发送。

    同步通信:通信带时钟同步信号传输。若没有时钟提供节拍信号则无法通信。
    异步通信:通信不带时钟同步信号。不需要时钟提供信号,但要通信双方事先约定好波特率。

    波特率:每秒传输的码元符号的个数,是衡量数据传输速率的指标。

    单工通信:只允许数据在一个方向上传输。
    半双工:允许数据在两个方向上传输,同一时刻只有一个传输方向。
    全双工:允许数据同时在两个方向上传输。

    4、UART通信

    在这里插入图片描述
    在这里插入图片描述

    5、51单片机串行口

    在这里插入图片描述

    6、SCON寄存器

    用于设定通信工作方式、控制收发和设定中断标志。
    在这里插入图片描述
    SCON寄存器位功能:
    在这里插入图片描述
    SCON中重要的位:
    在这里插入图片描述在这里插入图片描述
    SM0、SM1设定工作方式。

    在这里插入图片描述
    在这里插入图片描述
    SCON寄存器的配置:
    在这里插入图片描述

    7、PCON寄存器

    PCON寄存器只有最高位与串口通信有关。
    在这里插入图片描述
    SMOD:波特率倍增位。当SMOD=1时,波特率倍增。

    PCON寄存器的配置:
    在这里插入图片描述

    8、TMOD寄存器

    串口通信中,定时器1常被用于波特率发生器。因此想要利用串口进行通信需要配置定时器1相关的寄存器。

    TMOD的配置:
    在这里插入图片描述

    9、方式1的具体通信过程

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    10、数值计算

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    11、软件设计

    51单片机利用串口和电脑进行通信。

    串口初始化函数:

    void uart_init(u8 baud)
    {
    	SCON = 0x50;	//设置工作方式1
    	PCON = 0x80;	//波特率加倍
    	TMOD |= 0x20;	//设置计数器工作方式2
    	
    	TH1 = baud;	//计数器初始值设置
    	TL1 = baud;	//计数器初始值设置
    	
    	ES = 1;	//打开接收中断
    	EA = 1;	//打开总中断
    	TR1 = 1;	//打开计数器
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    串口中断服务函数:

    typedef unsigned int u16;
    typedef unsigned char u8;
    
    void uart() interrupt 4
    {
    	u8 rec_data;
    	
    	RI = 0;	//清除标志位
    	rec_data = SBUF;	//存储接收到的数据
    	SBUF = rec_data;	//将收到的数据放入发送寄存器
    	while(!TI);	//等待发送完成
    	TI = 0;	//清除发送完成标志位
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    main函数:

    int main()
    {
    	uart_init(0xFA); //波特率为9600
    	while(1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    实验现象:
    在这里插入图片描述
    在这里插入图片描述

    总结

    本次培训通过PPT的演示、认真仔细的讲解和严谨的例程,手把手地传授了51单片机相关的知识和IO、中断、定时器、串口通信的编程方法,启发同学们继续练习学习单片机,增进对电子技术的了解。

  • 相关阅读:
    临床实验室软件:LAB MASTER 48.0 Crack
    scala入门
    git 本地多个账号错乱问题解决
    postgresql-常用数学函数
    vue中渲染器原理
    [云原生1.] Docker容器的简单介绍和基本管理
    从0开始的异世界编程 [3]
    jdk定时任务的使用
    Go运行时的内存分配器以及消耗指定大小的内存(C语言)
    【Java 进阶篇】JDBC DriverManager 详解
  • 原文地址:https://blog.csdn.net/NOTFOUND_Liu/article/details/133896863