0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux下IIC子系统和触摸屏驱动

嵌入式技术 来源:嵌入式技术 作者:嵌入式技术 2022-09-25 08:58 次阅读

Linux下IIC子系统和触摸屏驱动


1.IIC简介

I2C( Inter-Integrated Circuit)总线是由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。

  • I2C特性

(1)只要求两条总线线路一条串行数据线SDA,一条串行时钟线SCL;
(2)每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机关系软件设定地址,主机可以作为主机发送器或主机接收器;
(3)它是一个真正的多主机总线,如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁防止数据被破坏;
(4)串行的 8 位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达 3.4Mbit/s;
(5)片上的滤波器可以滤去总线数据线上的毛刺波保证数据完整;
(6)连接到相同总线的IC数量只受到总线的最大电容400pF;
IIC是属串行通讯总线,同步传输、半双工。

 I2C 总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从I2C 器件的数据手册得知),主从设备之间就通过这个地址来确定与哪个器件进行通信,在通常的应用中,我们把 CPU 带 I2C 总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。

IIC时序介绍参考:STM32CubeMx之硬件IIC驱动EEPROM

2.Linux下IIC驱动

Linux下编写IIC驱动有两种方式:
1. 将IIC驱动当做普通字符类设备来实现驱动注册;
2. 利用Linux下IIC驱动框架(IIC子系统)完成注册;
第一种方式操作简单,只需要会基本的字符设备驱动框架即可完成对IIC驱动注册;但可移植性较差;第二种方式则操作比较复杂,需要掌握IIC子系统框架注册流程,但可移植性强。

2.1 IIC子系统驱动模型

pYYBAGMtpqCAGyGtAAF4DwVPbG4674.png#pic_center

  从上图可以看出,IIC框架注意分为三层:适配器层、设备层和驱动层。三层之间关系可以简化如下关系:

poYBAGMtpqCAErncAADiSY30FIA272.png#pic_center

 适配器层主要实现IIC硬件接口配置、产生IIC实现,该代码一般由芯片厂商提供。
设备层主要是通过调用获取适配器的函数获取IIC硬件资源(得到IIC时序的相关接口函数),根据IIC模块手册和硬件原理图确定模块的设备地址,完成IIC设备层注册。
驱动层通过设备名字完成和设备层匹配,获取设备资源,注册设备驱动,实现应用层相关接口函数。

2.2 IIC子系统相关接口函数

2.2.1 设备层接口函数

IIC设备层注册步骤:

  1. 调用获取适配器函数i2c_get_adapter根据IIC总线编号获取IIC适配器资源;
  2. 填充struct i2c_board_info 结构体,调用i2c_new_device注册IIC设备;
  3. 注销设备时调用IIC设备注销函数i2c_unregister_device;
  • 获取适配器资源i2c_get_adapter
struct i2c_adapter *i2c_get_adapter(int nr)
函数功能: 获取适配器资源
形参: nr --IIC总线编号
返回值: 成功返回适配器资源结构体指针
  • struct i2c_board_info结构体
      在struct i2c_board_info结构体中,需要关系的参数是 设备名字type 和 设备地址addr,若有中断资源,则可填入 中断号irqr。
struct i2c_board_info {
	char		type[I2C_NAME_SIZE];//名字,驱动层和设备层匹配参数
	unsigned short	flags;//设备地址位数,一般不填或填0表示7位地址
	unsigned short	addr;//IIC设备地址
	void		*platform_data;//私有数据
	struct dev_archdata	*archdata;
	struct device_node *of_node;
	int		irq;//中断号
};
  • 注册设备i2c_new_device
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
函数功能: 注册IIC设备层
形参: adap–IIC适配器资源
    info --IIC设备板级资源信息
返回值: 注册成功返回IIC信息结构体指针
  • 注销设备 i2c_unregister_device
void i2c_unregister_device(struct i2c_client *client)
函数功能: 注销IIC设备层
形参: client–IIC信息结构体指针,IIC注册函数返回值;

2.2.2 驱动层接口函数

IIC驱动层注册步骤:

  1. 填充驱动层结构体struct i2c_driver ,调用驱动注册函数i2c_add_driver ;
  2. 注销驱动时调用注销函数i2c_del_driver ;
  3. 驱动层结构体struct i2c_driver
  4. 在struct i2c_driver结构体中我们必须实现的有资源匹配函数probe、资源释放函数remove、结构体struct device_driver中的设备名name、资源匹配结构体id_table。
truct i2c_driver {
int (*probe)(struct i2c_client *, const struct i2c_device_id *);//资源匹配函数
	int (*remove)(struct i2c_client *);//资源释放函数
	struct device_driver driver;//驱动相关结构体信息
	const struct i2c_device_id *id_table;//资源匹配结构体
};
  • 驱动注册和注销函数
//驱动注册函数
#define i2c_add_driver(driver)
//驱动注销函数
void i2c_del_driver(struct i2c_driver *);

2.2.3 IIC读写字符串函数

//读取多个数据函数,成功返回读取到的字节数,注意,改函数最大多32字节
s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client,u8 command, u8 length, u8 *values);
//写入多个数据函数,成功返回0,注意,改函数最大多32字节
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,u8 command, u8 length,const u8 *values);

3.电容屏ft5x06驱动注册

FT5x06系列集成电路单片机电容式触控面板控制器集成电路与一个内置的8位微控制器单元(MCU)他们采用互电容的方法,支持多点触控功能。结合共同电容触摸面板,FT5x06友好的输入功能,可应用于许多便携式设备,如手机,MIDs,上网本和笔记本个人电脑

  • FT5X06内核架构
pYYBAGMtpqGAOloYAADORQm_-dQ593.png#pic_center

   FT5X06支持I2C、SPI、UART三种接口。本次我们采用IIC协议完成驱动注册。
   FT5X06的I2C接口时序:

poYBAGMtpqGAOSIdAADsiFGunuI448.png#pic_center

3.1 FT5X06驱动注册

硬件平台:tiny4412
开发平台:ubuntu18.04
交叉编译器:arm-linux-gcc
内核:linux3.5

3.1.1 根据原理图要到IIC硬件资源信息

  要实现IIC子系统的设备和驱动层注册,必要参数为:IIC总线编号、设备名字、设备地址。
  根据Tiny4412的开发板原理图,可知触摸屏接口采用的是I2C1总线,中断检测脚为GPX1_6。

pYYBAGMtpqGALXrJAACcdgtDocU731.png#pic_centerpoYBAGMtpqKAZ_ujAAGAhG-bUPA930.png#pic_center

  由于本身tiny4412的内核中已自带触摸屏驱动,所以FT5X06的设备地址可根据mach-tiny4412.c的板级注册中找到:

pYYBAGMtpqKAW4rWAAJgnZqtf50913.png#pic_centerpoYBAGMtpqKANfLUAAEbyx9Q5CE686.png#pic_center

3.1.2 裁剪内核,卸载自带的触摸屏驱动

由于本身tiny4412的内核中已自带触摸屏驱动,所以若想自己编写该驱动则需要先卸载内核中的触摸屏驱动,才能实现自己的驱动文件注册。即需要完成内核裁剪,卸载自带的触摸屏驱动。
打开linux3.5内核的配置界面,找到ft5x06的驱动位置,将其卸载。

[wbyq@wbyq linux-3.5]$ make menuconfig
Device Drivers  --->
Input device support  ---> 
    Touchscreens  --->
       FocalTech ft5x0x TouchScreen driver
pYYBAGMtpqOAbWYfAAGFLmbXSu4027.png#pic_center

  最后重新编译内核,烧写内核,重新启动开发板。

3.1.3 注册IIC设备层

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

static struct i2c_board_info ft5x06_info=
{
	I2C_BOARD_INFO("ft5x06",0x38),
	//.irq=	
};
static struct i2c_client *client;

static int __init wbyq_ft5x06_dev_init(void)
{
	ft5x06_info.irq=gpio_to_irq(EXYNOS4_GPX1(6));//中断号
	struct i2c_adapter *adap=i2c_get_adapter(1);//获取适配器编号
	client=i2c_new_device(adap,&ft5x06_info);
	i2c_put_adapter(adap);
	if(client==NULL)return -ENODEV;
	printk("ft5x06设备层注册成功n");
    return 0;
}
static void __exit wbyq_ft5x06_dev_cleanup(void)
{
	/*注销设备层*/
	i2c_unregister_device(client);
	printk("ft5x06设备层注销成功n");
	
}
module_init(wbyq_ft5x06_dev_init);//驱动入口函数
module_exit(wbyq_ft5x06_dev_cleanup);//驱动出口函数

MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 eeprom_dev Driver");

3.1.4 注册IIC驱动层

  注册IIC驱动层,完成FT5X06中断注册。初始化工作,初始化等待队列头,注册杂项设备。实现ioctl函数接口和poll函数接口。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#define TOUCH_GETXY 0X80
static struct work_struct ft5x06_work;
static struct i2c_client *i2c_client;
struct Touch_xy
{
	int x;
	int y;
	int stat;
};
static struct Touch_xy touchxy;
DECLARE_WAIT_QUEUE_HEAD(ft5x06_q);//等待队列头
static void ft5x06_work_func(struct work_struct *work)
{	
	u8 val_data[8];
	u32 x,y;
	u32 stat;
	i2c_smbus_read_i2c_block_data(i2c_client,0,8,val_data);
	x=(val_data[3]&0Xf)<<8|val_data[4];
	y=(val_data[5]&0Xf)<<8|val_data[6];
	stat=val_data[2]&0xf;
	if(stat)
	{
		touchxy.x=x;
		touchxy.y=y;
		touchxy.stat=1;
	}
	else
	{
		touchxy.stat=0;
	}
	wake_up(&ft5x06_q);//唤醒进程
	//printk("(x,y):%d,%dn",x,y);	
}
static irqreturn_t  ft5x06_irq_work(int irq, void *dev)
{
	schedule_work(&ft5x06_work);
	return IRQ_HANDLED;
}

static int ft5x06_open(struct inode *inode, struct file *fp)
{
	printk("open函数调用成功");
	return 0;
}
static long ft5x06_ioctl(struct file *fp, unsigned int cmd, unsigned long data)
{
	int ret;
	switch(cmd)
	{
		case TOUCH_GETXY:
			ret=copy_to_user((void *)data,&touchxy,sizeof(touchxy));
			if(ret)return -1;
		default:
				return -1;
	}
	return 0;
}
static unsigned int ft5x06_poll(struct file *filp, struct poll_table_struct *p)
{
	int mask=0;
	poll_wait(filp,&ft5x06_q, p);
	if(touchxy.stat)mask|=POLLIN;
	return mask;
}
static int ft5x06_release(struct inode *inode, struct file *fp)
{
	printk("release函数调用成功");
	return 0;
}
static struct file_operations  ft5x06_fops=
{
	.open			=ft5x06_open,
	.unlocked_ioctl	=ft5x06_ioctl,
	.poll			=ft5x06_poll,
	.release		=ft5x06_release
};
static struct miscdevice ft5x06_misc=
{
	.minor=MISC_DYNAMIC_MINOR,
	.name="ft5x06",
	.fops=&ft5x06_fops
};

static int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret;
	i2c_client=client;
	printk("资源匹配成功addr=%#x,irq=%dn",client->addr,client->irq);
	/*初始化工作*/
	INIT_WORK(&ft5x06_work, ft5x06_work_func);
	/*注册中断*/
	ret=request_irq(client->irq,ft5x06_irq_work,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"ft5x06",NULL);
	if(ret)
	{
		printk("中断注册失败n");
		return ret;
	}
	/*注册杂项设备*/
	misc_register(&ft5x06_misc);
	return 0;
}
static int eeprom_remove(struct i2c_client *client)
{
	printk("eeprom 驱动层资源释放成功n");
	misc_deregister(&ft5x06_misc);
	free_irq(client->irq,NULL);
	return 0;
}
static struct i2c_device_id id_table[]=
{
		{"ft5x06",0},
		{}
};

static struct i2c_driver ft5x06_driver=
{
	.probe=ft5x06_probe,
	.remove=eeprom_remove,
	.driver=
	{
		.name="ft5x06_drv",
	},
	.id_table=id_table,
};
static int __init wbyq_eeprom_drv_init(void)
{
	int ret;
	ret=i2c_add_driver(&ft5x06_driver);
	if(ret)return ret;
	printk("ft5x06驱动层注册成功n");
    return 0;
}
/*驱动释放*/
static void __exit wbyq_eeprom_drv_cleanup(void)
{
	/*注销设备层*/
	i2c_del_driver(&ft5x06_driver);
	printk("ft5x06驱动层注销成功n");

}
module_init(wbyq_eeprom_drv_init);//驱动入口函数
module_exit(wbyq_eeprom_drv_cleanup);//驱动出口函数

MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 platform_drv Driver");

3.1.5 编写应用层函数

  通过帧缓冲框架完成LCD应用编程,实现画点函数。打开触摸屏驱动,获取触摸屏坐标,调用画点函数,绘制实时坐标位置。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define TOUCH_GETXY 0X80
struct Touch_xy
{
	int x;
	int y;
	int stat;
};
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;

/*LCD画点函数*/
static unsigned char *lcd_p=NULL;//屏幕缓存地址
static struct fb_fix_screeninfo fb_fix;//固定参数结构体
static struct fb_var_screeninfo fb_var;//可变参数结构体
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2,u32 c);
static inline void LCD_DrawPoint(int x,int y,int c)
{
	//获取要绘制的点的地址
	unsigned int *p= (unsigned int *)(lcd_p+y*fb_fix.line_length+x*fb_var.bits_per_pixel/8);
	*p=c;//写入颜色值
}

int main()
{
	/*1.打开设备*/
	int fd=open("/dev/fb0", 2);
	if(fd<0)
	{
		printf("打开设备失败n");
	}
	/*2.获取固定参数*/
	memset(&fb_fix,0, sizeof(fb_fix));
 	ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix);
	printf("屏幕缓存大小:%dn",fb_fix.smem_len);
	printf("一行的字节数:%dn",fb_fix.line_length);
	/*3.获取屏幕可变参数*/
	memset(&fb_var,0, sizeof(fb_var));
	ioctl(fd,FBIOGET_VSCREENINFO,&fb_var);
	printf("屏幕尺寸:%d*%dn",fb_var.xres,fb_var.yres);
	printf("颜色位数:%dn",fb_var.bits_per_pixel);
	/*4.将屏幕缓冲区映射到进程空间*/
	lcd_p=mmap(NULL,fb_fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	close(fd);
	if(lcd_p==(void *)-1)
	{
		printf("内存映射失败n");
		return 0;
	}
	memset(lcd_p,0xff,fb_fix.smem_len);//将屏幕清空为白色
	
	fd=open("/dev/ft5x06",2);
	if(fd<0)
	{
		printf("设备打开失败n");
		return 0;
	}
	struct pollfd fds=
	{
		.fd=fd,
		.events=POLLIN,
		
	};
	int ret;
	struct Touch_xy touchxy;
	int x1,y1;
	while(1)
	{
		ret=poll(&fds,1,-1);
		ioctl(fd,TOUCH_GETXY,&touchxy);
		if((x1-touchxy.x >=25|| touchxy.x-x1>=25) || (y1-touchxy.y >=25|| touchxy.y-y1>=25))
		{
			x1=touchxy.x;
			y1=touchxy.y;
		}
		if(x1!=touchxy.x || touchxy.y!=y1)
		{
			LCD_DrawLine(touchxy.x, touchxy.y, x1, y1,0);
			x1=touchxy.x;
			y1=touchxy.y;
		}		
	}	
AA:	
	//取消映射
	munmap(lcd_p,fb_fix.smem_len);
	return 0;
}
/*
函数功能:画直线
参    数:
x1,y1:起点坐标
x2,y2:终点坐标 
*/
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2,u32 c)
{
	u16 t; 
	int xerr=0,yerr=0,delta_x,delta_y,distance; 
	int incx,incy,uRow,uCol; 
	delta_x=x2-x1; //计算坐标增量 
	delta_y=y2-y1; 
	uRow=x1; 
	uCol=y1; 
	if(delta_x>0)incx=1; //设置单步方向 
	else if(delta_x==0)incx=0;//垂直线 
	else {incx=-1;delta_x=-delta_x;} 
	if(delta_y>0)incy=1; 
	else if(delta_y==0)incy=0;//水平线 
	else{incy=-1;delta_y=-delta_y;} 
	if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
	else distance=delta_y; 
	for(t=0;t<=distance+1;t++ )//画线输出 
	{  
		LCD_DrawPoint	(uRow,uCol,c);//画点
		xerr+=delta_x ; 
		yerr+=delta_y ; 
		if(xerr>distance) 
		{ 
			xerr-=distance; 
			uRow+=incx; 
		} 
		if(yerr>distance) 
		{ 
			yerr-=distance; 
			uCol+=incy; 
		} 
	}  
}  

poYBAGMtpqSAMNGnAAjfVKBOLu0369.png#pic_center

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 触摸屏
    +关注

    关注

    42

    文章

    2289

    浏览量

    115912
  • Linux
    +关注

    关注

    87

    文章

    11217

    浏览量

    208818
  • IIC
    IIC
    +关注

    关注

    11

    文章

    298

    浏览量

    38259
收藏 人收藏

    评论

    相关推荐

    ARM触摸屏驱动系统设计

    介绍了基于飞思卡尔芯片i.MX27 和嵌入式linux 系统触摸屏硬件的连接设计和软件的驱动设计.
    发表于 05-02 10:56 2721次阅读

    关于Linux设备驱动中input子系统的介绍

    对于输入类设备如键盘、鼠标、触摸屏之类的Linux驱动,内核提供input子系统,使得这类设备的处理变得非常便捷。总体上来讲,input子系统
    发表于 01-09 16:06 2684次阅读

    技术干货:WinCE 7.0触摸屏驱动

    在嵌入式系统中较为常用的是四线电阻式触摸屏,通过检测x轴和y轴的电压,来确定触点的位置。一般触摸屏系统结构为:触摸屏->
    发表于 12-09 13:43 3805次阅读

    Linux输入子系统上报触摸屏坐标

      在 Linux 中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入
    的头像 发表于 09-25 08:56 2390次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>下</b>输入<b class='flag-5'>子系统</b>上报<b class='flag-5'>触摸屏</b>坐标

    Linux驱动分析之input子系统

    Linux内核为了能够处理各种不同类型的输入设备,比如: 触摸屏 ,鼠标 , 键盘 , 操纵杆等设备 ,设计并实现了Linux 输入子系统 ,它为
    发表于 02-01 10:38 515次阅读

    基于触摸屏驱动Linux内核输入子系统研究

    基于触摸屏驱动Linux 内核输入子系统研究华明, 徐造林( 东南大学 计算机科学与工程学院, 江苏 南京 210 096)摘要: Li nux 是目前最为优秀的开源
    发表于 03-20 16:04

    嵌入式Linux触摸屏在漆包线检测系统中的应用

    本文针对嵌入式Linux触摸屏在漆包线检测系统中的应用,介绍了本系统触摸屏的具体接口电路,嵌入式Lin
    发表于 07-27 15:47 29次下载

    基于MeeGo的电容式触摸屏驱动设计

    本文基于Nokia和Intel公司合作开发的开源操作系统MeeGo,采用基于内核对象的Linux输入子系统来设计触摸屏驱动。该方案极大地方
    发表于 05-25 10:55 1364次阅读
    基于MeeGo的电容式<b class='flag-5'>触摸屏</b><b class='flag-5'>驱动</b>设计

    基于有限状态机的Linux多点触摸屏驱动设计刘斌

    基于有限状态机的Linux多点触摸屏驱动设计_刘斌
    发表于 03-15 08:00 0次下载

    基于嵌入式linux系统的AD7873触摸屏驱动系统设计详解

    Linux 和飞思卡尔i.MX27芯片以及AD7873 触摸屏芯片的驱动程序,并成功移植到内核中,实现了家庭控制器系统触摸技术
    发表于 09-18 16:57 1667次阅读
    基于嵌入式<b class='flag-5'>linux</b><b class='flag-5'>系统</b><b class='flag-5'>下</b>的AD7873<b class='flag-5'>触摸屏</b><b class='flag-5'>驱动</b><b class='flag-5'>系统</b>设计详解

    Linux触摸屏驱动

    对于触摸屏驱动,我们主要需要掌握触摸屏驱动代码和应用层测试代码。下面讲的是基于Mini2440的触摸屏
    发表于 04-26 14:45 2503次阅读

    Android的触摸屏进行校准的方法详细说明

    本文记录了的Android触摸屏驱动的过程 我采用的触摸屏校准方法利用到了tslib-1.4。tslib的移植方法,网上有很多介绍,这里就不赘述了。 有过linux
    发表于 08-12 17:33 1次下载
    Android的<b class='flag-5'>触摸屏</b>进行校准的方法详细说明

    英创信息技术EM9280 Linux触摸屏应用开发简介

    提供对于4线制电阻式触摸屏的支持,在定制的Linux内核中已完全实现了该触摸屏驱动支持。 在EM9280中,触摸屏作为输入设备其设备文件为
    的头像 发表于 01-16 09:39 1876次阅读
    英创信息技术EM9280 <b class='flag-5'>Linux</b><b class='flag-5'>触摸屏</b>应用开发简介

    AD7877输入触摸屏控制器Linux驱动

    AD7877输入触摸屏控制器Linux驱动
    发表于 04-20 14:25 2次下载
    AD7877输入<b class='flag-5'>触摸屏</b>控制器<b class='flag-5'>Linux</b><b class='flag-5'>驱动</b>

    Linux驱动开发-编写FT5X06触摸屏驱动

    这篇文章介绍在Linux如何编写FT5X06系列芯片驱动,完成触摸屏驱动开发, FT5X06是一个系列,当前使用的具体型号是FT5206
    的头像 发表于 09-17 15:27 5787次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>驱动</b>开发-编写FT5X06<b class='flag-5'>触摸屏</b><b class='flag-5'>驱动</b>