Arduino

Arduino官网

Arduino IDE

1.下载Arduino IDE=>SOFTWARE页面下载所需适配的操作系统(Windows, Linux, macOS)等;
其中Windows版本MSI installer是需要安装的。zip的解压即可运行不需要安装。

esp32 自动更新时间 esp32官方固件_取值
2.Arduino IDE中添加ESP32开发板软件包数据:
Arduino IDE:文件>首选项>附加开发板管理器网址 中加入Arduino core for esp32的地址: https://github.com/espressif/arduino-esp32/releases/download/2.0.4/package_esp32_index.json
esp32 自动更新时间 esp32官方固件_嵌入式硬件_02

3.Arduino IDE中安装ESP32开发板软件包:
esp32 自动更新时间 esp32官方固件_单片机_03

ESP32简述

32-bit MCU & 2.4 GHz Wi-Fi & Bluetooth/Bluetooth LE

  • 集成 ESP32 系列芯片,两个或一个可以单独控制的 Xtensa® 32-bit LX6 处理器,时钟频率可调,范围为 80 MHz 到 240 MHz
  • 19.5 dBm 天线端输出功率,确保良好的覆盖范围
  • 传统蓝牙支持 L2CAP,SDP,GAP,SMP,AVDTP,AVCTP,A2DP (SNK) 和 AVRCP (CT) 协议
  • 低功耗蓝牙 (Bluetooth LE) 支持 L2CAP,GAP,GATT,SMP,和 GATT 之上的 BluFi,SPP-like 协议等
  • 低功耗蓝牙连接智能手机,发送低功耗信标,方便检测
  • 睡眠电流小于 5 μA,适用于电池供电的可穿戴电子设备
  • 外设包括电容式触摸传感器,霍尔传感器,SD 卡接口,以太网,高速 SPI,UART,I2S 和 I2C
    – 通过 RF 认证以及软件协议认证

GPIO端口

GPIO相关函数:

  • void pinMode(uint8_t pin, uint8_t mode):
    设置GPIO端口模式,pin:GPIO端口号,mode取值有:
  1. OUTPUT:输出模式
  2. OUTPUT_OPEN_DRAIN:开漏输出模式
  3. INPUT :输入模式
  4. INPUT_PULLUP :输入模式,使能上拉
  5. INPUT_PULLDOWN :输入模式,使能下拉
  • void digitalWrite(uint8_t pin, uint8_t val):
    写入GPIO状态,pin:GPIO端口号, val:LOW/HIGH
  • int digitalRead(uint8_t pin) :
    读取GPIO端口状态;
  • void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode):
    使能中断,无参回调函数,pin:GPIO端口号,handler:中断回调函数
    mode:中断触发方式:
  1. RISING: 上升沿触发
  2. FALLING:下降沿触发
  3. CHANGE:GPIO状态改变触发
  • void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void * arg, int mode):
    使能中断,有参回调函数,pin:GPIO端口号,handler:中断回调函数,arg:传入回调函数的参数,
    mode:中断触发方式:
  1. RISING: 上升沿触发
  2. FALLING:下降沿触发
  3. CHANGE:GPIO状态改变触发
  • void detachInterrupt(uint8_t pin):
    禁止中断,pin:GPIO端口号

GPIO输入

#define KEY_IO (0)  // 按键GPIO端口定义
static int val=0;   // 记录IO口状态变量

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200); // 设置串口打印波特率

  pinMode(KEY_IO, INPUT); // 设置PIN模式,为输入模式
  /*
    INPUT  :输入模式
    INPUT_PULLUP  :输入模式,使能上拉
    INPUT_PULLDOWN :输入模式,使能下拉
  */
}

void loop() {
  // put your main code here, to run repeatedly:
  val = digitalRead(KEY_IO); // 读取GPIO端口值
  Serial.println(val);
  delay(500);
}

GPIO输出

#define LED_IO (2)

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  // 无参中断模式
  pinMode(LED_IO, OUTPUT); // 设置GPIO为输入上拉模式
  
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(LED_IO, HIGH);
  delay(100);
  digitalWrite(LED_IO, LOW);
  delay(100);
}

GPIO中断


#define KEY_IRQ_PIN0 (0)
#define KEY_IRQ_PIN23 (23)

/* 有参中断回调函数 */
void irq_arg_callback(void *arg)
{
  Serial.printf("%s\n", arg);
}

/* 无参中断回调函数 */
void irq_callback(void)
{
  int io = digitalRead(KEY_IRQ_PIN0);
  Serial.printf("触发中断,GPIO电平:%d\n", io);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  // 无参中断模式
  pinMode(KEY_IRQ_PIN0, INPUT_PULLUP); // 设置GPIO为输入上拉模式
  attachInterrupt(KEY_IRQ_PIN0, irq_callback, CHANGE); // 使能中断, GPIO状态改变, CHANGE,FALLING,RISING
  OUTPUT_OPEN_DRAIN;
  // 有参中断模式
  pinMode(KEY_IRQ_PIN23, INPUT_PULLUP);
  attachInterruptArg(KEY_IRQ_PIN23, irq_arg_callback, (void*)"hello", CHANGE);
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(30000); // 延时10秒
  detachInterrupt(KEY_IRQ_PIN0);  // 禁止中断
  detachInterrupt(KEY_IRQ_PIN23); // 禁止中断
}

ADC模数转换

#define ADC_IO 36
int hallValue = 0;
uint16_t adcValue = 0;
uint32_t adcVoltage = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(1000);  
}

void loop() {
  // put your main code here, to run repeatedly:
  adcValue = analogRead(ADC_IO);
  adcVoltage = analogReadMilliVolts(ADC_IO);
  Serial.printf("ADC-Value:%d, ADC-Voltage:%\n", adcValue ,  adcVoltage);

  // 读取内置hall传感器值
  // hallValue = hallRead();
  // Serial.println(hallValue);
  delay(100);
}

DAC数模转换

两路8bits数模转换,取值范围0~255(0V ~ 3.3V)
void dacWrite(uint8_t pin, uint8_t value);

#define DAC1_IO (25)
#define DAC2_IO (26)

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
}

void loop() {
  // put your main code here, to run repeatedly:
  dacWrite(DAC1_IO, 0);
  dacWrite(DAC2_IO, 0);
  delay(1000);
  dacWrite(DAC1_IO, 128);
  dacWrite(DAC2_IO, 128);
  delay(1000);
  dacWrite(DAC1_IO, 255);
  dacWrite(DAC2_IO, 255);
  delay(1000);
}

LEDC(PWM)

LED_PWM 主要用于控制 LED 的亮度和颜色,也可以产生 PWM 信号用于其他用途。LED_PWM 有 16 路通道,即 8 路高速通道和 8 路低速通道。这 16 路通道能够产生独立的数字波形来驱动 RGB LED 设备。高速或低速通道可以由四个高速定时器之一或四个低速定时器之一进行驱动。PWM 控制器还能够自动逐渐增加或减少占空比,在无须处理器干预的情况下实现亮度和颜色渐变。LED_PWM 还支持小数分频。
esp32 自动更新时间 esp32官方固件_#define_04

  • double ledcSetup(uint8_t chan, double freq, uint8_t bit_num)
    功能:设置LEDC;
    参数:
    chan:通道,取值0~15
    freq:设置频率
    bit_num:计数位,取值0~20
  • void ledcWrite(uint8_t chan, uint32_t duty)
    功能:设置指定通道PWM占空比
    参数:
    chan:通道,取值0~15
    duty: 占空比参数,最大值有计数位决定,如10位 1 0 2 = 1024 10^2=1024 102=1024
  • uint32_t ledcRead(uint8_t chan)
    功能:读取指定通道占空比
    参数:
    chan:通道,取值0~15
    返回值:指定通道当前duty值
  • double ledcReadFreq(uint8_t chan)
    功能:获取指定通道当前频率值
    参数:
    chan:通道,取值0~15
    返回值:返回指定通道当前频率值
  • double ledcWriteTone(uint8_t chan, double freq)
    功能:指定通道设置音调频率(可以驱动蜂鸣器发出音符)
    参数:
    chan:通道,取值0~15
    freq: 设置频率
  • double ledcWriteNote(uint8_t chan, note_t note, uint8_t octave)
    功能:设置指定通道,指定音符的八度音
    参数:
    chan:通道,取值0~15
    note: 音符名(NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B)
    octave: 八度音符取值0~8
  • void ledcAttachPin(uint8_t pin, uint8_t chan)
    功能:将指定通道绑定到指定IO端口;
    参数:
    pin:GPIO端口号
    chan:通道,取值0~15
  • void ledcDetachPin(uint8_t pin)
    功能:解绑IO端口的LEDC功能;
    esp32 自动更新时间 esp32官方固件_取值_05
#define LEDC_CHANNEL_0 0    // LEDC通道 0~15
#define LEDC_FREQ 5000      // LEDC频率
#define LEDC_TIMER_12_BIT 12  // LEDC定时器精度
#define LED_PIN 2 // LED连接端口

void setup() {
  // put your setup code here, to run once:
  ledcSetup(LEDC_CHANNEL_0, LEDC_FREQ, LEDC_TIMER_12_BIT);
  ledcAttachPin(LED_PIN, LEDC_CHANNEL_0);
}

void loop() {
  // put your main code here, to run repeatedly:
  ledcWrite(LEDC_CHANNEL_0, 4095);        // 写入duty占空比
  delay(1000);
  ledcWriteTone(LEDC_CHANNEL_0, 262);     // 写入Tone频率
  delay(1000);
  ledcWriteNote(LEDC_CHANNEL_0, NOTE_C, 4); // 写入Note参数
  delay(1000);
  for (int octave=0; octave<=8; octave++) {
    ledcWriteNote(LEDC_CHANNEL_0, NOTE_C, octave);
    delay(1000);
  }

}

TOUCH触摸传感器

T0~T9 10路触摸端口,相关函数:

  • void touchSetCycles(uint16_t measure, uint16_t sleep)
  • uint16_t touchRead(uint8_t pin)
  • void touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), uint16_t threshold)

uint16_t t0val = 0; // Touch0 读取值

// Touch 3 PIN15 中断模式
int threshold = 40; // 中断模式阀值
bool touch3detected = false;

void Touch3Handler() {
  touch3detected = true;
  
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  // touchSetCycles(100, 50);
  // Touch 3 PIN15 中断模式
  touchAttachInterrupt(T3, Touch3Handler, threshold);
}

void loop() {
  // put your main code here, to run repeatedly:
  // Touch 0 PIN4 读取触摸传感器
  t0val = touchRead(T0);
  Serial.printf("t0:%d\n", t0val);
  
  // Touch 3 PIN15 中断模式
  if (touch3detected) {
    touch3detected = false;
    Serial.println("Touch 3 detected");
  }
  delay(100);
}

CPU主频


typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t;

typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);

bool addApbChangeCallback(void * arg, apb_change_cb_t cb);
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb);

//function takes the following frequencies as valid values:
//  240, 160, 80    <<< For all XTAL types
//  40, 20, 10      <<< For 40MHz XTAL
//  26, 13          <<< For 26MHz XTAL
//  24, 12          <<< For 24MHz XTAL
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz);

uint32_t getCpuFrequencyMhz();  // In MHz
uint32_t getXtalFrequencyMhz(); // In MHz
uint32_t getApbFrequency();     // In Hz

void apb_change_cb(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb)
{
  Serial.printf("arg:%s\n", arg);
  switch(ev_type) {
    case APB_BEFORE_CHANGE:
      Serial.printf("APB_BEFORE_CHANGE\n");
      break;
    case APB_AFTER_CHANGE:
      Serial.printf("APB_AFTER_CHANGE\n");
      break;
    default:
      break;
  }

  Serial.printf("old_apb:%d, new_apb:%d\n", old_apb, new_apb);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  addApbChangeCallback((void*)"APB-Add", apb_change_cb);

  uint32_t cpuf = getCpuFrequencyMhz();  // In MHz
  uint32_t xtal = getXtalFrequencyMhz(); // In MHz
  uint32_t apbf = getApbFrequency();     // In Hz

  Serial.printf("cpuf:%d, xtal:%d, apbf:%d\n", cpuf, xtal, apbf);
  delay(1000);

  setCpuFrequencyMhz(160);
  Serial.printf("cpuf:%d, xtal:%d, apbf:%d\n", getCpuFrequencyMhz(), getXtalFrequencyMhz(), getApbFrequency());

  delay(1000);
  removeApbChangeCallback((void*)"APB-Add", apb_change_cb);
  Serial.printf("--------------------\n");
}

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

}

Sigma-Delta Modulation(SDM)

ESP32有一个二阶西格玛-德尔塔调制器,可以产生独立的PDM脉冲。
德尔塔-西格玛调制将一个模拟电压信号转换为一个脉冲频率,或脉冲密度,可以理解为脉冲密度调制(PDM)。通俗一点对比PWM(脉宽调制)来说SDM是调节脉冲密度(类似于FM调频)。PWM是调节脉冲宽度即占空比,SDM是调节脉冲密度即单位时间内有多少个脉冲。

  • uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq)
    功能:设置使能SDM功能
    参数:
    channel:SDM通道,取值范围0-7
    freq:SDM频率,取值范围1220-312500
  • void sigmaDeltaWrite(uint8_t channel, uint8_t duty)
    功能:设置指定通道SDM的脉冲密度参数
    参数:
    channel:SDM通道,取值范围0-7
    duty:脉冲密度,取值范围0~255
  • uint8_t sigmaDeltaRead(uint8_t channel)
    功能:读取指定SDM通道的脉冲密度参数
    参数:
    channel:SDM通道,取值范围0-7
    返回值:返回指定通道SDM的脉冲密度参数(0~255)
  • void sigmaDeltaAttachPin(uint8_t pin, uint8_t channel)
    功能:绑定指定IO端口到指定SDM通道
    参数:
    pin: GPIO端口号
    channel:SDM通道,取值范围0-7
  • void sigmaDeltaDetachPin(uint8_t pin)
    功能:解除指定端口SDM功能
    参数:
    channel:SDM通道,取值范围0-7
#define D_SDM_IO (2)
#define D_SDM_FREQ  (2440)
#define D_SDM_CHANNEL (0)

void setup() {
  // put you1r setup code here, to run once:
  sigmaDeltaSetup(D_SDM_CHANNEL, D_SDM_FREQ);  //chan 0-7 freq 1220-312500
  sigmaDeltaAttachPin(D_SDM_IO, D_SDM_CHANNEL); //channel 0-7
}

void loop() {
  // put your main code here, to run repeatedly:
  for (int i=0; i<256; i++) {
    sigmaDeltaWrite(D_SDM_CHANNEL, i);
    delay(20);
  }

  // sigmaDeltaDetachPin(D_SDM_IO);
}

舵机控制

esp32 自动更新时间 esp32官方固件_取值_06
esp32 自动更新时间 esp32官方固件_嵌入式硬件_07

需要安装ServoESP32舵机驱动库:
esp32 自动更新时间 esp32官方固件_取值_08
简单循环设置舵机转角:

#include <Servo.h>

static const int servoXPin = 4;
static const int servoYPin = 5;

Servo servoX;
Servo servoY;

void setup() {
    Serial.begin(115200);
    servoX.attach(servoXPin);
    servoY.attach(servoYPin);
}

void loop() {
    for(int posDegrees = 0; posDegrees <= 180; posDegrees++) {
        servoX.write(posDegrees);
        servoY.write(posDegrees);
        Serial.println(posDegrees);
        delay(100);
    }

    for(int posDegrees = 180; posDegrees >= 0; posDegrees--) {
        servoX.write(posDegrees);
        servoY.write(posDegrees);
        Serial.println(posDegrees);
        delay(100);
    }
}

摇杆控制舵机旋转角度

#include <Servo.h>

#define D_JOYX_PIN 36
#define D_JOYY_PIN 39

uint16_t adcx = 0;
uint16_t adcy = 0;

static const int servoXPin = 4;
static const int servoYPin = 0;
Servo servoX;
Servo servoY;

void setup() {
    Serial.begin(115200);
    servoX.attach(servoXPin);
    servoY.attach(servoYPin);
}

void loop() {
  adcx = analogRead(D_JOYX_PIN);
  adcy = analogRead(D_JOYY_PIN);

  int xDegrees = map(adcx, 0, 4095, 0, 180);
  int yDegrees = map(adcy, 0, 4095, 0, 180);

  Serial.printf("adcx:%d, adcy:%d, xDegrees:%d, yDegrees:%d\n", adcx, adcy, xDegrees, yDegrees);

  servoX.write(xDegrees);
  servoY.write(yDegrees);
  delay(20);
}