このレッスンでは、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
プログラムが実行されると、ランダムな角度に回転します。
サンプルコードと解説コメント:
#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
プログラムが実行されると、サーボモーターが2つの角度の間を回転します。
DownLoad Url osoyoo.com