PCA9685:I2C转16路PWM,助力你的系统

1 基本介绍

1.1 该IC主要参数特征如下:

  • I2C接口,支持高达16路PWM输出,每路12位分辨率(4096级)
  • 内置25MHz晶振,可不连接外部晶振,也可以连接外部晶振,最大50MHz
  • 支持2.3V-5.5V电压,最大耐压值5.5V,逻辑电平3.3V
  • 具有上电复位,以及软件复位等功能

    :本教程侧重PCA9685的PWM输出,但PCA9685亦可用于WS2812等LED颜色控制等。

    1.2 控制精度

    假设舵机为50HZ的控制频率,脉宽为0.5ms~2.5ms,12位分辨率(4096级),相关精度计算如下:

  • PWM周期:

  • 时间分辨率:

  • 最大脉宽时间:

  • 最大脉宽时间可分成的份数:

  • 0-180度的舵机,角度分辨率:

    2 硬件参数

    2.1 封装及引脚排列

    PCA9685有两种封装:TSSOP28, HVQFN28,其相应的引脚排列如下图所示:

    引脚功能描述如下图所示:

    2.2 器件地址

    PCA9685的器件地址是由引脚A0,A1,A2,A3,A4,A5共同决定,并且该引脚不可悬空,由于有6个引脚共同决定器件地址,因此,可以有64个器件地址,由于该IC上电便保留LED All Call address (E0h,1110 000)以及Software Reset address(06h,0000 0110),实际仅有62个可用器件地址,因此,理论上,1个I2C接口可控制16*62=992路PWM,其引脚控制器件地址的示意图如下图所示:

    默认情况下,若将A0-A5全部接地,则其器件地址为:0x40。

    2.3 寄存器及其地址

    默认情况下,上电复位后,寄存器地址的默认值均为0,寄存器地址及其用途见下图所示:


    图中节选的部分寄存器地址中,主要关心以下寄存器:

  • 模式设置寄存器:MODE1,MODE2。

  • 脉宽(占空比)设置寄存器:LED0_ON_L,LED0_ON_H,LED0_OFF_L,LED0_OFF_H……LED15……每一路PWM通道占用4个寄存器。
  • 周期(频率)设置寄存器:PRE_SCALE。
    接下来介绍以上寄存器的使用及其注意事项。

    2.4 模式设置寄存器

    2.4.1 MODE1寄存器

    首先介绍MODE1寄存器,如下图:

    在使用该寄存器的时候要注意:

  • 如果未停止所有PWM输出就将其进入到睡眠模式,那么,所有输出通道在下一轮都将输出高电平。

  • 睡眠后重新启动PWM的操作为:

  • 注意,在设置PWM频率(写PRESCALE寄存器)的时候,要先设置为Sleep模式,请参考后面源码部分。

    2.4.2 MODE2寄存器

    该寄存器的各位功能如下图所示:

    2.5 PWM通道寄存器及其占空比设置

    PWM通道寄存器如下图:

    由图可知,对于每一个通道,有4个寄存器,每个寄存器图解如下图所示:

    在设置PWM占空比的时候,首先,配置舵机的示例如下图所示(ON < OFF的情况):

    当特殊情况下,PWM周期大于定时器一次计数时,如下图所示(ON>OFF的情况):

2.6 PWM周期(频率)寄存器及其周期(频率)设置

接下来介绍配置PWM频率的寄存器:
一般情况下,在用内置晶振,为25MHZ,通过配置PRE_SCALE寄存器进行配置,配置的PRE_SCALE寄存器的值与PWM频率的关系如下图所示:

如果在舵机控制中,采用内置晶振,取osc_clock=25000000,update_rate=50(舵机控制频率50Hz)

2.7 推荐硬件设计

首先,OE引脚须接低电平以确保IC使能,如果连接LED灯,则推荐以下几种连接方式,如下图所示:

3 软件设计

3.1 Micro:bit平台TypeScript版

接下来进行软件设计部分讲解,由于本次开发采用Micro:bit底层开发,采用的是TypeScript(JavaScript的超类),所以暂提供该语言,提供基本操作方法及其思路,日后再更新C,C++及其它平台(STM32,Linux树莓派,Arduino等)操作方法,源码如下,可结合DataSheet及以上教程理解:

/*** 使用此文件来定义自定义函数和图形块。* 想了解更详细的信息,请前往 https://makecode.microbit.org/blocks/custom*//*** 自定义图形块*/
//% weight=5 color=#0fbc11 icon="\uf113"
namespace Servo {const PCA9685_ADDRESS = 0x40const MODE1 = 0x00const MODE2 = 0x01const SUBADR1 = 0x02const SUBADR2 = 0x03const SUBADR3 = 0x04const PRESCALE = 0xFEconst LED0_ON_L = 0x06const LED0_ON_H = 0x07const LED0_OFF_L = 0x08const LED0_OFF_H = 0x09const ALL_LED_ON_L = 0xFAconst ALL_LED_ON_H = 0xFBconst ALL_LED_OFF_L = 0xFCconst ALL_LED_OFF_H = 0xFDconst STP_CHA_L = 2047const STP_CHA_H = 4095const STP_CHB_L = 1const STP_CHB_H = 2047const STP_CHC_L = 1023const STP_CHC_H = 3071const STP_CHD_L = 3071const STP_CHD_H = 1023let initialized = falsefunction i2cwrite(addr: number, reg: number, value: number) {let buf = pins.createBuffer(2)buf[0] = regbuf[1] = valuepins.i2cWriteBuffer(addr, buf)}function i2cread(addr: number, reg: number) {pins.i2cWriteNumber(addr, reg, NumberFormat.UInt8BE);let val = pins.i2cReadNumber(addr, NumberFormat.UInt8BE);return val;}function initPCA9685(): void {i2cwrite(PCA9685_ADDRESS, MODE1, 0x00)setFreq(50);setPwm(0, 0, 4095);for (let idx = 1; idx < 16; idx++) {setPwm(idx, 0, 0);}initialized = true}function setFreq(freq: number): void {// Constrain the frequencylet prescaleval = 25000000;prescaleval /= 4096;prescaleval /= freq;prescaleval -= 1;let prescale = prescaleval; //Math.Floor(prescaleval + 0.5);let oldmode = i2cread(PCA9685_ADDRESS, MODE1);let newmode = (oldmode & 0x7F) | 0x10; // sleepi2cwrite(PCA9685_ADDRESS, MODE1, newmode); // go to sleepi2cwrite(PCA9685_ADDRESS, PRESCALE, prescale); // set the prescaleri2cwrite(PCA9685_ADDRESS, MODE1, oldmode);control.waitMicros(5000);i2cwrite(PCA9685_ADDRESS, MODE1, oldmode | 0xa1);}function setPwm(channel: number, on: number, off: number): void {if (channel < 0 || channel > 15)return;let buf = pins.createBuffer(5);buf[0] = LED0_ON_L + 4 * channel;buf[1] = on & 0xff;buf[2] = (on >> 8) & 0xff;buf[3] = off & 0xff;buf[4] = (off >> 8) & 0xff;pins.i2cWriteBuffer(PCA9685_ADDRESS, buf);}/*** Servo Execute* @param degree [0-180] degree of servo; eg: 90, 0, 180*///% blockId=setServo block="Servo channel|%channel|degree %degree"//% weight=85//% degree.min=0 degree.max=180export function Servo(channel: number,degree: number): void {if (!initialized) {initPCA9685();}// 50hz: 20,000 uslet v_us = (degree * 1800 / 180 + 600); // 0.6 ~ 2.4let value = v_us * 4096 / 20000;setPwm(channel, 0, value);}/*** Servo Execute* @param pulse [500-2500] pulse of servo; eg: 1500, 500, 2500*///% blockId=setServoPulse block="Servo channel|%channel|pulse %pulse"//% weight=85//% pulse.min=500 pulse.max=2500export function ServoPulse(channel: number,pulse: number): void {if (!initialized) {initPCA9685();}// 50hz: 20,000 uslet value = pulse * 4096 / 20000;setPwm(channel, 0, value);}
}

以上便是Micro:bit驱动PCA9685的源代码,注意源代码中的时间为us,而教程中的时间为ms。

3.2 树莓派平台Python版

要运行该程序,首先选装python,安装好Python后,还需要安装树莓派平台的smbus库:

sudo apt-get install python-smbus

树莓派平台采用Python驱动PCA9685的Python代码如下所示:

#!/usr/bin/pythonimport time
import math
import smbus# ============================================================================
# Raspi PCA9685 16-Channel PWM Servo Driver
# ============================================================================class PCA9685:# Registers/etc.__SUBADR1            = 0x02__SUBADR2            = 0x03__SUBADR3            = 0x04__MODE1              = 0x00__PRESCALE           = 0xFE__LED0_ON_L          = 0x06__LED0_ON_H          = 0x07__LED0_OFF_L         = 0x08__LED0_OFF_H         = 0x09__ALLLED_ON_L        = 0xFA__ALLLED_ON_H        = 0xFB__ALLLED_OFF_L       = 0xFC__ALLLED_OFF_H       = 0xFDdef __init__(self, address=0x40, debug=False):self.bus = smbus.SMBus(1)self.address = addressself.debug = debugif (self.debug):print("Reseting PCA9685")self.write(self.__MODE1, 0x00)def write(self, reg, value):"Writes an 8-bit value to the specified register/address"self.bus.write_byte_data(self.address, reg, value)if (self.debug):print("I2C: Write 0x%02X to register 0x%02X" % (value, reg))def read(self, reg):"Read an unsigned byte from the I2C device"result = self.bus.read_byte_data(self.address, reg)if (self.debug):print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % (self.address, result & 0xFF, reg))return resultdef setPWMFreq(self, freq):"Sets the PWM frequency"prescaleval = 25000000.0    # 25MHzprescaleval /= 4096.0       # 12-bitprescaleval /= float(freq)prescaleval -= 1.0if (self.debug):print("Setting PWM frequency to %d Hz" % freq)print("Estimated pre-scale: %d" % prescaleval)prescale = math.floor(prescaleval + 0.5)if (self.debug):print("Final pre-scale: %d" % prescale)oldmode = self.read(self.__MODE1);newmode = (oldmode & 0x7F) | 0x10        # sleepself.write(self.__MODE1, newmode)        # go to sleepself.write(self.__PRESCALE, int(math.floor(prescale)))self.write(self.__MODE1, oldmode)time.sleep(0.005)self.write(self.__MODE1, oldmode | 0x80)def setPWM(self, channel, on, off):"Sets a single PWM channel"self.write(self.__LED0_ON_L+4*channel, on & 0xFF)self.write(self.__LED0_ON_H+4*channel, on >> 8)self.write(self.__LED0_OFF_L+4*channel, off & 0xFF)self.write(self.__LED0_OFF_H+4*channel, off >> 8)if (self.debug):print("channel: %d  LED_ON: %d LED_OFF: %d" % (channel,on,off))def setServoPulse(self, channel, pulse):"Sets the Servo Pulse,The PWM frequency must be 50HZ"pulse = pulse*4096/20000        #PWM frequency is 50HZ,the period is 20000usself.setPWM(channel, 0, pulse)if __name__=='__main__':pwm = PCA9685(0x40, debug=True)pwm.setPWMFreq(50)while True:# setServoPulse(2,2500)for i in range(500,2500,10):  pwm.setServoPulse(0,i)   time.sleep(0.02)     for i in range(2500,500,-10):pwm.setServoPulse(0,i) time.sleep(0.02)

保存文件命名为: pca9685.py,命令行进入该文件所在的路径,运行该Python脚本:

sudo python pca9685.py

执行该命令后,便可控制舵机从0度转到180度,再从180度转到0度。

PCA9685:I2C转16路PWM,助力你的系统相关推荐

  1. 树莓派控制16路PWM输出的PCA9685模块

    树莓派控制16路PWM输出的PCA9685模块 1.PCA9685 1.1简介 采用I2C通信,内置了PWM驱动器和一个时钟,不需要不断发送信号占用单片机资源 支持16路PWM输出,每路12位分辨率( ...

  2. 树莓派4B通过16路PWM输出的PCA9685模块控制舵机(机械臂)附完整代码

    树莓派4B通过16路PWM输出的PCA9685模块控制舵机(机械臂)附完整代码 文章目录 树莓派4B通过16路PWM输出的PCA9685模块控制舵机(机械臂)附完整代码 一. Servo Driver ...

  3. 【雕爷学编程】Arduino动手做(161)---16路PWM舵机驱动板PCA9685

    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的.鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为 ...

  4. STM32F103单片机生成16路PWM波

    单片机输出pwm波在项目中是恨常用的一个功能,今天就总结一下用STM32F103C8T6单片机输出16路 pwm波. 使用keil5软件,用标准库函数来实现. 首先看定时器初始化 void TIM1_ ...

  5. STM32F103 16路PWM 配置

    1. 前言 底层库:基于原子的标准库函数 适用芯片:STM32F103系列型号芯片,其他型号未测试(注:已测型号:C8T6.CBT6.RCT6.RET6.ZET6) 所用到的定时器:TIM1.TIM2 ...

  6. Arduino uno使用PCA9685模块实现16路舵机控制

    PCA9685模块 PCA9685是16路12位PWM信号发生器,可用于控制舵机.led.电机等设备,采用I2C通信.主机只需要I2C接口即可实现16路舵机控制. PCA9685的I2C地址默认0x4 ...

  7. PCA9685 16路12位pwm信号发生器

    PCA9685 16路12位pwm信号发生器 一.概述和硬件 1.概述 2.硬件 1.电压 2.i2c地址 3.使能脚 二.寄存器功能 MODE1寄存器 各个通道的ON和OFF寄存器 PRE_SCAL ...

  8. 立创梁山派GD32F450ZGT6--通过PCA9685控制16路舵机

    PCA9685芯片,每一路LED输出端均可自由调节PWM波的频率 (40~1000Hz) 和占空比 (0%~100%) .这款芯片主要通过输出不同占空比的PWM脉冲信号来控制舵机转动的角度.是16通道 ...

  9. 树莓派python控制舵机_使用树莓派控制16路舵机驱动板(pca9685)

    使用树莓派控制16路舵机驱动板(pca9685) 在树莓派上,可以通过RPI.GPIO方便地输出PWM进行舵机控制. 使用RPI.GPIO 创建一个 PWM 实例: 1 p =GPIO.PWM(cha ...

最新文章

  1. Ubuntu18.04的网络配置(静态IP和动态IP) - OpsDrip - 博客园
  2. 张宁北大计算机系,同是北大出身,差距悬殊!张宁在山西坐冷板凳,祝铭震已坐稳首发...
  3. 单选框radio绑定click事件
  4. linux安装tensorflow教程,Ubuntu 16.04 安装 TensorFlow(GPU支持)
  5. Angular消息通知组件ngx-notification
  6. 震后十年,他重走那条生死送货线
  7. c语言如何调用外部文件的函数调用,keil 中如何调用其他文件的函数
  8. iOS病毒门还没完:美国210家企业仍在用感染App
  9. 字符多维php递归遍历目录
  10. aspose.pdf-17.8暴力破解,附破解过程和使用代码。仅供参考,不可用于商业用途
  11. KumiaoQQ机器人框架源码
  12. 趣谈网络协议-应用层(P2P协议)
  13. IP地址介绍,子网划分(VLSM),构造超网(CIDR) 详解
  14. DialogFragment的OnDismissListener
  15. ubuntu Rhythmbox 乱码解决
  16. 可编程数据平面(论文阅读)
  17. 6个炒菜习惯 丢失营养
  18. MOOC编程题#2: 魔兽世界之二:装备
  19. KVM虚拟化技术的-虚拟机配置文件
  20. 电信融合机ip906h-fv2,线刷包(当贝桌面)

热门文章

  1. 指定IDEA的字符编码
  2. Winsock 的初始化
  3. CentOS8(8.2)安装mysql8
  4. 【youcans 的 OpenCV 例程 200 篇】119. 图像的形态学梯度
  5. 这是一篇理工男写的口红科普文
  6. 【路径规划】基于matlab蚁群算法机器人大规模栅格地图最短路径规划【含Matlab源码 1860期】
  7. 清除IE浏览器SSL缓存
  8. eChars实现哈尔滨市区县地图展示
  9. String类型转json格式
  10. WordPress搭建“外贸独立站”完整操作教程