跳转到内容

开发示例

使用arduino作为代码示例

提示

注:示例代码中没有做任何的错误处理 (∪.∪ )...zzz

一些前置代码

C++
#include <Wire.h>

// 地址
uint8_t address = 0x7F;

void setup() {
    // 初始化I²C
    Wire.begin();
    
    // 复位芯片并进入正常工作模式
    write(0x00, 0x00);
    
    // 设置PWM频率为50Hz
    write(0xFE, 0x79);  // 预分频值计算得出约为0x79
}

// 写一个寄存器
bool write(uint8_t command, uint8_t value) {
    Wire.beginTransmission(address);
    Wire.write(command);
    Wire.write(value);
    return Wire.endTransmission() == 0;
}

// 批量写入寄存器
bool writeBytes(uint8_t command, uint8_t* data, size_t length) {
    Wire.beginTransmission(address);
    Wire.write(command);
    Wire.write(data, length);
    return Wire.endTransmission() == 0;
}

// 读取一个寄存器的值
uint8_t read(uint8_t command) {
    Wire.beginTransmission(address);
    Wire.write(command);
    Wire.endTransmission();
    Wire.requestFrom(address, 1);
    return Wire.read();
}

设置PWM频率

PCA9685通过 预分频寄存器(PRE_SCALE) 来设置PWM输出频率
频率计算公式:

C++
prescale_value = round(oscillator_clock / (4096 * desired_frequency)) - 1
// 其中 oscillator_clock 为内部时钟频率(默认25MHz)
C++
// 设置PWM频率的函数
void setPWMFrequency(float frequency) {
    // 限制频率范围在24Hz至1526Hz之间
    if (frequency < 24) frequency = 24;
    if (frequency > 1526) frequency = 1526;
    
    // 计算预分频值
    // 使用25MHz典型时钟频率
    int prescale_val = round(25000000.0f / (4096 * frequency)) - 1;
    
    // 在更改频率前需要进入睡眠模式
    uint8_t old_mode = read(0x00);              // 读取旧模式
    uint8_t new_mode = (old_mode & 0x7F) | 0x10; // 设置sleep位
    write(0x00, new_mode);                      // 进入睡眠模式
    
    // 写入新的预分频值
    write(0xFE, prescale_val);
    
    // 恢复原来的工作模式
    write(0x00, old_mode);
    
    // 等待一段时间让电路稳定
    delay(5);
    
    // 重新启动
    write(0x00, old_mode | 0x80);
}

// 常用频率设置示例
// 设置为50Hz (适用于舵机控制)
setPWMFrequency(50);

// 设置为1000Hz (适用于LED调光)
setPWMFrequency(1000);

// 获取当前PWM频率 (反向计算)
float getPWMFrequency() {
    uint8_t prescale_val = read(0xFE);
    return 25000000.0f / (4096 * (prescale_val + 1));
}

// 使用示例: 读取当前频率
float currentFreq = getPWMFrequency();  // 应该接近50Hz (如果之前设置了50Hz)

控制单个PWM通道

PWM输出由 寄存器0x06~0x45 控制

C++
// 一个通道的PWM控制使用四个寄存器 (以通道0为例)
// 0x06: LED0 开启低字节
// 0x07: LED0 开启高字节
// 0x08: LED0 关闭低字节
// 0x09: LED0 关闭高字节

// 设置PWM周期为完整的4096步长(12位精度)
// 通过调节ON和OFF的时间点来控制占空比
// 当ON=0, OFF=4096时,输出恒为高电平
// 当ON=4096, OFF=0时,输出恒为低电平


// 函数方式实现PWM控制
void setPWM(uint8_t channel, uint16_t on, uint16_t off) {
    // channel  通道
    // on  开启时间 (0-4095)
    // off  关闭时间 (0-4095)
    write(channel * 4 + 0x06, on & 0xFF);
    write(channel * 4 + 0x07, (on >> 8) & 0x0F);
    write(channel * 4 + 0x08, off & 0xFF);
    write(channel * 4 + 0x09, (off >> 8) & 0x0F);
}


// 使用示例: 设置通道0为25%占空比
// 25%占空比意味着在4096个步长中,前25%为高电平,后75%为低电平
setPWM(0, 0, 1024);  // 1024/4096 ≈ 25%

// 设置通道1为75%占空比
setPWM(1, 0, 3072);  // 3072/4096 ≈ 75%

// 特殊值的使用
// 全0值表示完全关闭
setPWM(2, 4096, 0);  // 完全关闭通道2

// 特定相位控制示例
// 从1000步开始,持续2000步的高电平脉冲
setPWM(3, 1000, 3000);  // 在1000-3000步期间为高电平

使用自动递增模式

当然你肯定发现了控制PWM的寄存器是连续的
PCA9685支持自动递增地址功能
开启了自动递增模式(Auto-Increment)后,可以在一次传输中写入多个连续的寄存器

C++
// 首先启用自动递增功能 (设置MODE1寄存器的AI位)
void enableAutoIncrement() {
    uint8_t mode1 = read(0x00);   // 读取当前MODE1寄存器值
    mode1 |= 0x20;                // 设置AI位 (bit 5)
    write(0x00, mode1);           // 写回MODE1寄存器
}

// 禁用自动递增功能
void disableAutoIncrement() {
    uint8_t mode1 = read(0x00);   // 读取当前MODE1寄存器值
    mode1 &= ~0x20;               // 清除AI位 (bit 5)
    write(0x00, mode1);           // 写回MODE1寄存器
}
C++
// 使用自动递增功能设置一个通道的PWM值
void setPWMAutoIncrement(uint8_t channel, uint16_t on, uint16_t off) {
    // 与setPWM函数功能一致
    uint8_t data[4];
    data[0] = on & 0xFF;           // LEDn_ON_L
    data[1] = (on >> 8) & 0x0F;    // LEDn_ON_H
    data[2] = off & 0xFF;          // LEDn_OFF_L
    data[3] = (off >> 8) & 0x0F;   // LEDn_OFF_H
    
    writeBytes(channel * 4 + 0x06, data, 4);
}

// 使用自动递增功能批量设置所有通道的PWM值
void setAllPWM(uint16_t on, uint16_t off) {
    // 起始寄存器地址
    uint8_t startReg = 0x06;
    
    // 准备数据缓冲区 (每个通道需要4个字节)
    uint8_t data[16 * 4];  // 16个通道
    for (int i = 0; i < 16; i++) {
        data[i*4 + 0] = on & 0xFF;
        data[i*4 + 1] = (on >> 8) & 0x0F;
        data[i*4 + 2] = off & 0xFF;
        data[i*4 + 3] = (off >> 8) & 0x0F;
    }
    
    // 一次性写入所有数据
    writeBytes(startReg, data, 16 * 4);
}
setPWMAutoIncrement(1, 0, 2048); // 设置通道1为50%占空比
setAllPWM(0, 2048);  // 设置所有通道为50%占空比

控制板子的电机

我们使用的L9110S的驱动芯片,一个驱动需要两路通道进行控制

c++
// 设置电机速度 (0-4095对应0-100%占空比) 
void setMotorSpeed(uint8_t channel, int speed) { 
    // 限制速度范围 
    if (speed > 4095) speed = 4095;
    // 等待完善...
}