ROS2进阶 -- 硬件篇第五章第一节 -- PlateFormIO开发环境介绍与安装 -- 新建PlateFormIO工程

序:

这章我们对嵌入式开发平台进行了介绍,并说明为什么选择PlateFormIO来作为我们的使用平台。

并且学习怎么使用PlateFormIO来创建我们的项目,以及编写我们的第一个工程

参考资料:https://blog.csdn.net/qq_27865227/article/details/131363638

一、基础篇-嵌入式开发介绍与环境搭建

1.什么是单片机

单片机(Microcontroller,简称MCU)是一种集成了中央处理器(CPU)、存储器(RAM、ROM或Flash)以及输入输出接口(I/O)的微型计算机。它是一种高度集成的芯片,通常用于嵌入式系统中,执行特定的控制任务。单片机广泛应用于家用电器、汽车电子、工业控制、通信设备、消费电子等领域。

单片机的主要特点包括:

  • 高度集成:单片机将CPU、存储器和各种外设(如定时器、串行通信接口、模数转换器等)集成在一个芯片上,减少了外部元件的数量和系统的复杂性。
  • 专用性:单片机通常用于执行特定的任务,而不像通用的计算机那样具备广泛的应用领域。其程序通常是固化的,一旦编程完成,单片机就执行预定的控制任务。
  • 低功耗:单片机通常设计为低功耗,以适应便携式设备和电池供电的应用需求。
  • 实时性:单片机需要对外界的输入做出迅速的响应,具备良好的实时性。这对于控制系统来说非常重要。
  • 经济性:单片机的成本相对较低,适合大规模生产和应用。

2.单片机开发平台

单片机运行不仅仅需要硬件,类似于电脑需要配套的操作系统一样,单片机还需要与之配套的软件,本节我们学习下常见的开发平台。

我们的MicroROS板采用的单片机是ESP32 DEVKIT_V1芯片,该芯片支持蓝牙和WIFI并且是双核的国产芯片,用途很广,所以小鱼就介绍下该芯片的常用的几个开发平台。

2.1 官方平台-ESPIDF(ESP IoT Development FrameWork)

官网地址:https://www.espressif.com/zh-hans/products/sdks/esp-idf
在这里插入图片描述
所谓官方平台就是单片机的厂商,针对单片机提供的开发框架,该框架为我们提供了一个C/C++ SDK,我们通过include相应的头文件就可以实现对硬件的控制。

下面这一段是官方介绍

ESP-IDF 是乐鑫官方的物联网开发框架,适用于 ESP32、ESP32-S、ESP32-C 和 ESP32-H 系列 SoC。它基于 C/C++ 语言提供了一个自给自足的 SDK,方便用户在这些平台上开发通用应用程序。ESP-IDF 目前已服务支持数以亿计的物联网设备,并已开发构建了多种物联网产品,例如照明、消费电子大小家电、支付终端、工控等各类物联网设备。

ESP-IDF的核心其实是基于开源的FreeRTOS优化而来的,而FreeRTOS是一个迷你(几k大小)的实时操作系统内核,所以别看它小,照样跑了个操作系统。

展示一段ESP_IDF版本的HelloWorld,感受一下

/*
 * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main(void)
{
    
    
    printf("Hello world!\n");
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    fflush(stdout);
    esp_restart();
}

2.2 Arduino平台

官网地址:https://www.arduino.cc/
在这里插入图片描述
Arduino是一款便捷灵活、方便上手的开源电子原型平台,本次MicroROS学习就是基于该平台进行开发。

展示一段Arduino版本的HelloWorld代码,感受一下

#include <Arduino.h>
void setup() {
    
    
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Hello World!");
}

void loop() {
    
    
  // put your main code here, to run repeatedly:

}

2.3 MicroPython平台

官网地址:https://micropython.org/
在这里插入图片描述
MicroPython是 Python 3 语言的精简实现 ,包括Python标准库的一小部分,经过优化可在微控制器和受限环境中运行。

同样的我们的MicroROS板同时也是支持使用MicroPython进行开发,只需要刷入相应的固件即可。

展示一段MicroPython的HelloWorld代码,感受下它的简单

print('Hello, World!')

2.4 PlateFormIO

PlatformIO平台,使用的是VScode打开
因为是使用的vscode平台,所以可以完美集成所有vscode的优秀插件(即写代码的体验好,代码智能提示,跳转,文件管理等等)
在这里插入图片描述

#include <Arduino.h>

void setup() {
    
    
  Serial.begin(115200);
}

void loop() {
    
    
  Serial.printf("Hello World!\n");
}

PlatformIO的平台支持的板子超多,(官方的,魔改的等等),

  • 插件库也比Arduino更加强大(因为有很多开发者上传了自己开发的库,比如各类传感器,摄像头,芯片),
  • 库自带demo,函数的调用方法都有示例,环境非常好(相比之下STM32的环境就有点一言难尽)
  • 自带框架,有一些常用的硬件开发框架。

2.5 对比与总结

上面介绍了三种常见的平台,用表格对比下三种平台的优缺点。

平台名称 优点 缺点
ESP_IDF 官方出品、测试完成度高、安全稳定、有官方支持、适合产品化、支持microROS 三方教程少、工程复杂、新手不友好
Arduino 社区庞大,教程丰富、新手友好,简单易用、支持microROS 封装较多
MicroPython Python语言、简单易用 解释执行,效率低下,封装较多,不支持microROS
PlatformIO 写代码的体验好,代码智能提示,跳转,文件管理等

看完上面的对比,对于新手来说选择Ardunio平台容易入门且教程丰富,并且ESP32单片机是官方出品了Arduino支持(开源地址:https://github.com/espressif/arduino-esp32),PlatformIO是资深一点的。

3. 搭建PlateFormIO开发环境

PIO的开发我们采用VsCode,一键安装PIO(注意这一步仅适用Ubuntu22.04系统,非该系统请直接跳过)

使用一键安装,选项12

wget http://fishros.com/install -O fishros && . fishros

在这里插入图片描述

手动安装教程及常见问题解决:ROS2进阶 – 硬件篇第五章 – Ubuntu安装VSCode platformio Python3.10(PlatformIO离线安装方法,安装失败问题、新建项目卡等问题)

4. PIO工程结构&构建方式

4.1 新建项目

在这里插入图片描述
填写项目名,选择开发板,选择路径即可
在这里插入图片描述

4.2 工程目录概述

在这里插入图片描述

首先展开工程,可以看到工程一共有8个部分如上图所示。

  • PIO配置文件
  • VsCode配置文件
  • 头文件放置目录
  • 库文件放置目录
  • 代码资源放置目录,主函数就在这里
  • 测试文件放置目录
  • git忽略文件
  • platformio配置文件

4.3 在哪里写代码?

打开src/main.cpp就是我们工程的程序入口文件,打开该文件,已经默认给我们生成了9行代码,后续的主要开发就在这里进行。

#include <Arduino.h>

void setup() {
    
    
  // put your setup code here, to run once:
}

void loop() {
    
    
  // put your main code here, to run repeatedly:
}

4.4 工程配置文件-platformio.ini

打开工程主目录下的platformio.ini文件,预生成的配置文件如下

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:featheresp32]
platform = espressif32
board = featheresp32
framework = arduino

这里用到的主要配置有四个

  • [env:featheresp32]编译环境
  • platform = espressif32,单片机平台
  • board = featheresp32,开发板
  • framework = arduino,开发框架-arduino

4.5 编译工程

在VsCode中编译PIO,编译工程和将编译结果下载到开发板上都非常的方便。

编译工程可以手动点击左下角的对号进行,其他操作也可以通过按钮进行。

在这里插入图片描述

点击编译按钮,看到如下界面则代表编译成功
在这里插入图片描述

其中打印信息有很多有用的提示,比如工程占用的RAM和Flash大小(可以理解为系统程序大小)

RAM:   [          ]   4.9% (used 16144 bytes from 327680 bytes)
Flash: [==        ]  16.2% (used 212961 bytes from 1310720 bytes)

编译完成工程,在.pio/build/featheresp32目录下可以看到firmware.bin,这个就是我们工程编译之后生成的二进制文件,将该文件下载到开发板上就可以运行了。

4.6 PIO快捷键

快捷键 内容
Ctrl+Alt+B 编译工程
Ctrl+Alt+U 将程序上传烧录到开发板
Ctrl+Alt+S 打开串口Monitor

5. 第一个HelloWord工程

5.1 Arduino开发流程

Arduino和其他单片机开发,一共分为四步。

  1. 编写代码,根据相关的API和SDK进行代码的编写。
  2. 编译工程,将工程的代码文件编译成二进制文件。
  3. 烧录二进制文件,将上一步生成的二进制文件通过工具烧录到开发板中。
  4. 运行测试,重启开发板,观察硬件执行情况(数据打印一般通过串口查看)

5.2 编写代码

5.2.1 Arduino函数介绍

Ardunio平台的一大特点就是简单易用,而Ardunio使用的开发语言是C/C++,从工程生成的默认代码就可以看出来。

#include <Arduino.h>

void setup() {
    
    
  // put your setup code here, to run once:
}

void loop() {
    
    
  // put your main code here, to run repeatedly:
}

整个代码可以分为三个部分

  1. 头文件#include <Arduino.h>
  2. setup()函数,该函数只会在启动时被系统调用一次
  3. loop()函数,该函数会被系统循环调用,直到重启或者断电
5.2.2 为什么没有入口函数main函数?

在学习C语言和C++时你应该学过,程序的入口文件是main函数,但在这个Arduino中却没有main函数的存在,这是为什么?

Arduino其实是有main函数的,Arduino的main函数长这样(ESP32-Arduino库有所不同,但原理一样:https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/main.cpp)

#include <Arduino.h>

// Declared weak in Arduino.h to allow user redefinitions.
int atexit(void (* /*func*/ )()) {
    
     return 0; }

// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
void initVariant() {
    
     }

void setupUSB() __attribute__((weak));
void setupUSB() {
    
     }

int main(void)
{
    
    
    init();

    initVariant();

#if defined(USBCON)
    USBDevice.attach();
#endif

    setup();

    for (;;) {
    
    
        loop();
        if (serialEventRun) serialEventRun();
    }

    return 0;
}

核心的代码就这一段

    setup();

    for (;;) {
    
    
        loop();
        if (serialEventRun) serialEventRun();
    }

从这里就可以看出来,setup和loop函数之间的关系,在main函数中先调用一次setup函数,再使用for死循环调用loop函数。

5.2.3 串口输出HelloWorld

要实现将HelloWorld从开发板输出到电脑上,我们需要了解一个常用的通信协议Serial,常称串口通信。
这里我们了解三个函数,串口初始化、串口打印、串口读取。

函数原型 参数 返回值 描述
void begin(unsigned long baud) baud:串口波特率 void 该函数用于初始化串口,主要配置串口波特率,波特率类似于频道号,串口收发双方保持相同的波特率才能进行正常通信。常见的波特率有9600,115200等,波特率其实代表每秒数据收发的频率,波特率越高,速度越快。
size_t printf(const char *format, …) format:格式化字符串 size_t 打印的字符数量 该函数和我们常见的printf函数一致,eg:Serial.printf(“Hello World!”);
int read(void) void int 读取的字符值,ASSIC表示 该函数用于读取一个字节的数据,返回值就是这个字节的值,如果没有数据则返回-1

基于上面的函数,我们可以这样输出HelloWorld!

#include <Arduino.h>

void setup() {
    
    
  Serial.begin(115200);
}

void loop() {
    
    
  Serial.printf("Hello World!\n");
}

在setup()函数里进行串口的初始化,波特率设置成了115200,在loop函数中不断的输出Hello World!

5.3 编译代码

点击对号,或者使用快捷键Ctrl+Alt+B,即可编译。
在这里插入图片描述

看到Building .pio/build/featheresp32/firmware.binSuccessfully created esp32 image.就代表已经成功生成了二进制文件,下一步我们就开始烧录二进制文件。

5.4 烧录二进制文件

5.4.1 连接开发板到电脑

MicroROS学习板采用TypeC接口,你需要一个USB数据线将开发板连接到你的电脑。连接电脑后,Linux系统驱动会被自动搜索和加载,查看是否有正确驱动,可以使用lsusb进行测试。

lsusb

输入后,如果可以看到CP210x这个设备,就代表驱动加载成功了,如图第四行输出即esp32开发板
在这里插入图片描述
驱动加载成功后在/dev目录下会多出一个ttyUSBx的设备,这里是/dev/ttyUSB0

使用指令可以将其列出

ls /dev/ttyUSB*

在这里插入图片描述

5.4.2 设置设备权限

我们想让开发板和电脑通过串口进行通信,电脑端只需对这个串口进行读写就行了。因为设备默认的生成目录是在/dev目录下,普通用户是没有读写权限的,所以在使用之前我们可以修改下该设备的权限。
临时修改

sudo chmod 666 /dev/ttyUSBx

在这里插入图片描述
也可以永久修改,将用户添加到dialout和plugdev组(重启后方生效)

sudo usermod -a -G dialout $USER
sudo usermod -a -G plugdev $USER
5.4.3 烧录二进制文件

点击左下角的上传烧录按钮,或者使用快捷键Ctrl+Alt+U进行烧录。
在这里插入图片描述
看到上面四部分打印代表烧录成功,可以看到PIO可以自动检测串口并进行连接,接着上传文件到开发板,最后自动重启。

5.5 运行测试

因为在下载完成后,下载程序帮我们自动重启了,所以这里我们不需要进行重启。接着我们使用串口Monitor打开串口看看有没有数据。

点击Serial Monitor(是一个插件)按钮,或者使用快捷键Ctrl+Alt+S,如果没有出错,你将看到下面的乱码。

在VSCODE的侧边栏中,点击“Extensions”图标,然后在搜索框中搜索“Serial Monitor”,找到并安装Serial
Monitor插件。

原因是终端的波特率不对,开发板发送给电脑数据的波特率是115200,而电脑接手的波特率是9600,不匹配就会造成乱码。
在这里插入图片描述

通过修改配置文件,可以修改Serial Monitor的默认波特率。

在platformio.ini中添加一行代码

monitor_speed = 115200

接着关闭刚刚的终端,再重新打开,接着我们就可以看到嗖嗖嗖的Hello World!
在这里插入图片描述

6. 串口通信-接收实验

6.1 检测并接收单个字符

6.1.1 代码编写

#include <Arduino.h>

void setup()
{
    
    
    // 初始化串口
    Serial.begin(115200);
}

void loop()
{
    
    
    // 判断是否有有效数据,返回值是有效数据的长度
    if (Serial.available())
    {
    
    
        // 读取一个数据
        int c = Serial.read();
        // -1 代表接收失败
        if (c != -1)
        {
    
    
            // 以%c字符的格式输出接收的数据
            Serial.printf("I receve %c\n", c);
        }
    }
}

这里多用了一个函数Serial.available(),该函数代表当前串口中缓存有效数据的长度。

6.1.2 使用串口监视器发送消息

使用快捷键,编译 Ctrl+Alt+B、上传 Ctrl+Alt+U,接着准备发送数据
使用Ctrl+Alt+~打开终端,接着在终端中你可以看到串口监视器一栏
接着打开我们板子对应的串口设备

  • 选择串口编号
  • 设置波特率
  • 点击开始监视
    在这里插入图片描述

发送测试

  • 输入数据
  • 点击发送
  • 查看返回

发送123
在这里插入图片描述
可以看到受到了三条返回,这是因为我们每次只接收一个数据,所以即使发送123,接收数据也是一个一个接收和打印的。

那有没有办法一次性接收多个数据呢?我们换个函数即可。

使用完串口监视记得关闭,有些时候需要重启软件

6.2 一次性接收一串数据

6.2.1 代码编写

#include <Arduino.h>

void setup()
{
    
    
    // 初始化串口
    Serial.begin(115200);
}

void loop()
{
    
    
    // 判断是否有有效数据
    if (Serial.available())
    {
    
    
        // 读取一个String字符串数据
        String str = Serial.readString();
        // 以%s的格式输出接收的数据
        Serial.printf("I receve %s\n", str.c_str());
    }
}
6.2.2 编译下载测试

点击按钮或者使用快捷键编译下载代码。
下载完成后,重新打开串口,接着发送一串消息
在这里插入图片描述

7. 学会安装第三方开源库

在我们的PIO工程中有多种方式可以添加第三方库,常用的有以下三种:

  • 通过PIO搜索安装
  • 通过GIT地址安装
  • 手动下载安装

接下来我们以安装OLED库和IMU的驱动库为例,学习安装第三方库的方法。

开始之前先新建一个工程example01_depends
在这里插入图片描述

7.1 通过PIO搜索安装

我们以安装OLED常用的三方库Adafruit SSD1306安装为例。

  1. 点击PIO图标

  2. 点击Libraries

  3. 输入Adafruit SSD1306

  4. 点击下载按钮
    在这里插入图片描述

  5. 点击Add to Project

  6. 选择要添加到的工程

  7. 点击Add
    在这里插入图片描述

看到界面表示成功
在这里插入图片描述
此时打开platformio.ini你将看到

[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
lib_deps = adafruit/Adafruit SSD1306@^2.5.10

lib_deps = adafruit/Adafruit SSD1306@^2.5.10就是我们安装的库的名字。

既然安装好了那安装的文件位置在哪里呢?打开.pio/libdeps/esp32doit-devkit-v1,这里就是我们安装的第三方库的代码位置。
在这里插入图片描述

7.2 通过GIT地址安装

我们的开发板,板载了一块MPU6050模块,通过该模块可以实现对温度、加速度、加速度、重力测量。

这里推荐一个简单易用MPU6050的三方驱动库MPU6050_light: https://github.com/rfetick/MPU6050_light
在这里插入图片描述

接着我们来看如何将该库添加到我们的工程中

  1. 复制仓库地址
    在这里插入图片描述

  2. 打开platformio.ini,将地址复制进去即可

[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
lib_deps = adafruit/Adafruit SSD1306@^2.5.10
		   https://github.com/rfetick/MPU6050_light.git

稍等片刻,等待PIO下载完成,接着打开==.pio/libdeps/esp32doit-devkit-v1==可以看到MPU6050_light库被下载到该目录。
在这里插入图片描述

7.3 手动下载安装

该方式更简单,我们直接将工程克隆到工程的lib目录下即可。

cd lib
git clone https://github.com/rfetick/MPU6050_light.git

7.4 git加速

在前面加https://mirror.ghproxy.com/

lib_deps = 
	https://mirror.ghproxy.com/https://github.com/rfetick/MPU6050_light.git

8. 使用开源库驱动IMU

这一节我们尝试使用该库将我们板子上的IMU模块驱动起来。

8.1 MPU6050介绍

MPU6050 为全球首例集成六轴传感器的运动处理组件,内置了运动融合引擎,用于手持和桌面的应用程序、游戏控制器、体感遥控以及其他消费电子设备。它内置一个三轴 MEMS 陀螺仪、一个三轴 MEMS 加速度计、一个数字运动处理引擎(DMP)以及用于第三方的数字传感器接口的辅助 I2C 端口(常用于扩展磁力计)。当辅助 I2C 端口连接到一个三轴磁力计,MPU6050 能提供一个完整的九轴融合输出到其主 I2C 端口。
在这里插入图片描述

8.2 调用开源库驱动

新建工程example02_mpu6050
在这里插入图片描述

8.2.1 添加依赖

修改platformio.ini

[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
lib_deps = 
    https://mirror.ghproxy.com/https://github.com/rfetick/MPU6050_light.git
8.2.2 复制样例程序并修改代码

该开源库作者提供了开源库的使用方式,
将.pio/libdeps/esp32doit-devkit-v1/MPU6050_light/examples/GetAllData/GetAllData.ino复制到main.cpp中。

修改代码

  1. 修改波特率 9600->115200
  2. 修改IO地址 Wire.begin();->Wire.begin(21, 22);
#include "Wire.h"          // 导入I2C相关头文件
#include <MPU6050_light.h> // 导入MPU6050库

MPU6050 mpu(Wire); // 新建MPU6050对象mpu

unsigned long timer = 0;

void setup()
{
    
    
  Serial.begin(115200);
  Wire.begin(21, 22); // 初始化I2C,设置sda引脚为GPIO18,SCL引脚为GPIO19

  byte status = mpu.begin(); // 检测IMU模块状态
  Serial.print(F("MPU6050 status: "));
  Serial.println(status);
  while (status != 0)
  {
    
    
  } // stop everything if could not connect to MPU6050

  Serial.println(F("Calculating offsets, do not move MPU6050"));
  delay(1000);
  mpu.calcOffsets(true, true); // gyro and accelero 校准
  Serial.println("Done!\n");
}

void loop()
{
    
    
  mpu.update();

  if (millis() - timer > 1000)
  {
    
     // print data every second
    Serial.print(F("TEMPERATURE: "));
    Serial.println(mpu.getTemp()); // 温度
    Serial.print(F("ACCELERO  X: "));
    Serial.print(mpu.getAccX()); // X轴加速度
    Serial.print("\tY: ");
    Serial.print(mpu.getAccY()); // Y轴加速度
    Serial.print("\tZ: ");
    Serial.println(mpu.getAccZ()); // Z轴加速度

    Serial.print(F("GYRO      X: "));
    Serial.print(mpu.getGyroX()); // X轴 角速度
    Serial.print("\tY: ");
    Serial.print(mpu.getGyroY()); // Y轴 角速度
    Serial.print("\tZ: ");
    Serial.println(mpu.getGyroZ()); // Z轴 角速度

    Serial.print(F("ACC ANGLE X: "));
    Serial.print(mpu.getAccAngleX()); // X轴角加速度
    Serial.print("\tY: ");
    Serial.println(mpu.getAccAngleY()); // Y轴角加速度

    Serial.print(F("ANGLE     X: "));
    Serial.print(mpu.getAngleX()); // X角度
    Serial.print("\tY: ");
    Serial.print(mpu.getAngleY()); // Y角度
    Serial.print("\tZ: ");
    Serial.println(mpu.getAngleZ()); // Z角度
    Serial.println(F("=====================================================\n"));
    timer = millis();
  }
}


8.3 编译测试

保存代码,编译下载到开发板。打开串口监视器,查看结果。
在这里插入图片描述

总结

本节我们了解了PlateFormIO开发环境介绍,并成功开始了第一个PlateFormIO工程以及通过两个串口接收数据小实验,学习了串口数据的接收和发送。

猜你喜欢

转载自blog.csdn.net/weixin_45897172/article/details/139395464