背景

最近尝试做六足机器人,遇到了需要用pwm控制板控制舵机的问题。淘了快PCA9685的16路舵机控制板,卖家给的参考程序是在Arduino uno的驱动,想着移植到nodeMCU上,用lua能省不少开发时间。

clipboard.png

c版驱动

/*************************************************** 
  This is a library for our Adafruit 16-channel PWM & Servo driver

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815

  These displays use I2C to communicate, 2 pins are required to  
  interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4

  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#ifndef _ADAFRUIT_PWMServoDriver_H
#define _ADAFRUIT_PWMServoDriver_H

#if ARDUINO >= 100
 #include "Arduino.h"
#else
 #include "WProgram.h"
#endif


#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4

#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


class Adafruit_PWMServoDriver {
 public:
  Adafruit_PWMServoDriver(uint8_t addr = 0x40);
  void begin(void);
  void reset(void);
  void setPWMFreq(float freq);
  void setPWM(uint8_t num, uint16_t on, uint16_t off);
  void setPin(uint8_t num, uint16_t val, bool invert=false);

 private:
  uint8_t _i2caddr;

  uint8_t read8(uint8_t addr);
  void write8(uint8_t addr, uint8_t d);
};

#endif
/*************************************************** 
  This is a library for our Adafruit 16-channel PWM & Servo driver

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815

  These displays use I2C to communicate, 2 pins are required to  
  interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4

  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#ifndef _ADAFRUIT_PWMServoDriver_H
#define _ADAFRUIT_PWMServoDriver_H

#if ARDUINO >= 100
 #include "Arduino.h"
#else
 #include "WProgram.h"
#endif


#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4

#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


class Adafruit_PWMServoDriver {
 public:
  Adafruit_PWMServoDriver(uint8_t addr = 0x40);
  void begin(void);
  void reset(void);
  void setPWMFreq(float freq);
  void setPWM(uint8_t num, uint16_t on, uint16_t off);
  void setPin(uint8_t num, uint16_t val, bool invert=false);

 private:
  uint8_t _i2caddr;

  uint8_t read8(uint8_t addr);
  void write8(uint8_t addr, uint8_t d);
};

#endif

c版测试

/*************************************************** 
  This is an example for our Adafruit 16-channel PWM & Servo driver
  PWM test - this will drive 16 PWMs in a 'wave'

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815

  These displays use I2C to communicate, 2 pins are required to  
  interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4

  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);

void setup() {
  Serial.begin(9600);
  Serial.println("16 channel PWM test!");

  // if you want to really speed stuff up, you can go into 'fast 400khz I2C' mode
  // some i2c devices dont like this so much so if you're sharing the bus, watch
  // out for this!

  pwm.begin();
  pwm.setPWMFreq(1600);  // This is the maximum PWM frequency
    
  // save I2C bitrate
  uint8_t twbrbackup = TWBR;
  // must be changed after calling Wire.begin() (inside pwm.begin())
  TWBR = 12; // upgrade to 400KHz!
    
}

void loop() {
  // Drive each PWM in a 'wave'
  for (uint16_t i=0; i<4096; i += 8) {
    for (uint8_t pwmnum=0; pwmnum < 16; pwmnum++) {
      pwm.setPWM(pwmnum, 0, (i + (4096/16)*pwmnum) % 4096 );
    }
  }
}

lua版驱动

PwmSvr={
    id=0,            PCA9685_SUBADR1=0x2,    LED0_ON_L=0x6,    ALLLED_ON_L=0xFA,
    pinSCL=1,        PCA9685_SUBADR2=0x3,    LED0_ON_H=0x7,    ALLLED_ON_H=0xFB,
    pinSDA=2,        PCA9685_SUBADR3=0x4,    LED0_OFF_L=0x8,    ALLLED_OFF_L=0xFC,
    _i2caddr=0x40,    PCA9685_MODE1=0x0,        LED0_OFF_H=0x9,    ALLLED_OFF_H=0xFD,
    addr=nil,        PCA9685_PRESCALE=0xFE
}
function PwmSvr.read8(addr)
    i2c.start(PwmSvr.id)
    i2c.address(PwmSvr.id, PwmSvr._i2caddr,i2c.TRANSMITTER)
    i2c.write(PwmSvr.id,addr)
    i2c.stop(PwmSvr.id)
    i2c.start(PwmSvr.id)
    i2c.address(PwmSvr.id, PwmSvr._i2caddr,i2c.RECEIVER)
    local c=i2c.read(PwmSvr.id,1)
    i2c.stop(PwmSvr.id)
    return string.byte(c)
end
function PwmSvr.write8(addr,d)
    i2c.start(PwmSvr.id)
    i2c.address(PwmSvr.id, PwmSvr._i2caddr ,i2c.TRANSMITTER)
    i2c.write(PwmSvr.id,addr,d)
    i2c.stop(PwmSvr.id)
end
function PwmSvr.reset()
    PwmSvr.write8(PwmSvr.PCA9685_MODE1,0x0)
end
function PwmSvr.begin()
    i2c.setup(PwmSvr.id, PwmSvr.pinSDA, PwmSvr.pinSCL, i2c.SLOW)
    PwmSvr.reset()
end
function PwmSvr.setPWMFreq(freq)
    local prescale=math.ceil((25000000/4096)/(freq*0.9)+0.5)
    local oldmode = PwmSvr.read8(PwmSvr.PCA9685_MODE1)
    local newmode =bit.bor(bit.band(oldmode,0x7F),0x10)
    PwmSvr.write8(PwmSvr.PCA9685_MODE1,newmode)
    PwmSvr.write8(PwmSvr.PCA9685_PRESCALE,prescale)
    PwmSvr.write8(PwmSvr.PCA9685_MODE1,oldmode)
    for i=0,6000 do tmr.wdclr() end
    PwmSvr.write8(PwmSvr.PCA9685_MODE1, bit.bor(oldmode,0xa1))
end
function PwmSvr.setPWM(num,on,off)
    i2c.start(PwmSvr.id)
    i2c.address(PwmSvr.id, PwmSvr._i2caddr ,i2c.TRANSMITTER)
    i2c.write(PwmSvr.id
        ,PwmSvr.LED0_ON_L+4*num
        ,bit.band(on,0xff)
        ,bit.rshift(on,8)
        ,bit.band(off,0xff)
        ,bit.rshift(off,8)
    )
    i2c.stop(PwmSvr.id)
end
function PwmSvr.setPin(num,val,invert)
    if(val>4095) then val=4095 end
    if(invert) then
        if(val==0) then
            PwmSvr.setPWM(num, 4096, 0)
        elseif(val==4095) then
            PwmSvr.setPWM(num, 0, 4096)
        else
            PwmSvr.setPWM(num, 0, 4095-val)
        end
    else
        if(val==4095) then
            PwmSvr.setPWM(num, 4096, 0)
        elseif(val==0) then
            PwmSvr.setPWM(num, 0, 4096)
        else
            PwmSvr.setPWM(num, 0, val)
        end
    end
    
end

lua版测试

dofile("PwmSvr_PCA9685.lua")
PwmSvr.begin()
PwmSvr.setPWMFreq(60)
for pin=0,15 do
    for r=150,400 do
        PwmSvr.setPWM(pin, 0, r)
        tmr.wdclr()
    end
end

源码地址

https://github.com/Seasonley/...


seasonley
607 声望693 粉丝

一切皆数据


引用和评论

0 条评论