孩子们,我是牢大,我回来辣,期待你的关注!
以前开始接触esp8266的时候用的是Arduino IDE来开发的,虽然很方便,但是现在突然想了解一下乐鑫的SDK是怎么玩的。目前电脑搭建的是ESP8266 NONOS SDK 3.0.5版本的SDK。然后手头上有一块逻辑分析仪,之前没用过就拿这个esp8266读mpu6050来练练手,但是我发现官方的iic库根本读不了,用逻辑分析仪看了下时序,才发现时序都是乱七八糟的,于是决定自己写一个例程顺便复习一下iic。esp8266用的是esp-12f开发板。废话不多说直接上代码。
#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, ¶, 1);
para = 0x08;
mpu6050_write_byte(ACCEL_CONFIG, ¶, 1);
para = 0x18;
mpu6050_write_byte(GYRO_CONFIG, ¶, 1);
para = 0x04;
mpu6050_write_byte(SMPLRT_DIV, ¶, 1);
para = 0x02;
mpu6050_write_byte(CONFIG, ¶, 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];
}
#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
/*
* 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语言标准库文件都添加不了。