自学内容网 自学内容网

嵌入式基础学习笔记(51)

一、GPIO(通用输入输出)

1. GPIO基本概念

  • 定义:General Purpose Input Output,单片机与外界交互最基本的形式

  • 特点:可以独立将引脚配置为输入模式或输出模式

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单片机中断源

中断源中断向量地址触发条件优先级顺序
外部中断00003HINT0引脚电平变化最高(0)
定时器0000BHT0计数器溢出1
外部中断10013HINT1引脚电平变化2
定时器1001BHT1计数器溢出3
串口中断0023H串口收发完成4

3. 中断优先级寄存器(IP、IPH)

寄存器地址功能位定义
IPB8H中断优先级低字节见下表
IPHB7H中断优先级高字节见下表

中断优先级设置表:

优先级设置含义中断优先级
0.0PX0H=0, PX0=0优先级0(最低)
0.1PX0H=0, PX0=1优先级1
1.0PX0H=1, PX0=0优先级2
1.1PX0H=1, PX0=1优先级3(最高)

4. 中断相关寄存器

中断允许寄存器(IE - 地址A8H):

符号功能说明
B7EA总中断允许1:允许所有中断
B6-保留位
B5ET2定时器2中断允许1:允许T2中断
B4ES串口中断允许1:允许串口中断
B3ET1定时器1中断允许1:允许T1中断
B2EX1外部中断1允许1:允许INT1中断
B1ET0定时器0中断允许1:允许T0中断
B0EX0外部中断0允许1:允许INT0中断

定时器控制寄存器(TCON - 地址88H):

符号功能说明
B7TF1定时器1溢出标志溢出时硬件置1
B6TR1定时器1运行控制1:启动定时器1
B5TF0定时器0溢出标志溢出时硬件置1
B4TR0定时器0运行控制1:启动定时器0
B3IE1外部中断1标志INT1中断请求标志
B2IT1外部中断1类型0:低电平触发,1:下降沿触发
B1IE0外部中断0标志INT0中断请求标志
B0IT0外部中断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描述特点
模式00 013位定时器兼容8048,TL用低5位
模式10 116位定时器常用模式,TH+TL全用
模式21 08位自动重装自动重装载初值
模式31 1双8位定时器T0分为两个8位定时器

TMOD寄存器结构:

符号功能(高4位-T1)功能(低4位-T0)
B7GATE门控位(T1)
B6C/T计数/定时选择(T1)
B5M1模式选择高位(T1)
B4M0模式选择低位(T1)
B3GATE门控位(T0)
B2C/T计数/定时选择(T0)
B1M1模式选择高位(T0)
B0M0模式选择低位(T0)

3. 定时器初值计算方法

时钟频率定时时间计算公式示例
12MHz1ms初值 = 65536 - 1000TH0=0xFC, TL0=0x18
12MHz50ms初值 = 65536 - 50000TH0=0x3C, TL0=0xB0
11.0592MHz1ms初值 = 65536 - 921.6TH0=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)!