stm32入门学习之IIC

IIC通信协议

IIC有两根通信线,SCL和SDA;是同步半双工通信,带数据应答协议;支持总线挂载多设备(一主多从,多主多从).

所有IIC设备的SCL连接在一起,SDA连接在一起,设备的SCL和SDA均要配置成开漏输出模式.SCL和SDA各自添加一个上拉电阻,阻值一般为4.7K欧左右.

主机对SCL完全控制,在空闲状态下,主机可以主动发起对 SDA的控制;从机发送数据和应答时,主机才会转交SDA控制权给从机。 从机对于SCL时钟线任何时候只能被动读取,从机不允许控制SCL线.对于SDA线,从机不允许主动发起对SDA的控制.只有主机发送读取从机的命令后,或者从机应答的时候,从机才能短暂地获得对SDA的控制权.SCL和SDA线空闲时均为高电平状态.

IIC的时序基本单元

起止条件

起始条件:在SCL高电平期间,SDA由高电平转为低电平,产生下降沿.SDA下降沿之后主机要把SCL设置为低电平.

终止条件:在SCL高电平期间,SDA由低电平转为高电平,产生上升沿.SDA上升沿后主机要把SCL设置为高电平.

对应的逻辑代码:

void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}



void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

 发送一个字节

 在SCL低电平期间,主机会将数据位依次放到SDA上,然后释放SCL.从机在SCL高电平期间读取数据位,所以SCL高电平期间SDA不能够变化.

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

接收一个字节

在SCL低电平期间,从机会将数据位依次放在SDA上,然后释放SCL;主机会在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化.主机在接收数据前要释放SDA(即拉高SDA),交由从机控制.

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;
	MyI2C_W_SDA(1);//主机释放SDA
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1);
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
		MyI2C_W_SCL(0);
	}
	return Byte;
}

 发送/接收应答位

发送应答:主机在接收完一个字节后在下一个时钟发送一位数据,0表示应答,1表示非应答.

接收应答:主机发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,0表示应答,1表示非应答.

//发送应答
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}


//接收应答
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);//释放SDA
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();//此时读取SDA的电平
	MyI2C_W_SCL(0);
	return AckBit;//应答位
}

指定地址写

 在指定从机地址为1101000的设备在其内部0x19地址的寄存器中写入0xAA这个数据.

 空闲状态可以看到SCL和SDA是高电平.在SCL高电平期间,拉低SDA产生起始条件.发送字节内容:从机地址加读写位(确定通信对象与写读).在SCL低电平期间SDA变换数据.

第二个字节送到指定设备内部(寄存器地址/指令控制字)

第三个字节:写入到寄存器的内容.

当前地址读

对于指定设备(Slave Address),在当前地址(当前指针指向的值)指针指示的地址下,读取从机数据(Data)

第一个字节:从机寻址和指定读写标志位;

第二个字节:从机发送的字节数据

指定地址读

对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)

 对于指定设备,在指定地址下读取从机数据.

发送字节进行寻址(1101000),从机应答后再发送字节(指定地址),从机寄存器指针指向0x19;然后再来个起始条件.另起一个时序,重新寻址并且指定读写标志位,开始读.

读取MPU6050 

模块简介

MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景

3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度

3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度

代码示例

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
//设置SCL电平
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
	Delay_us(10);
}
//设置SDA电平 
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
}
//读SDA电平数据
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);//读取SDA线的电平
	Delay_us(10);
	return BitValue;//返回值
}

void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	//
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
//起始条件
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}
//终止条件
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}
//发送字节
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));//高位先行 1000 0000与上Byte最高位,1所在的位置数据保持不变  同时1往后面移动一位
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}
//接收字节
uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;//先定义为0 
	MyI2C_W_SDA(1);//主机释放SDA
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1);//SCL高电平读取数据
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}// if成立该位为1 if不成立该位为0 
		MyI2C_W_SCL(0);//SCL拉低
	}
	return Byte;
}


//发送应答位
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}
//接收应答位 
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);//主机释放SDA  现在SDA由从机是否控制 
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();//读取ACK的值  0应答  1无应答
	MyI2C_W_SCL(0);
	return AckBit;
}
#ifndef __MYI2C_H
#define __MYI2C_H

void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);

#endif
#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"

#define MPU6050_ADDRESS		0xD0   //mpu地址
//指定地址写
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	MyI2C_Start();//IIC起始
	MyI2C_SendByte(MPU6050_ADDRESS);//写入设备地址  从机地址+读写位 
	MyI2C_ReceiveAck();//IIC应答
	MyI2C_SendByte(RegAddress);//指定寄存器地址
	MyI2C_ReceiveAck();//IIC应答
	MyI2C_SendByte(Data);//发送数据
	MyI2C_ReceiveAck();//IIC应答
	MyI2C_Stop();//IIC停止
}
//指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;//
	
	MyI2C_Start();//IIC起始
	MyI2C_SendByte(MPU6050_ADDRESS);//MPU地址
	MyI2C_ReceiveAck();//接收应答位
	MyI2C_SendByte(RegAddress);//指定寄存器地址
	MyI2C_ReceiveAck();
	
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);//最后一位读写位变为1 表示读
	MyI2C_ReceiveAck();
	Data = MyI2C_ReceiveByte();
	MyI2C_SendAck(1);//从机不应答
	MyI2C_Stop();
	
	return Data;
}
//初始化MPU
void MPU6050_Init(void)
{
	MyI2C_Init();
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//配置电源管理寄存器1 
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//配置电源管理寄存器2 
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//采样率分频   10分频
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//滤波参数
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//陀螺仪配置寄存器
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//加速度计配置寄存器
}

uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}



void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//读取X轴高八位
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);//读取x轴低八位
	*AccX = (DataH << 8) | DataL;//合成16位数据
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
}
#ifndef __MPU6050_H
#define __MPU6050_H

void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);

void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);

#endif
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C

#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75

#endif

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值