简介
数码管相信大家都不陌生, 其数码管一共分为共阴极数码管, 或者共阳极数码管. 其中的com则为公共端. 而数码管的点亮主要是通过GPIO来控制其中的某一个段的点亮. 然后通过控制不同的段点亮的组合从而来显示对一个的数字. 而对于多个数码管的点亮的话主要是通过扫描的方式在某一个时间只点亮其中的一位(节省IO资源), 但是由于扫描的速度非常快, 从而使人眼看到多个数码管点亮的情况.如果不使用任何的外置芯片控制单位数码管的点亮的话, 那么至少是需要7-9个IO. 而点亮多位数码管的话,情况会变得更加复杂.

图片来源于(侵权请联系删除): 元器件科普之LED数码管的原理和应用
TM1637简介
TM1637 是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU 数字 接口、数据锁存器、LED 高压驱动、键盘扫描等电路. 其单颗芯片即可控制4位数码管的驱动.

其引脚定义如上所示, 最多的位输出一共支持6位输出, 即同时驱动6个数码管. 但是由于手中的数码管模块只有四个,所以其中的两位是用不上的.

背面

通过使用TM1637只需要使用两个IO便可以控制四位数码管的显示. 接下来我们使用ESP32, 基于ESP-IDF来控制这四位数码管的显示.
1- 首先编辑idf_component.yml 增加库文件依赖
dependencies:
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
nopnop2002/tm1637:
path: components/tm1637/
git: https://github.com/nopnop2002/esp-idf-tm1637.git2- 编译项目, 然后打开menuconfig, 配置对应的PIN

3- 使用esp-idf-tm1637提供的代码进行测试
/**
* @file app_main.c
* @brief 使用 TM1637 数码管的示例应用程序
*/
#include <stdio.h>
#include <stdbool.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <time.h>
#include <sys/time.h>
#include <esp_system.h>
#include <driver/gpio.h>
#include <esp_log.h>
#include "sdkconfig.h"
#include "tm1637.h"
#define TAG "app"
// 从 menuconfig 配置中获取 CLK 和 DIO 引脚
const gpio_num_t LED_CLK = CONFIG_TM1637_CLK_PIN;
const gpio_num_t LED_DTA = CONFIG_TM1637_DIO_PIN;
// 数码管测试任务
void tm1637_task(void *arg)
{
// 初始化 TM1637 数码管
tm1637_led_t *led = tm1637_init(LED_CLK, LED_DTA);
if (led == NULL)
vTaskDelete(NULL); // 初始化失败则删除任务
while (true)
{
// **测试段选控制**:依次点亮每一段,形成流水灯效果
uint8_t seg_data[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
for (uint8_t x = 0; x < 32; ++x)
{
uint8_t v_seg_data = seg_data[x % 6];
for (int i = 0; i < led->segment_max; ++i)
{
tm1637_set_segment_fixed(led, led->segment_idx[i], v_seg_data);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
// **测试亮度调节**
for (int x = 0; x < 7; x++)
{
tm1637_set_brightness(led, x);
for (int i = 0; i < led->segment_max; ++i)
{
tm1637_set_segment_fixed(led, led->segment_idx[i], 0xFF); // 全亮
}
vTaskDelay(30 / portTICK_PERIOD_MS);
}
vTaskDelay(100 / portTICK_PERIOD_MS);
// **测试整数显示(靠右对齐)**
tm1637_set_number(led, 1, true, 0x00); // 显示 0001
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, 12, true, 0x00); // 显示 0012
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, 123, true, 0x00); // 显示 0123
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, 1234, true, 0x00); // 显示 1234
vTaskDelay(100 / portTICK_PERIOD_MS);
if (led->segment_max == 6)
{
tm1637_set_number(led, 12345, true, 0x00); // 显示 12345
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, 123456, true, 0x00); // 显示 123456
vTaskDelay(100 / portTICK_PERIOD_MS);
}
// **测试整数显示(靠左对齐)**
tm1637_set_number(led, 1, false, 0x00); // 显示 ____1
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, 12, false, 0x00); // 显示 ___12
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, 123, false, 0x00); // 显示 __123
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, 1234, false, 0x00); // 显示 _1234
vTaskDelay(100 / portTICK_PERIOD_MS);
if (led->segment_max == 6)
{
tm1637_set_number(led, 12345, false, 0x00); // 显示 12345
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, 123456, false, 0x00); // 显示 123456
vTaskDelay(100 / portTICK_PERIOD_MS);
}
// **测试负数显示(靠右)**
tm1637_set_number(led, -1, true, 0x00); // 显示 -001
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, -12, true, 0x00); // 显示 -012
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, -123, true, 0x00); // 显示 -123
vTaskDelay(100 / portTICK_PERIOD_MS);
if (led->segment_max == 6)
{
tm1637_set_number(led, -1234, true, 0x00); // 显示 -1234
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, -12345, true, 0x00); // 显示 -12345
vTaskDelay(100 / portTICK_PERIOD_MS);
}
// **测试负数显示(靠左)**
tm1637_set_number(led, -1, false, 0x00); // 显示 ____-1
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, -12, false, 0x00); // 显示 ___-12
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, -123, false, 0x00); // 显示 __-123
vTaskDelay(100 / portTICK_PERIOD_MS);
if (led->segment_max == 6)
{
tm1637_set_number(led, -1234, false, 0x00); // 显示 _-1234
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_number(led, -12345, false, 0x00); // 显示 -12345
vTaskDelay(100 / portTICK_PERIOD_MS);
}
// **测试文本显示**
if (led->segment_max == 4)
{
tm1637_set_segment_ascii(led, "PLAY"); // 4位显示
}
else
{
tm1637_set_segment_ascii(led, " PLAY "); // 6位居中显示
}
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_segment_ascii(led, "1234567890"); // 显示前6位
vTaskDelay(100 / portTICK_PERIOD_MS);
tm1637_set_segment_ascii(led, "IP 192.168.10.20"); // 仅显示前几位
vTaskDelay(100 / portTICK_PERIOD_MS);
if (led->segment_max == 4)
{
tm1637_set_segment_ascii(led, "STOP");
}
else
{
tm1637_set_segment_ascii(led, " STOP ");
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
// 主应用入口
void app_main()
{
// 创建 TM1637 数码管测试任务
xTaskCreate(&tm1637_task, "tm1637_task", 1024 * 4, NULL, 5, NULL);
}实验效果如下

显示 0012

总结
本文主要对数码管的工作原理进行了简要的阐述,以及对TM1637芯片进行了介绍. 同时使用ESP-IDF结合对应的组件管理器的驱动库, 对TM1637的四位数码管进行了驱动, 使其可以驱动显示数字和动画等. 大家可以下载这个工程进行尝试, 驱动库文件较为完善, 可以直接使用.
附件
我要赚赏金
