最近项目中需要用到数码管显示,于是买了一个TM1637芯片驱动的四位数码显示模块,现将调试过程记录一下,方便以后参考。

使用的单片机是STM32F103C8T6最小系统

使用的数码管模块是TM1637四位数码管显示模块

实际运行效果

下面先看一下TM1637和数码管连接的具体线路图

实际使用的模块没有带按键,只用了4个数码管,模块和单片机连接只需要4根线VCC、GND、CLK、DIO。芯片和单片机通信使用的是I2C总线,下面就来说一下如何通过I2C总线驱动这个数码管模块。

为了方便移植,这里使用 IO口模拟I2C总线,所以首先要将延时函数准备好,延时函数使用任何一种方式都可以,可以根据自己的习惯使用自己的延时函数。

/** t : 定时时间* Ticks : 多少个时钟周期产生一次中断* f : 时钟频率 72000000* t = Ticks * 1/f = (72000000/1000000) * (1/72000000) = 1us*/void SysTick_Delay_Us( __IO uint32_t us )
{uint32_t i;SysTick_Config( SystemCoreClock / 1000000 );for( i = 0; i < us; i++ ){// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1while( !( ( SysTick->CTRL ) & ( 1 << 16 ) ) );}// 关闭SysTick定时器SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}void SysTick_Delay_Ms( __IO uint32_t ms )
{uint32_t i;SysTick_Config( SystemCoreClock / 1000 );for( i = 0; i < ms; i++ ){// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1// 当置1时,读取该位会清0while( !( ( SysTick->CTRL ) & ( 1 << 16 ) ) );}// 关闭SysTick定时器SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}

这里直接读取系统定时器的标志位来进行延时,设置系统定时器1us中断一次。直接判断系统的中断次数就可以实现us级的延时了。

下来就需要编写I2C的时序了,官方资料上也提提供了参考代码,这个代码也是参考官方代码修改的。

首先要定义需要用到的IO口,为了方便移植,将所用到的IO口直接在头文件中定义,需要更改IO口的时候,只需要在头文件中修改就行。

/* 定义IIC连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
#define TM1637_CLK_GPIO_PORT        GPIOB                       /* GPIO端口 */
#define TM1637_CLK_GPIO_CLK         RCC_APB2Periph_GPIOB        /* GPIO端口时钟 */
#define TM1637_CLK_GPIO_PIN           GPIO_Pin_6#define TM1637_DIO_GPIO_PORT        GPIOB                         /* GPIO端口 */
#define TM1637_DIO_GPIO_CLK         RCC_APB2Periph_GPIOB        /* GPIO端口时钟 */
#define TM1637_DIO_GPIO_PIN         GPIO_Pin_7

在模拟时序的时候为了方便编写代码,将用到的时钟口和数据口也重新定义。

//使用 位带 操作
#define TM1637_CLK           PBout(6)
#define TM1637_DIO           PBout(7)
#define TM1637_READ_DIO      PBin(7)//IO方向设置                                        0011输出模式   1000上下拉输入模式
#define TM1637_DIO_IN()      {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define TM1637_DIO_OUT()     {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}

下来需要初始化 IO口

//端口初始化
void TM1637_Init( void )
{GPIO_InitTypeDef  GPIO_InitStructure;RCC_APB2PeriphClockCmd( TM1637_CLK_GPIO_CLK | TM1637_DIO_GPIO_CLK, ENABLE );GPIO_InitStructure.GPIO_Pin = TM1637_CLK_GPIO_PIN | TM1637_DIO_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init( TM1637_CLK_GPIO_PORT, &GPIO_InitStructure );
}

下来模拟I2C的时序


//起始位 CLK为高电平时,DIO由高变低
void TM1637_Start( void )
{TM1637_DIO_OUT();TM1637_CLK = 1;TM1637_DIO = 1;delay_us( 2 );TM1637_DIO = 0;
}//等待应答 传输数据正确时,在第八个时钟下降沿,芯片内部会产生一个ACK信号,将DIO管脚拉低,在第九个时钟结束之后释放DIO总线。
void TM1637_Ack( void )
{TM1637_DIO_IN();TM1637_CLK = 0;delay_us( 5 );                                                 //在第八个时钟下降沿之后延时 5us,开始判断 ACK 信号while( TM1637_READ_DIO );                             //等待应答位  这一行代码也可以不要 不影响实际使用效果 在使用软件仿真的时候需要屏蔽这句代码,否则程序就会卡在这里。TM1637_CLK = 1;delay_us( 2 );TM1637_CLK = 0;
}//停止位 CLK为高电平时,DIO由低变高
void TM1637_Stop( void )
{TM1637_DIO_OUT();TM1637_CLK = 0;delay_us( 2 );TM1637_DIO = 0;delay_us( 2 );TM1637_CLK = 1;delay_us( 2 );TM1637_DIO = 1;
}
//输入数据在CLK的低电平变化,在CLK的高电平被传输。
//每传输一个字节,芯片内部在第八个时钟下降沿产生一个ACK
// 写一个字节
void TM1637_WriteByte( unsigned char oneByte )
{unsigned char i;TM1637_DIO_OUT();for( i = 0; i < 8; i++ ){TM1637_CLK = 0;if( oneByte & 0x01 )                                   //低位在前{TM1637_DIO = 1;}else{TM1637_DIO = 0;}delay_us( 3 );oneByte = oneByte >> 1;TM1637_CLK = 1;delay_us( 3 );}
}

需要用到的时序主要有开始位、停止位、等待应答位、写一个字节。通过上面这四个函数就可以直接操作TM1637芯片了。

根据官方的资料,有两种写数据的方式,第一种是地址自加,第二种是地址固定。先来实现地址自加的模式。

根据这个时序看,首先要发送起始位,接着发送设置数据命令,下来等待应答,最后发送停止位。下来在发送起始位、设置地址命令、等待应答,发送显示数据1、等待应答、发送显示数据2,等待应答,……发送显示数据N、等待应答、停止位、起始位、发送显示命令、等待应答、停止位。

下面看看官方的命令表

根据上面命令表可以看出,数据命令中,自动地址增加命令 B6为1,其他的都为0。也就是0x40就是地址自增命令。接下来看地址命令,显示地址从00---05表示6个数码管的地址,此时B7和B6必须为1,也就是显示地址范围是0xC0-----0xC5。最后是显示控制,这个命令是控制开关显示和亮度的。开显示需要B7和B3为1,也就是0x88,最后3位0--7表示8级亮度。这样显示控制的值的范围就是0x88-----0x8F。

命令分析完之后就可以编写代码了

//写显示寄存器  地址自增
void TM1637_Display_INC( void )
{unsigned char i;TM1637_Start();TM1637_WriteByte( 0x40 );                                   //写数据到显示寄存器 40H 地址自动加1 模式,44H 固定地址模式,本程序采用自加1模式TM1637_Ack();TM1637_Stop();TM1637_Start();TM1637_WriteByte( 0xC0 );                                  //地址命令设置 显示地址 00HTM1637_Ack();for( i = 0; i < 6; i++ )                                    //地址自加,不必每次都写地址{TM1637_WriteByte( disp_num[i] );         //发送数据   disp_num[]中存储6个数码管要显示的内容TM1637_Ack();}TM1637_Stop();TM1637_Start();TM1637_WriteByte( 0x88 | 0x07 );                    //开显示,最大亮度-----调节脉冲宽度控制0---7  脉冲宽度14/16TM1637_Ack();TM1637_Stop();}

发送数据显示命令和显示亮度命令时都需要停止位,但是发送地址命令和显示数据内容时,是不需要停止位的,可以连续发送。数据循环发送结束后再发送停止位就行。其中 disp_num[]数组中依次存放6个数码管需要显示的内容。这样如下需要改变哪个数码管显示的内容是,只需要给disp_num[]数组中的对应位置重新赋值就行。

下面编写地址固定的写数据模式

时序和上面地址自增的基本一样,只是设置数据的命令不同,根据上面的命令表格可以看出,地址固定命令B6和B2都为1,也就是0x44。

//写显示寄存器  地址不自增
// add 数码管的地址 0--5
// value 要显示的内容
void TM1637_Display_NoINC( unsigned char add, unsigned char value )
{unsigned char i;TM1637_Start();TM1637_WriteByte( 0x44 );                               //写数据到显示寄存器 40H 地址自动加1 模式,44H 固定地址模式,本程序采用自加1模式TM1637_Ack();TM1637_Stop();TM1637_Start();TM1637_WriteByte( 0xC0 | add );                    //地址命令设置 显示地址 C0H---C5HTM1637_Ack();TM1637_WriteByte( value );                          //发送数据   value存储要显示的内容TM1637_Ack();TM1637_Stop();TM1637_Start();TM1637_WriteByte( 0x88 | 0x07 );                            //开显示,最大亮度-----调节脉冲宽度控制0---7  脉冲宽度14/16TM1637_Ack();TM1637_Stop();
}

地址固定模式每次只写一个固定的地址,地址值是由低三位值控制的,所以这里将高5位的值固定不变,只需要将低3位的值和高5位的值进行位或运算就行。这样在传递地址参数的时候,只需要发送0--5就可以了。同样亮度的设置也是用这个方法,亮度值由低3位值决定,将低3位值和高5位值进行位或运算。设置亮度的时候直接发送0--7就行。这里亮度没有使用参数,直接使用的是定值。想要改变亮度,可以把亮度也设置为参数传递进来。

到这里就可以直接调用这两个函数,控制数码管显示了。

为了方便查看代码,下面贴出完整代码TM1637.c完整代码

#include "TM1637.h"
#include "bsp_SysTick.h"unsigned char tab[] =
{0x3F,/*0*/0x06,/*1*/0x5B,/*2*/0x4F,/*3*/0x66,/*4*/0x6D,/*5*/0x7D,/*6*/0x07,/*7*/0x7F,/*8*/0x6F,/*9*/0x77,/*10 A*/0x7C,/*11 b*/0x58,/*12 c*/0x5E,/*13 d*/0x79,/*14 E*/0x71,/*15 F*/0x76,/*16 H*/0x38,/*17 L*/0x54,/*18 n*/0x73,/*19 P*/0x3E,/*20 U*/0x00,/*21 黑屏*/
};// 最高位设置为1时显示 数码管上的":" 符号
unsigned char disp_num[] = {0x3F, 0x06 | 0x80, 0x5B, 0x4F, 0x66, 0x6D};            //存放6个数码管要显示的内容//端口初始化
void TM1637_Init( void )
{GPIO_InitTypeDef  GPIO_InitStructure;RCC_APB2PeriphClockCmd( TM1637_CLK_GPIO_CLK | TM1637_DIO_GPIO_CLK, ENABLE );GPIO_InitStructure.GPIO_Pin = TM1637_CLK_GPIO_PIN | TM1637_DIO_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init( TM1637_CLK_GPIO_PORT, &GPIO_InitStructure );
}//起始位 CLK为高电平时,DIO由高变低
void TM1637_Start( void )
{TM1637_DIO_OUT();TM1637_CLK = 1;TM1637_DIO = 1;delay_us( 2 );TM1637_DIO = 0;
}//等待应答 传输数据正确时,在第八个时钟下降沿,芯片内部会产生一个ACK信号,将DIO管脚拉低,在第九个时钟结束之后释放DIO总线。
void TM1637_Ack( void )
{TM1637_DIO_IN();TM1637_CLK = 0;delay_us( 5 );                                                 //在第八个时钟下降沿之后延时 5us,开始判断 ACK 信号while( TM1637_READ_DIO );                             //等待应答位  这一行代码也可以不要 不影响实际使用效果 在使用软件仿真的时候需要屏蔽这句代码,否则程序就会卡在这里。TM1637_CLK = 1;delay_us( 2 );TM1637_CLK = 0;
}//停止位 CLK为高电平时,DIO由低变高
void TM1637_Stop( void )
{TM1637_DIO_OUT();TM1637_CLK = 0;delay_us( 2 );TM1637_DIO = 0;delay_us( 2 );TM1637_CLK = 1;delay_us( 2 );TM1637_DIO = 1;
}
//输入数据在CLK的低电平变化,在CLK的高电平被传输。
//每传输一个字节,芯片内部在第八个时钟下降沿产生一个ACK
// 写一个字节
void TM1637_WriteByte( unsigned char oneByte )
{unsigned char i;TM1637_DIO_OUT();for( i = 0; i < 8; i++ ){TM1637_CLK = 0;if( oneByte & 0x01 )                                   //低位在前{TM1637_DIO = 1;}else{TM1637_DIO = 0;}delay_us( 3 );oneByte = oneByte >> 1;TM1637_CLK = 1;delay_us( 3 );}
}//写显示寄存器  地址自增
void TM1637_Display_INC( void )
{unsigned char i;TM1637_Start();TM1637_WriteByte( 0x40 );                                   //写数据到显示寄存器 40H 地址自动加1 模式,44H 固定地址模式,本程序采用自加1模式TM1637_Ack();TM1637_Stop();TM1637_Start();TM1637_WriteByte( 0xC0 );                                  //地址命令设置 显示地址 00HTM1637_Ack();for( i = 0; i < 6; i++ )                                    //地址自加,不必每次都写地址{TM1637_WriteByte( disp_num[i] );         //发送数据   disp_num[]中存储6个数码管要显示的内容TM1637_Ack();}TM1637_Stop();
#if 0TM1637_Start();TM1637_WriteByte( 0x88 | 0x07 );                    //开显示,最大亮度-----调节脉冲宽度控制0---7  脉冲宽度14/16TM1637_Ack();TM1637_Stop();
#endif
}//写显示寄存器  地址不自增
// add 数码管的地址 0--5
// value 要显示的内容
void TM1637_Display_NoINC( unsigned char add, unsigned char value )
{unsigned char i;TM1637_Start();TM1637_WriteByte( 0x44 );                               //写数据到显示寄存器 40H 地址自动加1 模式,44H 固定地址模式,本程序采用自加1模式TM1637_Ack();TM1637_Stop();TM1637_Start();TM1637_WriteByte( 0xC0 | add );                    //地址命令设置 显示地址 C0H---C5HTM1637_Ack();TM1637_WriteByte( value );                          //发送数据   value存储要显示的内容TM1637_Ack();TM1637_Stop();
#if 0TM1637_Start();TM1637_WriteByte( 0x88 | 0x07 );                            //开显示,最大亮度-----调节脉冲宽度控制0---7  脉冲宽度14/16TM1637_Ack();TM1637_Stop();
#endif
}// level : 设置亮度等级  0---7
void TM1637_SetBrightness( unsigned char level )
{TM1637_Start();TM1637_WriteByte( 0x88 | level );                           //开显示,最大亮度-----调节脉冲宽度控制0---7  脉冲宽度14/16TM1637_Ack();TM1637_Stop();
}//读按键  读按键时,时钟频率应小于 250K,先读低位,后读高位。
unsigned char TM1637_ScanKey( void )
{unsigned char reKey, i;TM1637_Start();TM1637_WriteByte( 0x42 );                        //读键扫数据TM1637_Ack();TM1637_DIO = 1;                                            //在读按键之前拉高数据线TM1637_DIO_IN();for( i = 0; i < 8; i++ )                     //从低位开始读{TM1637_CLK = 0;reKey = reKey >> 1;delay_us( 30 );TM1637_CLK = 1;if( TM1637_READ_DIO ){reKey = reKey | 0x80;}else{reKey = reKey | 0x00;}delay_us( 30 );}TM1637_Ack();TM1637_Stop();return( reKey );
}//按键处理函数,按键数据低位在前高位在后
unsigned char TM1637_KeyProcess( void )
{unsigned char temp;unsigned char keyNum = 0;temp = TM1637_ScanKey();                                 //读取按键返回值if( temp != 0xff ){switch( temp ){case 0xf7 :                         //K1与SG1对应按键按下keyNum = 1;break;case 0xf6 :                         //K1与SG2对应按键按下keyNum = 2;break;case 0xf5 :                         //K1与SG3对应按键按下keyNum = 3;break;case 0xf4 :                         //K1与SG4对应按键按下keyNum = 4;break;case 0xf3 :                         //K1与SG5对应按键按下keyNum = 5;break;case 0xf2 :                         //K1与SG6对应按键按下keyNum = 6;break;case 0xf1 :                         //K1与SG7对应按键按下keyNum = 7;break;case 0xf0 :                         //K1与SG8对应按键按下keyNum = 8;break;case 0xef :                         //K2与SG1对应按键按下keyNum = 9;break;case 0xee :                         //K2与SG2对应按键按下keyNum = 10;break;case 0xed :                            //K2与SG3对应按键按下keyNum = 11;break;case 0xec :                            //K2与SG4对应按键按下keyNum = 12;break;case 0xeb :                            //K2与SG5对应按键按下keyNum = 13;break;case 0xea :                            //K2与SG6对应按键按下keyNum = 14;break;case 0xe9 :                            //K2与SG7对应按键按下keyNum = 15;break;case 0xe8 :                            //K2与SG8对应按键按下keyNum = 16;break;default   :keyNum = 0;break;}}return keyNum;
}

下面是主函数代码

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_SysTick.h"
#include "TM1637.h"int main( void )
{u8 flag_s = 0;NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );LED_GPIO_Config();TM1637_Init();//    delay_config();                      //使用方法二延时的时候调用while ( 1 ){
#if 1LED1( ON );delay_ms( 1000 );LED1( OFF );flag_s = ~flag_s;disp_num[0] = tab[0];if( flag_s )disp_num[1] = tab[1] | 0x80;              //最高位设置为1 显示":"elsedisp_num[1] = tab[1];disp_num[2] = tab[2];disp_num[3] = tab[3];TM1637_Display_INC();TM1637_SetBrightness( 3 );                  //设置亮度等级 0---7#else         //延时精确测试代码LED1_ON;delay_us( 1 );LED1_OFF;delay_us( 1 );#endif}}

主函数中设置四个数码管依次显示0、1、2、3,冒号是在第二个数码管的dp引脚接着,dp为最高位,所以给最高位写1就可以点亮时钟的冒号。通过一个标志位控制冒号1秒钟闪烁一次。

完整工程下载链接 STM32F103单片机使用TM1637芯片驱动4位数码管

STM32F103单片机驱动TM1637数码管显示模块相关推荐

  1. LED数码管静态显示 C语言程序,PIC单片机驱动LED数码管显示程序

    ;*****该程序用于驱动led数码管显示,在8个LED数码管上依次显示数字1.2.3.4.5.6.7.8******* ;****http://www.51hei.com 单片机学习网经典程序已测试 ...

  2. 51单片机入门——动态数码管显示详解

    51单片机:动态数码管显示 - 模块图 - 显示原理 – 消抖 静态数码管技术 + 人眼视觉停留 显示原理类似于以前的 胶片机放影视,也就是只要我切换的速度足够快你就看不出来其实我是一次次显示的(滑稽 ...

  3. void函数调用时显示不允许使用不完整的_4位数码管显示模块驱动

    TM1637四位数码管模块是一个带时钟点的4位共阳数码管(0.36英寸)的显示模块,驱动芯片为TM1637,驱动方式为IIC,因此只需2根信号线即可使单片机控制4位8段数码管(数码管8级亮度可调). ...

  4. K_A12_007 基于STM32等单片机驱动AS608光学指纹识别模块 OLED0.96显示

    K_A12_007 基于STM32等单片机驱动AS608光学指纹识别模块 OLED0.96显示 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 对应程序: 四.部分代码说明 1.接线引脚定义 ...

  5. 单片机学习笔记————51单片机实现带数码管显示的象棋比赛专用计时器

    一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 /***************************************************************** ...

  6. 基于单片机病房呼叫系统数码管显示房号设计-基于单片机工业生产现场的光照强度控制设计-基于单片机多功能智能台灯设计-基于单片机二维码LCD显示设计-基于单片机多功能时钟闹钟万年历控制系统设计【毕设分享】

    1641基于单片机病房呼叫系统数码管显示房号设计 设计思路:此设计实现的功能通过按键模拟房号,当按键按下时会将房号编号发送到数目管上进行显示,并通过声光进行提示.包含的电路有:数码管显示电路.按键电路 ...

  7. 单片机学习笔记————51单片机实现带数码管显示的加法简易计算器

    一.使用proteus绘制简单的电路图,用于后续仿真 二.编写程序 /***************************************************************** ...

  8. 单片机入门——动态数码管显示

    前言 在实际应用中通常都需要显示多位数值,如果采用静态数码管显示就不够好,因此就需要采用另外一种显示方式,即数码管动态显示.开发板上板载2 个四位一体的共阴数码管,接下来我们就来介绍下如何使用51 单 ...

  9. Arduino实时时钟设计(TM1637数码管显示)

    设计者:STCode(公众号同名) 1.实时时钟功能介绍 该设计利用DS1302获取时间数据,用4位led数码管显示模块显示当前的年月日信息,并且设计有一个按键,可以切换时间数据. 2. 使用主要器件 ...

最新文章

  1. 【PHP】微信官方代码Log调试输出类,面向对象设计模式!来看看,你会有收益!...
  2. /etc/hosts错误配置,导致TNS-12535
  3. Oracle中rank() over, dense_rank(), row_number() 的区别
  4. STL源码剖析 读书笔记一 2013-5-4
  5. 【offer去哪了】我一连面试了十个Java岗,统统石沉大海!
  6. 【原创】构建高性能ASP.NET站点 第五章—性能调优综述(后篇)
  7. (47)Verilog HDL UART接收设计
  8. NYOJ113 - 字符串替换
  9. CSS遮罩层:hover状态丢失及解决方案
  10. JS学习总结(2)——变量
  11. Squid 配置文件详解
  12. Atitit nlp重要节点 v3 目录 1. 语法分析重点 节点余额365个 1 2. nlp词性表 2 2.1. 词语分类13类 2 2.2. 副词 约20个 3 2.3. 代词30个 3 2
  13. 3星|《未来公司》:Uber简史
  14. STM32F103 驱动DS18B20
  15. 软件测试工程师相关证书
  16. linux 好书推荐
  17. 麦克风没声音,这个选项你注意到了吗?
  18. SAP月结在制品结算时不产生凭证的一个问题
  19. 腾讯云运维tca题库
  20. oracle数据库恢复aul_ORACLE恢复神器之ODU/AUL/DUL

热门文章

  1. mysql定时任务,每天凌晨1点执行
  2. windows网站操作系统管理【1】
  3. Python培训:python中写文件的操作方法
  4. Android Studio 美化
  5. SQL SERVER 日期时间 格式
  6. LogicFlow(Vue3)
  7. OSI 七层网络结构
  8. ipad html 禁止放大镜,IOs Cordova长按显示文本选择放大镜即使禁用文本选择,如何删除?...
  9. 泛域名证书ACME 阿里云域名
  10. 【该文章已被封禁】区块链钱包APP逆向分析及实现