PCA9685 拥有16路PWM,通过 IIC 与 STM32 进行通信,以下驱动代码已通过测试,你可以进行更多代码优化
#include "pca9685.h"// 向 PCA9685 写入一个字节数据
static void PCA9685_write8( uint8_t addr, uint8_t d) {while (I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY)); // 等待总线空闲I2C_GenerateSTART(I2C2, ENABLE); // 产生起始信号while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); // 等待起始信号发送完成I2C_Send7bitAddress(I2C2, PCA9685_ADRESS << 1, I2C_Direction_Transmitter); // 发送从机地址while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // 等待地址发送完成I2C_SendData(I2C2, addr); // 发送寄存器地址while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 等待寄存器地址发送完成I2C_SendData(I2C2, d); // 发送数据while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 等待数据发送完成I2C_GenerateSTOP(I2C2, ENABLE); // 产生停止信号delay_ms(1); // 延时一段时间
}// 从 PCA9685 读取一个字节数据
static uint8_t PCA9685_read8( uint8_t addr) {uint8_t data;while (I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY)); // 等待总线空闲I2C_GenerateSTART(I2C2, ENABLE); // 产生起始信号while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); // 等待起始信号发送完成I2C_Send7bitAddress(I2C2, PCA9685_ADRESS << 1, I2C_Direction_Transmitter); // 发送从机地址while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // 等待地址发送完成I2C_SendData(I2C2, addr); // 发送寄存器地址while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 等待寄存器地址发送完成I2C_GenerateSTART(I2C2, ENABLE); // 再次产生起始信号while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); // 等待起始信号发送完成I2C_Send7bitAddress(I2C2, PCA9685_ADRESS << 1 | 0x01, I2C_Direction_Receiver); // 发送从机地址(读模式)while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); // 等待地址发送完成I2C_AcknowledgeConfig(I2C2, DISABLE); // 关闭应答I2C_GenerateSTOP(I2C2, ENABLE); // 产生停止信号while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED)); // 等待数据接收完成data = I2C_ReceiveData(I2C2); // 读取数据return data;
}// 初始化 PCA9685 结构体
void PCA9685_Init() {GPIO_InitTypeDef GPIO_InitStructure;I2C_InitTypeDef I2C_InitStructure;// 使能 I2C2 和 GPIOB 时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);// 配置 I2C2 引脚(PB10 - SCL, PB11 - SDA)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);// 配置 I2C2I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStructure.I2C_OwnAddress1 = 0x00;I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_InitStructure.I2C_ClockSpeed = 100000;I2C_Init(I2C2, &I2C_InitStructure);// 使能 I2C2I2C_Cmd(I2C2, ENABLE);delay_ms(5);PCA9685_begin();
}// 开始使用 PCA9685
void PCA9685_begin() {PCA9685_reset();
}// 复位 PCA9685
void PCA9685_reset() {PCA9685_write8(PCA9685_MODE1, 0x0);
}// 设置 PWM 频率
void PCA9685_setPWMFreq( float freq) {freq *= 0.9; // 校正频率设置中的过冲float prescaleval = 25000000;prescaleval /= 4096;prescaleval /= freq;prescaleval -= 1;uint8_t prescale = (uint8_t)floor(prescaleval + 0.5);uint8_t oldmode = PCA9685_read8( PCA9685_MODE1);uint8_t newmode = (oldmode & 0x7F) | 0x10; // 进入睡眠模式PCA9685_write8( PCA9685_MODE1, newmode); // 进入睡眠模式PCA9685_write8( PCA9685_PRESCALE, prescale); // 设置预分频器PCA9685_write8( PCA9685_MODE1, oldmode);delay_ms(5);PCA9685_write8( PCA9685_MODE1, oldmode | 0xa1); // 开启自动递增模式
}// 设置单个通道的 PWM 值 4095
void PCA9685_setPWM( uint8_t num, uint16_t on, uint16_t off) {PCA9685_write8( LED0_ON_L + 4 * num, on & 0xFF);PCA9685_write8( LED0_ON_H + 4 * num, on >> 8);PCA9685_write8( LED0_OFF_L + 4 * num, off & 0xFF);PCA9685_write8( LED0_OFF_H + 4 * num, off >> 8);
}// 工具函数,根据占空比设置 PWM
// channel: 0- 15 dutyCycle:0-1.0
void setPCA9685PWMWithDutyCycle(uint8_t channel, float dutyCycle) {// 检查占空比是否在有效范围内if (dutyCycle < 0.0) {dutyCycle = 0.0;} else if (dutyCycle > 1.0) {dutyCycle = 1.0;}// PCA9685 的 PWM 分辨率是 12 位,最大值为 4095const uint16_t pwmMax = 4095;uint16_t offValue = (uint16_t)(dutyCycle * pwmMax);// 调用 PCA9685_setPWM 函数设置 PWMPCA9685_setPWM(channel, 0, offValue);
}// 设置引脚的 PWM 值
void PCA9685_setPin( uint8_t num, uint16_t val, uint8_t invert) {// 限制值在 0 到 4095 之间if (val > 4095) val = 4095;if (invert) {if (val == 0) {// 信号全高的特殊值PCA9685_setPWM(num, 4096, 0);} else if (val == 4095) {// 信号全低的特殊值PCA9685_setPWM( num, 0, 4096);} else {PCA9685_setPWM( num, 0, 4095 - val);}} else {if (val == 4095) {// 信号全高的特殊值PCA9685_setPWM( num, 4096, 0);} else if (val == 0) {// 信号全低的特殊值PCA9685_setPWM(num, 0, 4096);} else {PCA9685_setPWM(num, 0, val);}}
}
#ifndef PCA9685_H
#define PCA9685_H
#include "stm32f10x.h"
#include <math.h>
#include "delay.h"// PCA9685 寄存器地址
#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4#define PCA9685_ADRESS 0x40#define PCA9685_MODE1 0x0
#define PCA9685_PRESCALE 0xFE#define LED0_ON_L 0x6
#define LED0_ON_H 0x7
#define LED0_OFF_L 0x8
#define LED0_OFF_H 0x9#define ALLLED_ON_L 0xFA
#define ALLLED_ON_H 0xFB
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD// 函数声明
void PCA9685_Init(void);
void PCA9685_begin(void);
void PCA9685_reset(void);
void PCA9685_setPWMFreq(float freq);
void PCA9685_setPWM( uint8_t num, uint16_t on, uint16_t off);
void PCA9685_setPin( uint8_t num, uint16_t val, uint8_t invert);
void setPCA9685PWMWithDutyCycle(uint8_t channel, float dutyCycle);
#endif