stm32f4硬件IIC读取MPU6050数据

stm32f4硬件IIC读取MPU6050数据


** JY901B 串口10轴加速度计 MK695**


stm32f4“硬件协议”方式,STM32 的I2C 片上外设专门负责实现I2C 通讯协议,只要置好该外设,它就会自动
根据协议要求产生通讯信号,收发数据并缓存起来,CPU 只要检测该外设的状态和访问数据寄存器,就能完
成数据收发。这种由硬件外设处理I2C 协议的方式减轻了CPU 的工作,且使软件设计更加简单。不需要关心
底层的设计。
  1. I2C 通讯协议

I2C 通讯协议(Inter - Integrated Circuit) 是由Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC) 间的通讯。
通讯的起始和停止信号
在通讯的起始和停止信号
数据有效性
在这里插入图片描述
地址及数据方向
在这里插入图片描述
响应
在这里插入图片描述

2. 设备
在这里插入图片描述

**3. STM32的硬件IIC
由stm32f4xx的中文参考手册可得,f4在f1的基础上对iic的性能进行了加强,稳定性方面得到了很大的增强。
“硬件协议”方式,STM32 的I2C 片上外设专门负责实现I2C 通讯协议,只要配
置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU 只要检测该
外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C 协议的方式减轻了
CPU 的工作,且使软件设计更加简单。不需要关心底层的库设计。
在这里插入图片描述

4.*** 程序设计****(最最干货)

STM32 标准库提供了I2C 初始化结构体及初始化函数来配置I2C 外设。初始
化结构体及函数定义在库文件“stm32f4xx_i2c.h”及“stm32f4xx_i2c.c”中,编程时我们可以结合
这两个文件内的注释使用或参考库帮助文档。了解初始化结构体后我们就能对I2C 外设运用自
如了。

4.1. 首先要对IIC总线的IO口和iic的结构体进行初始化

/**
  * @brief  初始化I2C总线,使用I2C前需要调用
  * @param  无
  * @retval 无
  */
void I2cMaster_Init(void) 
{
  GPIO_InitTypeDef GPIO_InitStructure;
  I2C_InitTypeDef I2C_InitStructure;

    /* Enable I2Cx clock */
  RCC_APB1PeriphClockCmd(SENSORS_I2C_RCC_CLK, ENABLE);

  /* Enable I2C GPIO clock */
  RCC_AHB1PeriphClockCmd(SENSORS_I2C_SCL_GPIO_CLK | SENSORS_I2C_SDA_GPIO_CLK, ENABLE);

  /* Configure I2Cx pin: SCL ----------------------------------------*/
  GPIO_InitStructure.GPIO_Pin =  SENSORS_I2C_SCL_GPIO_PIN; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;

  /* Connect pins to Periph */
  GPIO_PinAFConfig(SENSORS_I2C_SCL_GPIO_PORT, SENSORS_I2C_SCL_GPIO_PINSOURCE, SENSORS_I2C_AF);  
  GPIO_Init(SENSORS_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);

  /* Configure I2Cx pin: SDA ----------------------------------------*/
  GPIO_InitStructure.GPIO_Pin = SENSORS_I2C_SDA_GPIO_PIN; 

  /* Connect pins to Periph */
  GPIO_PinAFConfig(SENSORS_I2C_SDA_GPIO_PORT, SENSORS_I2C_SDA_GPIO_PINSOURCE, SENSORS_I2C_AF);  
  GPIO_Init(SENSORS_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);  
  
  I2C_DeInit(SENSORS_I2C);
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStructure.I2C_OwnAddress1 = I2C_OWN_ADDRESS;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;
    
  /* Enable the I2C peripheral */
  I2C_Cmd(SENSORS_I2C, ENABLE);  
    
  /* Initialize the I2C peripheral */
  I2C_Init(SENSORS_I2C, &I2C_InitStructure);
  
  return;
}

这些结构体成员说明如下,其中括号内的文字是对应参数在STM32 标准库中定义的宏:

(1) I2C_ClockSpeed
本成员设置的是I2C 的传输速率,在调用初始化函数时,函数会根据我们输入的数值经过
运算后把时钟因子写入到I2C 的时钟控制寄存器CCR。而我们写入的这个参数值不得高于400KHz。由于CCR 寄存器不能写入小数类型的时钟因子,固件库计算CCR 值时会向下取
整,影响到SCL 的实际频率可能会低于本成员设置的参数值,这时除了通讯稍慢一点以外,
不会对I2C 的标准通讯造成其它影响。

(2) I2C_Mode
本成员是选择I2C 的使用方式,有I2C 模式(I2C_Mode_I2C) 和SMBus 主、从模式
(I2C_Mode_SMBusHost、I2C_Mode_SMBusDevice ) 。I2C 不需要在此处区分主从模式,直接
设置I2C_Mode_I2C 即可。

(3) I2C_DutyCycle
本成员设置的是I2C 的SCL 线时钟的占空比。该配置有两个选择,分别为低电平时间比高
电平时间为2:1 ( I2C_DutyCycle_2) 和16:9 (I2C_DutyCycle_16_9)。其实这两个模式的比
例差别并不大,一般要求都不会如此严格,这里随便选就可以了。

(4) I2C_OwnAddress1
本成员配置的是STM32 的I2C 设备自己的地址,每个连接到I2C 总线上的设备
都要有一个自己的地址,作为主机也不例外。地址可设置为7 位或10 位(受下面
I2C_AcknowledgeAddress 成员决定),只要该地址是I2C 总线上唯一的即可。
STM32 的I2C 外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员
I2C_OwnAddress1 配置的是默认的、OAR1 寄存器存储的地址,若需要设置第二个地址寄存
器OAR2,可使用I2C_OwnAddress2Config 函数来配置,OAR2 不支持10 位地址。

(5) I2C_Ack_Enable
本成员是关于I2C 应答设置,设置为使能则可以发送响应信号。该成员值一般配置为允
许应答(I2C_Ack_Enable),这是绝大多数遵循I2C 标准的设备的通讯要求,改为禁止应答
(I2C_Ack_Disable) 往往会导致通讯错误。

(6) I2C_AcknowledgeAddress
本成员选择I2C 的寻址模式是7 位还是10 位地址。这需要根据实际连接到I2C 总线上设备
的地址进行选择,这个成员的配置也影响到I2C_OwnAddress1 成员,只有这里设置成10 位
模式时,I2C_OwnAddress1 才支持10 位地址。
配置完这些结构体成员值,调用库函数I2C_Init 即可把结构体的配置写入到寄存器中。

4.2 写一个字节

/**
  * @brief   写一个字节到I2C EE中
  * @param    @param  slave_addr, 外设地址
  *		@arg pBuffer:缓冲区指针
  *		@arg WriteAddr:写地址 
  * @retval  无
  */
u8 I2C_ByteWrite(u8 slave_addr,u8* pBuffer, u8 WriteAddr)
{
	
	u16 I2CTimeout;
  /* Send STRAT condition */
  I2C_GenerateSTART(SENSORS_I2C, ENABLE);

  I2CTimeout = ((uint32_t)0x1000);

  /* Test on EV5 and clear it */
  while(!I2C_CheckEvent(SENSORS_I2C, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) printf("I2C 等待超时!errorCode = 0 \r\n");
  }    

  /* Send EE address for write */
  I2C_Send7bitAddress(SENSORS_I2C, slave_addr, I2C_Direction_Transmitter);
  
  
  I2CTimeout = ((uint32_t)0x1000);
  /* Test on EV6 and clear it */
  while(!I2C_CheckEvent(SENSORS_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    if((I2CTimeout--) == 0)   printf("I2C 等待超时!errorCode = 1 \r\n");
  }    
      
  /* Send the EE's internal address to write to */
  I2C_SendData(SENSORS_I2C, WriteAddr);
  
  I2CTimeout = ((uint32_t)0x1000);

  /* Test on EV8 and clear it */
  while(!I2C_CheckEvent(SENSORS_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))  
  {
    if((I2CTimeout--) == 0)  printf("I2C 等待超时!errorCode = 2 \r\n");
  } 
  /* Send the byte to be written */
  I2C_SendData(SENSORS_I2C, *pBuffer); 
   
  I2CTimeout = ((uint32_t)0x1000);

  /* Test on EV8 and clear it */
  while(!I2C_CheckEvent(SENSORS_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if((I2CTimeout--) == 0) printf("I2C 等待超时!errorCode = 3 \r\n");
  } 
  
  /* Send STOP condition */
  I2C_GenerateSTOP(SENSORS_I2C, ENABLE);
  
  return 1;
}

先来分析I2C_TIMEOUT_UserCallback 函数,它的函数体里只调用了宏EEPROM_ERROR,这个
宏封装了printf 函数,方便使用串口向上位机打印调试信息,阅读代码时把它当成printf 函数即
可。在I2C 通讯的很多过程,都需要检测事件,当检测到某事件后才能继续下一步的操作,但有
时通讯错误或者I2C 总线被占用,我们不能无休止地等待下去,所以我们设定每个事件检测都有
等待的时间上限,若超过这个时间,我们就调用I2C_TIMEOUT_UserCallback 函数输出调试信息
(或可以自己加其它操作),并终止I2C 通讯。
了解了这个机制,再来分析I2C_EE_ByteWrite 函数,这个函数实现了前面讲的I2C 主发送器通
讯流程

(1) 使用库函数I2C_GenerateSTART 产生I2C 起始信号,其中的EEPROM_I2C 宏是前面硬件定义相关的I2C 编号;

(2) 对I2CTimeout 变量赋值为宏I2CT_FLAG_TIMEOUT,这个I2CTimeout 变量在下面的while 循
环中每次循环减1,该循环通过调用库函数I2C_CheckEvent 检测事件,若检测到事件,则进入通
讯的下一阶段,若未检测到事件则停留在此处一直检测,当检测I2CT_FLAG_TIMEOUT 次都还
没等待到事件则认为通讯失败,调用前面的I2C_TIMEOUT_UserCallback 输出调试信息,并退出
通讯;

(3) 调用库函数I2C_Send7bitAddress 发送EEPROM 的设备地址,并把数据传输方向设置为
I2C_Direction_Transmitter(即发送方向),这个数据传输方向就是通过设置I2C 通讯中紧跟地址后
面的R/W 位实现的。发送地址后以同样的方式检测EV6 标志;

(4) 调用库函数I2C_SendData 向EEPROM 发送要写入的内部地址,该地址是I2C_EE_ByteWrite
函数的输入参数,发送完毕后等待EV8 事件。要注意这个内部地址跟上面的EEPROM 地址不一
样,上面的是指I2C 总线设备的独立地址,而此处的内部地址是指EEPROM 内数据组织的地址,
也可理解为EEPROM 内存的地址或I2C 设备的寄存器地址;

(5) 调用库函数I2C_SendData 向EEPROM 发送要写入的数据,该数据是I2C_EE_ByteWrite 函数
的输入参数,发送完毕后等待EV8 事件;

(6) 一个I2C 通讯过程完毕,调用I2C_GenerateSTOP 发送停止信号。

4.3对读写函数的封装
封装后的函数主要是增加了错误重试机制,若读写出现错误,则会进行多次尝试,多次尝试均失
败后会返回错误代码。这个函数作为I2C 驱动对外的接口,其它使用I2C 的传感器调用这个函数
进行读写寄存器。
封装后的函数主要是增加了错误重试机制,若读写出现错误,则会进行多次尝试,多次尝试均失
败后会返回错误代码。这个函数作为I2C 驱动对外的接口,其它使用I2C 的传感器调用这个函数
进行读写寄存器。
封装后的函数主要是增加了错误重试机制,若读写出现错误,则会进行多次尝试,多次尝试均失
败后会返回错误代码。这个函数作为I2C 驱动对外的接口,其它使用I2C 的传感器调用这个函数
进行读写寄存器。

/**
  * @brief  写寄存器(多次尝试),这是提供给上层的接口
	* @param  slave_addr: 从机地址
	* @param 	reg_addr:寄存器地址
	* @param len:写入的长度
	*	@param data_ptr:指向要写入的数据
  * @retval 正常为0,不正常为非0
  */
u8 Sensors_I2C_WriteRegister(unsigned char slave_addr,
                                        unsigned char reg_addr,
                                        unsigned short len, 
                                        const unsigned char *data_ptr)
{
  char retries=0;
  int ret = 0;
  unsigned short retry_in_mlsec = Get_I2C_Retry();
                              
tryWriteAgain:  
  ret = 0;
  ret = ST_Sensors_I2C_WriteRegister( slave_addr, reg_addr, len, data_ptr); 

  if(ret && retry_in_mlsec)
  {
    if( retries++ > 4 )
        return ret;
    
    Delay(retry_in_mlsec);
    goto tryWriteAgain;
  }
  return ret;  
}

/**
  * @brief  读寄存器(多次尝试),这是提供给上层的接口
	* @param  slave_addr: 从机地址
	* @param 	reg_addr:寄存器地址
	* @param len:要读取的长度
	*	@param data_ptr:指向要存储数据的指针
  * @retval 正常为0,不正常为非0
  */
u8 Sensors_I2C_ReadRegister(unsigned char slave_addr,
                                       unsigned char reg_addr,
                                       unsigned short len, 
                                       unsigned char *data_ptr)
{
  char retries=0;
  int ret = 0;
  unsigned short retry_in_mlsec = Get_I2C_Retry();
  
tryReadAgain:  
  ret = 0;
  ret = ST_Sensors_I2C_ReadRegister( slave_addr, reg_addr, len, data_ptr);

  if(ret && retry_in_mlsec)
  {
    if( retries++ > 4 )
        return ret;
    
    Delay(retry_in_mlsec);
    goto tryReadAgain;
  } 
  return ret;
}


/**
  * @brief  写寄存器(单次尝试),这是底层I2C接口
	* @param  slave_addr: 从机地址
	* @param 	reg_addr:寄存器地址
	* @param len:写入的长度
	*	@param data_ptr:指向要写入的数据
  * @retval 正常为0,不正常为非0
  */
static unsigned long ST_Sensors_I2C_WriteRegister(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue)
{
  uint32_t  result = 0;
  uint32_t  i = 0; // i = RegisterLen;
  __IO uint32_t  I2CTimeout = I2Cx_LONG_TIMEOUT;
  
//  RegisterValue = RegisterValue + (RegisterLen - 1);

  /* Wait for the busy flag to be cleared */
  WAIT_FOR_FLAG (I2C_FLAG_BUSY, RESET, I2Cx_LONG_TIMEOUT, 1);
  
  /* Start the config sequence */
  I2C_GenerateSTART(SENSORS_I2C, ENABLE);

  /* Wait for the start bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_SB, SET, I2Cx_FLAG_TIMEOUT, 2);

  /* Transmit the slave address and enable writing operation */
  I2C_Send7bitAddress(SENSORS_I2C, (Address<<1), I2C_Direction_Transmitter);
  
  /* Wait for address bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_ADDR, SET, I2Cx_FLAG_TIMEOUT, 3);
  
  /* clear the ADDR interrupt bit  - this is done by reading SR1 and SR2*/
  CLEAR_ADDR_BIT
  
  /* Wait for address bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_TXE, SET, I2Cx_FLAG_TIMEOUT, 4);

  /* Transmit the first address for write operation */
  I2C_SendData(SENSORS_I2C, RegisterAddr);

  
  for(i=0; i<(RegisterLen); i++)
  {
    /* Wait for address bit to be set */
    WAIT_FOR_FLAG (I2C_FLAG_TXE, SET, I2Cx_FLAG_TIMEOUT, 5);
  
    /* Prepare the register value to be sent */
    I2C_SendData(SENSORS_I2C, RegisterValue[i]);
  }  
   
  /* Wait for address bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_BTF, SET, I2Cx_FLAG_TIMEOUT, 6);
  
  /* End the configuration sequence */
  I2C_GenerateSTOP(SENSORS_I2C, ENABLE);  
  
  /* Return the verifying value: 0 (Passed) or 1 (Failed) */
  return result;  
}


/**
  * @brief  读寄存器(单次尝试),这是底层I2C接口
	* @param  slave_addr: 从机地址
	* @param 	reg_addr:寄存器地址
	* @param len:要读取的长度
	*	@param data_ptr:指向要存储数据的指针
  * @retval 正常为0,不正常为非0
  */
static unsigned long ST_Sensors_I2C_ReadRegister(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue)
{
  uint32_t i=0, result = 0;
  __IO uint32_t  I2CTimeout = I2Cx_LONG_TIMEOUT;
   
  /* Wait for the busy flag to be cleared */
  WAIT_FOR_FLAG (I2C_FLAG_BUSY, RESET, I2Cx_LONG_TIMEOUT, 7);
  
  /* Start the config sequence */
  I2C_GenerateSTART(SENSORS_I2C, ENABLE);

  /* Wait for the start bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_SB, SET, I2Cx_FLAG_TIMEOUT, 8);
  
  /* Transmit the slave address and enable writing operation */
  I2C_Send7bitAddress(SENSORS_I2C, (Address<<1), I2C_Direction_Transmitter);

  /* Wait for the start bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_ADDR, SET, I2Cx_FLAG_TIMEOUT, 9);

  /* clear the ADDR interrupt bit  - this is done by reading SR1 and SR2*/
  CLEAR_ADDR_BIT;
  
  /* Wait for address bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_TXE, SET, I2Cx_FLAG_TIMEOUT, 10);
  
  /* Transmit the register address to be read */
  I2C_SendData(SENSORS_I2C, RegisterAddr);
  
  /* Wait for address bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_TXE, SET, I2Cx_FLAG_TIMEOUT, 11);  

  /*!< Send START condition a second time */  
  I2C_GenerateSTART(SENSORS_I2C, ENABLE);
  
  /* Wait for the start bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_SB, SET, I2Cx_FLAG_TIMEOUT, 12);
  
  /*!< Send address for read */
  I2C_Send7bitAddress(SENSORS_I2C, (Address<<1), I2C_Direction_Receiver);  
  
  /* Wait for the start bit to be set */
  WAIT_FOR_FLAG (I2C_FLAG_ADDR, SET, I2Cx_FLAG_TIMEOUT, 13);
  
  if (RegisterLen == 1) 
  {
    /*!< Disable Acknowledgment */
    I2C_AcknowledgeConfig(SENSORS_I2C, DISABLE);
    
    /* clear the ADDR interrupt bit  - this is done by reading SR1 and SR2*/
    CLEAR_ADDR_BIT;
    
    /*!< Send STOP Condition */
    I2C_GenerateSTOP(SENSORS_I2C, ENABLE);
    
    /* Wait for the RXNE bit to be set */
    WAIT_FOR_FLAG (I2C_FLAG_RXNE, SET, I2Cx_FLAG_TIMEOUT, 14);
    
    RegisterValue[0] = I2C_ReceiveData(SENSORS_I2C);
  } 
  else if( RegisterLen == 2) 
  {
     /*!< Disable Acknowledgment */
    I2C_AcknowledgeConfig(SENSORS_I2C, DISABLE);
    
   /* Set POS bit */ 
   SENSORS_I2C->CR1 |= I2C_CR1_POS;
   
   /* clear the ADDR interrupt bit  - this is done by reading SR1 and SR2*/
   CLEAR_ADDR_BIT; 
   
   /* Wait for the buffer full bit to be set */
   WAIT_FOR_FLAG (I2C_FLAG_BTF, SET, I2Cx_FLAG_TIMEOUT, 15);
   
   /*!< Send STOP Condition */
   I2C_GenerateSTOP(SENSORS_I2C, ENABLE);

   /* Read 2 bytes */
   RegisterValue[0] = I2C_ReceiveData(SENSORS_I2C);
   RegisterValue[1] = I2C_ReceiveData(SENSORS_I2C);
  } 
  else if( RegisterLen == 3)
  {
    CLEAR_ADDR_BIT;
    
    /* Wait for the buffer full bit to be set */
    WAIT_FOR_FLAG (I2C_FLAG_BTF, SET, I2Cx_FLAG_TIMEOUT, 16);
    /*!< Disable Acknowledgment */
    I2C_AcknowledgeConfig(SENSORS_I2C, DISABLE);
    /* Read 1 bytes */
    RegisterValue[0] = I2C_ReceiveData(SENSORS_I2C);
    /*!< Send STOP Condition */
    I2C_GenerateSTOP(SENSORS_I2C, ENABLE);        
    /* Read 1 bytes */
    RegisterValue[1] = I2C_ReceiveData(SENSORS_I2C);
    /* Wait for the buffer full bit to be set */
    WAIT_FOR_FLAG (I2C_FLAG_RXNE, SET, I2Cx_FLAG_TIMEOUT, 17);
    /* Read 1 bytes */
    RegisterValue[2] = I2C_ReceiveData(SENSORS_I2C);  
  }  
  else /* more than 2 bytes */
  { 
    /* clear the ADDR interrupt bit  - this is done by reading SR1 and SR2*/
    CLEAR_ADDR_BIT;
    
    for(i=0; i<(RegisterLen); i++)
    {
      if(i==(RegisterLen-3))
      {
        /* Wait for the buffer full bit to be set */
        WAIT_FOR_FLAG (I2C_FLAG_BTF, SET, I2Cx_FLAG_TIMEOUT, 16);
        
        /*!< Disable Acknowledgment */
        I2C_AcknowledgeConfig(SENSORS_I2C, DISABLE);
        
        /* Read 1 bytes */
        RegisterValue[i++] = I2C_ReceiveData(SENSORS_I2C);
        
        /*!< Send STOP Condition */
        I2C_GenerateSTOP(SENSORS_I2C, ENABLE);        
        
        /* Read 1 bytes */
        RegisterValue[i++] = I2C_ReceiveData(SENSORS_I2C);
        
        /* Wait for the buffer full bit to be set */
        WAIT_FOR_FLAG (I2C_FLAG_RXNE, SET, I2Cx_FLAG_TIMEOUT, 17);
        
        /* Read 1 bytes */
        RegisterValue[i++] = I2C_ReceiveData(SENSORS_I2C);  
        goto endReadLoop;
      }
            
      /* Wait for the RXNE bit to be set */
      WAIT_FOR_FLAG (I2C_FLAG_RXNE, SET, I2Cx_FLAG_TIMEOUT, 18);
      RegisterValue[i] = I2C_ReceiveData(SENSORS_I2C); 
    }   
  } 
  
endReadLoop:  
  /* Clear BTF flag */
  I2C_ClearFlag(SENSORS_I2C, I2C_FLAG_BTF);
  /* Wait for the busy flag to be cleared */
  WAIT_FOR_FLAG (I2C_FLAG_BUSY, RESET, I2Cx_LONG_TIMEOUT, 19);  
  /*!< Re-Enable Acknowledgment to be ready for another reception */
  I2C_AcknowledgeConfig(SENSORS_I2C, ENABLE);
  //Disable POS -- TODO
  SENSORS_I2C->CR1 &= ~I2C_CR1_POS;  
     
  /* Return the byte read from sensor */
  return result;
}

4.3宏定义

#include "sys.h" 
#include "delay.h"
#include "usart.h"

/****************************** Defines *******************************/

#define I2C_SPEED                 400000
#define I2C_OWN_ADDRESS           0x00

//毫秒级延时(需要定时器支持),或者重写Delay宏
#define Delay 		delay_ms
#define I2Cx_FLAG_TIMEOUT             ((uint32_t) 900) //0x1100
#define I2Cx_LONG_TIMEOUT             ((uint32_t) (300 * I2Cx_FLAG_TIMEOUT)) //was300
 
/*引脚定义*/ 
#define SENSORS_I2C_SCL_GPIO_PORT         GPIOB
#define SENSORS_I2C_SCL_GPIO_CLK          RCC_AHB1Periph_GPIOB
#define SENSORS_I2C_SCL_GPIO_PIN          GPIO_Pin_8
#define SENSORS_I2C_SCL_GPIO_PINSOURCE    GPIO_PinSource8
 
#define SENSORS_I2C_SDA_GPIO_PORT         GPIOB
#define SENSORS_I2C_SDA_GPIO_CLK          RCC_AHB1Periph_GPIOB
#define SENSORS_I2C_SDA_GPIO_PIN          GPIO_Pin_9
#define SENSORS_I2C_SDA_GPIO_PINSOURCE    GPIO_PinSource9

#define SENSORS_I2C_AF                    GPIO_AF_I2C1

#define SENSORS_I2C              					 I2C1
#define SENSORS_I2C_RCC_CLK               RCC_APB1Periph_I2C1
/*信息输出*/
#define I2C_DEBUG_ON         1
#define I2C_DEBUG_FUNC_ON    0

#define I2C_INFO(fmt,arg...)           printf("<<-I2C-INFO->> "fmt"\n",##arg)
#define I2C_ERROR(fmt,arg...)          printf("<<-I2C-ERROR->> "fmt"\n",##arg)
#define I2C_DEBUG(fmt,arg...)          do{\
                                          if(I2C_DEBUG_ON)\
                                          printf("<<-I2C-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

#define I2C_DEBUG_FUNC()               do{\
                                         if(I2C_DEBUG_FUNC_ON)\
                                         printf("<<-I2C-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\
                                       }while(0)
																			 
/*函数定义*/
#define I2C_Config() I2cMaster_Init();
																			 
void I2cMaster_Init(void);
void Set_I2C_Retry(unsigned short ml_sec);
unsigned short Get_I2C_Retry(void);

//写一个字节,这是提供给上层的接口		
u8 I2C_ByteWrite(u8 slave_addr,u8* pBuffer, u8 WriteAddr);									   
//读寄存器(多次尝试),这是提供给上层的接口																			 
u8 Sensors_I2C_ReadRegister(unsigned char slave_addr,
                                       unsigned char reg_addr,
                                       unsigned short len, 
                                       unsigned char *data_ptr);
//写寄存器(多次尝试),这是提供给上层的接口
u8 Sensors_I2C_WriteRegister(unsigned char slave_addr,
                                        unsigned char reg_addr,
                                        unsigned short len, 
                                        const unsigned char *data_ptr);

4.5 读取JY901B 串口10轴加速度计 MK695**
在这里插入图片描述
可通过iic总线直接读取内部数据,数据已经经过dmp姿态解读和卡尔曼滤波
直接读取 直接读取 直接读取
附带mian函数

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h" 
#include "myiic.h"



void ShortToChar(short sData,unsigned char cData[])
{
	cData[0]=sData&0xff;
	cData[1]=sData>>8;
}
short CharToShort(unsigned char cData[])
{
	return ((short)cData[1]<<8)|cData[0];
}
 
	
int main(void)
{ 
	unsigned char chrTemp[30];
//	unsigned char str[100];
	float a[3],w[3],h[3],Angle[3];
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);    //初始化延时函数
	uart_init(115200);	//初始化串口波特率为115200
	
	LED_Init();					//初始化LED 
 	
	KEY_Init(); 				//按键初始化  
	
 	I2cMaster_Init();
	
	while(1)
	{
		//IICreadBytes(0x50, AX, 24,&chrTemp[0]);
		//IICreadBytes(u8 dev, u8 reg, u8 length, u8 *data);
		//I2C_BufferRead(u8 slave_addr,u8* pBuffer, u8 ReadAddr, u8 NumByteToRead);
		Sensors_I2C_ReadRegister(0x50,AX,24,&chrTemp[0]);
		a[0] = (float)CharToShort(&chrTemp[0])/32768*16;
		a[1] = (float)CharToShort(&chrTemp[2])/32768*16;
		a[2] = (float)CharToShort(&chrTemp[4])/32768*16;
		w[0] = (float)CharToShort(&chrTemp[6])/32768*2000;
		w[1] = (float)CharToShort(&chrTemp[8])/32768*2000;
		w[2] = (float)CharToShort(&chrTemp[10])/32768*2000;
		h[0] = CharToShort(&chrTemp[12]);
		h[1] = CharToShort(&chrTemp[14]);
		h[2] = CharToShort(&chrTemp[16]);
		Angle[0] = (float)CharToShort(&chrTemp[18])/32768*180;
		Angle[1] = (float)CharToShort(&chrTemp[20])/32768*180;
		Angle[2] = (float)CharToShort(&chrTemp[22])/32768*180;
		
		printf("0x50:  a:%.3f %.3f %.3f w:%.3f %.3f %.3f  h:%.0f %.0f %.0f  Angle:%.3f %.3f %.3f \r\n",a[0],a[1],a[2],w[0],w[1],w[2],h[0],h[1],h[2],Angle[0],Angle[1],Angle[2]);
		//  printf("%s\n",str);
		//UART1_Put_String(str);		
		//USB_TxWrite(str, strlen((char*)str));
	} 	    
}

其中AX为内部数据存放的首地址

#define YYMM				0x30
#define DDHH				0x31
#define MMSS				0x32
#define MS					0x33
#define AX					0x34
#define AY					0x35
#define AZ					0x36
#define GX					0x37
#define GY					0x38
#define GZ					0x39
#define HX					0x3a
#define HY					0x3b
#define HZ					0x3c			
#define Roll				0x3d
#define Pitch				0x3e
#define Yaw					0x3f
#define TEMP				0x40

尾言
感谢大家能够看到这里,我会后期上传这个工程代码,以方便大家学习,也希望每一位嵌入式工程师能够不断前行。

  • 7
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
STM32 是一款嵌入式微控制器,而 MPU6050 是一款常用的三轴运动传感器。通过HAL库中的I2CIIC)接口来读取MPU6050数据是一种常见的方法。 首先,我们需要在STM32的工程中导入相关的头文件和库文件,以便使用HAL库中的函数和定义。然后,我们需要初始化I2C总线和MPU6050模块。 在主程序循环中,可以使用HAL库中的函数来发送I2C启动信号、选择设备地址和寄存器地址,并通过I2C总线读取返回的数据。通过读取MPU6050的加速度、角速度等数据,我们可以进行姿态或者运动的实时监测。 具体的代码大致如下: ```c #include "stm32fxxx_hal.h" #include "i2c.h" #define MPU6050_ADDR 0xD0 // MPU6050设备地址 I2C_HandleTypeDef hi2c; void MPU6050_Read_Accelerometer(int16_t* AccData) { uint8_t buffer[6]; // 用于储存读取的加速度数据 HAL_I2C_Mem_Read(&hi2c, MPU6050_ADDR, 0x3B, I2C_MEMADD_SIZE_8BIT, buffer, 6, HAL_MAX_DELAY); // 将读取到的数据组合成16位的加速度数据 AccData[0] = ((buffer[0] << 8) | buffer[1]); AccData[1] = ((buffer[2] << 8) | buffer[3]); AccData[2] = ((buffer[4] << 8) | buffer[5]); } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C_Init(); int16_t AccData[3]; // 存储三个方向的加速度数据 while (1) { MPU6050_Read_Accelerometer(AccData); // 处理读取到的加速度数据,进行后续操作 } } ``` 通过以上代码,我们可以实现STM32通过HAL库中的I2C接口读取MPU6050的加速度数据。当然,具体的实现还需要根据具体的开发板和库函数来进行调整和配置。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值