ARM开发之基于IIC协议的TM1650驱动实现(模拟IIC实现)

目录

一、内存映射

1、什么是内存映射?为什么要内存映射?

2、gec6818如何进行内存映射?(相关的函数)

3、内存映射代码 :

二、模拟IIC的底层代码实现

1、配置输入输出方向

2、拉高拉低引脚

3、获得总线传回的数据

4、协议的实现

三、TM1650的使用

1、TM1650简介

2、TM1650怎么驱动?

2.1 控制命令

 2.2 显存地址

 2.3 对显存地址写数据时序

四、针对TM1650实现的IIC驱动

1、TM1650模块驱动(完整驱动代码)

2、驱动测试程序

3、Makefile

4、Linux操作

关键词:

1、模拟IIC协议

2、S5P6818

3、ARM内存访问控制

4、TM1650数码管

本片博客记录在gec6818平台上的模拟IIC实现,并成功驱动 TM1650 数码管

模拟IIC也可适用其他所有用IIC协议驱动的外设!

如文章有错误,请各位不吝赐教。


一、内存映射

1、什么是内存映射?为什么要内存映射?

外设的SFR(特殊功能寄存器)编址与内存的编址是同一个地址空间,叫做IO内存
但是当内核想要访问某些特殊寄存器或者内存单元的时候,不能直接使用IO内存的物理地址进行访问,而要通过该物理地址所对应的虚拟地址进行访问,因此,必须要进行地址的转换,而从物理地址到虚拟地址的映射我们就称为内存映射。

2、gec6818如何进行内存映射?(相关的函数)

//struct resource *
#include <linux/ioport.h> 

//ioremap iounmap 之类的函数
#include <linux/io.h> 
/*

ioremap函数

1、参数:
offset:要映射的物理内存区的起始地址
size:物理地址的范围

2、返回值:
虚拟地址的指针

3、头文件引用:
#include <linux/io.h>

*/
static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size);


/*

iounmap函数

1、参数:
io_addr:虚拟地址的指针

2、返回值:
无

3、头文件引用:
#include <linux/io.h>

*/
void __iounmap(volatile void __iomem *io_addr);

3、内存映射代码 :

//1、定义SCL、SDA的GPIO虚拟内存指针
static void __iomem * IIC_OUT_VA; //0x00
static void __iomem * IIC_OUTENB_VA; //0x04
static void __iomem * IIC_ALTFN0_VA; //0x20
static void __iomem * IIC_ALTFN1_VA; //0x24
static void __iomem * IIC_PAD_VA; //0x18

//1、定义tm1650设备结构体
struct miscdevice tm1650_IIC_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "tm1650_drv",
	.fops = &tm1650_IIC_fops,
};

//2、IO内存的动态映射,由物理地址得到虚拟地址
//这里用到 GPIOC7 GPIOC8 两个引脚  查阅GPIOC的基址
IIC_OUT_VA = ioremap(0xc001c000, 0x1000);
if(IIC_OUT_VA == NULL){
	printk("ioremap error\n");
	misc_deregister(&tm1650_IIC_misc);
	return -EBUSY;
}	

//3、得到每个寄存器的虚拟地址(根据s5p6818文档查阅)
IIC_OUTENB_VA = IIC_OUT_VA + 0x04; //0x04
IIC_ALTFN0_VA = IIC_OUT_VA + 0x20; //0x20
IIC_ALTFN1_VA = IIC_OUT_VA + 0x24; //0x24
IIC_PAD_VA    = IIC_OUT_VA + 0x18; //0x18

下面讲解上述的代码以及如何在文档查阅物理地址和偏移量:

1)三星s5p6818文档的这个部分是讲GPIO控制器的,相关寄存器解说就在这里。

2) 查看寄存器描述,可以知道基址(Base Address)以及各寄存器的偏移(Offset),当然大家也可以看后面的描述(Description)来了解一下各寄存器的作用是什么。

 3)举例来说,譬如我实验的IIC引脚用的GPIO是GPIOC,那么配置输出模式的寄存器GPIOCOUT基址就为 0xc001c000 ,偏移就为 0x1000。

 所以也就可以看到内存映射代码的ioremap函数参数(上面)写的是:

IIC_OUT_VA = ioremap(0xc001c000, 0x1000);

同理,我们想找到 GPIOCOUTENB 寄存器的物理地址也就很简单了,也就是:

基址+偏移:

GPIOCOUTENB  = 0xc001c000  +  0x004;

 但是!!我们不用都去使用基址+偏移找到其物理地址,再用ioremap进行映射获得虚拟地址给内核使用,因为我们可以发现:

每个GPIOXOUT寄存器偏移都为 0x1000 ,后面的寄存器(譬如GPIOXENB......等)偏移都在 0x1000内(GPIOXENB的偏移是 0x004),也就是说我们可以根据GPIOXOUT寄存器的首地址来找到其他寄存器,这个“首地址”可以是物理地址,也可以是ioremap后的虚拟地址,偏移量都生效。所以明白了这点之后,我们就不用对每个寄存器进行ioremap来获得虚拟地址,我们只需要获得GPIOXOUT的虚拟地址,然后用该虚拟地址加上偏移量就可以获得其他寄存器的虚拟地址!也就说我们只使用了一次ioremap,巧妙地使用了偏移量

因此就可以看到上面内存映射代码:

//得到每个寄存器的虚拟地址
IIC_OUTENB_VA = IIC_OUT_VA + 0x04; //0x04
IIC_ALTFN0_VA = IIC_OUT_VA + 0x20; //0x20
IIC_ALTFN1_VA = IIC_OUT_VA + 0x24; //0x24
IIC_PAD_VA    = IIC_OUT_VA + 0x18; //0x18

总结:至此,内存映射我们就完成了,我们找到了相关寄存器的虚拟地址,我们就可以使用这些虚拟地址进行读写以控制IO,来实现IIC。

二、模拟IIC的底层代码实现

     模拟IIC通信将涉及到SDA、SCL的

  1. 配置输入输出方向
  2. 拉高拉低
  3. 获得总线传回的数据
  4. 协议的实现(起始、结束信号等)

1、配置输入输出方向

主要是SDA的方向需要配置,SCL只是时钟线

void SDA_OUT(void){
	(*(unsigned int *)IIC_OUTENB_VA) |= (1<<8);
}

void SDA_IN(void){
	(*(unsigned int *)IIC_OUTENB_VA) &= ~(1<<8);
}

可以看到我们操作的是上面我们通过内存映射获得的虚拟地址(IIC_OUTENB_VA),通过对这个虚拟地址的读写来对寄存器进行读写,达到控制GPIO的作用。

2、拉高拉低引脚

SCL

void SCL_set0(void){
	(*(unsigned int *)IIC_OUT_VA) &= ~(1<<7);
}

void SCL_set1(void){
	(*(unsigned int *)IIC_OUT_VA) |= (1<<7);
}

SDA

void SDA_set0(void){
	(*(unsigned int *)IIC_OUT_VA) &= ~(1<<8);
}

void SDA_set1(void){
	(*(unsigned int *)IIC_OUT_VA) |= (1<<8);
}

3、获得总线传回的数据

char SDA_VAL(void){
	return ((*(unsigned int *)IIC_PAD_VA) >> 8)&0X01;
}

4、协议的实现

//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	SDA_set1();	  	  
	SCL_set1();
	udelay(2);
 	SDA_set0();//START:when CLK is high,DATA change form high to low 
	udelay(2);
	SCL_set0();//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	SCL_set0();
	SDA_set0();//STOP:when CLK is high DATA change form low to high
	udelay(2);
	SCL_set1(); 
	SDA_set1();//发送I2C总线结束信号
	udelay(2);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	SDA_set1();udelay(2);	   
	SCL_set1();udelay(2);	 
	while(SDA_VAL())//
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	SCL_set0();//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	SCL_set0();
	SDA_OUT();
	SDA_set0();
	udelay(2);
	SCL_set1();
	udelay(2);
	SCL_set0();
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	SCL_set0();//SCL = 0
	SDA_OUT();//SDA设为输出
	SDA_set1();//SDA置高平
	udelay(2);
	SCL_set1();
	udelay(2);
    SCL_set0();
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    SCL_set0();//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
		if(((txd&0x80)>>7) == 1)
			SDA_set1();
		else if(((txd&0x80)>>7) == 0)
			SDA_set0();
        txd<<=1; 	  
		    SCL_set1();
		    udelay(2); 
		    SCL_set0();	
		    udelay(2);
    }	 
} 	    

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        SCL_set0(); 
        udelay(2);
		SCL_set1();
        receive<<=1;
        if(SDA_VAL())receive++;   
		udelay(2); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

总结:至此我们就实现了模拟IIC,根据相关外设的IIC通信时序的要求,我们就可以实现与外设的IIC通信。下面我们就来实现IIC设备 TM1650 控制。

三、TM1650的使用

1、TM1650简介

2、TM1650怎么驱动?

2.1 控制命令

我们只用模式命令来显示数码管  0x48

 2.2 显存地址

写LED显示数据的时候,按照从显示地址从高位到低位,从数据字节的高位到低位操作。

1)DIG1~DIG4 :数码管第一位到第四位。

2)显存地址 :0x68 0x6A 0x6C 0x6E

 2.3 对显存地址写数据时序

1)显示数据码表 NUM[10]

/* TM1650 码表*/
const uint8_t NUM[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

2)根据文档时序,封装写字节函数 TM1650_Wr_RAM(uint8_t Address, uint8_t Data)

/* TM1650 写字节函数 */
void TM1650_Wr_RAM(uint8_t Address, uint8_t Data)
{
    IIC_Start();
    IIC_Send_Byte(Address);
    IIC_Wait_Ack();
    IIC_Send_Byte(Data);
    IIC_Wait_Ack();
    IIC_Stop();
}

3)往显存写数据显示数据,同样我们封装成函数 set_number(char mode, int num)

/* 
	TM1650 写入数字函数 
	第一位:num / 1000
	第二位:小数点.
	第三位:num % 100 / 10   --十位
	第四位:num % 10             --个位
*/
void set_number(char mode, int num)
{
	TM1650_Wr_RAM(0x68, NUM[num / 1000]);
	if(mode == 1)
	{
		TM1650_Wr_RAM(0x6A, 0X80);// 第二位只显示小数点
	}
	else
	{
		TM1650_Wr_RAM(0x6A, NUM[num % 1000 / 100]);// 第二位显示正常数据
	}	
	TM1650_Wr_RAM(0x6C, NUM[num % 100 / 10]);
	TM1650_Wr_RAM(0x6E, NUM[num % 10]);
}

 4)打开显示设置命令:

//初始化TM1650模块
TM1650_Wr_RAM(0x48, 0x71);

四、针对TM1650实现的IIC驱动

1、TM1650模块驱动(完整驱动代码)

下面是源文件:tm1650_drv.c

使用 insmod 插入模块 tm1650_drv.ko:

[root@GEC6818 /IOT/tangJW/a53_10]#insmod tm1650_drv

#include <linux/module.h>
#include <linux/printk.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <mach/platform.h>
#include <linux/errno.h>
#include <linux/ioport.h> //struct resource *
#include <linux/io.h> //ioremap
#include <linux/uaccess.h> //copy_from_user
#include <linux/gpio.h>
#include <linux/delay.h>

//定义SCL、SDA的GPIO虚拟内存指针
static void __iomem * IIC_OUT_VA; //0x00
static void __iomem * IIC_OUTENB_VA; //0x04
static void __iomem * IIC_ALTFN0_VA; //0x20
static void __iomem * IIC_ALTFN1_VA; //0x24
static void __iomem * IIC_PAD_VA; //0x18

void SDA_OUT(void){
	(*(unsigned int *)IIC_OUTENB_VA) |= (1<<8);
}

void SDA_IN(void){
	(*(unsigned int *)IIC_OUTENB_VA) &= ~(1<<8);
}

void SDA_set0(void){
	(*(unsigned int *)IIC_OUT_VA) &= ~(1<<8);
}

void SDA_set1(void){
	(*(unsigned int *)IIC_OUT_VA) |= (1<<8);
}

char SDA_VAL(void){
	return ((*(unsigned int *)IIC_PAD_VA) >> 8)&0X01;
}

void SCL_set0(void){
	(*(unsigned int *)IIC_OUT_VA) &= ~(1<<7);
}

void SCL_set1(void){
	(*(unsigned int *)IIC_OUT_VA) |= (1<<7);
}

//模拟IIC协议用到的函数

//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	SDA_set1();	  	  
	SCL_set1();
	udelay(2);
 	SDA_set0();//START:when CLK is high,DATA change form high to low 
	udelay(2);
	SCL_set0();//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	SCL_set0();
	SDA_set0();//STOP:when CLK is high DATA change form low to high
	udelay(2);
	SCL_set1(); 
	SDA_set1();//发送I2C总线结束信号
	udelay(2);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	SDA_set1();udelay(2);	   
	SCL_set1();udelay(2);	 
	while(SDA_VAL())//
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	SCL_set0();//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	SCL_set0();
	SDA_OUT();
	SDA_set0();
	udelay(2);
	SCL_set1();
	udelay(2);
	SCL_set0();
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	SCL_set0();//SCL = 0
	SDA_OUT();//SDA设为输出
	SDA_set1();//SDA置高平
	udelay(2);
	SCL_set1();
	udelay(2);
    SCL_set0();
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    SCL_set0();//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
		if(((txd&0x80)>>7) == 1)
			SDA_set1();
		else if(((txd&0x80)>>7) == 0)
			SDA_set0();
        txd<<=1; 	  
		    SCL_set1();
		    udelay(2); 
		    SCL_set0();	
		    udelay(2);
    }	 
} 	    

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        SCL_set0(); 
        udelay(2);
		SCL_set1();
        receive<<=1;
        if(SDA_VAL())receive++;   
		udelay(2); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

//------------- TM1650 @TangJW ---------------
/* TM1650 码表*/
const uint8_t NUM[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

/* TM1650 写字节函数 */
void TM1650_Wr_RAM(uint8_t Address, uint8_t Data)
{
    IIC_Start();
    IIC_Send_Byte(Address);
    IIC_Wait_Ack();
    IIC_Send_Byte(Data);
    IIC_Wait_Ack();
    IIC_Stop();
}

/* 
	TM1650 写入数字函数 
	第一位:num / 1000
	第二位:小数点.
	第三位:num % 100 / 10   --十位
	第四位:num % 10             --个位
*/
void set_number(char mode, int num)
{
	TM1650_Wr_RAM(0x68, NUM[num / 1000]);
	if(mode == 1)
	{
		TM1650_Wr_RAM(0x6A, 0X80);// 第二位只显示小数点
	}
	else
	{
		TM1650_Wr_RAM(0x6A, NUM[num % 1000 / 100]);// 第二位显示正常数据
	}	
	TM1650_Wr_RAM(0x6C, NUM[num % 100 / 10]);
	TM1650_Wr_RAM(0x6E, NUM[num % 10]);
}

// 定义字符设备的文件操作集
/* 
	open函数:
	初始化TM1650模块 
*/
int TM1650_open(struct inode *inode, struct file *filp)
{
	//初始化TM1650模块
	TM1650_Wr_RAM(0x48, 0x71);
	
	printk("TM1650_init! \n");
	return 0;
}

/*
	ioctl函数:实现set_number
	cmd:数据参数
	args:
	  --0  第二位显示正常数据
	  --1  第二位显示小数点
*/
static void TM1650_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
	set_number(args, cmd);
}

//定义文件操作集
const struct file_operations tm1650_IIC_fops = {
	.owner = THIS_MODULE,
	.open = TM1650_open,
	.unlocked_ioctl = TM1650_ioctl,
};

struct miscdevice tm1650_IIC_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "tm1650_drv",
	.fops = &tm1650_IIC_fops,
};

/* 
	TM1650初始化
	-SCL: GPIOC 7
	-SDA: GPIOC 8
*/
static int __init TM1650_init(void)
{
	int ret;
	
	printk("TM1650_init... \n");
    //注册混杂设备
	ret =  misc_register(&tm1650_IIC_misc);
	if(ret < 0)
	{
		printk("misc_register error\n");
		return -EBUSY;
	}

	//IO内存的动态映射,由物理地址得到虚拟地址  GPIOC7 GPIOC8
	IIC_OUT_VA = ioremap(0xc001c000, 0x1000);
	if(IIC_OUT_VA == NULL){
		printk("ioremap error\n");
		misc_deregister(&tm1650_IIC_misc);
		return -EBUSY;
	}	

	//得到每个寄存器的虚拟地址
  	IIC_OUTENB_VA = IIC_OUT_VA + 0x04; //0x04
  	IIC_ALTFN0_VA = IIC_OUT_VA + 0x20; //0x20
  	IIC_ALTFN1_VA = IIC_OUT_VA + 0x24; //0x24
  	IIC_PAD_VA    = IIC_OUT_VA + 0x18; //0x18
	
	//访问虚拟地址:GPIOC7 SCL         GPIOC8 SDA
	//将引脚设置成普通的GPIO
	*(unsigned int *)IIC_ALTFN0_VA &=~((3<<14) + (3<<16));
	*(unsigned int *)IIC_ALTFN0_VA |=((1<<14) + (1<<16));

	//将2个GPIO设置为输出
	*(unsigned int *)IIC_OUTENB_VA |= ((1<<7)+(1<<8));
	
	//2个GPIO输出高电平,IIC默认是释放模式
	*(unsigned int *)IIC_OUT_VA |= ((1<<7)+(1<<8));

	return ret;
}

static void __exit TM1650_exit(void)
{
	iounmap(IIC_OUT_VA);
	misc_deregister(&tm1650_IIC_misc);
}

module_init(TM1650_init);
module_exit(TM1650_exit);

MODULE_AUTHOR("tangjingwei@qq.com");
MODULE_DESCRIPTION("IIC Device /TM1650/ Driver @TangJW");
MODULE_LICENSE("GPL");

2、驱动测试程序

下面是源文件:tm1650_test.c

测试程序运行:

1、第二个参数为 1,控制第二位总是显示小数点

[root@GEC6818 /IOT/tangJW/a53_10]#./tm1650_test 2023 1

 2、第二个参数为 0,控制第二位显示正常数据

[root@GEC6818 /IOT/tangJW/a53_10]#./tm1650_test 2023 0

/*****************************

TM1650测试函数,请配合 tm1650_drv.ko 使用
作者:唐京伟 
联系:tangjingwei@qq.com

IIC测试硬件引脚:
-SCL: GPIOC 7
-SDA: GPIOC 8 

*****************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv)
{	
	int num, mode;
	
	/* 1、调用设备的open函数(即发送初始化tm1650的iic时序信号)*/
	int fd = open("/dev/tm1650_drv",O_RDWR); 
	if(fd<0){
		perror("open /dev/tm1650_drv failed! ");
		return 0;
	}

	/* 2、调用设备的ioctl函数(即发送控制显示tm1650的iic时序信号)*/
	num = ((int)(argv[1][0]) - 48)*1000 + ((int)(argv[1][1]) - 48)*100 + ((int)(argv[1][2]) - 48)*10 + ((int)(argv[1][3]) - 48);
	mode = (int)(argv[2][0]) - 48;
	printf("Open TM1650 successfully, check it out!\n");
	//mode: 1-第二位显示小数点			0-第二位显示正常数据
	ioctl(fd, num, mode); 

	while(1)
	{		

    }
}

3、Makefile

下面是文件:Makefile

Makefile 注意点:

1、obj-m  应为  tm1650_drv.o 

2、KERNELDIR  应为  自己的Linux环境下的6818源码kernel目录路径

3、CROSS_COMPILE  应为  自己的Linux环境下的交叉编译器路径

4、命令介绍

make 命令:使用交叉编译器编译 tm1650_drv.c

make test 命令:使用arm-linux-gcc编译 tm1650_test.c

obj-m:=tm1650_drv.o


KERNELDIR:=/usr/local/6818GEC/kernel
CROSS_COMPILE:=/usr/local/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
PWD:=$(shell pwd)

default:
	$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules

test:
	arm-linux-gcc tm1650_test.c -o tm1650_test

clean:
	rm -rf *.o *.order .*.cmd *.mod.c *.symvers

4、Linux操作

 编译驱动:

 编译测试程序:

上传文件至开发板,我用的是SecureCRT,并插入模块驱动:

相关文档链接:

三星S5P6818官方文档 + TM1650官方文档     提取码:2023

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目中提到了iic控制和TM1650,可以看出是要回答关于TM1650的I2C控制方法。 TM1650是一种常见的数字管驱动芯片,广泛应用于数码管显示等设备中。它通过I2C总线进行控制,即通过I2C协议来发送命令和数据来操作TM1650。 I2C(Inter-Integrated Circuit)是一种串行总线标准,可以实现多个设备之间的数据传输和通信。在使用I2C控制TM1650之前,我们首先需要确保系统中有I2C控制器与TM1650相连。 I2C通信主要涉及两根线:SCL(时钟线)和SDA(数据线)。首先,主控制器通过SCL线发送一串脉冲信号来确定通信时钟频率,而SDA线上的信号则用于传输数据和命令。 具体控制TM1650的步骤如下: 1. 启动信号:主控制器向SDA线发送一个低电平(0)的信号,同时保持SCL线为高电平(1)。这个低电平的信号表示启动一个通信过程的开始。 2. 设备地址:主控制器在SCL线保持高电平(1)的同时,按照I2C协议要求,向SDA线发送TM1650的设备地址。设备地址一般由芯片厂商预设,可以在TM1650的数据手册中找到。 3. 方向指令:主控制器会发送一个位,用以指示接下来的操作是写入数据(0)还是读取数据(1)。 4. 数据传输:根据方向指令,主控制器通过SCL和SDA线传输相应的命令和数据。具体的命令和数据可以根据TM1650的数据手册来确定。 5. 停止信号:主控制器向SDA线发送一个高电平(1)的信号,同时保持SCL线为高电平(1)。这个高电平的信号表示通信过程结束。 通过以上步骤,就可以通过I2C控制TM1650进行各种操作,例如显示数字、调节亮度等。 需要注意的是,具体的I2C控制细节还需参考TM1650的数据手册来进行调试和实现。此外,I2C控制TM1650还需要在相关的开发环境中编写相应的代码来完成控制操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值