开发示例
使用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%占空比