课程目录

【概述】0 什么是智能系统

【阶段一】1 PlatformIO环境配置和GPIO

【阶段一】2 PWM与电机控制

【阶段一】3 外部中断和定时器中断

【阶段一】4 串口与串口中断

【阶段一】5 I2C与MPU6050模块

2 PWM与电机控制

2.1 PWM&呼吸灯

在上一讲中我们学习了GPIO接口,实现了高电平、低电平的输出。通过高电平和低电平可以控制小灯的亮和灭。然而生活中,我们可能不仅仅要控制电气设备的工作与否,可能还需要控制它工作的状态,比如控制灯的亮度和电机的转速。

在高中物理中,我们学习过,如果想要调节小灯泡(白炽灯)的亮度可以通过调节灯泡两端的电压来调节。然而,我们的微处理器它可以很方便的输出高低电平信号,但是很难输出电压连续可变的信号。那我们要如何来调节灯泡的亮度呢?

我们可以来分析一下。假设我们把电灯的亮度变化曲线按单位时间均匀分割,如下图所示。

可以看到在单位时间内,小灯功率曲线下的面积是小灯在单位时间内对外输出的能量。如果我以单位时间为周期,在单位时间已开始让小灯打开,当小灯发出的能量达到一定数值之后就关闭直到单位时间结束,下一周期再控制开和关,以此类推,从而实现每个周期内小灯发出的光能量和之前一致。

由于我们在单位周期内让小灯发出的能量是一致的,而我们人类的眼睛有视觉暂留效应,我们感受到光的亮度也是和之前一致。而这种通过调节脉冲信号宽度的方式就是所谓的PWM。

2.1.1PWM信号是什么?

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制PWM是一种通过对模拟信号电平进行数字编码的方法,通过改变脉冲的宽度来控制电路的开关状态,从而实现对模拟信号的调节。

接下来聊聊与其相关的几个概念

(1)PWM频率(f):是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期);也就是说一秒钟 PWM有多少个周期 单位: Hz 表示方式: 50Hz 100Hz

(2)PWM的周期(T):信号从高电平到低电平再回到高电平的时间

T=1/f周期=1/频率50Hz = 20ms 一个周期

如果频率为50Hz ,也就是说一个周期是20ms 那么一秒钟就有 50次PWM周期

(3)占空比:是一个脉冲周期内,高电平的时间与整个周期时间的比例 单位: % (0%-100%)

2.1.2PWM用数字信号模拟模拟信号

占空比的不断变化可以使PWM从宏观上实现类似正弦波等不同的波形信号;将变化的PWM信号接在小灯泡上也就能实现小灯的渐亮渐暗的效果(呼吸灯)

2.2PWM实现呼吸灯

2.2.1拿出所需的器材并接线

  • ESP32开发板
  • 面包板
  • 红绿灯
  • 杜邦线
  • 跳线
  • Type-C数据线
  • 一台装好PlatformIO环境的电脑
PWM实现呼吸灯接线图

2.2.2软件程序设计

1.analogWrite() 函数实现呼吸灯效果

想要通过 Arduino 输出 PWM 有两种方法,第一种就是使用 Arduino 自带的 analogWrite(pin, value) 函数,其中的两个参数:

  • pin:要写入的 Arduino 引脚。允许的数据类型:int.
  • value:占空比:介于 0(始终关闭)和 255(始终开启)之间。允许的数据类型:int.
#include <Arduino.h>
// 宏定义 GPIO 输出引脚
#define LED_PIN   12

void setup() {
  // 配置 GPIO 输出引脚
  pinMode(LED_PIN, OUTPUT);

}

void loop() {
  // 实现渐亮效果
  for(int i=0;i<256;i++) {
    // 设置亮度模拟值
    analogWrite(LED_PIN, i);
    // 延时 10ms
    delay(10);
    }
  // 实现渐灭效果
  for(int i=255;i>=0;i--) {
    // 设置亮度模拟值
    analogWrite(LED_PIN, i);
    // 延时 10ms
    delay(10);
    }
}

(1)这里用到的analogWrite函数的语法非常简单,只需要指定要控制的引脚和输出电压即可。例如,analogWrite(9, 127)就表示将数字引脚9的输出电压设置为50%的占空比,即2.5V。在Arduino编程中,输出电压的范围是0~255,对应的电压值是0~5V。因此,analogWrite函数可以实现对模拟电路的精确控制。

(2)宏定义(#define)是可以将一对文本进行替换,在编译器读到需要被替换的文本的时候,会将这些文本全部替换成我们给定的文本。在这里,我们将 “LED_PIN” 使用宏定义,定义为 12,在以后的语句中,一旦编译器读取到 “LED_PIN” 就会直接将该位置的 “LED_PIN” 替换为对应的数字文本。

(3)for循环一般形式为

for (初始化表达式; 条件表达式; 更新表达式) {
    // 循环体
};

执行流程:第一步先执行初始化表达式。

  然后执行条件表达式。若其为真,则执行 for 语句中循环体和更新表达式

  若其为假,则结束循环。

2.LEDC外设输出PWM信号实现呼吸灯效果

第二种就是使用LEDC控制函数产生PWM信号,接下来我来介绍LEDC的有关控制函数

  • ledcSetup:这个函数的作用是设置LEDC通道【0 ~ 15一共有16路通道,高速通道是0-7由80兆赫兹时钟驱动,低速通道是8-16由1兆赫兹时钟驱动。(初学者这个不理解也ok,我们主要学习该如何产生这个PWM信号)】对应的频率和分辨率(分辨率:把一个周期平均分成多少2的多少次方,分辨率越高,分辨率可以实现的占空比变化的越精细,从而可以更好地满足不同的控制需求)这个函数需要三个参数分别是通道号,频率和分辨率(0 ~ 10)。函数最后返回频率
uint32_t    ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits);
  • ledcAttachPin:这个函数的作用是 将 GPIO 口与 LEDC 通道关联以实现输出;两个参数:引脚号和通道号
void        ledcAttachPin(uint8_t pin, uint8_t channel);
  • ledcWrite:这个函数作用是指定通道输出一定占空比波形;两个参数:通道号和占空比
void        ledcWrite(uint8_t channel, uint32_t duty);
  • ledcDetachPin:这个函数作用是解除 GPIO 口与 LEDC 通道的关联;一个参数:引脚号
void        ledcDetachPin(uint8_t pin);

使用 LEDC 外设的时候需要遵循以下步骤:

  1. 使用 ledcSetup() 函数建立 LEDC 通道;
  2. 通过 ledcAttachPin() 将 GPIO 口与 LEDC 通道关联;
  3. 通过 ledcWrite()设置频率;
  4. 通过 ledcDetachPin() 解除 GPIO 口与 LEDC 通道的关联

所有我们可以通过以下代码,实现呼吸灯效果:(pow是指数函数)

#include <Arduino.h>
#define FREQ        2000    // 频率
#define CHANNEL     0       // 通道
#define RESOLUTION  8       // 分辨率
#define LED         11     // LED 引脚

void setup()
{
  ledcSetup(CHANNEL, FREQ, RESOLUTION); // 设置通道
  ledcAttachPin(LED, CHANNEL);          // 将通道与对应的引脚连接
}

void loop()
{
  // 逐渐变亮
  for (int i=0;i<pow(2, RESOLUTION); i++)
  {
    ledcWrite(CHANNEL, i); // 输出PWM
    delay(5);
  }

  // 逐渐变暗
  for (int i=pow(2, RESOLUTION)-1;i>=0;i--)
  {
    ledcWrite(CHANNEL, i); // 输出PWM
    delay(5);
  }
}

2.3PWM实现电机驱动

2.3.1电机介绍

直流电动机是依靠直流电驱动的电动机,它有两个电极,一端接正极,一端接负极。电压越大转速越快。将正负极反过来,电机还可以反转。(不要直接将其接到面包板上,这种电机的驱动一般需要几百毫安的电流,单片机承受不住,会烧坏。这时候就需要电机驱动芯片)

2.3.2电机驱动芯片(tb6612)

TB6612FNG可以驱动两个电机,每一个驱动都有两个逻辑输入引脚,两个输出引脚和一个PWM引脚。可以通过给两个逻辑输入引脚不同的电平来控制电机的运行状态,通过PWM输入引脚实现电机调速。

1.tb6612引脚说明
2.tb6612模式说明

制动是刹车,停止/待机是滑行。

总结:

①PWM 信号控制输出电压大小从而达到控制转速的作用

②AIN1 与 ANI2 信号一起控制电机的正反转以及停止状态

2.3.3代码实现电机驱动

(1)拿出所需器材并接线
  • ESP32开发板
  • 面包板
  • tb6612电机驱动模块
  • 电机
  • 杜邦线
  • 跳线
  • Type-C数据线
  • 一台装好PlatformIO环境的电脑
PWM实现电机驱动接线图
#include <Arduino.h>
#define AIN1        10
#define AIN2        9
#define FREQ        2000    // 频率
#define CHANNEL     0       // 通道
#define RESOLUTION  8       // 分辨率
#define PWM         12    // PWM引脚

void setup() {
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  ledcSetup(CHANNEL, FREQ, RESOLUTION); // 设置通道
  ledcAttachPin(PWM, CHANNEL);          // 将通道与对应的引脚连接
}

void loop() {

  ledcWrite(CHANNEL, 255);   
  digitalWrite(AIN1,HIGH);
  digitalWrite(AIN2,LOW);

}

2.3.4代码实现电机变速驱动

#include <Arduino.h>
#define AIN1     10
#define AIN2     9
#define FREQ        2000    // 频率
#define CHANNEL     0       // 通道
#define RESOLUTION  8       // 分辨率
#define PWM         12    // PWM引脚

void setup() {
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  ledcSetup(CHANNEL, FREQ, RESOLUTION); // 设置通道
  ledcAttachPin(PWM, CHANNEL);          // 将通道与对应的引脚连接
}

void loop() {

   
  //逐渐加速
  for (int i=0;i<pow(2, RESOLUTION); i++)
  {
    ledcWrite(CHANNEL, i); // 输出PWM
    delay(10);
  }

  // 逐渐减速
  for (int i=pow(2, RESOLUTION)-1;i>=0;i--)
  {
    ledcWrite(CHANNEL, i); // 输出PWM
    delay(10);
  }

   
  digitalWrite(AIN1,HIGH);
  digitalWrite(AIN2,LOW);

}

2.4开环与闭环的区别

2.4.1开环的介绍

开环的英文名是open-loop。开环相对于闭环而言,也叫开环控制系统。意思就是不将控制的结果反馈回来影响当前控制的系统。开环就相当于单向操作,我们给控制器一个值,控制器就按这个值操作控制。也就是只控制输出,不计后果的控制。这种系统比较简单,容易掌握使用,工作稳定,但精度和速度的提高受到限制,所以一般仅用于不考虑外界影响,或惯性小、精度要求不高的一些系统。打个比方,开环相当于开水龙头,你拧到什么位置,水龙头就出多少大小的水,没有反馈信号。

2.4.2闭环的介绍

  闭环控制是指控制器输出信号不仅作用于执行器,同时也通过传感器等设备采集系统的实际输出信号,与设定值进行比较,并根据比较结果调整控制器输出信号的大小和时间,以使系统输出信号逐渐接近设定值。在闭环控制中,系统反馈信号的使用和处理可以使控制系统更加稳定和精确。

Avatar photo

作者 xhl

发表回复