问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

TM1637四数码管驱动详解

创作时间:
作者:
@小白创作中心

TM1637四数码管驱动详解

引用
CSDN
1.
https://blog.csdn.net/successhand/article/details/138411933

TM1637

1. TM1637概述

TM1637是一种用于驱动共阳极8段数码管的专用芯片,可以同时驱动6个数码管。此外,它还集成了按键扫描功能,支持亮度调节,并通过串行接口进行数据传输。芯片内部包含自动消隐电路,可以有效避免显示闪烁。

模块图:

2. 管脚定义

从模块图可以看出,TM1637有两个I/O引脚用于串行数据的输入和输出。除了驱动数码管外,该芯片还可以作为矩阵按键的扫描接口使用。

3. 接口说明

根据手册描述,数据在CLK为低电平时需要准备好,当CLK上升沿到来时,数据被传输。每传输一个字节,在第八个时钟下降沿,1637都会产生一个ACK信号,此时DIO总线会被拉低。

本文采用的是地址自动加1模式。在通信开始时,所有信号都处于空闲高电平状态。通信过程包括以下几个步骤:

  1. 发送start信号
  2. 发送命令字节
  3. 从机ACK应答(拉低DIO)
  4. 发送stop信号
  5. 再次发送start信号
  6. 设置地址
  7. 从机ACK应答
  8. 发送数据字节
  9. 最后发送stop信号

4. 数据指令

数据指令分为三类:

  1. 数据命令设置:0x40用于写数据到显示寄存器并自动地址增加
  2. 地址命令设置:0xC0用于设置第一个数码管显示寄存器地址,后续地址依次递增
  3. 显示控制:通过脉冲宽度来控制显示亮度,0x88表示开启显示

5. 四位数码管模块原理图

6. 程序驱动

采用固定地址的程序流程图

程序代码

main.c

#include "tm1637.h"

int main(void)
{
    tm1637_init();		//数码管初始化
    smg_display(2142,0);	//显示2142,冒号不显示
    while(1)
    {
    }
}

tm1637.c

#include "tm1637.h"
#include "main.h"
#include "uart.h"
#include <stdio.h>
#include <string.h>
#include "retarget.h"

//段码表
const uint8_t num_tab[] =
        {
                //0,	1,	2,	3,	4,	   5,	6,   7,  8,   9,    A,   b,  C,   d,   E,   F, :(数码管中间那两点)
                0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
                0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x80
        };

//显示缓冲区,要准备显示的段码值,从左到右
uint8_t show_buffer[4] = {0};

/**
* @brief: tm1637初始化
 * @param: void
 * @return: none
*/
void tm1637_init(void)
{
    GPIO_InitTypeDef tm1637_gpio_init;
    TM1637_CLK_GPIO_CLK_ENABLE();
    TM1637_DIO_GPIO_CLK_ENABLE();
    /// tm1637 CLK
    tm1637_gpio_init.Pin = TM1637_CLK_GPIO_PIN;
    tm1637_gpio_init.Pull = GPIO_PULLUP;
    tm1637_gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
    tm1637_gpio_init.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(TM1637_CLK_GPIO_PORT,&tm1637_gpio_init);
    /// tm1637 DIO
    tm1637_gpio_init.Pin = TM1637_DIO_GPIO_PIN;
    HAL_GPIO_Init(TM1637_DIO_GPIO_PORT,&tm1637_gpio_init);
    TM1637_CLK_HIGH;
    TM1637_DIO_HIGH;
}

/**
 * @brief: 初略延时,n ns
 * @param i
 * @return: none
 */
void delay_ns(uint32_t i)
{
    while(i--);
}

/**
 * @brief: tm1637 start 信号
 * @param: void
 * @return: none
 */
void tm1637_start(void)
{
    TM1637_DIO_GPIO_OUTPUT;
    TM1637_CLK_HIGH;
    TM1637_DIO_HIGH;
    delay_ns(6);
    TM1637_DIO_LOW;
}

/**
 * @brief: 检测应答信号,默认有超时时间
 * @param: void
 * @return: 0: nack, 1: ack
 */
uint8_t tm1637_is_ack(void)
{
    uint8_t ack_flag = 0,time = 60;
    TM1637_DIO_GPIO_INPUT;
    TM1637_CLK_LOW;
    while(time)
    {
        time--;
        if(HAL_GPIO_ReadPin(TM1637_DIO_GPIO_PORT,TM1637_DIO_GPIO_PIN) == GPIO_PIN_RESET)
        {
            ack_flag = 1;
            break;
        }
    }
    TM1637_CLK_HIGH;
    delay_ns(6);
    TM1637_CLK_LOW;
    return ack_flag;
}

/**
 * @brief: 停止信号
 * @param: void
 * @return: none
 */
void tm1637_stop(void)
{
    TM1637_DIO_GPIO_OUTPUT;
    TM1637_CLK_LOW;
    delay_ns(5);
    TM1637_DIO_LOW;
    delay_ns(5);
    TM1637_CLK_HIGH;
    delay_ns(5);
    TM1637_DIO_HIGH;
}

/**
 * @brief: 写一个字节,地址自动递增模式
 * @param data
 * @return: none
 */
void tm1637_write_byte(uint8_t data)
{
    uint8_t i;
    TM1637_DIO_GPIO_OUTPUT;
    for (i = 0; i < 8;i++)
    {
        TM1637_CLK_LOW;
        if(data & 0x01)
        {
            TM1637_DIO_HIGH;
        }
        else
        {
            TM1637_DIO_LOW;
        }
        delay_ns(3);
        data = data >> 1;
        TM1637_CLK_HIGH;
        delay_ns(3);
    }
}

/**
 * @brief: 数码管显示数值
 * @param show_num : 0000 ~ 9999
 * @param colon_flag: 冒号标志位,0:不显示,1:显示
 */
void smg_display(uint16_t  show_num , uint8_t colon_flag)
{
    uint8_t i;
    uint8_t ack_flag = 0;
    memset(show_buffer,0,sizeof(show_buffer));
    show_buffer[0] = num_tab[show_num/1000];
    if (colon_flag)     //冒号标志位
    {
        show_buffer[1] = num_tab[show_num/100%10] | 0x80;
    }
    else
    {
        show_buffer[1] = num_tab[show_num/100%10];
    }
//    show_buffer[1] = 0x80;
    show_buffer[2] = num_tab[show_num/10%10];
    show_buffer[3] = num_tab[show_num%10];
    tm1637_start();
    tm1637_write_byte(0x40);    //0x40,地址自动加1模式
    ack_flag = tm1637_is_ack();
    if(!ack_flag)
    {
        return; //结束
    }
    tm1637_stop();
    tm1637_start();
    tm1637_write_byte(0xc0);    //设置数码管显示的首地址
    ack_flag = tm1637_is_ack();
    if(!ack_flag)
    {
        return; //结束
    }
    for (i = 0; i < 4; i++)
    {
        tm1637_write_byte(show_buffer[i]);
//        tm1637_write_byte(0x5b);    //显示数值,从左到右
        ack_flag = tm1637_is_ack();
        if(!ack_flag)
        {
            return; //结束
        }
    }
    tm1637_stop();
    tm1637_start();
//    tm1637_write_byte(0x8f);    //开显示,最大亮度,14/16
    tm1637_write_byte(0x8b);    //开显示,亮度,10/16
//    tm1637_write_byte(0x8a);    //开显示,亮度,4/16
//    tm1637_write_byte(0x88);    //开显示,亮度,1/16
    ack_flag = tm1637_is_ack();
    if(!ack_flag)
    {
//        printf("error 0x8f ack.\r\n");
        return; //结束
    }
    tm1637_stop();
}

tm1637.h

#ifndef CLOCK_TM1637_H
#define CLOCK_TM1637_H
#include <stdbool.h>
#include "stm32f1xx.h"

#define TM1637_CLK_GPIO_PORT                GPIOB
#define TM1637_CLK_GPIO_PIN                 GPIO_PIN_8
#define TM1637_CLK_GPIO_CLK_ENABLE()        do{__HAL_RCC_GPIOB_CLK_ENABLE();}while(0)
#define TM1637_CLK_HIGH                     do{HAL_GPIO_WritePin(TM1637_CLK_GPIO_PORT,TM1637_CLK_GPIO_PIN,GPIO_PIN_SET);}while(0)
#define TM1637_CLK_LOW                      do{HAL_GPIO_WritePin(TM1637_CLK_GPIO_PORT,TM1637_CLK_GPIO_PIN,GPIO_PIN_RESET);}while(0)

#define TM1637_DIO_GPIO_PORT                GPIOB
#define TM1637_DIO_GPIO_PIN                 GPIO_PIN_9
#define TM1637_DIO_GPIO_CLK_ENABLE()        do{__HAL_RCC_GPIOB_CLK_ENABLE();}while(0)
#define TM1637_DIO_HIGH                     do{HAL_GPIO_WritePin(TM1637_DIO_GPIO_PORT,TM1637_DIO_GPIO_PIN,GPIO_PIN_SET);}while(0)
#define TM1637_DIO_LOW                      do{HAL_GPIO_WritePin(TM1637_DIO_GPIO_PORT,TM1637_DIO_GPIO_PIN,GPIO_PIN_RESET);}while(0)

//切换方向,输入或者输出
#define TM1637_DIO_GPIO_OUTPUT              {GPIOB->CRH &=  0xFFFFFF0F; GPIOB->CRH |=  (uint32_t)(3<<4);}
#define TM1637_DIO_GPIO_INPUT               {GPIOB->CRH &=  0xFFFFFF0F; GPIOB->CRH |=  (uint32_t)(8<<4);}

/**
* @brief: tm1637初始化
 * @param: void
 * @return: none
*/
void tm1637_init(void);

/**
 * @brief: 初略延时,n ns
 * @param i
 * @return: none
 */
void delay_ns(uint32_t i);

/**
 * @brief: tm1637 start 信号
 * @param: void
 * @return: none
 */
void tm1637_start(void);

/**
 * @brief: 检测应答信号,默认有超时时间
 * @param: void
 * @return: 0: nack, 1: ack
 */
uint8_t tm1637_is_ack(void);

/**
 * @brief: 停止信号
 * @param: void
 * @return: none
 */
void tm1637_stop(void);

/**
 * @brief: 写一个字节,地址自动递增模式
 * @param data
 * @return: none
 */
void tm1637_write_byte(uint8_t data);

void smg_display(uint16_t  show_num , uint8_t colon_flag);
#endif //CLOCK_TM1637_H

7. 遇到的问题

问题1:无法点亮数码管

在使用TM1637时,按照手册编写时序,发现数码管不亮。检查时序后发现,在发送data1~data n后,直接发送停止信号,接着又发送1个字节的数据,应答时刻等待了29us多,时间过长。经分析发现,最后一个字节命令是用来开显示和调整亮度的,缺少了一个开始信号。添加开始信号后,问题得以解决。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号