正規のオンライン小売業者:

AMAZON
AMAZON
amzuk

紹介

このレッスンでは、Raspberry Piを使用してSG90サーボを制御する方法を説明します。

ハードウェアの準備

1 * Raspberry Pi
1 * ブレッドボード
1 * SG90サーボモーター
1 * PCA9685
1 * ブレッドボード用電源
ジャンパーワイヤー
1 * T-Extension Board
1 * 40ピンケーブル

ソフトウェアの準備

注意:このレッスンでは、PuTTyを使用してPCからRaspberry Piをリモートで制御します。Raspberry Piの設定方法については、レッスン1:Raspberry Piの入門を参照してください。

作業原理

SG90は約180度回転できるサーボであり、DCモーター、位置決めシステム、ギアで構成されています。3本のワイヤーがあります:赤いワイヤーは電源に、茶色のワイヤーはグラウンドに、黄色のワイヤーはPWM信号に接続されています。回転角度は次の式によって決定されます。

DutyCycle=PulseWidth/Period

Period=1/Frequency

so

DutyCycle=PulseWidth/(1/Frequency)=PulseWidth*Frequency

このプロジェクトでは、PCA9685 16チャンネルサーボモータードライバーモジュールを使用してDutycycleを制御します。PCA9685モジュールはi2c制御のPWMドライバーであり、16個のPWM出力を制御するために2つのピンしか使用しません。同じモジュールを最大で62個i2cに接続できます。つまり、合計992の出力を制御できます。PCA9685の動作周波数は24Hzから1526Hzまで調整可能で、デューティサイクルは0%から100%まで調整可能です。PCA9685のデータシートについては、こちらを参照してください:datasheet

回路図は以下の通りです:

ハードウェアセットアップ

サンプルコード

プログラムを実行する前に、以下の手順でPiを設定してください。

1) I2Cを有効にする(既に済んでいる場合は、スキップしてください)

sudo  nano  /boot/config.txt

/boot/config.txtファイルを開き、コード行”dtparam=i2c_arm”を見つけ、行の前に#記号があるか確認し、それをコメント解除します(この行の前に#を削除)、最終的にコードは次のようになります:

2) IICモジュールをロードする(既に済んでいる場合は、スキップしてください)

sudo nano /etc/modules

/etc/modulesファイルを開き、以下の2行を追加してください:

i2c-bcm2708
i2c-dev

3) Piを再起動する

reboot


C言語ユーザー向け

1 )サンプルコードファイルpca9685-servo.tarをダウンロードして解凍します。

cd  ~

wget http://osoyoo.com/driver/pi3_start_learning_kit_lesson_12/pca9685-servo.tar.gz

sudo  tar  xzvf  pca9685-servo.tar.gz

2)パスを変更します。

cd  /home/pi/pca9685-servo

3) コードをコンパイルします。

gcc  -o servo servo.c  pca9685.c  -lwiringPi

4) プログラムを実行します。

sudo  ./servo

5) テスト結果

プログラムが実行されると、ランダムな角度に回転します。

サンプルコードと解説コメント:

#include "pca9685.h"
#include < wiringPi.h>
#include < stdio.h>
#include < stdlib.h>

#define PIN_BASE 300
#define MAX_PWM 4096
#define HERTZ 50

/**
 * Calculate the number of ticks the signal should be high for the required amount of time
 */
int calcTicks(float impulseMs, int hertz)
{
      float cycleMs = 1000.0f / hertz;
      return (int)(MAX_PWM * impulseMs / cycleMs + 0.5f);
}

/**
 * input is [0..1]
 * output is [min..max]
 */
float map(float input, float min, float max)
{
      return (input * max) + (1 - input) * min;
}


int main(void)
{
      printf("PCA9685 servo example\n");
      printf("Connect a servo to any pin. It will rotate to random angles\n");

      // Setup with pinbase 300 and i2c location 0x40
      int fd = pca9685Setup(PIN_BASE, 0x40, HERTZ);
      if (fd < 0) { printf("Error in setup\n"); return fd; } // Reset all output pca9685PWMReset(fd); // Set servo to neutral position at 1.5 milliseconds // (View http://en.wikipedia.org/wiki/Servo_control#Pulse_duration) float millis = 1.5; int tick = calcTicks(millis, HERTZ); pwmWrite(PIN_BASE + 16, tick); delay(2000); int active = 1; while (active) { // That's a hack. We need a random number < 1 float r = rand(); while (r > 1)
                  r /= 10;

            millis = map(r, 1, 2);
            tick = calcTicks(millis, HERTZ);

            pwmWrite(PIN_BASE + 16, tick);
            delay(1000);
      }

      return 0;
}

#include < wiringPi.h >
#include < wiringPiI2C.h >
#include "pca9685.h"
// Setup registers
#define PCA9685_MODE1 0x0
#define PCA9685_PRESCALE 0xFE

// Define first LED and all LED. We calculate the rest
#define LED0_ON_L 0x6
#define LEDALL_ON_L 0xFA

#define PIN_ALL 16


// Declare
static void myPwmWrite(struct wiringPiNodeStruct *node, int pin, int value);
static void myOnOffWrite(struct wiringPiNodeStruct *node, int pin, int value);
static int myOffRead(struct wiringPiNodeStruct *node, int pin);
static int myOnRead(struct wiringPiNodeStruct *node, int pin);
int baseReg(int pin);


/**
 * Setup a PCA9685 device with wiringPi.
 *  
 * pinBase:       Use a pinBase > 64, eg. 300
 * i2cAddress:      The default address is 0x40
 * freq:            Frequency will be capped to range [40..1000] Hertz. Try 50 for servos
 */
int pca9685Setup(const int pinBase, const int i2cAddress, float freq)
{
      // Create a node with 16 pins [0..15] + [16] for all
      struct wiringPiNodeStruct *node = wiringPiNewNode(pinBase, PIN_ALL + 1);

      // Check if pinBase is available
      if (!node)
            return -1;

      // Check i2c address
      int fd = wiringPiI2CSetup(i2cAddress);
      if (fd < 0) return fd; // Setup the chip. Enable auto-increment of registers. int settings = wiringPiI2CReadReg8(fd, PCA9685_MODE1) & 0x7F; int autoInc = settings | 0x20; wiringPiI2CWriteReg8(fd, PCA9685_MODE1, autoInc); // Set frequency of PWM signals. Also ends sleep mode and starts PWM output. if (freq > 0)
            pca9685PWMFreq(fd, freq);
      

      node->fd                  = fd;
      node->pwmWrite            = myPwmWrite;
      node->digitalWrite      = myOnOffWrite;
      node->digitalRead      = myOffRead;
      node->analogRead      = myOnRead;

      return fd;
}

/**
 * Sets the frequency of PWM signals.
 * Frequency will be capped to range [40..1000] Hertz. Try 50 for servos.
 */
void pca9685PWMFreq(int fd, float freq)
{
      // Cap at min and max
      freq = (freq > 1000 ? 1000 : (freq < 40 ? 40 : freq)); // To set pwm frequency we have to set the prescale register. The formula is: // prescale = round(osc_clock / (4096 * frequency))) - 1 where osc_clock = 25 MHz // Further info here: http://www.nxp.com/documents/data_sheet/PCA9685.pdf Page 24 int prescale = (int)(25000000.0f / (4096 * freq) - 0.5f); // Get settings and calc bytes for the different states. int settings = wiringPiI2CReadReg8(fd, PCA9685_MODE1) & 0x7F; // Set restart bit to 0 int sleep = settings | 0x10; // Set sleep bit to 1 int wake = settings & 0xEF; // Set sleep bit to 0 int restart = wake | 0x80; // Set restart bit to 1 // Go to sleep, set prescale and wake up again. wiringPiI2CWriteReg8(fd, PCA9685_MODE1, sleep); wiringPiI2CWriteReg8(fd, PCA9685_PRESCALE, prescale); wiringPiI2CWriteReg8(fd, PCA9685_MODE1, wake); // Now wait a millisecond until oscillator finished stabilizing and restart PWM. delay(1); wiringPiI2CWriteReg8(fd, PCA9685_MODE1, restart); } /** * Set all leds back to default values (: fullOff = 1) */ void pca9685PWMReset(int fd) { wiringPiI2CWriteReg16(fd, LEDALL_ON_L , 0x0); wiringPiI2CWriteReg16(fd, LEDALL_ON_L + 2, 0x1000); } /** * Write on and off ticks manually to a pin * (Deactivates any full-on and full-off) */ void pca9685PWMWrite(int fd, int pin, int on, int off) { int reg = baseReg(pin); // Write to on and off registers and mask the 12 lowest bits of data to overwrite full-on and off wiringPiI2CWriteReg16(fd, reg , on & 0x0FFF); wiringPiI2CWriteReg16(fd, reg + 2, off & 0x0FFF); } /** * Reads both on and off registers as 16 bit of data * To get PWM: mask each value with 0xFFF * To get full-on or off bit: mask with 0x1000 * Note: ALL_LED pin will always return 0 */ void pca9685PWMRead(int fd, int pin, int *on, int *off) { int reg = baseReg(pin); if (on) *on = wiringPiI2CReadReg16(fd, reg); if (off) *off = wiringPiI2CReadReg16(fd, reg + 2); } /** * Enables or deactivates full-on * tf = true: full-on * tf = false: according to PWM */ void pca9685FullOn(int fd, int pin, int tf) { int reg = baseReg(pin) + 1; // LEDX_ON_H int state = wiringPiI2CReadReg8(fd, reg); // Set bit 4 to 1 or 0 accordingly state = tf ? (state | 0x10) : (state & 0xEF); wiringPiI2CWriteReg8(fd, reg, state); // For simplicity, we set full-off to 0 because it has priority over full-on if (tf) pca9685FullOff(fd, pin, 0); } /** * Enables or deactivates full-off * tf = true: full-off * tf = false: according to PWM or full-on */ void pca9685FullOff(int fd, int pin, int tf) { int reg = baseReg(pin) + 3; // LEDX_OFF_H int state = wiringPiI2CReadReg8(fd, reg); // Set bit 4 to 1 or 0 accordingly state = tf ? (state | 0x10) : (state & 0xEF); wiringPiI2CWriteReg8(fd, reg, state); } /** * Helper function to get to register */ int baseReg(int pin) { return (pin >= PIN_ALL ? LEDALL_ON_L : LED0_ON_L + 4 * pin);
}




//------------------------------------------------------------------------------------------------------------------
//
//      WiringPi functions
//
//------------------------------------------------------------------------------------------------------------------




/**
 * Simple PWM control which sets on-tick to 0 and off-tick to value.
 * If value is <= 0, full-off will be enabled * If value is >= 4096, full-on will be enabled
 * Every value in between enables PWM output
 */
static void myPwmWrite(struct wiringPiNodeStruct *node, int pin, int value)
{
      int fd   = node->fd;
      int ipin = pin - node->pinBase;

      if (value >= 4096)
            pca9685FullOn(fd, ipin, 1);
      else if (value > 0)
            pca9685PWMWrite(fd, ipin, 0, value);      // (Deactivates full-on and off by itself)
      else
            pca9685FullOff(fd, ipin, 1);
}

/**
 * Simple full-on and full-off control
 * If value is 0, full-off will be enabled
 * If value is not 0, full-on will be enabled
 */
static void myOnOffWrite(struct wiringPiNodeStruct *node, int pin, int value)
{
      int fd   = node->fd;
      int ipin = pin - node->pinBase;

      if (value)
            pca9685FullOn(fd, ipin, 1);
      else
            pca9685FullOff(fd, ipin, 1);
}

/**
 * Reads off registers as 16 bit of data
 * To get PWM: mask with 0xFFF
 * To get full-off bit: mask with 0x1000
 * Note: ALL_LED pin will always return 0
 */
static int myOffRead(struct wiringPiNodeStruct *node, int pin)
{
      int fd   = node->fd;
      int ipin = pin - node->pinBase;

      int off;
      pca9685PWMRead(fd, ipin, 0, &off);

      return off;
}

/**
 * Reads on registers as 16 bit of data
 * To get PWM: mask with 0xFFF
 * To get full-on bit: mask with 0x1000
 * Note: ALL_LED pin will always return 0
 */
static int myOnRead(struct wiringPiNodeStruct *node, int pin)
{
      int fd   = node->fd;
      int ipin = pin - node->pinBase;

      int on;
      pca9685PWMRead(fd, ipin, &on, 0);

      return on;
}
 #ifdef __cplusplus
extern "C" {
#endif

// Setup a pca9685 at the specific i2c address
extern int pca9685Setup(const int pinBase, const int i2cAddress/* = 0x40*/, float freq/* = 50*/);

// You now have access to the following wiringPi functions:
//
// void pwmWrite (int pin, int value)
//            if value <= 0, set full-off // else if value >= 4096, set full-on
//            else set PWM
//
// void digitalWrite (int pin, int value)
//             if value != 0, set full-on
//            else set full-off
//
// int digitalRead (int pin)
//            read off-register
//            To get PWM: mask with 0xFFF
//            To get full-off bit: mask with 0x1000
//            Note: ALL_LED pin will always return 0
//
// int analogRead (int pin)
//            read on-register
//            To get PWM: mask with 0xFFF
//            To get full-on bit: mask with 0x1000
//            Note: ALL_LED pin will always return 0



// Advanced controls
// You can use the file descriptor returned from the setup function to access the following features directly on each connected pca9685
extern void pca9685PWMFreq(int fd, float freq);
extern void pca9685PWMReset(int fd);
extern void pca9685PWMWrite(int fd, int pin, int on, int off);
extern void pca9685PWMRead(int fd, int pin, int *on, int *off);

extern void pca9685FullOn(int fd, int pin, int tf);
extern void pca9685FullOff(int fd, int pin, int tf);

#ifdef __cplusplus
}
#endif

Pythonユーザー向け

1) python-smbusとi2c-toolsをインストールします(既にインストールされている場合は、このステップはスキップしてください)

sudo  apt-get   install  -y   python-smbus   i2c-tools

2) I2Cライブラリが正しくインストールされているかを確認するには、次のターミナルコマンドを実行できます。

sudo  i2cdetect  -y  1

または古いRaspberry Piの場合はポート0

sudo  i2cdetect  -y  0

プログラムが実行されると、接続されたデバイスがあれば、PCA9685のアドレスを示す下記のような表が表示されます。この例では、PCA9685のアドレスは0x40です。

3) PCA9685 pythonライブラリをインストールします:

cd  ~

git    clone  https://github.com/adafruit/Adafruit_python_PCA9685.git

cd  Adafruit_python_PCA9685

sudo  python  setup.py   install

4) プログラムを実行します。

cd  examples

sudo  python  ./simpletest.py

5) 実行結果

プログラムが実行されると、サーボモーターが2つの角度の間を回転します。