图文店做网站有用处吗,新闻稿,注册网站流程及费用,网站建设讯息#xff08;一#xff09;I2C硬件电路
stm32内部有I2C的硬件电路#xff0c;我们可以使用stm32的标准库函数来实现I2C#xff0c;这可以为我们减少对软件资源的占用
I2C硬件电路常用的标准库函数
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
/…一I2C硬件电路
stm32内部有I2C的硬件电路我们可以使用stm32的标准库函数来实现I2C这可以为我们减少对软件资源的占用
I2C硬件电路常用的标准库函数
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
//通过结构体初始化I2Cvoid I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);
//给初始化结构体赋默认值void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
//使能I2C初始化之后要使能其才开始工作void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
//开始I2C传输void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);
//结束I2C传输void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);
//发送一个字节数据uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
//接收一个字节数据void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
//发送7位地址在最开始I2C寻址调用这个函数ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
//接收事件在使用硬件开始传输或发送数据等操作之后都会有对应的事件需要接收判断后再执行下一个操作void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
//设置响应为相当于软件实现I2C中的ack
我们只要使用这些库函数按照其规定的流程进行数据的发送和接收即可不需要我们手动在软件中模拟I2C的高低电平跳变
二使用库函数实现I2C数据的发送与接收
1发送
I2C发送流程如图在开始传输完成后其会产生EV5事件我们在事件产生后即可发送地址位成功发送后会产生EV6事件接着发送寄存器地址因为这里使用的是一个寄存器和一个移位寄存器来发送数据所以我们在寄存器的数据写入到移位寄存器中正在发送EV8事件即可将新的数据写入寄存器在最后一个数据位我们没有数据写入寄存器且移位寄存器发送完毕会产生EV8_2事件表示发送完成然后即可停止传输 这里我们发送一个字节后的接收响应由硬件自动接收不需要我们手动接收响应
由于我们会频繁接收各种事件我们可以把接收事件的函数封装成一个简单的函数且让其超时退出不让程序卡死 接收事件函数
void mpu_wait_flag(int event)
{unsigned int time 10000;while (I2C_CheckEvent(I2C2, event) ! SUCCESS){time--;if (time 0){break;}}
}
按照流程我们可以这样编写发送函数
发送函数
void mpu_write(unsigned char address, unsigned char inf)
{I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_MODE_SELECT); //EV5I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Transmitter);mpu_wait_flag(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6I2C_SendData(I2C2, address);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTING); //EV8I2C_SendData(I2C2, inf);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2I2C_GenerateSTOP(I2C2, ENABLE);
}
第一行我们先开始传输第二行我们接收EV5事件第三行我们发送7位地址并且第三个参数为读模式还是写模式我们选择写模式第四行我们等待EV6事件紧接着第五行发送要读取的寄存器地址第六行等待寄存器地址移动到移位寄存器正在发送事件EV8最后第七行写要发送的数据等待发送完成事件最后停止传输
2接收
接收数据的流程如图所示
这里一样我们在开始之后会产生EV5事件在传递MPU地址后会产生EV6事件接着读取数据这里由于我们在读取事件之后硬件电路会立刻把我们之前预设的ack响应发给从机因此我们需要在读取最后一个数据之前把ack置0我们这里只读取一个字节的数据也就是要在发送MPU地址之后读取数据之前先把ack置0对应代码如下
unsigned char mpu_read(unsigned char address)
{unsigned char inf;I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Transmitter);mpu_wait_flag(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, address);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Receiver);mpu_wait_flag(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);I2C_AcknowledgeConfig(I2C2, DISABLE); //ack0mpu_wait_flag(I2C_EVENT_MASTER_BYTE_RECEIVED);inf I2C_ReceiveData(I2C2);I2C_GenerateSTOP(I2C2, ENABLE);I2C_AcknowledgeConfig(I2C2, ENABLE);return inf;
}
第一段对应我们软件I2C的给寄存器地址操作我们仍需要先写入寄存器地址然后在第二段我们重新开始传输接收EV5事件后发送MPU地址这里第三个参数选择读模式等待地址发送完成之后值得注意的是我们先执行的不是读取数据而是先把应答位置非应答再去读取数据读取完成后我们即可停止传输
三MPU-6050
经过上面两个函数的编写我们已经可以调用上面两个函数来实现在指定外设寄存器上写数据和读数据的操作我们即可进行MPU的初始化和读取转换数据所在的寄存器这里就和之前使用软件模拟I2C的代码没有什么区别因为软件我们只是模拟了I2C的波形最终也封装成了写字节和读字节的函数
1初始化
1打开时钟和初始化GPIO
这里因为我们使用内部的I2C硬件电路因此我们打开时钟我们要把I2C的时钟打开stm32F03C8有两个I2C这里使用的是I2C2对应的SCL、SDA引脚为PB10、PB11 void mpu_rcc_init()
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
}初始化GPIO这里要用复用开漏输出
void mpu_gpio_init()
{GPIO_InitTypeDef gpio_init;gpio_init.GPIO_Pin SCL | SDA;gpio_init.GPIO_Mode GPIO_Mode_AF_OD;gpio_init.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOB, gpio_init);
}
2I2C初始化
使用I2C硬件电路还要对I2C进行初始化可以看下初始化结构体 1第一个参数是选择时钟速度数值越大电平翻转频率越高低于100KHz的是正常模式高于100KHz的是高速模式这里选择100KHz
2第二个参数是选择模式可以选泽I2C模式、SMBus设备模式或SMBus主控模式这里选择I2C模式
3第三个参数是选择高低电平的占空比这里只有在高速模式下有效有169和21两种可选这里不是高速模式可以随便选择
4第四个参数是选择自身地址自身可以作为从机设置自身地址让其他主机呼叫随便选择只要不和MPU或其他I2C上的地址冲突即可
5第五个参数是应答位设置可以选择应答和非应答给应答
6第六个参数是选择7位地址模式还是10位地址模式这里选择7为地址模式
这样我们就可以初始化I2C了
void mpu_i2c_init()
{I2C_InitTypeDef i2c_init;i2c_init.I2C_ClockSpeed 100000;i2c_init.I2C_Mode I2C_Mode_I2C;i2c_init.I2C_DutyCycle I2C_DutyCycle_2;i2c_init.I2C_OwnAddress1 0x00;i2c_init.I2C_Ack I2C_Ack_Enable;i2c_init.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit;I2C_Init(I2C2, i2c_init);I2C_Cmd(I2C2, ENABLE);
}
最后不要忘了使能I2C否则其不工作
3MPU初始化
我们把时钟、gpio、I2C的初始化都集成于此我们还要像软件那样配置寄存器来关闭MPU睡眠模式和选择时钟等功能具体代码如下
void mpu_init()
{mpu_rcc_init();mpu_gpio_init();mpu_i2c_init();mpu_write(0x6B, 0x01); //PWR_MGMT_1 - 0000 0001mpu_write(0x6C, 0x00); //PWR_MGMT_2 - 0000 0000}mpu_write(0x19, 0x09); //SMPLRT_DIV - 0000 1001mpu_write(0x1A, 0x06); //CONFIG - 0000 0110mpu_write(0x1B, 0x18); //GYRO_CONFIG - 0001 1000mpu_write(0x1C, 0x18); //ACCEL_CONFIG - 0001 1000
}
2读取参数
最后我们选择将x轴、y轴和z轴的加速度和角速度放在一个结构体中返回我们还要注意16位整数转为32位整数中的符号问题
结构体定义
typedef struct infs
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;
16位整数转32为整数
int mpu_16_to_32(int original)
{int final;if (original 0x8000){final original | 0xFFFF0000;}return final;
}
我们只要读取MPU对应数据的寄存器即可这里和软件模拟的操作一样
information mpu_get_inf()
{uint8_t inf_L;uint8_t inf_H;information infor;inf_H mpu_read(0x3B);inf_L mpu_read(0x3C);infor.x_acceleration mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x3D);inf_L mpu_read(0x3E);infor.y_acceleration mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x3F);inf_L mpu_read(0x40);infor.z_acceleration mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x43);inf_L mpu_read(0x44);infor.x_angular_velocity mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x45); inf_L mpu_read(0x46); infor.y_angular_velocity mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x47); inf_L mpu_read(0x48); infor.z_angular_velocity mpu_16_to_32((inf_H8) | inf_L);return infor;
}3封装与声明
最后的.c 和 .h文件如下
#include stm32f10x.h // Device header#define SCL GPIO_Pin_10
#define SDA GPIO_Pin_11#define mpu_address 0xD0void mpu_wait_flag(int event)
{unsigned int time 10000;while (I2C_CheckEvent(I2C2, event) ! SUCCESS){time--;if (time 0){break;}}
}void mpu_write(unsigned char address, unsigned char inf)
{I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_MODE_SELECT); //EV5I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Transmitter);mpu_wait_flag(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6I2C_SendData(I2C2, address);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTING); //EV8I2C_SendData(I2C2, inf);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2I2C_GenerateSTOP(I2C2, ENABLE);
}unsigned char mpu_read(unsigned char address)
{unsigned char inf;I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Transmitter);mpu_wait_flag(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, address);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Receiver);mpu_wait_flag(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);I2C_AcknowledgeConfig(I2C2, DISABLE);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_RECEIVED);inf I2C_ReceiveData(I2C2);I2C_GenerateSTOP(I2C2, ENABLE);I2C_AcknowledgeConfig(I2C2, ENABLE);return inf;
}void mpu_rcc_init()
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
}void mpu_gpio_init()
{GPIO_InitTypeDef gpio_init;gpio_init.GPIO_Pin SCL | SDA;gpio_init.GPIO_Mode GPIO_Mode_AF_OD;gpio_init.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOB, gpio_init);
}void mpu_i2c_init()
{I2C_InitTypeDef i2c_init;i2c_init.I2C_ClockSpeed 100000;i2c_init.I2C_Mode I2C_Mode_I2C;i2c_init.I2C_DutyCycle I2C_DutyCycle_2;i2c_init.I2C_OwnAddress1 0x00;i2c_init.I2C_Ack I2C_Ack_Enable;i2c_init.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit;I2C_Init(I2C2, i2c_init);I2C_Cmd(I2C2, ENABLE);
}void mpu_init()
{mpu_rcc_init();mpu_gpio_init();mpu_i2c_init();mpu_write(0x6B, 0x01); //PWR_MGMT_1 - 0000 0001mpu_write(0x6C, 0x00); //PWR_MGMT_2 - 0000 0000}mpu_write(0x19, 0x09); //SMPLRT_DIV - 0000 1001mpu_write(0x1A, 0x06); //CONFIG - 0000 0110mpu_write(0x1B, 0x18); //GYRO_CONFIG - 0001 1000mpu_write(0x1C, 0x18); //ACCEL_CONFIG - 0001 1000
}typedef struct infs
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;int mpu_16_to_32(int original)
{int final;if (original 0x8000){final original | 0xFFFF0000;}return final;
}information mpu_get_inf()
{uint8_t inf_L;uint8_t inf_H;information infor;inf_H mpu_read(0x3B);inf_L mpu_read(0x3C);infor.x_acceleration mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x3D);inf_L mpu_read(0x3E);infor.y_acceleration mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x3F);inf_L mpu_read(0x40);infor.z_acceleration mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x43);inf_L mpu_read(0x44);infor.x_angular_velocity mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x45); inf_L mpu_read(0x46); infor.y_angular_velocity mpu_16_to_32((inf_H8) | inf_L);inf_H mpu_read(0x47); inf_L mpu_read(0x48); infor.z_angular_velocity mpu_16_to_32((inf_H8) | inf_L);return infor;
}#ifndef __MPU_H__
#define __MPU_H__typedef struct infs
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;void mpu_init(void);
information mpu_get_inf(void);#endif
四主函数调用
我们只要在主函数开始时对MPU初始化然后在循环中调用读取寄存器值的函数即可和软件模拟实现的程序一样
#include stm32f10x.h // Device header
#include OLED.h
#include MPU.h
#include Delay.h
int main()
{mpu_init();OLED_Init();information inf;while(1){inf mpu_get_inf();OLED_ShowSignedNum(1, 1, inf.x_acceleration, 5);OLED_ShowSignedNum(2, 1, inf.y_acceleration, 5);OLED_ShowSignedNum(3, 1, inf.z_acceleration, 5);OLED_ShowSignedNum(1, 8, inf.x_acceleration, 5);OLED_ShowSignedNum(2, 8, inf.y_acceleration, 5);OLED_ShowSignedNum(3, 8, inf.z_acceleration, 5);Delay_ms(500);}return 0;
}五总结
在学习软件模拟I2C后这里学习的是通过硬件I2C通过库函数来实现I2C通信读取MPU寄存器中的数据我们对I2C的理解更加深入对stm32的了解也更加具体