I2C总线
1.1 I2C总线电气连接
-
通信总线的方式:
- 同步/异步
- 全双工/半双工/单工
- 串行/并行
-
I2C总线:是同步、半双工、串行的总线,可以用来连接存储器(EEPROM、FLASH)、A/D、D/A
转换器、LCD驱动器、传感器等等。它支持多主多从的模式(一般情况使用的是一主多从)。 -
电气结构:
1.2、I2C时序
-
起始信号:时钟线高电平期间,数据线产生负跳变
-
停止信号:时钟线高电平期间,数据线产生正跳变
-
写数据时机:时钟周期的低电平期间;
-
读数据时间:时钟周期的高电平期间;
-
接收器收到每个字节后的第9个时钟周期会发送一个应答信号(ACK)或非应答信号(NACK)
- ACK应答:信息接收者将数据线设置为低电平
- NACK应答:信息接收者将数据线设置为高电平
1.3、I2C协议
- **设备地址:**每个支持i2c总线的设备,它都会有一个可以代表自己的地址。这个地址是唯一的,用7位或10位来表示,在出厂时已经确定固化。
- I2C数据传输办法:I2C为电平触发方式(数据先发高位,再发低位)SDA线上的数据必须在SCL的高电平周期保持稳定。SDA线的电平状态在SCL为低电平周期才可以改变。
- 工作模式:
- 标准模式:位速率100kbit/s。
- 快速模式:位速率可达400kbit/s,向下兼容。
- 高速模式:位速率可达3.4Mbit/s,向下兼容。
1.3.1、单字节读
1.3.2、多字节读
1.3.3、单字节写
1.3.4、多字节写
1.4、模拟时序协议通信
很多时候我们可以通过IO口自行模拟I2C时序而且方便灵活、易于使用。参考前面IIC时序分别模拟IIC起始信号、终止信号、应答信和数据传输。
在SCL为高电平时SDA线上不能有电平的改变,只能在SCL低电平期间改变数据。
1.4.1、时序相关
- 起始信号
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; /*这里的时钟变低是为了下次连续传输*/
}
- 发送应答/非应答信号
/* 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;
}
- 判断是否是应答信号
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返回假
}
- 接收一个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;
}
- 发送一个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
-
mma8653操作流程:
- 芯片在上电后需要配置CTRL_REG1(0x2A)为模式ACTIVE,此时芯片就可以正常工作
- 检测chid_id是否正确(0Dh)
- 读取坐标信息(x、y、z/01h-06h)
- 用户需要进行简单的初始化才能读取数据得到坐标值,如果想要特殊功能,还需要再配
置相应寄存器。
-
mma8653寄存器地址
-
1.4.3协议相关
- 接受传感器数据
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;
}
- 发送数据给传感器
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();
- 驱动代码实例(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");
- 头文件:
#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
- 测试代码实例:
#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;
}
- 头文件:
#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
- 测试代码实例:
#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;
}