PCA9685:I2C轉(zhuǎn)16路PWM俏竞,助力你的系統(tǒng)

PCA9685:I2C轉(zhuǎn)16路PWM,助力你的系統(tǒng)

1 基本介紹

1.1 該IC主要參數(shù)特征如下:

  • I2C接口吭从,支持高達(dá)16路PWM輸出朝蜘,每路12位分辨率(4096級(jí))
  • 內(nèi)置25MHz晶振,可不連接外部晶振涩金,也可以連接外部晶振,最大50MHz
  • 支持2.3V-5.5V電壓暇仲,最大耐壓值5.5V,邏輯電平3.3V
  • 具有上電復(fù)位步做,以及軟件復(fù)位等功能

    :本教程側(cè)重PCA9685的PWM輸出,但PCA9685亦可用于WS2812等LED顏色控制等奈附。

1.2 控制精度

假設(shè)舵機(jī)為50HZ的控制頻率全度,脈寬為0.5ms~2.5ms,12位分辨率(4096級(jí))斥滤,相關(guān)精度計(jì)算如下:

  • PWM周期:


  • 時(shí)間分辨率:


  • 最大脈寬時(shí)間:


  • 最大脈寬時(shí)間可分成的份數(shù):


  • 0-180度的舵機(jī)将鸵,角度分辨率:


2 硬件參數(shù)

2.1 封裝及引腳排列

PCA9685有兩種封裝:TSSOP28, HVQFN28,其相應(yīng)的引腳排列如下圖所示:



引腳功能描述如下圖所示:



2.2 器件地址

PCA9685的器件地址是由引腳A0佑颇,A1顶掉,A2,A3挑胸,A4痒筒,A5共同決定,并且該引腳不可懸空,由于有6個(gè)引腳共同決定器件地址簿透,因此移袍,可以有64個(gè)器件地址,由于該IC上電便保留LED All Call address (E0h老充,1110 000)以及Software Reset address(06h葡盗,0000 0110),實(shí)際僅有62個(gè)可用器件地址啡浊,因此觅够,理論上,1個(gè)I2C接口可控制16*62=992路PWM虫啥,其引腳控制器件地址的示意圖如下圖所示:



默認(rèn)情況下蔚约,若將A0-A5全部接地,則其器件地址為:0x40涂籽。

2.3 寄存器及其地址

默認(rèn)情況下苹祟,上電復(fù)位后,寄存器地址的默認(rèn)值均為0评雌,寄存器地址及其用途見下圖所示:



圖中節(jié)選的部分寄存器地址中树枫,主要關(guān)心以下寄存器:

  • 模式設(shè)置寄存器:MODE1,MODE2景东。
  • 脈寬(占空比)設(shè)置寄存器:LED0_ON_L,LED0_ON_H,LED0_OFF_L,LED0_OFF_H......LED15......每一路PWM通道占用4個(gè)寄存器砂轻。
  • 周期(頻率)設(shè)置寄存器:PRE_SCALE。
    接下來(lái)介紹以上寄存器的使用及其注意事項(xiàng)斤吐。

2.4 模式設(shè)置寄存器

2.4.1 MODE1寄存器

首先介紹MODE1寄存器,如下圖:



在使用該寄存器的時(shí)候要注意:

  • 如果未停止所有PWM輸出就將其進(jìn)入到睡眠模式搔涝,那么,所有輸出通道在下一輪都將輸出高電平和措。

  • 睡眠后重新啟動(dòng)PWM的操作為:


  • 注意庄呈,在設(shè)置PWM頻率(寫PRESCALE寄存器)的時(shí)候,要先設(shè)置為Sleep模式派阱,請(qǐng)參考后面源碼部分诬留。

2.4.2 MODE2寄存器

該寄存器的各位功能如下圖所示:


2.5 PWM通道寄存器及其占空比設(shè)置

PWM通道寄存器如下圖:



由圖可知,對(duì)于每一個(gè)通道贫母,有4個(gè)寄存器文兑,每個(gè)寄存器圖解如下圖所示:



在設(shè)置PWM占空比的時(shí)候,首先腺劣,配置舵機(jī)的示例如下圖所示(ON < OFF的情況):

當(dāng)特殊情況下绿贞,PWM周期大于定時(shí)器一次計(jì)數(shù)時(shí),如下圖所示(ON>OFF的情況):


2.6 PWM周期(頻率)寄存器及其周期(頻率)設(shè)置

接下來(lái)介紹配置PWM頻率的寄存器:
一般情況下,在用內(nèi)置晶振誓酒,為25MHZ樟蠕,通過(guò)配置PRE_SCALE寄存器進(jìn)行配置贮聂,配置的PRE_SCALE寄存器的值與PWM頻率的關(guān)系如下圖所示:



如果在舵機(jī)控制中,采用內(nèi)置晶振寨辩,取osc_clock=25000000吓懈,update_rate=50(舵機(jī)控制頻率50Hz)

2.7 推薦硬件設(shè)計(jì)

首先,OE引腳須接低電平以確保IC使能靡狞,如果連接LED燈耻警,則推薦以下幾種連接方式,如下圖所示:


3 軟件設(shè)計(jì)

3.1 Micro:bit平臺(tái)TypeScript版

接下來(lái)進(jìn)行軟件設(shè)計(jì)部分講解甸怕,由于本次開發(fā)采用Micro:bit底層開發(fā)甘穿,采用的是TypeScript(JavaScript的超類),所以暫提供該語(yǔ)言,提供基本操作方法及其思路梢杭,日后再更新C,C++及其它平臺(tái)(STM32,Linux樹莓派温兼,Arduino等)操作方法,源碼如下武契,可結(jié)合DataSheet及以上教程理解:

/**
 * 使用此文件來(lái)定義自定義函數(shù)和圖形塊募判。
 * 想了解更詳細(xì)的信息,請(qǐng)前往 https://makecode.microbit.org/blocks/custom
 */
 
/**
 * 自定義圖形塊
 */
//% weight=5 color=#0fbc11 icon="\uf113"
namespace Servo {
    const PCA9685_ADDRESS = 0x40
    const MODE1 = 0x00
    const MODE2 = 0x01
    const SUBADR1 = 0x02
    const SUBADR2 = 0x03
    const SUBADR3 = 0x04
    const PRESCALE = 0xFE
    const LED0_ON_L = 0x06
    const LED0_ON_H = 0x07
    const LED0_OFF_L = 0x08
    const LED0_OFF_H = 0x09
    const ALL_LED_ON_L = 0xFA
    const ALL_LED_ON_H = 0xFB
    const ALL_LED_OFF_L = 0xFC
    const ALL_LED_OFF_H = 0xFD
 
    const STP_CHA_L = 2047
    const STP_CHA_H = 4095
 
    const STP_CHB_L = 1
    const STP_CHB_H = 2047
 
    const STP_CHC_L = 1023
    const STP_CHC_H = 3071
 
    const STP_CHD_L = 3071
    const STP_CHD_H = 1023
 
    let initialized = false
 
    function i2cwrite(addr: number, reg: number, value: number) {
        let buf = pins.createBuffer(2)
        buf[0] = reg
        buf[1] = value
        pins.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 frequency
        let 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; // sleep
        i2cwrite(PCA9685_ADDRESS, MODE1, newmode); // go to sleep
        i2cwrite(PCA9685_ADDRESS, PRESCALE, prescale); // set the prescaler
        i2cwrite(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=180
    export function Servo(channel: number,degree: number): void {
        if (!initialized) {
            initPCA9685();
        }
        // 50hz: 20,000 us
        let v_us = (degree * 1800 / 180 + 600); // 0.6 ~ 2.4
        let 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=2500
    export function ServoPulse(channel: number,pulse: number): void {
        if (!initialized) {
            initPCA9685();
        }
        // 50hz: 20,000 us
        let value = pulse * 4096 / 20000;
        setPwm(channel, 0, value);
    }
}

以上便是Micro:bit驅(qū)動(dòng)PCA9685的源代碼咒唆,注意源代碼中的時(shí)間為us届垫,而教程中的時(shí)間為ms。

3.2 樹莓派平臺(tái)Python版

要運(yùn)行該程序全释,首先選裝python装处,安裝好Python后,還需要安裝樹莓派平臺(tái)的smbus庫(kù):

sudo apt-get install python-smbus

樹莓派平臺(tái)采用Python驅(qū)動(dòng)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,命令行進(jìn)入該文件所在的路徑浸船,運(yùn)行該P(yáng)ython腳本:

sudo python pca9685.py

執(zhí)行該命令后妄迁,便可控制舵機(jī)從0度轉(zhuǎn)到180度,再?gòu)?80度轉(zhuǎn)到0度李命。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末判族,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子项戴,更是在濱河造成了極大的恐慌,老刑警劉巖槽惫,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件周叮,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡界斜,警方通過(guò)查閱死者的電腦和手機(jī)仿耽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)各薇,“玉大人项贺,你說(shuō)我怎么就攤上這事君躺。” “怎么了开缎?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵棕叫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我奕删,道長(zhǎng)俺泣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任完残,我火速辦了婚禮伏钠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谨设。我一直安慰自己熟掂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布扎拣。 她就那樣靜靜地躺著赴肚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鹏秋。 梳的紋絲不亂的頭發(fā)上尊蚁,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音侣夷,去河邊找鬼横朋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛百拓,可吹牛的內(nèi)容都是我干的琴锭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼衙传,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼决帖!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蓖捶,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤地回,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后俊鱼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刻像,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年并闲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了细睡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡帝火,死狀恐怖溜徙,靈堂內(nèi)的尸體忽然破棺而出湃缎,到底是詐尸還是另有隱情,我是刑警寧澤蠢壹,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布嗓违,位于F島的核電站,受9級(jí)特大地震影響知残,放射性物質(zhì)發(fā)生泄漏靠瞎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一求妹、第九天 我趴在偏房一處隱蔽的房頂上張望乏盐。 院中可真熱鬧,春花似錦制恍、人聲如沸父能。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)何吝。三九已至,卻和暖如春鹃唯,著一層夾襖步出監(jiān)牢的瞬間爱榕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工坡慌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留黔酥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓洪橘,卻偏偏與公主長(zhǎng)得像跪者,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子熄求,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • ???本文主要介紹嵌入式系統(tǒng)的一些基礎(chǔ)知識(shí)渣玲,希望對(duì)各位有幫助。 嵌入式系統(tǒng)基礎(chǔ) 1弟晚、嵌入式系統(tǒng)的定義 (1)定義:...
    OpenJetson閱讀 3,309評(píng)論 0 13
  • 1忘衍、嵌入式系統(tǒng)的定義 (1)定義:以應(yīng)用為中心,以計(jì)算機(jī)技術(shù)為基礎(chǔ)卿城,軟硬件可裁剪淑履,適應(yīng)應(yīng)用系統(tǒng)對(duì)功能、可靠性藻雪、成本...
    榮卓然閱讀 1,823評(píng)論 0 5
  • 電力系統(tǒng)是一個(gè)綜合化的系統(tǒng),作為一個(gè)熟練的電工狸吞,對(duì)于通信有著一定的認(rèn)識(shí)勉耀。否則很多問(wèn)題指煎,我們將無(wú)從下手。首先我們從廣...
    洪城小電工閱讀 117,719評(píng)論 8 34
  • 摘要 現(xiàn)在需要用Arduino輸出可自定義頻率(100Hz)的PWM來(lái)控制電動(dòng)機(jī)轉(zhuǎn)速便斥。Arduino里有簡(jiǎn)單的語(yǔ)句...
    一森一森閱讀 62,595評(píng)論 5 23
  • 倘若不愿咫尺相凝 又何乎天涯相距 為何在離別之時(shí) 要一路灑滿玫瑰花的種子 待我滿目美麗 沁鼻芬香 卻只能踩著荊棘 ...
    空是不堪閱讀 143評(píng)論 0 1