当前位置: 首页 > article >正文

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

http://www.kler.cn/a/538253.html

相关文章:

  • 迁移学习 Transfer Learning
  • 利用 Python 爬虫获取按关键字搜索淘宝商品的完整指南
  • 天津三石峰科技——汽车生产厂的设备振动检测项目案例
  • AI安全最佳实践:AI应用开发安全评估矩阵(上)
  • MFC 应用最小化到系统托盘
  • 基于STM32的智能鱼缸水质净化系统设计
  • Python—批量获取文件夹内文件名及重命名文件教程(附赠GUI版本)—2025-2-7
  • DeepSeek如何在有限的计算资源下开发高级AI模型
  • firebase简介
  • vue实现自定义校验值是否有效
  • NginxWeb负载均衡集群搭建
  • java练习(11)
  • JVM虚拟机以及跨平台原理
  • SwiftUI 学习 Toggle 遇到的问题
  • MarkupLM:用于视觉丰富文档理解的文本和标记语言预训练
  • 2024最新版Java面试题及答案,【来自于各大厂】
  • 【11天从零基础入门flask】第 6 章:模板优化
  • 个人职业发展——效率为王:AI赋能前端开发
  • C语言简单练习题
  • 我的年度写作计划
  • 机器学习中常用的数据预处理方法
  • 深入解析AI技术原理
  • docker环境下部署face-search开源人脸识别模型
  • 我使用deepseek高效学习-分析外文网站Cron定时执行任务
  • 【GeeRPC】Day3:服务注册(Service Register)
  • 开源机器人+具身智能 解决方案+AI