基于ESP8266 NONOS SDK 3.0.5使用esp8266的模拟iic读取mpu6050

2023-12-22 11:24:50

孩子们,我是牢大,我回来辣,期待你的关注!


前言

以前开始接触esp8266的时候用的是Arduino IDE来开发的,虽然很方便,但是现在突然想了解一下乐鑫的SDK是怎么玩的。目前电脑搭建的是ESP8266 NONOS SDK 3.0.5版本的SDK。然后手头上有一块逻辑分析仪,之前没用过就拿这个esp8266读mpu6050来练练手,但是我发现官方的iic库根本读不了,用逻辑分析仪看了下时序,才发现时序都是乱七八糟的,于是决定自己写一个例程顺便复习一下iic。esp8266用的是esp-12f开发板。废话不多说直接上代码。


代码

1.mpu6050.c文件

#include "mpu6050.h"

/*=========================================
*iic延时函数,决定iic时钟频率
=========================================*/
void ICACHE_FLASH_ATTR
iic_mpu_delay(void)
{
	os_delay_us(2);//延时2微秒,IIC通信速率约145KHz
}

/*=========================================
*iic引脚初始化
=========================================*/
void ICACHE_FLASH_ATTR
iic_mpu_init(void)
{
	PIN_FUNC_SELECT(PIN_NAME_SCL, FUNC_SCL);	//选择SCL功能引脚
	PIN_FUNC_SELECT(PIN_NAME_SDA, FUNC_SDA);	//选择SDA功能引脚

	GPIO_OUTPUT_SET(SCL, 1);					//初始默认输出高电平
	GPIO_OUTPUT_SET(SDA, 1);
}

/*=========================================
*iic开始信号
=========================================*/
void ICACHE_FLASH_ATTR
iic_mpu_start(void)
{
	GPIO_OUTPUT_SET(SCL, 1);	//全部输出高电平空闲状态
	GPIO_OUTPUT_SET(SDA, 1);
	iic_mpu_delay();			//等待电平信号稳定

	GPIO_OUTPUT_SET(SDA, 0);	//在SCL为高电平时SDA下降沿为开始信号
	iic_mpu_delay();			//等待电平信号稳定
	GPIO_OUTPUT_SET(SCL, 0);	//拉低SCL为写或读数据做准备
	iic_mpu_delay();			//等待电平信号稳定
}

/*=========================================
*iic停止信号
=========================================*/
void ICACHE_FLASH_ATTR
iic_mpu_stop(void)
{
	GPIO_OUTPUT_SET(SDA, 0);	//先拉低SDA
	GPIO_OUTPUT_SET(SCL, 1);	//保持SCL为高电平
	iic_mpu_delay();			//等待电平信号稳定

	GPIO_OUTPUT_SET(SDA, 1);	//在SCL为高电平时SDA上升沿为停止信号
}

/*=========================================
*iic写一个字节
=========================================*/
void ICACHE_FLASH_ATTR
iic_mpu_write_byte(uint8_t data)
{
	uint8_t i;
	for(i=0;i<8;i++)
	{
		GPIO_OUTPUT_SET(SDA, (data>>(7-i))&0x01);	//发送数据位由高到低
		GPIO_OUTPUT_SET(SCL, 1);					//SCL为高电平,SDA应保持不变
		iic_mpu_delay();
		GPIO_OUTPUT_SET(SCL, 0);					//只有SCL为低电平,SDA才可以改变
		iic_mpu_delay();							//时钟脉冲频率由这两个延时决定
	}
}

/*=========================================
*iic读一个字节
=========================================*/
uint8_t ICACHE_FLASH_ATTR
iic_mpu_read_byte(void)
{
	uint8_t i, data=0;
	GPIO_DIS_OUTPUT(SDA);							//配置SDA引脚为输入,否则可能无法正确读取引脚电平信号
	for (i = 0; i < 8; i++)
	{
		GPIO_OUTPUT_SET(SCL, 1);					//SCL为高电平,SDA应保持不变
		iic_mpu_delay();
		data |= GPIO_INPUT_GET(SDA) << (7-i);		//接收数据位由高到低
		GPIO_OUTPUT_SET(SCL, 0);					//只有SCL为低电平,SDA才可以改变
		iic_mpu_delay();							//时钟脉冲频率由这两个延时决定
	}
	return data;
}

/*=========================================
*iic读应答信号,0-应答,1-非应答
=========================================*/
uint8_t ICACHE_FLASH_ATTR
iic_mpu_read_ack(void)
{
	uint8_t ack=1;
	GPIO_DIS_OUTPUT(SDA);				//配置SDA引脚为输入,否则可能无法正确读取引脚电平信号
	GPIO_OUTPUT_SET(SCL, 1);			//SCL为高电平,SDA应保持不变
	iic_mpu_delay();
	ack = GPIO_INPUT_GET(SDA);			//接收应答信号,0-应答,1-非应答
	GPIO_OUTPUT_SET(SCL, 0);			//只有SCL为低电平,SDA才可以改变
	iic_mpu_delay();					//时钟脉冲频率由这两个延时决定
	return ack;
}

/*=========================================
*iic写应答信号,0-应答,1-非应答
=========================================*/
void ICACHE_FLASH_ATTR
iic_mpu_send_ack(uint8_t ack)
{
	GPIO_OUTPUT_SET(SDA, ack);			//发送应答信号,0-应答,1-非应答
	GPIO_OUTPUT_SET(SCL, 1);			//SCL为高电平,SDA应保持不变
	iic_mpu_delay();
	GPIO_OUTPUT_SET(SCL, 0);			//只有SCL为低电平,SDA才可以改变
	iic_mpu_delay();					//时钟脉冲频率由这两个延时决定
}


///
///
///


void ICACHE_FLASH_ATTR
mpu6050_write_byte(uint8_t reg_addr, uint8_t *data, uint8_t length)
{
	uint8_t i;
	iic_mpu_start();
	iic_mpu_write_byte(MPU6050_ADDR << 1 | 0);
	if(iic_mpu_read_ack())
	{
		iic_mpu_stop();
		return;
	}
	iic_mpu_write_byte(reg_addr);
	if (iic_mpu_read_ack())
	{
		iic_mpu_stop();
		return;
	}
	for(i=0;i<length;i++)
	{
		iic_mpu_write_byte(*(data+i));
		if (iic_mpu_read_ack())
		{
			iic_mpu_stop();
			return;
		}
	}
	iic_mpu_stop();
	return;
}

void ICACHE_FLASH_ATTR
mpu6050_read_byte(uint8_t reg_addr, uint8_t *data, uint8_t length)
{
	uint8_t i;
	iic_mpu_start();
	iic_mpu_write_byte(MPU6050_ADDR << 1 | 0);
	if(iic_mpu_read_ack())
	{
		iic_mpu_stop();
		return;
	}
	iic_mpu_write_byte(reg_addr);
	if (iic_mpu_read_ack())
	{
		iic_mpu_stop();
		return;
	}
	iic_mpu_stop();

	iic_mpu_start();
	iic_mpu_write_byte(MPU6050_ADDR << 1 | 1);
	if (iic_mpu_read_ack())
	{
		iic_mpu_stop();
		return;
	}
	for(i=0;i<length;i++)
	{
		*(data+i) = iic_mpu_read_byte();
		iic_mpu_send_ack(i==(length-1)?1:0);
	}
	iic_mpu_stop();
}

void ICACHE_FLASH_ATTR
mpu6050_init(void)
{
	uint8_t para;
	iic_mpu_init();

	para = 0x00;
	mpu6050_write_byte(PWR_MGMT_1, &para, 1);

	para = 0x08;
	mpu6050_write_byte(ACCEL_CONFIG, &para, 1);

	para = 0x18;
	mpu6050_write_byte(GYRO_CONFIG, &para, 1);

	para = 0x04;
	mpu6050_write_byte(SMPLRT_DIV, &para, 1);

	para = 0x02;
	mpu6050_write_byte(CONFIG, &para, 1);
}

void ICACHE_FLASH_ATTR
mpu6050_get_data(Axis *gyro, Axis *acc)
{
	uint8_t data[14];

	mpu6050_read_byte(ACCEL_XOUT_H, data, 14);

	acc->x = (uint16_t) data[0] << 8 | data[1];
	acc->y = (uint16_t) data[2] << 8 | data[3];
	acc->z = (uint16_t) data[4] << 8 | data[5];

	gyro->x = (uint16_t) data[8] << 8 | data[9];
	gyro->y = (uint16_t) data[10] << 8 | data[11];
	gyro->z = (uint16_t) data[12] << 8 | data[13];
}

2.mpu6050.h文件

#ifndef _MPU6050_H_
#define _MPU6050_H_

#include "osapi.h"
#include "ets_sys.h"
#include "user_interface.h"
#include "gpio.h"

//iic引脚宏定义
#define PIN_NAME_SCL	PERIPHS_IO_MUX_GPIO5_U
#define PIN_NAME_SDA	PERIPHS_IO_MUX_GPIO4_U
#define FUNC_SCL		FUNC_GPIO5
#define FUNC_SDA		FUNC_GPIO4
#define SCL				5
#define SDA				4


//mpu6050内部寄存器宏定义
#define MPU6050_ADDR    0x68
#define SMPLRT_DIV      0x19
#define CONFIG          0x1A
#define GYRO_CONFIG     0x1B
#define SMPLRT_DIV      0x19
#define CONFIG          0x1A
#define GYRO_CONFIG     0x1B
#define ACCEL_CONFIG    0x1C
#define ACCEL_XOUT_H    0x3B
#define ACCEL_XOUT_L    0x3C
#define ACCEL_YOUT_H    0x3D
#define ACCEL_YOUT_L    0x3E
#define ACCEL_ZOUT_H    0x3F
#define ACCEL_ZOUT_L    0x40
#define TEMP_OUT_H      0x41
#define TEMP_OUT_L      0x42
#define GYRO_XOUT_H     0x43
#define GYRO_XOUT_L     0x44
#define GYRO_YOUT_H     0x45
#define GYRO_YOUT_L     0x46
#define GYRO_ZOUT_H     0x47
#define GYRO_ZOUT_L     0x48
#define PWR_MGMT_1      0x6B
#define WHO_AM_I        0x75


typedef struct{
  short x;
  short y;
  short z;
}Axis;


//iic通信相关函数声明
void iic_mpu_delay(void);
void iic_mpu_init(void);
void iic_mpu_start(void);
void iic_mpu_stop(void);
void iic_mpu_write_byte(uint8_t data);
uint8_t iic_mpu_read_byte(void);
uint8_t iic_mpu_read_ack(void);
void iic_mpu_send_ack(uint8_t ack);


//mpu6050通信相关函数声明
void mpu6050_init(void);
void mpu6050_write_byte(uint8_t reg_addr, uint8_t *data, uint8_t length);
void mpu6050_read_byte(uint8_t reg_addr, uint8_t *data, uint8_t length);

void mpu6050_get_data(Axis *gyro, Axis *acc);

#endif


3.user_main.c文件

/*
 * ESPRESSIF MIT License
 *
 * Copyright (c) 2016 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
 *
 * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
 * it is free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#include "ets_sys.h"
#include "osapi.h"

#include "user_interface.h"

#include "driver\uart.h"
#include "mpu6050.h"

os_timer_t ptimer;

void pfunction(void)
{
	Axis gyro, acc;
	mpu6050_get_data(&gyro, &acc);
	os_printf("%5d, %5d, %5d\r\n", gyro.x, gyro.y, gyro.z);
	os_printf("%5d, %5d, %5d\r\n\r\n", acc.x, acc.y, acc.z);
}

/******************************************************************************
 * FunctionName : user_init
 * Description  : entry of user application, init user function here
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
user_init(void)
{
	uart_init(115200, 115200);
	mpu6050_init();

    os_timer_disarm(&ptimer);
    os_timer_setfn(&ptimer, (os_timer_func_t *)pfunction, NULL);
    os_timer_arm(&ptimer, 10, 1);
}

//
//
//
//
//

//和flash有关不能删

#define SYSTEM_PARTITION_OTA_SIZE							0x6A000
#define SYSTEM_PARTITION_OTA_2_ADDR							0x81000
#define SYSTEM_PARTITION_RF_CAL_ADDR						0x3fb000
#define SYSTEM_PARTITION_PHY_DATA_ADDR						0x3fc000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR				0x3fd000
#define SYSTEM_PARTITION_CUSTOMER_PRIV_PARAM_ADDR           0x7c000

#define SYSTEM_PARTITION_CUSTOMER_PRIV_PARAM                SYSTEM_PARTITION_CUSTOMER_BEGIN

static const partition_item_t at_partition_table[] = {
    { SYSTEM_PARTITION_BOOTLOADER, 						0x0, 												0x1000},
    { SYSTEM_PARTITION_OTA_1,   						0x1000, 											SYSTEM_PARTITION_OTA_SIZE},
    { SYSTEM_PARTITION_OTA_2,   						SYSTEM_PARTITION_OTA_2_ADDR, 						SYSTEM_PARTITION_OTA_SIZE},
    { SYSTEM_PARTITION_RF_CAL,  						SYSTEM_PARTITION_RF_CAL_ADDR, 						0x1000},
    { SYSTEM_PARTITION_PHY_DATA, 						SYSTEM_PARTITION_PHY_DATA_ADDR, 					0x1000},
    { SYSTEM_PARTITION_SYSTEM_PARAMETER, 				SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR, 			0x3000},
    { SYSTEM_PARTITION_CUSTOMER_PRIV_PARAM,             SYSTEM_PARTITION_CUSTOMER_PRIV_PARAM_ADDR,          0x1000},
};

void ICACHE_FLASH_ATTR user_pre_init(void)
{
    if(!system_partition_table_regist(at_partition_table, sizeof(at_partition_table)/sizeof(at_partition_table[0]),SPI_FLASH_SIZE_MAP)) {
		os_printf("system_partition_table_regist fail\r\n");
		while(1);
	}
}

时序图

读取原始数据的时序图
在这里插入图片描述
在代码里面有注释延时2微秒,这里测出时钟频率大概为145KHz。如果延时1微秒的话大概就是200-333KHz
在这里插入图片描述
写一个字节,前面SDA在SCL为高电平的时候下降沿就是开始信号,后面每次SCL高电平都会发一位数据,最后第9个高电平为接收应答信号。
在这里插入图片描述
读一个字节,顺便看一下停止信号,就是在SCL高电平时SDA上升沿的时候,另外也是SCL高电平读取一位数据,然后最后第9个高电平为发送非应答信号。
在这里插入图片描述
文件目录
在这里插入图片描述

接线说明

VCC----3.3V/5V
GND----GND
SCL-----D1(GPIO5)
SDA---- D2(GPIO4)
可以在mpu6050头文件里面改引脚,除了esp-12f不能用的那几个引脚,其他都可以用。

以上为个人理解记录,仅供参考!
最后,我是牢大,期待你的关注!
有没有大佬知道怎么解决添加不了math.h头文件的问题吗还有string.h,好像是c语言标准库文件都添加不了。

文章来源:https://blog.csdn.net/lls3154850643/article/details/132782510
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。