【外设移植】TM1637 + M61-32S

[复制链接]
查看565 | 回复16 | 2024-4-1 01:20:51 | 显示全部楼层 |阅读模式
本帖最后由 WangChong 于 2024-4-16 20:34 编辑

一:  TM1637 规格参数及其硬件原理



TM1637 是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU 数
字接口、数据锁存器、LED 高压驱动、键盘扫描等电路。本产品性能优良,质量可靠。主要应用于电磁炉、
微波炉及小家电产品的显示屏驱动。



360截图16261001272175.png

管脚定义信息如下:
360截图178605319889102.png

其中和单片机的通讯使用类I2C的自定义通讯协议。可以通过串行Data和Clock时钟线将数据写到对应的寄存器(低位在前),从而往对应的六个寄存器内写数据来驱动最多六个数码管,但是由于模组设计的原因,在TM1637数码管驱动上最多可以驱动4个,也就是正好4位28段。

通信时序:
360截图187508159612098.png

起始信号:IO初始化之后,保持高电平, 在CLK为高电平期间 ,DIO 从高拉低。
结束信号:在ACK结束之后,SCK低电平期和SDA低电平期间,SCK先拉高,然后DIO 再拉高。
接受从机ACK:TM1637 的数据传输带有应答信号 ACK,当传输数据正确时,会在第八个时钟的下降沿,芯片内部会
产生一个应答信号 ACK 将 DIO 管脚拉低,在第九个时钟结束之后释放DIO口线
指令数据:在输入数据时当 CLK 是高电平时,DIO  上的信号必须保持不变;只有 CLK 上的时钟信号为低电平时,DIO  上的信号才能改变。数据输入的开始条件是 CLK为高电平时,DIO  由高变低;结束条件是 CLK 为高时,DIO由低电平变为高电平。

2- 写 SRAM 数据地址自动加 1 模式
360截图176402206811785.png

上述所说,通过往地址寄存器里写入数据可以控制数码管的显示,这里的写SARM数据地址自动加1模式的意思是,比如说我现在要往四个数码管写如数字1234, 通过使用这个模式,只需要往第一个数码管寄存器内写入1的数据后,数码管的地址会逐渐偏移 从原本的0x01 逐渐+1。 0x01,写入1, 0x02,写入2, 0x03,写入3,0x04,写入4。 而不需要每次去指定写入的地址。

3- 写 SRAM 数据固定地址模式
360截图16270830253137.png

写SRAM固定地址的模式的意思是说,通过往固定的地址内写入数据来操控对应的数码管显示。


官方程序Sequence diagram
360截图17290502225630.png
官方推荐的Sequence diagram中包括了我们程序所要写的处理逻辑,但是实际上我们并不需要来处理是否用用户按下按键的操作逻辑,所以我们再发送显示控制命令后,可以直接结束。

数据命令相关(Command)
360截图17290503718258.png

我们用于写数据驱动数码管,所以可以仅仅关注数据指令0100 0000 (0x40H)

地址命令相关
360截图17571115467990.png
这里地址命令我们仅仅需要关注的只有前四个,因为这个数码管设计虽然能驱动6个但是硬件实现貌似只有四个,地址分别为(0xC0H),(0xC1H)  (0xC2H)  (0xC3H)
来控制第一、第二、第三、第四个数码管显示。

显示控制
360截图18720116313352.png

通过往显示控制寄存器中写入不同的数据来控制数码管的显示。经过我的实际测试和上述寄存器定义,点亮数码管的HEX为从0x88, 到0x8F.  LSB第四位为1的原因是因为1是用来控制开关,所以是8. 也就是0x80. 所以亮度可以从0x80到0x8f。


二: 程序以及测试
我这里一共提供了两个文件,分别是头文件TM1637.h 和源文件TM1637.C. 具体的使用方法是,复制这两个文件到项目中,并且在Cmakelist.txt target_sources中引入即可。
在搞明白原理之后,我们来看一下代码和硬件连接部分,可以使用任意的板载GPIO口和TM1637进行连接。只需要在代码宏中更改IO定义。
微信图片_20240401010441.jpg 微信图片_20240401010435.jpg


我这里使用的是PIN16 (TM1637_CLOCK) 和 PIN17(TM1637_DATA) 分别连接模块的 CLK和DIO。 VCC-VCC, GND-GND

头文件:
  1. #ifndef __TM1637__
  2. #define __TM1637__
  3. #include "bflb_mtimer.h"
  4. #include "bflb_gpio.h"

  5. // 亮度等级
  6. enum LIGHT_DEGREE
  7. {
  8.     LEVEL1 = 0x88, // 设置脉冲宽度为 1/16
  9.     LEVEL2 = 0x89, // 设置脉冲宽度为 2/16
  10.     LEVEL3 = 0x8A, // 设置脉冲宽度为 4/16
  11.     LEVEL4 = 0x8B, // 设置脉冲宽度为 10/16
  12.     LEVEL5 = 0x8C, // 设置脉冲宽度为 11/16
  13.     LEVEL6 = 0x8D, // 设置脉冲宽度为 12/16
  14.     LEVEL7 = 0x8E, // 设置脉冲宽度为 13/16
  15.     LEVEL8 = 0x8F, // 设置脉冲宽度为 14/16
  16. };

  17. /*定义时钟PIN*/
  18. #define TM1637_CLOCK GPIO_PIN_16
  19. /*定义dataPIN*/
  20. #define TM1637_DATA GPIO_PIN_17

  21. /**
  22. * @brief 初始化GPIO
  23. *
  24. */
  25. void TM1637_init();

  26. /**
  27. * @brief 使数码管显示数字
  28. *
  29. * @param numer1 第一个数字
  30. * @param numer2 第二个数字
  31. * @param numer3 第三个数字
  32. * @param numer4 第四个数字
  33. * @param colon  是否显示冒号(:) 接受参数 0 or 1
  34. * 0 不显示
  35. * 1 显示
  36. * @param level 亮度等级,
  37. */
  38. void TM1637_display(unsigned char numer1, unsigned char numer2, unsigned char numer3, unsigned char numer4, unsigned char colon, enum LIGHT_DEGREE level);

  39. #endif
复制代码

源文件
  1. #include "TM1637.h"

  2. struct bflb_device_s *gpio;

  3. /* 数码管支持的数字 */
  4. unsigned char tab[] = {
  5.     0x3F, /*0*/
  6.     0x06, /*1*/
  7.     0x5B, /*2*/
  8.     0x4F, /*3*/
  9.     0x66, /*4*/
  10.     0x6D, /*5*/
  11.     0x7D, /*6*/
  12.     0x07, /*7*/
  13.     0x7F, /*8*/
  14.     0x6F, /*9*/
  15.     0x77, /*10 A*/
  16.     0x7C, /*11 b*/
  17.     0x58, /*12 c*/
  18.     0x5E, /*13 d*/
  19.     0x79, /*14 E*/
  20.     0x71, /*15 F*/
  21.     0x76, /*16 H*/
  22.     0x38, /*17 L*/
  23.     0x54, /*18 n*/
  24.     0x73, /*19 P*/
  25.     0x3E, /*20 U*/
  26.     0x00, /*21 黑屏*/
  27. };

  28. void TM1637_init()
  29. {
  30.     gpio = bflb_device_get_by_name("gpio");
  31.     bflb_gpio_init(gpio, TM1637_CLOCK, GPIO_OUTPUT); // 修改为 TM1637_CLOCK
  32.     bflb_gpio_init(gpio, TM1637_DATA, GPIO_OUTPUT);  // 修改为 TM1637_DATA
  33. }

  34. void clock_set()
  35. {
  36.     bflb_gpio_set(gpio, TM1637_CLOCK); // 修改为 TM1637_CLOCK
  37. }

  38. void clock_reset()
  39. {
  40.     bflb_gpio_reset(gpio, TM1637_CLOCK); // 修改为 TM1637_CLOCK
  41. }

  42. void data_set()
  43. {
  44.     bflb_gpio_set(gpio, TM1637_DATA); // 修改为 TM1637_DATA
  45. }

  46. void data_reset()
  47. {
  48.     bflb_gpio_reset(gpio, TM1637_DATA); // 修改为 TM1637_DATA
  49. }

  50. void Delay_us(unsigned int us)
  51. {
  52.     bflb_mtimer_delay_us(us);
  53. }

  54. void TM1637_start(void)
  55. {
  56.     clock_set();
  57.     data_set();
  58.     Delay_us(2);
  59.     data_reset();
  60. }

  61. void TM1637_ack(void)
  62. {
  63.     unsigned char i = 0;
  64.     clock_reset();
  65.     Delay_us(5);
  66.     while (bflb_gpio_read(gpio, TM1637_DATA) && (i < 250))
  67.     {
  68.         i++;
  69.     }
  70.     clock_set();
  71.     Delay_us(2);
  72.     clock_reset();
  73. }

  74. void TM1637_stop(void)
  75. {
  76.     clock_reset();
  77.     Delay_us(2);
  78.     data_reset();
  79.     Delay_us(2);
  80.     clock_set();
  81.     Delay_us(2);
  82.     data_set();
  83.     Delay_us(2);
  84. }

  85. void TM1637_Write(unsigned char DATA)
  86. {
  87.     unsigned char i;
  88.     for (i = 0; i < 8; i++)
  89.     {
  90.         clock_reset();
  91.         if (DATA & 0x01)
  92.         {
  93.             data_set();
  94.         }
  95.         else
  96.         {
  97.             data_reset();
  98.         }
  99.         Delay_us(3);
  100.         DATA = DATA >> 1;
  101.         clock_set();
  102.         Delay_us(3);
  103.     }
  104. }

  105. void TM1637_display(unsigned char numer1, unsigned char numer2, unsigned char numer3, unsigned char numer4, unsigned char colon, enum LIGHT_DEGREE level)
  106. {
  107.     // 写 SRAM 数据地址自动加 1 模式
  108.     TM1637_start();
  109.     // 发送起始地址
  110.     TM1637_Write(0x40);
  111.     TM1637_ack();
  112.     TM1637_stop();
  113.     TM1637_start();
  114.     TM1637_Write(0xc0);
  115.     TM1637_ack();

  116.     TM1637_Write(tab[numer1]);
  117.     TM1637_ack();
  118.     TM1637_Write(tab[numer2] | colon << 7); // h为1时显示时钟中间的两点
  119.     TM1637_ack();
  120.     TM1637_Write(tab[numer3]);
  121.     TM1637_ack();
  122.     TM1637_Write(tab[numer4]);
  123.     TM1637_ack();

  124.     TM1637_stop();
  125.     TM1637_start();
  126.     TM1637_Write(level); // 开启显示
  127.     TM1637_ack();
  128.     TM1637_stop();
  129. }
复制代码



用户不需要关注那么多细节,只需要引入TM1637.h 然后调用初始化,和显示方法即可,如下所示。
  1. #include "bflb_mtimer.h"
  2. #include "board.h"
  3. #include "bflb_gpio.h"
  4. #include "TM1637.h"

  5. int main(void)
  6. {
  7.     int index = 0;
  8.     board_init();
  9.     TM1637_init();
  10.     unsigned char flag = 0x01;
  11.     while (1)
  12.     {
  13.         int digit1 = index % 10;          // 个位
  14.         int digit2 = (index / 10) % 10;   // 十位
  15.         int digit3 = (index / 100) % 10;  // 百位
  16.         int digit4 = (index / 1000) % 10; // 千位
  17.         TM1637_display(digit4, digit3, digit2, digit1, flag, LEVEL1);
  18.         arch_delay_ms(100);
  19.         flag = !flag;
  20.         index++;
  21.     }
  22. }
复制代码

上述代码提供了一个测试方法,使其数码管数字进行累加。

效果演示:

【M61点亮TM1637】


VERSION2: 新增了调整亮度等级的功能。
VERSION2: 代码:
TM1637 (2).zip (4.76 KB, 下载次数: 2)
回复

使用道具 举报

干簧管 | 2024-4-1 09:18:35 | 显示全部楼层
给妖哥点赞
回复 支持 反对

使用道具 举报

bzhou830 | 2024-4-1 09:41:06 | 显示全部楼层
王哥效率好高
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

1084504793 | 2024-4-1 09:57:21 | 显示全部楼层
回复

使用道具 举报

爱笑 | 2024-4-1 10:23:09 | 显示全部楼层
不错不错
用心做好保姆工作
回复

使用道具 举报

xiaoch669 | 2024-4-1 10:52:32 | 显示全部楼层
回复

使用道具 举报

putin | 2024-4-1 10:58:03 | 显示全部楼层
wc可以
回复

使用道具 举报

xktx09 | 2024-4-1 11:46:09 | 显示全部楼层
强的
回复

使用道具 举报

1055173307 | 2024-4-1 16:07:11 | 显示全部楼层
学习
回复

使用道具 举报

WT_0213 | 2024-4-2 08:53:18 | 显示全部楼层
非常厉害
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则