当前位置: 首页 > 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.jsqmd.com/news/946982/

相关文章:

  • Harness Engineering:Agent自主决策审计
  • Android混合开发避坑指南:WebView与H5通信的5种姿势与安全实践
  • 2026降AIGC革命:AI率92%暴降至5%!实测10款降AI率工具!薅羊毛技巧!
  • 别再用BertModel直接喂给Chroma了!手写一个EmbeddingFunction解决HuggingFaceEmbeddings离线调用难题
  • AUTOSAR SPI实战避坑:同步调用Spi_SyncTransmit阻塞了CPU?试试异步Spi_AsyncTransmit提升效率
  • 深入探秘 Golang 源码中 channel 管道通信的真正设计意图与边界
  • 用MATLAB批量生成卫星TLE文件:STK11自动化脚本实战(附完整代码)
  • DDD-013:仓储(Repository)
  • Python 爬虫进阶技巧:批量解析 html 实体转义字符还原原始文本
  • Xcode 15开发者的终端效率手册:除了CMD+R运行,你的快捷键还缺这一块
  • 从Demo到量产:Davinci工程添加自定义模块与变体文件的完整指南(以BRS模块为例)
  • 告别WebView黑盒:用Chrome DevTools调试Android混合开发页面(附Androidx-WebKit实战)
  • 钢材表面缺陷检测实战工程:含NEU-DET数据集与YOLOv5/v8多版本训练配置
  • 2026深度测评10款降AI率软件红黑榜!优缺点全曝光,达标率直接对标行业天花板
  • 绝区零自动化脚本终极指南:3分钟快速上手完整教程
  • 用FPGA控制步进电机是种什么体验?从状态机到分频器,详解Verilog驱动A4988全流程
  • 企业级AI角色扮演对话系统
  • MATLAB图像质量评价避坑指南:为什么你的PSNR/SSIM结果和OpenCV差那么多?
  • 你的旧笔记本别扔!巧用闲置MiniPCIe接口,低成本变身4G物联网网关或监控终端
  • Apex Legends智能压枪助手终极指南:10分钟掌握精准射击
  • 零基础如何学会Appium自动化测试
  • 用MATLAB复现DWA算法:从二维到三维,手把手教你搞定无人机避障路径规划
  • 1、VTK+QT + cmake编程 三维圆柱体
  • 保姆级教程:华为交换机DHCP地址池配置与查询全流程(含防IP冲突指南)
  • 如何2分钟搞定iPhone在Windows上的网络共享:终极驱动安装方案
  • Spring AI Alibaba-ChatClient
  • MATLAB环境下可直接运行的KNN分类代码包:含主程序、核心函数与调用说明
  • 2026学术写作新范式:Gemini 3.1 Pro、Claude 3.5与GPT-4o协同润色实战指南
  • Appium Inspector 保姆级配置指南:从Desired Capabilities到元素定位,一次搞定
  • 别再死记硬背CSRF原理了!用Pikachu靶场实战Get/Post/Token三种攻击,手把手教你复现