历史上的今天
返回首页

历史上的今天

今天是:2024年12月31日(星期二)

正在发生

2021年12月31日 | STM32F103单片机驱动TM1637数码管显示模块

2021-12-31 来源:eefocus

最近项目中需要用到数码管显示,于是买了一个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会置1

        while( !( ( 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时,读取该位会清0

        while( !( ( 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 ); //地址命令设置 显示地址 00H

    TM1637_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/16

    TM1637_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---C5H

    TM1637_Ack();

 

    TM1637_WriteByte( value ); //发送数据   value存储要显示的内容

    TM1637_Ack();

    TM1637_Stop();

 

    TM1637_Start();

    TM1637_WriteByte( 0x88 | 0x07 ); //开显示,最大亮度-----调节脉冲宽度控制0---7  脉冲宽度14/16

    TM1637_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 ) //低位在前

        {

推荐阅读

史海拾趣

Crystal Semiconductor Corp公司的发展小趣事

随着市场竞争的加剧,Crystal Semiconductor Corp意识到只有不断创新才能在行业中立足。于是,公司加大了对研发的投入,引进了一批高端人才。经过数年的努力,公司成功开发出了一种新型的半导体制程工艺,大幅提高了晶体管的性能和可靠性。这一创新不仅为公司带来了丰厚的利润,也为整个半导体行业树立了新的标杆。

光大芯业公司的发展小趣事

在快速发展的过程中,光大芯业始终重视人才的培养和团队的建设。公司拥有一支来自国内外顶尖半导体设计公司的资深专家组成的核心技术团队,他们具备丰富的产品开发经验和创新理念。同时,公司还注重本地优秀设计人才的培养和引进,为公司的长远发展提供了坚实的人才保障。

Edsun Laboratories Inc公司的发展小趣事

面对日益严重的环境问题,ECM Electronics Limited.积极响应国家节能减排的号召,将绿色发展和可持续发展作为企业的重要战略。公司采用环保材料和节能技术,降低产品能耗和排放。同时,公司还积极参与环保公益活动,推动行业绿色发展。这些举措不仅提升了公司的社会形象,也为公司的长期发展奠定了坚实的基础。

Davies Molding公司的发展小趣事

Davies Molding公司在发展过程中,始终注重市场布局与拓展。公司不仅在美国中西部建立了稳固的市场地位,还积极开拓国际市场,将产品出口到全球多个国家和地区。通过与全球客户的合作,Davies Molding公司不仅提升了品牌影响力,还获得了更多的发展机遇。

Dfx公司的发展小趣事

在智能手机行业快速发展的背景下,某知名手机制造商面临着如何将更多功能集成到更小空间的挑战。通过引入DFX的设计理念,该公司成功实现了硬件的小型化,同时保持了高性能和可靠性。通过优化内部布局、采用先进封装技术和选用高性能元器件,该手机制造商在保持产品竞争力的同时,满足了消费者对轻薄、高性能手机的需求。

天二科技(EVER OHMS)公司的发展小趣事

天二科技(EVER OHMS)于1988年正式成立,其初创团队便专注于电阻器领域的研发与制造。面对当时电子行业的激烈竞争,天二科技凭借对电阻器技术的深入理解,成功开发出多款高性能、高精度的电阻器产品,迅速在市场中崭露头角。这些产品不仅满足了当时市场的需求,更为公司后续的发展奠定了坚实的基础。

问答坊 | AI 解惑

单片机和FPGA通信

本帖最后由 paulhyde 于 2014-9-15 03:33 编辑 我找到的一些单片机和FPGA通信的材料,里面含有两片资料文章 [ 本帖最后由 open82977352 于 2010-2-10 16:46 编辑 ]  …

查看全部问答>

STM32W108问题,还请指教

大家好,我想用STM32W108做一个ZIGBEE的无线通信方案,有两个问题请教。 (1)这款新品有卖了吗?大致多少价格?    或者哪里能申请样片? (2)如果说我采用树型网络,ARM9作为协调器,STM32W108作为路由设备,CC2430作为终端设备。 ...…

查看全部问答>

出口香港食品营养标签将有新规

   香港政府《2008年食物及药物(成分组合及标签)(修订:关于营养标签及营养声称的规定)规例》将于2010年7月1日起实施。届时所有在香港销售的预包装食品(规例规定可豁免的食品除外)须标注营养标签,该营养标签必须标示能量和七种核心 ...…

查看全部问答>

修改config.bib,配置内存分配

大家好: 我在修改config.bib文件,但是修改后,系统不能启动,大家看看是怎么回事.还有修改config.bib还需要修改其他文件吗? config.bib原文件 MEMORY ;#define CHAIN_ADDRESS        81E40000 ;        ...…

查看全部问答>

TBsoft-GUI,能支持真正事件驱动程序设计的嵌入式GUI,自己的作品

笔者第一个用于实际项目,并获得成功的的嵌入式系统小作品问世。 TBsoft-GUI,一个很小的,功能一般的GUI,一个结合了某些现代程序设计要素的GUI。 市面上少见的可以支持真正事件驱动程序设计的嵌入式GUI。使用控件,引发事件,能像VB一样,直接 ...…

查看全部问答>

如何将Windows CE 5.0的模拟器集成到VS2005中?

我现在已经安装了Windows CE 5.0的模拟器,但是它是独立运行的,我想将它集成到VS2005中,具体应该怎么做? 我也试过安装了pocket pc的sdk,但是没有ce 的模拟器,那么具体应该安装哪个sdk哪? …

查看全部问答>

基于AT89S52单片机的nRF2401无线模块全套资料

刚来报到,给大家传份资料,有用到的可以下载看看,今后多多照顾! 有点不会用,上一个附件没传上来   [local]1[/local]…

查看全部问答>

求CC2430实现无线抄表的资料

用CC2430,利用ZigBee无线技术,将水表数据发送到另个CC2430模块,再在液晶屏上显示数据。 请问该怎么做,求有关的资料…

查看全部问答>

E型电感绕制求助

E型电感  留了点气隙 电感值的考虑50UH-100UH 计算公式有个N^2=L/AL 就可得出N 而EI33里给出了AL的参考值 无气隙是  4400 有气隙是 200和400 请问大侠  我有气隙是应该选哪个值呢…

查看全部问答>