第八讲 Linux I2C总线及mma8653驱动编写实例

I2C总线

1.1 I2C总线电气连接

  • 通信总线的方式:

    • 同步/异步
    • 全双工/半双工/单工
    • 串行/并行
  • I2C总线:是同步、半双工、串行的总线,可以用来连接存储器(EEPROM、FLASH)、A/D、D/A
    转换器、LCD驱动器、传感器等等。它支持多主多从的模式(一般情况使用的是一主多从)。

  • 电气结构:

    image-20220402234335787

1.2、I2C时序

  • 起始信号:时钟线高电平期间,数据线产生负跳变

  • 停止信号:时钟线高电平期间,数据线产生正跳变

  • 写数据时机:时钟周期的电平期间;

  • 读数据时间:时钟周期的电平期间;

    image-20220402213402110

  • 接收器收到每个字节后的第9个时钟周期会发送一个应答信号(ACK)或非应答信号(NACK)

    • ACK应答:信息接收者将数据线设置为低电平
    • NACK应答:信息接收者将数据线设置为高电平

    image-20220402213130338

    image-20220402234648460

1.3、I2C协议

  • **设备地址:**每个支持i2c总线的设备,它都会有一个可以代表自己的地址。这个地址是唯一的,用7位或10位来表示,在出厂时已经确定固化。
  • I2C数据传输办法:I2C为电平触发方式(数据先发高位,再发低位)SDA线上的数据必须在SCL的高电平周期保持稳定。SDA线的电平状态在SCL为低电平周期才可以改变。
  • 工作模式
    • 标准模式:位速率100kbit/s。
    • 快速模式:位速率可达400kbit/s,向下兼容。
    • 高速模式:位速率可达3.4Mbit/s,向下兼容。

1.3.1、单字节读

image-20220402214351354

1.3.2、多字节读

image-20220402215622789

image-20220402215648421

1.3.3、单字节写

image-20220402220005242

1.3.4、多字节写

image-20220402220322879

1.4、模拟时序协议通信

很多时候我们可以通过IO口自行模拟I2C时序而且方便灵活、易于使用。参考前面IIC时序分别模拟IIC起始信号、终止信号、应答信和数据传输。

在SCL为高电平时SDA线上不能有电平的改变,只能在SCL低电平期间改变数据。

image-20220402235451968

1.4.1、时序相关

  1. 起始信号
void start_signal(void)
{
	SDA_OUT;
	SDA_HIGHT;
	SCL_HIGHT;
	I2C_DELAY;
	SDA_LOW;
	I2C_DELAY;
	SCL_LOW;	/*这里的时钟变低是为了下次连续传输*/
}
  1. 结束信号
void stop_signal(void)
{
	SDA_OUT;
	SCL_HIGHT;
	SDA_LOW;
	I2C_DELAY;
	SDA_HIGHT;
	I2C_DELAY;
	SCL_LOW; 	/*这里的时钟变低是为了下次连续传输*/
}
  1. 发送应答/非应答信号
/* ack = 0应答信号,ack = 1非应答信号*/

void send_ack(u8 ack)
{
	SDA_OUT;
	SCL_LOW;
	if(ack)
		SDA_HIGHT;
	else
		SDA_LOW;
	I2C_DELAY;
	SCL_HIGHT;
	I2C_DELAY;
	SCL_LOW;
}
  1. 判断是否是应答信号
u8 is_recv_ack(void)
{
	u8 ack=0;
	SDA_IN;
	SCL_LOW;
	I2C_DELAY;
	SCL_HIGHT;
	I2C_DELAY;
	ack = SDA_GET_VAL; //SCL为高电平时采样SDA线
	SCL_LOW;
	return !ack;	//ack返回真,nack返回假
}
  1. 接收一个byte
u8 i2c_recv_byte(void)
{
	int i;
	u8 val = 0;
	SDA_IN;
	SCL_LOW;				/*让发送方准备数据*/
	for(i=7; i>=0; i--){
		I2C_DELAY;			/*准备一段时间*/
		SCL_HIGHT;			/*准备读取数据*/
		I2C_DELAY;			/*等待数据稳定*/
		val |= (SDA_GET_VAL<<i);	/* MSB */
		SCL_LOW;			/*发送方准备下一位数据*/
	}
	return val;
}
  1. 发送一个byte
void i2c_send_byte(u8 val)
{
	int i;
	SDA_OUT;
	SCL_LOW;			/*低电平准备数据(写),高电平取数据(读)*/
	for(i=7; i>=0; i--){
		if(val & (1<<i))
			SDA_HIGHT;
		else
			SDA_LOW;
		I2C_DELAY;		/*待数据稳定*/
		SCL_HIGHT;		/*开始获取数据*/
		I2C_DELAY;		/*等待发送数据完毕*/
		SCL_LOW;		/*置低准备下一轮数据*/
	}
}

1.4.2 IIC应用实例——mma8653

  • mma8653是一款三轴重力加速度传感器,能够感知到加速度的变化,比如晃动、跌落、上升、下降等各种移动变化都能被mma8653转化为电信号,用户直接从寄存器读取坐标即可。

  • mma8653可以测量配置+/-2g +/-4g +/-8g范围的加速度,同时也可测量温度。

  • mma8653提供给用户i2c接口。

  • 电源电压范围 : 2.0 V ~ 3.6 V

    image-20220402235740717

    • mma8653操作流程:

      • 芯片在上电后需要配置CTRL_REG1(0x2A)为模式ACTIVE,此时芯片就可以正常工作
      • 检测chid_id是否正确(0Dh)
      • 读取坐标信息(x、y、z/01h-06h)
      • 用户需要进行简单的初始化才能读取数据得到坐标值,如果想要特殊功能,还需要再配
        置相应寄存器。
    • mma8653寄存器地址

      image-20220403000013186

1.4.3协议相关

  1. 接受传感器数据
u8 mma8653_read_reg(u8 addr)
{
	u8 val = 0;
	start_signal();
	i2c_send_byte((I2C_DEVICE_ADDR<<1)|I2C_WRITE_MODE);
	if(!is_recv_ack()){
		printk(KERN_ERR"is_recv_ack failed...\n");
    	return;
    }
    i2c_send_byte(addr);	/* 若有应答,发送addr(deivce address) */
	if(!is_recv_ack()){
		printk(KERN_ERR"is_recv_ack failed...\n");
        return;
    }
	start_signal();
	i2c_send_byte((I2C_DEVICE_ADDR<<1)|I2C_READ_MODE);
	if(!is_recv_ack()){
		printk(KERN_ERR"is_recv_ack failed...\n");
        return;
    }
	val = i2c_recv_byte();
	send_ack(1);	//发送非应答信号
	stop_signal();
	return val;
}
  1. 发送数据给传感器
void mma8653_write_reg(u8 addr,u8 val)
{
	start_signal();
	i2c_send_byte((I2C_DEVICE_ADDR<<1)|I2C_WRITE_MODE);
	if(!is_recv_ack()){
		printk(KERN_ERR"is_recv_ack failed...\n");
    	return;
    }
	i2c_send_byte(addr);	/* deivce address */
	if(!is_recv_ack()){
		printk(KERN_ERR"is_recv_ack failed...\n");
    	return;
    }
	i2c_send_byte(val);		/* send a byte */
	if(!is_recv_ack()){
		printk(KERN_ERR"is_recv_ack failed...\n");
    	return;
    }
	stop_signa();
  1. 驱动代码实例(x6818 plat — 加速度传感器mma8653的驱动):
/*********************************************************************
 *env:x6818 plat --- mma8653
 ********************************************************************/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include "mma8653.h"


/*---macro define---*/
#define NAME			 "s5p6818_mma8653"
#define DEVCOUNT 4

#define I2C_DEVICE_ADDR	 0x1d

#define I2C_WRITE_MODE	 	0
#define I2C_READ_MODE 	 	1

#define WHO_AM_I  		0x0D
#define CTRL_REG1		0x2A

#define OUT_X_MSB		0x01
#define OUT_X_LSB		0x02

#define OUT_Y_MSB		0x03
#define OUT_Y_LSB		0x04

#define OUT_Z_MSB		0x05
#define OUT_Z_LSB		0x06

#define MMA_SDA		(32*3 + 7)
#define MMA_SCL		(32*3 + 6)
	
#define SDA_IN		gpio_direction_input(MMA_SDA)
#define SDA_OUT		gpio_direction_output(MMA_SDA,1)
#define SCL_OUT 	gpio_direction_output(MMA_SCL,1)

#define SDA_HIGHT	gpio_set_value(MMA_SDA,1)
#define SDA_LOW		gpio_set_value(MMA_SDA,0)
#define SDA_GET_VAL	gpio_get_value(MMA_SDA)

#define SCL_HIGHT	gpio_set_value(MMA_SCL,1)
#define SCL_LOW		gpio_set_value(MMA_SCL,0)

#define I2C_DELAY	udelay(80)


#define Debug_log(level) \
	printk(level "---%s---%s---%d---\n",__FILE__,__func__,__LINE__)

/*---macro define end---*/

/* Functions Statement */
static int demo_open(struct inode *, struct file *);
static int demo_release(struct inode *, struct file *);
static long demo_ioctl(struct file *, unsigned int, unsigned long);
/* Functions Statement End */


/* DataType Define */

typedef struct chrdev_demo{
	int major;
	int dev_code;
	struct class *cls;
	struct device *devp;
	struct file_operations fops;
	struct mma_data xyz;
}chrdev_demo_t;

/* DataType Define End */


/* chrdev structure */
chrdev_demo_t mychrdev = {
	.major = 0,
	.fops = {
		.open    = demo_open,
		.release = demo_release,
		.unlocked_ioctl = demo_ioctl,
	},
};

struct gpio s5p6818_mma8653[] = {
		[0] = {
			.gpio  = MMA_SDA,
			.flags = GPIOF_OUT_INIT_HIGH,
			.label = "mma8653_sda",
		},
		[1] = {
			.gpio  = MMA_SCL,
			.flags = GPIOF_OUT_INIT_HIGH,
			.label = "mma8653_scl",
		},
};

/*------------------------------i2c timing --------------------------------*/

void start_signal(void)
{
	SDA_OUT;
	
	SDA_HIGHT;
	SCL_HIGHT;
	I2C_DELAY;
	SDA_LOW;
	I2C_DELAY;
	SCL_LOW;

}
void stop_signal(void)
{
	SDA_OUT;

	SCL_HIGHT;
	SDA_LOW;
	I2C_DELAY;
	SDA_HIGHT;
	I2C_DELAY;
	SCL_LOW;		/* 这里的时钟变低是为了下次连续传输 */
}



u8 i2c_recv_byte(void)
{	
	int i;
	u8 val = 0;

	SDA_IN;
	
	SCL_LOW;				/* 让发送方准备数据 */
	for(i=7;i>=0;i--)
	{
		I2C_DELAY;			/* 准备一段时间 */
		SCL_HIGHT;			/* 准备读取数据 */
		I2C_DELAY;		    /* 等待数据稳定 */
		val |= (SDA_GET_VAL<<i);
		SCL_LOW;			/* 发送方准备下一位数据 */
	}
	return val;
}

void i2c_send_byte(u8 val)
{
	int i;

	SDA_OUT;
	
	SCL_LOW;/* 低电平准备数据,高电平取数据 */
	for(i=7;i>=0;i--)
	{
		if(val&(1<<i))
			SDA_HIGHT;
		else
			SDA_LOW;
		I2C_DELAY;			/* 待数据稳定 */
		SCL_HIGHT;			/* 开始获取数据 */
		I2C_DELAY;			/* 等待获取完数据 */
		SCL_LOW;			/* 置低准备下一轮数据 */
	}
}


u8 is_recv_ack(void)
{	
	u8 ack = 0;

	SDA_IN;

	SCL_LOW;
	I2C_DELAY;	
	SCL_HIGHT;			
	I2C_DELAY;
	ack = SDA_GET_VAL;
	SCL_LOW;
	return !ack;
}
void send_ack(u8 ack)
{
	SDA_OUT;

	SCL_LOW;
	if(ack)
		SDA_HIGHT;
	else
		SDA_LOW;
	I2C_DELAY;
	SCL_HIGHT;
	I2C_DELAY;
	SCL_LOW;
}


u8 mma8653_read_reg(u8 addr)
{
	u8 val = 0;
	start_signal();
	i2c_send_byte((I2C_DEVICE_ADDR<<1)|I2C_WRITE_MODE);
	if(!is_recv_ack())
		printk(KERN_ERR "is_recv_ack failed...\n");
	i2c_send_byte(addr);								/* deivce address */
	if(!is_recv_ack())
		printk(KERN_ERR "is_recv_ack failed...\n");
		
	start_signal();
	i2c_send_byte((I2C_DEVICE_ADDR<<1)|I2C_READ_MODE);
	if(!is_recv_ack())
		printk(KERN_ERR "is_recv_ack failed...\n");

	val = i2c_recv_byte();
	
	send_ack(1);

	stop_signal();
	return val;
}

void mma8653_write_reg(u8 addr,u8 val)
{
	
	start_signal();
	
	i2c_send_byte((I2C_DEVICE_ADDR<<1)|I2C_WRITE_MODE);
	if(!is_recv_ack())
		printk(KERN_ERR "is_recv_ack failed...\n");
	i2c_send_byte(addr);					/* deivce address */
	if(!is_recv_ack())
		printk(KERN_ERR "is_recv_ack failed...\n");

		
	i2c_send_byte(val);	/* send data */
	if(!is_recv_ack())
		printk(KERN_ERR "is_recv_ack failed...\n");

	stop_signal();
}


void mma8653_init(void)
{
	u8 val ;
	val = mma8653_read_reg(CTRL_REG1);
	mma8653_write_reg(CTRL_REG1,val | (0x1<<0));
}


void mma8653_read_xyz_data(struct mma_data *data)
{
	short x , y , z ;

	x = ((mma8653_read_reg(OUT_X_MSB)<<8)|mma8653_read_reg(OUT_X_LSB))>>6;
	y = ((mma8653_read_reg(OUT_Y_MSB)<<8)|mma8653_read_reg(OUT_Y_LSB))>>6;
	z = ((mma8653_read_reg(OUT_Z_MSB)<<8)|mma8653_read_reg(OUT_Z_LSB))>>6;
	data->x_data = x;
	data->y_data = y;
	data->z_data = z;

	printk("--x:%d--y:%d--z:%d--\n",x,y,z);
}

/*------------------------fileoperations functions-------------------------------*/

static int demo_open(struct inode *inode, struct file *filp)
{
	mma8653_init();
	Debug_log(KERN_INFO);
	return 0;
}


static long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{	
	switch(cmd){
		case MMA_READ_DATA:
							mma8653_read_xyz_data(&(mychrdev.xyz));
							if(copy_to_user((void *)args,&(mychrdev.xyz),sizeof(struct mma_data))){
								printk(KERN_ERR "copy_to_user failed...\n");
								return -EAGAIN;
							}
							break;
		default:break;
	}
	Debug_log(KERN_INFO);
	return 0;
}

	
static int demo_release(struct inode *inode, struct file *filp)
{	
	Debug_log(KERN_INFO);
	return 0;
}

/*----------------------fileoperations functions end----------------------------*/

static int __init demo_init(void)
{	
	int ret = 0 , i = 0 , chip_id;
	/* 1. register chrdev */
	mychrdev.major = register_chrdev(mychrdev.major,NAME, &(mychrdev.fops));
	if(0 > mychrdev.major){
		printk(KERN_ERR "register_chrdev failed...\n");
		ret = mychrdev.major;
		goto err0;
	}

	/* driver information  for debug */
	printk(KERN_INFO "---chrdev_demo major : %d---\n",mychrdev.major);

	/* 2. class create */
	mychrdev.cls = class_create(THIS_MODULE,NAME);
	if(IS_ERR(mychrdev.cls)){
		printk(KERN_ERR "class_create failed...\n");
		ret = PTR_ERR(mychrdev.cls);
		goto err1;
	}

	/* 3. device create */
	for(i = 0 ; i < DEVCOUNT; i++){
		mychrdev.devp = device_create(mychrdev.cls,NULL,MKDEV(mychrdev.major,i),\
					NULL,"%s%d",NAME,i);
		if(IS_ERR(mychrdev.devp)){
			printk(KERN_ERR "device_create %dth failed...\n",i);
			ret = PTR_ERR(mychrdev.devp);
			goto err2;
		}
	}
	gpio_free(MMA_SDA);
	gpio_free(MMA_SCL);
#if 1
	ret = gpio_request_array(s5p6818_mma8653, ARRAY_SIZE(s5p6818_mma8653));
	if(0 > ret){
		printk(KERN_ERR "gpio_request_array failed...\n");
		goto err2;
	}
#endif
	
	chip_id = mma8653_read_reg(WHO_AM_I);
	printk(KERN_INFO "---CHIP_ID:0x%x---\n",chip_id);
	
	Debug_log(KERN_INFO);
	return 0;

err2:
	for(--i;i >=0;i--){
		device_destroy(mychrdev.cls,MKDEV(mychrdev.major,i));
	}
	class_destroy(mychrdev.cls);
err1:
	unregister_chrdev(mychrdev.major,"NAME");
err0:
	return ret;
}

static void __exit demo_exit(void)
{	
	int i = 0;
	//gpio_free_array(s5p6818_mma8653, ARRAY_SIZE(s5p6818_mma8653));
	for(i = 0;i < DEVCOUNT;i++){
		device_destroy(mychrdev.cls,MKDEV(mychrdev.major,i));
	}
	class_destroy(mychrdev.cls);
	unregister_chrdev(mychrdev.major,"NAME");
	Debug_log(KERN_INFO);
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liangzc1124@163.com");
MODULE_DESCRIPTION("This is about hwo to create a module");
  1. 头文件:
#ifndef _MMA8653_H_
#define _MMA8653_H_

#define MMA_READ_DATA _IO('Z',1)

struct mma_data{
	short x_data;
	short y_data;
	short z_data;
};
#endif
  1. 测试代码实例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "mma8653.h"
#include <sys/ioctl.h>

struct mma_data data;

int main(int argc, const char *argv[])
{
	int fd ;
	fd = open(argv[1],O_RDWR);
	if(fd < 0){
		perror("open()");
		return -1;
	}

	while(1){
		ioctl(fd,MMA_READ_DATA,&data);
		printf("---x:%d , y: %d , z: %d ---\n",\
               data.x_data,data.y_data,data.z_data);
		sleep(1);
	}

	close(fd);
	return 0;
}
  1. 头文件:
#ifndef _MMA8653_H_
#define _MMA8653_H_

#define MMA_READ_DATA _IO('Z',1)

struct mma_data{
	short x_data;
	short y_data;
	short z_data;
};
#endif
  1. 测试代码实例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "mma8653.h"
#include <sys/ioctl.h>

struct mma_data data;

int main(int argc, const char *argv[])
{
	int fd ;
	fd = open(argv[1],O_RDWR);
	if(fd < 0){
		perror("open()");
		return -1;
	}

	while(1){
		ioctl(fd,MMA_READ_DATA,&data);
		printf("---x:%d , y: %d , z: %d ---\n",\
               data.x_data,data.y_data,data.z_data);
		sleep(1);
	}

	close(fd);
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leon_George

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值