第26章 IO扩展(并转串)
第二十六章 IO扩展(并转串)
1. 导入
在独立按键实验章节中, 假如使用的独立按键数不止 8 个, 而是 16 个或者更多, 这时 51 单片机的 IO 口就显得非常拮据, 因此就需要扩展单片机 IO 口。
开发板板载一个 74HC165芯片, 本章就来介绍如何来扩展单片机 IO 口, 通过本章的学习, 让大家学会使用 74HC165 芯片来扩展 IO 口。 本章所要实现的功能是: 使用开发板上的 74HC165模块扩展 8 位 IO 与独立按键连接, 通过独立按键控制 LED 灯。
2. 74HC165芯片介绍
74HC165 是 8 位并行输入串行输出移位寄存器,其管脚封装及功能如下:
-
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 模块电路如下图所示:
从上图中可以看出, 该电路是独立的, 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. 小结
- 74HC165移位寄存器:将按键的并行状态转换为串行数据,传输给单片机。
- 读取数据:通过时钟信号逐位读取串行数据,并组装成一个8位字节。
- 显示数据:将读取到的按键状态直接输出到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 第二次修订,后期不再维护