Led模块+理解

一、模块概述

LED模块封装了LED灯、蜂鸣器、电机、继电器的控制功能。这些设备虽然功能不同,但都通过74HC138译码器进行片选控制。

文件位置

  • driver/led.c - 实现文件

  • driver/led.h - 头文件

包含功能

函数 功能 控制引脚
Led_Disp() LED显示控制 Y4 (0x80)
Led_Off() 关闭所有LED Y4 (0x80)
Beep() 蜂鸣器控制 Y5 (0xA0)
Motor() 电机控制 Y5 (0xA0)
Relay() 继电器控制 Y5 (0xA0)

二、硬件原理

LED电路(共阳极)

VCC ────┬────┬────┬────┬────┬────┬────┬────
        │    │    │    │    │    │    │    │
       LED1 LED2 LED3 LED4 LED5 LED6 LED7 LED8
        │    │    │    │    │    │    │    │
        ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼
┌───────────────────────────────────────────┐
│              74HC573 锁存器               │
│   D0   D1   D2   D3   D4   D5   D6   D7   │
└───────────────────────────────────────────┘
        │    │    │    │    │    │    │    │
       P0.0 P0.1 P0.2 P0.3 P0.4 P0.5 P0.6 P0.7
​
锁存使能 ← Y4 (74HC138)

关键点

  • 共阳极:LED阳极接VCC,阴极接P0

  • P0=0 → LED亮P0=1 → LED灭

  • 需要取反操作:P0 = ~led_data

蜂鸣器/电机/继电器电路

                     VCC
                      │
              ┌───────┴───────┐
              │   驱动电路     │
              │  (ULN2003)    │
              └───────┬───────┘
                      │
        ┌─────────────┼─────────────┐
        │             │             │
      继电器        蜂鸣器         电机
      (bit4)       (bit6)        (bit5)
        │             │             │
        ▼             ▼             ▼
┌─────────────────────────────────────────┐
│              74HC573 锁存器              │
│     D4          D5          D6          │
└─────────────────────────────────────────┘
        │             │             │
      P0.4         P0.5         P0.6
​
锁存使能 ← Y5 (74HC138)

关键点

  • 高电平有效:P0=1 → 设备启动

  • 继电器:bit4 (0x10)

  • 电机:bit5 (0x20)

  • 蜂鸣器:bit6 (0x40)


三、代码详解

全局变量

// LED状态变量
idata unsigned char temp_1 = 0x00;      // 当前LED状态
idata unsigned char temp_1_old = 0xff;  // 上次LED状态(用于状态变化检测)
​
// 蜂鸣器/电机/继电器状态变量
idata unsigned char temp_2 = 0x00;      // 当前状态
idata unsigned char temp_2_old = 0xff;  // 上次状态

LED显示函数

/**
 * @brief LED显示函数
 * @param ucLed 指向8元素数组的指针,每个元素0或1
 *              ucLed[0]对应LED1,ucLed[7]对应LED8
 */
void Led_Disp(unsigned char *ucLed)
{
    unsigned char temp;
​
    // 1. 将8个数组元素合并成一个字节
    temp_1 = 0x00;
    temp_1 = (ucLed[0] << 0) | (ucLed[1] << 1) | (ucLed[2] << 2) | (ucLed[3] << 3) |
             (ucLed[4] << 4) | (ucLed[5] << 5) | (ucLed[6] << 6) | (ucLed[7] << 7);
​
    // 2. 状态变化检测:只有状态改变才更新硬件
    if (temp_1 != temp_1_old)
    {
        // 3. 写入硬件(取反,因为共阳极)
        P0 = ~temp_1;
​
        // 4. 选中LED锁存器(Y4) BCD码100对应4
        temp = P2 & 0x1f;//清零高三位0001 0000
        temp = temp | 0x80;//对应Y4 1000 0000
        P2 = temp;
​
        // 5. 取消选择,锁存数据
        temp = P2 & 0x1f;
        P2 = temp;
​
        // 6. 更新状态缓存
        temp_1_old = temp_1;
    }
}

LED关闭函数

/**
 * @brief 关闭所有LED
 */
void Led_Off()
{
    unsigned char temp;
​
    // 共阳极,P0=0xFF全部熄灭
    P0 = 0xff;
​
    // 选中并锁存
    temp = P2 & 0x1f;
    temp = temp | 0x80;
    P2 = temp;
    temp = P2 & 0x1f;
    P2 = temp;
​
    // 重置状态缓存
    temp_1_old = 0x00;
}

蜂鸣器控制函数

/**
 * @brief 控制蜂鸣器
 * @param enable 1=响,0=停
 */
void Beep(bit enable)
{
    unsigned char temp;
​
    // 蜂鸣器在bit6位置 (0x40 = 0100 0000b)
    if (enable)
        temp_2 |= 0x40;      // 置位:开启
    else
        temp_2 &= (~0x40);   // 清位:关闭
​
    // 状态变化检测
    if (temp_2 != temp_2_old)
    {
        // 直接写入(高电平有效,不需取反)
        P0 = temp_2;
​
        // 选中Y5锁存器
        temp = P2 & 0x1f;
        temp = temp | 0xa0;
        P2 = temp;
        temp = P2 & 0x1f;
        P2 = temp;
​
        temp_2_old = temp_2;
    }
}

电机控制函数

/**
 * @brief 控制电机
 * @param enable 1=转动,0=停止
 */
void Motor(bit enable)
{
    unsigned char temp;
​
    // 电机在bit5位置 (0x20 = 0010 0000b)
    if (enable)
        temp_2 |= 0x20;
    else
        temp_2 &= (~0x20);
​
    if (temp_2 != temp_2_old)
    {
        P0 = temp_2;
​
        temp = P2 & 0x1f;
        temp = temp | 0xa0;
        P2 = temp;
        temp = P2 & 0x1f;
        P2 = temp;
​
        temp_2_old = temp_2;
    }
}

继电器控制函数

/**
 * @brief 控制继电器
 * @param enable 1=吸合,0=断开
 */
void Relay(bit enable)
{
    unsigned char temp;
​
    // 继电器在bit4位置 (0x10 = 0001 0000b)
    if (enable)
        temp_2 |= 0x10;
    else
        temp_2 &= (~0x10);
​
    if (temp_2 != temp_2_old)
    {
        P0 = temp_2;
​
        temp = P2 & 0x1f;
        temp = temp | 0xa0;
        P2 = temp;
        temp = P2 & 0x1f;
        P2 = temp;
​
        temp_2_old = temp_2;
    }
}

四、位操作技巧

设备控制位对照

设备 位置 掩码 开启操作 关闭操作
继电器 bit4 0x10 temp_2 |= 0x10 temp_2 &= ~0x10
电机 bit5 0x20 temp_2 |= 0x20 temp_2 &= ~0x20
蜂鸣器 bit6 0x40 temp_2 |= 0x40 temp_2 &= ~0x40

位操作图解

开启蜂鸣器(保持其他设备状态不变):
temp_2:  0b00100000  (电机开)
0x40:    0b01000000
OR结果:  0b01100000  (电机开 + 蜂鸣器开)
​
关闭蜂鸣器(保持其他设备状态不变):
temp_2:  0b01100000  (电机开 + 蜂鸣器开)
~0x40:   0b10111111
AND结果: 0b00100000  (电机开)

五、使用方法

在main.c中定义LED数组

pdata unsigned char ucLed[8] = {0, 0, 0, 0, 0, 0, 0, 0};

LED处理任务

void Led_Proc()
{
    // 方式1:直接设置单个LED
    ucLed[0] = 1;  // LED1亮
    ucLed[1] = 0;  // LED2灭
​
    // 方式2:根据条件控制
    if (Temperature > 30)
        ucLed[2] = 1;  // 温度过高,LED3报警
    else
        ucLed[2] = 0;
​
    // 方式3:流水灯效果
    static unsigned char flow = 0;
    for (i = 0; i < 8; i++)
        ucLed[i] = (i == flow) ? 1 : 0;
    flow = (flow + 1) % 8;
}

在定时器中断中调用

void Timer1_Isr(void) interrupt 3
{
    // PWM调光方式
    pwm_period = (++pwm_period) % 10;
    if (pwm_period < pwm_compare)
        Led_Disp(ucLed);  // 亮
    else
        Led_Off();        // 灭
}

蜂鸣器/电机/继电器使用

// 开启蜂鸣器
Beep(1);
​
// 关闭蜂鸣器
Beep(0);
​
// 开启电机
Motor(1);
​
// 开启继电器
Relay(1);