天天看點

tiny4412的I2C驅動實作案例(基于MMA7660)自己寫的,親測有效

1.配置I2C_client結構體的從機位址和名字

非裝置樹配置:

a.在arch/arm/mach-exynos/ 下打開mach-tiny4412.c

b.在其中仿照其他結構體添加裝置名字與從機位址:如下圖,代表我的裝置名字是mma7660,從機位址為:0X4C。

static struct i2c_board_info smdk4x12_i2c_devs3[] __initdata = {
	{
		I2C_BOARD_INFO("mma7660", 0x4c),
	},
};
           

c.然後繼續在這個mach-tiny4412.c檔案下找到 smdk4x12_machine_init 這個函數,仿照原有的其他裝置在其中添加:

i2c_register_board_info(2, smdk4x12_i2c_devs3,
			ARRAY_SIZE(smdk4x12_i2c_devs3)); 
//其中i2c_register_board_info這個函數為向I2c_adapter建構的I2C_client添加從機位址與名字,
名字用來和之後的I2C_driver中的做比對,
第一個參數為上一步驟所填寫的結構體數組名字,第二個參數為該對象的大小
           

d.編譯核心:make zImage -j8

MMA7660所需寄存器以及配置

MMA7660的寄存器可以參考其資料手冊,我是仿照開發闆商在核心中提供的mma7660.c裡面的寄存器配置實作的寄存器和功能配置如下:

#define MMA7660_XOUT 0x00 // 6位輸出值X,從該寄存器中讀取X值
#define MMA7660_YOUT 0x01 // 6位輸出值Y, Y值
#define MMA7660_ZOUT 0x02 // 6位輸出值Z,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 // 利用防反跳計數

           

一些配置的典型值:

struct register_info_data reg[7] = {
	{MMA7660_MODE, 0x00},
 	{MMA7660_SR, ((2<<5) | (1<<3) | 1)},
	{MMA7660_SPCNT, 0xA0}, 
	{MMA7660_PDET, 4}, 
	{MMA7660_PD, 15},
  	{MMA7660_INTSU, 0xe7}, 
  	{MMA7660_MODE, 0x59}
  };
           

按照上述寄存器配置可完成等待态的寄存器讀取,可參考核心中的mma7660.c和mma7660.h檔案檢視具體資訊。

代碼如下:

mma7660驅動代碼:mma7660_drv.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/uaccess.h>

#include "mma7660wxbiao.h"


struct register_info_data{
	char buf[2];
};
struct mma7660_desc{
	int major;
	struct device *dev;
	struct class *cls;
	struct i2c_client *cli;
};
struct mma7660_desc *mma7660_dev;
int mma_read_reg_bytes(const struct i2c_client * client, char reg);


ssize_t mma7660_read (struct file *file, char __user *usr, size_t count, loff_t *lof)
{
	return 0;
}
ssize_t mma7660_write(struct file *file, const char __user *usr, size_t count, loff_t *lof)
{
	return 0;
}

int mma7660_open(struct inode *inode, struct file *file)
{
	return 0;
}
int mma7660_close (struct inode *inode, struct file *file)
{
	return 0;
}

long mma7660_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct mma7660_data data;
	int ret = 0;
	
	switch(cmd){
		case MMA7660_X:
			data.x = mma_read_reg_bytes(mma7660_dev->cli, MMA7660_XOUT);
			break;
		case MMA7660_Y:
			data.y = mma_read_reg_bytes(mma7660_dev->cli, MMA7660_YOUT);
			break;
		case MMA7660_Z:
			data.z = mma_read_reg_bytes(mma7660_dev->cli, MMA7660_ZOUT);
			break;
		default:
			printk("please MMA7660_X or MMA7660_Y or MMA7660_Z");
			return -1;
	}

	printk("ioctl success \n");
	ret = copy_to_user((void __user *) arg, &data, sizeof(data));
	if(ret > 0){
		printk("fail to copy_to_user\n");
		return -1;
	}

	return 0;
}

static struct file_operations fops = {
	.open = mma7660_open,
	.read = mma7660_read,
	.write = mma7660_write,
	.release = mma7660_close,
	.unlocked_ioctl = mma7660_ioctl,
};

int mma_write_bytes(const struct i2c_client * client, const char * buf, int count)
{
	int ret;
	struct i2c_adapter *adapt = client->adapter;
	struct i2c_msg msg;

	msg.addr = client->addr;
	msg.buf = (char *)buf;
	msg.flags = 0;
	msg.len = count;
	ret = i2c_transfer(adapt , &msg, 1);

	if(ret == 1){
		printk("success write\n");
	}
	return (ret == 1) ? count : ret;
}


/*
	讀取XYZ所對應的寄存器中的數值(I2C先發送寄存器位址,再進行讀數值)
*/
int mma_read_reg_bytes(const struct i2c_client * client, char reg)
{
	printk("-----------%s------------\n", __FUNCTION__);
	int ret;
	struct i2c_adapter *adapt = client->adapter;
	struct i2c_msg msg[2];
	int rxbuffer[1] = {0}; 	//注意:必須使用數組名來得到讀到的資料
							//如果直接定義int buff = 0, 或者 int buff;則調用transfer函數傳參&buff時,
							//該位址就位NULL指針或者野指針,會導緻核心崩潰。

	printk("client->adapt = %p\n", adapt);
	msg[0].addr = client->addr;
	msg[0].buf = &reg;
	msg[0].flags = 0;
	msg[0].len = 1;

	msg[1].addr = client->addr;
	msg[1].buf = (char *)rxbuffer;
	msg[1].flags = I2C_M_RD;
	msg[1].len = 4;
	
	ret = i2c_transfer(adapt , msg, 2);
	printk("rxbuffer[0] = %d\n", rxbuffer[0]);
	printk("ret = %d\n", ret);
	if(ret != 2){
		printk("fail to read_reg_bytes\n");
		return ret;
	}
	
	return rxbuffer[0];
}

void mma7660_init_reg(void)
{
	struct register_info_data reg[7] = {{MMA7660_MODE, 0x00}, {MMA7660_SR, ((2<<5) | (1<<3) | 1)}, {MMA7660_SPCNT, 0xA0}, {MMA7660_PDET, 4}, {MMA7660_PD, 15}, {MMA7660_INTSU, 0xe7}, {MMA7660_MODE, 0x59}};
	int i = 0;
	int ret = 0;

	for(i = 0; i < sizeof(reg) / sizeof(reg[0]); i++)
	{
		ret = mma_write_bytes(mma7660_dev->cli, reg[i].buf, 2);
		if(ret != 2){
			printk("127:fail to mma_write_bytes\n");
			return;
		}
		if(i == 0 || i == 6){
			mdelay(10);
		}
	}
}

int mma7600_probe(struct i2c_client *client, const struct i2c_device_id *idt)
{
	/*
	1.申請裝置号
	2.實作fops操作方法集合
	3.通過i2c接口去初始化i2c裝置
	*/
	printk("-----------%s--------------\n", __FUNCTION__);
	mma7660_dev = kzalloc(sizeof(mma7660_dev), GFP_KERNEL);
	if(NULL == mma7660_dev){
		printk("fail to kzalloc\n");
		return -1;
	}	
	mma7660_dev->cli = client;
	mma7660_dev->major = register_chrdev(0, "mma7600", &fops);
	if(mma7660_dev->major < 0){
		printk("fail to register_chrdev\n");
		goto err1;
	}
	mma7660_dev->cls = class_create(THIS_MODULE, "mmacls");
	mma7660_dev->dev = device_create(mma7660_dev->cls, NULL, MKDEV(mma7660_dev->major, 0), NULL, "mma_sensor");

	mma7660_init_reg();
	return 0;
err1:
	kfree(mma7660_dev);
	return -1;
}


int mma7600_remove(struct i2c_client *cleint)
{
	device_destroy(mma7660_dev->cls, MKDEV(mma7660_dev->major, 0));
	class_destroy(mma7660_dev->cls);
	unregister_chrdev(mma7660_dev->major, "mma7660");
	kfree(mma7660_dev);

	return 0;
}

static struct i2c_device_id mma7660_id[2] = {
	[0] = {
		.name = "mma7660",
		.driver_data = 0,
	},
	[1] = {
		.name = "mpu6050",
		.driver_data = 0,
	},
};


static struct i2c_driver mma7660_drv = {
	.probe = mma7600_probe,
	.remove = mma7600_remove,
	.id_table = mma7660_id, //記錄該驅動服務于哪些裝置
	.driver = {
		.name = "mma7600",
	},
};

static int __init mma7660_init(void)
{
	return i2c_add_driver(&mma7660_drv); //向I2C總線中添加driver結構體,其中屬性id_table數組中
											//mma7660_id數組任意元素的名字隻要和最開始在mach-tiny4412.c中添加結構體的裝置
											//名稱相同,即可調用到I2C_driver結構體中的proble方法。
}

static void __exit mma7660_exit(void)
{
	i2c_del_driver(&mma7660_drv);
}

module_init(mma7660_init);
module_exit(mma7660_exit);
MODULE_LICENSE("GPL");
           

測試程式:mma7660_test.c

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

int main(void)
{
	int fd = 0;
	int ret = 0;
	struct mma7660_data data;

	fd = open("/dev/mma_sensor", O_RDWR);
	if(-1 == fd){
		perror("open");
		return -1;
	}
	while(1){
		ret = ioctl(fd, MMA7660_X, &data);
		if(ret < 0){
			perror("fail to ioctl\n");
		}
		ret = ioctl(fd, MMA7660_Y, &data);
		if(ret < 0){
			perror("fail to ioctl\n");
		}
		ret = ioctl(fd, MMA7660_Z, &data);
		if(ret < 0){
			perror("fail to ioctl\n");
		}
		printf("gsensor_x: %d\n", data.x);
		printf("gsensor_y: %d\n", data.y);
		printf("gsensor_z: %d\n", data.z);
		sleep(1);
	}
	close(fd);
	return 0;
}

           

Makefile

obj-m +=mma7660_drv.o

TAPP=mma7660_test

LMAKE=/home/linux/nfsroot/yingjian/neiheyuan/linux-3.5  //核心源碼主目錄路徑


module:
	make -C $(LMAKE) M=$(shell pwd) modules
	arm-linux-gcc $(TAPP).c -o $(TAPP)
.PHYON:
clean:
	rm *.mod.c *.o *.ko *.order *.symvers
install:
	cp ./*.ko /home/linux/nfsroot/nfs/home/neoway/i2c_1/
	cp ./$(TAPP) /home/linux/nfsroot/nfs/home/neoway/i2c_1/
           

上述代碼會根據開發闆的移動在序列槽工具中斷顯示出不同的X,Y,Z的數值,為應用程式提供了ioctl函數結構,根據不同的幻數與鍵值可查詢X,Y,Z的數值。