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

主要引數以及引腳定義:

在這裡插入圖片描述

  1. 電壓:舵機供電5-7v,接受高一點的電壓。

大多數的舵機設計電壓都是在5~6V,尤其在多個舵機同時執行時,跟需要有大功率的電源供電。如果直接使用Arduino
5V引腳直接為舵機供電,會出現一些難以預測的問題,所以我們建議你能有個合適的外部電源為驅動板供電。

  1. 邏輯電路電壓:3-5V

  2. 通訊介面:使用i2c通訊,就是SCL、SDA引腳

7位的I2C地址為:0x40 + A5:A0,A5到A0如果不做任何處理的話是0,想要把哪一位置1就把那個引腳焊到一起。另外用i2cdetect檢測出還有一個0x70地址一直存在,這是一個通用地址,可以給所有從機下達指令。

  1. OE反使能腳:這個引腳低電平使能,不接的話模組內部預設已經接地使能了,所以正常使用可以不接。

  2. 工作頻率:40-1000HZ

接線圖:

這裡寫圖片描述

注意!!!下面例子需要用到外部庫檔案,如果你IDE沒有<Adafruit_PWMServoDriver.h>,那麼下載這個並放在安裝路徑的文件—arduino-libraries路徑下面

例子程式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
    }
  }
}

基礎知識:

上面有出現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;

這裡寫圖片描述
_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 +8191= 1228)
LED0_OFF_H = 4h; LED0_OFF_L = CCh(此計數到1228後LED開始關閉)

這居然是最高閱讀量的Blog,開始寫的有點迷惑,今天抽點時間優化一下結構,後面會繼續優化,博主也不是完全掌握,有任何疑問評論區見,大家共同學習,如果有幫到你,記得點贊噢!——2018年10月11日13點32分編輯

完。