1.Exynos_4412下的IIC控制器
Exynos 4412 SCP简化指令集计算机(RISC)微处理器支持四个多主控间集成电路(I2C)总线串行接口。为了在连接到I2C总线的总线主机和外围设备之间传输信息,我们使用了一条专用的串行数据线(SDA)和串行时钟线(SCL)。SDA线和SCL线都是双向的。
在多主I2c总线模式下,多个Exynos 4412 SCP RISC微处理器接收或从从设备或从设备传输串行数据。主Exynos 4412 SCP通过I2C总线启动并终止数据传输。Exynos 4412 SCP中的I2C总线采用了标准的I2C总线仲裁程序,实现了多主从传输和多从传输。
要控制多主I2C总线操作,必须将值写入这些寄存器:
I2C总线接口的特点是:9通道多主通道、从属I2C总线接口(8通道通用,1通道专用高清多媒体接口(HDMI))。7-位寻址模式串行、8位定向和双向数据传输支持在标准模式下的100kbit/秒支持在快速模式下的400kbit/秒。支持主传输、主接收、从传输和从接收操作支持中断或轮询事件
Master/Transmitter mode.
Master/Receiver Mode.
Slave/Transmitter mode.
Slave/Receiver Mode.
2.Exynos_4412下的IIC控制器的寄存器
I2CCONn (n = 0 to 7)
I2CSTATn (n = 0 to 7)
I2CADDn (n = 0 to 7)
I2CDSn (n = 0 to 7)
I2CLCn (n = 0 to 7)
3. MPU6050
3.1 MPU6050
MPU6050是一个运动处理传感器,其内部集成了3轴加速度传感器和3轴陀螺仪(角速度传感器),以及一个可扩展数字运动处理器
MPU6050主要参数:
可测量X、Y、Z轴三个方向的角速度
可编程设置角速度测量范围为±250、±500、±1000、±2000°/sec
可测量X、Y、Z轴三个方向的加速度
可编程设置加速度测量范围为±2g、±4g、±8g、±16g
可编程设置低功耗模式
可编程设置采样频率
… …
MPU6050通信接口:
MPU6050可以使用IIC总线和其他器件进行数据交互,我们可以使用IIC总线向MPU6050中的控制寄存器写入数据来设置MPU6050的工作参数
也可以使用IIC总线从MPU6050中的数据寄存器读取数据来获取加速度、角速度等信息
3.2 MPU6050寄存器读写时序
向MPU6050的一个寄存器写一个字节的数据
从MPU6050的一个寄存器读一个字节的数据
4. 编程
#include "exynos_4412.h"
void bee_init(void);
/****************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
}
void RTC_Init(void)
{
/*使能RTC控制*/
RTCCON = RTCCON | 1;
/*校准时间信息*/
RTC.BCDYEAR = 0x022;
RTC.BCDMON = 0x12;
RTC.BCDDAY = 0x6;
RTC.BCDWEEK = 0x17;
RTC.BCDHOUR = 0x04;
RTC.BCDMIN = 0x23;
RTC.BCDSEC = 0x50;
/*禁止RTC控制*/
RTCCON = RTCCON & (~(1));
}
void GPX1_1_Int_init(void){
/*(一)外设层次————让外部的硬件控制器能产生一个中断信号并发送给中断控制器*/
/*将GPX1_1设置为中断功能*/
GPX1.CON = GPX1.CON | (0xF << 4);
/*设置GPX1_1中断触发方式*/
EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
/*使能GPX1_1的中断功能*/
EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1));
return ;
}
void Int_controler_init(void)
{
/* (二)中断控制器层次————让中断控制器接收外设发来的中断信号并对其进行管理然后转发给一个合适的CPU去处理*/
/*全局使能中断控制器----使其能够接收外部设备产生的中断信号并转发给CPU*/
ICDDCR = ICDDCR | 1;
/*在中断控制器中使能57号端口,使中断控制器在接收到57号中断后能将其进一步转发到CPU接口*/
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
/*将57号中断交予CPU0处理*/
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8)) | (0x01 << 8);
/*将中断控制器与CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/
CPU0.ICCICR = CPU0.ICCICR | 1;
return;
}
void do_irq(void)
{
unsigned int IrqNum = 0;
static int temp = 0;
//从中断控制器获取终端号
IrqNum = CPU0.ICCIAR & (0x3ff);
switch(IrqNum)
{
case 0:
//0号中断处理程序
break;
case 1:
//1号中断处理程序
break;
/*
*
* */
case 57:
// printf("Key2 Pressed\n");
PWM.TCON = PWM.TCON & 0;
//清除GPIO控制器中的中断挂起位
EXT_INT41_PEND = (1 << 1);
//通知中断控制器,当前中断已经处理完成,可以发送其他的中断
CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3ff)) | (57);
break;
case 58:
printf("Key3 Pressed\n");
GPX2.CON = GPX2.CON & (~(0xf<<28)) | (0x1<<28);
if(temp == 0){
delay(1000000);
GPX2.DAT = GPX2.DAT | (1<<7); //led2点亮
temp = 1;
}else{
delay(1000000);
GPX2.DAT = GPX2.DAT & (~(1<<7));
temp = 0;
}
//清除GPIO控制器中的中断挂起位
EXT_INT41_PEND = (1 << 2);
//通知中断控制器,当前中断已经处理完成,可以发送其他的中断
CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3ff)) | (58);
break;
/**
*...
*/
case 159:
//159号中断处理程序
break;
default:
break;
}
}
/**********************************************************************
* 函数功能:主函数
**********************************************************************/
int main(void)
{
unsigned char zvalue_h,zvalue_l,xvalue_h,xvalue_l,yvalue_h,yvalue_l; //存储读取结果
short int zvalue,xvalue,yvalue;
unsigned int oldsec = 0,newsec = 0;
unsigned int oldmin = 0,newmin = 0;
/*设置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
RTC_Init();
GPX1_1_Int_init();
Int_controler_init();
// PWM.TCON = PWM.TCON & (0 << 0);
printf("\n********** I2C test!! ***********\n");
// mydelay_ms(100);
while(1)
{
xvalue_h = iic_read(SlaveAddress, ACCEL_XOUT_H);
xvalue_l = iic_read(SlaveAddress, ACCEL_XOUT_L);
xvalue = (xvalue_h<<8)|xvalue_l;
xvalue = xvalue * 106 / 16384;
yvalue_h = iic_read(SlaveAddress, ACCEL_YOUT_H);
yvalue_l = iic_read(SlaveAddress, ACCEL_YOUT_L);
yvalue = (yvalue_h<<8)|yvalue_l;
yvalue = yvalue * 106 / 16384;
zvalue_h = iic_read(SlaveAddress, ACCEL_ZOUT_H);
zvalue_l = iic_read(SlaveAddress, ACCEL_ZOUT_L);
zvalue = (zvalue_h<<8)|zvalue_l;
zvalue = zvalue * 106 / 16384;
mydelay_ms(100);
newmin = RTC.BCDMIN;
newsec = RTC.BCDSEC;
if((zvalue >= 97 && zvalue <= 99)&&(xvalue >=1 && xvalue <= 5 )&&(yvalue >= -2 && yvalue <= 0))
{
if(oldmin != newmin){
printf("20%x-%x-%x %x:%x:%x Status:Normal\n",RTC.BCDYEAR,RTC.BCDMON,RTC.BCDWEEK,RTC.BCDHOUR,RTC.BCDMIN,RTC.BCDSEC);
oldmin = newmin;
}
}else{
if(oldsec != newsec){
printf("20%x-%x-%x %x:%x:%x Status:Warning\n",RTC.BCDYEAR,RTC.BCDMON,RTC.BCDWEEK,RTC.BCDHOUR,RTC.BCDMIN,RTC.BCDSEC);
printf(" ACCEL--X : %d \n", xvalue);
printf(" ACCEL--Y : %d \n", yvalue);
printf(" ACCEL--Z : %d \n", zvalue);
oldsec = newsec;
}
bee_init();
PWM.TCON = PWM.TCON | 1;
}
mydelay_ms(100);
}
return 0;
}
void delay(unsigned int Time)
{
while(Time--);
}
void bee_init(void)
{
/*1.将GPD0_0引脚设置成PWM0的输出引脚*/
GPD0.CON = GPD0.CON & (~(0XF)) | 0x2;
/*2.设置PWM0的一级分频 100倍 */
PWM.TCFG0 = PWM.TCFG0 & (~(0xff)) | 99;
/*3.设置PWM0的二级分频 1倍*/
PWM.TCFG1 = PWM.TCFG1 & (~(0Xf));
/*4.设置PWM0为自动重装载,使其能够产生连续的脉冲信号*/
PWM.TCON = PWM.TCON | (1 << 3);
/*5.设置PWm0的频率为1000HZ
*
*1/1000 /(1/1000000)
*
* */
PWM.TCNTB0 = 1000;
/*6.设置PWM0的占空比*/
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;
}