技术标签: esp32 mpu6050 ESP32-C3F 嵌入式硬件 单片机
MPU6050是一个带有3轴加速度计和3轴陀螺仪的传感器,也称之为惯性测量单元(IMU)传感器:
陀螺仪测量回转的速度(rad/s),是在X、Y、Z三个轴的角位置变化,分别称为roll、pitch、yaw,这可以使我们判断物体的朝向:
加速度计用来测量加速度,也就是物体速度的变化率。
MPU6050的I2C从机地址是110100X
,7bit长度,最低位X由AD0引脚来控制。
MPU6050支持的最大I2C速度为400kHz。
I2C Device Library(i2cdevlib)是一组基本统一且文档良好的类的集合,为I2C设备提供简单直观的接口。
Github仓库地址:https://github.com/jrowberg/i2cdevlib
拉取到之后,将其中Arduino下的I2Cdev文件夹和MPU6050文件夹复制到platformIO工程的lib路径中。
包含头文件:
#include "I2Cdev.h"
#include "MPU6050.h"
MPU6050_Base(uint8_t address=MPU6050_DEFAULT_ADDRESS, void *wireObj=0);
构造函数中address参数是指MPU6050的从机地址,
默认是0x68(AD0引脚为低电平),如果AD0引脚接为高电平,可以指定地址为0x69。
/** Power on and prepare for general usage.
* This will activate the device and take it out of sleep mode (which must be done
* after start-up). This function also sets both the accelerometer and the gyroscope
* to their most sensitive settings, namely +/- 2g and +/- 250 degrees/sec, and sets
* the clock source to use the X Gyro for reference, which is slightly better than
* the default internal clock source.
*/
void MPU6050_Base::initialize();
/** Verify the I2C connection.
* Make sure the device is connected and responds as expected.
* @return True if connection is valid, false otherwise
*/
bool MPU6050_Base::testConnection() {
return getDeviceID() == 0x34;
}
/** Get raw 6-axis motion sensor readings (accel/gyro).
* Retrieves all currently available motion sensor values.
* @param ax 16-bit signed integer container for accelerometer X-axis value
* @param ay 16-bit signed integer container for accelerometer Y-axis value
* @param az 16-bit signed integer container for accelerometer Z-axis value
* @param gx 16-bit signed integer container for gyroscope X-axis value
* @param gy 16-bit signed integer container for gyroscope Y-axis value
* @param gz 16-bit signed integer container for gyroscope Z-axis value
* @see getAcceleration()
* @see getRotation()
* @see MPU6050_RA_ACCEL_XOUT_H
*/
void MPU6050_Base::getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz);
#include <Arduino.h>
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"
class IMU {
private:
MPU6050 imu;
int16_t ax, ay, az;
int16_t gx, gy, gz;
int16_t temperature;
public:
int init();
void update();
int16_t getAccelX();
int16_t getAccelY();
int16_t getAccelZ();
int16_t getGyroX();
int16_t getGyroY();
int16_t getGyroZ();
int16_t getTemperature();
};
IMU imu;
void setup() {
Serial.begin(115200);
imu.init();
}
void loop() {
imu.update();
// display tab-separated accel/gyro x/y/z values
Serial.print("a/g/t:\t");
Serial.print(imu.getAccelX()); Serial.print("\t");
Serial.print(imu.getAccelY()); Serial.print("\t");
Serial.print(imu.getAccelZ()); Serial.print("\t");
Serial.print(imu.getGyroX()); Serial.print("\t");
Serial.print(imu.getGyroY()); Serial.print("\t");
Serial.print(imu.getGyroZ()); Serial.print("\t");
Serial.println(imu.getTemperature());
delay(100);
}
int IMU::init()
{
// initialize i2c
Wire.begin();
Wire.setClock(400000);
// initialize device
Serial.println("Initializing I2C devices...");
imu.initialize();
// verify connection
Serial.println("Testing device connections...");
if (imu.testConnection()) {
Serial.println("MPU6050 connection successful");
return 0;
} else {
Serial.println("MPU6050 connection failed");
return -1;
}
}
void IMU::update()
{
// read raw accel/gyro measurements from device
imu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
// read temperature
temperature = imu.getTemperature();
}
int16_t IMU::getAccelX()
{
return ax;
}
int16_t IMU::getAccelY()
{
return ay;
}
int16_t IMU::getAccelZ()
{
return az;
}
int16_t IMU::getGyroX()
{
return gx;
}
int16_t IMU::getGyroY()
{
return gy;
}
int16_t IMU::getGyroZ()
{
return gz;
}
int16_t IMU::getTemperature()
{
return temperature;
}
添加 引脚GPIO16用来连接MPU6050中断引脚:
#include <Arduino.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"
#define INTERRUPT_PIN 16
class IMU {
private:
MPU6050 imu;
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
int16_t temperature;
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
public:
int init(uint8_t pin);
void update();
float getYaw();
float getPitch();
float getRoll();
int16_t getTemperature();
};
IMU imu;
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void setup() {
Serial.begin(115200);
imu.init(INTERRUPT_PIN);
}
void loop() {
imu.update();
Serial.print("ypr\t");
Serial.print(imu.getYaw());
Serial.print("\t");
Serial.print(imu.getPitch());
Serial.print("\t");
Serial.println(imu.getRoll());
delay(100);
}
int IMU::init(uint8_t pin)
{
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
// initialize i2c
Wire.begin();
Wire.setClock(400000);
// initialize device
Serial.println("Initializing I2C devices...");
imu.initialize();
// verify connection
Serial.println("Testing device connections...");
if (imu.testConnection()) {
Serial.println("MPU6050 connection successful");
} else {
Serial.println("MPU6050 connection failed");
return -1;
}
pinMode(pin, INPUT);
// load and configure the DMP
devStatus = imu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
imu.setXGyroOffset(220);
imu.setYGyroOffset(76);
imu.setZGyroOffset(-85);
imu.setZAccelOffset(1788); // 1688 factory default for my test chip
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
// Calibration Time: generate offsets and calibrate our MPU6050
imu.CalibrateAccel(6);
imu.CalibrateGyro(6);
imu.PrintActiveOffsets();
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
imu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
Serial.println(F(")..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = imu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = imu.dmpGetFIFOPacketSize();
} else {
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
}
void IMU::update()
{
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
// MPU control/status vars
uint8_t fifoBuffer[64]; // FIFO storage buffer
// if programming failed, don't try to do anything
if (!dmpReady) return;
// read a packet from FIFO
if (imu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
// Get the Latest packet
// display Euler angles in degrees
imu.dmpGetQuaternion(&q, fifoBuffer);
imu.dmpGetGravity(&gravity, &q);
imu.dmpGetYawPitchRoll(ypr, &q, &gravity);
}
// read temperature
temperature = imu.getTemperature();
}
float IMU::getYaw()
{
return ypr[0] * 180/M_PI;
}
float IMU::getPitch()
{
return ypr[1] * 180/M_PI;
}
float IMU::getRoll()
{
return ypr[2] * 180/M_PI;
}
int16_t IMU::getTemperature()
{
return temperature;
}
下载地址:https://processing.org/download。
添加格式定义:
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = {
'$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
删除之前的打印格式:
Serial.print("ypr\t");
Serial.print(imu.getYaw());
Serial.print("\t");
Serial.print(imu.getPitch());
Serial.print("\t");
Serial.println(imu.getRoll());
新增一个IMU类的发送数据函数:
void IMU::sendDataToProcessing()
{
// display quaternion values in InvenSense Teapot demo format:
teapotPacket[2] = fifoBuffer[0];
teapotPacket[3] = fifoBuffer[1];
teapotPacket[4] = fifoBuffer[4];
teapotPacket[5] = fifoBuffer[5];
teapotPacket[6] = fifoBuffer[8];
teapotPacket[7] = fifoBuffer[9];
teapotPacket[8] = fifoBuffer[12];
teapotPacket[9] = fifoBuffer[13];
Serial.write(teapotPacket, 14);
teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
}
在update函数调用之后,调用该函数发送数据到上位机。
修改完毕,烧录代码。
上位机为lib\MPU6050\examples\MPU6050_DMP6\Processing\MPUTeapot\MPUTeapot.pde
,使用processing打开。
修改连接ESP32的串口:
文章浏览阅读1.7w次,点赞20次,收藏310次。主要参考:(1)使用sed输出文件的指定行(2)sed 字符串替换(3)sed之添加空行仅供自己学习使用,如有侵权,请联系删除获取指定的行(1) 获取test.txt的第二行,输入到屏幕sed -n 2p test.txt(1) 获取test.txt的第一行至第二行sed -n 1,2p test.txt# 加不加引号均可 sed -n ‘1,2p’ test.txt(2)获取test.txt的第二行至最后一行sed -n '2,$p' test.txt# 必须加单引号(3_sed '/^$/d
文章浏览阅读1.4w次,点赞5次,收藏35次。音乐推荐数据集_million song dataset
文章浏览阅读7.1k次,点赞7次,收藏28次。之前我们简单介绍过 Go-zero 详见《Go-zero:开箱即用的微服务框架》。这次我们从动手实现一个 Blog 项目的用户模块出发,详细讲述 Go-zero 的使用。特别说明本文涉及的所有资料都已上传 Github 仓库 “kougazhang/go-zero-demo”, 感兴趣的同学可以自行下载。Go-zero 实战项目:blog本文以 blog 的网站后台为例,着重介绍一下如何使用 Go-zero 开发 blog 的用户模块。用户模块是后台管理系统常见的模块,它的功能大家也非常熟悉。管理用_gozero 验证参数
文章浏览阅读337次。系统调用open打开文件,函数声明如下:#include#include#includeint open(coust char *pathname,int flags);int open(const char *pathname,int flags,made_t mode);pathname 文件名称mode 文件权限调用成功时,返回值为所打开文件的文件描述符,反_ctx文档怎么打开 site:blog.csdn.net
文章浏览阅读3.2k次。http://blog.csdn.net/lanyzh0909/article/details/5543399查MSND,对SetCapture()函数的说明为:“该函数在属于当前线程的指定窗口里设置鼠标捕获。一旦窗口捕获了鼠标,所有鼠标输入都针对该窗口,无论光标是否在窗口的边界内。同一时刻只能有一个窗口捕获鼠标。如果鼠标光标在另一个线程创建的窗口上,只有当鼠标键按下时系统才将鼠标输入指向_c# releasecapture
文章浏览阅读442次。Python简单入门 交互式开发环境(IDLE)Windows下在开始菜单应用程序那里可以找到,而Unix在shell下输入idle即可运行。我选择的是Sublime Text的代码编辑器,下载安装插件SublimeREPL,下载完之后,在【Tool】【SublimeREPL】【Python】【Python】启动交互式开发环境>>> 1+5*2-38>>> 1/30>>> 1.0/30._maternal affection on the side of the former
文章浏览阅读703次。文件系统VFS内核4.0.4版本基本概念源码题外话:Linux内核从2.x和3.x到现在最新的4.x变化非常大,最直观的表现就是很多书上的内核代码已经无法直接继续使用,所以看看新的源码是非常有意义的! (下文中的内核源码都来自于 kernel 4.0.4 版本,本人都验证过正确,正文假设读者对 linux系统下mount命令有操作经验。另外,linux内核源码中关于文件操作的代码量比内_linux kernel 文件系统源码
文章浏览阅读1.7w次,点赞6次,收藏24次。字符(串)指针char *p ="hello world!"; //相当于:char *p; p="hello world!"; p是字符串"hello world!"的首地址。字符串"hello world"存放在数据段。p[0]='a'; //错误,不能修改。printf("%s",p); //打印出字..._c语言用二维数组存一个hello world在列里
文章浏览阅读1.3k次。一、InputStream..._fileinputstream和inputstream
文章浏览阅读1.3w次。https://nanxiao.me/use-subscription-manager-register-on-rhel/在RHEL系统中注册和使用subscription是两个过程:NOTE: With Red Hat Subscription-Manager, registration and utilization of a subscription is actually a t..._[root@localhost spdk]# sudo subscription-manager register registering to: su
文章浏览阅读2k次。前言: 笔记上一篇介绍了,go语言如何使用protobuf及生成go的protobuf文件,具体内容请见上一篇:go 与 protobuf 安装和使用1.protobuf文件定义及注意事项// [开始声明]syntax = "proto3"; //定义protobuf的包名称空间package message;// [结束声明]// [开始 java 选项配置]option java_package = "xxxx.core.message";opt..._golang可以连netty
文章浏览阅读3k次。文章来自作者维护的社区微信公众号【虚拟化云计算】)(目前有两个微信群《kvm虚拟化》和《openstack》,扫描二维码点击“云-交流”,进群交流提问)一。防火墙与安全组区别防火墙一般放在网关上,用来隔离子网之间的访问。因此,防火墙即服务也是在网络节点上(具体说来是在路由器命名空间中)来实现。防火墙可以在安全组之前隔离外部过来的恶意流量,但是对于同个子网内部不同虚拟网卡间的通..._安全组的防护对象是?