Bootstrap

51单片机从入门到精通:理论与实践指南常用资源篇(四)

我知道学习51单片机枯燥,但是希望大家可以坚持下来

在人生的旅途中,每一步都充满了挑战与未知。然而,正是这些挑战塑造了我们的坚韧与毅力。请记住,坚持不仅是通往成功的必经之路,更是内心力量的体现。即使前路漫漫,即使困难重重,只要我们坚定信念,永不放弃,最终定能迎来属于自己的辉煌时刻。坚持就是胜利,每一份努力都不会白费,每一次坚持都会成为未来成功的基石。愿你在追梦的路上,勇往直前,无畏风雨,最终收获属于你的精彩人生。

我先和大家说说我接下来写博客的计划吧,接下来我会对51单片机的常用资源使用进行讲解,讲解的内容如下:

1、按键和单片机对灯和电机等器件的控制,单片机的中断系统和应用示例(博客四)

2、数码管的静态显示和动态显示,单片机的串行通信(博客五)

3、液晶显示屏和OLED屏的使用,A/D与D/A的应用入门(博客六)

最后我还会写几个实例(实战演练)给大家参考,现在还在敲,所以大家先看看基础知识吧。

废话不多说,我们进入今天的正题。

按键和单片机对灯和电机等器件的控制

1独立按键的原理及应用
1 常见的轻触按键的实物

轻触按键是一种常见的输入设备,广泛应用于各种电子设备中。它们通常体积小巧,安装方便,通过按压触发开关,实现电路的通断。轻触按键的内部结构通常包括一个金属弹片和一个塑料按钮,按下按钮时,金属弹片接触导通,松开按钮时,金属弹片恢复原位,断开电路。

2 轻触按键的通、断过程及消抖

通、断过程

  • 闭合状态:当按键被按下时,按键内部的金属弹片接触,电路导通,单片机可以检测到按键按下。
  • 断开状态:当按键被释放时,金属弹片恢复原位,电路断开,单片机检测不到按键按下。

消抖: 按键在按下和释放的瞬间,由于机械原因,可能会出现短暂的抖动现象,导致单片机误判。为了消除这种抖动,通常采用软件延时或硬件滤波的方法。

软件延时

if (KEY == 0) {  // 检测按键是否按下
    delay(10);  // 延时去抖动
    if (KEY == 0) {
        // 按键确实按下
    }
}
3 实现按键给单片机传指令的硬件结构

硬件连接

  • 将按键的一端连接到单片机的某个I/O口(如P3.0),另一端接地。
  • 在按键和单片机之间连接一个上拉电阻,确保按键未按下时,I/O口为高电平。

电路图

按键 ----+---- GND
         |
         +---- P3.0
         |
         +---- 上拉电阻 (10kΩ)
         |
         +---- VCC
4 独立按键的典型应用示例——按键控制蜂鸣器鸣响

硬件连接

  • 将按键的一端连接到P3.0,另一端接地。
  • 将蜂鸣器的一端连接到P1.0,另一端接地。

代码示例

#include <reg52.h>

sbit KEY = P3^0;
sbit BUZZER = P1^0;

void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 1275; j++);
}

void main() {
    while (1) {
        if (KEY == 0) {  // 检测按键是否按下
            delay(10);  // 去抖动
            if (KEY == 0) {
                BUZZER = 1;  // 蜂鸣器鸣响
                delay(500);  // 鸣响500ms
                BUZZER = 0;  // 关闭蜂鸣器
                while (KEY == 0);  // 等待按键释放
            }
        }
    }
}
2 矩阵按键的应用
1 矩阵按键的原理和硬件设计

原理: 矩阵按键通过行列扫描的方式,用较少的I/O口实现多个按键的识别。假设有一个4x4的矩阵按键,只需要8个I/O口即可实现16个按键的功能。

硬件连接

  • 将按键矩阵的行线连接到单片机的P1.0-P1.3。
  • 将按键矩阵的列线连接到单片机的P2.0-P2.3。

电路图

P1.0 ----+---- K1 ----+---- P2.0
         |           |
P1.1 ----+---- K2 ----+---- P2.1
         |           |
P1.2 ----+---- K3 ----+---- P2.2
         |           |
P1.3 ----+---- K4 ----+---- P2.3
2 矩阵键盘的典型编程方法——扫描法和利用二维数组存储键值

扫描法: 通过逐行扫描,检测按键是否按下。具体步骤如下:

  1. 将所有行线设为低电平,所有列线设为高电平。
  2. 逐行将行线设为低电平,读取列线的状态。
  3. 如果某列线为低电平,说明该行该列的按键被按下。

代码示例

#include <reg52.h>

#define ROWS 4
#define COLS 4

char key_map[ROWS][COLS] = {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'}
};

void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 1275; j++);
}

char scan_key() {
    char row, col;
    char key = 0;

    for (row = 0; row < ROWS; row++) {
        P1 = ~(1 << row);  // 选择当前行
        P2 = 0xFF;  // 所有列设为高电平
        delay(1);  // 延时稳定
        for (col = 0; col < COLS; col++) {
            if ((P2 & (1 << col)) == 0) {
                key = key_map[row][col];
                while ((P2 & (1 << col)) == 0);  // 等待按键释放
                return key;
            }
        }
    }
    return 0;  // 无按键按下
}

void main() {
    while (1) {
        char key = scan_key();
        if (key != 0) {
            // 处理按键
        }
    }
}
3 按键和单片机控制电机的运行状态
1 按钮控制直流电机和交流电机的启动和停止

直流电机

  • 通过控制电机驱动芯片(如L298N)的输入端,实现电机的启动和停止。

交流电机

  • 通过继电器控制交流电机的电源,实现电机的启动和停止。

代码示例(直流电机)

#include <reg52.h>

sbit KEY = P3^0;
sbit MOTOR = P1^0;

void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 1275; j++);
}

void main() {
    MOTOR = 0;  // 初始状态:电机停止
    while (1) {
        if (KEY == 0) {  // 检测按键是否按下
            delay(10);  // 去抖动
            if (KEY == 0) {
                MOTOR = !MOTOR;  // 切换电机状态
                while (KEY == 0);  // 等待按键释放
            }
        }
    }
}
2 按键控制交流电机的顺序启动

硬件连接

  • 将继电器的控制端连接到单片机的P1.0和P1.1。
  • 继电器的常开触点连接到交流电机的电源。

代码示例

#include <reg52.h>

sbit KEY = P3^0;
sbit MOTOR1 = P1^0;
sbit MOTOR2 = P1^1;

void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 1275; j++);
}

void main() {
    MOTOR1 = 0;  // 初始状态:电机1停止
    MOTOR2 = 0;  // 初始状态:电机2停止
    while (1) {
        if (KEY == 0) {  // 检测按键是否按下
            delay(10);  // 去抖动
            if (KEY == 0) {
                MOTOR1 = 1;  // 启动电机1
                delay(1000);  // 延时1秒
                MOTOR2 = 1;  // 启动电机2
                while (KEY == 0);  // 等待按键释放
            }
        }
    }
}
3 按键控制电机的正反转

硬件连接

  • 使用电机驱动芯片(如L298N)控制电机的正反转。
  • 将L298N的IN1和IN2连接到单片机的P1.0和P1.1。

代码示例

#include <reg52.h>

sbit KEY = P3^0;
sbit IN1 = P1^0;
sbit IN2 = P1^1;

void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 1275; j++);
}

void main() {
    IN1 = 0;  // 初始状态:电机停止
    IN2 = 0;  // 初始状态:电机停止
    while (1) {
        if (KEY == 0) {  // 检测按键是否按下
            delay(10);  // 去抖动
            if (KEY == 0) {
                IN1 = !IN1;  // 切换电机方向
                IN2 = !IN2;  // 切换电机方向
                while (KEY == 0);  // 等待按键释放
            }
        }
    }
}
4 直流电机的PWM调速

硬件连接

  • 使用电机驱动芯片(如L298N)控制电机的PWM信号。
  • 将PWM信号连接到单片机的P1.1。

代码示例

#include <reg52.h>

sbit MOTOR = P1^1;

void delay_us(unsigned int us) {
    _nop_();
    _nop_();
    while (--us);
}

void pwm(int duty_cycle) {
    while (1) {
        MOTOR = 1;
        delay_us(duty_cycle * 10);  // 高电平时间
        MOTOR = 0;
        delay_us((100 - duty_cycle) * 10);  // 低电平时间
    }
}

void main() {
    pwm(50);  // 50%占空比
}
4 开关与灯的灵活控制
1 钮子开关控制单片机实现停电自锁与来电提示

硬件连接

  • 将钮子开关的一端连接到单片机的P3.0,另一端接地。
  • 将LED的一端连接到P1.0,另一端接地。

代码示例

#include <reg52.h>

sbit SWITCH = P3^0;
sbit LED = P1^0;

void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 1275; j++);
}

void main() {
    LED = 0;  // 初始状态:LED熄灭
    while (1) {
        if (SWITCH == 0) {  // 检测开关是否闭合
            LED = 1;  // LED点亮
            while (SWITCH == 0);  // 等待开关断开
            LED = 0;  // LED熄灭
        }
    }
}
2 按键和单片机控制灯

硬件连接

  • 将按键的一端连接到单片机的P3.0,另一端接地。
  • 将LED的一端连接到P1.0,另一端接地。

代码示例

#include <reg52.h>

sbit KEY = P3^0;
sbit LED = P1^0;

void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 1275; j++);
}

void main() {
    while (1) {
        if (KEY == 0) {  // 检测按键是否按下
            delay(10);  // 去抖动
            if (KEY == 0) {
                LED = !LED;  // 切换LED状态
                while (KEY == 0);  // 等待按键释放
            }
        }
    }
}

单片机的中断系统及应用示例

1 单片机的中断系统
1 中断的基本概念

中断是指当外部事件或内部事件发生时,单片机暂时停止当前正在执行的任务,转而去处理该事件的过程。处理完中断后,单片机会返回到原来被中断的任务继续执行。

中断源

  • 外部中断0(INT0):P3.2引脚
  • 外部中断1(INT1):P3.3引脚
  • 定时器0溢出中断:定时器0计数溢出时触发
  • 定时器1溢出中断:定时器1计数溢出时触发
  • 串行口中断:串行口接收或发送数据时触发
2 中断优先级和中断嵌套

中断优先级

  • 单片机的中断源分为两个优先级:高优先级和低优先级。
  • 可以通过设置IP寄存器来配置每个中断源的优先级。

中断嵌套

  • 当一个中断正在处理时,如果另一个更高优先级的中断发生,单片机会暂停当前的中断服务程序,转而去处理新的中断。
  • 处理完新的中断后,再返回到原来的中断服务程序继续执行。
3 应用中断需要设置的4个寄存器
  1. IE寄存器(中断允许寄存器)

    • EA:全局中断允许位,1表示允许中断,0表示禁止中断。
    • EX0、EX1:外部中断0和外部中断1允许位。
    • ET0、ET1:定时器0和定时器1溢出中断允许位。
    • ES:串行口中断允许位。
  2. IP寄存器(中断优先级寄存器)

    • PX0、PX1:外部中断0和外部中断1优先级位。
    • PT0、PT1:定时器0和定时器1溢出中断优先级位。
    • PS:串行口中断优先级位。
  3. TCON寄存器(定时器控制寄存器)

    • TF0、TF1:定时器0和定时器1溢出标志位。
    • TR0、TR1:定时器0和定时器1运行控制位。
    • IT0、IT1:外部中断0和外部中断1触发方式位。
  4. SCON寄存器(串行控制寄存器)

    • RI:接收中断标志位。
    • TI:发送中断标志位。
4 中断服务程序的写法(格式)

中断服务程序的基本格式

void interrupt_handler() interrupt n {
    // 中断处理代码
}
  • interrupt关键字用于声明中断服务程序。
  • n是中断号,对应不同的中断源。
2 定时器TO和T1的工作方式1
1 单片机的几个周期

机器周期

  • 单片机的一个机器周期由12个振荡周期组成。
  • 例如,如果单片机的晶振频率为12MHz,则一个机器周期为1μs。

定时器周期

  • 定时器每次加1或减1的时间间隔为一个机器周期。
2 定时器的工作方式1工作过程详解

工作方式1

  • 定时器工作在16位计数模式。
  • 计数范围为0到65535(0xFFFF)。
  • 当计数器计满65535后,会溢出并置位TF0或TF1标志位,触发中断。

定时时间计算

  • 定时时间 T=(65536−初始值)×机器周期T=(65536−初始值)×机器周期

代码示例

#include <reg52.h>

void timer0_init() {
    TMOD = 0x01;  // 定时器0工作在方式1
    TH0 = 0xFC;   // 设置初值,定时50ms
    TL0 = 0x18;
    EA = 1;       // 允许总中断
    ET0 = 1;      // 允许定时器0中断
    TR0 = 1;      // 启动定时器0
}

void timer0_isr() interrupt 1 {
    TH0 = 0xFC;   // 重新加载初值
    TL0 = 0x18;
    // 中断处理代码
}

void main() {
    timer0_init();
    while (1) {
        // 主程序代码
    }
}
3 定时器TO和T1的工作方式1应用示例

应用示例:使用定时器0实现1秒定时

代码示例

#include <reg52.h>

void timer0_init() {
    TMOD = 0x01;  // 定时器0工作在方式1
    TH0 = 0xFC;   // 设置初值,定时50ms
    TL0 = 0x18;
    EA = 1;       // 允许总中断
    ET0 = 1;      // 允许定时器0中断
    TR0 = 1;      // 启动定时器0
}

unsigned char count = 0;

void timer0_isr() interrupt 1 {
    TH0 = 0xFC;   // 重新加载初值
    TL0 = 0x18;
    count++;
    if (count >= 20) {  // 50ms * 20 = 1000ms
        count = 0;
        // 每1秒执行一次的操作
    }
}

void main() {
    timer0_init();
    while (1) {
        // 主程序代码
    }
}
3 外部中断的应用
1 低电平触发外部中断的应用示例

硬件连接

  • 将按键的一端连接到P3.2(外部中断0引脚),另一端接地。
  • 在按键和单片机之间连接一个上拉电阻。

代码示例

#include <reg52.h>

sbit LED = P1^0;

void external_interrupt0() interrupt 0 {
    LED = !LED;  // 切换LED状态
    while (P3_2 == 0);  // 等待按键释放
}

void main() {
    IT0 = 0;     // 低电平触发
    IE = 0x81;   // 允许外部中断0
    while (1) {
        // 主程序代码
    }
}
2 下降沿触发外部中断的应用示例

硬件连接

  • 将按键的一端连接到P3.2(外部中断0引脚),另一端接地。
  • 在按键和单片机之间连接一个上拉电阻。

代码示例

#include <reg52.h>

sbit LED = P1^0;

void external_interrupt0() interrupt 0 {
    LED = !LED;  // 切换LED状态
    while (P3_2 == 0);  // 等待按键释放
}

void main() {
    IT0 = 1;     // 下降沿触发
    IE = 0x81;   // 允许外部中断0
    while (1) {
        // 主程序代码
    }
}
;