Linux驱动开发_IIC子系统、编写EEPROM、MMA7660加速度计驱动

举报
DS小龙哥 发表于 2022/04/29 00:01:23 2022/04/29
【摘要】 介绍Linux下IIC子系统编写EEPROM驱动、编写MMA7660飞思卡尔三轴加速度计驱动、完成XYZ轴的驱动编写。

任务1: 复习IIC子系统

IIC子系统分为3个部分: IIC适配器、IIC设备端、IIC驱动端。

IIC适配器: 提供IIC协议底层的读写函数,复制与硬件交互

IIC设备端: 根据总线编号获取适配器结构,注册IIC设备。

IIC驱动端: 使用IIC子系统提供的函数,与硬件进行交互。

触摸屏使用总线: 1号。

任务2: EEPROM驱动

2.1 EEPROM设备端

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/irq.h>
#include <linux/i2c.h>

static struct i2c_client *touch_client;

/*填充板级信息(设备信息)*/
static struct i2c_board_info touch_info=
{
	.type="tiny4412_eeprom",  /*该名称会与IIC驱动端进行匹配*/
	.addr=0x50, /*IIC设备地址*/
};

static int __init tiny4412_touch_dev_init(void) 
{
	/*根据总线编号获取适配器*/
	 struct i2c_adapter *adapter=i2c_get_adapter(0);
	 
	/*注册一个新的IIC设备*/
	touch_client=i2c_new_device(adapter,&touch_info);
	printk("EEPROM设备端注册成功!\n");
	return 0;
}

static void __exit tiny4412_touch_dev_exit(void) 
{
	/*注销IIC设备结构*/
	i2c_unregister_device(touch_client);
}

module_init(tiny4412_touch_dev_init);
module_exit(tiny4412_touch_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tiny4412 wbyq");

2.1 EEPROM驱动端

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/fcntl.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>

/*
24AA025E48芯片容量:  256字节 、一页的大小16个字节
*/
static int touch_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	u8 buff[10];
	printk("触摸屏驱动端匹配成功:0x%X\n",client->addr);

	/*IIC子系统标准的写函数*/
	printk("成功写入%d个字节.\n",i2c_smbus_write_i2c_block_data(client,0,10,"123456789")); 
	mdelay(10);
	
	/*IIC子系统标准的读函数*/
	printk("成功读取%d个字节\n",i2c_smbus_read_i2c_block_data(client,0,10,buff)); 

	printk("buff=%s\n",buff);
	return 0;
}

static int touch_remove(struct i2c_client *client)
{
	printk("EEPROM驱动端卸载成功!\n");
}

static struct i2c_device_id touch_id[]=
{
	{"tiny4412_eeprom",0},
	{}
};

static struct i2c_driver touch_driver=
{
	.probe=touch_probe,
	.remove=touch_remove,
	.driver=
		{
			.name="eeprom_drv",
		},
	.id_table=touch_id
};

static int __init tiny4412_touch_init(void) 
{
	/*1. 注册IIC子系统驱动端*/
	i2c_add_driver(&touch_driver);
	return 0;
}

static void __exit tiny4412_touch_exit(void) 
{
	/*2. 注销IIC子系统驱动端*/
	i2c_del_driver(&touch_driver);
}

module_init(tiny4412_touch_init);
module_exit(tiny4412_touch_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tiny4412 wbyq");

任务3: 三轴加速度计(MMA7660飞思卡尔)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/fcntl.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>

static struct i2c_client *mma7660_client=NULL;
static int last_tilt=0;

/* MMA7760 寄存器 */
#define MMA7660_XOUT			0x00	// 6-bit output value X
#define MMA7660_YOUT			0x01	// 6-bit output value Y
#define MMA7660_ZOUT			0x02	// 6-bit output value Z
#define MMA7660_TILT			0x03	// 倾斜状态
#define MMA7660_SRST			0x04	// 采样率状态
#define MMA7660_SPCNT			0x05	// 睡眠计数
#define MMA7660_INTSU			0x06	// 中断设置
#define MMA7660_MODE			0x07	// 模式
#define MMA7660_SR				0x08	// 自动唤醒/睡眠和去抖滤波器
#define MMA7660_PDET			0x09	// 点击检测
#define MMA7660_PD				0x0a	// 点按去抖计数

#define __need_retry(__v)	(__v & (1 << 6))
#define __is_negative(__v)	(__v & (1 << 5))

static const char *mma7660_bafro[] = {
	"未知", "前面", "背面"
};

static const char *mma7660_pola[] = {
	"未知",
	"左面", "向右",
	"保留", "保留",
	"向下", "向上",
	"保留",
};

static int mma7660_read_xyz(struct i2c_client *client, int idx, int *xyz)
{
	int val;
	do
	{
		val = i2c_smbus_read_byte_data(client, idx + MMA7660_XOUT);
	} while (__need_retry(val));

	*xyz = __is_negative(val) ? (val | ~0x3f) : (val & 0x3f);
	return 0;
}

/*
读取倾斜角:tilt
*/
static int mma7660_read_tilt(struct i2c_client *client, int *tilt)
{
	int val;
	do
	{
		  val=i2c_smbus_read_byte_data(client, MMA7660_TILT);
	}while (__need_retry(val));
	*tilt = (val & 0xff);
	return 0;
}

/*
初始化 initialize
*/
static int mma7660_initialize(struct i2c_client *client)
{
	int val;
	/* 使用测试模式探测芯片 */
	i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);
	mdelay(10);
	i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x04);
	mdelay(10);
	i2c_smbus_write_byte_data(client, MMA7660_XOUT, 0x3f);
	i2c_smbus_write_byte_data(client, MMA7660_YOUT, 0x01);
	i2c_smbus_write_byte_data(client, MMA7660_ZOUT, 0x15);
	i2c_smbus_read_byte_data(client, MMA7660_ZOUT);

	/* 转到待机(正常)模式进行配置 */
	i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);
	mdelay(10);

	/* 采样率:64Hz / 16Hz  */
	i2c_smbus_write_byte_data(client, MMA7660_SR, ((2<<5) | (1<<3) | 1));

	/* 睡眠计数*/
	i2c_smbus_write_byte_data(client, MMA7660_SPCNT, 0xA0);

	/* 点击检测并去抖~4ms */
	i2c_smbus_write_byte_data(client, MMA7660_PDET, 4);
	i2c_smbus_write_byte_data(client, MMA7660_PD, 15);

	/* 除退出自动睡眠外,启用中断 */
	i2c_smbus_write_byte_data(client, MMA7660_INTSU, 0xe7);

	/* IPP,自动唤醒,自动睡眠和待机*/
	i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x59);
	mdelay(10);

	/* 保存当前倾斜状态 */
	mma7660_read_tilt(client, &last_tilt);

	mma7660_client = client;
	return 0;
}



/*工作处理函数*/
static void tiny4412_work_func(struct work_struct *work)
{
	int bafro, pola, shake, tap;
	int val = 0;

	/*读取倾斜角度值*/
	mma7660_read_tilt(mma7660_client, &val);

	/*探测前面和背面*/
	bafro = val & 0x03;
	if (bafro != (last_tilt & 0x03)) {
		printk("%s\n", mma7660_bafro[bafro]);
	}

	pola = (val >> 2) & 0x07;
	if (pola != ((last_tilt >> 2) & 0x07)) {
		printk("%s\n", mma7660_pola[pola]);
	}

	shake = (val >> 5) & 0x01;
	if (shake && shake != ((last_tilt >> 5) & 0x01)) {
		printk("抖动\n");
	}

	tap = (val >> 7) & 0x01;
	if (tap && tap != ((last_tilt >> 7) & 0x01)) {
		printk("敲击(磕)\n");
	}
	
	/* 保存当前状态 */
	last_tilt = val;

	int axis[3];
	int i;
	for (i = 0; i < 3; i++) 
	{
		mma7660_read_xyz(mma7660_client, i, &axis[i]);
	}
	
	printk("xyz=%3d, %3d, %3d\n", axis[0], axis[1], axis[2]);
}

/*静态声明工作队列*/
static DECLARE_WORK(mma7660_wq,tiny4412_work_func);

/*触摸屏的中断服务函数*/
static irqreturn_t tiny4412_mma7660_handler(int irq, void *dev)
{
	schedule_work(&mma7660_wq);  /*将工作加入到一个工作队列里去*/
	return IRQ_HANDLED;
}

static int mma7660_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	u8 mma7660_id;
	printk("MMA7660驱动端匹配成功:0x%X\n",client->addr);

	/*1. 初始化MMA7660*/
	mma7660_initialize(client);

	/*2. 注册中断*/
	request_irq(client->irq,tiny4412_mma7660_handler,IRQF_TRIGGER_FALLING,client->name,NULL);
	return 0;
}

static int mma7660_remove(struct i2c_client *client)
{
	printk("mma7660驱动端卸载成功!\n");

	/*释放中断号*/
	free_irq(client->irq,NULL);
	return 0;
}

static struct i2c_device_id mma7660_id[]=
{
	{"tiny4412_mma7660",0},
	{}
};

static struct i2c_driver mma7660_driver=
{
	.probe=mma7660_probe,
	.remove=mma7660_remove,
	.driver=
		{
			.name="mma7660_drv",
		},
	.id_table=mma7660_id
};

static int __init tiny4412_mma7660_init(void) 
{
	/*1. 注册IIC子系统驱动端*/
	i2c_add_driver(&mma7660_driver);
	return 0;
}

static void __exit tiny4412_mma7660_exit(void) 
{
	/*2. 注销IIC子系统驱动端*/
	i2c_del_driver(&mma7660_driver);
}

module_init(tiny4412_mma7660_init);
module_exit(tiny4412_mma7660_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tiny4412 wbyq");

练习:

1.​ 触摸屏驱动独立写一次,学习IIC子系统注册流程。

2.​ 编写一次EEPROM驱动。

3.​ 按照厂家提供的驱动,编写MMA7660驱动。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。