嵌入式人工智能(45-基于树莓派4B的扩展板-舵机驱动板PCA9685)

1、简介

智能小车、机械臂、摄像头云台会有多个舵机,而微控制器芯片的PWM输出引脚不够的情况下,就可以用PCA9685(16路舵机)来解决这一问题。

PCA9685是一款I2C总线控制的16通道LED控制器,专为红/绿/蓝/琥珀(RGBA)彩色背光应用而优化。每个LED输出都有自己的12位分辨率(4096步)固定频率单独PWM控制器,可在可编程频率下工作,典型频率为24 Hz到1526 Hz,占空比可调节从0%到100%,以使LED达到特定的亮度值。所有输出的PWM频率都设置为相同。

每个LED输出可以关闭或打开(无PWM控制),或设置为其单独的PWM控制器值。LED输出驱动器被编程为开漏,带有在5V时25mA的电流下沉能力,或者是totem极,带有在5V时25mA的下沉和10mA的源能力。PCA9685的工作电压范围为2.3V至5.5V,输入和输出均可容忍5.5V。LED可以直接连接到LED输出(高达25mA,5.5V),或通过外部驱动器和最少量的离散元件控制更大电流或更高电压的LED。

2、驱动板特性

• PCA9685允许交替的LED输出开启和关闭时间以最小化电流浪涌。每个16个通道的开启和关闭时间延迟可以独立编程。这个特性在PCA9635中不可用。

• PCA9685具有4096个步骤(12位PWM)的单独LED亮度控制。

• 当在系统中合并多个LED控制器时,如果使用PCA9635,多个设备之间的PWM脉冲宽度可能会有所不同。PCA9685具有可编程分频器,以调整多个设备的PWM脉冲宽度。

• PCA9685具有外部时钟输入引脚,可接受用户提供的时钟(最大50 MHz)代替内部25 MHz振荡器。此功能允许多个设备同步。PCA9635没有外部时钟输入功能。

• 与PCA9635一样,PCA9685也具有用于PWM控制的内置振荡器。但是,与PCA9635的典型频率97.6kHz相比,PCA9685用于PWM控制的频率可调节约为24 Hz至1526 Hz。这允许使用PCA9685与外部电源控制器。所有位都以相同的频率设置。

• LEDn输出引脚的上电复位(POR)默认状态在PCA9685的情况下为LOW。而对于PCA9635,则为HIGH。

• 所有PWM输出线上都放一个220欧姆系列电阻器来保护他们,并能轻易的驱动LED。

3、多驱动板级联

地址选择引脚使你可以把62个驱动板挂在单个12C总线上,总共有992路PWM输出,级联的每个驱动板都需要有一个唯一的访问地址。每个驱动板的初始I2C地址是0×40,可以通过右上角的跳线修改I2C地址。用焊锡将一个跳线连上就表示一个二进制数字"1"。有6个地址控制脚,通过这些引脚可以控制设备的i2c地址。7位的I2C地址为:0x40 + A5:A0

Board 0: Address = 0x40 Offset = binary 00000 (默认)

Board 1: Address = 0x41 Offset = binary 00001 (如上图,接上A0)

Board 2: Address = 0x42 Offset = binary 00010 (接上A1)

Board 3: Address = 0x43 Offset = binary 00011 (接上A0和A1)

Board 4: Address = 0x44 Offset = binary 00100 (接上A2)

4、外部电源接入

大多数的舵机设计电压都是在5~6V,尤其在多个舵机同时运行时,跟需要有大功率的电源供电。如果直接使用5V引脚直接为舵机供电,会出现一些难以预测的问题,所以建议你能有个合适的外部电源为驱动板供电。中间上面的绿色接线柱,是提供外围输入电压,舵机后面提示外围输入供电电压不要超过6V。V+线上放置一个大电容(在某些场合你会需要) 外围输入最大电压取决于这个10V1000uf的电容。

PCA9685-16路舵机是一个采用IIC通信,内置了PWM驱动器和一个时钟,这个意味着,这将和TLCG940系列有很大不同,你不需要不断发送信号占用你的单片机。它是5V的兼容,这意味你还可以用3.3V单片机控制并且安全地驱动到6V输出(当你想要控制白色或蓝色指示灯用3.4+正电压也是可以的)。约1.6Khz可调频PWM输出,为步进电机准备输出12位分辨率,可配置的推拉输出或开路输出,输出使能引脚能够快速禁用所有输出。

这个地方说明书上的意思我重新给同学们解释下。如果不仔细思考理解,可能很多同学有点晕。

VCC和V+这两个地方是有区别的。VCC是给PCA9685芯片供电的电压3-5V。而V+是给舵机供电的电压。那么问题来了,1是扩展板好几个地方都有V+,哪里是输入哪里是输出?2是V+的那个一会最大6V,一会6-10V是什么意思呢?

扩展板要给舵机供电,首先要有电压输入,如果仅接VCC,无法提供输出电压。要提供输出电压必须接上左图的绿色接线柱,这个输入电压范围为6-10V,然后V+的所有输出电压就有了。这个输出的电压都是来自绿色接线柱的输入电压,输入多少输出就是多少,但是为了保护舵机,扩展板提供稳压保护,即便输入的电压有9V,它输出的电压最大不超过6.5V,因为大多数舵机的工作电压5-6V。所以如果要带的舵机额定电压需要6.5V以上,就单独供电,把PWM信号线接扩展板即可。

这里我再扩展下,介绍输出电压、电流与负载的关系。这里我可以拿手机电池和手机充电器给大家举例。如果手机电池的的输入电压为5V,那么充电器的输出电压一定要5V才行不能大也不能小。如果输入电压大了不烧坏手机说明内部有保护的稳压电路,小了更不行,芯片不能工作。而充电器提供的输出电流则越大越好,当然价格也高,这个完全看负载所需的电流,只要电池所需要的输入电流小于等于充电器提供的输出电流都可以。但是要是充电器输出电流小于电池需要的输入电流则不行,它有一个阈值,低于阈值充不进去电,高于阈值则充电比较慢,也能充。舵机也是一样,所以最好是满足负载所需的电压和电流才行。

我们也做了很多实验了,树莓派或其他芯片提供的输出电压往往可以满足负载,但输出电流较弱,驱动大电流的设备往往需要单独供电。

5、电路原理图

6、PCA9685的驱动

该模块仍然采用IIC总线,可以查看其地址为0x40。

python 复制代码
from __future__ import division
import logging
import time
import math

# Registers/etc:
PCA9685_ADDRESS    = 0x40
MODE1              = 0x00
MODE2              = 0x01
SUBADR1            = 0x02
SUBADR2            = 0x03
SUBADR3            = 0x04
PRESCALE           = 0xFE
LED0_ON_L          = 0x06
LED0_ON_H          = 0x07
LED0_OFF_L         = 0x08
LED0_OFF_H         = 0x09
ALL_LED_ON_L       = 0xFA
ALL_LED_ON_H       = 0xFB
ALL_LED_OFF_L      = 0xFC
ALL_LED_OFF_H      = 0xFD

# Bits:
RESTART            = 0x80
SLEEP              = 0x10
ALLCALL            = 0x01
INVRT              = 0x10
OUTDRV             = 0x04


logger = logging.getLogger(__name__)



class PCA9685(object):
    """PCA9685 PWM LED/servo controller."""

    def __init__(self, address=PCA9685_ADDRESS, i2c=None, **kwargs):
        """Initialize the PCA9685."""
        # Setup I2C interface for the device.
        if i2c is None:
            import Adafruit_GPIO.I2C as I2C
            i2c = I2C
        self._device = i2c.get_i2c_device(address, **kwargs)
        self.set_all_pwm(0, 0)
        self._device.write8(MODE2, OUTDRV)
        self._device.write8(MODE1, ALLCALL)
        time.sleep(0.005)  # wait for oscillator
        mode1 = self._device.readU8(MODE1)
        mode1 = mode1 & ~SLEEP  # wake up (reset sleep)
        self._device.write8(MODE1, mode1)
        time.sleep(0.005)  # wait for oscillator

    def set_pwm_freq(self, freq_hz):
        """Set the PWM frequency to the provided value in hertz."""
        prescaleval = 25000000.0    # 25MHz
        prescaleval /= 4096.0       # 12-bit
        prescaleval /= float(freq_hz)
        prescaleval -= 1.0
        logger.debug('Setting PWM frequency to {0} Hz'.format(freq_hz))
        logger.debug('Estimated pre-scale: {0}'.format(prescaleval))
        prescale = int(math.floor(prescaleval + 0.5))
        logger.debug('Final pre-scale: {0}'.format(prescale))
        oldmode = self._device.readU8(MODE1);
        newmode = (oldmode & 0x7F) | 0x10    # sleep
        self._device.write8(MODE1, newmode)  # go to sleep
        self._device.write8(PRESCALE, prescale)
        self._device.write8(MODE1, oldmode)
        time.sleep(0.005)
        self._device.write8(MODE1, oldmode | 0x80)

    def set_pwm(self, channel, on, off):
        """Sets a single PWM channel."""
        self._device.write8(LED0_ON_L+4*channel, on & 0xFF)
        self._device.write8(LED0_ON_H+4*channel, on >> 8)
        self._device.write8(LED0_OFF_L+4*channel, off & 0xFF)
        self._device.write8(LED0_OFF_H+4*channel, off >> 8)

    def set_all_pwm(self, on, off):
        """Sets all PWM channels."""
        self._device.write8(ALL_LED_ON_L, on & 0xFF)
        self._device.write8(ALL_LED_ON_H, on >> 8)
        self._device.write8(ALL_LED_OFF_L, off & 0xFF)
        self._device.write8(ALL_LED_OFF_H, off >> 8)


        

一共这个舵机控制类就4个函数,init为初始化函数,set_pwm_freq设置频率,set_pwm控制单个舵机,set_all_pwm控制全部舵机。

7、实验代码与现象

(1)我手头没有多个舵机,先用多个LED流水灯做实验,多个舵机的实验代码我也放到最后,回头拿到机械臂之后,我再想办法连接到树莓派上面。因为这个PCA9685的输出最大电压为6.5V。机械臂上有的舵机电压可能要大于6.5V。(我回头要实验看下,这个9685能否给机械臂提供供电)

(2)我们这边要控制多个LED的灯光,可以多个灯不同时亮暗,采用多线程实现。

python 复制代码
import pca9685
import time
import threading

Led_pwm = pca9685.PCA9685(busnum=1)
Led_pwm.set_pwm_freq(600)  # 设置频率为60HZ


def LED0_ctrl():
    for i in range(0,5000,10):
        Led_pwm.set_pwm(0,0,i)
        time.sleep(0.01)

def LED1_ctrl():
    for i in range(0,3000,10):
        Led_pwm.set_pwm(1,0,i)
        time.sleep(0.01)

def LED2_ctrl():
    for i in range(4000,0,-20):
        Led_pwm.set_pwm(2,0,i)
        time.sleep(0.03)
try:
    while True:
        t0=threading.Thread(target=LED0_ctrl)  # 多线程
        t1=threading.Thread(target=LED1_ctrl)
        t2=threading.Thread(target=LED2_ctrl)
        t0.start()   # 开启线程
        t1.start() 
        t2.start() 
        t0.join()
        t1.join()
        t2.join()
except KeyboardInterrupt:
    exit()

(3)Python有个专门的舵机旋转角度控制函数,我们安装这些库来调用。

sudo pip install adafruit-circuitpython-pca9685

sudo pip install adafruit-circuitpython-servokit

python 复制代码
from adafruit_pca9685 import PCA9685
from board import SCL, SDA
import busio
from adafruit_motor import servo

i2c_bus = busio.I2C(SCL, SDA)
pwm = PCA9685(i2c_bus)                                     # 使用默认地址初始化PWM设备
pwm.frequency = 50                                         # 将频率设置为50 Hz
servo_12 = servo.Servo(pwm.channels[12])                   # 指定第12通道的舵机(从0开始)
servo_15 = servo.Servo(pwm.channels[15])                   # 指定第15通道的舵机

print('Moving servo on channel 0, press Ctrl-C to quit...')
servo_12.angle = 90
servo_15.angle = 90
while True:
    # 伺服电机转动最小角度和最大角度
    servo_12.angle = 0
    servo_15.angle = 0
    time.sleep(1)
    servo_12.angle = 180
    servo_15.angle = 180
    time.sleep(1)

可以接舵机试试,控制角度非常方便。

相关推荐
TDengine (老段)1 小时前
TDengine 字符串函数 CHAR 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
安娜的信息安全说2 小时前
深入浅出 MQTT:轻量级消息协议在物联网中的应用与实践
开发语言·物联网·mqtt
Kandiy180253981875 小时前
PHY6252国产蓝牙低成本透传芯片BLE5.2智能灯控智能家居
人工智能·物联网·智能家居·射频工程
卍郝凝卍7 小时前
物联网卡摄像头从前端至后台的实现过程
前端·物联网·视频解决方案
北京盛世宏博7 小时前
科技引领,档案管理更高效之智慧档案馆三维立体恒温恒湿消毒净化系统
科技·物联网·智慧档案·档案馆温湿度
明达智控技术9 小时前
MR30分布式IO在自动上料机的应用
分布式·物联网·自动化
小莞尔11 小时前
【51单片机】【protues仿真】基于51单片机简易电子琴系统(8键)
c语言·单片机·嵌入式硬件·物联网·51单片机
塔能物联运维11 小时前
物联网设备物理环境自适应监控与运维策略优化
运维·物联网
塔能物联运维11 小时前
物联网边缘节点数据缓存优化与一致性保障技术
java·后端·物联网·spring·缓存
北京阿尔泰科技厂家19 小时前
从数据采集到智能诊断:阿尔泰科技实时高精度远距离管道状态监测全流程
物联网·安全·能源·信号采集·数据采集器·工业测试·管道监测