嵌入式基础学习笔记(51)
🕗 发布于 01-15 10:03 学习 笔记 单片机
一、GPIO(通用输入输出)
1. GPIO基本概念
2. GPIO工作模式
| 模式类型 | 子模式 | 功能描述 | 应用场景 |
|---|
| 输出模式 | 开漏输出 | 只能输出低电平或高阻态 | I2C总线等 |
| 推挽输出 | 可输出强高/低电平 | LED驱动、数码管 |
| 复用开漏 | 外设功能开漏输出 | I2C复用 |
| 复用推挽 | 外设功能推挽输出 | UART、SPI |
| 输入模式 | 上拉输入 | 内部上拉电阻使能 | 按键检测 |
| 下拉输入 | 内部下拉电阻使能 | 特殊应用 |
| 浮空输入 | 高阻抗输入状态 | 外部中断 |
| 模拟输入 | 模拟信号输入 | ADC采样 |
3. GPIO配置示例代码
/**
* GPIO配置示例
* P1口配置为推挽输出,P3.2配置为上拉输入
*/
#include <reg52.h>
// 宏定义
#define LED_PORT P1 // LED连接到P1口
#define KEY_PIN P3_2 // 独立按键连接到P3.2
void delay_ms(unsigned int ms) {
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 120; j++);
}
void main() {
// 初始化GPIO
LED_PORT = 0xFF; // 初始化为高电平(LED灭)
while(1) {
// 检测按键状态
if(KEY_PIN == 0) { // 按键按下(低电平)
delay_ms(10); // 消抖延时
if(KEY_PIN == 0) { // 确认按键按下
LED_PORT = ~LED_PORT; // 翻转LED状态
while(!KEY_PIN); // 等待按键释放
}
}
}
}
二、独立按键
1. 独立按键工作原理
| 状态 | 引脚电平 | 电路状态 |
|---|
| 按键未按下 | 高电平(VCC通过上拉电阻) | 引脚与GND断开 |
| 按键按下 | 低电平(引脚与GND短路) | 引脚直接接地 |
2. 按键消抖处理
| 消抖方法 | 原理 | 实现方式 |
|---|
| 硬件消抖 | RC电路滤波 | 并联电容 |
| 软件消抖 | 延时检测 | 检测后延时10-20ms再确认 |
3. 独立按键示例代码
c
/**
* 独立按键检测示例
* 4个独立按键控制4个LED
*/
#include <reg52.h>
// 引脚定义
sbit KEY1 = P3^0; // 按键1
sbit KEY2 = P3^1; // 按键2
sbit KEY3 = P3^2; // 按键3
sbit KEY4 = P3^3; // 按键4
sbit LED1 = P1^0; // LED1
sbit LED2 = P1^1; // LED2
sbit LED3 = P1^2; // LED3
sbit LED4 = P1^3; // LED4
// 消抖延时函数
void delay_ms(unsigned int ms) {
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 120; j++);
}
// 按键检测函数
unsigned char key_scan() {
unsigned char key_value = 0;
if(KEY1 == 0) { key_value = 1; }
if(KEY2 == 0) { key_value = 2; }
if(KEY3 == 0) { key_value = 3; }
if(KEY4 == 0) { key_value = 4; }
return key_value;
}
void main() {
unsigned char key_val;
// 初始化LED为熄灭状态
LED1 = LED2 = LED3 = LED4 = 1;
while(1) {
key_val = key_scan(); // 检测按键
if(key_val != 0) {
delay_ms(10); // 消抖延时
if(key_val == key_scan()) { // 确认按键
// 根据按键值控制LED
switch(key_val) {
case 1: LED1 = ~LED1; break;
case 2: LED2 = ~LED2; break;
case 3: LED3 = ~LED3; break;
case 4: LED4 = ~LED4; break;
}
// 等待按键释放
while(key_scan() != 0);
}
}
}
}
三、中断系统
1. 中断基本概念
| 概念 | 描述 | 说明 |
|---|
| 中断 | CPU暂停当前任务处理紧急事件 | 提高响应速度 |
| 中断源 | 引发中断的事件或设备 | 51单片机有5个中断源 |
| 中断优先级 | 决定同时请求时的处理顺序 | 4个优先级 |
| 中断嵌套 | 处理中断时响应更高优先级中断 | 最多嵌套2层 |
2. 51单片机中断源
| 中断源 | 中断向量地址 | 触发条件 | 优先级顺序 |
|---|
| 外部中断0 | 0003H | INT0引脚电平变化 | 最高(0) |
| 定时器0 | 000BH | T0计数器溢出 | 1 |
| 外部中断1 | 0013H | INT1引脚电平变化 | 2 |
| 定时器1 | 001BH | T1计数器溢出 | 3 |
| 串口中断 | 0023H | 串口收发完成 | 4 |
3. 中断优先级寄存器(IP、IPH)
| 寄存器 | 地址 | 功能 | 位定义 |
|---|
| IP | B8H | 中断优先级低字节 | 见下表 |
| IPH | B7H | 中断优先级高字节 | 见下表 |
中断优先级设置表:
| 优先级设置 | 含义 | 中断优先级 |
|---|
| 0.0 | PX0H=0, PX0=0 | 优先级0(最低) |
| 0.1 | PX0H=0, PX0=1 | 优先级1 |
| 1.0 | PX0H=1, PX0=0 | 优先级2 |
| 1.1 | PX0H=1, PX0=1 | 优先级3(最高) |
4. 中断相关寄存器
中断允许寄存器(IE - 地址A8H):
| 位 | 符号 | 功能 | 说明 |
|---|
| B7 | EA | 总中断允许 | 1:允许所有中断 |
| B6 | - | 保留位 | |
| B5 | ET2 | 定时器2中断允许 | 1:允许T2中断 |
| B4 | ES | 串口中断允许 | 1:允许串口中断 |
| B3 | ET1 | 定时器1中断允许 | 1:允许T1中断 |
| B2 | EX1 | 外部中断1允许 | 1:允许INT1中断 |
| B1 | ET0 | 定时器0中断允许 | 1:允许T0中断 |
| B0 | EX0 | 外部中断0允许 | 1:允许INT0中断 |
定时器控制寄存器(TCON - 地址88H):
| 位 | 符号 | 功能 | 说明 |
|---|
| B7 | TF1 | 定时器1溢出标志 | 溢出时硬件置1 |
| B6 | TR1 | 定时器1运行控制 | 1:启动定时器1 |
| B5 | TF0 | 定时器0溢出标志 | 溢出时硬件置1 |
| B4 | TR0 | 定时器0运行控制 | 1:启动定时器0 |
| B3 | IE1 | 外部中断1标志 | INT1中断请求标志 |
| B2 | IT1 | 外部中断1类型 | 0:低电平触发,1:下降沿触发 |
| B1 | IE0 | 外部中断0标志 | INT0中断请求标志 |
| B0 | IT0 | 外部中断0类型 | 0:低电平触发,1:下降沿触发 |
5. 外部中断示例代码
/**
* 外部中断示例
* INT0(P3.2)控制LED翻转
*/
#include <reg52.h>
// 引脚定义
sbit LED = P1^0; // LED连接到P1.0
sbit KEY_INT0 = P3^2; // 外部中断0按键
// 外部中断0服务函数
void int0_isr() interrupt 0 {
LED = ~LED; // LED状态翻转
}
void main() {
// 初始化
LED = 0; // LED初始熄灭
// 配置外部中断0
IT0 = 1; // 设置为下降沿触发(1:下降沿, 0:低电平)
EX0 = 1; // 允许外部中断0
EA = 1; // 开启总中断
while(1) {
// 主循环可执行其他任务
// 中断发生时自动跳转到int0_isr函数
}
}
6. 中断嵌套示例
/**
* 中断嵌套示例
* 外部中断0(高优先级)和定时器0中断(低优先级)
*/
#include <reg52.h>
// 全局变量
unsigned int timer0_count = 0;
// 定时器0中断服务函数(低优先级)
void timer0_isr() interrupt 1 {
TH0 = (65536 - 50000) / 256; // 重装50ms初值
TL0 = (65536 - 50000) % 256;
timer0_count++;
// 每10次中断(0.5秒)翻转P1.1
if(timer0_count >= 10) {
P1_1 = ~P1_1;
timer0_count = 0;
}
}
// 外部中断0服务函数(高优先级)
void int0_isr() interrupt 0 {
// 高优先级中断可以打断低优先级中断
P1_0 = ~P1_0; // 翻转P1.0
}
void main() {
// 配置中断优先级
PX0 = 1; // 外部中断0设为高优先级
PT0 = 0; // 定时器0设为低优先级
// 配置定时器0
TMOD = 0x01; // 定时器0,模式1(16位定时器)
TH0 = (65536 - 50000) / 256; // 50ms初值
TL0 = (65536 - 50000) % 256;
TR0 = 1; // 启动定时器0
ET0 = 1; // 允许定时器0中断
// 配置外部中断0
IT0 = 1; // 下降沿触发
EX0 = 1; // 允许外部中断0
EA = 1; // 开启总中断
while(1) {
// 主程序空循环
}
}
四、定时器
1. 定时器基本概念
| 特性 | 描述 |
|---|
| 数量 | 2个(Timer0和Timer1) |
| 类型 | 16位自增型定时器 |
| 工作模式 | 4种工作模式 |
| 时钟源 | 系统时钟分频 |
2. 定时器工作模式(TMOD寄存器)
| 模式 | M1 M0 | 描述 | 特点 |
|---|
| 模式0 | 0 0 | 13位定时器 | 兼容8048,TL用低5位 |
| 模式1 | 0 1 | 16位定时器 | 常用模式,TH+TL全用 |
| 模式2 | 1 0 | 8位自动重装 | 自动重装载初值 |
| 模式3 | 1 1 | 双8位定时器 | T0分为两个8位定时器 |
TMOD寄存器结构:
| 位 | 符号 | 功能(高4位-T1) | 功能(低4位-T0) |
|---|
| B7 | GATE | 门控位(T1) | |
| B6 | C/T | 计数/定时选择(T1) | |
| B5 | M1 | 模式选择高位(T1) | |
| B4 | M0 | 模式选择低位(T1) | |
| B3 | GATE | 门控位(T0) | |
| B2 | C/T | 计数/定时选择(T0) | |
| B1 | M1 | 模式选择高位(T0) | |
| B0 | M0 | 模式选择低位(T0) | |
3. 定时器初值计算方法
| 时钟频率 | 定时时间 | 计算公式 | 示例 |
|---|
| 12MHz | 1ms | 初值 = 65536 - 1000 | TH0=0xFC, TL0=0x18 |
| 12MHz | 50ms | 初值 = 65536 - 50000 | TH0=0x3C, TL0=0xB0 |
| 11.0592MHz | 1ms | 初值 = 65536 - 921.6 | TH0=0xFC, TL0=0x66 |
4. 定时器示例代码
/**
* 定时器0示例
* 使用定时器0实现1秒精确延时
*/
#include <reg52.h>
// 全局变量
unsigned int timer0_count = 0;
sbit LED = P1^0;
// 定时器0中断服务函数
void timer0_isr() interrupt 1 {
// 重装50ms初值(假设晶振12MHz)
TH0 = (65536 - 50000) / 256;
TL0 = (65536 - 50000) % 256;
timer0_count++;
// 20次中断 = 1秒(50ms × 20 = 1000ms)
if(timer0_count >= 20) {
LED = ~LED; // 翻转LED
timer0_count = 0; // 计数器清零
}
}
void main() {
// 配置定时器0
TMOD = 0x01; // 定时器0,模式1(16位定时器)
TH0 = (65536 - 50000) / 256; // 50ms初值
TL0 = (65536 - 50000) % 256;
// 开启中断
ET0 = 1; // 允许定时器0中断
TR0 = 1; // 启动定时器0
EA = 1; // 开启总中断
LED = 0; // 初始化LED状态
while(1) {
// 主程序可执行其他任务
// 定时器中断会周期性执行
}
}
5. 定时器模式2示例(自动重装载)
/**
* 定时器模式2示例
* 8位自动重装载模式,产生精确的100us定时
*/
#include <reg52.h>
sbit LED = P1^0;
unsigned char t_count = 0;
// 定时器0中断服务函数(模式2自动重装)
void timer0_isr() interrupt 1 {
t_count++;
// 100us × 10000 = 1秒
if(t_count >= 100) {
LED = ~LED; // 每0.01秒翻转一次
t_count = 0;
}
}
void main() {
// 配置定时器0为模式2(8位自动重装)
TMOD = 0x02; // 模式2
// 计算100us的初值(12MHz晶振)
// 机器周期 = 1us,100us需要100个计数
TH0 = 256 - 100; // 自动重装载值
TL0 = 256 - 100; // 初始计数值
// 开启中断
ET0 = 1; // 允许定时器0中断
TR0 = 1; // 启动定时器0
EA = 1; // 开启总中断
LED = 0; // 初始化LED
while(1) {
// 主程序
}
}
五、PWM(脉冲宽度调制)
1. PWM基本概念
| 概念 | 定义 | 公式 |
|---|
| PWM周期 | 一个完整方波的时间 | T = 1/f |
| 占空比 | 高电平时间占周期的比例 | D = Ton/T |
| 频率 | 单位时间内周期数 | f = 1/T |
2. PWM波形示意图
text
占空比 = 25%: ▁▁▁▁▇▇▇▇▁▁▁▁▇▇▇▇
占空比 = 50%: ▁▁▁▇▇▇▇▁▁▁▇▇▇▇
占空比 = 75%: ▁▇▇▇▇▁▇▇▇▇▁▇▇▇▇
3. PWM控制LED亮度示例
/**
* PWM控制LED亮度示例
* 使用定时器产生PWM信号控制LED亮度
*/
#include <reg52.h>
// 全局变量
unsigned char pwm_duty = 50; // 初始占空比50%
unsigned char pwm_counter = 0;
sbit LED = P1^0;
// 定时器0中断服务函数
void timer0_isr() interrupt 1 {
TH0 = (65536 - 100) / 256; // 重装100us初值
TL0 = (65536 - 100) % 256;
pwm_counter++;
if(pwm_counter >= 100) { // PWM周期 = 100×100us = 10ms
pwm_counter = 0;
}
// 根据占空比控制LED
if(pwm_counter < pwm_duty) {
LED = 0; // 低电平点亮(假设共阳极)
} else {
LED = 1; // 高电平熄灭
}
}
void main() {
// 配置定时器0
TMOD = 0x01; // 模式1,16位定时器
TH0 = (65536 - 100) / 256; // 100us初值
TL0 = (65536 - 100) % 256;
// 开启中断
ET0 = 1; // 允许定时器0中断
TR0 = 1; // 启动定时器0
EA = 1; // 开启总中断
while(1) {
// 可以通过按键改变pwm_duty值来调整亮度
// 例如:pwm_duty从0到100变化,实现呼吸灯效果
}
}
六、蜂鸣器控制
1. 蜂鸣器类型对比
| 特性 | 有源蜂鸣器 | 无源蜂鸣器 |
|---|
| 驱动方式 | 直流电压 | 方波信号 |
| 音调 | 固定频率 | 可调频率 |
| 控制 | 简单(电平控制) | 复杂(需要PWM) |
| 成本 | 较低 | 较高 |
| 应用 | 报警、提示音 | 音乐播放 |
2. 蜂鸣器驱动电路
text
有源蜂鸣器:VCC → 蜂鸣器 → NPN三极管 → GND
无源蜂鸣器:PWM信号 → 蜂鸣器 → 三极管放大
3. 蜂鸣器驱动示例代码
/**
* 作业示例:按键控制蜂鸣器不同频率
* Key1: 200Hz, Key2: 400Hz, Key3: 600Hz, Key4: 800Hz, Key5: 1000Hz
*/
#include <reg52.h>
// 引脚定义
sbit BEEP = P1^5; // 蜂鸣器(无源)
sbit KEY1 = P3^0;
sbit KEY2 = P3^1;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;
sbit KEY5 = P3^4;
// 全局变量
unsigned int freq_table[5] = {200, 400, 600, 800, 1000}; // 频率表
unsigned int current_freq = 200; // 当前频率
unsigned int timer_reload; // 定时器重装值
// 定时器0中断服务函数
void timer0_isr() interrupt 1 {
// 根据当前频率重装定时器初值
TH0 = timer_reload / 256;
TL0 = timer_reload % 256;
BEEP = ~BEEP; // 翻转蜂鸣器引脚,产生方波
}
// 初始化定时器
void timer0_init(unsigned int freq) {
unsigned long reload;
// 计算定时器初值(12MHz晶振)
// 方波周期T = 1/f,半周期 = 1/(2f)
// 定时时间 = 1/(2f)秒
// 机器周期 = 1us,所以计数值 = 1/(2f) / 0.000001 = 500000/f
reload = 65536 - (500000UL / freq);
timer_reload = (unsigned int)reload;
// 配置定时器0
TMOD &= 0xF0; // 清除T0配置位
TMOD |= 0x01; // T0模式1,16位定时器
TH0 = timer_reload / 256;
TL0 = timer_reload % 256;
ET0 = 1; // 允许定时器0中断
TR0 = 1; // 启动定时器0
EA = 1; // 开启总中断
}
// 按键扫描函数
unsigned char key_scan() {
unsigned char key_val = 0;
if(KEY1 == 0) key_val = 1;
else if(KEY2 == 0) key_val = 2;
else if(KEY3 == 0) key_val = 3;
else if(KEY4 == 0) key_val = 4;
else if(KEY5 == 0) key_val = 5;
return key_val;
}
// 消抖延时
void delay_ms(unsigned int ms) {
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 120; j++);
}
void main() {
unsigned char key_val, last_key = 0;
// 初始化蜂鸣器为关闭状态
BEEP = 0;
// 初始化为200Hz
timer0_init(current_freq);
while(1) {
key_val = key_scan();
if(key_val != 0 && key_val != last_key) {
delay_ms(10); // 消抖
if(key_val == key_scan()) { // 确认按键
switch(key_val) {
case 1: current_freq = 200; break;
case 2: current_freq = 400; break;
case 3: current_freq = 600; break;
case 4: current_freq = 800; break;
case 5: current_freq = 1000; break;
}
// 重新配置定时器
TR0 = 0; // 先关闭定时器
timer0_init(current_freq);
// 等待按键释放
while(key_scan() != 0);
}
}
last_key = key_val;
}
}
原文地址:https://blog.csdn.net/weixin_68916793/article/details/156912720
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!