第26章 IO扩展(并转串)

第二十六章 IO扩展(并转串)

1. 导入

在独立按键实验章节中, 假如使用的独立按键数不止 8 个, 而是 16 个或者更多, 这时 51 单片机的 IO 口就显得非常拮据, 因此就需要扩展单片机 IO 口。

开发板板载一个 74HC165芯片, 本章就来介绍如何来扩展单片机 IO 口, 通过本章的学习, 让大家学会使用 74HC165 芯片来扩展 IO 口。 本章所要实现的功能是: 使用开发板上的 74HC165模块扩展 8 位 IO 与独立按键连接, 通过独立按键控制 LED 灯。

2. 74HC165芯片介绍

74HC165 是 8 位并行输入串行输出移位寄存器,其管脚封装及功能如下:

屏幕截图 2024 06 13 090134

  • CLK、CLK INH: 74HC165 的时钟输入是一个“ 或非门” 结构 CLK 和 CLK INH功能是可以等效的可以互换使用的, 一般采取相连在一起、 上升沿时数据一位一位的串行输出。

  • A- H: 并行数据输入端

  • SER: 为串行输入端、 将数据串行进入到寄存器中、 在每一个时钟的上升沿向右移动一位, 利用这种特性, 只要把 QH 输出连接到下一级的 SER 输入既可实现并转串扩展。

  • QH: 串行输出端

  • Q`H: 互补串行输出端

  • SH/LD: 为低电平时并行数据进入移位寄存器中、 为高电平时并行数据不能传进移位寄存器。

当移位/置入控制端( SH/LD) 为低电平时, 并行数据( A- H) 被置入寄存器,而时钟( CLK,CLK INH) 及串行数据( SER) 均无关。 当 SH/LD 为高电平时, 并行置数功能被禁止。

CLK 和 CLK INK 在功能上是等价的, 可以交换使用。 当 CLK 和 CLK INK 有一个为低电平并且 SH/LD 为高电平时, 另一个时钟可以输入。 当 CLK 和 CLK INK有一个为高电平时, 另一个时钟被禁止。 只有在 CLK 为高电平时 CLK INK 才可变为高电平。

更多介绍参考:74HC165的使用-CSDN博客

3. 硬件设计

本实验使用到硬件资源如下:

  • LED 模块( D1-D8)

  • 独立按键模块

  • 74HC165 模块

LED 模块和独立按键模块电路在前面章节都介绍过, 这里就不多说, 开发板上的 74HC165 模块电路如下图所示:

屏幕截图 2024 06 13 090954

从上图中可以看出, 该电路是独立的, 74HC165 芯片的控制管脚 QH、 CLK、SH/LD 并未直接连接到 51 单片机的 IO 上, 而是连接到 J5 端子上。 芯片的并口输入端连接在 JP2。

如果要想 51 单片机能够获取 74HC165 串行输出的数据, 就必须将单片机管脚通过导线连接到 J5 端子上。 因此需使用 3 根杜邦线将单片机的管脚与 J5 端子连接。 由于 74HC165 模块电路是独立的, 所以使用任意单片机管脚都可以, 为了与我们例程程序配套, 这里使用 P1.5、 P1.6、 P1.7 管脚来控制 74HC165 获取数据, 将 8 个独立按键 JP1 端子顺序连接在 74HC165 模块的并行输入口 JP2 端子上,并用获取的数据体现在 LED 模块上, 即将 74HC165 读取的值赋值给 P2 口控制 LED。

4. 软件设计

本章所要实现的功能是: 独立按键 K1-K8 按下对应控制 D1-D8 亮。

#include <REGX52.H>

// 定义74HC165控制管脚
sbit HC165_QH = P1^7; // 串行数据输出
sbit HC165_CLK = P1^6; // 时钟输入
sbit HC165_SHLD = P1^5; // 寄存器存储控制

#define LED_PORT    P2    // LED模块控制

// 延时函数
void delay_10us(unsigned int ten_us)
{
    while(ten_us--);    
}

// 读取数据函数
// 从74HC165中读取一个字节数据
unsigned char hc165_read_data(void)
{
    unsigned char i = 0;
    unsigned char value = 0;

    HC165_SHLD = 0;// SHLD拉低,165并口数据传入到移位寄存器中
    delay_10us(5); // 短暂延时
    HC165_SHLD=1;  // SHLD拉高,禁止165并口数据传入到移位寄存器中

    for(i = 0; i < 8; i++) // 循环8次读取一个字节
    {
        value <<= 1;   // 左移1位,先读取的数据是高位,经过8次循环则将第一次读取的位放到字节高位
        HC165_CLK = 0; // CLK INH为低电平开始读取数据
        delay_10us(5);

        value |= HC165_QH;
        HC165_CLK = 1; //上升沿读取数据
        delay_10us(5);                
    }    
    return value;    
}

void main()
{    
    unsigned char value = 0;

    LED_PORT = 0xff; // 保证LED默认是熄灭的
    while(1)
    {        
        value = hc165_read_data();
        if(value != 0xff)
            LED_PORT = value;                                
    }        
}

5. 小结

  1. 74HC165移位寄存器:将按键的并行状态转换为串行数据,传输给单片机。
  2. 读取数据:通过时钟信号逐位读取串行数据,并组装成一个8位字节。
  3. 显示数据:将读取到的按键状态直接输出到LED端口,实现按键控制LED的功能。

还是有点抽象啊,那么结合代码再分析一遍:

硬件连接

  • 74HC165:用于将多个按键的并行状态转换为串行数据,并将这些数据传输给单片机。
  • LED模块:通过P2端口控制,显示读取到的按键状态。

代码分析

定义管脚和端口

sbit HC165_QH = P1^7; // 串行数据输出 sbit HC165_CLK = P1^6; // 时钟输入 sbit HC165_SHLD = P1^5; // 寄存器存储控制 #define LED_PORT P2 // LED模块控制

  • HC165_QH:连接到74HC165的串行数据输出引脚,用于读取移位寄存器中的数据。
  • HC165_CLK:连接到74HC165的时钟引脚,用于同步数据传输。
  • HC165_SHLD:连接到74HC165的存储控制引脚,用于锁存并行输入数据。

延时函数

void delay_10us(unsigned int ten_us) { while(ten_us--); }

  • delay_10us:简单的延时函数,以便在特定操作间提供必要的时间间隔。

读取74HC165数据的函数

unsigned char hc165_read_data(void) { unsigned char i = 0; unsigned char value = 0; HC165_SHLD = 0; // SHLD拉低,把并行数据锁存到移位寄存器 delay_10us(5); // 短暂延时 HC165_SHLD = 1; // SHLD拉高,禁止新的并行数据进入移位寄存器 for(i = 0; i < 8; i++) // 循环8次读取一个字节 { value <<= 1; // 左移1位,为新读入的数据腾出位置 HC165_CLK = 0; // 产生一个时钟下降沿 delay_10us(5); value |= HC165_QH; // 读取当前的串行输出位并存入value HC165_CLK = 1; // 产生一个时钟上升沿 delay_10us(5); } return value; // 返回最终读取的8位数据 }

  • HC165_SHLD = 0:将SHLD拉低,将并行输入的数据锁存到移位寄存器中。
  • HC165_SHLD = 1:将SHLD拉高,停止新的并行数据进入移位寄存器。
  • for循环(i<8):通过时钟信号逐位读取串行数据,组装成一个8位的字节。
    • value <<= 1:左移一位,为新读入的数据腾出位置。
    • HC165_CLK = 0 和 HC165_CLK = 1:产生一个完整的时钟脉冲。
    • value |= HC165_QH:读取当前的串行输出位,并将其存入value。

这个函数通过控制时钟信号和存储控制信号,从74HC165移位寄存器中逐位读取串行数据,并将其组合成一个8位的字节返回。

主程序

void main() { unsigned char value = 0; LED_PORT = 0xff; // 初始化LED,设为熄灭状态 while(1) { value = hc165_read_data(); // 调用函数读取数据 if(value != 0xff) // 如果读取到的数据不是0xff(即有按键按下) LED_PORT = value; // 将读取到的数据输出到LED端口 } }

  • LED_PORT = 0xff:初始化LED端口,假设所有LED初始状态为熄灭(0xff表示全高电平)。
  • 无限循环(while(1))
    • hc165_read_data():调用函数从74HC165移位寄存器读取按键状态。
    • if(value != 0xff):检查读取到的值是否不是0xff(即是否有按键按下),因为0xff通常表示没有按键按下。
    • LED_PORT = value:将读取到的按键状态直接输出到LED端口,使对应的LED点亮。

2024.7.24 第一次修订

2024.8.24 第二次修订,后期不再维护

posted @ 2024-08-24 09:51  hazy1k  阅读(33)  评论(0编辑  收藏  举报