嵌入式 · 2023年 12月 9日 0

树莓派4b控制pca9685模块详情与技术细节

配置详情:

树莓派4b控制pca9685模块,地址是0x41 ,在pca9685模块的0和1号输出口接入mg90s舵机

示例代码:

#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <stdio.h>
#include <unistd.h>

// PCA9685 registers
#define MODE1 0x00
#define PRESCALE 0xFE
#define LED0_ON_L 0x06
#define LED_ON_STEP 4

// PCA9685 constants
#define PCA9685_ADDRESS 0x41
#define FREQUENCY 50
#define PWM_RANGE 4096

// MG90S constants
#define MG90S_MIN_PULSE_WIDTH 600
#define MG90S_MAX_PULSE_WIDTH 2400
#define MG90S_DEGREE_RANGE 180

// Function to initialize PCA9685
int initPCA9685(int fd) {
    wiringPiI2CWriteReg8(fd, MODE1, 0x00); // Reset PCA9685
    usleep(10000);

    int prescaleval = (25000000 / (PWM_RANGE * FREQUENCY)) - 1;
    wiringPiI2CWriteReg8(fd, MODE1, 0x10); // Sleep mode
    wiringPiI2CWriteReg8(fd, PRESCALE, (prescaleval & 0xFF)); // Set the prescaler
    wiringPiI2CWriteReg8(fd, MODE1, 0x00); // Wake up

    return fd;
}

// Function to set the PWM pulse for a specific servo
void setServoPulse(int fd, int channel, int pulse) {
    int on = 0;
    int off = pulse * PWM_RANGE / 20000; // 20ms base
    wiringPiI2CWriteReg8(fd, LED0_ON_L + LED_ON_STEP * channel, on & 0xFF);
    wiringPiI2CWriteReg8(fd, LED0_ON_L + LED_ON_STEP * channel + 1, on >> 8);
    wiringPiI2CWriteReg8(fd, LED0_ON_L + LED_ON_STEP * channel + 2, off & 0xFF);
    wiringPiI2CWriteReg8(fd, LED0_ON_L + LED_ON_STEP * channel + 3, off >> 8);
}

// Function to move the servo to a specific degree
void moveServo(int fd, int channel, int degree) {
    if (degree < 0) {
        degree = 0;
    } else if (degree > MG90S_DEGREE_RANGE) {
        degree = MG90S_DEGREE_RANGE;
    }

    int pulseWidth = MG90S_MIN_PULSE_WIDTH + (degree * (MG90S_MAX_PULSE_WIDTH - MG90S_MIN_PULSE_WIDTH) / MG90S_DEGREE_RANGE);
    setServoPulse(fd, channel, pulseWidth);
}

int main() {
    wiringPiSetup();

    int fd = wiringPiI2CSetup(PCA9685_ADDRESS);
    if (fd == -1) {
        printf("Error initializing I2C\n");
        return 1;
    }

    initPCA9685(fd);

    while (1) {
        // Move servo on channel 0
        moveServo(fd, 0, 0);
        usleep(1000000); // Wait for 1 second

        moveServo(fd, 0, 180);
        usleep(1000000); // Wait for 1 second

        // Move servo on channel 1
        moveServo(fd, 1, 0);
        usleep(1000000); // Wait for 1 second

        moveServo(fd, 1, 180);
        usleep(1000000); // Wait for 1 second
    }

    return 0;
}

代码详解:

寄存器详解:

PCA9685 模块的寄存器地址,每个寄存器存储着不同的配置信息。以下是这些寄存器的简要解释:

子地址寄存器:

PCA9685_SUBADR1 (0x2)
PCA9685_SUBADR2 (0x3)
PCA9685_SUBADR3 (0x4)
这些寄存器用于设置 PCA9685 模块的从设备地址。你可以通过配置这些寄存器,使 PCA9685 响应多个 I2C 地址。

控制模式寄存器:

PCA9685_MODE1 (0x0)
这个寄存器用于配置 PCA9685 的控制模式。通过设置不同的位,你可以控制 PCA9685 的工作模式,比如启用/禁用自动递增、设置输出驱动能力等。

预分频器寄存器:

PCA9685_PRESCALE (0xFE)
通过设置这个寄存器,可以调整 PCA9685 的时钟预分频器,从而影响 PWM 的频率。

LED寄存器:

LED0_ON_L (0x6)
LED0_ON_H (0x7)
LED0_OFF_L (0x8)
LED0_OFF_H (0x9)
这些寄存器用于配置每个 PWM 通道的 PWM 脉冲宽度。每个通道有两个寄存器,分别用于设置 PWM 脉冲的开始和结束位置。

全局 LED寄存器:

ALLLED_ON_L (0xFA)
ALLLED_ON_H (0xFB)
ALLLED_OFF_L (0xFC)
ALLLED_OFF_H (0xFD)
这些寄存器用于配置所有通道的 PWM 脉冲宽度。与单个通道的寄存器类似,每个通道有两个寄存器,分别用于设置 PWM 脉冲的开始和结束位置。

在控制 PCA9685 进行舵机控制时,通常会用到 LED0_ON_L、LED0_ON_H、LED0_OFF_L 和 LED0_OFF_H 这几个寄存器,因为它们控制单个通道的 PWM 脉冲。

pca9685各个工作模式的不同详解:

在 PCA9685 模块中,工作模式主要通过控制模式寄存器(MODE1)的设置来实现。下面详细解释 PCA9685 模块的不同工作模式:

MODE1 寄存器(0x00)
MODE1 寄存器控制 PCA9685 的各种配置和工作模式。下面是 MODE1 寄存器中各个位的含义:

Bit 7 (SLEEP):

0: PCA9685 处于正常工作模式。
1: PCA9685 进入睡眠模式。在睡眠模式下,所有 PWM 输出将被关闭,设备进入低功耗状态。
Bit 6 (SUB1):

用于启用/禁用 PCA9685 的 I2C 从设备地址 1。
Bit 5 (SUB2):

用于启用/禁用 PCA9685 的 I2C 从设备地址 2。
Bit 4 (SUB3):

用于启用/禁用 PCA9685 的 I2C 从设备地址 3。
Bit 3 (ALLCALL):

0: 禁用 PCA9685 的全局 I2C 地址。
1: 启用 PCA9685 的全局 I2C 地址。启用后,I2C 总线上的所有设备都可以响应这个地址。
Bits 2-0 (AI2, AI1, SLEEP):

这些位控制自动递增功能和外部时钟源的选择。

自动递增功能详解:

PCA9685 模块的自动递增功能允许你按顺序递增 PWM 通道寄存器的地址。通过配置 MODE1 寄存器中的 AI(Auto-Increment)位,可以控制是否启用这一功能。

在 PCA9685 模块中,MODE1 寄存器的 AI 位是第 5 位(从右数第 3 位)。以下是该位的含义:

Bit 2 (AI):
0: 禁用自动递增功能。
1: 启用自动递增功能。
如果启用了自动递增功能,写入 PWM 通道寄存器时,PCA9685 将自动递增到下一个 PWM 通道寄存器的地址。这对于依次设置多个 PWM 通道非常方便,而不必每次都手动指定地址。

在初始化 PCA9685 模块时,如果你想使用自动递增功能,可以将 MODE1 寄存器的 AI 位设置为 1。在示例代码中,这是在设置预分频器之前进入睡眠模式之后的操作:

参数#define PRESCALE 0xFE含义:

在PCA9685模块中,PRESCALE 寄存器用于设置时钟预分频器的值,从而影响PWM输出的频率。PRESCALE 寄存器的地址是 0xFE

具体来说,时钟频率是通过以下公式计算的:

Clock frequency=Oscillator frequency/(4096×(prescale value+1))

其中:

  • Oscillator frequency 是 PCA9685 内部振荡器的频率,通常为 25MHz。
  • Prescale value 是 PRESCALE 寄存器的值。

通过调整 PRESCALE 寄存器的值,你可以改变时钟频率,从而影响 PWM 输出的频率。在示例代码中,时钟频率被设置为 50 Hz50Hz,对应的 PRESCALE 值通过以下计算得到:

prescale value= Oscillator frequency/(4096×desired PWM frequency)−1

PRESCALE 设置为计算得到的值,就可以使 PCA9685 产生期望的 PWM 频率。

在代码中,有以下行用于设置 PRESCALE 寄存器的值:

wiringPiI2CWriteReg8(fd, PRESCALE, (prescaleval & 0xFF)); // Set the prescaler

其中 (prescaleval & 0xFF) 确保只有低8位的值被写入寄存器,因为 PRESCALE 寄存器只占一个字节。

修改运动模式为反复摆动:

while (1) {
    // Move servo on channel 0 from 0 to 180 degrees
    for (int degree = 0; degree <= 180; degree++) {
        moveServo(fd, 0, degree);
        usleep(10000); // Small delay between steps, adjust as needed
    }

    // Move servo on channel 0 from 180 to 0 degrees
    for (int degree = 180; degree >= 0; degree--) {
        moveServo(fd, 0, degree);
        usleep(10000); // Small delay between steps, adjust as needed
    }

    // Move servo on channel 1 from 0 to 180 degrees
    for (int degree = 0; degree <= 180; degree++) {
        moveServo(fd, 1, degree);
        usleep(10000); // Small delay between steps, adjust as needed
    }

    // Move servo on channel 1 from 180 to 0 degrees
    for (int degree = 180; degree >= 0; degree--) {
        moveServo(fd, 1, degree);
        usleep(10000); // Small delay between steps, adjust as needed
    }
}