IIC
1. 概念,IIC是由PHILIPS公司推出的两线式串行总线,通过串行数据线(SDA)和串行时钟线(SCL)在连接到总线上的设备间进行
传递消息,每一个设备都有自己唯一的地址识别
2. 看原理图,找到从机设备(三轴加速度传感器)所接的IIC引脚 -- I2C_SCL5 I2C_SDA5
配置相关引脚为IIC功能 -- XspiMISO0/I2C_5_SDA/GPB_2 XspiMOSI0/I2C_5_SCL/GPB_3
1. GPBCON
GPBCON[2] [11:8]:0x3 = I2C_5_SDA
GPBCON[3] [15:12]:0x3 = I2C_5_SCL
2. GPBPUD
GPBPUD[n] [2n + 1:2n]:0x0 = Disables Pull-up/Pull-down
3. 看芯片手册上的IIC概述
1. 当IIC被闲置的时候,SDA和SCL都处于高电平,在SCL处于高电平的时候,SDA的一个下降沿表示一个start信号
SDA的一个上升沿表示一个stop信号
2. 我们的start和stop信号都是有主机设备产生的
3. 主机发送start信号后接着通过SDA发送一个7bits的地址,这个地址决定主机要通信的从机设备,第八位决定主机是要读
从机设备还是写从机设备
从SDA发送的数据只能是八位八位的,在接收到一个字节后会立马发出一个响应信号(ACK)
主机通过判断中断挂起位来判断是否接受到数据或者ACK应答信号
4. IIC的四种操作模式
主机发送、主机接收、从机发送、从机接收
5. 分析时序图
6. 寄存器 - 看原理图发现我们的三轴加速度传感器接I2C_SCL5
1. I2CCON5
Acknowledge generation [7]: 1 = Enables
In Tx mode, the I2CSDA is idle in the ACK time.
In Rx mode, the I2CSDA is low in the ACK time
注意: ACK generation is disabled before Reading the last data to generate the STOP condition in Rx mode.
Tx clock source selection [6]: 0 = I2CCLK = fPCLK/16
1 = I2CCLK = fPCLK/512
Tx/Rx Interrupt (5) [5]: 1 = Enables
Interrupt pending flag [4]: 0 = 1) No interrupt is pending (If Read).
2) Clears pending condition and resumes the operation (if Write).
1 = 1) Interrupt is pending (If Read)
2) N/A (If Write)
Transmit clock value (4) [3:0]: Tx clock = I2CCLK/(I2CCON[3:0] + 1)
2. I2CSTAT5
Mode selection [7:6]: 00 = Slave receive mode
01 = Slave transmit mode
10 = Master receive mode
11 = Master transmit mode
Busy signal status/START STOP condition [5]: 0 = (Read) Not busy (If Read)
(write) STOP signal generation
1 = (Read) Busy (If Read)
(write) START signal generation.
Transfers the data in I2CDS automatically just after the start signal.
Serial output [4]: 1 = Enables Rx/Tx
Last-received bit status flag [0]:1 = Last-received bit is set to 1 (does not receive ACK).
3. I2CADD5 //用来读从机地址的
Slave address [7:0]: Slave address: [7:1]
4. I2CDS5
Data shift [7:0]: 8-bit data shift register for I2C-bus Tx/Rx operation
4. 看MPU6050的数据手册(Datasheet)
1. The slave address of the MPU-60X0 is b110100X which is 7 bits long. The LSB bit of the 7 bit address is
determined by the logic level on pin AD0
经查看三轴加速度的原理图发现AD0为0,所以从机地址是0x68 ==》 slave_addr = 0x68
2. To read the internal MPU-60X0 registers, the master sends a start condition, followed by the IIC address and
a write bit, and then the register address that is going to be read
当要对MPU6050内部的寄存器进行操作时,在发送完IIC从机地址收到ACk后还要接着发送6050内部寄存器地址
2. 分析主发从(MPU6050)收的时序
5. 查看MPU6050的寄存器手册(Register Map) 寄存器都是8位的
SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,2G,5Hz)
GYRO_ZOUT_H 0x47 //获取MPU6050-Z轴角速度高字节
GYRO_ZOUT_L 0x48 //获取MPU6050-Z轴角速度低字节
PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用) 用来设置6050内部时钟
6. 写程序
iic_init
/* 1. 初始化GPIO引脚为IIC相关功能 */
GPBCON = GPBCON & (~(0xf<<8)) | (0x3<<8); //配置GPB_2引脚为IIC_SDA
GPBCON = GPBCON & (~(0xf<<12)) | (0x3<<12); //配置GPB_3引脚为IIC_SCL
/* 2. 设置IIC的工作时钟,使能IIC中断,使能ACK */
I2CCON5 |= (0x7<<5);
/* 3. 使能IIC串口输出 */
I2CSTAT5 |= (1<<4);
/*********************************************************************************************************/
iic_write -- 参数:addr_slave(从机地址) addr_slave_reg(从机寄存器地址) data
/* 1. 发送start信号 */
I2CSTAT5 |= (1<<5);
/* 2. 设置主发模式 */
I2CSTAT5 |= (3<<6);
/* 3. 发送IIC从机设备地址 */
I2CDS5 = (0x68<<1);
while(!(I2CCON5 & (1<<4))); //等待,直到中断被挂起(收到ACK)
/* 4. 发送从设备内部寄存器地址,发送后先清中断挂起(从新开始发送),然后再等待中断被挂起(收到ACK) */
I2CDS5 = addr_slave_reg;
I2CCON5 &= ~(1<<4); //清中断挂起位
while(!(I2CCON5 & (1<<4))); //等待,直到中断被挂起(收到ACK)
/* 5. 发送数据,发送前先等待中断被挂起(收到ACK),然后清中断挂起,最后再发送 */
I2CDS5 = data;
I2CCON5 &= ~(1<<4);
while(!(I2CCON5 & (1<<4)));
/* 6. 发送stop信号,禁止接受ACK */
I2CSTAT5 = 0xd0;
/* 请中断挂起 */
I2CCON5 &= ~(1<<4);
delay_ms(10);
/*********************************************************************************************************/
iic_read -- 参数:addr_slave(从机地址) addr_slave_reg(从机寄存器地址)
/* 1. 发送start信号 */
I2CSTAT5 |= (1<<5)|(1<<4);
/* 2. 设置主发模式 */
I2CSTAT5 |= (3<<6);
/* 3. 发送IIC从机设备地址+写0 */
I2CDS5 = (0x68<<1);
while(!(I2CCON5 & (1<<4))); //等待,直到中断被挂起(收到ACK)
/* 4. 发送从设备内部寄存器地址,发送后先清中断挂起(从新开始发送),然后再等待中断被挂起(收到ACK) */
I2CDS5 = addr_slave_reg;
I2CCON5 &= ~(1<<4); //清中断挂起位
while(!(I2CCON5 & (1<<4))); //等待,直到中断被挂起(收到ACK)
/* 5. 清中断,发stop信号 */
I2CCON5 &= ~(1<<4);
I2CSTAT5 = 0xd0;
/* 6. 设置主机接收模式 */
I2CSTAT5 = I2CSTAT5& (~(3<<6)) | (2<<6);
/* 7. 发送start信号,发从机地址+读1位 */
I2CSTAT5 |= (1<<5);
I2CDS5 = (0x68<<1) | 0x1;
while(!(I2CCON5 & (1<<4)));
/* 5. 发送数据,发送前先等待中断被挂起(收到ACK),然后清中断挂起,最后再发送 */
I2CDS5 = data;
I2CCON5 &= ~(1<<4);
while(!(I2CCON5 & (1<<4)));
/* 6. 发送stop信号 */
I2CSTAT5 = 0xd0;
/* 清中断挂起位 */
I2CCON5 &= ~(1<<4);
delay_ms(10);
/*********************************************************************************************************/
init_mpu6050
iic_write(SlaveAddress, PWR_MGMT_1, 0x00); //设置使用内部时钟8M
iic_write(SlaveAddress, SMPLRT_DIV, 0x07); //设置陀螺仪采样率
iic_write(SlaveAddress, CONFIG, 0x06); //设置数字低通滤波器
iic_write(SlaveAddress, GYRO_CONFIG, 0x18); //设置陀螺仪量程+-2000度/s
iic_write(SlaveAddress, ACCEL_CONFIG, 0x0); //设置加速度量程+-2g
最后一步,在main中读Z轴的加速度
zvalue_h = iic_read(SlaveAddress, GYRO_ZOUT_H); //获取MPU6050-Z轴角速度高字节
zvalue_l = iic_read(SlaveAddress, GYRO_ZOUT_L); //获取MPU6050-Z轴角速度低字节
zvalue = (zvalue_h<<8)|zvalue_l; //获取MPU6050-Z轴角速度
-
#include "exynos_4412.h"
-
#include "uart.h"
-
-
-
//****************************************
-
// MPU6050内部地址
-
//****************************************
-
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
-
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
-
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
-
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,2G,5Hz)
-
#define ACCEL_XOUT_H 0x3B
-
#define ACCEL_XOUT_L 0x3C
-
#define ACCEL_YOUT_H 0x3D
-
#define ACCEL_YOUT_L 0x3E
-
#define ACCEL_ZOUT_H 0x3F
-
#define ACCEL_ZOUT_L 0x40
-
#define TEMP_OUT_H 0x41
-
#define TEMP_OUT_L 0x42
-
#define GYRO_XOUT_H 0x43
-
#define GYRO_XOUT_L 0x44
-
#define GYRO_YOUT_H 0x45
-
#define GYRO_YOUT_L 0x46
-
#define GYRO_ZOUT_H 0x47
-
#define GYRO_ZOUT_L 0x48
-
#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
-
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
-
#define SlaveAddress 0x68 //MPU6050-I2C地址
-
-
-
-
/**********************************************************************
-
*函数功能:延时函数
-
**********************************************************************/
-
void mydelay_ms(int time)
-
{
-
int i, j;
-
while(time--)
-
{
-
for (i = 0; i < 5; i++)
-
for (j = 0; j < 514; j++);
-
}
-
}
-
-
-
/**********************************************************************
-
* 函数功能:I2C向特定地址写一个字节
-
* 输入参数:
-
* slave_addr: I2C从机地址
-
* addr: 芯片内部特定地址
-
* data:写入的数据
-
**********************************************************************/
-
-
void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
-
{
-
-
I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5); //设置I2C时钟预分配512、使能I2C中断
-
I2C5.I2CSTAT |= 0x1<<4; //使能I2C串口输出
-
-
I2C5.I2CDS = slave_addr<<1 ; //MPU6050-I2C地址+写位0
-
I2C5.I2CSTAT = 0xf0; //主机发送模式 、使能I2C的发送和接受、发出开始信号
-
while(!(I2C5.I2CCON & (1<<4))); //等待ACK周期后,中断挂起
-
-
I2C5.I2CDS = addr; //数据写入地址(MPU6050芯片内部地址)
-
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4)); //清除中断标志位
-
while(!(I2C5.I2CCON & (1<<4))); //等待ACK周期后,中断挂起
-
-
I2C5.I2CDS = data; //要写入的数据
-
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4)); //清除中断标志位
-
while(!(I2C5.I2CCON & (1<<4))); //等待ACK周期后,中断挂起
-
-
I2C5.I2CSTAT = 0xD0; //发出停止信号
-
//1101 0000
-
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4)); //清除中断标志位
-
mydelay_ms(10); //延时等待I2C停止信号生效
-
}
-
-
-
/**********************************************************************
-
* 函数功能:I2C从特定地址读取1个字节的数据
-
* 输入参数: slave_addr: I2C从机地址
-
* addr: 芯片内部特定地址
-
* 返回参数: unsigned char: 读取的数值
-
**********************************************************************/
-
-
unsigned char iic_read(unsigned char slave_addr, unsigned char addr)
-
{
-
-
unsigned char data = 0;
-
-
I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5); //设置I2C时钟预分配512、使能I2C中断
-
I2C5.I2CSTAT |= 0x1<<4; //使能I2C串口输出
-
-
I2C5.I2CDS = slave_addr<<1; //MPU6050-I2C地址+写位0
-
I2C5.I2CSTAT = 0xf0; //主机发送模式 、使能I2C的发送和接受、发出开始信号
-
while(!(I2C5.I2CCON & (1<<4))); //等待ACK周期后,中断挂起
-
-
I2C5.I2CDS = addr; //读取数据的地址(MPU6050芯片内部地址)
-
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4)); //清除中断标志位
-
while(!(I2C5.I2CCON & (1<<4))); //等待ACK周期后,中断挂起
-
-
-
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4)); //清除中断标志位
-
-
I2C5.I2CDS = slave_addr << 1 | 0x01; //MPU6050-I2C地址+读位1
-
I2C5.I2CSTAT = 0xb0; //主机接受模式 、使能I2C的发送和接受、发出开始信号
-
while(!(I2C5.I2CCON & (1<<4))); //等待ACK周期后,中断挂起
-
-
-
-
I2C5.I2CCON = I2C5.I2CCON & (~(1<<7))&(~(1<<4));//禁止ACK信号、清除中断标志位
-
while(!(I2C5.I2CCON & (1<<4))); //等待ACK周期后,中断挂起
-
data = I2C5.I2CDS; //读取数据
-
-
-
I2C5.I2CSTAT = 0x90; //发出停止信号
-
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4)); //清除中断标志位
-
mydelay_ms(10); //延时等待I2C停止信号生效
-
-
return data; //返回读取的数值
-
-
}
-
-
-
/**********************************************************************
-
* 函数功能:MPU6050初始化
-
**********************************************************************/
-
-
void MPU6050_Init ()
-
{
-
iic_write(SlaveAddress, PWR_MGMT_1, 0x00); //设置使用内部时钟8M
-
iic_write(SlaveAddress, SMPLRT_DIV, 0x07); //设置陀螺仪采样率
-
iic_write(SlaveAddress, CONFIG, 0x06); //设置数字低通滤波器
-
iic_write(SlaveAddress, GYRO_CONFIG, 0x18); //设置陀螺仪量程+-2000度/s
-
iic_write(SlaveAddress, ACCEL_CONFIG, 0x0); //设置加速度量程+-2g
-
}
-
-
-
-
/**********************************************************************
-
* 函数功能:主函数
-
**********************************************************************/
-
-
int main(void)
-
{
-
-
unsigned char zvalue_h,zvalue_l; //存储读取结果
-
short int zvalue;
-
-
/*设置GPB_2引脚和GPB_3引脚功能为I2C传输引脚*/
-
GPB.CON = (GPB.CON & ~(0xF<<12)) | 0x3<<12; //设置GPB_3引脚功能为I2C_5_SCL
-
GPB.CON = (GPB.CON & ~(0xF<<8)) | 0x3<<8; //设置GPB_2引脚功能为I2C_5_SDA
-
-
uart_init(); //初始化串口
-
MPU6050_Init(); //初始化MPU6050
-
-
printf("\n********** I2C test!! ***********\n");
-
while(1)
-
{
-
zvalue_h = iic_read(SlaveAddress, GYRO_ZOUT_H); //获取MPU6050-Z轴角速度高字节
-
zvalue_l = iic_read(SlaveAddress, GYRO_ZOUT_L); //获取MPU6050-Z轴角速度低字节
-
zvalue = (zvalue_h<<8)|zvalue_l; //获取MPU6050-Z轴角速度
-
-
printf(" GYRO--Z :Hex: %d \n", zvalue); //打印MPU6050-Z轴角速度
-
mydelay_ms(100);
-
}
-
return 0;
-
}