目前市面上有一些数码管显示芯片,其中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官方的思路,文章纯手打,如果转载使用望注明一下博客地址。 |・ω・)