最近更新于 2022-12-30 17:26

简介

舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统,通过 PWM 可以控制舵机转动到指定的角度。

演示用的舵机都是 180° 的

测试环境

  • 硬件(系统):树莓派 cm4 (官方64位系统 Debian 11)
  • 编译器 GNU gcc 10.2.1
  • WiringPi 库
  • 舵机 MG996R x 1 (演示 PWM 舵机控制使用)
  • 舵机 TS90A x 2 (演示手柄控制云台)
  • PCF 8591 数模转换模块
  • 手柄

GPIO 布局图

使用 BCM 编号

舵机使用演示

(1)接线

舵机有三条线,红线为正极,可 3.3V,也可 5V,建议 5V 动力更强;棕线接 GND;黄线接 PWM 控制,这里演示我就接 BCM 17。

(2)源码示例

#include "wiringPi.h"
#include "softPwm.h"

#define PIN 17  // BCM 17

int main()
{
    wiringPiSetupGpio();  // 初始化 GPIO,按 BCM 编号使用
    pinMode(PIN, OUTPUT);  // 设置引脚为输出
    softPwmCreate(PIN, 15, 200);  // 20ms 周期,初始高电平占比 1.5ms,居中
    delay(1000);  // 延时 1 s,给予时间足够时间给舵机转动
    softPwmWrite(PIN, 5);  // 转动到 -90°
    delay(2000);
    softPwmWrite(PIN, 25);  // 转动到 90°
    delay(1000);
    softPwmStop(PIN);  // 关闭 PWM
}

首先舵机会转动到中间 0° 位置,然后向右转到 -90° 位置,然后向左转到 90°位置

编译时 gcc 要加参数 -lwiringPi 进行链接

手柄控制

(1)接线

下面左右转动的舵机 PWM 接 BCM 17,上面上下摆动的舵机接 BCM 18。

PCF 8591 对树莓派接口就是 IIC,GND、VCC、SCL、SDA 分别对应就行,PCF 8591 插了跳线帽的话,全部拔掉,另外树莓派设置记得打开 I2C

手柄 GND、VCC 对着接到树莓派就行,然后 PCF 8591 的 AIN0、AIN1 分别接手柄 X、Y,手柄还有一根 SW 线,那是手柄可以下按,有一个按钮,这里不使用就不接线。

(2)源码示例

#include "wiringPi.h"
#include "softPwm.h"
#include "pcf8591.h"

#include <math.h>

#define PIN1 17  // BCM 17
#define PIN2 18  // BCM 18
#define PCF  64  // PCF8591 起始地址
#define AIN0 PCF
#define AIN1 PCF+1

/**
 * @brief 控制舵机
 *          pcf8591 读值范围为 0~255,125 时手柄居中(实际居中时不一定是 125,可能有偏差)
 *          然后要将手柄的读值转换为舵机转动角度,对应这里 5~25(0°~180°)
 *          (25 - 5)/ 256 = 0.78125
 *          所以手柄值 dir 与舵机转动角度控制值 ang 的关系为 ang = 5 + 0.78125 * dir
 * 
 * @param pin 舵机 PWM 引脚
 * @param op 0~255, 对应
 */
void steer(int pin, int dir)
{
    int ang = 5 + (int)round(0.078125 * dir);  // 四舍五入取整
    softPwmWrite(pin, ang);
}

int main()
{
    // 初始化
    wiringPiSetupGpio();
    pcf8591Setup(PCF, 0x48);  // AIN0、AIN1 分别对应 PCF+0、PCF+1,0x48为模块自己的 I2C 地址
    pinMode(PIN1, OUTPUT);
    pinMode(PIN2, OUTPUT);
    softPwmCreate(PIN1, 15, 200);  // 居中 0°
    softPwmCreate(PIN2, 15, 200);
    delay(1000);
    while (1)
    {
        int x = analogRead(AIN0);  // 读手柄值
        int y = analogRead(AIN1);

        steer(PIN1, x);
        steer(PIN2, y);
        delay(10);
    }
}

编译时要链接 wiringPi 库和数学库,加参数 -lwiringPi -lm

作者 IYATT-yx