我知道学习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 矩阵键盘的典型编程方法——扫描法和利用二维数组存储键值
扫描法: 通过逐行扫描,检测按键是否按下。具体步骤如下:
- 将所有行线设为低电平,所有列线设为高电平。
- 逐行将行线设为低电平,读取列线的状态。
- 如果某列线为低电平,说明该行该列的按键被按下。
代码示例:
#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个寄存器
IE寄存器(中断允许寄存器):
- EA:全局中断允许位,1表示允许中断,0表示禁止中断。
- EX0、EX1:外部中断0和外部中断1允许位。
- ET0、ET1:定时器0和定时器1溢出中断允许位。
- ES:串行口中断允许位。
IP寄存器(中断优先级寄存器):
- PX0、PX1:外部中断0和外部中断1优先级位。
- PT0、PT1:定时器0和定时器1溢出中断优先级位。
- PS:串行口中断优先级位。
TCON寄存器(定时器控制寄存器):
- TF0、TF1:定时器0和定时器1溢出标志位。
- TR0、TR1:定时器0和定时器1运行控制位。
- IT0、IT1:外部中断0和外部中断1触发方式位。
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) { // 主程序代码 } }