Jetson Xavier NX (16) -- Jetson IIC: PCA9685
Posted Techblog of HaoWANG
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetson Xavier NX (16) -- Jetson IIC: PCA9685相关的知识,希望对你有一定的参考价值。
详情参考:https://blog.csdn.net/qq_18676517/article/details/104873374
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级),相关精度计算如下:
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 软件设计
树莓派平台Python版
要运行该程序,首先选装python,安装好Python后,还需要安装树莓派平台的smbus库:
sudo apt-get install python-smbus
树莓派平台采用Python驱动PCA9685的Python代码如下所示:
#!/usr/bin/python import 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 = 0xFD def __init__(self, address=0x40, debug=False): self.bus = smbus.SMBus(1) self.address = address self.debug = debug if (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 result def setPWMFreq(self, freq): "Sets the PWM frequency" prescaleval = 25000000.0 # 25MHz prescaleval /= 4096.0 # 12-bit prescaleval /= float(freq) prescaleval -= 1.0 if (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 # sleep self.write(self.__MODE1, newmode) # go to sleep self.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 20000us self.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度。
以上是关于Jetson Xavier NX (16) -- Jetson IIC: PCA9685的主要内容,如果未能解决你的问题,请参考以下文章
Jetson Xavier NX (15) -- Jetson.GPIO
Jetson Xavier NX (15) -- Jetson.GPIO
Jetson Xavier NX (14) -- 40-Pin扩展插针
Jetson Xavier NX (14) -- 40-Pin扩展插针