最简单最全面TM1650 IIC数码管STM32驱动代码

一 最重要的地址操作

1 基本模式

在这里插入图片描述

B7-B0共8位,负责控制数码管的一些基本模式,能够控制亮度,开启关闭显示,以及是否显示小数点,操作如下:

(1)调控亮度

把上述表格对应转化位具体地址:
二进制           十六进制
0001 0001       0x11   /*一级亮度,打开LED显示*/
0010 0001       0x21   /*二级亮度,打开LED显示*/
0011 0001       0x31   /*三级亮度,打开LED显示*/
0100 0001       0x41   /*四级亮度,打开LED显示*/
0101 0001       0x51   /*五级亮度,打开LED显示*/
0110 0001       0x61   /*六级亮度,打开LED显示*/
0111 0001       0x71   /*七级亮度,打开LED显示*/
0000 0001       0x01   /*八级亮度,打开LED显示*/
0000 0000       0x00   /*关闭LED显示*/

在IIC通信中,通信格式要求如下:
IIC通信格式:起始信号,模式命令(0x48 固定格式),显示命令(上表的十六进制地址),结束信号

(2)特殊的:关于B3为选择7段与8段的作用,其实就是控制是否全显示小数点,大家都知道,数码管一个数字位由7个灯段abcdefg外加小数点位组成。可能大家对这里有疑问:假如B3不设置为1,是不是后面就没法让屏幕显示小数点了呢?
其实不是的,这里设1的效果是这样的:

8级亮度为例:
打开B3地址就变为:  0x08|0x01  //位运算,控制0x01的B3位变为1

TM1650_bright(0x08|0x01);

如果只写入地址,而不发送后续数据,效果是这样的:
在这里插入图片描述

而如果只是写入8级亮度:

TM1650_bright(0x01);

注意:屏幕是根本不会亮的,就是说没有后续数据时,不管几级亮度屏幕不会点亮。而若写入B3,只不过让所有小数点全都亮起来而已,并且后续无法单独关闭其中某几个了,相当于无法设置可控的小数模式了,小数点会常亮,所以除非完全不用这四个小数点位置,否则B3位保持0就可以。


2 显示数据

在这里插入图片描述
(1)上图DIG1324分别代表数码管的4个数字位置,68 6A 6C 6E 可以理解为对应这4个数字位置的 起始地址。

而往一个指定(0x68 0x6A 0x6C 0x6E)的数字位置写入数字的通信格式为:

IIC发送格式:起始信号,地址码(0x68 0x6A 0x6C 0x6E其中之一),数码管段数据(1字节),结束信号

(2)地址码我们已经很而清楚了,那么数码管后面1字节的数据,是怎么转化为数字呢?怎么用一个字节让对应位置显示出像要设置的数字或小数点呢?
首先我们先了解数码管一个数字位可以点亮的位置:
在这里插入图片描述
可以看到共有8个位置,而一个字节是不是也是刚好8位,刚好每一位二进制01可以控制一个位置的点亮,那问题就很简单了,搞清楚每个二进制位是对应哪一个位置就可以了。我们可以使用 1 2 4 6 8…64 这种位中只有1位是1二进制,测试一下后7位的内容:

for(int i=1;i<=64;i*=2){
	TM1650_addPrint(DIG,i); //直接以10进制即可,DIGb表哪个数码管数字位
	Delay_ms(400);
}

以随便一位为例,效果如下:
请添加图片描述

并且经单独测试可得出,从左往右第一位:控制的是小数点的点亮(1亮 0灭),对比可以得出下图:
在这里插入图片描述
那么根据这个图,来足组合出1到9,就没什么难度了,控制小数点只需要用得出的地址0x80进行按位或运算即可,也就是把地址的第一位修改为1。

以数字1 为例,只要2 和 4 点亮,那么就是在1字节的2位 和 4 位设置1 ,其余为位设0,准换为16进制表示即可,如下图:
在这里插入图片描述

得出0-9对应地址:

{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}

同理,A-F 也一样可计算得出

二 封装功能与测试样例

1 封装函数一览

Tm1650的 SCL,SDA 分别接stm32f103c8t6的 B10,B11,有需求的宏定义直接一键修改

//头文件:#include "Tm1650.h" 
#ifndef TM1650_H_
#define TM1650_H_

//设置亮度参数
#define TM1650_BRIGHT1       0x11   /*一级亮度,打开LED显示*/
#define TM1650_BRIGHT2       0x21   /*二级亮度,打开LED显示*/
#define TM1650_BRIGHT3       0x31   /*三级亮度,打开LED显示*/
#define TM1650_BRIGHT4       0x41   /*四级亮度,打开LED显示*/
#define TM1650_BRIGHT5       0x51   /*五级亮度,打开LED显示*/
#define TM1650_BRIGHT6       0x61   /*六级亮度,打开LED显示*/
#define TM1650_BRIGHT7       0x71   /*七级亮度,打开LED显示*/
#define TM1650_BRIGHT8       0x01   /*八级亮度,打开LED显示*/
#define TM1650_DSP_OFF       0x00   /*关闭LED显示*/

//数码管位选
#define TM1650_DIG1     0
#define TM1650_DIG2     1
#define TM1650_DIG3     2
#define TM1650_DIG4     3

//是否开启小数点
#define ENABLE      1
#define DISABLE     0

//Tm1650的 SCL,SDA 分别接 B10,B11,有需求自己修改
#define IICGPIO GPIOB
#define RCC_APB2Periph_IICGPIO RCC_APB2Periph_GPIOB //需单独修改,勿忘
#define SCLPIN GPIO_Pin_10
#define SDAPIN GPIO_Pin_11

 
void TM1650_init(void);
void TM1650_bright(uint8_t param); //设置亮度 TM1650_BRIGHT1-8
void TM1650_showNum(int num);      //显示一个4位以内的数字,右对齐,前补0
void TM1650_clear(void);
void TM1650_print(uint8_t dig,uint8_t seg_data); //第几位,显示数字几
void TM1650_pointPrint(uint8_t dig,uint8_t seg_data,uint8_t point);//第几位,显示数字几,带小数点

void TM1650_addPrint(uint8_t dig,uint8_t seg_data);//大佬请看这里:直接写入地址
void TM1650_lambLineTest(uint8_t DIG);//测试灯段损坏

#endif


2 功能实现

IIC与Tm1650封装在一起,便于移植函数功能都有介绍

#include "stm32f10x.h" 
#include "Delay.h"
#include "Tm1650.h" 

//显示内容阴码:0-9
const uint8_t number[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; 

//A-F:0x77,0x7c,0x39,0x5e,0x79,0x71
 
//----------------------IIC设置部分------------------------
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(IICGPIO, SCLPIN, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(IICGPIO, SDAPIN, (BitAction)BitValue);		//根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(IICGPIO, SDAPIN);		//读取SDA电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
	return BitValue;											//返回SDA电平
}


void MyI2C_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_IICGPIO, ENABLE);	//开启IICGPIO的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = SCLPIN | SDAPIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(IICGPIO, &GPIO_InitStructure);					//将PB10和PB11引脚初始化为开漏输出
	
	/*设置默认电平*/
	GPIO_SetBits(IICGPIO, SCLPIN | SDAPIN);			//设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}


void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);							//释放SDA,确保SDA为高电平
	MyI2C_W_SCL(1);							//释放SCL,确保SCL为高电平
	MyI2C_W_SDA(0);							//在SCL高电平期间,拉低SDA,产生起始信号
	MyI2C_W_SCL(0);							//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);							//拉低SDA,确保SDA为低电平
	MyI2C_W_SCL(1);							//释放SCL,使SCL呈现高电平
	MyI2C_W_SDA(1);							//在SCL高电平期间,释放SDA,产生终止信号
}

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)				//循环8次,主机依次发送数据的每一位
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));	//使用掩码的方式取出Byte的指定一位数据并写入到SDA线
		MyI2C_W_SCL(1);						//释放SCL,从机在SCL高电平期间读取SDA
		MyI2C_W_SCL(0);						//拉低SCL,主机开始发送下一位数据
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	for (i = 0; i < 8; i ++)				//循环8次,主机依次接收数据的每一位
	{
		MyI2C_W_SCL(1);						//释放SCL,主机机在SCL高电平期间读取SDA
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}	//读取SDA数据,并存储到Byte变量
														//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0
		MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDA
	}
	return Byte;							//返回接收到的一个字节数据
}

void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);					//主机把应答位数据放到SDA线
	MyI2C_W_SCL(1);							//释放SCL,从机在SCL高电平期间,读取应答位
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
}

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;							//定义应答位变量
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	MyI2C_W_SCL(1);							//释放SCL,主机机在SCL高电平期间读取SDA
	AckBit = MyI2C_R_SDA();					//将应答位存储到变量里
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
	return AckBit;							//返回定义应答位变量
}

//--------------------------------------------------

/*
功能:初始化数码管
参数:IIC: SCL-B10,SDA-B11,有需要跳转到MyI2C.c中修改
解释:IIC与设置亮度,清空显示
*/
void TM1650_init(void)
{
	MyI2C_Init();
	TM1650_bright(TM1650_BRIGHT5);   //初始化为5级亮度,打开显示
	TM1650_clear();     //将显存内容清0
}

/*
功能:设置亮度并打开显示
参数:param : TM1650_BRIGHTx,x为1-8表亮度,TM1650_DSP_OFF为关闭led显示
解释:这个操作不影响显存中的数据
*/
void TM1650_bright(uint8_t param)
{
	MyI2C_Start();
	MyI2C_SendByte(0x48);  //通信格式:起始信号,模式命令(1字节),显示命令(1字节),结束信号
	MyI2C_ReceiveAck();     
	
	MyI2C_SendByte(param); 
	MyI2C_ReceiveAck();    //显示命令
	MyI2C_Stop();
}
 
 
/*
功能:清空数据,屏幕熄灭
解释:正常只能显示0-9,10作特殊判断,0000 0000 全部熄灭。
*/
void TM1650_clear(void)
{
	uint8_t dig;
	for(dig = TM1650_DIG1 ; dig<= TM1650_DIG4 ;dig++)
	{
		TM1650_print(dig,10);//特殊数字,将显存数据刷为0
	}
}


/*
功能:往一个指定的数码管位写入指定的显示数字
参数1:DIG:  TM1650_DIGX,(X:1234),对应1234位置
参数2:数字0~9
参数3:是否开启当前位小数点:ENABLE,DIAABLE
解释:通信格式:起始信号,地址码(1字节),数码管段数据(1字节),结束信号
*/
void TM1650_print(uint8_t dig,uint8_t seg_data)
{
	MyI2C_Start();
	MyI2C_SendByte(dig*2+0x68); //显存起始地址为0x68,dig直接用0x68,0x6A,0x6e,0x6c也行
	MyI2C_ReceiveAck();  
	
	if(seg_data == 10)   //clear 清空显示
		MyI2C_SendByte(0);
	else 
	  MyI2C_SendByte(number[seg_data]); //数码管段数据
	MyI2C_ReceiveAck();    
	MyI2C_Stop();
}

/*
功能:往一个指定的数码管位写入指定的显示数字,可设置小鼠点
参数1:DIG:  TM1650_DIGX,(X:1234),对应1234位置
参数2:数字0~9
参数3:是否开启当前位小数点:ENABLE,DIAABLE
解释:通信格式:起始信号,地址码(1字节),数码管段数据(1字节),结束信号
*/
void TM1650_pointPrint(uint8_t dig,uint8_t seg_data,uint8_t point)
{
	MyI2C_Start();
	MyI2C_SendByte(dig*2+0x68); //显存起始地址为0x68,dig直接用0x68,0x6A,0x6e,0x6c也行
	MyI2C_ReceiveAck();  
	
	if(seg_data == 10)   //clear 清空显示
		MyI2C_SendByte(0);
	else if(seg_data==11){  //仅显示小数点
		MyI2C_SendByte(0x80);
	}
	else {                //显示数字+小数点
		uint8_t t=number[seg_data];  //附加小数点
		if(point)  t=t|0x80;
	  MyI2C_SendByte(t); //数码管段数据
	}
	MyI2C_ReceiveAck();    
	MyI2C_Stop();
}


/*
功能:(保留地址写入功能)指定的数码管位写入指定的地址
参数1:DIG:  TM1650_DIGX,(X:1234),对应1234位置
参数2:阴码地址
解释:通信格式:起始信号,地址码(1字节),数码管段数据(1字节),结束信号
*/
void TM1650_addPrint(uint8_t dig,uint8_t seg_data)
{
	MyI2C_Start();
	MyI2C_SendByte(dig*2+0x68); //显存起始地址为0x68,dig直接用0x68,0x6A,0x6e,0x6c也行
	MyI2C_ReceiveAck();  
	
	MyI2C_SendByte(seg_data);//直接发地址
  MyI2C_ReceiveAck();	
	MyI2C_Stop();
}



/*
功能:数字位灯段测试
参数:DIG:TM1650_DIGX,(X:1234)
解释:灯段测试,观察二进制1~64分别对应哪一位灯光变化
*/
void TM1650_lambLineTest(uint8_t DIG){
   for(int i=1;i<=64;i*=2){
			TM1650_addPrint(DIG,i);
			Delay_ms(400);
	}
	TM1650_pointPrint(DIG,11,ENABLE);
	Delay_ms(400);
	TM1650_clear();
	
}

/*
功能:显示一个数字
参数:num<=9999
解释:空位补0,右对其
*/
void TM1650_showNum(int num){
		int ge=num%10;
		int sw=num>10?num/10%10:0;
		int bw=num>100?num/100%10:0;
		int qw=num>1000?num/1000:0;
		TM1650_print(TM1650_DIG4,ge);
		TM1650_print(TM1650_DIG3,sw);
		TM1650_print(TM1650_DIG2,bw);
		TM1650_print(TM1650_DIG1,qw);
}

3 demo全面测试

全亮度测试,所有灯段位置异常检测,显示4位以为数字,单独位数字设置(小数点亦可在设置数据时单独点亮),保留地址写入功能。有需求所有代码直接粘过去就行,接线想该就改一下Tm160.h里引脚宏定义部分,很好改。不想改就SCL SDA 接 B10 B11,应该可以直接使用。

数码管这东西,初学者看IIC,原理图和寄存器一脸懵逼,不知道如何下手,但其实也就那么一回事,硬件其实并不难,只是入门起来麻烦些,一堆原理对自学者不怎么友好,希望这篇博客能节省你的时间,(因为博主本人非常讨厌,去慢悠悠的看长视频)

#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"//可不用
#include "Tm1650.h"


int main(void)
{
	//oled模块,CD接的B8 B9,可不用管
	OLED_Init();
	OLED_ShowString(1, 1, "digital test");
	
	TM1650_init();
	while (1)
	{
		
		/*亮度测试
		int speed=90;
		TM1650_showNum(6666);
		TM1650_bright(TM1650_BRIGHT1);Delay_ms(speed);
		TM1650_bright(TM1650_BRIGHT2);Delay_ms(speed);
		TM1650_bright(TM1650_BRIGHT3);Delay_ms(speed);
		TM1650_bright(TM1650_BRIGHT4);Delay_ms(speed);
		TM1650_bright(TM1650_BRIGHT5);Delay_ms(speed);
		TM1650_bright(TM1650_BRIGHT6);Delay_ms(speed);
		TM1650_bright(TM1650_BRIGHT7);Delay_ms(speed);
		TM1650_bright(TM1650_BRIGHT8);Delay_ms(speed);
		TM1650_bright(TM1650_DSP_OFF);Delay_ms(speed);
		*/
		
		
		/*每个位置灯段异常测试
		TM1650_lambLineTest(TM1650_DIG1);//TM1650_DIGX: X-1324
		Delay_ms(200);
		TM1650_lambLineTest(TM1650_DIG2);//TM1650_DIGX: X-1324
		Delay_ms(200);
		TM1650_lambLineTest(TM1650_DIG3);//TM1650_DIGX: X-1324
		Delay_ms(200);
		TM1650_lambLineTest(TM1650_DIG4);//TM1650_DIGX: X-1324
		Delay_ms(200);
		*/
		
		
		/*显示多位数,0-9999
		int k=0;
		while(k<10000){
			TM1650_showNum(k++);
	    Delay_ms(20);
		  //TM1650_clear(); //后加delay可控制闪烁
	  }
		*/
		
		
		/*单个数字位 测试
		for(int i=0;i<10;i++){
		  TM1650_print(TM1650_DIG1,i);  //TM1650_DIGX: X-1324
		  Delay_ms(400);
	  }
		*/
		
		
		/*地址测试  0-9:0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f
		TM1650_addPrint(TM1650_DIG1,0x06); // 1
		Delay_ms(400);
		TM1650_addPrint(TM1650_DIG1,0x80|0x06); //用1000 0000 与其余地址或运算,即可带小数点
		Delay_ms(400);
		*/
		
		
		/*小数测试 13.14
		TM1650_print(TM1650_DIG1,1);
		TM1650_pointPrint(TM1650_DIG2,3,ENABLE);
		TM1650_print(TM1650_DIG3,1);
		TM1650_print(TM1650_DIG4,4);
		*/
		
		
	}
}
### 关于TM1650驱动数码管的程序代码及教程 对于STC8系列单片机而言,存在一个已经过实际验证且稳定可靠的TM1650数码管驱动程序[^1]。此驱动程序不仅包含了完整的TM1650数码管驱动代码,还提供了基础的显示功能支持。 #### 示例代码展示 下面是一个简化版的例子来说明如何利用这个驱动库在数码管上显示出特定数值: ```c #include "tm1650.h" void main() { // 初始化TM1650模块 TM1650_Init(); while (1) { // 设置亮度等级(范围通常是0-7) TM1650_SetBrightness(5); // 显示数字'1234' TM1650_DisplayData(0x00, '1'); TM1650_DisplayData(0x01, '2'); TM1650_DisplayData(0x02, '3'); TM1650_DisplayData(0x03, '4'); delay_ms(1000); // 延迟一秒 // 清除屏幕 TM1650_ClearDisplay(); delay_ms(1000); // 再次延迟一秒 } } ``` 上述代码实现了每隔两秒交替显示“1234”和空白的效果。`TM1650_Init()`用于初始化TM1650设备;`TM1650_SetBrightness(x)`用来调整背光强度;而`TM1650_DisplayData(addr,val)`则是向指定位置写入字符数据的方法之一。后通过调用`TM1650_ClearDisplay()`可以清除当前屏幕上所有的内容。 为了使这段代码正常工作,在将其集成至个人项目之前还需要完成几个前置条件: - 将所有必要的文件从GitHub仓库下载下来; - 把这些文件加入到自己的IDE工程里边去; - 正确设置好I²C接口对应的GPIO引脚定义; - 编译链接之后把终生成的目标文件烧录进MCU内部闪存当中。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值