找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 9370|回复: 7
收起左侧

STM8L的I2C之MPU6050基本读写

[复制链接]
ID:140343 发表于 2016-10-9 23:56 | 显示全部楼层 |阅读模式
之前有写过I2C的基本读写程序,但拿来驱动MPU6050时,还是遇到一些麻烦,搞了两天终于把问题解决了.
这两天遇到的最大的问题是,I2C的SCL频率达不到设置的100KHz,只有8KHz,这相差也太远了吧.
还好我手上有逻辑分析仪可以看到波形,第一行是SCL的波形.

从上面的波形可以看到,低电平明显比高电平要长,上图的数据是高电平是10us,低电平时间是100us,而数据手册上说,在标准模式下,低电平和高电平应该是1:1的关系.这里的高电平时间和程序里设置的是一样的,就是低电平不正常.
因为低电平时间离奇的长,这样就导致I2C读写速度很慢,读或写一个字节数据要用4ms,这根本就不能用.如果我用mpu6050来做平衡小车,这样的速度根本就没法控制小车平衡.为此我冥思苦想想,不断修改程序,给mpu6050模块焊拆电阻,折腾了好久,最终还是没有解决.搞的我崩溃的想放弃了.

在群里发了这个求助,我自己群,也没报太大希望能解决这个问题.就是想问一下,要把握憋坏了,闷声我憋不出大招了.

直到群里的一位大神出现,我才重拾信心,突发灵感找到问题所在.因为大神说,STM8L的I2C是可以达到100KHz的频率的,之前都怀疑STM8L的硬件I2C能不能达到100KHz.

不知道,怎么就想到打开电路图,看了下,发现了问题所在了.PC1这个引脚上接了个按键,最重要的是还接了阻容滤波的电路.PC1就是SCL引脚.
还好,ST聪明的工程师,在这里设计了一个跳线,用烙铁,拆掉SB17这个零欧电阻跳线,就可以了.
拆掉后,果然看到的久违的波形了,感觉这波形很清爽,心情一下好了很多,愁眉舒展.

虽然只有95KHz,但我已经很满意了,肯定会有偏差的,毕竟用的是默认的内部时钟源,可能还有其他原因,导致频率偏差。
总体感觉,这个逻辑分析仪的功能很强大,查看I2C读写数据很方便,不用人工去根据波形翻译读写数据。老外这个逻辑分析仪做的不错,对不住了,我用的是山寨的。
下面就是我用的这个逻辑分析仪,其实是一块CY7C68013A最小系统板,我烧写了开源的saleae逻辑分析仪固件,配合saleae的上位机软件,就可以用了。这个在淘宝上,三十几块钱就可以买到。

这个其实我也能做的,如果大家需要,我可以亲自画板,做几个,给大家,不要我垫钱就行了。

介绍一下本例程,之前的I2C例程,里面读一个字节的函数有个问题,就是读完一个字节后不会发送无应答NACK时序,最终的时序是错误的。注:之前的文章会被删掉,不用担心找到的是错误的程序版本)本次进行了修正,在 I2C1_CR2_STOP=1语句前面加了I2C1_CR2_ACK=0。同时针对MPU6050增加了一个读多个连续字节的函数,下面贴出的例程,是正在测试这个读多个字节的函数能否正常工作,例程已经通过测试。读出到数组中的数据和写入的数据完全一致。

/*硬件连接*/
// PC0<--->SDA   PC1---->SCL
/****************************************************************************************
*开发环境:IAR for stm8 v6.5.3
*硬件平台:STM8L-DISCOVERY
*功能说明:通过硬件I2C等待的方法,测试连续读MPU6050寄存器函数是否正常
*作    者:茗风
****************************************************************************************/
#include"iostm8l152c6.h"
#include"stdbool.h"
#include"stdint.h"

uint8_t ui8Read_mpu6050_buffer[14];
/******************************************************************************************************
*  名    称:void delay_10ms(uint8_t x_ms)
*  功    能:延时10ms
*  入口参数:无
*  出口参数:无
*  说    明:
*  范    例:无
******************************************************************************************************/
void delay_100ms(void)
{
  uint8_t i,j;
  for(i=0;i<255;i++)//2*255个指令周期
    for(j=0;j<255;j++);//2*255个指令周期
  // delay_10ms共消耗 x_ms*2*255+2*x_ms个指令周期  
  // 255*2*255+2*255=130610us=130ms
  // 此延时函数,延时时间为130ms
  // 16M/8/2=1M 一个指令周期为1us
}
/******************************************************************************************************
* 名 称: uint8_t I2C_ReadOneByteDataFromSlave(uint8_t address)
* 功 能:从I2C从设备中读取一字节的数据
* 入口参数:address:读取数据的寄存器地址
* 出口参数:返回一个从I2C从设备指定地址读到的数据
* 说 明:
* 范 例:无
******************************************************************************************************/
uint8_t I2C_ReadOneByteDataFromSlave(uint8_t address)
{
  volatile uint8_t t;
  //----------I2C起始信号--------------
  I2C1_CR2_START=1;//产生一个起始条件
  while(!(I2C1_SR1_SB==1));//读SR1寄存器,清除SB标志位
//  _5NOPS;//根据数据手册,检测到标志位后,需插入5个NOP进行延时

  //-------发送写I2C从器件地址---------
  I2C1_DR=0xD0;//发送从设备地址
  while(!(I2C1_SR1_ADDR==1));//读SR1寄存器,清除ADDR标志位
// _5NOPS;//根据数据手册,检测到标志位后,需插入5个NOP进行延时
  if(I2C1_SR3_TRA==0)return 1;//读SR3寄存器,清除ADDR标志位
  //  0: Data bytes received
  //  1: Data bytes transmitted

  //-----写I2C从器件寄存器地址--------
  I2C1_DR=address;
  while(!(I2C1_SR1_BTF==1));//等待地址发送完成
//  _5NOPS;//根据数据手册,检测到标志位后,需插入5个NOP进行延时

  //--------I2C重复起始信号-----------
  I2C1_CR2_START=1;//重复产生一个起始条件
  while(!(I2C1_SR1_SB==1));//读SR1寄存器,清除SB标志位
//  _5NOPS;//根据数据手册,检测到标志位后,需插入5个NOP进行延时

  //-------发送读I2C从器件地址---------
  I2C1_DR=0xD1;//发送从设备地址
  while(!(I2C1_SR1_ADDR==1));//读SR1寄存器,清除ADDR标志位
//  _5NOPS;//根据数据手册,检测到标志位后,需插入5个NOP进行延时
  if(I2C1_SR3_TRA==1)return 1;//读SR3寄存器,清除ADDR标志位

  //-------------停止信号-------------
  I2C1_CR2_ACK=0;//ACK位控制着ACK信号,此位为0可以产生一个NOACK信号
  I2C1_CR2_STOP=1;

  //-------------等待接收到数据-------------
  while(!(I2C1_SR1_RXNE==1));//等待地址发送完成

  //-------------读取数据-------------
  t=I2C1_DR;

  return t;
}
/******************************************************************************************************
* 名 称:void I2C_WriteOneByteDataToSlave(uint8_t address,uint8_t dat)
* 功 能:写入一字节的数据到I2C设备中
* 入口参数:address:写入的数据存储地址    dat:待写入的数据
* 出口参数:无
* 说 明: 通过MSTM8L硬件写入I2C设备一个字节的数据
* 范 例:无
******************************************************************************************************/
uint8_t I2C_WriteOneByteDataToSlave(uint8_t address,uint8_t dat)
{
  volatile uint8_t t;
  I2C1_CR2_ACK=1;
  //----------I2C起始信号--------------
  I2C1_CR2_START=1;//产生一个起始条件
  while(!(I2C1_SR1_SB==1));
// _5NOPS;//根据数据手册,检测到标志位后,需插入5个NOP进行延时
  I2C1_DR=0xD0;

  //--------写I2C从器件地址-----------
  while(!(I2C1_SR1_ADDR==1));
//  _5NOPS;//根据数据手册,检测到标志位后,需插入5个NOP进行延时
  if(I2C1_SR3_TRA==0)return 1;//读SR3寄存器,清除ADDR标志位

  //-----写I2C从器件寄存器地址--------
  while(!(I2C1_SR1_TXE==1));
  I2C1_DR=address;

  //-------写I2C数据到寄存器中--------
  while(!(I2C1_SR1_TXE==1));
  I2C1_DR=dat;
  while(!(I2C1_SR1_TXE==1));
  while(!(I2C1_SR1_BTF==1));
// _5NOPS;//根据数据手册,检测到标志位后,需插入5个NOP进行延时

  //-------------停止信号-------------
  I2C1_CR2_STOP=1;
  return 0;
}
/******************************************************************************************************
* 功    能:从I2C从设备读取多个字节数据
* 入口函数:
* 出口函数:
* 说    明:
* 范    例:
* 日    期:
******************************************************************************************************/
uint8_t I2C_ReadMultiBytesFromSlave(uint8_t address,uint8_t *rxbuf,uint8_t len)
{
  volatile uint8_t i=0;

  if(len==0)return 1;//如果写入字节长度为0退出

  I2C1_CR2_ACK=1;
  //----------I2C起始信号--------------
  I2C1_CR2_START=1;//产生一个起始条件
  while(!(I2C1_SR1_SB==1));//读SR1寄存器,清除SB标志位

  //-------发送写I2C从器件地址---------
  I2C1_DR=0xD0;//发送从设备地址
  while(!(I2C1_SR1_ADDR==1));//读SR1寄存器,清除ADDR标志位
  if(I2C1_SR3_TRA==0)return 1;//读SR3寄存器,清除ADDR标志位
  //  0: Data bytes received
  //  1: Data bytes transmitted

  //-----写I2C从器件寄存器地址--------
  I2C1_DR=address;
  while(!(I2C1_SR1_BTF==1));//等待地址发送完成

  //--------I2C重复起始信号-----------
  I2C1_CR2_START=1;//重复产生一个起始条件
  while(!(I2C1_SR1_SB==1));//读SR1寄存器,清除SB标志位

  //-------发送读I2C从器件地址---------
  I2C1_DR=0xD1;//发送从设备地址
  while(!(I2C1_SR1_ADDR==1));//读SR1寄存器,清除ADDR标志位
  if(I2C1_SR3_TRA==1)return 1;//读SR3寄存器,清除ADDR标志位
  //-------------读取数据-------------
  if(len>1)
  {
      for( i=len;i>1;i-- )
      {
        while(!(I2C1_SR1_RXNE==1));//等待I2C1_DR接收到数
        *rxbuf++ = I2C1_DR;  
      }
  }

  //-------------停止信号-------------
  I2C1_CR2_ACK=0;//ACK位控制着ACK信号,此位为0可以产生一个NOACK信号
  I2C1_CR2_STOP=1;

  while(!(I2C1_SR1_RXNE==1));//等待I2C1_DR接收到数
  *rxbuf++ = I2C1_DR;

  return 0;
}
/******************************************************************************************************
* 名 称: IIC_init()
* 功 能:初始化IIC
* 入口参数:无
* 出口参数:无
* 说 明:PC0--SDA   PC1--SCL
* 范 例:无
******************************************************************************************************/
void I2C_Init(void)
{
  //----打开IIC外设时钟----
  CLK_PCKENR1_PCKEN13=1;//
  I2C1_CR1_PE=0;
//  I2C1_CR2_SWRST=1;
  I2C1_CR2_ACK=1;

  //----I2C输入时钟频率选择----
  I2C1_FREQR_FREQ=0x02;//2MHz
  /*  The allowed range is between 1 MHz and 16 MHz
  000000: not allowed
  000001: 1 MHz
  000010: 2 MHz
  ...
  010000: 16 MHz                                 */

  //----配置时钟控制寄存器----
  I2C1_CCRH=0;
//  I2C1_CCRH_F_S=0; //Standard mode I2C
//  I2C1_CCRH_DUTY=0;
  I2C1_CCRL=10;    //SCL高电平时间配置
  //I2C的SCK时钟设置为100KHz,则SCK周期为10us
  //因为I2C1_FREQR_FREQ=0x02,即I2C输入时钟频率为2M,周期为0.5us
  //CCR=10时,SCK的低电平时间为tlow=10*0.5us=5us,SCk高电平时间为thigh=10*0.5us=5us
  //所以CCR=10时,SCK输出频率为100KHz

  //----配置上升时间寄存器----
  I2C1_TRISER_TRISE=1;//in standard mode, the maximum allowed SCL rise time is 1000 ns.
  //1 us / 0.5 us = 2 + 1

  I2C1_CR1_PE=1;//

}
/******************************************************************************************************
* 功    能:MPU6050功能配置
* 入口函数:
* 出口函数:
* 说    明:
* 范    例:
* 日    期:
******************************************************************************************************/
void MPU6050_Config(void)
{
//  static uint8_t tmp=0;
//        I2C_WriteOneByteDataToSlave(0x6B,0x00);//电源管理
//        I2C_WriteOneByteDataToSlave(0x19,0x00);//陀螺仪采样率,不分频,8khz
//        I2C_WriteOneByteDataToSlave(0x1A,0x00);//不启用低通滤波器
//        I2C_WriteOneByteDataToSlave(0x1B,0x18);//陀螺仪不自检,2000deg/s
//        I2C_WriteOneByteDataToSlave(0x1C,0x1F);//加速度计不自检,16g
        I2C_WriteOneByteDataToSlave(0x6B,0x00);//电源管理
        I2C_WriteOneByteDataToSlave(0x27,0x01);
        I2C_WriteOneByteDataToSlave(0x28,0x02);
        I2C_WriteOneByteDataToSlave(0x29,0x03);
        I2C_WriteOneByteDataToSlave(0x2A,0x04);
  I2C_WriteOneByteDataToSlave(0x2B,0x05);
        I2C_WriteOneByteDataToSlave(0x2C,0x06);
        I2C_WriteOneByteDataToSlave(0x2D,0x07);
        I2C_WriteOneByteDataToSlave(0x2E,0x08);
  I2C_WriteOneByteDataToSlave(0x2F,0x09);
}
void main(void)
{
  static uint8_t tmp=0;
  delay_100ms();
  I2C_Init();
  MPU6050_Config();
//  tmp=I2C_ReadOneByteDataFromSlave(0x1C);
  I2C_ReadMultiBytesFromSlave(0x27,ui8Read_mpu6050_buffer,9);
  while(1)
  {
//    asm("wfi");
  }
}

回复

使用道具 举报

ID:164108 发表于 2017-2-18 21:16 | 显示全部楼层
你好楼主,能向你请教一下做那个逻辑分析仪的问题嘛?
回复

使用道具 举报

ID:184406 发表于 2017-6-13 17:45 | 显示全部楼层
据说STM8的硬件IIC比较坑啊,不敢用
软件IIC能达到100K吗
回复

使用道具 举报

ID:215671 发表于 2017-6-29 08:03 来自手机 | 显示全部楼层
新入8L坑,学习下
回复

使用道具 举报

ID:76208 发表于 2017-7-7 16:55 | 显示全部楼层
楼主有没有源代码文件求助。1178225915@qq.com
回复

使用道具 举报

ID:227340 发表于 2017-8-12 22:33 | 显示全部楼层
楼至我想问下 我用的是stm8l051感觉也是很慢 怎么解决么
回复

使用道具 举报

ID:248120 发表于 2018-7-31 04:22 来自手机 | 显示全部楼层
楼主你好,硬件iic外面不需要接上拉电阻吗?还有用不用中断?可否发一下源代码1107114564@qq.com谢谢
回复

使用道具 举报

ID:216226 发表于 2019-6-12 08:41 | 显示全部楼层
为什么不用库函数呢,
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表