常见I2C设备 的使用
常见I2C设备 的使用
I2C的使用还是比较简单的,Arduino固件自带的Wire库足以让我们实现通信。但鉴于常见的模块别人已经开发了好几十年了,直接调用驱动库。
0.96寸OLED屏幕 的使用(内置SSD1306驱动芯片)
OLED,即有机发光二极管(Organic Light Emitting Diode)。OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被称为是第三代显示技术。
LCD 都需要背光,而 OLED 不需要,因为它是自发光的。这样同样的显示 OLED 效果要来得好一些。以目前的技术,OLED 的尺寸还难以大型化,但是分辨率确可以做到很高。
我们今天用到的屏幕是 0.96 寸的 SSD1306 芯片驱动的 OLED 屏幕。ta的分辨率是 128*64,意思就是横向有 128 个像素点,纵向有 64 个。
基于Adafruit OLED库
首先是接线:
OLED屏幕----------ESP32S3
SDA --------->>> GPIO3
SCL --------->>> GPIO46
#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#define SDA1 3
#define SCl1 46
// 创建oled对象,其中第一个参数128是屏幕的宽度,第二个参数64是屏幕高度,第三个参数是I2C地址
// 第四个参数是屏幕的RST引脚,我们的屏幕没有RST引脚,给-1即可
Adafruit_SSD1306 oled(128, 64, &Wire1, -1);
void setup()
{
// 注意,在刚刚的创建对象时我们指定了硬件I2C_1(在arduino固件中称为Wire1),所以接下来
// 要初始化Wire1
Wire1.begin(SDA1, SCL1);
// 接下来是初始化屏幕的必要操作
oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);// 设置电压3.3v和屏幕的地址0x3c
oled.setTextColor(SSD1306_WHITE);//设置字体颜色,不管你的屏幕什么颜色,直接给白色就好
//必要操作已完成,接下来是个性化操作
oled.setTextSize(1);//设置字体大小,1是最小,2已经有点大了,可以自己去看看实际效果
到此,屏幕初始化完成了,可以选择在其它地方进行显示了,例如想在初始化屏幕之后就显示一个文本
oled.clearDisplay();// 先清屏,防止单片机复位后之前现实的内容和接下来要显示的文本重叠
oled.setCursor(0, 0);// 设置光标位置
oled.print("HELLO");
oled.setCursor(0, 10);
oled.print("ESP32S3");
oled.display();// 最后调用这个方法将屏幕的内容显示出来,不可或缺
delay(2000);// 延时2秒看看现象
}
void loop()
{
//此处展示如何打印变量
for (int i = 0; i <= 123; i++)
{
oled.clearDisplay();
oled.setCursor(0, 0);
oled.printf("current i: %d", i);
oled.display();
delay(10);//此处延时是为了更好地观察现象,一般来说不需要在刷新屏幕中使用延时
}
for (float i = 0.0f; i <= 1.23; i+=0.01)
{
oled.clearDisplay();
oled.setCursor(0, 0);
oled.print("current i: "+String(i));
oled.display();
delay(10);//此处延时是为了更好地观察现象,一般来说不需要在刷新屏幕中使用延时
}
}
基于U8g2库(感兴趣可以自主探素)
#include <Arduino.h>
#include <>
void setup(){}
void loop(){}
MPU6050六轴姿态传感 的使用(调用DMP和不调用DMP)
MPU6050是六轴陀螺仪,没有地磁校准, 本身不适合测量偏航角,不建议花费大量精力浪费在对它的优化上,不如买个九轴陀螺仪
图示为生活中常用的Yaw,Pitch,Roll


接下来还有更直观的例子:
1.航向yaw:

2.俯仰pitch:

3.横滚roll:

/*
MPU6050 DMP6
数字运动处理器(DMP)执行复杂的运动处理任务:
- 融合来自加速度计、陀螺仪和外部磁力计(如果有的话)的数据,补偿单个传感器的噪声和误差。
- 检测特定类型的运动,而无需连续监控原始传感器数据。
- 减轻微控制器的工作负担。
- 输出经过处理的数据,如四元数、欧拉角和重力向量。
该代码包括自动校准和偏移生成任务。支持多种输出格式。
该代码与茶壶项目兼容,使用茶壶输出格式。
电路:除了连接3.3V、GND、SDA和SCL外,此示例程序还依赖于MPU6050的INT引脚连接到Arduino的外部中断#0引脚。
如果使用DMP 6.12固件版本,茶壶处理示例可能会因FIFO结构变化而失效。
查看完整的MPU6050库文档:
https://github.com/ElectronicCats/mpu6050/wiki
*/
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
// #include "MPU6050_6Axis_MotionApps612.h" // 解除注释以使用DMP 6.12固件,并注释掉上面的库。
Adafruit_SSD1306 oled(128, 64, &Wire1, -1);
/* MPU6050默认I2C地址为0x68 */
MPU6050 mpu;
// MPU6050 mpu(0x69); // AD0高电平时使用
// MPU6050 mpu(0x68, &Wire1); // AD0低电平,但使用第二个Wire (TWI/I2C) 对象
/* 输出格式定义-------------------------------------------------------------------------------------------
- 使用 "OUTPUT_READABLE_YAWPITCHROLL" 输出航向角/俯仰角/横滚角(度),从FIFO中的四元数计算得出。
注意:航向角/俯仰角/横滚角存在万向锁问题。
- 使用 "OUTPUT_READABLE_QUATERNION" 输出四元数 [w, x, y, z] 格式。四元数不会出现万向锁问题,但在远程主机或软件环境中解析或处理效率较低。
- 使用 "OUTPUT_READABLE_EULER" 输出欧拉角(度),从FIFO中的四元数计算得出。
注意:欧拉角存在万向锁问题。
- 使用 "OUTPUT_READABLE_REALACCEL" 输出去除重力后的加速度分量。加速度参考框架未进行方向补偿。+X将始终是传感器的+X。
- 使用 "OUTPUT_READABLE_WORLDACCEL" 输出去除重力并调整为世界参考框架的加速度分量。如果没有磁力计,偏航角是相对的。
- 使用 "OUTPUT_TEAPOT" 输出与InvenSense茶壶演示匹配的数据。
-------------------------------------------------------------------------------------------------------------------------------*/
#define OUTPUT_READABLE_YAWPITCHROLL
// #define OUTPUT_READABLE_QUATERNION
// #define OUTPUT_READABLE_EULER
#define OUTPUT_READABLE_REALACCEL
// #define OUTPUT_READABLE_WORLDACCEL
// #define OUTPUT_TEAPOT
const int INTERRUPT_PIN = 19; // 定义外部中断#0引脚
bool blinkState;
/*---MPU6050控制/状态变量---*/
bool DMPReady = false; // DMP初始化成功后设置为true
uint8_t MPUIntStatus; // 存储实际的MPU中断状态字节
uint8_t devStatus; // 每次设备操作后的返回状态(0表示成功,非0表示错误)
uint16_t packetSize; // 预期的DMP数据包大小(默认为42字节)
uint8_t FIFOBuffer[64]; // FIFO存储缓冲区
/*---方向/运动变量---*/
Quaternion q; // [w, x, y, z] 四元数容器
VectorInt16 aa; // [x, y, z] 加速度传感器测量值
VectorInt16 gy; // [x, y, z] 陀螺仪传感器测量值
VectorInt16 aaReal; // [x, y, z] 去除重力后的加速度传感器测量值
VectorInt16 aaWorld; // [x, y, z] 世界框架下的加速度传感器测量值
VectorFloat gravity; // [x, y, z] 重力向量
float euler[3]; // [psi, theta, phi] 欧拉角容器
float ypr[3]; // [yaw, pitch, roll] 航向角/俯仰角/横滚角容器和重力向量
/*-用于InvenSense茶壶演示的数据包结构-*/
uint8_t teapotPacket[14] = {'$', 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, '\r', '\n'};
/*------中断检测例程------*/
volatile bool MPUInterrupt = false; // 表示MPU6050中断引脚是否变高
void DMPDataReady()
{
MPUInterrupt = true;
}
void setup()
{
Wire1.begin(SDA1, SCL1, 400000);
oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
oled.setTextColor(WHITE);
oled.setTextSize(1);
oled.setRotation(3);
oled.display();
oled.clearDisplay();
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 设置I2C时钟为400kHz。如果编译有困难,请注释掉此行
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(230400); // 115200波特率是茶壶演示所需的
while (!Serial)
;
/* 初始化设备 */
Serial.println(F("正在初始化I2C设备..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
/* 验证连接 */
Serial.println(F("测试MPU6050连接..."));
if (mpu.testConnection() == false)
{
Serial.println("MPU6050连接失败");
while (true)
;
}
else
{
Serial.println("MPU6050连接成功");
}
/* 等待串行输入 */
// Serial.println(F("\n发送任意字符以开始: "));
// while (Serial.available() && Serial.read()); // 清空缓冲区
// while (!Serial.available()); // 等待数据
// while (Serial.available() && Serial.read()); // 再次清空缓冲区
/* 初始化并配置DMP */
Serial.println(F("正在初始化DMP..."));
devStatus = mpu.dmpInitialize();
/* 设置陀螺仪偏移量,按最小灵敏度缩放 */
mpu.setXGyroOffset(0);
mpu.setYGyroOffset(0);
mpu.setZGyroOffset(0);
mpu.setXAccelOffset(0);
mpu.setYAccelOffset(0);
mpu.setZAccelOffset(0);
/* 确保成功(返回0表示成功) */
if (devStatus == 0)
{
mpu.CalibrateAccel(6); // 校准时间:生成偏移量并校准MPU6050
mpu.CalibrateGyro(6);
Serial.println("这些是当前有效的偏移量: ");
mpu.PrintActiveOffsets();
Serial.println(F("启用DMP...")); // 开启DMP
mpu.setDMPEnabled(true);
/* 启用Arduino中断检测 */
Serial.print(F("启用中断检测(Arduino外部中断 "));
Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
Serial.println(F(")..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), DMPDataReady, RISING);
MPUIntStatus = mpu.getIntStatus();
/* 设置DMP就绪标志,以便主循环函数知道可以使用它 */
Serial.println(F("DMP准备就绪!等待第一次中断..."));
DMPReady = true;
packetSize = mpu.dmpGetFIFOPacketSize(); // 获取预期的DMP数据包大小以供后续比较
}
else
{
Serial.print(F("DMP初始化失败(代码 ")); // 打印错误代码
Serial.print(devStatus);
Serial.println(F(")"));
// 1 = 初始内存加载失败
// 2 = DMP配置更新失败
}
}
void loop()
{
if (!DMPReady)
return; // 如果DMP编程失败,停止程序
/* 从FIFO读取一个数据包 */
if (mpu.dmpGetCurrentFIFOPacket(FIFOBuffer))
{ // 获取最新数据包
oled.clearDisplay();
#ifdef OUTPUT_READABLE_YAWPITCHROLL
/* 显示航向角/俯仰角/横滚角(度) */
mpu.dmpGetQuaternion(&q, FIFOBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
oled.setCursor(0, 0);
oled.print("Yaw:");
oled.setCursor(0, 8);
oled.print(" ");
oled.print(ypr[0] * 180 / M_PI);
oled.setCursor(0, 16);
oled.print("Pitch: ");
oled.setCursor(0, 24);
oled.print(" ");
oled.print(ypr[1] * 180 / M_PI);
oled.setCursor(0, 32);
oled.print("Roll:");
oled.setCursor(0, 40);
oled.print(" ");
oled.print(ypr[2] * 180 / M_PI);
#endif
#ifdef OUTPUT_READABLE_QUATERNION
/* 显示四元数值,采用简单矩阵形式:[w, x, y, z] */
mpu.dmpGetQuaternion(&q, FIFOBuffer);
Serial.print("quat\t");
Serial.print(q.w);
Serial.print("\t");
Serial.print(q.x);
Serial.print("\t");
Serial.print(q.y);
Serial.print("\t");
Serial.println(q.z);
#endif
#ifdef OUTPUT_READABLE_EULER
/* 显示欧拉角(度) */
mpu.dmpGetQuaternion(&q, FIFOBuffer);
mpu.dmpGetEuler(euler, &q);
Serial.print("euler\t");
Serial.print(euler[0] * 180 / M_PI);
Serial.print("\t");
Serial.print(euler[1] * 180 / M_PI);
Serial.print("\t");
Serial.println(euler[2] * 180 / M_PI);
#endif
#ifdef OUTPUT_READABLE_REALACCEL
/* 显示去除重力后的实际加速度 */
mpu.dmpGetQuaternion(&q, FIFOBuffer);
mpu.dmpGetAccel(&aa, FIFOBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
oled.setCursor(0, 48);
oled.print("Accel X: ");
oled.setCursor(0, 56);
oled.print(" ");
oled.print(aaReal.x);
oled.setCursor(0, 64);
oled.print("Accel Y: ");
oled.setCursor(0, 72);
oled.print(" ");
oled.print(aaReal.y);
oled.setCursor(0, 80);
oled.print("Accel Z: ");
oled.setCursor(0, 88);
oled.print(" ");
oled.print(aaReal.z);
#endif
#ifdef OUTPUT_READABLE_WORLDACCEL
/* 显示初始世界框架下的加速度,去除重力并根据已知的方向从四元数旋转 */
mpu.dmpGetQuaternion(&q, FIFOBuffer);
mpu.dmpGetAccel(&aa, FIFOBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
Serial.print("aworld\t");
Serial.print(aaWorld.x);
Serial.print("\t");
Serial.print(aaWorld.y);
Serial.print("\t");
Serial.println(aaWorld.z);
#endif
#ifdef OUTPUT_TEAPOT
/* 以InvenSense茶壶演示格式显示四元数值 */
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]++; // 数据包计数,故意在0xFF处循环
#endif
oled.display();
}
}
VL53L0X激光测距芯片/模块 的使用
1. 原理
采用940nm垂直腔面发射激光器(Vertical-Cavity Surface-Emitting Laser,简称VCSEL)发射出激光,激光碰到障碍物后反射回来被VL53L0X接收到,测量激光在空气中的传播时间,进而得到距离。
2.参数
超小体积:4.4 x 2.4 x 1.0mm
最大测距:2m
发射的激光对眼镜安全,且完全不可见。
工作电压:2.6 to 3.5 V
通信方式:IIC,400KHz,设备地址0x52,最低位是读写标志位。0表示写,1表示读。
3. 代码
评价,这是个好模块。然后就是这个模块上面的排针需要想方法焊上去,我已经在半个多月前通知过你们了 先把代码放上,后面我再详细介绍
/*
使用
adafruit/Adafruit_VL53L0X@^1.2.4
adafruit/Adafruit SSD1306@^2.5.13
这两个库
*/
#include <Arduino.h>
#include "Adafruit_VL53L0X.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire1, OLED_RESET);
Adafruit_VL53L0X tof = Adafruit_VL53L0X(); // 查看头文件可知默认Wire0连接,因此屏幕走Wire1连接,不过这些都是可以修改的,详细看库文件
void setup()
{
pinMode(20, OUTPUT);//
pinMode(19, OUTPUT);//这两个是当时用来根据不同距离点亮不同的灯
Serial.begin(115200);
Wire.begin(SDA,SCL,100000);
Wire1.begin(SDA1, SCL1, 100000);
tof.begin();
oled.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
oled.setTextSize(2);
oled.clearDisplay();
oled.setTextColor(WHITE);
oled.setCursor(0, 0);
oled.println("VL53L0-TOF");
oled.setCursor(0, 25);
oled.println("Loading...");
oled.display();
delay(1500);
oled.setCursor(0, 0);
oled.println("VL53L0-TOF");
oled.setCursor(0, 25);
oled.println("Loading...");
oled.setCursor(0, 50);
oled.println("|** OK **|");
oled.display();
delay(1500);
}
void loop()
{
oled.clearDisplay();
oled.setTextSize(2);
VL53L0X_RangingMeasurementData_t measure;//该结构体是一定要有的
tof.rangingTest(&measure, false);
if (measure.RangeMilliMeter < 150)
{
digitalWrite(19, HIGH);
digitalWrite(20, LOW);
}
else if (measure.RangeMilliMeter > 450)
{
digitalWrite(19, LOW);
digitalWrite(20, HIGH);
}
else
{
digitalWrite(19, LOW);
digitalWrite(20, LOW);
}
oled.setCursor(0, 0);
oled.print("Distance:");
oled.setCursor(0, 20);
oled.printf("%d mm", measure.RangeMilliMeter);
oled.setCursor(0, 40);
oled.setTextSize(1);
oled.print("Blue < 150;Red > 450");
oled.display();
}