标签: Arduino

  • 如何安装Arduino IDE?添加附加开发板?以 ESP32 为例

    本篇文章主要介绍了如何安装Arduino IDE、如何添加附加开发板,并以 ESP32 库为例。开发板使用了最近自己做的 ESP32-C3-BCHG-02 为例,当然使用 Arduino UNO 、 Arduino NANO 、D1 mini 或者是其他 ESP32 板子、Arduino IDE 生态支持的板子均可。

    注意事项

    • 本文使用的 Arduino IDE 为 1.8.19 版本,最新版本为 2.0.4,一般正常情况下用 1.X.X 会比较稳定,但是整体设置大差不差。
    • 本文的操作系统均为 MAC,但是 Windows 端的操作基本差不多,所以亦可参考。
    • 注意:自制的 ESP32-C3-BCHG-02 在 Windows 端可以完全正常使用,但是在 MAC 端会报错 A fatal error occurred: Unable to verify flash chip connection (No serial data received.).,需要将波特率改成 115200,即可解决该问题。
    • 注意:本文的安装方式均在网络环境通畅的环境下进行,也就是说至少能够通畅访问 Github 的情况下进行。
    • 注意:在网络不通畅时的安装方法,即离线安装方法参考链接为 https://arduino.me/a/esp32 (仅限于Windows)。

    安装Arduino IDE

    https://www.arduino.cc/en/software 这个页面上下载 Arduino 安装包,也可以进入本站分享的百度网盘链接获取安装包,安装完成打开软件之后,界面如下图所示。

    安装完成打开软件之后,界面如上图所示。

    添加附加开发板

    点击“文件”-“首选项”,在“附加开发板管理器网址”中输入下列地址,其中 esp8266 和 esp32 分别是以下两个链接,可以通过回车的方式将两个链接分开。其中需要注意的是 esp32 需要2.0.0以上的版本才能支持 ESP32-C3。

    点击“文件”-“首选项”
    • ESP8266 Community: https://arduino.esp8266.com/stable/package_esp8266com_index.json
    • Espressif ESP32: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

    注意:请不要使用 https://dl.espressif.com/dl/package_esp32_index.json ,里面没有 ESP32 Arduino 2.0.0 以上的版本。

    在“附加开发板管理器网址”中输入上述地址

    点击“工具”-“开发板”-“开发板管理器”,在“开发板管理器”中搜索“esp”,既可以找到 esp32 和 esp8266 的安装包,选择最新版本(目前为 2.0.7),点击“安装”即可。

    注意:此处步骤需要网络通畅。

    点击“工具”-“开发板”-“开发板管理器”
    在“开发板管理器”中搜索“esp”

    上传测试代码

    安装完成后,点击“工具”-“开发板”,此时开发版多出一个叫做 esp32 的选项,点击选择 ESP32C3 Dev Module。

    点击选择 ESP32C3 Dev Module

    将开关插入 ESP32-C3-BCHG-02 背面的开关接口中,将 type-c 线缆将开发板和电脑相连,打开开关。正常情况下,可以在电脑中识别出对应的端口,并选择该端口。

    注意:Mac 系统需要将 Upload Speed 从 921600 改成 115200,不然会出现 A fatal error occurred: Unable to verify flash chip connection (No serial data received.) 错误。 相关参考链接(来自 forum.arduino.cc)实测 Windows 系统下无需更改此项。

    Mac 系统需要将 Upload Speed 从 921600 改成 115200

    在“文件”-“示例”-“ESP32”-“GPIO”中找到“blinkRGB”,点击菜单栏的“上传”按钮,在编译成功后即可上传至开发板。上传完成后会显示“上传成功”字样。

    在“文件”-“示例”-“ESP32”-“GPIO”中找到“blinkRGB”
    上传完成后会显示“上传成功”字样。

    此处使用的开发板是 ESP32-C3-BCHG-02,它的 GPIO8 连接了一颗 RGB 灯珠(WS2812B),上传blinkRGB 示例程序中即可点亮这颗 RGB 灯珠。

  • Arduino 中的 setup() 和 loop() 函数用法

    在 Arduino 中,setup()loop()是两个重要的函数,它们是每个Arduino程序的基本组成部分。

    setup()

    setup()函数在程序开始执行时只运行一次,它用于进行初始化设置。你可以在这个函数中设置引脚模式(输入或输出)、初始化库、启动串口通信等。例如,如果你要将引脚13设置为输出模式,可以在setup()函数中添加以下代码:

    void setup() {
      pinMode(13, OUTPUT);
    }

    loop()

    loop()函数是一个无限循环,在setup()函数运行完后立即开始执行,它包含了你的主要程序逻辑。在loop()函数中,你可以编写代码来读取传感器的值、控制输出等。程序将会不断循环执行loop()函数中的代码,直到你断开Arduino的电源。

    下面是一个简单的示例,将引脚13设置为输出模式,并在循环中交替设置引脚13的电平状态:

    void setup() {
      pinMode(13, OUTPUT);
    }
    
    void loop() {
      digitalWrite(13, HIGH);  // 将引脚13设置为高电平
      delay(1000);             // 延时1秒
      digitalWrite(13, LOW);   // 将引脚13设置为低电平
      delay(1000);             // 延时1秒
    }

    上述代码会使LED(如果连接到引脚13)每隔1秒交替亮和灭。

    总结来说,setup()函数用于初始化设置,它只运行一次;loop()函数包含你的主要程序逻辑,会不断循环执行。通过使用这两个函数,你可以编写出更复杂的Arduino程序。

  • 使用 MH-CD42 模块管理锂电池充放电🔋

    本文所用的测试开发板为 D1 mini,它是一块拥有 ESP8266 WiFi 模组的、并且支持 Arduino IDE 的高性价比开发板,其本身使用 5V 供电(详情可参见这篇文章:性价比 + WIFI + Arduino:开发板 D1 mini),其他使用 5V 供电的开发板均可以参考使用 MH-CD42 模块。

    MH-CD42 模块特点

    根据店家提供的资料,该模块的输出负载不能大于 10W,充电电压一般推荐 DC 5V(一般旧款的手机充电器差不多就可以),充电电流 0到2.1A,输出也差不多类似。此外,该模块还带有锂电池保护功能:过流保护、过压保护、短路保护、过温保护。

    它允许边冲边放,在模块的侧边还有4档电量指示的小灯。当负载小于 10mA 并持续 30 秒后会自动断电关闭输出(也就是说,关闭电源开关之后,模块的电量指示灯还会亮30秒)。它的管脚作用:

    1. VIN:充电口输入 +;
    2. GND:充电口输入 -;
    3. GND:锂电池 -;
    4. BAT:锂电池 +;
    5. GND:输出至负载 -;
    6. VOUT:输出至负载 +;
    7. KEY:输出使能触发端口(低脉冲触发);若使用低功耗设备,需要单片机向该口每隔20秒发送一次低电平,以防电源模块关机。
    MH-CD42 模块外观和引脚接口
    MH-CD42 模块外观和引脚接口

    我们可以使用 18650 电池或者淘宝在售的一些锂电池作为 MH-CD42 模块的电池输入(连接上述的3、4接口)。淘宝一块型号为 103040 3.7V 锂电池的售价一般在15元左右,其中 103040 分别代表着电池的”厚度、宽度和长度”,单位为毫米。电池价格和大小(容量)成正比。而相对于 18650 的好处是这种锂电池的大小、型号特别多,总有一款符合自己的需求。

    淘宝上的一些锂电池的价格

    由于 D1 mini 采用了过时的 Micro USB 接口,所以打算替换成 Type-C 接口进行充电或供电,相关文章可以参见这篇:Micro USB 公头转 Type-C 母头转接器制作。店家给的资料中列出了最基本的使用方法,在本例中再增加一个开关和上述的 Type-C 接口,实物图接线如下所示:

    • Type-C 的 V+ 和 G 分别连接 MH-CD42 模块的 VIN 和 GND;
    • Type-C 的 D+ 和 D- 分别连接 Micro USB 的 D+ 和 D- ;
    • MH-CD42 模块的 VOUT 和 GND 分别连接 Micro USB 的 V+ 和 G,并在中间接入开关。

    参考资料

  • Arduino 制造光污染 | 点亮使用 MAX7219 驱动的 32×8 点阵屏

    材料准备 和 MAX7219 点阵屏

    MAX7219 驱动的点阵屏在网上各种 Arduino 教程中无处不在,不过在本页面中,将结合使用 MD_ParolaMD_MAX72XX 这两个 Arduino 库,使用这两个库将会使在点阵屏上显示滚动文本和其他动画变得非常容易。 本文示例的最终效果如下图所示:

    MAX7219 点阵屏 实物图
    MAX7219 点阵屏 实物图

    MAX7219 LED 驱动器可用于控制 64 个单独的LED,该驱动器通过 SPI 与 Arduino 进行通信。由于 MAX7219 最多可以控制 64 个LED,它可以驱动的最大尺寸的点阵显示器是 8×8 像素。然而,将多个驱动器和点阵屏串联起来,可以轻松控制更大的显示屏,如 8×32、8×64,甚至更大。

    在本例中将使用到以下一些电子模块或者材料配件:

    电子模块或者材料配件 数量
    8×32 MAX7219 LED点阵显示屏1
    D1 mini 控制板1
    连接线若干
    Micro USB 数据线1

    下面是一个典型的 MAX7219 8×32 LED 点阵屏的规格:

    电压5 V
    驱动MAX7219 x 4
    亮度等级16
    尺寸32 x 128 x 15 mm
    像素点8×32, ⌀ 3 mm
    MAX7219 点阵屏 渲染图 (Solidworks)
    MAX7219 点阵屏 渲染图 (使用 Solidworks 渲染)

    硬件接线方式

    MAX7219 LED 点阵屏驱动器通过 SPI(Serial Peripheral Interface,串行外设接口)与 Arduino 进行通信。通过 SPI 接口,一个主设备(Arduino)控制外围设备(也称为从属设备)。通过 Arduino 的硬件 SPI 接口 或 三个任意的数字引脚(软件 SPI)来控制点阵屏。硬件 SPI 引脚(MOSI、MISO 和 SCK)在每种 Arduino 板上有其特定的位置。这比起使用软件 SPI 要快,但需要使用以下固定的输出引脚。各个常用的 Arduino 硬件 SPI 针脚位置如下表所示:

    控制板MOSIMISOSCK电压
    Arduino Uno11 or ICSP-412 or ICSP-113 or ICSP-35 V
    Arduino Mega51 or ICSP-450 or ICSP-152 or ICSP-35 V
    Arduino LeonardoICSP-4ICSP-1ICSP-35 V
    各个常用的 Arduino 硬件 SPI 针脚位置

    而在本例中将使用 D1 mini 进行 MAX7219 点阵屏的控制,连接方式如下表所示。如果将第一个点阵屏的 DOUT 连接到下一个点阵屏的 DIN 上,可以使所有点阵屏之间共享 VCC、GND、CLK 和 CS。

    MAX7219 LED 点阵屏D1 mini
    CLKD2
    DATA 或 DIND4
    CSD3
    GNDGND
    VCC5V

    一般来说,这样大小的点阵屏(4块 8*8 串联)组合使用控制板的供电就没问题,但是如果想控制一个大的点阵屏组合,建议使用外部电源。

    库文件安装

    如何安装库文件可以查看这篇文章:Arduino 制造光污染 | 全彩灯带 WS2812 中“找一个合适的库”这一节。

    MD_Parola 库可用于创建许多不同的文本动画,如滚动文本效果。这个库依赖 MD_MAX72XX 库,它实现了 LED 点阵屏的硬件功能。在本例中软件部分将使用 Arduino IDE,此外还将使用到以下一些库文件:

    库名称作用下载地址
    MD_Parola 创建许多不同的文本动画,如滚动文本效果。 Github
    MD_MAX72XX 实现了 LED 点阵屏的硬件功能。 Github
    必要的库文件列表

    为了配合本页中的示例代码,还需要以下库文件以及 ESP8266 的 Arduino 官方库:

    库名称作用下载地址
    NTPClient提供与 NTP 服务器主机的连线,并回传日期时间。Github
    其他必要的库文件列表

    示例代码

    在 Arduino 代码中设置点阵屏时,需要将 HARDWARE_TYPE 设置为 FC16_HW,并指定所连接的设备数量。一个 8×8 的点阵屏算作一个设备,所以如果想控制一个 8×32 的模块,那需要将 MAX_DEVICES 设置为 4(一个8×32的点阵屏包含 4 个 MAX7219),设置针脚和参数如下图所示:

    MD_MAX72XX 和 MD_Parola 代码设置
    #include <ESP8266WiFi.h>
    #include <NTPClient.h>
    #include <WiFiUdp.h>
    #include <MD_Parola.h>
    #include <MD_MAX72xx.h>
    #include <SPI.h>
    
    String formattedDate;
    String dayStamp;
    String timeStamp;
    
    #define DEMO_DURATION 5000
    typedef void (*Demo)(void);
    int modeNumber = 0;
    
    // Define the number of devices we have in the chain and the hardware interface
    // NOTE: These pin numbers are for ESO8266 hardware SPI and will probably not
    // work with your hardware and may need to be adapted
    #define HARDWARE_TYPE MD_MAX72XX::FC16_HW
    #define MAX_DEVICES 4
    #define CLK_PIN   D2 // or SCK
    #define DATA_PIN  D4 // or MOSI
    #define CS_PIN    D3 // or SS
    #define buttonIO  D1
    // SOFTWARE SPI
    MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
    // Scrolling parameters
    uint8_t scrollSpeed = 25;    // default frame delay value
    textEffect_t scrollEffect = PA_SCROLL_LEFT;
    textPosition_t scrollAlign = PA_LEFT;
    uint16_t scrollPause = 2000; // in milliseconds
    // Global message buffers shared by Wifi and Scrolling functions
    #define BUF_SIZE  512
    char curMessage[BUF_SIZE] = { "" };
    char newMessage[BUF_SIZE] = { "Hello! Enter new message?" };
    bool newMessageAvailable = true;
    int state = 0;
    int buttonState = 0;
    int timeCount = 0;
    
    const char* ssid     = "ce";
    const char* password = "ce";
    
    WiFiUDP ntpUDP;
    NTPClient timeClient(ntpUDP);
    
    void setup() {
      Serial.begin(57600);
      P.begin();
    
      WiFi.begin(ssid, password);
      Serial.print("\n\r \n\rWorking to connect");
    
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }
    
      timeClient.begin();
    
      // 时区 *60分 * 60秒,例如:
      // GMT +1 = 3600
      // GMT +8 = 28800
      // GMT 0 = 0
      timeClient.setTimeOffset(28800);
      pinMode(buttonIO, INPUT);
    }
    
    void loop() {
      while (!timeClient.update()) {
        timeClient.forceUpdate();
      }
    
      buttonState = digitalRead(buttonIO);
      if (buttonState == LOW) {
        state = 2;
      }
    
      if (P.displayAnimate()) {
        // formattedDate 取得的日期格式为:2018-05-28T16:00:13Z
        formattedDate = timeClient.getFormattedDate();
        Serial.println(formattedDate);
    
        // 获取日期
        int splitT = formattedDate.indexOf("T");
        dayStamp = formattedDate.substring(0, splitT);
        Serial.print("DATE: ");
        Serial.println(dayStamp);
    
        // 获取时间
        timeStamp = formattedDate.substring(splitT + 1, formattedDate.length() - 1);
        Serial.print("TIME:");
        Serial.println(timeStamp);
    
        switch (state) {
          case 0: {
              timeStamp.toCharArray(newMessage, timeStamp.length() + 1);
              strcpy(curMessage, newMessage);
              newMessageAvailable = true;
              state = 1;
              P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
              break;
            }
          case 1: {
              dayStamp.toCharArray(newMessage, dayStamp.length() + 1);
              strcpy(curMessage, newMessage);
              newMessageAvailable = true;
              state = 0;
              P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
              break;
            }
          case 2: {
              scrollPause = random(50, 800);
              scrollSpeed = random(5, 25);
              timeCount = timeCount + scrollPause + scrollSpeed * 32;
              Serial.println(timeCount);
              String newMessageTemp = "666";
              newMessageTemp.toCharArray(newMessage, newMessageTemp.length() + 1);
              strcpy(curMessage, newMessage);
              newMessageAvailable = true;
              P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
              state = 3;
              break;
            }
          case 3: {
              scrollPause = 3000;
              scrollSpeed = 50;
              String newMessageTemp = String(timeCount)+" ms";
              newMessageTemp.toCharArray(newMessage, newMessageTemp.length() + 1);
              strcpy(curMessage, newMessage);
              newMessageAvailable = true;
              state = 0;
              P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
              timeCount = 0;
              break;
            }
        }
        P.displayReset();
      }
    }

  • 稳步推进 | 用 Arduino 驱动步进电机、丝杠滑台

    丝杠滑台、步进电机参数

    丝杆规格 T6*2-100mm ,前两位T6代表丝杆的直径6mm,后两位代表丝杆的导程(螺距)1mm/2mm/4mm/6mm/12mm(电机转动一圈滑块移动的距离),最后的100mm表示滑台的有效行程(滑块所能移动的距离不含滑块的长度)。

    该丝杠滑台使用了 28 步进电机(28*28*30mm,两相四线):

    • 电压:24v
    • 电流:0.6A
    • 转矩:0.07N·m
    • 步距角:1.8°

    步进电机驱动器

    TB6600 升级版驱动器是一款专业的两相混合式步进电机驱动器,可适配国内外各种品牌,电流在 4.0A 及以下,外径 39, 42, 57mm 的四线,六线,八线两相混合式步进电机。适合各种小中型自动化设备和仪器,例如:雕刻机、打标机、切割机、激光照排、绘图仪、数控机床、拿放装置等。在用户期望低成本、大电流运行的设备中效果特性。

    信号输入端

    • PUL:脉冲输入信号。默认脉冲上升沿有效,为了可靠响应脉冲信号,脉冲宽度应大于1.2us。
    • DIR:方向输入信号,高/低电平信号,为保证电机可靠换向,方向信号应先于脉冲信号至少 5us 建立。电机的初始运行方向与电机绕组接线关,互换任一相绕组(如A+, A-交换)可以改变电机初始运行方向。
    • ENA:使能输入信号(脱机信号)用,使能或禁止驱动器输出使能时,驱动器将切断电机各相的电流使电机处于自由状态,不响应步进脉冲。当不需用此功能时,使能信号端悬空即可。

    接线注意事项

    1. 为了防止驱动器受干扰,建议控制信号采用屏蔽电缆线,并且屏蔽层与地线短接,同一机器内只允许在同一点接地,如果不是真实接地线,可能干扰严重,此时屏蔽层不接。
    2. 脉冲和方向信号线与电机线和电源线不允许并排包扎在一起,最好分开至少10cm以上,否则电机噪声容易干扰脉冲方向信号引起电机定位不准,系统不稳定等故障。
    3. 如果一个电源供多台驱动器,应在电源处采取并联连接,不允许先到一台再到另一台链状式连接。
    4. 严禁带电拔插驱动器电源端子,带电的电机停止时仍有大电流流过线圈,拔插电源端子将导致巨大的瞬间感生电动势将烧坏驱动器。
    5. 严禁将导线头加锡后接入接线端子,否则可能因接触电阻变大而过热损坏端子。
    6. 接线线头不能裸露在端子外,以防意外短路而损坏驱动器。

    电流、细分拨码开关设定

    机械尺寸图

    装置接线方法

    28 步进电机(28*28*30mm,两相四线)中有四条线:红接A+;蓝接A-;绿接B+;黑接B-。(图中的电机上的贴纸指示:红接B+;蓝接B-;绿接A-;黑接A+)。

    控制步进电机,要有3条线接到 Arduino 控制板上,不管是共阴还是共阳接法。三条线分别是:PUL,DIR,ENA。

    • 共阳接法:将 PUL+ DIR+ ENA+ 串联一起,再接到 Arduino 的 5V 供电引脚上,PUl- DIR- 分别接到 Arduino 板的控制引脚上,还剩下的ENA-,可以接到5V,或者悬空不接。
    • 共阴接法:与上述接法相反,即 PUL-, DIR-, EN- 连接一起,再接到 Arduino 的 GND上面。

    本例采用共阴接法:

    1. 将 PUL-, DIR-, EN- 连接一起,再接到 Arduino 的 GND 上面。
    2. PUL+ 连接到 Arduino 的 9 引脚,控制步进转动。
    3. DIR+ 连接到 Arduino 的 8 引脚,控制步进转动方向。
    4. EN+ 连接到 Arduino 的 GND 上,也可以悬空不接。

    Arduino 代码

    /***********************
      Connect to the Arduino:
      1. 将 PUL-, DIR-, EN- 连接一起,再接到 Arduino 的 GND 上面。
      2. PUL+ 连接到 Arduino 的 9 引脚,控制步进转动。
      3. DIR+ 连接到 Arduino 的 8 引脚,控制步进转动方向。
      4. EN+ 连接到 Arduino 的 GND 上,也可以悬空不接。
    ************************/
    #define dirpin 8  //DIR+
    #define steppin  9 //PUL+
    
    void setup() {
      Serial.begin(115200);
      pinMode(dirpin, OUTPUT);
      digitalWrite(dirpin, LOW);
      pinMode(steppin, OUTPUT);
      digitalWrite(steppin, LOW);
    }
    
    void loop() {
        MotorZ();
    }
    
    void MotorZ() {
      digitalWrite(dirpin, HIGH); // 设置转动的方向
      Serial.println("MotorZ...");
      for (int i = 0; i < 1; i++) {
        digitalWrite(steppin, LOW); // 从低到高的变化
        delayMicroseconds(400);
        digitalWrite(steppin, HIGH);
        delayMicroseconds(400); // 修改此处可以改变步进电机速度;修改驱动器上的细分亦可实现
      }
    }
    
    void MotorR() {
      digitalWrite(dirpin, LOW); // 设置转动的方向
      Serial.println("MotorR...");
      for (int i = 0; i < 1; i++) {
        digitalWrite(steppin, LOW); // 从低到高的变化
        delayMicroseconds(400);
        digitalWrite(steppin, HIGH);
        delayMicroseconds(400);
    
      }
    }
  • 测一测温度 | Arduino 和 NTC 热敏电阻

    NTC 热敏电阻阻值测试

    待测热敏电阻 A

    ntc 热敏电阻常温 (约29度)
    上 84.5k欧
    下 84.2k欧
    标称阻值:100k kΩ @25℃;公差精度: + / -1%
    B值 :3950K@25/50℃;B值精度:+ / – 1%

    待测热敏电阻 B

    我自己的 8.24k欧
    标称阻值:10k kΩ @25℃;公差精度: + / -1%
    B值 :3950K@25/50℃;B值精度:+ / – 1%
    NTC 10K±1% B值3950 RT表(NTC温度/阻值参数对照表)- 敏创电子 (mcnic.com)

    参考代码

    下列的参考代码使用了thermistor的库,库文件可以从下述链接进行下载;若使用百度网盘,可以在文件夹中查找名为thermistor-master.zip的压缩包。

    #include "thermistor.h"
    #include "HardwareSerial.h"
    
    // 使用模拟引脚A0读取数值
    #define NTC_PIN               A0
    
    // Thermistor object
    THERMISTOR thermistor(NTC_PIN,        // 模拟引脚 A0
                          10000,          // 25 ºC 时电阻阻值
                          3950,           // 热敏电阻 beta 系数
                          10000);         // 分压电阻
    
    // Global temperature reading
    uint16_t temp;
    
    void setup()
    {
      Serial.begin(9600);
    }
    
    void loop()
    {
      temp = thermistor.read();   // Read temperature
      Serial.println(temp);
      delay(5000);
    }
  • Arduino 极简入门

    Arduino 早在 2005 年就已经开发出了第一版原型,直到 2010 年左右 Arduino 开始红遍全球,而 2015 年之后的创客热潮曾把它推向一个新的高度。国内只要和 STEM 教育、机器人教育、科技竞赛相关的场合,都离不开它的身影。而现今已是 2021 年,Arduino 过时了吗?感觉还没有,虽然有更多的替代产品出现、它的热度也逐渐褪去,但是它的功能和简单易用的特性依然实用。

    此外,还找到了一个以漫画形式的 Arduino 入门“说明书”。英文原版在这里:layout_template (jodyculkin.com) 由 Jody Culkin 在 2011年绘制 ;各国语言版可以在 Arduino Comic | Arduino 下载,官网上的中文翻译太弱鸡了,打算重新翻一下或者重新编排一下(但是水平有限,错误之处在所难免,敬请指正)。

  • 超便宜的 MP3 播放器 | TF卡 MP3 解码模块

    因为有个项目需要用到自动播放声音的功能,所以就用到了这个模块,该模块其实根本用不到 Arduino ,直接将模块上电 5v 即可。而整个模块加上喇叭成本只需要 RMB 8 元,及其划算,也就是说自制一个音响的成本也就 8 块人民币的🤣。板载 2W 单声道功放(5V供电最高可达3W)直接接喇叭(建议搭配4Ω 3W喇叭),3.5mm镀金耳机插座可接耳机或者外部音响。

    下列内容摘自淘宝店铺,仅供参考。

    • 带有MicroUSB供电接口,通过手机数据线使用移动电源或者USB充电器供电,也可采用3.7V锂电池 或 USB 5V供电。
    • 支持TF卡(手机内储卡),U盘播放模式。
    • 带有喇叭接线端子无需焊接,方便接线。
    • 模块设计方便改装。

    模块概述:

    • 支持MP3格式,上电自动播放,播放状态时红色LED指示灯更新。 
    • 支持U盘(测试过32G),TF卡(测试过16G)播放模式;上电默认TF卡模式,若TF卡不存在自动跳到U盘模式,而且两种设备都有安装,可以手动设置播放模式,详情请看按键操作说明。
    • 按键可调节上下曲目切换,音量+-,暂停/播放,模式切换,详情请看按键操作说明。
    • 可按键设置“单曲/全曲”循环,上电默认全曲循环,按“Repeat”键可更换循环模式。

    按键操作

    • Prev/V–”键:“短按”为切换“上一首”歌曲,“长按”为“音量递减”
    • Next/v++”键:“短按”为切换“下一首”歌曲,“长按”为“音量递增”
    • P/P/Mode”键:“短按”为“播放/暂停”切换,“长按”为U盘,TF卡“模式”选择
    • Repeat”键:“短按”,为“单曲/全曲”循环切换(无长按功能)

    注意:“长按”时间大约2S;若使用U盘模式,建议5V供电。

    接线方式

    接线方式特别简单,只需按正负极连接外接喇叭和外接电源即可,音质居然还不错(手动狗头)。

  • Arduino 使用 millis() 函数实现多任务处理

    多任务处理可以使一个或多个程序可以同时运行。在嵌入式系统中,微控制器还可以处理多任务处理并同时执行两个或多个任务,而无需停止当前指令。

    通常在 Arduino 中使用 delay() 函数来执行 LED 闪烁等周期性任务,但是 delay() 函数会暂停程序一段时间,并且不允许执行其他操作。因此,在 Arduino 中将 delay 替换为 millis() 函数可以同时执行多个任务,并使 Arduino 成为多任务控制器

    为什么不用 Arduino 中的 delay()

    在 Arduino 的参考文档中有两种类型的延迟函数,第一种是 delay() ,第二种是 delayMicroseconds()。两个函数在产生延迟时间方面是相同的。唯一的区别在于,在 delay() 函数中,传递的参数整数是以毫秒为单位,即如果我们写入 delay(1000) 则延迟将是1000毫秒,即1秒。类似地,在 delayMicroseconds() 函数中,传递的参数是微秒,即如果我们写 delayMicroseconds(1000),则延迟将是1000微秒,即1毫秒。

    重点是,两个函数都会暂停程序延迟函数传递的时间。因此,如果我们给出1秒的延迟,则处理器在1秒钟之前不能进入下一条指令。类似地,如果延迟是10秒,则程序将停止10秒,并且处理器将不允许进行下一指令,直到10秒过去。这妨碍了微控制器在速度和执行指令方面的性能。

    解释延迟函数缺点的最好例子是使用两个按钮:比如使用两个按钮切换两个LED。如果按下一个按钮,则相应的LED发光2秒;如果按下第二个,则LED应该发光4秒。但是当我们使用 delay() 时,如果用户按下第一个按钮,程序将停止2秒,如果用户在2秒延迟之前按下第二个按钮,则微控制器将不接受输入,因为程序是在停止阶段。

    Arduino的官方文档在其delay()函数描述的注释和警告中明确提到了这一点。

    为什么要使用 millis()

    为了克服使用 delay() 函数引起的问题,我们可以使用 millis() 函数,这个函数在习惯使用后很容易使用,它将使用100%的 CPU 性能而不会在执行指令时产生任何延迟。 millis() 函数只返回自 Arduino 开发板开始运行当前程序以来没有冻结程序所经过的毫秒数。大约50天后,这个时间数将溢出(即回到零)。

    就像 Arduino 有 delayMicroseconds() 一样,它也有微型版本的 millis(),那就是 micros()。 micros 和 millis 之间的差异是,micros() 在大约70分钟后会溢出,而millis()则是50天。

    使用 millis() 而不是 delay()

    要使用 millis() 进行计时和延迟,就是要记录并存储操作开始时间的时间,然后定期检查定义的时间是否已过。 如上所述,将当前时间存储在变量中。

    unsigned long currentMillis = millis();

    我们还需要两个变量来确定是否已经过了所需的时间。 我们已将当前时间存储在 currentMillis 变量中,但我们还需要知道定时周期何时开始以及周期有多长。 因此声明了 Interval (下方为 Period ,即延迟周期。类似于 delay 函数括号中的时间)和 previousMillis。 Interval 将告诉我们时间延迟周期,单位为毫秒;previosMillis 将存储事件发生的最后时间:

    unsigned long previousMillis;
    unsigned long period = 1000;

    但是如果同时有多个需要延迟的进程,那么需要定义多个 previousMillis,以便进行区分:

    unsigned long previousMillisHeater = 0;  //用来存时间
    unsigned long previousMillisVentilateur = 0;  //用来存时间
    unsigned long previousMillisMoteur = 0;  //用来存时间
    const long period = 30000;         // 周期,不中断正在进行的进程,单位为ms

    举一个简单的闪烁LED示例。 period = 1000 将告诉我们 LED 将闪烁1秒:

    const int ledPin =  4; // the LED pin number connected
    int ledState = LOW;             // used to set the LED state
    unsigned long previousMillis = 0;  //will store last time LED was blinked
    const long period = 1000;         // period at which to blink in ms
    
    void setup() {
      pinMode(ledPin, OUTPUT); // set ledpin as output
    }
    
    void loop() {
      unsigned long currentMillis = millis(); // store the current time
      if (currentMillis - previousMillis >= period) { // check if 1000ms passed
        previousMillis = currentMillis;   // save the last time you blinked the LED
        if (ledState == LOW) { // if the LED is off turn it on and vice-versa
          ledState = HIGH;
        } else {
          ledState = LOW;
        }
        digitalWrite(ledPin, ledState);//set LED with ledState to blink again
      }
    }

    这里,语句 if (currentMillis - previousMillis >= period) 检查是否已经过去了 1000ms 。如果超过1000ms,则LED闪烁并再次进入相同状态;此效果和 delay 函数相同,但是同时它不会暂停整个 Arduino 代码的运行。

    Arduino 的中断与其他微控制器的工作方式相同。 Arduino UNO 板有两个独立的引脚,用于连接 GPIO 引脚 2和 3 上的中断,这可能会在之后的文章中提到。

    参考资料