PS2 双轴按键摇杆实验
之前我们已经学习了如何使用 PWM 和 ADC,这节课我们来学习如何使用 PS2 双轴摇杆来控制舵机。
实验原理
摇杆一般在航模、电玩、遥控车、云台等设备上应用广泛,很多带有屏幕的设备也经常使用摇杆作为菜单选择的输入控制。
双轴按键摇杆主要由两个电位器和一个按键开关组成,两个电位器随着摇杆扭转角度分别输出 X、Y 轴上对应的电压值,在 Z 轴方向上按下摇杆可触发轻触按键。在配套机械结构的作用下,无外力扭动的摇杆初始状态下,两个电位器都处在量程的中间位置。它就是两个电位器和按键的组合体。电位器是可变电阻器,与中学时学的滑动变阻器类似。
PS2 摇杆的五个端口分别为 VCC,X,Button,Y,GND。
摆动 PS2 游戏摇杆时,随着接触刷改变接触位置,可变电阻器(电位器)的引脚处的输出电压即发生变化。X,Y 轴为模拟输入信号而 Z 轴是数字输入信号,因此,X 和 Y 端口连接到 ADC 引脚,而 Z 端口连接到数字端口。所以我们一共需要使用 ESP32 的三个 GPIO 引脚,其中两个模拟信号输入引脚和一个数字信号输入引脚。
PS2 游戏摇杆正常状态(不受力状态)检测电压常态时为 1.65V 附近,最大值 3.3V,最小值 0V,用 ESP32 自带 ADC 模数转换模块的两个通道分别检测电压值的变化就可以知道摇杆指向的位置了。
硬件电路设计
物料清单(BOM 表):
材料名称 | 数量 |
---|---|
PS2 摇杆模块 | 1 |
舵机 | 1 |
杜邦线(跳线) | 若干 |
PS2 模块的 +5V 引脚接 ESP32 的 3V3 引脚,一般情况下 ESP32 的 ADC 电压输入范围为 0-3.3V,高于 3.3V 可能会烧坏 ADC。摇杆模块的 GND 接 GND;模块的 SW 接 D4,VRX 接 D15,VRY 接 D2。
舵机接开发板另一侧的 5V 引脚、GND 和 D13。
软件程序设计
我们可以先在串口监视器中打印读取到的数值,代码如下:
#define PS2_X 15
#define PS2_Y 2
#define SW 4
void setup() {
// 配置衰减器
// analogSetAttenuation(ADC_11db);
// 配置输入模式
pinMode(PS2_X, INPUT);
pinMode(PS2_Y, INPUT);
pinMode(SW, INPUT_PULLUP);
// 配置串口通信波特率
Serial.begin(9600);
}
void loop() {
// 读取数值
Serial.printf("x: %d, y: %d, z: %d\n", analogRead(PS2_X), analogRead(PS2_Y), digitalRead(SW));
delay(100);
}
了解完如何在 Arduino 中控制 PS2 摇杆模块后,我们就可以使用 PS2 摇杆模块控制舵机了,这里我们还需要用到 Arduino 中的一个新的函数 map
。
map
函数主要功能为将范围为 A 的变量等比例转化至 B 中,在 Arduino 编程中有广泛应用,例如将 10 位模拟输入结果转化至 8 位模拟输出、利用模拟输入值控制舵机角度等。
x = map(value, fromLow, fromHigh, toLow, toHigh);
x,value 为同类型变量,fromHigh 与 fromLow 为 t 变量本身的上下界,toHigh 与 toLow 为 x 变量的上下界。该函数将t变量值根据范围比例变换后将结果存入 x 变量。
代码如下:
#define PS2_X 15
#define PS2_Y 2
#define SW 4
#define RESOLUTION 12
#define SERVO 13
#define CHANNEL 0
#define FREQ 50
int value;
//20ms 周期内,高电平持续时长 0.5-2.5 ms,对应 0-180 度舵机角度。
//对应 0.5ms(0.5ms/(20ms/256))
int min_width = 0.6 / 20 * pow(2, RESOLUTION);
//对应 2.5ms(2.5ms/(20ms/256))
int max_width = 2.5 / 20 * pow(2, RESOLUTION);
void setup() {
// 配置衰减器
analogSetAttenuation(ADC_11db);
// 设置 ADC 分辨率
analogReadResolution(RESOLUTION);
// 配置输入模式
pinMode(PS2_X, INPUT);
pinMode(PS2_Y, INPUT);
pinMode(SW, INPUT_PULLUP);
// 配置串口通信波特率
Serial.begin(9600);
// 用于设置 LEDC 通道的频率和分辨率
ledcSetup(CHANNEL, FREQ, RESOLUTION);
// 将通道与对应的引脚连接
ledcAttachPin(SERVO, CHANNEL);
}
void loop() {
// 将 ADC 的值映射到舵机的转动范围
value = map(analogRead(PS2_Y), 0, pow(2, RESOLUTION), min_width, max_width);
// 读取数值
Serial.printf("x: %d, y: %d, z: %d, 映射后的 y: %d\n", analogRead(PS2_X), analogRead(PS2_Y), digitalRead(SW), value);
ledcWrite(CHANNEL, value);
delay(100);
}
最后,我们通过第三方库 ESP32Servo.h
,让 PS2 摇杆控制舵机转动,代码如下:
#include <ESP32Servo.h>
#define PS2_X 15
#define PS2_Y 2
#define SW 4
#define SERVO 13
#define RESOLUTION 12
#define FREQ 50
// 定义 Servo 对象
Servo my_servo;
int value;
void setup() {
// 配置输入模式
pinMode(PS2_X, INPUT);
pinMode(PS2_Y, INPUT);
pinMode(SW, INPUT_PULLUP);
// 配置串口通信波特率
Serial.begin(9600);
// 分配硬件定时器
ESP32PWM::allocateTimer(0);
// 设置频率
my_servo.setPeriodHertz(FREQ);
// 关联 servo 对象与 GPIO 引脚,设置脉宽范围
my_servo.attach(SERVO, 500, 2500);
}
void loop() {
value = map(analogRead(PS2_Y), 0, pow(2, RESOLUTION), 0, 180);
// 读取数值
Serial.printf("x: %d, y: %d, z: %d, 映射后的 y: %d\n", analogRead(PS2_X),
analogRead(PS2_Y), digitalRead(SW), value);
// 输出PWM
my_servo.write(value);
delay(100);
}