一、软件IIC的引脚配置及六块时序单元
通过函数来间接设置SCL和SDA总线,方便加延时
//写 scl总线
void myiic_w_scl(uint8_t bitvalue)
{
//Delay_us();
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)bitvalue);
Delay_us(10); //方便修改延时
}
//写 sda总线
void myiic_w_sda(uint8_t bitvalue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)bitvalue);
Delay_us(10); //方便修改延时
}
//读 sda总线
uint8_t myiic_r_sda(void)
{
uint8_t bitvalue;
bitvalue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
Delay_us(10); //方便修改延时
return bitvalue;
}
1、引脚配置
void myiic_init(void)
{
//开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//配置GPIO SCL,SDA初始化为开漏输出,
// 开漏模式既可以输出,又可以输入。 输入时,先输出1,再直接读取输入寄存器
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_10MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//SCL和SDA置为高电平, 释放总线, 此时IIC总线处于空闲状态
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
2、起始信号
//起始信号需要注意:先拉高sda,再拉高scl, 防止和停止信号冲突
void myiic_start(void)
{
myiic_w_sda(1);
myiic_w_scl(1);
myiic_w_sda(0);
myiic_w_scl(0); //结束再将时钟线拉低,方便下次使用
}
3、停止信号
//停止信号:先拉低数据线,原理同上
void myiic_stop(void)
{
myiic_w_sda(0);
myiic_w_scl(1);
myiic_w_sda(1);
}
4、发送一个字节
void myiic_sendbyte(uint8_t byte)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
//设置最高位数据
myiic_w_sda(byte & (0x80 >> i));
//释放SCL (释放SCL之后,从机就会立刻将SDA的数据读走)
myiic_w_scl(1);
//拉低SCL 驱动时钟走一个脉冲(可以继续放下一位数据)
myiic_w_scl(0);
}
}
5、接收一个字节
//接收一个字节:SCL低电平,从机需要将数据放到SDA上,为了防止主机干扰从机写入数据,主机需要先释放SDA(切换为输入模式)
uint8_t myiic_ReceiveByte(void)
{
uint8_t i, byte = 0x00;
//主机释放SDA 从机使用,将数据放在SDA上
myiic_w_sda(1);
for(i = 0; i < 8; i++)
{
//主机释放SCL 读取数据
myiic_w_scl(1);
//如果是 1 置1,如果是 0 ,不用管,因为 byte初始化为 0x00;
if(myiic_r_sda() == 1)
{
byte |= 0x80 >> i;
}
//读取一位数据后,拉低SCL,从机放下一位数据
myiic_w_scl(0);
}
return byte;
}
6、发送应答
void myiic_SendAck(uint8_t ackBit)
{
//函数进来时SCL低电平,主机将数据放在SDA上
myiic_w_sda(ackBit);
//SCL高电平,从机读取应答
myiic_w_scl(1);
//SCL低电平,进入下一个时序单元
myiic_w_scl(0);
}
7、接收应答
uint8_t myiic_ReceiveAck(void)
{
uint8_t ackBit;
//函数进入时SCL低电平,主机释放SDA,防止从机干扰,同时从机将应答位放在SDA上
myiic_w_sda(1);
//SCL高电平,主机读取应答位
myiic_w_scl(1);
ackBit = myiic_r_sda();
//SCL低电平,进入下一个时序单元
myiic_w_scl(0);
return ackBit;
}
二、读写MPU6050
MPU6050命令的宏定义
//0xD0: 1101 0000(1101 000 是MPU6050的从机地址, 最后一位读写位,0 代表写入操作)
#define MPU6050_ADDRESS 0xD0 //写地址 读地址,需要或上 0x01
#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
1、MPU6050初始化
void MPU6050_Init(void)
{
myiic_init();
//对MPU6050硬件电路进行初始化配置
//芯片的写寄存器功能,需要解除芯片的睡眠模式,否则写入是无效的
//睡眠模式 是电源管理寄存器1 的SLEEP控制的,写入0x00,解除睡眠模式,该寄存器地址为 0x6B;
//解除睡眠,选择陀螺仪时钟
MPU6050_Write_Reg(MPU6050_PWR_MGMT_1, 0x01);
//6 轴均不待机
MPU6050_Write_Reg(MPU6050_PWR_MGMT_2, 0x00);
//采样分频为 10
MPU6050_Write_Reg(MPU6050_SMPLRT_DIV, 0x09);
//滤波参数给最大
MPU6050_Write_Reg(MPU6050_CONFIG, 0x06);
//陀螺仪和下面加速度计都选择最大量程
MPU6050_Write_Reg(MPU6050_GYRO_CONFIG, 0x18);
MPU6050_Write_Reg(MPU6050_ACCEL_CONFIG, 0x18);
}
2、MPU6050指定地址写一个字节
void MPU6050_Write_Reg(uint8_t regAddress, uint8_t data)
{
myiic_start();
myiic_sendbyte(MPU6050_ADDRESS);
myiic_ReceiveAck();
myiic_sendbyte(regAddress);
myiic_ReceiveAck();
myiic_sendbyte(data);
myiic_ReceiveAck();
myiic_stop();
}
3、MPU6050指定地址读一个字节
uint8_t MPU6050_Read_Reg(uint8_t regAddress)
{
uint8_t data;
myiic_start();
myiic_sendbyte(MPU6050_ADDRESS);
myiic_ReceiveAck();
myiic_sendbyte(regAddress);
myiic_ReceiveAck();
myiic_start();
myiic_sendbyte(MPU6050_ADDRESS | 0x01);
myiic_ReceiveAck();
data = myiic_ReceiveByte();
myiic_SendAck(1);
myiic_stop();
return data;
}
4、获取ID号
uint8_t MPU6050_GetID(void)
{
return MPU6050_Read_Reg(MPU6050_WHO_AM_I);
}
5、获取加速度、陀螺仪
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_Read_Reg(MPU6050_ACCEL_XOUT_H);
dataL = MPU6050_Read_Reg(MPU6050_ACCEL_XOUT_L);
*accX = (dataH << 8) | dataL;
dataH = MPU6050_Read_Reg(MPU6050_ACCEL_YOUT_H);
dataL = MPU6050_Read_Reg(MPU6050_ACCEL_YOUT_L);
*accY = (dataH << 8) | dataL;
dataH = MPU6050_Read_Reg(MPU6050_ACCEL_ZOUT_H);
dataL = MPU6050_Read_Reg(MPU6050_ACCEL_ZOUT_L);
*accZ = (dataH << 8) | dataL;
dataH = MPU6050_Read_Reg(MPU6050_GYRO_XOUT_H);
dataL = MPU6050_Read_Reg(MPU6050_GYRO_XOUT_L);
*gyroX = (dataH << 8) | dataL;
dataH = MPU6050_Read_Reg(MPU6050_GYRO_YOUT_H);
dataL = MPU6050_Read_Reg(MPU6050_GYRO_YOUT_L);
*gyroY = (dataH << 8) | dataL;
dataH = MPU6050_Read_Reg(MPU6050_GYRO_ZOUT_H);
dataL = MPU6050_Read_Reg(MPU6050_GYRO_ZOUT_L);
*gyroZ = (dataH << 8) | dataL;
}