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 设备的寄存器地

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值