当前位置: 首页 > news >正文

用STM32F103C8T6搞定74HC165扩展16个按键(附完整代码和接线图)

STM32F103C8T6与74HC165实现16键扩展实战指南

在嵌入式开发中,GPIO资源紧张是常见问题。当我们需要控制多个按键时,直接连接会占用大量引脚。本文将详细介绍如何利用STM32F103C8T6和74HC165移位寄存器芯片,仅用3个GPIO引脚实现16个独立按键的扩展方案。

1. 硬件准备与原理分析

74HC165是一款8位并行输入/串行输出移位寄存器,通过级联两片74HC165可以实现16位输入的扩展。其核心工作原理是将并行输入的按键状态转换为串行数据输出,从而大幅减少MCU引脚占用。

所需材料清单

  • STM32F103C8T6最小系统板 ×1
  • 74HC165芯片 ×2
  • 按键开关 ×16
  • 10kΩ电阻 ×16(用于按键上拉)
  • 面包板或洞洞板
  • 杜邦线若干

关键引脚连接说明

STM32引脚74HC165引脚功能描述
PB6CLK时钟信号
PC0SH/LD加载控制
PB5QH数据输出

两片74HC165通过QH引脚串联,第二片的QH输出连接到STM32的PB5引脚。这种级联方式可以扩展输入通道而不增加STM32的引脚占用。

2. 硬件电路搭建

正确的硬件连接是项目成功的基础。以下是详细的接线步骤和注意事项:

  1. 电源连接

    • 将两片74HC165的VCC引脚接3.3V
    • GND引脚接地
    • /CE引脚接地(使能芯片)
  2. 级联配置

    • 第一片74HC165的QH引脚连接第二片的SER引脚
    • 第二片的QH引脚连接STM32的PB5
    • 两片的CLK和SH/LD引脚分别并联后接STM32
  3. 按键连接

    • 每个按键一端接地,另一端接74HC165的并行输入引脚(A-H)
    • 建议添加10kΩ上拉电阻,确保未按下时为高电平

注意:实际焊接或插接时,建议先完成电源部分的连接,用万用表确认无误后再进行信号线的连接,避免短路损坏芯片。

常见接线错误排查

  • 按键无反应:检查SH/LD信号是否正常,时钟信号是否产生
  • 数据错位:确认级联顺序是否正确,QH到SER的连接是否可靠
  • 电平不稳定:检查上拉电阻是否接好,电源是否稳定

3. STM32CubeMX配置

使用STM32CubeMX工具可以快速完成GPIO初始化配置:

  1. 新建工程,选择STM32F103C8T6型号

  2. 配置系统时钟为72MHz(外部晶振8MHz)

  3. GPIO配置:

    • PB6设置为GPIO_Output(CLK信号)
    • PC0设置为GPIO_Output(SH/LD信号)
    • PB5设置为GPIO_Input(数据输入)
  4. 生成代码时注意:

    • 选择HAL库
    • 为方便调试,可以启用USART1用于状态输出

关键配置代码片段:

/* GPIO初始化函数 */ void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); /*Configure GPIO pins : PB6 PC0 */ GPIO_InitStruct.Pin = GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_0; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /*Configure GPIO pin : PB5 */ GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); }

4. 按键读取与消抖处理

74HC165的数据读取需要遵循特定的时序:

  1. 拉低SH/LD引脚,加载当前按键状态
  2. 拉高SH/LD引脚,准备移位输出
  3. 通过CLK信号控制,逐位移出数据
  4. 组合16位数据得到按键状态

优化后的读取函数

#define HC165_CLK_PIN GPIO_PIN_6 #define HC165_CLK_PORT GPIOB #define HC165_LD_PIN GPIO_PIN_0 #define HC165_LD_PORT GPIOC #define HC165_DATA_PIN GPIO_PIN_5 #define HC165_DATA_PORT GPIOB uint16_t Read_74HC165(void) { uint16_t key_state = 0; // 加载并行数据 HAL_GPIO_WritePin(HC165_LD_PORT, HC165_LD_PIN, GPIO_PIN_RESET); HAL_Delay(1); // 保持低电平至少20ns HAL_GPIO_WritePin(HC165_LD_PORT, HC165_LD_PIN, GPIO_PIN_SET); // 读取第一位数据 if(HAL_GPIO_ReadPin(HC165_DATA_PORT, HC165_DATA_PIN) == GPIO_PIN_SET) key_state |= 0x0001; // 移位读取剩余15位 for(uint8_t i=0; i<15; i++) { key_state <<= 1; HAL_GPIO_WritePin(HC165_CLK_PORT, HC165_CLK_PIN, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(HC165_CLK_PORT, HC165_CLK_PIN, GPIO_PIN_SET); if(HAL_GPIO_ReadPin(HC165_DATA_PORT, HC165_DATA_PIN) == GPIO_PIN_SET) key_state |= 0x0001; } return key_state; }

按键消抖处理: 机械按键在按下和释放时会产生抖动,通常持续5-20ms。可以采用以下方法处理:

  1. 硬件消抖:在按键两端并联0.1μF电容
  2. 软件消抖:连续多次采样确认状态
// 带消抖的按键状态检测 uint16_t Get_Stable_Keys(void) { static uint16_t last_state = 0xFFFF; uint16_t current_state = Read_74HC165(); if(current_state == last_state) return current_state; else { HAL_Delay(20); // 等待20ms消抖 last_state = Read_74HC165(); return last_state; } }

5. 实际应用与性能优化

在游戏手柄或控制面板等应用中,可以考虑以下优化措施:

  1. 中断驱动:将按键状态变化通过外部中断通知MCU,降低轮询开销
  2. 状态机处理:实现按键按下、保持、释放等不同状态的识别
  3. 多级联扩展:通过增加74HC165芯片,可以进一步扩展更多按键

典型应用代码框架

void Key_Process(uint16_t key_state) { static uint16_t last_key = 0xFFFF; uint16_t changed = key_state ^ last_key; // 检测按键按下 for(uint8_t i=0; i<16; i++) { if(changed & (1<<i)) { if(key_state & (1<<i)) printf("Key %d released\r\n", i); else printf("Key %d pressed\r\n", i); } } last_key = key_state; } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); while(1) { uint16_t keys = Get_Stable_Keys(); Key_Process(keys); HAL_Delay(10); // 10ms轮询间隔 } }

在实际项目中,74HC165的时钟频率最高可达25MHz(3.3V供电时),但考虑到STM32的GPIO操作速度和实际需求,通常使用1-10kHz的时钟频率即可。过高的时钟频率可能导致信号完整性问题,特别是在面包板或飞线连接的情况下。

http://www.zskr.cn/news/1458481.html

相关文章:

  • 2026降AIGC革命:AI率92%暴降至5%!实测10款降AI率工具!薅羊毛技巧!
  • 深入探秘 Golang 源码中 channel 管道通信的真正设计意图与边界
  • 绝区零自动化脚本终极指南:3分钟快速上手完整教程
  • Xcode 15开发者的终端效率手册:除了CMD+R运行,你的快捷键还缺这一块
  • 告别WebView黑盒:用Chrome DevTools调试Android混合开发页面(附Androidx-WebKit实战)
  • MATLAB图像质量评价避坑指南:为什么你的PSNR/SSIM结果和OpenCV差那么多?
  • 你的旧笔记本别扔!巧用闲置MiniPCIe接口,低成本变身4G物联网网关或监控终端
  • 1、VTK+QT + cmake编程 三维圆柱体
  • 如何2分钟搞定iPhone在Windows上的网络共享:终极驱动安装方案
  • FlagOS实现DeepSeekV4八芯片Day0适配技术解析
  • 保姆级教程:I3C总线初始化与动态地址分配实战(基于SDR模式)
  • 蓝桥杯5G仿真平台保姆级配置指南:从BBU到核心网,手把手带你打通第一个5G呼叫
  • 2026年实测AI写作辅助平台榜单(实测甄选版)
  • 从零开始组装电脑:硬件选型、兼容性检查与装机全流程实战指南
  • Qwen3.6-Plus实战:8分钟生成可部署官网的前端工作流
  • RK3568双网口开发板,u-boot下如何固定网络设备?一个env变量ethact就搞定
  • 创客教育中电路设计的多元应用:从模块化到生活场景实践
  • SpringBoot项目OOM排查实录:一个10MB的max-http-header-size配置是如何吃光8G堆内存的
  • 消费返利模式的底层困局:为什么很多平台从一开始就走不远?
  • KAN实战:用5行代码解决偏微分方程,参数效率比传统PINNs高100倍
  • C++多线程安全传参避坑指南:detach()模式下如何正确传递指针和对象?
  • 告别Windows 7!手把手教你用DevEco Studio 2.0.12.201搭建鸿蒙开发环境(附华为账号注册避坑)
  • 从汽车悬架到手机陀螺仪:阻尼振动微分方程在工程中的实际应用盘点
  • 别再让一条宽带拖后腿!H3C防火墙双WAN口负载均衡保姆级配置(附HCL模拟器避坑点)
  • DS18B20测温不准?可能是你的51单片机时序搞错了(AT89C51实战调试心得)
  • Kimi K2.5多智能体协作:任务拆解×角色分工×结果整合
  • 量子不变量在4维流形拓扑研究中的应用
  • 数模小白别乱报!2024年这5个竞赛含金量、难度、适合人群全解析(附数维杯报名攻略)
  • 直流电机改造与太阳能控制器应用:构建人力驱动离网发电系统
  • 基于Arduino与NDIR传感器的巨型模拟CO2监测仪设计与实现