STM32标准库学习笔记-11.I2C通信

参考教程:【STM32入门教程-2023版 细致讲解 中文字幕】

I2C通信
  • I2C(Inter IC Bus)是由Philips公司八十年代推出的一种通用数据总线
  • 两根通信线:SCL(Serial Clock)、SDA(Serial Data) 同步,半双工 带数据应答
  • 支持总线挂载多设备(一主多从、多主多从)

硬件电路
  • 所有I2C设备的SCL连在一起,SDA连在一起
  • 设备的SCL和SDA均要配置成开漏输出模式
  • SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

I2C时序基本单元
  • 起始条件:SCL高电平期间,SDA从高电平切换到低电平

  • 终止条件:SCL高电平期间,SDA从低电平切换到高电平

  • 发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节

  • 接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)

  • 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答

  • 接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

I2C时序
  • 指定地址写
  • 对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)

  • 当前地址读
  • 对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)

  • 指定地址读
  • 对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)

        Sr为重复起始

代码实现软件I2C通信
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

//标准库I2C函数以I2C开头,这里自己实现用IIC作为前缀

#define IIC_PORT GPIOB
#define IIC_SCL GPIO_Pin_10
#define IIC_SDA GPIO_Pin_11

void IIC_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Pin = IIC_SCL | IIC_SDA;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    
    //默认状态给高电平
    GPIO_SetBits(IIC_PORT,IIC_SCL | IIC_SDA);
}

void IIC_W_SCL(uint8_t BitVal)
{
    GPIO_WriteBit(IIC_PORT,IIC_SCL,(BitAction)BitVal);
    Delay_us(10);
}

void IIC_W_SDA(uint8_t BitVal)
{
    GPIO_WriteBit(IIC_PORT,IIC_SDA,(BitAction)BitVal);
    Delay_us(10);
}

uint8_t IIC_R_SDA(void)
{
    uint8_t Bitval = GPIO_ReadInputDataBit(IIC_PORT,IIC_SDA);
    Delay_us(10);
    return Bitval;
}

void IIC_Start(void)
{
    IIC_W_SDA(1);
    IIC_W_SCL(1);
    IIC_W_SDA(0);
    IIC_W_SCL(0);
}

void IIC_End(void)
{
    //其余的函数保证了SCL结束操作时是0
    IIC_W_SDA(0);
    IIC_W_SCL(1);
    IIC_W_SDA(1);
}

void IIC_SendByte(uint8_t Byte)
{
    uint8_t i;
    for(i = 0;i < 8;i++)
    {
        //00000001
        IIC_W_SDA(Byte & (0x80 >> i));
        IIC_W_SCL(1);
        IIC_W_SCL(0);
    }
}

void IIC_SendAck(BitAction Ack)
{
    IIC_W_SDA(Ack);
    IIC_W_SCL(1);
    IIC_W_SCL(0);
}

uint8_t IIC_ReceiveByte(void)
{
    uint8_t i;
    uint8_t Byte = 0x00;
    IIC_W_SDA(1);
    for(i = 0;i < 8;i++)
    {
        IIC_W_SCL(1);
        if(IIC_R_SDA())
        {
            Byte |= (0x80 >> i);
        }
        IIC_W_SCL(0);
    }
    return Byte;
}

uint8_t IIC_ReceiveAck(void)
{
    uint8_t Ack;
    IIC_W_SDA(1);   //主机释放SDA,以接受从机Ack
    IIC_W_SCL(1);
    Ack = IIC_R_SDA();
    IIC_W_SCL(0);
    return Ack;
}
MPU6050使用软件I2C通信
#include "stm32f10x.h"                  // Device header
#include "IIC_SOFTWARE.h"
#include "MPU6050.h"
#include "MPU6050_Regs.h"


void MPU6050_WriteRegData(uint8_t RegAddr,uint8_t Data)
{
    IIC_Start();
    IIC_SendByte(MPU6050_DEVICE_ADDRESS);
    IIC_ReceiveAck();
    IIC_SendByte(RegAddr);
    IIC_ReceiveAck();
    IIC_SendByte(Data);
    IIC_ReceiveAck();
    IIC_End();
}

uint8_t MPU6050_ReadRegData(uint8_t RegAddr)
{
    uint8_t RegData;
    
    IIC_Start();
    IIC_SendByte(MPU6050_DEVICE_ADDRESS);
    IIC_ReceiveAck();
    IIC_SendByte(RegAddr);
    IIC_ReceiveAck();
    
    IIC_Start();
    IIC_SendByte(MPU6050_DEVICE_ADDRESS | 0x01);
    IIC_ReceiveAck();
    RegData = IIC_ReceiveByte();
    IIC_SendAck(1);
    IIC_End();
    return RegData;
}

uint8_t MPU6050_ReadID(void)
{
    return MPU6050_ReadRegData(MPU6050_WHO_AM_I);
}

void MPU6050_GetData(MPU6050_GYRO_TypeDef *MPU6050_GYRO_Structure,MPU6050_ACCEL_TypeDef *MPU6050_ACCEL_Structure)
{
    uint16_t Reg_H,Reg_L;
    
    //MPU6050_GYRO_OUT
    Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_XOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_XOUT_L);
    MPU6050_GYRO_Structure->GYRO_XOUT = (Reg_H << 8) | Reg_L;
    
    Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_YOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_YOUT_L);
    MPU6050_GYRO_Structure->GYRO_YOUT = (Reg_H << 8) | Reg_L;
    
    Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_ZOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_ZOUT_L);
    MPU6050_GYRO_Structure->GYRO_ZOUT = (Reg_H << 8) | Reg_L;
    
    //MPU6050_ACCEL_OUT
    Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_XOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_XOUT_H);
    MPU6050_ACCEL_Structure->ACCEL_XOUT = (Reg_H << 8) | Reg_L;
    
    Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_YOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_YOUT_H);
    MPU6050_ACCEL_Structure->ACCEL_YOUT = (Reg_H << 8) | Reg_L;
    
    Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_ZOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_ZOUT_H);
    MPU6050_ACCEL_Structure->ACCEL_ZOUT = (Reg_H << 8) | Reg_L;
}
void MPU6050_MeasureData(MPU6050_GYRO_TypeDef *MPU6050_GYRO_Structure,MPU6050_ACCEL_TypeDef *MPU6050_ACCEL_Structure,MPU6050_Measure_TypeDef *MPU6050_Measure_Structure)
{
    MPU6050_Measure_Structure->Gx = ((float)MPU6050_GYRO_Structure->GYRO_XOUT)/32768.0*2000.0;
    MPU6050_Measure_Structure->Gy = ((float)MPU6050_GYRO_Structure->GYRO_YOUT)/32768.0*2000.0;
    MPU6050_Measure_Structure->Gz = ((float)MPU6050_GYRO_Structure->GYRO_ZOUT)/32768.0*2000.0;
    
    MPU6050_Measure_Structure->Ax = ((float)MPU6050_ACCEL_Structure->ACCEL_XOUT)/32768.0*16.0;
    MPU6050_Measure_Structure->Ay = ((float)MPU6050_ACCEL_Structure->ACCEL_YOUT)/32768.0*16.0;
    MPU6050_Measure_Structure->Az = ((float)MPU6050_ACCEL_Structure->ACCEL_ZOUT)/32768.0*16.0;
}

void MPU6050_Init(void)
{
    IIC_Init();
    MPU6050_WriteRegData(MPU6050_PWR_MGMT_1,0x00);
    
    MPU6050_WriteRegData(MPU6050_PWR_MGMT_2,0x00);
    MPU6050_WriteRegData(MPU6050_SMPRT_DIV,0x00);
    MPU6050_WriteRegData(MPU6050_CONFIG,0x06);
    MPU6050_WriteRegData(MPU6050_GYRO_CONFIG,0x18);
    MPU6050_WriteRegData(MPU6050_ACCEL_CONFIG,0x18);
}

float MPU6050_Abs(float num)
{
    return num < 0 ? -num:num;
}
I2C外设简介
  • STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
  • 支持多主机模型
  • 支持7位/10位地址模式
  • 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
  • 支持DMA
  • 兼容SMBus协议
  • STM32F103C8T6 硬件I2C资源:I2C1、I2C2

I2C外设基本结构

I2C主机发送数据

I2C主机接收数据

MPU6050使用I2C外设通信
#include "stm32f10x.h"                  // Device header
#include "MPU6050.h"
#include "MPU6050_Regs.h"

#define IIC_PORT GPIOB
#define IIC_SCL GPIO_Pin_10
#define IIC_SDA GPIO_Pin_11

void MPU6050_WriteRegData(uint8_t RegAddr,uint8_t Data)
{
//    IIC_Start();
//    IIC_SendByte(MPU6050_DEVICE_ADDRESS);
//    IIC_ReceiveAck();
//    IIC_SendByte(RegAddr);
//    IIC_ReceiveAck();
//    IIC_SendByte(Data);
//    IIC_ReceiveAck();
//    IIC_End();
    
    I2C_GenerateSTART(I2C2,ENABLE);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
    
    I2C_Send7bitAddress(I2C2,MPU6050_DEVICE_ADDRESS,I2C_Direction_Transmitter);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
    
    I2C_SendData(I2C2,RegAddr);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);
    
    I2C_SendData(I2C2,Data);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);
    
    I2C_GenerateSTOP(I2C2,ENABLE);
}

uint8_t MPU6050_ReadRegData(uint8_t RegAddr)
{
    uint8_t RegData;
    
//    IIC_Start();
//    IIC_SendByte(MPU6050_DEVICE_ADDRESS);
//    IIC_ReceiveAck();
//    IIC_SendByte(RegAddr);
//    IIC_ReceiveAck();
//    
//    IIC_Start();
//    IIC_SendByte(MPU6050_DEVICE_ADDRESS | 0x01);
//    IIC_ReceiveAck();
//    RegData = IIC_ReceiveByte();
//    IIC_SendAck(1);
//    IIC_End();
    
    I2C_GenerateSTART(I2C2,ENABLE);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
    
    I2C_Send7bitAddress(I2C2,MPU6050_DEVICE_ADDRESS,I2C_Direction_Transmitter);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
    
    I2C_SendData(I2C2,RegAddr);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);
    
    I2C_GenerateSTART(I2C2,ENABLE);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
    
    I2C_Send7bitAddress(I2C2,MPU6050_DEVICE_ADDRESS,I2C_Direction_Receiver);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);
    
    I2C_AcknowledgeConfig(I2C2,DISABLE);
    I2C_GenerateSTOP(I2C2,ENABLE);
    
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);
    RegData = I2C_ReceiveData(I2C2);

    I2C_AcknowledgeConfig(I2C2,ENABLE);
    return RegData;
}

uint8_t MPU6050_ReadID(void)
{
    return MPU6050_ReadRegData(MPU6050_WHO_AM_I);
}

void MPU6050_GetData(MPU6050_GYRO_TypeDef *MPU6050_GYRO_Structure,MPU6050_ACCEL_TypeDef *MPU6050_ACCEL_Structure)
{
    uint16_t Reg_H,Reg_L;
    
    //MPU6050_GYRO_OUT
    Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_XOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_XOUT_L);
    MPU6050_GYRO_Structure->GYRO_XOUT = (Reg_H << 8) | Reg_L;
    
    Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_YOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_YOUT_L);
    MPU6050_GYRO_Structure->GYRO_YOUT = (Reg_H << 8) | Reg_L;
    
    Reg_H = MPU6050_ReadRegData(MPU6050_GYRO_ZOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_GYRO_ZOUT_L);
    MPU6050_GYRO_Structure->GYRO_ZOUT = (Reg_H << 8) | Reg_L;
    
    //MPU6050_ACCEL_OUT
    Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_XOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_XOUT_H);
    MPU6050_ACCEL_Structure->ACCEL_XOUT = (Reg_H << 8) | Reg_L;
    
    Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_YOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_YOUT_H);
    MPU6050_ACCEL_Structure->ACCEL_YOUT = (Reg_H << 8) | Reg_L;
    
    Reg_H = MPU6050_ReadRegData(MPU6050_ACCEL_ZOUT_H);
    Reg_L = MPU6050_ReadRegData(MPU6050_ACCEL_ZOUT_H);
    MPU6050_ACCEL_Structure->ACCEL_ZOUT = (Reg_H << 8) | Reg_L;
}
void MPU6050_MeasureData(MPU6050_GYRO_TypeDef *MPU6050_GYRO_Structure,MPU6050_ACCEL_TypeDef *MPU6050_ACCEL_Structure,MPU6050_Measure_TypeDef *MPU6050_Measure_Structure)
{
    MPU6050_Measure_Structure->Gx = ((float)MPU6050_GYRO_Structure->GYRO_XOUT)/32768.0*2000.0;
    MPU6050_Measure_Structure->Gy = ((float)MPU6050_GYRO_Structure->GYRO_YOUT)/32768.0*2000.0;
    MPU6050_Measure_Structure->Gz = ((float)MPU6050_GYRO_Structure->GYRO_ZOUT)/32768.0*2000.0;
    
    MPU6050_Measure_Structure->Ax = ((float)MPU6050_ACCEL_Structure->ACCEL_XOUT)/32768.0*16.0;
    MPU6050_Measure_Structure->Ay = ((float)MPU6050_ACCEL_Structure->ACCEL_YOUT)/32768.0*16.0;
    MPU6050_Measure_Structure->Az = ((float)MPU6050_ACCEL_Structure->ACCEL_ZOUT)/32768.0*16.0;
}

void MPU6050_Init(void)
{
//    IIC_Init();
    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 = IIC_SCL | IIC_SDA;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 50000;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_Init(I2C2,&I2C_InitStructure);
    I2C_Cmd(I2C2,ENABLE);
    
    MPU6050_WriteRegData(MPU6050_PWR_MGMT_1,0x00);
    MPU6050_WriteRegData(MPU6050_PWR_MGMT_2,0x00);
    MPU6050_WriteRegData(MPU6050_SMPRT_DIV,0x00);
    MPU6050_WriteRegData(MPU6050_CONFIG,0x06);
    MPU6050_WriteRegData(MPU6050_GYRO_CONFIG,0x18);
    MPU6050_WriteRegData(MPU6050_ACCEL_CONFIG,0x18);
}

float MPU6050_Abs(float num)
{
    return num < 0 ? -num:num;
}

I2C通信只需要SDA和SCL两根通信线,节省IO口,一般常用软件实现。

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值