背景
最近尝试做六足机器人,遇到了需要用pwm控制板控制舵机的问题。淘了快PCA9685的16路舵机控制板,卖家给的参考程序是在Arduino uno的驱动,想着移植到nodeMCU上,用lua能省不少开发时间。
c版驱动
/*************************************************** This is a library for our Adafruit 16-channel PWM & Servo driverPick one up today in the adafruit shop!------> http://www.adafruit.com/products/815These displays use I2C to communicate, 2 pins are required to interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4Adafruit 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 0xFDclass 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 driverPick one up today in the adafruit shop!------> http://www.adafruit.com/products/815These displays use I2C to communicate, 2 pins are required to interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4Adafruit 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 0xFDclass 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 driverPWM test - this will drive 16 PWMs in a 'wave'Pick one up today in the adafruit shop!------> http://www.adafruit.com/products/815These displays use I2C to communicate, 2 pins are required to interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4Adafruit 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
#include
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm &#61; 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 &#39;fast 400khz I2C&#39; mode// some i2c devices dont like this so much so if you&#39;re sharing the bus, watch// out for this!pwm.begin();pwm.setPWMFreq(1600); // This is the maximum PWM frequency// save I2C bitrateuint8_t twbrbackup &#61; TWBR;// must be changed after calling Wire.begin() (inside pwm.begin())TWBR &#61; 12; // upgrade to 400KHz!}void loop() {// Drive each PWM in a &#39;wave&#39;for (uint16_t i&#61;0; i<4096; i &#43;&#61; 8) {for (uint8_t pwmnum&#61;0; pwmnum <16; pwmnum&#43;&#43;) {pwm.setPWM(pwmnum, 0, (i &#43; (4096/16)*pwmnum) % 4096 );}}
}
lua版驱动
PwmSvr&#61;{id&#61;0, PCA9685_SUBADR1&#61;0x2, LED0_ON_L&#61;0x6, ALLLED_ON_L&#61;0xFA,pinSCL&#61;1, PCA9685_SUBADR2&#61;0x3, LED0_ON_H&#61;0x7, ALLLED_ON_H&#61;0xFB,pinSDA&#61;2, PCA9685_SUBADR3&#61;0x4, LED0_OFF_L&#61;0x8, ALLLED_OFF_L&#61;0xFC,_i2caddr&#61;0x40, PCA9685_MODE1&#61;0x0, LED0_OFF_H&#61;0x9, ALLLED_OFF_H&#61;0xFD,addr&#61;nil, PCA9685_PRESCALE&#61;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&#61;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&#61;math.ceil((25000000/4096)/(freq*0.9)&#43;0.5)local oldmode &#61; PwmSvr.read8(PwmSvr.PCA9685_MODE1)local newmode &#61;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&#61;0,6000 do tmr.wdclr() endPwmSvr.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&#43;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&#61;4095 endif(invert) thenif(val&#61;&#61;0) thenPwmSvr.setPWM(num, 4096, 0)elseif(val&#61;&#61;4095) thenPwmSvr.setPWM(num, 0, 4096)elsePwmSvr.setPWM(num, 0, 4095-val)endelseif(val&#61;&#61;4095) thenPwmSvr.setPWM(num, 4096, 0)elseif(val&#61;&#61;0) thenPwmSvr.setPWM(num, 0, 4096)elsePwmSvr.setPWM(num, 0, val)endendend
lua版测试
dofile("PwmSvr_PCA9685.lua")
PwmSvr.begin()
PwmSvr.setPWMFreq(60)
for pin&#61;0,15 dofor r&#61;150,400 doPwmSvr.setPWM(pin, 0, r)tmr.wdclr()end
end
源码地址
https://github.com/Seasonley/...