STM32G071RB与MC74HC165A实现高效IO扩展方案

发布时间:2026/7/4 18:42:48
STM32G071RB与MC74HC165A实现高效IO扩展方案 1. 项目背景与核心价值在嵌入式系统开发中IO资源紧张是常见的设计瓶颈。当需要接入大量输入设备如按钮阵列时传统方案要么需要占用大量MCU引脚要么需要复杂的扩展电路设计。MC74HC165A这款8位并行输入/串行输出移位寄存器配合STM32G071RB的SPI接口可以完美解决这个问题。我最近在一个工业控制面板项目中需要接入16个功能按钮。如果直接连接需要占用16个GPIO而STM32G071RB总共只有38个可用IO。通过MC74HC165A方案最终仅用4个SPI引脚就实现了全部按钮的检测同时支持所有按钮同时按下的场景。这种方案特别适合需要精简布线、节省IO资源的应用场景。2. 硬件架构解析2.1 MC74HC165A关键特性这款移位寄存器有三个核心优势并行加载通过PL(Parallel Load)引脚可以一次性锁存8位输入状态串行输出通过Q7引脚配合时钟信号逐位输出数据级联能力Q7输出可以直接连接下一片的SER输入实现多片级联典型参数工作电压2V-6V完美匹配STM32的3.3V逻辑最大时钟频率36MHz 4.5V输入电流±1μA超低功耗传输延迟13ns满足实时控制需求2.2 STM32G071RB接口设计STM32G071RB的SPI1接口配置要点// SPI初始化关键参数 hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_32; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB;硬件连接示意图MC74HC165A1 MC74HC165A2 STM32G071RB ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ PL ┌──┼───┤SER │ │ │ │ CP │ │ │ │ │ SPI_SCK│ │ Q7 ├──┘ │ │ │ │ └─────────────┘ └─────────────┘ └─────────┘3. 软件实现细节3.1 数据读取时序读取两片级联的165A需要特别注意时序拉低PL引脚至少35ns加载并行数据拉高PL后在CP上升沿移位数据连续读取3字节实际只用前2字节void ReadButtons(uint16_t *data) { HAL_GPIO_WritePin(PL_GPIO_Port, PL_Pin, GPIO_PIN_RESET); delay_ns(50); HAL_GPIO_WritePin(PL_GPIO_Port, PL_Pin, GPIO_PIN_SET); uint8_t buf[3]; HAL_SPI_Receive(hspi1, buf, 3, 100); *data (buf[0] 8) | buf[1]; }3.2 按键消抖处理实测中发现机械按键需要至少5ms的消抖延时。我的优化方案#define DEBOUNCE_TIME 10 // ms uint16_t last_valid_state 0; uint32_t last_change_time 0; void PollButtons() { uint16_t current_state; ReadButtons(current_state); if(current_state ! last_valid_state) { if(HAL_GetTick() - last_change_time DEBOUNCE_TIME) { // 状态变化处理 last_valid_state current_state; } } else { last_change_time HAL_GetTick(); } }4. 性能优化技巧4.1 SPI时钟配置通过示波器实测发现当SPI时钟超过8MHz时165A的输出数据会出现畸变。建议配置hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_8; // 8MHz 64MHz PCLK4.2 中断驱动方案相比轮询方式使用定时器中断可以降低CPU占用void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim6) { // 10ms定时器 PollButtons(); } }4.3 功耗优化在低功耗应用中可以动态关闭SPI外设void EnterLowPowerMode() { HAL_SPI_DeInit(hspi1); __HAL_RCC_SPI1_CLK_DISABLE(); } void WakeUp() { __HAL_RCC_SPI1_CLK_ENABLE(); MX_SPI1_Init(); }5. 常见问题排查5.1 数据移位错误症状读取的数据位错位 解决方法检查CP极性配置应设置为模式0确认PL信号宽度 35ns测量时钟信号质量示波器观察是否有振铃5.2 多片级联异常症状第二片数据不正确 排查步骤确认级联顺序Q7→SER检查片间走线长度建议10cm在级联处加100Ω电阻消除反射5.3 按键响应延迟优化方向减小SPI时钟分频改用DMA传输优化消抖算法如改用状态机实现6. 扩展应用实例6.1 工业控制面板通过4片165A级联实现32路按钮检测uint32_t Read32Buttons() { uint8_t buf[5]; // 加载并行数据 HAL_GPIO_WritePin(PL_GPIO_Port, PL_Pin, GPIO_PIN_RESET); delay_ns(50); HAL_GPIO_WritePin(PL_GPIO_Port, PL_Pin, GPIO_PIN_SET); // 读取4字节1冗余 HAL_SPI_Receive(hspi1, buf, 5, 100); return (buf[0]24)|(buf[1]16)|(buf[2]8)|buf[3]; }6.2 旋转编码器接口配合165A读取多个编码器状态struct Encoder { uint8_t last_state; int32_t count; }; void UpdateEncoders(struct Encoder encoders[], uint16_t current_state) { for(int i0; i8; i) { uint8_t state (current_state (i*2)) 0x03; uint8_t change encoders[i].last_state ^ state; if(change) { // 编码器计数逻辑 encoders[i].count (state 0b00 encoders[i].last_state 0b10) ? 1 : -1; encoders[i].last_state state; } } }7. 替代方案对比当IO资源更为紧张时可以考虑以下方案方案引脚需求最大扩展优缺点MC74HC165A4线理论上无限级联成本低但需要软件移位I2C GPIO扩展器2线受地址限制协议复杂速度较慢矩阵扫描NM线N×M按键需要二极管不支持全键按下专用键盘控制器1线(UART)视芯片而定成本高集成度高在最近的一个家电控制项目中我对比测试了165A和PCF8574方案。虽然I2C方案更省引脚但在电磁干扰较强的环境中165A的稳定性明显更优最终采用了2片165A级联的方案。