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