【实验目的】

  • 掌握I2C协议的内容,了解I2C接口的特点。
  • 了解陀螺仪MPU6050的用途及数据采集过程。
  • 熟悉STM32F10xx系列微控制器的I2C配置,完成通过I2C读取陀螺仪MPU6050数据的实验。

【实验原理】

I2C (Inter-Integrated Circuit)协议是由Phiilps公司开发的,由于它具引脚少,硬件实现简单,可扩展性强,不需要如USART、CAN的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

一、I2C总线协议:

I2C总线在传送数据过程中共有三种类型信号,它们分别是:开始信号、结束信号和应答信号。

  • 开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
  • 结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
  • 应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。

这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。

二、I2C接口特性

  1. STM32的中等容量和大容量型号的芯片均有多达2个的I2C总线接口。
  2. 能够工作于多主模式或从模式,分别为主接收器,主发送器,从接收器及从发。
  3. 支持标准模式100Kbit/s和快速模式400Kbit/s,不支持高速模式。
  4. 支持7位或10位寻址。
  5. 内置了硬件CRC发生器/校验器。
  6. I2C的接收和发送都可以使用DMA操作。
  7. 支持系统管理总线(SMBus)总线2.0版。

三、MPU6050相关

MPU-6050为9轴运动处理传感器。它集成了3轴陀螺仪,3轴加速度计,以及一个可扩展的数字运动处理器DMP(Digital Motion Processor),可用I2C接口连接一个第三方的数字传感器,比如磁力计。MPU-6050对陀螺仪和加速度计分别用了三个16位的ADC,将其测量的模拟量转化为可输出的数字量。MPU-6050上所有设备寄存器之间的通信采用400kHz的I2C接口。另外,片上还内嵌了一个温度传感器和在工作环境下仅有±1%变动的振荡器。

四、硬件连接和库函数介绍

嵌入式实验 之 I2C实验(IIC)_嵌入式实验
图1 硬件连接示意图
本实验中使用的库函数主要有unsigned char I2C_ReadOneByte(unsigned char I2C_Addr,unsigned char addr),其功能为读取指定设备,指定寄存器的一个值,其参数分别为:
I2C_Addr:目标设备地址,在我们的实验中为陀螺仪和加速度计的地址。
Addr:寄存器地址,即存放陀螺仪信息和加速度计信息的寄存器地址

五、软件流程图

本实验项目需要包涵oled.c显示屏库函数、MPU6050.c运动传感器库函数、delay.c延时库函数,本实验的实验流程图如图2所示:

图2 实验流程示意图

【实验环境】

硬件设备

双轮自平衡机器人。如图3所示,平衡车所搭载的电路板上已经搭载MPU6050芯片。
ST-Link下载器(包含USB线与下载线)。如图4所示。

操作系统

Windows7/8/10,32bit/64bit

嵌入式实验 之 I2C实验(IIC)_嵌入式实验_02
图3 双轮自平衡机器人
嵌入式实验 之 I2C实验(IIC)_编程环境_03
图4 ST-Link下载器与下载线

软件环境

Keil 5
实验场地:

【实验步骤】

第一步 配置工程环境

打开已经建立好的工程模板,在新建立的工程模板中已经添加五个文件夹,分别命名为USER、HARDWARE、SYSTEM、CORE、FWLib文件夹,如图5所示。其中USER文件夹存放的是主函数,HARDWARE文件夹存放的是本实验对应的硬件设备函数,SYSTEM存放的是本课程所有实验通用的函数,CORE文件夹存放的是启动文件,FWLib文件夹存放的是底层驱动函数。
嵌入式实验 之 I2C实验(IIC)_编程环境_04
图5工程模板对应的文件夹
在HARDWARE文件夹下右键导入已存在文件IOI2C.c和IOI2C.h文件,这两个文件保存在工程目录下MiniBalance_HARDWARE文件夹中IIC文件夹内,如图6所示。
嵌入式实验 之 I2C实验(IIC)_编程环境_05
图6 在HARDWARE文件夹下导入IOI2C.c和IOI2C.h文件

第二步 编写IIC实现函数

打开刚刚导入程序中的IOI2C.h文件,可以发现文件里已经预定义了许多函数。

/*----------------------------------IIC的操作函数---------------------------------*/
void IIC_Init(void);						//初始化IIC的IO口			 	int IIC_Start(void);						//发送IIC 开始信号
void IIC_Stop(void); 					//发送IIC停止信号
void IIC_Send_Byte(u8 txd);				//IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);		//IIC读取一个字节
int IIC_Wait_Ack(void);					//IIC等待ACK信号
void IIC_Ack(void);					//IIC发送ACK信号
void IIC_NAck(void);					//IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data); //将一个字节写入指定设备,指定寄存器
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);	 //读取指定设备,指定寄存器中的一个字节
unsigned char I2C_Readkey(unsigned char I2C_Addr);
unsigned char I2C_ReadOneByte(unsigned char I2C_Addr,unsigned char addr);//读取指定设备,指定寄存器的一个值
unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data);// 写入指定设备,指定寄存器一个字节
u8 IICwriteBytes(u8 dev, u8 reg, u8 length, u8* data); //将多个字节写入指定设备,指定寄存器
u8 IICwriteBits(u8 dev,u8 reg,u8 bitStart,u8 length,u8 data);//读,修改,写指定设备,指定寄存器一个字节中的多个位
u8 IICwriteBit(u8 dev,u8 reg,u8 bitNum,u8 data);//读,修改,写指定设备,指定寄存器一个字节中的1个位
u8 IICreadBytes(u8 dev, u8 reg, u8 length, u8 *data);//读取指定设备,指定寄存器的 length个值

打开程序中的IOI2C.c文件,编写IIC的IO口初始化函数。

void IIC_Init(void)
{			
	GPIO_InitTypeDef GPIO_InitStructure;
//使能GPIOB端口时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//端口配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
//推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//50M
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//根据设定的参数初始化GPIOB
	GPIO_Init(GPIOB, &GPIO_InitStructure); 
}

在IOI2C.c文件中,编写IIC_Start函数,产出IIC起始信号

int IIC_Start(void)
{
SDA_OUT();		//sda线输出
IIC_SDA=1;
if(!READ_SDA)  return 0;	
IIC_SCL=1;
delay_us(1);
 IIC_SDA=0;	//开始:当CLK线即时钟线处于高电平时,数据线从高电平变为低电平
if(READ_SDA)  return 0;
delay_us(1);
IIC_SCL=0;		//钳住I2C总线,准备发送或接收数据
return 1;
}

在IOI2C.c文件中,编写IIC_Stop函数,产出IIC停止信号

void IIC_Stop(void)
{
SDA_OUT();			//sda线输出
IIC_SCL=0;
IIC_SDA=0;	//停止:当CLK线即时钟线处于高电平时,数据线从低电平变为高电平
delay_us(1);
IIC_SCL=1; 
IIC_SDA=1;			//发送I2C总线结束信号
delay_us(1);
}

为了减轻任务量,其他IIC操作函数已经编写完成,并在IOI2C.c中给出。
嵌入式实验 之 I2C实验(IIC)_嵌入式实验_06
表1 IOI2C.c中已编写函数

第三步 编写main.c文件

将工程编译需要用到的头文件包含进来,并且定义相关变量

#include "ioi2c.h"			//IIC头文件
#include "sys.h"			//包含体统头文件
#include "stm32f10x.h"		//包含系统寄存器定义声明的头文件

float Accel_Y,Accel_Angle,Accel_Z,Gyro_X,Gyro_Z;  //定义加速度计和陀螺仪变量
在主函数中调用延时函数、IIC初始化函数、MPU6050初始化函数、OLED屏幕初始化函数。
delay_init();				//调用延时函数
IIC_Init();				//调用IIC初始化函数
MPU6050_initialize();		//调用MPU6050初始化函数
OLED_Init();				//调用OLED初始化函数

在主函数中,加入主循环,并编写IIC读取程序,以读取陀螺仪加速度计信息。其中,使用的函数主要为
unsigned char I2C_ReadOneByte(unsigned char I2C_Addr,unsigned char addr),其功能为读取指定设备,指定寄存器的一个值,
I2C_Addr:目标设备地址,在我们的实验中为陀螺仪和加速度计的地址。
Addr:寄存器地址,即存放陀螺仪信息和加速度计信息的寄存器地址。

while(1)				//进入主循环
{
/*----------------------------读取X轴陀螺仪信息-------------------------*/
// devAddr : MPU6050的设备地址
// MPU6050_RA_GYRO_XOUT_H :Gyro_X数值的寄存器地址
Gyro_X=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_XOUT_H)<<8)
+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_XOUT_L);
/*----------------------------读取Z轴陀螺仪信息-------------------------*/
Gyro_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_H)<<8)
+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_L);
/*----------------------------读取Y轴加速度计信息-------------------------*/
Accel_Y=(I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_YOUT_H)<<8)
+I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_YOUT_L);
/*----------------------------读取Z轴加速度计信息-------------------------*/
Accel_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_ZOUT_H)<<8)
+I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_ZOUT_L);
/*----------------------------数据类型转换,-------------------------*/
		if(Gyro_X>32768)  Gyro_X-=65536;
		if(Gyro_Z>32768)  Gyro_Z-=65536;
		if(Accel_Y>32768)  Accel_Y-=65536;
		if(Accel_Z>32768)  Accel_Z-=65536; 
}

在main.c中,编写显示函数oled_show(),并在主循环中调用oled_show()函数。


void oled_show(void)
{
	OLED_ShowString(00,10,"Gyro_X");
	OLED_ShowNumber(95,10,Gyro_X,5,12);
	OLED_ShowString(00,20,"Gyro_Z");
	OLED_ShowNumber(95,20,Gyro_Z,5,12);

	OLED_ShowString(00,30,"Accel_Y");
	OLED_ShowNumber(95,30,Accel_Y,5,12);
	OLED_ShowString(00,40,"Accel_Z");
	OLED_ShowNumber(95,40,Accel_Z,5,12);
	OLED_Refresh_Gram();	
} 

第四步 编译并下载,观察实验现象

本实验采用仿真器为STLink V2,将仿真器与小车相连,注意正负极不要接反,如图7所示。
嵌入式实验 之 I2C实验(IIC)_物联网_07
图7 仿真器与下载线连接图
编译程序:点击如图8所示的编译按键。
嵌入式实验 之 I2C实验(IIC)_嵌入式实验_08
图8 Keil编译环境下的编译按键
当编译完成后,如果没有问题,Build Output栏会出现无错误、无警告的提示,如图9所示。
嵌入式实验 之 I2C实验(IIC)_嵌入式实验_09
图9 编译通过后Build Output栏提示信息
下载程序:点击如图所示的下载按键,程序就会下载到STM32的芯片中。下载按键如图1.10所示。
嵌入式实验 之 I2C实验(IIC)_编程环境_10
图10 Keil编译环境下的下载按键
观察实验现象,在小车的OLED显示屏上能够实时的显示出小车当前采集到的陀螺仪加速度计信息,微微晃动小车,可以看到显示屏上的数值有明显变化。
嵌入式实验 之 I2C实验(IIC)_嵌入式实验_11
图11 平衡车上OLED显示屏

【思考题】

1、选择题

题目1:下面哪个结构体是设置I2C传输速率(C)
A:I2C_AcknowledgeAddress
B:I2C_DutyCycle
C:I2C_ClockSpeed
D:I2C_OwnAddress1

题目2:在I2C协议中,起始信号后产生的信号是哪种信号?(A)
A:地址位
B:传输方向选择位

2、简答题

题目1:I2C有几种传输模式,速率分别是多少?
具有三种传输模式:标准模式传输速率为100kbit/s,快速模式为400kbit/s,高速模式下可达3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。

附录:I2C 库函数

嵌入式实验 之 I2C实验(IIC)_物联网_12