STM32通过I2C驱动TM1637显示四位数码管

时间:2024-03-01 18:54:21

  目前市面上有一些数码管显示芯片,其中TM1637是比较经典,也是我个人比较喜欢的一款芯片。TM1637是天微电子的一款带按键扫描的8段*6位数码管驱动芯片,本次使用STM32F103C8T6驱动四位数码管。(下图:TM1637功能及管脚,来源TM1637开发手册)

I2C驱动:

  TM1637采用的通信方式是I2C通信,所以我们首先来简单介绍下I2C的通信方式:

  I2C通信是一种经典的通信协议,在物理层上,它使用SDA(串行数据线),SCL(串行时钟线)两条线控制一个或多个设备。既然通过一条数据线控制多个设备,那么设备就必定有一个独属于自己的地址(类似于ID),以便于主机可以和不同的设备通信。

  在协议层上,I2C通信时会首先发送一个Start信号,这时,在数据线上的设备都会听到这个信号,并等待着下一个信号:地址信号的到来。主机在开始信号后会发送地址信号,如果这个地址不是从机设备的地址,那么这个从机会忽略后面的消息,因为主机的通信对象不是它,它不需要继续听下去(这也不太礼貌)。但如果这个地址是从机的地址,那么从机会发送一个应答信号,表示”我听到了“。

  由于本次是我们是单方面向TM1637写数据,所以I2C中主机由从机中读数据不再赘述。接下来主机会发送多个字节的数据,主机每发送一个字节的数据,从机就会应答一次,当主机想要停止发送,就会发送一个停止数据,整个通信流程就完成了。(图源网络,地址位也可能是10位)

 

 

TM1637驱动代码:

  知道了通信方式,我们就可以比较轻松的编写出TM1637的驱动代码,先看看官方给出的程序流程:

  

  这里我们舍去按键扫描的程序,直接使用I2C驱动,并使用图中的地址自加模式

  我们先进行头文件中宏的定义:

#ifndef   __TM1637_H
#define   __TM1637_H

#include "stm32f10x.h"
#include "sys.h"

//与库函数操作取一个
#define    SDA_IN()  {GPIOA->CRL&=0X0FFFFFFF;GPIOA->CRL|=(u32)8<<28;}      //通过寄存器更改为输入
#define    SDA_OUT() {GPIOA->CRL&=0X0FFFFFFF;GPIOA->CRL|=(u32)3<<28;}      //通过寄存器更改为输出

#define    TM_SCL_PORT        GPIOA
#define    TM_SCL_CLK         RCC_APB2Periph_GPIOA
#define    TM_SCL_PIN         GPIO_Pin_5

#define    TM_DIO_PORT        GPIOA
#define    TM_DIO_CLK         RCC_APB2Periph_GPIOA
#define    TM_DIO_PIN         GPIO_Pin_7

 #define TM_SCL PAout(5)
 #define TM_SDA PAout(7)

  #define READ_SDA PAin(7)

 

/*函数声明,这里省略,使用时请自加*/

#endif

 

  在驱动文件TM1637.c中首先是I2C初始化,这里使用了软件I2C

#include "TM1637.h"
#include "delay.h"



void TM_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(TM_DIO_CLK|TM_SCL_CLK,ENABLE);    
    
    GPIO_InitStructure.GPIO_Pin = TM_DIO_PIN | TM_SCL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    
    GPIO_Init(TM_DIO_PORT,&GPIO_InitStructure);
    
//    TM_SCL=1;
//    TM_SDA=1;
}

  

  然后是开始,停止,等待应答,写一个8位数据的函数

void TM_Start(void)
{
    TM_SDA=1;
    delay_us(2);
    TM_SCL=1;
    delay_us(2);
    TM_SDA=0;
    delay_us(2);
    TM_SCL=0;
    delay_us(2);
}

void TM_Stop(void)
{
    TM_SCL=0;
    delay_us(2);
    TM_SDA=0;
    delay_us(2);
    TM_SCL=1;
    delay_us(2);
    TM_SDA=1;
    delay_us(2);
}

void TM_Wait_Ask(void)
{
    SDA_IN();
    unsigned char i;
    TM_SCL=0;
    delay_us(5);
    while(READ_SDA==1&&(i<250))i++;
    TM_SCL=1;
    delay_us(2);
    TM_SCL=0;
    SDA_OUT();
}

void TM_WriteByte(uint8_t txd)
{
    uint8_t i;
    for(i=0;i<8;i++)
  {
        TM_SCL=0;
        delay_us(2);
        if(txd & 0x01){
            TM_SDA=1;
        }
        else {
            TM_SDA=0;
        }
        delay_us(3);
        txd>>=1;
        TM_SCL=1;
        delay_us(3);
  }
    //TM_Wait_Ask();
}

  

  最后是main中使用的Display函数,这里的函数适用于4位数码管:

void TM_Display(uint8_t *discode)
{
    uint8_t i;
    
    TM_Start();
    TM_WriteByte(0x40);    //40 地址自加模式     44 固定地址模式
    TM_Wait_Ask();
    TM_Stop();
    
    TM_Start();
    TM_WriteByte(0xc0);   //首地址
    TM_Wait_Ask();
    
    for(i=0;i<4;i++)
    {
        TM_WriteByte(*(discode+i));  //依次发送数组数据
        TM_Wait_Ask();
    }
    TM_Stop();
    
    TM_Start();
    TM_WriteByte(0x89);   //亮度
    TM_Wait_Ask();
    TM_Stop();
}

   在主函数中,我们需要定义一个字符数组,以显示正确的字符,同时需要定义一个uint8_t长度的四位数组,即可进行显示。

  

unsigned char  Data[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//不带点

unsigned char  DataD[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
//带点

uint8_t time[4]={0x3f,0x3f,0x3f,0x3f};   //一个四位数组,这里代表0000

TM_Display(time);  //进行显示!

 

  以上就是STM32通过I2C驱动TM1637的相关内容。

  代码有几处借鉴了网上或者TM官方的思路,文章纯手打,如果转载使用望注明一下博客地址。 |・ω・)