PCA9685 16路PWM 控制板 STM32F103 驱动
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);
// 配置 I2C2
I2C_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);
// 使能 I2C2
I2C_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 位,最大值为 4095
const uint16_t pwmMax = 4095;
uint16_t offValue = (uint16_t)(dutyCycle * pwmMax);
// 调用 PCA9685_setPWM 函数设置 PWM
PCA9685_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