Linux驱动开发_IIC子系统、编写EEPROM、MMA7660加速度计驱动
【摘要】 介绍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)