16路12位PWM信号发生器 PCA 9685
节省主机资源,值得拥有。此舵机驱动板使用PCA9685芯片,是16通道12bit PWM舵机驱动,用2个引脚通过I2C就可以驱动16个舵机。
强大如斯!
先对单个舵机尝试一下,了解一下PWM
20ms周期 = 频率为50Hz
单个舵机例程1:
非常简单的舵机例子,实现效果是使得电机转动:
#include <Servo.h>
#define PIN_SERVO 10
Servo myservo;
void setup()
{
myservo.attach(PIN_SERVO);
}
void loop()
{
myservo.write(0);
delay(1000);
myservo.write(80);
delay(1000);
myservo.write(160);
delay(1000);
myservo.write(80);
delay(1000);
myservo.write(0);
delay(1000);
}
单个舵机例程2:
#include <Servo.h>
#define PIN_SERVO 9
int i=0;
Servo myservo;
void setup()
{
myservo.attach(PIN_SERVO);
}
void loop()
{
for(i=0;i<160;i++)
{
myservo.write(i);
delay(30);
}
for(i=160;i>0;i--)
{
myservo.write(i);
delay(30);
}
delay(1000);
}
单个舵机使用板上的I/O口就可以,可是舵机多了呢?
来学习PCA 9685
主要参数以及引脚定义:
- 电压:舵机供电5-7v,接受高一点的电压。
大多数的舵机设计电压都是在5~6V,尤其在多个舵机同时运行时,跟需要有大功率的电源供电。如果直接使用Arduino
5V引脚直接为舵机供电,会出现一些难以预测的问题,所以我们建议你能有个合适的外部电源为驱动板供电。
-
逻辑电路电压:3-5V
-
通信接口:使用i2c通信,就是SCL、SDA引脚
7位的I2C地址为:0x40 + A5:A0,A5到A0如果不做任何处理的话是0,想要把哪一位置1就把那个引脚焊到一起。另外用i2cdetect检测出还有一个0x70地址一直存在,这是一个通用地址,可以给所有从机下达指令。
-
OE反使能脚:这个引脚低电平使能,不接的话模块内部默认已经接地使能了,所以正常使用可以不接。
-
工作频率:40-1000HZ
接线图:
注意!!!下面例子需要用到外部库文件,如果你IDE没有<Adafruit_PWMServoDriver.h>,那么下载这个并放在安装路径的文档—arduino-libraries路径下面
16路舵机驱动板资料(内含模块级联说明):https://pan.baidu.com/s/1gfhFGDP
例子程序1
(有时间就简化一下,好像还不是最好的例子)
16路舵机同时转动。原理是定义脉宽最大和最小,通过改变0-15号舵机的脉宽大小实现角度转动
/***************************************************
This is an example for our Adafruit 16-channel PWM & Servo driver
Servo test - this will drive 16 servos, one after the other
这是我们的Adafruit 16通道PWM和伺服驱动器的一个例子,驱动16个伺服电机
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
这些显示器使用I2C进行通信,需要2个引脚。
接口。对于ARDUINO UNOS,这是SCL->模拟5,SDA - >模拟4
****************************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
// called this way, it uses the default address 0x40
以这种方式调用,它使用默认地址0x40。
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
//也可以用不同的地址调用它
/* Depending on your servo make, the pulse width min and max may vary, you want these to be as small/large as possible without hitting the hard stop
for max range. You'll have to tweak them as necessary to match the servos you
have!*/
/*根据你的伺服制作,脉冲宽度最小和最大可能变化,你想要这些尽可能小大而不碰到
硬停止,对于最大范围。你必须调整它们以匹配你的伺服系统!*/
#define SERVOMIN 150 // this is the 'minimum' pulse length count (out of 4096)
//这是“最小”脉冲长度计数(在4096)中
#define SERVOMAX 600 // this is the 'maximum' pulse length count (out of 4096)
//这是“最大”脉冲长度计数(在4096中)
// our servo # counter
//uint8_t servonum = 0;
void setup() {
Serial.begin(9600);
Serial.println("16 channel Servo test!");
pwm.begin();
pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates
模拟伺服在60赫兹更新下运行
}
// you can use this function if you'd like to set the pulse length in seconds
// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. its not precise!
//如果您想以秒为单位设置脉冲长度,则可以使用此函数。
//例如SET伺服脉冲(0,0.001)是一个1毫秒的脉冲宽度。它不是
void setServoPulse(uint8_t n, double pulse) {
double pulselength;//精度浮点数
pulselength = 1000000; // 1,000,000 us per second 每秒100万
pulselength /= 60; // 60 Hz
Serial.print(pulselength); Serial.println(" us per period");
pulselength /= 4096; // 12 bits of resolution 12位分辨率
Serial.print(pulselength); Serial.println(" us per bit");
pulse *= 1000;
pulse /= pulselength;
Serial.println(pulse);
pwm.setPWM(n, 0, pulse);
}
void loop() {
// Drive each servo one at a time
//Serial.println(servonum);
//每次驱动一个伺服驱动器
//串行打印(伺服);
for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
pwm.setPWM(0, 0, pulselen);
pwm.setPWM(1, 0, pulselen);
pwm.setPWM(2, 0, pulselen);
pwm.setPWM(3, 0, pulselen);
pwm.setPWM(4, 0, pulselen);
pwm.setPWM(5, 0, pulselen);
pwm.setPWM(6, 0, pulselen);
pwm.setPWM(7, 0, pulselen);
pwm.setPWM(8, 0, pulselen);
pwm.setPWM(9, 0, pulselen);
pwm.setPWM(10, 0, pulselen);
pwm.setPWM(11, 0, pulselen);
pwm.setPWM(12, 0, pulselen);
pwm.setPWM(13, 0, pulselen);
pwm.setPWM(14, 0, pulselen);
pwm.setPWM(15, 0, pulselen);
}
delay(500);
for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
pwm.setPWM(0, 0, pulselen);
pwm.setPWM(1, 0, pulselen);
pwm.setPWM(2, 0, pulselen);
pwm.setPWM(3, 0, pulselen);
pwm.setPWM(4, 0, pulselen);
pwm.setPWM(5, 0, pulselen);
pwm.setPWM(6, 0, pulselen);
pwm.setPWM(7, 0, pulselen);
pwm.setPWM(8, 0, pulselen);
pwm.setPWM(9, 0, pulselen);
pwm.setPWM(10, 0, pulselen);
pwm.setPWM(11, 0, pulselen);
pwm.setPWM(12, 0, pulselen);
pwm.setPWM(13, 0, pulselen);
pwm.setPWM(14, 0, pulselen);
pwm.setPWM(15, 0, pulselen);
}
delay(500);
}
例子程序2:
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// our servo # counter
//uint8_t servonum = 0;
void setup() {
Serial.begin(9600);
Serial.println("16 channel Servo test!");
pwm.begin();
pwm.setPWMFreq(50); // Analog servos run at ~50 Hz updates模拟伺服运行在50赫兹更新
}
void loop() {
//setServoPulse(0, 0.001);
//setServoPulseMS(1, 50);
pwm.setPin(0,0,0);
pwm.setPin(1,4096,0);
pwm.setPin(2,(0.5/20.0)*4096,0);
}
IDE自带例程:
/***************************************************
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
这是我们的AdfRuIT 16通道PWM和伺服驱动器的一个例子。
PWM测试-这将驱动16个波姆斯在“波”
今天在AdfRuIT商店挑选一个!
------HTTP://www. AdAfRuIT.COM/PROSTS/815
这些显示器使用I2C进行通信,需要2个引脚。
接口。对于ARDUINO UNOS,这是SCL->模拟5,SDA - >模拟4
ADAFRUIT投入时间和资源来提供这个开源代码,
请通过购买支持AdfRuIT和开源硬件
来自AdfRuIT的产品!
由Limor Fray/Lajayad撰写的AdfuRIT产业。
BSD许可证,以上所有文本必须包含在任何再分配中
***********************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
//以这种方式调用,它使用默认地址0x40。
// 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);
//你也可以用不同的地址称呼它。
//ADAFRUITIGPWM伺服驱动器PWM=ADAFRUITIGPWM伺服驱动器(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!
//如果你真的想加快速度,你可以进入“快速400千赫I2C”模式。
//一些I2C设备不太喜欢这个,所以如果你在共享公共汽车,请注意。
//为了这个!
pwm.begin();
pwm.setPWMFreq(1600); // This is the maximum PWM frequency这是最大PWM频率
// save I2C bitrate
uint8_t twbrbackup = TWBR;
// must be changed after calling Wire.begin() (inside pwm.begin())
//保存I2C比特率
//必须在调用Word.No.2之后更改(内部PWM(开始))
TWBR = 12; // upgrade to 400KHz!升级到400千赫!
}
void loop() {
// Drive each PWM in a 'wave'
//在“波”中驱动每个PWM
for (uint16_t i=0; i<4096; i += 8) { //i=i+8
for (uint8_t pwmnum=0; pwmnum < 16; pwmnum++) {
pwm.setPWM(pwmnum, 0, (i + (4096/16)*pwmnum) % 4096 ); //%求余数 x = 7 % 5; //x=2,余数为2,如果i=8,pwmnum=2,(8+256*2)=520,520%4096
}
}
}
Arduino的例子暂时没有找到很简洁的,OpenMv例子很简单易懂,欢迎围观我的Blog——【OpenMV学习笔记】 pca9685学习之用python尝试写代码:https://blog.csdn.net/qq_42807924/article/details/82354781
OpenMV控制单个PWM舵机:https://blog.csdn.net/qq_42807924/article/details/82563671
基础知识:
上面有出现uint8_t uint16_t,那它们是什么?其实很简单:
1字节 uint8_t //无符号整型
2字节 uint16_t
编程语言不扎实看一下字节定义:
B就是Byte,也就是字节;b是bit的缩写,b就是bit,也就是比特位(bit)。B与b不同,注意区分,KB是千字节,Kb是千比特位。
8bit(比特位)=1Byte(字节);
1024Byte(字节)=1KB(千字节);
1024KB(千字节)=1MB(兆字节);
1MB(兆字节)=1024KB(千字节)=1024*1024B(字节)=1048576B(字节);
1024MB=1GB;
1024GB=1TB;
![这里写图片描述](https://i-blog.csdnimg.cn/blog_migrate/95d5aeab53d82afdebb68d000efb820e.jpeg)
_t的意思到底表示什么?
它就是一个结构的标注,可以理解为type/typedef的缩写,表示它是通过typedef定义的,而不是其它数据类型
头文件内定义:
typedef signed char int8_t;
//无符号整型
typedef unsigned char uint8_t;
typedef int int16_t;
typedef unsigned int uint16_t;
进阶学习:
从例子中模仿,从库函数中掌握!
库文件:AdafruitPWMServoDriverLibrary
1.
setPWMFreq(freq)
eg: pwm.setPWMFreq(1000); // set the PWM frequency to the maximum value of 1000Hz
Description: 用来调节PWM频率,范围 freq:40-1000Hz
setPWM(channel, on, off)
Description
This function sets the start (on) and end (off) of the high segment of the PWM pulse on a
specific channel. You specify the ‘tick’ value between 0…4095 when the signal will turn on,
and when it will turn of. Channel indicates which of the 16 PWM outputs should be updated
with the new values.
Arguments
channel: The channel that should be updated with the new values (0…15)
on: The tick (between 0…4095) when the signal should transition from low to high
off:the tick (between 0…4095) when the signal should transition from high to low
Example
The following example will cause channel 15 to start low, go high around 25% into the pulse
(tick 1024 out of 4096), transition back to low 75% into the pulse (tick 3072), and remain low
for the last 25% of the pulse:
描述
该功能设置a上PWM脉冲高段的开始(on)和end(off)
特定渠道。 当信号打开时,指定0…4095之间的’tick’值,
什么时候会转。 通道指示应更新16个PWM输出中的哪一个
用新的价值观。
参数
channel:应使用新值更新的通道(0…15)
on:当信号从低电平变为高电平时的滴答(在0…4095之间)
off:当信号从高电平变为低电平时的滴答(在0…4095之间)
例
以下示例将导致通道15从低电平开始,在脉冲中高达25%左右
(在4096中打勾1024),转换回脉冲的低75%(勾选3072),并保持低位
对于最后25%的脉冲:
eg: setPWM(15, 1024, 3072);
4096这个数到底是什么?
The turn-on time of each LED driver output and the duty cycle of PWM can be controlled
independently using the LEDn_ON and LEDn_OFF registers.
There will be two 12-bit registers per LED output. These registers will be programmed by
the user. Both registers will hold a value from 0 to 4095. One 12-bit register will hold a
value for the ON time and the other 12-bit register will hold the value for the OFF time. The
ON and OFF times are compared with the value of a 12-bit counter that will be running
continuously from 0000h to 0FFFh (0 to 4095 decimal).
Update on ACK requires all 4 PWM channel registers to be loaded before outputs will
change on the last ACK.
The ON time, which is programmable, will be the time the LED output will be asserted and
the OFF time, which is also programmable, will be the time when the LED output will be
negated. In this way, the phase shift becomes completely programmable. The resolution
for the phase shift is 1⁄4096
4096 of the target frequency. Table 7 lists these registers.
The following two examples illustrate how to calculate values to be loaded into these
registers.
Example 1: (assumes that the LED0 output is used and
(delay time) + (PWM duty cycle) 100 %)
Delay time = 10 %; PWM duty cycle = 20 % (LED on time = 20 %; LED off time = 80 %).
Delay time = 10 % = 409.6 ~ 410 counts = 19Ah.
Since the counter starts at 0 and ends at 4095, we will subtract 1, so delay time = 199h
counts.
LED0_ON_H = 1h; LED0_ON_L = 99h (LED start turn on after this delay count to 409)
LED on time = 20 % = 819.2 ~ 819 counts.
LED off time = 4CCh (decimal 410 + 819 1 = 1228)
LED0_OFF_H = 4h; LED0_OFF_L = CCh (LED start turn off after this count to 1228)
PWM频率PRE_SCALE
硬件强制可以加载到PRE_SCALE寄存器中的最小值
在’3’。 PRE_SCALE寄存器定义输出调制的频率。该
预分频值由公式1中所示的公式确定:
(1)
其中更新速率是所需的输出调制频率。例如,对于
输出默认频率为200 Hz,振荡器时钟频率为25 MHz:
(2)
如果PRE_SCALE寄存器设置为“0x03h”,则最大PWM频率为1526 Hz。
如果PRE_SCALE寄存器设置为“0xFFh”,则最小PWM频率为24 Hz。
只有当MODE1寄存器的SLEEP位设置为时,才能设置PRE_SCALE寄存器
逻辑1.例1 :(假设使用了LED0输出和
(延迟时间)+(PWM占空比)100%)
延迟时间= 10%; PWM占空比= 20%(LED导通时间= 20%; LED关闭时间= 80%)。
延迟时间= 10%= 409.6~410计数= 19Ah。
由于计数器从0开始并在4095结束,我们将减去1,因此延迟时间= 199h
计数。
LED0_ON_H = 1h; LED0_ON_L = 99h(此延迟计数到之后LED开启亮起
409)
LED导通时间= 20%= 819.2~819计数。
LED关闭时间= 4CCh(十进制410 +8191= 1228)
LED0_OFF_H = 4h; LED0_OFF_L = CCh(此计数到1228后LED开始关闭)