目录
1 Exynos4412下IIC控制器介绍
1.1 总览
Exynos 4412 SCP是一款支持四个多主I2C总线串行接口的精简指令集计算机(RISC)微处理器。为了在总线主和外围设备之间传输信息,使用了专用的串行数据线(SDA)和串行时钟(SCL)。SDA和SCL线都是双向的。
在多主I2C总线模式下,多个Exynos 4412 SCP RISC微处理器接收或发送串行数据到或从从设备。主Exynos 4412 SCP启动和终止I2C总线上的数据传输。 Exynos 4412 SCP的I2C总线使用标准的I2C总线仲裁过程实现多主和多从传输。
要控制多主I2C总线操作,必须向以下寄存器写入值:
• 多主I2C总线控制寄存器-I2CCON
• 多主I2C总线控制/状态寄存器-I2CSTAT
• 多主I2C总线Tx / Rx数据移位寄存器-I2CDS
• 多主I2C总线地址寄存器-I2CADD
如果I2C总线处于空闲状态,则SDA和SCL线都应处于高电平。 SDA的高至低转换会引发起始条件。 SDA的低至高转换会引发停止条件,而SCL保持稳定在高电平。
主设备始终生成起始和停止条件。在启动条件被初始化后,数据字节中的前7位地址值通过SDA线传输。该地址值确定了总线主设备选择的从设备。第8位确定传输的方向(读或写)。
放置在SDA线上的每个数据字节应为8位。在总线传输操作期间,发送或接收字节的数量没有限制。 I2C主设备和从设备始终从最高有效位(MSB)开始发送数据,然后立即跟随每个ACK位。
1.2 特征
I2C总线接口的特点包括:
• 9个通道的多主、从I2C总线接口(其中8个通道用于通用目的,1个通道专用于高清晰度多媒体接口(HDMI))。
• 7位寻址模式 • 串行、8位导向和双向数据传输
• 在标准模式下支持最高100 kbit/s的速率
• 在快速模式下支持最高400 kbit/s的速率
• 支持主设备发送、主设备接收、从设备发送和从设备接收操作
• 支持中断或轮询事件
当IIC发送完或者接收到一个字节数据,会以中断方式告知。
1.3 工作框图
1.4 其他内容介绍
四种工作模式及操作细节
起始结束信号
一个字节数据传输
应答信号
读写
多主机仲裁机制
仲裁发生在SDA线上,以防止两个主设备之间的总线冲突。如果一个具有SDA高电平的主设备检测到另一个具有SDA低电平的主设备,则不会启动数据传输。这是因为总线上的电平不符合发起数据传输的条件。仲裁过程将一直进行,直到SDA线变为高电平。 当两个或多个主设备同时将SDA线置为低电平时,每个主设备都会评估自己是否具有主设备的资格。为了进行评估,每个主设备都会检测地址位。当每个主设备生成从设备地址时,它会检测SDA线上的地址位。这是因为SDA线变为低电平而不是高电平。 假设一个主设备生成低电平作为第一个地址位,而另一个主设备维持高电平。在这种情况下,两个主设备都会检测到总线上的低电平。这是因为低电平在电源中优于高电平。当发生这种情况时,生成低电平作为第一个地址位的主设备获得主设备的资格,而生成高电平作为第一个地址位的主设备则放弃主设备的资格。 当两个主设备都将第一个地址位生成为低电平时,再次进行第二个地址位的仲裁。这个仲裁过程会一直持续到最后一个地址位。
1.5 四种工作模式寄存器流程
2 IIC寄存器详解
2.1 概述
2.2 控制寄存器
2.3 状态寄存器
2.4 地址寄存器
作为从机才有用
2.5 数据寄存器
2.6 其他寄存器
不常用
3 MPU06050
3.1 简介
MPU6050是一个运动处理传感器,其内部集成了3轴加速度传感器 和3轴陀螺仪(角速度传感器),以及一个可扩展数字运动处理器
3.2 MPU6050主要参数
可测量X、Y、Z轴三个方向的角速度
可编程设置角速度测量范围为±250、±500、±1000、±2000°/sec
可测量X、Y、Z轴三个方向的加速度
可编程设置加速度测量范围为±2g、±4g、±8g、±16g
可编程设置低功耗模式
可编程设置采样频率
3.3 MPU6050通信接口
MPU6050可以使用IIC总线和其他器件进行数据交互,我们可以使用IIC总线向MPU6050中的控制寄存器写入数据来设置MPU6050的工作参数 也可以使用IIC总线从MPU6050中的数据寄存器读取数据来获取加速度、角速度等信息
3.4 6050芯片手册
datasheet重点关注这个时序
寄存器
3.4.1 概览(8位寄存器)
3.4.2 实验需要用到的寄存器
/****************MPU6050内部常用寄存器地址****************/
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000°/s)
#define ACCEL_CONFIG 0x1C //加速计自检及测量范围及高通滤波频率,典型值:0x0(不自检,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 SlaveAddress 0x68 //MPU6050-I2C地址
上面设定了典型值,详细介绍参考手册
加速度和角速度都有正负,数据存放在2个寄存器,需要分别读取合并成16位。
加速度正负参考
从机地址(注意要地址需要根据ADO引脚确定,我们开发板即0x68,怕地址冲突)
4 MPU6050寄存器读写时序
4.1 向MPU6050的一个寄存器写一个字节的数据
1.主机(Exynos4412)发送起始信号
2.主机发送从机地址(MPU6050的地址)及读写方向(写)
3.从机(MPU6050)发送应答信号
4.主机发送一个字节数据(要写的寄存器的地址)
5.从机发送应答信号
6.主机发送一个字节数据(要写到寄存器的数据)
7.从机发送应答信号
8.主机发送停止信号
4.2 从MPU6050的一个寄存器读一个字节的数据
1.主机(Exynos4412)发送起始信号
2.主机发送从机地址(MPU6050的地址)及读写方向(写)
3.从机(MPU6050)发送应答信号
4.主机发送一个字节数据(要写的寄存器的地址)
5.从机发送应答信号
6.主机(Exynos4412)发送起始信号
7.主机发送从机地址(MPU6050的地址)及读写方向(读)
8.从机(MPU6050)发送应答信号
9.从机发送一个字节数据(要读的寄存器中的数据)
10.主机发送非应答信号(不再接收更多的数据)
11.主机发送停止信号
5 练习
简述通过主机从MPU6050中的一个寄存器中读一个字节的数据的过程
略
6 MPU6050基础代码
#include "exynos_4412.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)
{
/*对时钟源进行512倍预分频 打开IIC中断(每次完成一个字节的收发后中断标志位会自动置位)*/
I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);
/*设置IIC模式为主机发送模式 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xd0;
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+写位0)*/
I2C5.I2CDS = slave_addr<<1;
/*设置IIC模式为主机发送模式 发送起始信号启用总线 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xf0;
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第二个字节数据(即MPU6050内部寄存器的地址)写入发送寄存器*/
I2C5.I2CDS = addr;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第三个字节数据(即要写入到MPU6050内部指定的寄存器中的数据)写入发送寄存器*/
I2C5.I2CDS = data;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*发送停止信号 结束本次通信*/
I2C5.I2CSTAT = 0xD0;
/*清除中断挂起标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*延时*/
mydelay_ms(10);
}
/**********************************************************************
* 函数功能:I2C从特定地址读取1个字节的数据
* 输入参数: slave_addr: I2C从机地址
* addr: 芯片内部特定地址
* 返回参数: unsigned char: 读取的数值
**********************************************************************/
unsigned char iic_read(unsigned char slave_addr, unsigned char addr)
{
unsigned char data = 0;
/*对时钟源进行512倍预分频 打开IIC中断(每次完成一个字节的收发后中断标志位会自动置位)*/
I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);
/*设置IIC模式为主机发送模式 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xd0;
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+写位0)*/
I2C5.I2CDS = slave_addr<<1;
/*设置IIC模式为主机发送模式 发送起始信号启用总线 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xf0;
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第二个字节数据(即要读取的MPU6050内部寄存器的地址)写入发送寄存器*/
I2C5.I2CDS = addr;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*清除中断挂起标志位 重新开始一次通信 改变数据传送方向*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+读位1)*/
I2C5.I2CDS = slave_addr << 1 | 0x01;
/*设置IIC为主机接收模式 发送起始信号 使能IIC收发*/
I2C5.I2CSTAT = 0xb0;
/*等待从机接收到数据后应答*/
while(!(I2C5.I2CCON & (1<<4)));
/*禁止主机应答信号(即开启非应答 因为只接收一个字节) 清除中断标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<7))&(~(1<<4));
/*等待接收从机发来的数据*/
while(!(I2C5.I2CCON & (1<<4)));
/*将从机发来的数据读取*/
data = I2C5.I2CDS;
/*直接发起停止信号结束本次通信*/
I2C5.I2CSTAT = 0x90;
/*清除中断挂起标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*延时等待停止信号稳定*/
mydelay_ms(10);
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;
}
7 练习
综合项目:
实时监测开发板的放置状态,当监测到开发板水平放置时,每隔一分钟向终端上打印一次当前的时间以及开发板的状态
如:“2023-04-05 23:45:00 Status: Normal”
当监测到开发板发生倾斜时,每隔一秒钟向终端上打印一次当前的时间以及开发板的状态
如:“2023-04-05 23:45:00 Status: Warning”
同时让蜂鸣器产生“滴滴”的警报声,在警报状态下,若按下Key2按键,解除蜂鸣器的警报声
提示:
开发板水平静止放置时MPU6050的Z轴上的加速度应该等于重力加速度的值(9.8m/s2),而其X轴和Y轴上的加速度应该等于0
当开发板发生倾斜时MPU6050的Z轴上的加速度的分量会减小,而其X轴和Y轴上的加速度分量会增大
我们可以以此来判断开发板是否发生倾斜
#include "exynos_4412.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++);
}
}
void Rtc_init(void)
{
/*使能RTC控制*/
RTCCON = RTCCON | 1;
/*校准时间信息*/
RTC.BCDYEAR = 0x023;
RTC.BCDMON = 0x12;
RTC.BCDDAY = 0x7;
RTC.BCDWEEK = 0x31;
RTC.BCDHOUR = 0x23;
RTC.BCDMIN = 0x59;
RTC.BCDSEC = 0x50;
/*禁止RTC控制*/
RTCCON = RTCCON & (~(1));
}
void Buz_init(void)
{
/*1.将GPD0_0引脚设置成PWM0的输出引脚*/
GPD0.CON = GPD0.CON & (~(0xF)) | (0x2);
/*2.设置PWM0的一级分频 一级分频倍数设置为100倍*/
PWM.TCFG0 = PWM.TCFG0 & (~(0xFF)) | 99;
/*2.设置PWM0的二级分频 二级分频倍数设置为1倍 递减计数器递减频率 = PLCK / (99 + 1) / 1 = 1M*/
PWM.TCFG1 = PWM.TCFG1 & (~(0xF));
/*4.设置PWM0为自动重装载,使其能够产生连续的脉冲信号*/
PWM.TCON = PWM.TCON | (1 << 3);
/*5.设置PWM0的频率为1000HZ*/
PWM.TCNTB0 = 1000;
/*6.设置PWM0的占空比为60%*/
PWM.TCMPB0 = 600;
/*7.将TCNTB0中的值手动装载到递减计数器*/
PWM.TCON = PWM.TCON | (1 << 1);
/*8.关闭手动更新*/
PWM.TCON = PWM.TCON & (~(1 << 1));
/*9.使能PWM0,递减计数器开始递减*/
PWM.TCON = PWM.TCON | 1;
}
void Led_on(int num)
{
switch(num)
{
case 2:
GPX2.DAT = GPX2.DAT | (1 << 7);
case 3:
GPX1.DAT = GPX1.DAT | (1 << 0);
case 4:
GPF3.DAT = GPF3.DAT | (1 << 4);
case 5:
GPF3.DAT = GPF3.DAT | (1 << 5);
default:
break;
}
}
void Led_off(int num)
{
switch(num)
{
case 2:
GPX2.DAT = GPX2.DAT & ~(1 << 7);
case 3:
GPX1.DAT = GPX1.DAT & ~(1 << 0);
case 4:
GPF3.DAT = GPF3.DAT & ~(1 << 4);
case 5:
GPF3.DAT = GPF3.DAT & ~(1 << 5);
default:
break;
}
}
void Led_init(void)
{
GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28); //LED2 GPX2_7 output
//GPX1.CON = GPX1.CON & (~0xF) | 0x1; //LED3 GPX1_0 output
//GPF3.CON = GPF3.CON & (~(0xF << 16)) | (0x1 << 16); //LED4 GPF3_4 output
//GPF3.CON = GPF3.CON & (~(0xF << 20)) | (0x1 << 20); //LED5 GPF3_5 output
Led_off(2);
}
void Key_init(void)
{
//GPIO X1_1 input
GPX1.CON = GPX1.CON & (~(0xf << 4));
}
/**********************************************************************
* 函数功能:I2C向特定地址写一个字节
* 输入参数:
* slave_addr: I2C从机地址
* addr: 芯片内部特定地址
* data:写入的数据
**********************************************************************/
void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
{
/*对时钟源进行512倍预分频 打开IIC中断(每次完成一个字节的收发后中断标志位会自动置位)*/
I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);
/*设置IIC模式为主机发送模式 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xd0;
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+写位0)*/
I2C5.I2CDS = slave_addr<<1;
/*设置IIC模式为主机发送模式 发送起始信号启用总线 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xf0;
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第二个字节数据(即MPU6050内部寄存器的地址)写入发送寄存器*/
I2C5.I2CDS = addr;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第三个字节数据(即要写入到MPU6050内部指定的寄存器中的数据)写入发送寄存器*/
I2C5.I2CDS = data;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*发送停止信号 结束本次通信*/
I2C5.I2CSTAT = 0xD0;
/*清除中断挂起标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*延时*/
mydelay_ms(10);
}
/**********************************************************************
* 函数功能:I2C从特定地址读取1个字节的数据
* 输入参数: slave_addr: I2C从机地址
* addr: 芯片内部特定地址
* 返回参数: unsigned char: 读取的数值
**********************************************************************/
unsigned char iic_read(unsigned char slave_addr, unsigned char addr)
{
unsigned char data = 0;
/*对时钟源进行512倍预分频 打开IIC中断(每次完成一个字节的收发后中断标志位会自动置位)*/
I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);
/*设置IIC模式为主机发送模式 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xd0;
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+写位0)*/
I2C5.I2CDS = slave_addr<<1;
/*设置IIC模式为主机发送模式 发送起始信号启用总线 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xf0;
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第二个字节数据(即要读取的MPU6050内部寄存器的地址)写入发送寄存器*/
I2C5.I2CDS = addr;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*清除中断挂起标志位 重新开始一次通信 改变数据传送方向*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+读位1)*/
I2C5.I2CDS = slave_addr << 1 | 0x01;
/*设置IIC为主机接收模式 发送起始信号 使能IIC收发*/
I2C5.I2CSTAT = 0xb0;
/*等待从机接收到数据后应答*/
while(!(I2C5.I2CCON & (1<<4)));
/*禁止主机应答信号(即开启非应答 因为只接收一个字节) 清除中断标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<7))&(~(1<<4));
/*等待接收从机发来的数据*/
while(!(I2C5.I2CCON & (1<<4)));
/*将从机发来的数据读取*/
data = I2C5.I2CDS;
/*直接发起停止信号结束本次通信*/
I2C5.I2CSTAT = 0x90;
/*清除中断挂起标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*延时等待停止信号稳定*/
mydelay_ms(10);
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,xvalue_h,xvalue_l,yvalue_h,yvalue_l; //存储读取结果
short int zvalue,xvalue,yvalue;
int alarm = 0;
Rtc_init();
Led_init();
Key_init();
Buz_init();
/*设置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, ACCEL_ZOUT_H); //获取MPU6050-Z轴角速度高字节
zvalue_l = iic_read(SlaveAddress, ACCEL_ZOUT_H); //获取MPU6050-Z轴角速度低字节
zvalue = (zvalue_h<<8)|zvalue_l; //获取MPU6050-Z轴角速度
xvalue_h = iic_read(SlaveAddress, ACCEL_XOUT_H); //获取MPU6050-x轴角速度高字节
xvalue_l = iic_read(SlaveAddress, ACCEL_XOUT_H); //获取MPU6050-x轴角速度低字节
xvalue = (xvalue_h<<8)|xvalue_l; //获取MPU6050-x轴角速度
yvalue_h = iic_read(SlaveAddress, ACCEL_YOUT_H); //获取MPU6050-y轴角速度高字节
yvalue_l = iic_read(SlaveAddress, ACCEL_YOUT_H); //获取MPU6050-y轴角速度低字节
yvalue = (yvalue_h<<8)|yvalue_l; //获取MPU6050-y轴角速度
if(!(GPX1.DAT & ( 1 << 1)))
{
//等待松手
while(!(GPX1.DAT & (1 << 1)));
printf("key press\n");
alarm = 0;
}
if( xvalue>10 || xvalue < -10 || yvalue >10 || yvalue<-10 ||zvalue < 13480)
{
Led_on(2);
alarm = 1;
}
if(alarm == 1)
{
printf("20%x-%x-%x %x %x:%x:%x,Status:Warning\n",RTC.BCDYEAR, RTC.BCDMON, RTC.BCDWEEK, RTC.BCDDAY, RTC.BCDHOUR, RTC.BCDMIN, RTC.BCDSEC);
PWM.TCON = PWM.TCON | 1;
mydelay_ms(500);
PWM.TCON = PWM.TCON & (~(1));
mydelay_ms(500);
}
else
{
Led_off(2);
PWM.TCON = PWM.TCON & (~(1));
printf("20%x-%x-%x %x %x:%x:%x,Status:Normal\n",RTC.BCDYEAR, RTC.BCDMON, RTC.BCDWEEK, RTC.BCDDAY, RTC.BCDHOUR, RTC.BCDMIN, RTC.BCDSEC);
mydelay_ms(1000);
}
printf(" x:%d,y:%d,z:%d \n", xvalue,yvalue,zvalue); //打印MPU6050-Z轴角速度
//mydelay_ms(100);
}
return 0;
}