IIC特点:

同步串行半双工通信总线

IIC有一个弱上拉电阻,在主机和从机都没有传输数据下拉时,总线会自动上拉

SCL在低电平期间,改变SDA的值来上传数据,方便SCL电平上升时进行数据读取

SCL在高电平期间,不能改变SDA的值,若改变,SDA高到低为起始信号,低到高为终止信号

IIC配置步骤

1.使能SCL和SDA对应时钟_HAL_RCC_GPIOB_CLK_ENABLE()

2.设置GPIO工作模式HAL_GPIO_Init()

3.编写基本信号起始send ack 停止send nak 应答wait ack

4.编写读和写函数 iic_read_byte iic_send_byte

软件驱动外设步骤

1.初始化IIC接口

2.编写写入/读取一个字节数据的函数

3.编写连续读和连续写函数

iic代码

//myiic.c#include "./BSP/IIC/myiic.h"#include "./SYSTEM/delay/delay.h"//初始化IICvoid iic_init(void){GPIO_InitTypeDef gpio_init_struct;IIC_SCL_GPIO_CLK_ENABLE();/* SCL引脚时钟使能 */IIC_SDA_GPIO_CLK_ENABLE();/* SDA引脚时钟使能 */gpio_init_struct.Pin = IIC_SCL_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;/* 推挽输出 */gpio_init_struct.Pull = GPIO_PULLUP;/* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 快速 */HAL_GPIO_Init(IIC_SCL_GPIO_PORT, &gpio_init_struct);/* SCL */gpio_init_struct.Pin = IIC_SDA_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;/* 开漏输出 */HAL_GPIO_Init(IIC_SDA_GPIO_PORT, &gpio_init_struct);/* SDA *//* SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */iic_stop(); /* 停止总线上所有设备 */}//IIC延时函数,给芯片反应时间static void iic_delay(void){delay_us(2);/* 2us的延时, 读写速度在250Khz以内 */}//编写时序,产生IIC起始信号void iic_start(void){IIC_SDA(1);IIC_SCL(1);iic_delay();IIC_SDA(0); /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */iic_delay();IIC_SCL(0); /* 下拉I2C总线,准备发送或接收数据 */iic_delay();}//编写时序,产生IIC结束信号void iic_stop(void){IIC_SDA(0); /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */iic_delay();IIC_SCL(1);iic_delay();IIC_SDA(1); /* 发送I2C总线结束信号 */iic_delay();}//接收应答信号,1为接收失败,0为接收成功uint8_t iic_wait_ack(void){uint8_t waittime = 0;uint8_t rack = 0;IIC_SDA(1); /* 主机释放SDA线(此时外部器件可以拉低SDA线) */iic_delay();IIC_SCL(1); /* SCL=1, 此时从机可以返回ACK应答信号 */iic_delay();while (IIC_READ_SDA)/* 等待应答 */{waittime++;if (waittime > 250){iic_stop();rack = 1;break;}}IIC_SCL(0); /* SCL=0, 结束ACK检查 */iic_delay();return rack;}/** * @brief 从机产生ACK应答 */void iic_ack(void){IIC_SDA(0); /* SCL = 1 时 SDA = 0,表示应答 */iic_delay();IIC_SCL(1);iic_delay();IIC_SCL(0); /* 产生下一个时钟 */iic_delay();IIC_SDA(1); /* 释放SDA线 */iic_delay();}/** * @brief 不产生ACK应答 */void iic_nack(void){IIC_SDA(1); /* SCL = 1时 SDA = 1,表示不应答 */iic_delay();IIC_SCL(1);iic_delay();IIC_SCL(0); /* 产生下一个时钟 */iic_delay();}/** * @brief IIC发送一个字节 */void iic_send_byte(uint8_t data){uint8_t t;for (t = 0; t > t));/* 高位先发送,发完后右移 */iic_delay();IIC_SCL(1);iic_delay();IIC_SCL(0);}IIC_SDA(1); /* 发送完成, 主机释放SDA线 */}/** * @brief IIC读取一个字节 * @param ack:ack=1时,发送ack; ack=0时,发送nack * @retval接收到的数据 */uint8_t iic_read_byte(uint8_t ack){uint8_t i, receive = 0;IIC_SDA(1);for (i = 0; i > i);}//SDA为1时,读取当前这位,为0时,不读取因为本身该位为0IIC_SCL(0);}if (!ack){iic_nack(); /* 发送nACK */}else{iic_ack();/* 发送ACK */}return receive;}//myiic.h#ifndef __MYIIC_H#define __MYIIC_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************//* 引脚 定义 */#define IIC_SCL_GPIO_PORT GPIOB#define IIC_SCL_GPIO_PINGPIO_PIN_8#define IIC_SCL_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */#define IIC_SDA_GPIO_PORT GPIOB#define IIC_SDA_GPIO_PINGPIO_PIN_9#define IIC_SDA_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 *//******************************************************************************************//* IO操作 */#define IIC_SCL(x)do{ x " />//MPU6050.c#include "./BSP/IIC/myiic.h"#include "./SYSTEM/delay/delay.h"#include "./BSP/MPU6050/MPU6050.h"#define MPU6050_ADDRESS 0xD0void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)//向外设写数据{iic_start();//开始信号iic_send_byte(MPU6050_ADDRESS);//向该外设地址发送数据iic_wait_ack(); //接收应答iic_send_byte(RegAddress);//发送目标寄存器地址iic_wait_ack();iic_send_byte(Data);//向该地址发送数据,这里是发送一个数据iic_wait_ack();/*for(int i = 0;i<8; i++)//发送多个字节数据{iic_send_byte(Data);iic_wait_ack();}*/iic_stop(); //终止信号}uint8_t MPU6050_ReadReg(uint8_t RegAddress)//从外设读取数据{uint*_t Data;iic_start();//开始信号iic_send_byte(MPU6050_ADDRESS);//向该外设地址发送数据iic_wait_ack(); //接收应答iic_send_byte(RegAddress);//发送目标寄存器地址iic_wait_ack();iic_start();//重新起始iic_send_byte(MPU6050_ADDRESS | 0x01) //将最后一位变1,进行从机的读取数据iic_wait_ack();Data = iic_read_byte(); //读取数据iic_nack(); //因为读取一个字节,所以不用产生应答iic_stop();/*for(int i = 0;i AccX = (DataH <AccY = (DataH <AccZ = (DataH <GyroX = (DataH <GyroY = (DataH <GyroZ = (DataH << 8) | DataL;}uint8_t MPU6050_GetID(void) //获取芯片ID号{return MPU6050_ReadReg(MPU6050_WHO_AM_I);}//MPU6050.h#ifndef __MPU6050_H#define __MPU6050_H#include "./SYSTEM/sys/sys.h"typedef struct MPU6050_Data{uint16_t AccX;//加速度x轴数据uint16_t AccY;uint16_t AccZ;uint16_t GyroX; //陀螺仪x轴数据uint16_t GyroY;uint16_t GyroZ;}Data;void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data);uint8_t MPU6050_ReadReg(uint8_t RegAddress);void MPU6050_Init(void);uint8_t MPU6050_GetID(void);void MPU6050_GetData(struct MPU6050_Data* p);#define MPU6050_SMPLRT_DIV0x19//陀螺仪采样率,典型值:0x07(125Hz)#define MPU6050_CONFIG0x1A//低通滤波频率,典型值:0x06(5Hz)#define MPU6050_GYRO_CONFIG 0x1B//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)#define MPU6050_ACCEL_CONFIG0x1C//加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)#define MPU6050_ACCEL_XOUT_H0x3B#define MPU6050_ACCEL_XOUT_L0x3C#define MPU6050_ACCEL_YOUT_H0x3D#define MPU6050_ACCEL_YOUT_L0x3E#define MPU6050_ACCEL_ZOUT_H0x3F#define MPU6050_ACCEL_ZOUT_L0x40#define MPU6050_TEMP_OUT_H0x41#define MPU6050_TEMP_OUT_L0x42#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_10x6B//电源管理,典型值:0x00(正常启用)#define MPU6050_PWR_MGMT_20x6C//电源管理寄存器2#define MPU6050_WHO_AM_I0x75//IIC地址寄存器(默认数值0x68,只读)#endif

main.c

int main(void){Data Data1;int ID;OLED_Init();//显示屏初始化MPU6050_Init();//MPU6050初始化OLED_ShowString(1,1,"ID:");ID = MPU6050_GetID();//获取芯片IDwhile(1){MPU6050_GetData(&Data1);OLED_ShowSignedNum(2,1,Data1.AccX,5);//获取x的加速度值}}