江科大学习笔记 _12.软件IIC
IIC硬件电路

IIC两条线(SCL/SDA)开漏输出 三极管导通时,接地,强下拉;截止时,浮空;
开漏加弱上拉模式
添加上拉电阻,阻值4.7KΩ
void MyIIC_W_SCL(uint8_t Bitvalue)//控制SCL电平 Bitvalue:0 or 1
{
GPIO_WriteBit(IIC_Port,IIC_SCL,(BitAction)Bitvalue);
Delay_us(10);
}
void MyIIC_W_SDA(uint8_t Bitvalue)//控制SDA电平 Bitvalue:0 or 1
{
GPIO_WriteBit(IIC_Port,IIC_SDA,(BitAction)Bitvalue);
Delay_us(10);
}
uint8_t MyIIC_R_SDA(void)//读SDA电平 返回:0 or 1
{
uint8_t Bitvalue;
Bitvalue = GPIO_ReadInputDataBit(IIC_Port,IIC_SDA);
Delay_us(10);
return Bitvalue;
}
IIC初始化
//初始化IIC:配置两个GPIO端口,开漏输出
void MyIIC_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出
GPIO_InitStruct.GPIO_Pin = IIC_SCL|IIC_SDA;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_Port,&GPIO_InitStruct);
GPIO_SetBits(IIC_Port,IIC_SCL|IIC_SDA);//置高电平,IIC中的空闲状态
}
IIC起始、结束

类型串口中的起始位和停止位
void MyIIC_Start(void)
{
MyIIC_W_SDA(1);
MyIIC_W_SCL(1);
MyIIC_W_SDA(0);
MyIIC_W_SCL(0);
}
void MyIIC_Stop(void)
{
MyIIC_W_SDA(0);
//MyIIC_W_SCL(0);
MyIIC_W_SCL(1);
MyIIC_W_SDA(1);
}
SCL全程由主机控制
主机发送数据:
SCL低电平时,主机放置数据位到SDA线上,然后拉高SCL;经过一段时间(等待从机读取),继续拉低SCL传输下一位数据,重复8次即为一个字节。
从机在SCL高电平时(上升沿)读取SDA电平(数据);

低电平主机放数据,高电平从机读数据
void MyIIC_SendByte(uint8_t Byte)
{
int8_t i;
//写SDA时 默认SCL低电平
for(i = 7; i >= 0; i--)
{
//一个字节分为 8位,从高到低一次发送
MyIIC_W_SDA(Byte & (1 << i));
MyIIC_W_SCL(1);//从机在SCL高电平时读走这个电平
MyIIC_W_SCL(0);//拉低再写电平
}
}
主机接收数据:

主机需要释放SDA线的控制权,将控制权交给从机
在SCL低电平期间,从机将数据位放在SDA线上,SCL高电平时,主机读取SDA数据(电平)
低电平从机放数据,高电平主机读数据
uint8_t MyIIC_ReceiveByte(void)
{
uint8_t Byte = 0x00;
int8_t i;
//主机读SDA时,默认SCL低电平
MyIIC_W_SDA(1);//释放SDA
for(i = 7; i >= 0; i--)
{
//这时从机写SDA电平
MyIIC_W_SCL(1);//SCL置高,主机读
if(MyIIC_R_SDA() == 1) {Byte |= (1 << i);}
MyIIC_W_SCL(0);//读取完之后,继续把SCL拉低,等待从机写
}
return Byte;
}
应答机制

和发送一位数据的机制相同;
主机接收完一个字节,发送一个应答位,0为应答,1为非应答
void MyIIC_SendAck(uint8_t AckBit)
{
MyIIC_W_SDA(AckBit);
MyIIC_W_SCL(1);//从机在SCL高点平时读走这个电平
MyIIC_W_SCL(0);//拉低再写电平
}
主机接收应答
主机发送完一个字节,接收一个应答位,0为应答,1为非应答
(判断从机是否应答)(需要释放SDA控制权给从机)
uint8_t MyIIC_ReceiveAck(void)
{
uint8_t AckByte;
MyIIC_W_SDA(1);//释放SDA
//这时从机写SDA电平
MyIIC_W_SCL(1);//SCL置高,主机读
AckByte = MyIIC_R_SDA();
MyIIC_W_SCL(0);//读取完之后,继续把SCL拉低,等待从机写
return AckByte;
}


S:起始 从机地址7位 + 读写1位 应答位
读取位:(0写入/1读入)
#define MPU6050_ADDRESS 0XD0 //芯片地址
//MPU6050 在寄存器地址上写数据时序
void MPU6050_WriteReg(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();
}

较少用,因为不能指定读地址,只能根据寄存器地址指针读数据


看图

先起始,第一个字节为地址(7位)加写(1位),一个应答位 ,
再发一个寄存器地址,寄存器地址指针这时指向了这个地址
重新起始,读当前位置,SDA交给从机写,应答,停止
uint8_t MPU6050_ReadReg(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);//只读一个字节,不给应答
//如果要读取多个字节 for循环32 33 行,最后一个字节应答1,之前应答0
MyIIC_Stop();
return Data;
}
MPU6050参数

void MPU6050_Init()
{
MyIIC_Init();
//以下配置需要看芯片的寄存器手册
MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//电源管理寄存器1,解除睡眠,选择X轴陀螺仪时钟
MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//电源管理寄存器2,6轴均不待机
MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//时钟分频 0x09 10分频
MPU6050_WriteReg(MPU6050_CONFIG, 0x06); //数字低通滤波器 , 最大滤波参数
MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//陀螺仪配置寄存器 ,最大量程
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//加速度计配置寄存器 , 最大量程
}
获取数据全部封装在这个函数中
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);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
*AccX = (DataH << 8) | DataL;//uint8_t 左移8位 会自动类型转换 为uint16
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;
}
uint8_t MPU6050_GetID(void)
{
return (MPU6050_ReadReg(MPU6050_WHO_AM_I));
}

