STM32利用GPIO复现查理复用

笔记 · 8 天前 · 46 人浏览
STM32利用GPIO复现查理复用

  最近沉迷抖音,无意看到点灯大师的点灯操作,发现了一种有趣的LED控制技术——查理复用(Charlieplexing)。通过这种技术,可以用n个GPIO引脚控制n×(n-1)个LED。本文将详细记录我的学习过程、实现方案以及遇到的问题和解决方法。

查理复用原理

这里不赘述,就想更新一下😎
这里已经足够详细,简单来讲即将LED反并联连接。这里更加详细

  在本文中我们将利用STM32的GPIO引脚三态特性(输出高、输出低、高阻输入)来控制多个LED的技术。其核心思想是:

  1. 每个LED连接在两个不同的GPIO引脚之间
  2. 通过控制引脚的电平状态决定LED是否导通
  3. 任何时候只允许一个电流路径存在

硬件设计

电路原理图

原理图

以下电路连接方式以及真值表:

连接方式
Num GPIO
LED1 PA0(+) → PA1(-)
LED2 PA1(+) → PA0(-)
LED3 PA0(+) → PA2(-)
LED4 PA2(+) → PA0(-)
LED5 PA1(+) → PA2(-)
LED6 PA2(+) → PA1(-)
真值表

realValue

驱动代码部分

已模块封装,便于扩展更多LED。代码需在最后一行留空,否则Keil编译会有警告。

CharlieLED.h
#ifndef __CHARLIELED_H
#define __CHARLIELED_H
#include "stm32f10x.h"                  // Device header

typedef enum {
    LED_1,  // PA0 + PA1 
    LED_2,  // PA1 + PA0
    LED_3,  // PA0 + PA2
    LED_4,  // PA2 + PA0
    LED_5,  // PA1 + PA2
    LED_6   // PA2 + PA1
} CharlieLED;

void Set_CharlieLED(CharlieLED led, FunctionalState state);

#endif
CharlieLED.c
#include "CharlieLED.h"

// FunctionalState 是一个枚举类型,用于表示功能状态的开启或关闭
void Set_CharlieLED(CharlieLED led, FunctionalState state)
{
    static const struct 
        {
        uint16_t anode_pin;
        uint16_t cathode_pin;
        } led_map[6] = {
        {GPIO_Pin_0, GPIO_Pin_1}, // LED1
        {GPIO_Pin_1, GPIO_Pin_0}, // LED2
        {GPIO_Pin_0, GPIO_Pin_2}, // LED3
        {GPIO_Pin_2, GPIO_Pin_0}, // LED4
        {GPIO_Pin_1, GPIO_Pin_2}, // LED5
        {GPIO_Pin_2, GPIO_Pin_1}  // LED6
    };

    // 初始化  1. 先全部设为高阻态(安全隔离)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 默认保持彻底高阻态

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 判断是否点亮
    if (state == ENABLE)
    {
         // 2. 配置阳极(推挽输出高)
        GPIO_SetBits(GPIOA, led_map[led].anode_pin);   // 高电平
        GPIO_InitStructure.GPIO_Pin = led_map[led].anode_pin;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);

         // 3. 配置阴极(推挽输出低)
        GPIO_ResetBits(GPIOA, led_map[led].cathode_pin); // 低电平
        GPIO_InitStructure.GPIO_Pin = led_map[led].cathode_pin;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
}

学习笔记


1️⃣. 为何不使用开漏输出而是推挽输出?
  推挽输出首选是因为它内置了完整的驱动能力,高电平时通过PMOS强上拉,低电平时通过NMOS强下拉,无需外接元件。而开漏输出在高电平时依赖外部上拉电阻,会导致驱动能力不足、电路复杂化,且在多LED系统中可能引发电流路径冲突。
  在驱动实现中,推挽输出相比开漏输出能直接提供高/低电平驱动能力,无需外接上拉电阻,可双向电流快速切换LED状态,降低功耗并提高响应速度,同时简化电路设计(省去上拉网络)。直白来讲,推挽输出对IO有绝对的控制权

2️⃣. 为什么必须用高阻态?
  在查理复用中,高阻态是确保LED精确控制的关键。高阻态可以彻底断开引脚,防止非目标LED因漏电微亮,通过GPIO_Mode_IN_FLOATING实现真正电气隔离,确保每次只有一条确定的电流路径。

3️⃣. 切换机制是怎样的?
  首先将所有引脚默认设为高阻态(电气隔离),然后配置目标阳极为推挽输出高,最后设置阴极为推挽输出低。

4️⃣. 如何进行扩展,软件应该怎样实现?
  可以知道该原理为n个GPIO可控制n×(n-1)个LED,沿用二维数组存储引脚组合。然后需先建立引脚映射表,然后加以扩展,如动态扫描(定时器中断控制)、PWM 调光(呼吸灯、亮度渐变)、矩阵键盘(输入检测)以及多路 ADC 模拟信号采集等。查理复用的核心思想是 “分时复用 + 三态控制”,只要灵活运用,可以用极少的 GPIO 实现复杂的交互功能!

5️⃣. 多路 ADC 模拟信号采集具体方式?
  利用GPIO的三态特性(高电平、低电平、高阻态)分时切换多个模拟开关,将不同传感器轮流连接到同一路ADC上。
  例如,3个GPIO通过组合控制可管理6个传感器——当GPIO1输出高、GPIO2输出低、GPIO3设为高阻时,选通传感器1;切换其他组合即可选通其他传感器。每次仅一个传感器被连接,ADC分时读取各信号。其关键点在于:
  🟢通过GPIO状态组合动态控制模拟开关;
  🔵严格分时避免信号冲突;
  🟣切换后需短暂延时确保信号稳定。虽以最少引脚实现多路采集,但需注意抗干扰和时序控制。
一句话总结:通过动态切换GPIO输入/输出状态或控制模拟开关,实现多路模拟信号的分时采集。

STM32
  1. xxcheng 7 天前

    原来还能这样玩 GPIO!用 STM32 的三态特性实现查理复用,3 个引脚就能点亮 6 个 LED,代码还做了模块封装,学到了推挽输出和高阻态的关键应用~这种分时复用的思路太妙了,感觉还能拓展到传感器采集之类的场景,收藏起来慢慢研究!

  2. L 7 天前

    哇 很有用 期待下一次的内容

Theme Jasmine by Kent Liao

本网站由 又拍云 提供CDN加速/云存储服务

鄂ICP备2023005457号    鄂公网安备 42011302000815号