datasheet

STM32驱动MPU6050(二)——软件实现

2019-01-09来源: eefocus 关键字:STM32  驱动MPU6050  软件实现

软件实现将从下面三个部分来介绍:IIC通信;MPU6050数据读取;数据融合。


1. IIC通信

为了移植的方便,这里的 IIC 采用软件模拟的方式实现。关于 IIC 的基础知识介绍,可参考IIC专题(一)——基础知识准备。


下面以程序的实现过程,梳理一下 IIC 的通信时序。注:这里就采用正点原子的 mpu6050 的学习教程进行学习。


1.1 SDA 和SCL初始化


//初始化IIC

void MPU_IIC_Init(void)

{      

  GPIO_InitTypeDef  GPIO_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//先使能外设IO PORTC时钟 


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11; // 端口配置

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz

  GPIO_Init(GPIOC, &GPIO_InitStructure); //根据设定参数初始化GPIO 


  GPIO_SetBits(GPIOC,GPIO_Pin_12|GPIO_Pin_11); //PB10,PB11 输出高

 

}


对于 SDA 输入输出的方向切换,这里直接通过配置寄存器实现:

//IO方向设置

#define MPU_SDA_IN()  {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}

#define MPU_SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}


也可通过配置库函数配置来实现:


void MPU_SDA_OUT(void)

{

GPIO_InitTypeDef  GPIO_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);


GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOC, &GPIO_InitStructure);

}

void MPU_SDA_IN(void)

{

GPIO_InitTypeDef  GPIO_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);


GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOC, &GPIO_InitStructure);

}



1.2 IIC时序模拟实现

程序需要根据 IIC 的时序要求进行书写。注意:SDA和SCL需要接上拉电阻。


void MPU_IIC_Start(void)

{

MPU_SDA_OUT();     //sda线输出

MPU_IIC_SDA=1;     

MPU_IIC_SCL=1;

MPU_IIC_Delay();

  MPU_IIC_SDA=0;//START:when CLK is high,DATA change form high to low 

MPU_IIC_Delay();

MPU_IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 

}   

//产生IIC停止信号

void MPU_IIC_Stop(void)

{

MPU_SDA_OUT();//sda线输出

MPU_IIC_SCL=0;

MPU_IIC_SDA=0;//STOP:when CLK is high DATA change form low to high

  MPU_IIC_Delay();

MPU_IIC_SCL=1;  

MPU_IIC_SDA=1;//发送I2C总线结束信号

MPU_IIC_Delay();    

}



//IIC发送一个字节

//返回从机有无应答

//1,有应答

//0,无应答   

void MPU_IIC_Send_Byte(u8 txd)

{                        

    u8 t;   


MPU_SDA_OUT();     

    MPU_IIC_SCL=0;//拉低时钟开始数据传输

    for(t=0;t<8;t++)

    {              

        MPU_IIC_SDA=(txd&0x80)>>7;

        txd<<=1;   

MPU_IIC_SCL=1;

MPU_IIC_Delay(); 

MPU_IIC_SCL=0;

MPU_IIC_Delay();

    }  

}     

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   

u8 MPU_IIC_Read_Byte(unsigned char ack)

{

unsigned char i,receive=0;

MPU_SDA_IN();//SDA设置为输入

    for(i=0;i<8;i++ )

{

        MPU_IIC_SCL=0; 

        MPU_IIC_Delay();

MPU_IIC_SCL=1;

        receive<<=1;

        if(MPU_READ_SDA)

receive++;   

MPU_IIC_Delay(); 

    }  

    if (!ack)

        MPU_IIC_NAck();//发送nACK

    else

        MPU_IIC_Ack(); //发送ACK   

    return receive;

}


//等待应答信号到来

//返回值:1,接收应答失败

//        0,接收应答成功

u8 MPU_IIC_Wait_Ack(void)

{

u8 ucErrTime=0;


MPU_SDA_IN();      //SDA设置为输入  

MPU_IIC_SDA=1;

MPU_IIC_Delay();    

MPU_IIC_SCL=1;

MPU_IIC_Delay();  

while(MPU_READ_SDA)

{

ucErrTime++;

if(ucErrTime>250)

{

MPU_IIC_Stop();

return 1;

}

}

MPU_IIC_SCL=0;//时钟输出0    

return 0;  

//产生ACK应答

void MPU_IIC_Ack(void)

{

MPU_IIC_SCL=0;

MPU_SDA_OUT();

MPU_IIC_SDA=0;

MPU_IIC_Delay();

MPU_IIC_SCL=1;

MPU_IIC_Delay();

MPU_IIC_SCL=0;

}

//不产生ACK应答     

void MPU_IIC_NAck(void)

{

MPU_IIC_SCL=0;

MPU_SDA_OUT();

MPU_IIC_SDA=1;

MPU_IIC_Delay();

MPU_IIC_SCL=1;

MPU_IIC_Delay();

MPU_IIC_SCL=0;

}


1.3 IIC 通信写入读取数据


//IIC写一个字节 

//reg:寄存器地址

//data:数据

//返回值:0,正常

//    其他,错误代码

u8 MPU_Write_Byte(u8 reg,u8 data)  

    MPU_IIC_Start(); 

MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令

if(MPU_IIC_Wait_Ack()) //等待应答

{

MPU_IIC_Stop();  

return 1;

}

    MPU_IIC_Send_Byte(reg); //写寄存器地址

    MPU_IIC_Wait_Ack(); //等待应答 

MPU_IIC_Send_Byte(data);//发送数据

if(MPU_IIC_Wait_Ack()) //等待ACK

{

MPU_IIC_Stop();  

return 1;  

}  

    MPU_IIC_Stop();  

return 0;

}


//IIC读一个字节 

//reg:寄存器地址 

//返回值:读到的数据

u8 MPU_Read_Byte(u8 reg)

{

u8 res;

    MPU_IIC_Start(); 

MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令

MPU_IIC_Wait_Ack(); //等待应答 

    MPU_IIC_Send_Byte(reg); //写寄存器地址

    MPU_IIC_Wait_Ack(); //等待应答

    MPU_IIC_Start();

MPU_IIC_Send_Byte((MPU_ADDR<<1)|1);//发送器件地址+读命令

    MPU_IIC_Wait_Ack(); //等待应答 

res=MPU_IIC_Read_Byte(0);//读取数据,发送nACK 

    MPU_IIC_Stop(); //产生一个停止条件 

return res;

}


//IIC连续写

//addr:器件地址 

//reg:寄存器地址

//len:写入长度

//buf:数据区

//返回值:0,正常

//    其他,错误代码

u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)

{

u8 i; 

    MPU_IIC_Start(); 

MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令

if(MPU_IIC_Wait_Ack()) //等待应答

{

MPU_IIC_Stop();  

return 1;

}

    MPU_IIC_Send_Byte(reg); //写寄存器地址

    MPU_IIC_Wait_Ack(); //等待应答

for(i=0;i

{

MPU_IIC_Send_Byte(buf[i]); //发送数据

if(MPU_IIC_Wait_Ack()) //等待ACK

{

MPU_IIC_Stop();  

return 1;  

}

}    

    MPU_IIC_Stop();  

return 0;

//IIC连续读

//addr:器件地址

//reg:要读取的寄存器地址

//len:要读取的长度

//buf:读取到的数据存储区

//返回值:0,正常

//    其他,错误代码

u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)

  MPU_IIC_Start(); 

MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令

if(MPU_IIC_Wait_Ack()) //等待应答

{

MPU_IIC_Stop();  

return 1;

}

    MPU_IIC_Send_Byte(reg); //写寄存器地址

    MPU_IIC_Wait_Ack(); //等待应答

    MPU_IIC_Start();

MPU_IIC_Send_Byte((addr<<1)|1);//发送器件地址+读命令

    MPU_IIC_Wait_Ack(); //等待应答 

while(len)

{

if(len==1)

*buf=MPU_IIC_Read_Byte(0);//读数据,发送nACK 

else 

*buf=MPU_IIC_Read_Byte(1); //读数据,发送ACK  

len--;

buf++; 

}    

    MPU_IIC_Stop(); //产生一个停止条件 

return 0;

}


至此,与 IIC 相关的代码已经实现了,上面的程序只要是 IIC 通信的设备,均可以使用。


2. MPU6050读取数据

首先需要进行设备初始化,为数据通信做好准备。


2.1 分为以下几个步骤:

(来自原子教程)


(1) 初始化 IIC 接口


MPU6050 采用 IIC 与 STM32F1 通信,需要先初始化与 MPU6050 连接的 SDA和 SCL 数据线。


(2)复位 MPU6050


这一步让 MPU6050 内部所有寄存器恢复默认值,通过对电源管理寄存器 1(0X6B)的bit7 写 1 实现。 复位后, 电源管理寄存器 1 恢复默认值(0X40),然后必须设置该寄存器为0X00,以唤醒 MPU6050,进入正常工作状态。


(3)设置角速度传感器(陀螺仪)和加速度传感器的满量程范围


这一步设置两个传感器的满量程范围(FSR),分别通过陀螺仪配置寄存器(0X1B)和加速度传感器配置寄存器(0X1C)设置。一般设置陀螺仪的满量程范围为±2000dps,加速度传感器的满量程范围为±2g。


(4)其他参数设置


需要配置的参数有:关闭中断、关闭 AUX IIC 接口、禁止 FIFO、设置陀螺仪采样率和设置数字低通滤波器(DLPF)等。


这里不用中断方式读取数据,关闭中断即可,也没用到 AUX IIC 接口外接其他传感器,同样的关闭这些接口。分别通过中断使能寄存器(0X38)和用户控制寄存器(0X6A)控制。 MPU6050 可以使用 FIFO 存储传感器数据,此处没有用到,关闭所有 FIFO 通道,这个通过 FIFO 使能寄存器(0X23)控制,默认都是 0(即禁止 FIFO),所以用默认值就可以了。陀螺仪采样率通过采样率分频寄存器(0X19)控制,这个采样率一般设置为 50 。数字低通滤波器(DLPF)则通过配置寄存器(0X1A)设置,一般设置 DLPF 为带宽的 1/2 。


(5)配置系统时钟源并使能角速度传感器和加速度传感器


系统时钟源同样是通过电源管理寄存器 1(0X1B)来设置,该寄存器的最低三位用于设置系统时钟源选择,默认值是 0(内部 8M RC

[1] [2] [3]

关键字:STM32  驱动MPU6050  软件实现

编辑:什么鱼 引用地址:http://www.eeworld.com.cn/mcu/2019/ic-news010942848.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32驱动NRF24L01
下一篇:STM32驱动WS2812D全彩LED

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32:STM32库函数配置

stm32 固件库V3.0以上的版本,main等源文件中不再直接包含stm32f10x_conf.h,而是stm32f10x.h,stm32f10x.h则定义了启动设置,以及所有寄存器宏定义,此文件中需要注意的有:使用V3.0以上版本固件库的方法如下:1.选择device(配置函数STM32F10x.h,具体配置方法如下)在STM32F10x.h中有如下代码:#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined
发表于 2019-01-17
STM32:STM32库函数配置

STM32:STM32学习记录1:MDK基本数据类型及代码优化

大概一年前开始接触STM32,当时就被它的库函数开发所吸引,但是迫于各种压力放弃了学习,一直在使用所谓稳定的单片机来开发(忍不住要吐槽),现在终于有时间了,开始自己的兴趣之旅喽!!现在网上有各种大牛的经验文档使我受益匪浅,也感谢室友的无私帮助!!!大概看了一下大牛的经验文档,好像没有一个提到MDK的基本数据类型的,自己找找看在MDK的帮助里面有。打开MDK-----help----uVision help----RealView Compiler Reference Guide----C and C++ implementation details----C and C++ implementation
发表于 2019-01-17
STM32:STM32学习记录1:MDK基本数据类型及代码优化

STM32:STM32学习记录5: 外部中断

配置流程:1:系统时钟初始化,包括系统时钟和要开放的IO口或者功能的时钟配置。2:IO口初始化,包括引脚,速率,输入输出模式等。3:NVIC 中断向量配置 ,中断向量基地址和优先级的配置。4:EXTI 中断/事件控制器,使能或失能外部线路,使能的模式(事件请求和中断请求),边沿触发模式,状态等。说明:1:主函数写在main.c中,中断函数写在stm32f10x_it.c 中,找到相应的中断函数(一般都是空白),加入自己的中断代码即可。2:中断函数名在startup_stm32f10x_xx.s中查阅3:清除 EXT13 线路的挂起位  注意此处一定要清除!!!!!!!!在EXTI_PR寄存器中3:NVIC一般配
发表于 2019-01-17

STM32学习记录——printf函数重定位

功能: 重定位printf函数,使printf作为串口打印输出函数。代替usart_send_string()函数步骤: usart.c中包含USART初始化函数 1、USART初始化(使能时钟、使能GPIO、GPIO和USART初始化) 2、打开USART 3、在usart.c中加入如下代码#ifdef __GNUC__     /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf      
发表于 2019-01-17

STM32USART串口调节与printf重定义

首先,printf重定义后可以直接使用printf函数从串口发送数据在usart.c中添加代码:#ifdef __GNUC__  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf     set to 'Yes') calls __io_putchar() */  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else  #define PUTCHAR_PROTOTYPE int f
发表于 2019-01-17

STM32中使用标准库重定义printf()函数

//重定义函数1PUTCHAR_PROTOTYPE{ /* Place your implementation of fputc here */ /* e.g. write a character to the USART */  USART_SendData(USART1, (uint8_t) ch);   /* 循环等待直到发送结束*/  while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)  {}   return ch;}//重定义函数2 int fputc
发表于 2019-01-17

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2018 EEWORLD.com.cn, Inc. All rights reserved