赞
踩
图片均来自江科大STM32教学PPT
实线代表主机控制,虚线代表从机控制
对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
主机发出了“读”的命令,随后数据传输方向就要反过来。从机发,主机读。要把SDA的控制权交给从机,主机调用接收一个字节的时序,进行接收操作。从机在SCL低电平写入SDA,主机在SCL高电平期间读取SDA。
✨ 【在读的时序中,IIC协议规定是,主机进行寻址时,一旦读(1)写(0)标志位给1,下一个字节就必须立马转为读的时序,所以主机还来不及指定,要读哪个寄存器,就得开始接收,所以这里就没有指定地址的环节,就需要用到当前地址指针】
对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
IIC.c
#include "stm32f10x.h" // Device header #include "Delay.h" // 用于将SCL信号线设置为指定值。GPIOB的Pin10被用来控制SCL信号线 void MyI2C_W_SCL(uint8_t BitValue) { GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue); Delay_us(10); } // 用于将SDA信号线设置为指定值。GPIOB的Pin11被用来控制SDA信号线。 void MyI2C_W_SDA(uint8_t BitValue) { GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue); Delay_us(10); } // 用于读取SDA信号线的值。GPIOB的Pin11被用来读取SDA信号线的值 uint8_t MyI2C_R_SDA(void) { uint8_t BitValue; BitValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11); Delay_us(10); return BitValue; } // 用于初始化IIC总线 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); // SCL和SDA都处于高电平,空闲状态 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)); MyI2C_W_SCL(1); MyI2C_W_SCL(0); } } uint8_t MyI2C_ReceiveByte(void) { uint8_t i, Byte = 0x00; // 声明变量i和Byte,Byte用于存储接收到的字节数据,初始值为0x00 MyI2C_W_SDA(1); // 设置SDA线为高电平,准备接收数据 for(i = 0; i < 8; i++) // 循环8次,接收8位数据 { MyI2C_W_SCL(1); // 设置SCL线为高电平,准备读取数据位 if(MyI2C_R_SDA() == 1) // 检查SDA线的电平,如果为高电平,则表示接收到的数据位为1 { Byte |= (0x80 >> i); // 使用位或操作将接收到的数据位存储到Byte中,右移操作(0x80 >> i)确定位的位置 } MyI2C_W_SCL(0); // 设置SCL线为低电平,准备接收下一个数据位 } return Byte; // 返回接收到的字节数据 } // 发送应答 void MyI2C_SendAck(uint8_t AckBit) { MyI2C_W_SDA(AckBit); // 设置SDA线的电平,用于发送ACK位(AckBit) MyI2C_W_SCL(1); // 设置SCL线为高电平,发送ACK信号 MyI2C_W_SCL(0); // 设置SCL线为低电平,完成ACK信号的发送 } // 接收应答 uint8_t MyI2C_ReceiveAck(void) { uint8_t AckBit; MyI2C_W_SDA(1); // 将SDA线设置为输入模式,准备接收ACK位 MyI2C_W_SCL(1); // 设置SCL线为高电平,准备读取ACK位的电平 AckBit = MyI2C_R_SDA(); // 读取SDA线的电平,获取ACK位的值 MyI2C_W_SCL(0); // 设置SCL线为低电平,完成ACK位的读取 return AckBit; // 返回接收到的ACK位 }
MPU6050.c
#include "stm32f10x.h" // Device header #include "MyI2C.h" #include "MPU6050_Reg.h" #define MPU6050_ADDRESS 0xD0 void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { // 启动I2C通信 MyI2C_Start(); // 发送设备地址,并等待应答 MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_ReceiveAck(); // 发送寄存器地址,并等待应答 MyI2C_SendByte(RegAddress); MyI2C_ReceiveAck(); // 发送数据,并等待应答 MyI2C_SendByte(Data); MyI2C_ReceiveAck(); // 停止I2C通信 MyI2C_Stop(); } uint8_t MPU6050_ReadReg(uint8_t RegAddress) { uint8_t Data; // 启动I2C通信 MyI2C_Start(); // 发送设备地址,并等待应答 MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_ReceiveAck(); // 发送寄存器地址,并等待应答 MyI2C_SendByte(RegAddress); MyI2C_ReceiveAck(); // 再次启动I2C通信 MyI2C_Start(); // 发送设备地址,并等待应答,设置为读模式 MyI2C_SendByte(MPU6050_ADDRESS | 0x01); MyI2C_ReceiveAck(); // 读取数据 Data = MyI2C_ReceiveByte(); // 发送应答位 MyI2C_SendAck(1); // 停止I2C通信 MyI2C_Stop(); return Data; } void MPU6050_Init(void) { MyI2C_Init(); // 配置电源管理寄存器 MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0X01); // 选择陀螺仪时钟 MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0X00); // 这部分参考MPU6050寄存器映射手册 MPU6050_WriteReg(MPU6050_SMPLRT_DIV ,0X09); // 配置采样率分频 MPU6050_WriteReg(MPU6050_CONFIG ,0X06); // 配置寄存器 MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0X18); // 陀螺仪配置寄存器 MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0X18); // 加速度计寄存器配置 } // 获取MPU6050的ID号 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; // 读取加速度计X轴数据 DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); *AccX = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的加速度计X轴数据 // 读取加速度计Y轴数据 DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); *AccY = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的加速度计Y轴数据 // 读取加速度计Z轴数据 DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); *AccZ = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的加速度计Z轴数据 // 读取陀螺仪X轴数据 DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); *GyroX = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的陀螺仪X轴数据 // 读取陀螺仪Y轴数据 DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); *GyroY = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的陀螺仪Y轴数据 // 读取陀螺仪Z轴数据 DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); *GyroZ = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的陀螺仪Z轴数据 }
STM32F103C8T6 硬件I2C资源:I2C1、I2C2
#include "stm32f10x.h" // Device header #include "MPU6050_Reg.h" #define MPU6050_ADDRESS 0xD0 // 防止超时 void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) { uint32_t Timeout; Timeout=10000; while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) { Timeout--; if(Timeout==0) { break; } } } void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { // 在I2C总线上生成起始条件 I2C_GenerateSTART(I2C2, ENABLE); // 等待起始条件发送完成并且主模式被选中 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); // 发送带有写方向的MPU6050设备的7位地址 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); // 等待地址发送完成并且发送器模式被选中 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); // 发送要写入的寄存器地址 I2C_SendData(I2C2, RegAddress); // 等待数据字节传输完成 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); // 发送要写入寄存器的数据 I2C_SendData(I2C2, Data); // 等待数据字节传输完成 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); // 在I2C总线上生成停止条件 I2C_GenerateSTOP(I2C2, ENABLE); } uint8_t MPU6050_ReadReg(uint8_t RegAddress) { uint8_t Data; // 在I2C总线上生成起始条件 I2C_GenerateSTART(I2C2, ENABLE); // 等待起始条件发送完成并且主模式被选中 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); // 发送带有写方向的MPU6050设备的7位地址 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); // 等待地址发送完成并且发送器模式被选中 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); // 发送要读取的寄存器地址 I2C_SendData(I2C2, RegAddress); // 等待数据字节传输完成 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); // 重新生成起始条件 I2C_GenerateSTART(I2C2, ENABLE); // 等待起始条件发送完成并且主模式被选中 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); // 发送带有读方向的MPU6050设备的7位地址 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver); // 等待地址发送完成并且接收器模式被选中 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); // 禁用应答 I2C_AcknowledgeConfig(I2C2, DISABLE); // 生成停止条件 I2C_GenerateSTOP(I2C2, ENABLE); // 等待数据字节接收完成 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); // 读取接收到的数据 Data = I2C_ReceiveData(I2C2); // 启用应答 I2C_AcknowledgeConfig(I2C2, ENABLE); return Data; } void MPU6050_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); I2C_InitTypeDef I2C_InitStruct; I2C_InitStruct.I2C_Mode=I2C_Mode_I2C; I2C_InitStruct.I2C_ClockSpeed=50000; I2C_InitStruct.I2C_DutyCycle=I2C_DutyCycle_2; I2C_InitStruct.I2C_Ack=I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; I2C_InitStruct.I2C_OwnAddress1=0X00; I2C_Init(I2C2,&I2C_InitStruct); I2C_Cmd(I2C2,ENABLE); // 配置电源管理寄存器 MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0X01); // 选择陀螺仪时钟 MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0X00); // 这部分参考MPU6050寄存器映射手册 MPU6050_WriteReg(MPU6050_SMPLRT_DIV ,0X09); // 配置采样率分频 MPU6050_WriteReg(MPU6050_CONFIG ,0X06); // 配置寄存器 MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0X18); // 陀螺仪配置寄存器 MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0X18); // 加速度计寄存器配置 } // 获取MPU6050的ID号 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; // 读取加速度计X轴数据 DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); *AccX = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的加速度计X轴数据 // 读取加速度计Y轴数据 DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); *AccY = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的加速度计Y轴数据 // 读取加速度计Z轴数据 DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); *AccZ = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的加速度计Z轴数据 // 读取陀螺仪X轴数据 DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); *GyroX = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的陀螺仪X轴数据 // 读取陀螺仪Y轴数据 DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); *GyroY = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的陀螺仪Y轴数据 // 读取陀螺仪Z轴数据 DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); *GyroZ = (DataH << 8) | DataL; // 组合高位和低位数据得到16位的陀螺仪Z轴数据 }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。