赞
踩
注意:
有SCL和SDA本来需要外挂上拉电阻,但是由于STM32内部内置了,就不需要了。
由于模块内部内置了下拉电阻,所以引脚悬空就相当于是接地。
发送一个字节的编码步骤:
SCL低电平:写入数据
SCL高电平:保证数据稳定
高位先行,所以变换数据的时候先放最高位
起始位后,SCL和SDA均进入低电平
发送一个字节,即SCL低电平,写入第一个字节
写完一位,释放SCL,将SDA拉下来,
//发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i; //1000 0000
for(i= 0;i<8;i++)
{
MyI2C_W_SDA(Byte & (0x80 >> i) );//高位先行,首先趁着SCL低电平,将byte的最高位放到SDA上。
//结果不是 0x00 就是0x80
MyI2C_W_SCL(1); //SCL为高电平 //从机读取数据
MyI2C_W_SCL(0);//SCL为低电平 //主机下一次写数据
}
}
1.将SDA,SCL已经被拉到高电平
2.从机读取SDA上的数据
3.读完后,将SCL置为低电平,即等待主机继续写数据
返回值为1,表示高电平
返回值为0,表示低电平
//接收一个字节 uint8_t MyI2C_RecByte(void) //主机读数据,从机写数据 { uint8_t i; uint8_t Byte = 0x00; MyI2C_W_SDA(1); //主机将SDA释放 ,为高电平,切换为输入模式 for(i = 0;i<8;i++) { MyI2C_W_SCL(1); //SCL为高电平开始读SDA if(MyI2C_R_SDA()== 1){Byte |= 0x80>>i;} //按位或 ,byte =1 ,则 byte最高位置为1 //如果if不成立则写入0 MyI2C_W_SCL(0); } return Byte; }
//发送应答位 void MyI2C_SendACK(uint8_t ACKBit) { MyI2C_W_SDA(ACKBit); MyI2C_W_SCL(1); MyI2C_W_SCL(0); } //接收应答位 uint8_t MyI2C_RecvACK(void) //主机读数据,从机写数据 { uint8_t ACKBit; MyI2C_W_SDA(1); //即使主机释放SDA为高电平,而从机写数据到SDA MyI2C_W_SCL(1); //SCL为高电平开始读SDA ACKBit = MyI2C_R_SDA(); //读取的 MyI2C_W_SCL(0); return ACKBit; }
注意:
芯片上电后默认的时睡眠模式,睡眠模式写入寄存器时无效的
#include "stm32f10x.h" // Device header #include "Delay.h" //利用函数将写数据为封装起来 void MyI2C_W_SCL(uint8_t BitValue) { GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue); Delay_us(10); } void MyI2C_W_SDA(uint8_t BitValue) { GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue); Delay_us(10); } uint8_t MyI2C_R_SDA(void) { uint8_t BitValue; BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11); Delay_us(10); return BitValue; } //因为STM32中读和写不是同一个寄存器 //#define SCL_PORT GPIOB 由于I2C对频率有要求,因此我这里采用直接定义一个函数,来设置端口 //#define SCL_PIN GPIO_pin_10 //#define MyI2C_setBit() GPIO_SetBits(GPIOA, GPIO_Pin_10|GPIO_Pin_11); void MYI2C_Init(void) { //将SCL和SDA配置位开漏输出模式 //将SCL和SDA均置为高电平 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出模式 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStruct); GPIO_SetBits(GPIOB, GPIO_Pin_10|GPIO_Pin_11); } //起始条件 void MyI2C_Start(void) { MyI2C_W_SCL(1); //将SCL和SDA都释放 MyI2C_W_SDA(1); MyI2C_W_SDA(0); //拉低SDA MyI2C_W_SCL(0); //拉低SCL } //终止条件 void MyI2C_Stop(void) { MyI2C_W_SDA(0); //先拉低SDA MyI2C_W_SCL(1); MyI2C_W_SDA(1); } //发送一个字节 void MyI2C_SendByte(uint8_t Byte) { uint8_t i; //1000 0000 for(i= 0;i<8;i++) { MyI2C_W_SDA(Byte & (0x80 >> i) );//高位先行,首先趁着SCL低电平,将byte的最高位放到SDA上。 //结果不是 0x00 就是0x80 MyI2C_W_SCL(1); //SCL为高电平 //从机读取数据 MyI2C_W_SCL(0);//SCL为低电平 //主机下一次写数据 } } //接收一个字节 uint8_t MyI2C_RecByte(void) //主机读数据,从机写数据 { uint8_t i; uint8_t Byte = 0x00; MyI2C_W_SDA(1); //主机将SDA释放 ,为高电平,切换为输入模式 for(i = 0;i<8;i++) { MyI2C_W_SCL(1); //SCL为高电平开始读SDA if(MyI2C_R_SDA()== 1){Byte |= 0x80>>i;} //按位或 ,byte =1 ,则 byte最高位置为1 //如果if不成立则写入0 MyI2C_W_SCL(0); } return Byte; } //发送应答位 void MyI2C_SendACK(uint8_t ACKBit) { MyI2C_W_SDA(ACKBit); MyI2C_W_SCL(1); MyI2C_W_SCL(0); } //接收应答位 uint8_t MyI2C_RecvACK(void) //主机读数据,从机写数据 { uint8_t ACKBit; MyI2C_W_SDA(1); //即使主机释放SDA为高电平,而从机写数据到SDA MyI2C_W_SCL(1); //SCL为高电平开始读SDA ACKBit = MyI2C_R_SDA(); //读取的 MyI2C_W_SCL(0); return ACKBit; }
#include "stm32f10x.h" #include "MyI2C.h" #include "MPU6050.h" #include "MPU6050_Reg.h" // Device header #define MPU6050_ADDRESS 0xD0 //从机地址 //指定地址写 void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data) { MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS);//发送从机地址 MyI2C_RecvACK(); MyI2C_SendByte(RegAddress); MyI2C_RecvACK(); MyI2C_SendByte(Data); MyI2C_RecvACK(); MyI2C_Stop(); } //指定地址读 uint8_t MPU6050_ReadReg(uint8_t RegAddress) { MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_RecvACK(); MyI2C_SendByte(RegAddress); MyI2C_RecvACK(); MyI2C_Start(); //重新指定读写 MyI2C_SendByte(MPU6050_ADDRESS | 0x01);//变成0xD1,读写位变1 MyI2C_RecvACK(); uint8_t Byte; Byte = MyI2C_RecByte();//返回值位接收到的数据 MyI2C_SendACK(1);//无需应答 MyI2C_Stop(); return Byte; } void MPU6050_Init(void) { MYI2C_Init(); MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);//选择X轴陀螺仪时钟 MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);//10分频 MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09); 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) { //x,y,z加速度值和陀螺仪值 uint8_t DataH, DataL; DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);//读取数据 *AccX = (DataH << 8) | DataL; //加速度x轴,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; }
将寄存器地址存起来。然后就可以直接操作了。
读取的数据/32768=x/满量程,解得X,就是具体的角速度的值
#ifndef __MPU6050_REG_H #define __MPU6050_REG_H //MPU6050各模块的寄存器地址 #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
可以将硬件2C的引脚却配置再芯片对应2C引脚上。就可以选泽软件2C和硬件2C
软件12C和硬件2C的区别:
软件都是通过程序手动反转引脚的电平
而硬件就帮我们把这些事都干了。
第一步:配置12C外设,对12C外设进行初始化
第二步:控制外设电路,实现指定地址写的时序
第三步:控制外设电路,实现指定地址读
当时钟频率设置为101kHz。进入高速模式.
占空比设置为2:1低电平:高电平=2:1
占空比的作用;给低电平多分配一些资源,
因为低电平写数据,也就是数据变化,而高电平读数据。
所以给数据变化的时间延长。高电平才能读取数据。
(一般读取速度都大于写入速度)
软件2C起始函数,发送字节的函数,内部都是延迟函数。都是阻塞式的,当函数执行完毕对应的波形也会
生成。
而硬件2C是非阻塞式,只管给寄存器的位置置1,或者只在DR写数据,就结束就退出函数
所以对于这种函数,就必须在函数执行完以后,需要等待对应的标志位状态,确保函数都执行到位
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { I2C_GenerateSTART(I2C2, ENABLE);//生成起始条件,实际是在操作CR1寄存器,置1,产生起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件的到来 //发送从机地址,接收应答,直接向DR寄存器写入一个字节即可 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//I2C2、从机地址、发送(即最低地址清0) 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_TRANSMITTED);//字节已经发送完毕transmitted I2C_GenerateSTOP(I2C2, ENABLE);//生成终止条件 }
//指定地址读 uint8_t MPU6050_ReadReg(uint8_t RegAddress) { // I2C_GenerateSTART(I2C2,ENABLE); MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //EV5:已发送起始位 I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); //发送的地址 MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //EV61:地址发送结束。 I2C_SendData(I2C2,RegAddress); //指定寄存器地址 MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING); //EV7:数据寄存器非空。数据已经移入数据寄存器 I2C_GenerateSTART(I2C2,ENABLE);//重复起始条件 MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//EV5事件 已发送起始位 I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver); //发送接收的地址 MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //EV6 //在发送最后一个字节之前,要提前给ACK置零和发Stop //如果只需要接收一个字节,则在EV6事件之后需要ACK置0且配置stop标志位,否则会多读取一个字节 //如果是接收多个字节则直接等待EV7事件,在EV7_1事件之前ACK置0且配置stop标志位为1*/ //如果要接收多个字节,将下面的四行套个循环体,在接收最后一个字节之前。只执行后面两行(if),如果执行到最后一个字节,那就4行全都执行。 I2C_AcknowledgeConfig(I2C2,DISABLE); //无应答 I2C_GenerateSTOP(I2C2,ENABLE); MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);//EV7:代表数已经移位到数据寄存器 Data = I2C_ReceiveData(I2C2); I2C_AcknowledgeConfig(I2C2,ENABLE); return Data; }
#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_GenerateSTART(I2C2, ENABLE);//生成起始条件,实际是在操作CR1寄存器,置1,产生起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件的到来 //发送从机地址,接收应答,直接向DR寄存器写入一个字节即可 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//I2C2、从机地址、发送(即最低地址清0) 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_TRANSMITTED);//字节已经发送完毕transmitted I2C_GenerateSTOP(I2C2, ENABLE);//生成终止条件 } uint8_t MPU6050_ReadReg(uint8_t RegAddress) { uint8_t Data; I2C_GenerateSTART(I2C2, ENABLE); MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); 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); 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_InitStructure; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_ClockSpeed = 50000; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_OwnAddress1 = 0x68; I2C_Init(I2C2, &I2C_InitStructure); I2C_Cmd(I2C2, ENABLE); MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); 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); DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); *AccX = (DataH << 8) | DataL; 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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。