/********************************************************************** Copyright (C) 2013 SARVESH KULKARNI Associate Professor, ECE Department Villanova University, PA, USA. LEGAL NOTICE: This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Library of routines for Adafruit's PCA9685 16-channel PWM breakout board. Version 0.8.01, Released April 2013. The Adafruit PWM board for which this library is written, is an I2C slave. You need to connect at least 4 pins from your microcontroller to drive the board. On the Raspberry Pi, Rev.2, these pins are as follows: SDA = pin 3, SCL = pin 5 VCC (3.3V) = pin 1, GND = pin 6 In addition, should you want to control any device with a power draw greater than that of an LED (e.g. servo motor) using this PWM board, you do need to provide a 5V power supply with sufficient amperage at the +V and GND p[ins of the PWM board. *********************************************************************/ #include "adafruit_pca9685_pwm_driver.h" #include #include #include #include #include #include #include #include #include using namespace std; // Class Constructor; If board's I2C address is not supplied, the default // address of 0x40 is presumed. PWM_Board::PWM_Board(uint8_t i2c_addr) { my_i2caddr = i2c_addr; my_fd = initialize(); // Set up file descriptor for I2C slave soft_reset(); } // Initialize all higher level device support and return a file descriptor // for use by subsequent write() and read() calls in other member fns. If // either the open() or ioctl() call fails, a negative integer error code is // returned. At this time, the function does not report which call (i.e. // open()or ioctl()) failed! If that info is important to your needs, please // modify this code accordingly. int PWM_Board::initialize(void) { int retcode, file_descriptor; char filename[20], adapter_no[4]; // I2C device files are character device files with major number 89 // and a minor device no. The command "i2cdetect -l" in the i2c-tools // package lists all minor device nos. in use in the format: i2c-X // (e.g. i2c-0, i2c-1). The minor device number below is hardcoded // below, but it is better to dynamically determine this no. strcpy(adapter_no, "1"); // Copy "/path/i2cdevicename" to filename strcpy(filename, "/dev/i2c-"); strcat(filename, adapter_no); // Open i2c device file using above filename file_descriptor = open(filename, O_RDWR); if (file_descriptor < 0) { // ERROR - could not open the I2C device file return file_descriptor; } // If I2C device file opened OK, then link the device addr to the // file descriptor for subsequent use. retcode = ioctl(file_descriptor, I2C_SLAVE_FORCE, my_i2caddr); if (retcode < 0) { // ERROR - ioctl call failed return retcode; } // I2C slave board is now set up for access. At this point, any read() // or write() from/to the slave does not need to specify the slave's addr // because the ioctl call has already associated the file descriptor with // the slave. So, use the file descriptor for all communication with the // slave. // HARDWARE NOTE: Any read/write from/to the slave's registers will cause // the kernel driver to automatically signal the I2C bus 'start' condition, // followed by the slave's address on the I2C bus --> this happens behind // the scenes. See timing diagrams in the PCA9685 datasheet to see how the // I2C bus master and slave devices operate. return file_descriptor; } // Check to see if the board is initialized i.e. check if the file descriptor // stored in the object is non-zero. bool PWM_Board::isInitialized() { if (my_fd > 0) return true; else return false; } // Set PWM frequency in Hz // Valid range of frequencies is from 24 Hz -- 1.6 kHz // This translates to prescale values from 253 -- 3 // At frequencies below 24 Hz, the 8-bit prescale value rolls under, and // at frequencies above 1.6 kHz, the board either cannot generate the freq, // or the prescale counter rolls over. // IMP: Precise frequency selection is not possible due to the inexactness // in the prescale value formula; you could be off by as much as 10% // NOTE: If the chip is in sleep mode, this function will awaken it!!! int PWM_Board::setFreq(float freq) { // freq is in Hz char buf[10]; uint8_t prescale_val; if (isInitialized()) { if (freq < 24) freq = 24; // sane default on extreme lower range if (freq > 1600) freq = 1600; // sane default on extreme upper range // See page 24 of PCA9685 Data Sheet for the prescale value formula prescale_val = (uint8_t) floorf((OSC_CLK / (4096*freq))+ 0.5) - 1; buf[0] = _MODE1_REG; buf[1] = MODE1_REG_DEFAULT|0x10; // must sleep first, otherwise writes // to prescale register are blocked write(my_fd, buf, 2); // sleep (low power mode, oscillator off) buf[0] = _PRE_SCALE_REG; buf[1] = prescale_val; write(my_fd, buf, 2); // load prescale register buf[0] = _MODE1_REG; buf[1] = MODE1_REG_DEFAULT|0x81; // awaken chip write(my_fd, buf, 2); // restore old mode at new frequency usleep(500); // insert delay; internal oscillator may // need up to 500 us to restart & stabilize return 0; // success! } else return FD_UNINITIALIZED; // failed! ..since my_fd is uninitialized } // Set PWM Duty Cycle // Duty cycle and delay are in %ages; channel_num = 0xff means ALL outputs // NOTE: If the chip is in sleep mode, this function will awaken it!!! int PWM_Board::setDutyCycle(float duty_cycle, int channel_num, float delay) { char buf[10]; uint8_t led_on_l, led_on_h, led_off_l, led_off_h; uint16_t led_on, led_off; if (!isInitialized()) return FD_UNINITIALIZED; // failed! my_fd is uninitialized // Validate inputs first if(duty_cycle > 100) return DUTY_CYCLE_ILLEGAL; // duty cycle error if(delay > 100) return DELAY_ILLEGAL; // led ON delay error if ((channel_num < 0) || ((channel_num > 15) && (channel_num != 255))) return CHANNEL_NUM_ILLEGAL; // channel no. error // I/p params are valid; so proceed with our calculations led_on = floorf((40.96*delay) + 0.5) - 1; // see example 1 on page 16 led_off = led_on + floorf((40.96*duty_cycle)+ 0.5); // of PCA9685 data sheet if (led_off > 4095) led_off = led_off - 4096; // see example 2 on page 16 if (duty_cycle == 100) { // otherwise we get no o/p since led_on = led_off if (led_on == 0) led_off = 4095; else led_off = led_on - 1; } led_on_l = led_on & 0x0f; led_on_h = led_on >> 8; led_off_l = led_off & 0x0f; led_off_h = led_off >> 8; buf[0] = _MODE1_REG; buf[1] = MODE1_REG_DEFAULT | 0x21; // Set to Auto Increment mode so that we write(my_fd, buf, 2); // can write to all 4 registers at once if (channel_num == 255) // Update ALL PWM registers buf[0] = _ALL_LED_ON_L; // ALL_LED_ON base address else buf[0] = _LED0_ON_L + 4*channel_num; // specific channel base address buf[1] = led_on_l; buf[2] = led_on_h; buf[3] = led_off_l; buf[4] = led_off_h; write(my_fd, buf, 5); return 0; } // Put PCA9685 in sleep mode - low power, oscillator off. Since this function // executes an orderly shutdown, the contents of all PWM registers are // invalidated and must be reloaded upon wakeup. The wakeup function is not // provided by this library since it is unnecessary - the functions to set // the PWM frequency and duty cycle automatically awaken the chip. int PWM_Board:: sleep(void) { if (isInitialized()) { char buf[10]; uint8_t mode1_val; // Orderly shutdown - turn off all PWM channel o/ps by setting // bit 4 of _ALL_LED_OFF_H to 1 buf[0] = _ALL_LED_OFF_H; buf[1] = 0x10; write(my_fd, buf, 2); // Let's attempt to preserve the register's contents buf[0] = _MODE1_REG; if(read(my_fd, buf, 1) != 1) { // ERROR Reading Mode1 register mode1_val = 0x0; // try to continue anyway } // else buf[0] contains the newly read byte buf[1] = mode1_val | 0x10; // just set the sleep bit buf[0] = _MODE1_REG; write(my_fd, buf, 2); return 0; // sucess! } else return FD_UNINITIALIZED; // failed! ..since my_fd is uninitialized } // Reset internal MODE1 and MODE2 registers to useful defaults; the prescale // value register of the PCA9685 is preset so as to yield a 1 KHz PWM signal. // You *do* have to set the duty cycle explicitly to actually activate the PWM o/ps. int PWM_Board::soft_reset(void) { char buf[10]; if (isInitialized()) { // MODE1 register: Restart=0, ExtClk=0, AutoIncr=1, Sleep=1, Sub1-3=0, AllCall=1 buf[0] = _MODE1_REG; buf[1] = (MODE1_REG_DEFAULT | 0x30); // must sleep first, otherwise writes // to pre_scale register are blocked // MODE2 register: O/p uninverted, LED o/ps are Open drain, O/ps change on STOP buf[2] = MODE2_REG_DEFAULT; write(my_fd, buf, 3); // Set Pre_Scale register to 0x5 (= 1kHz freq) // This is not strictly necessary, but it allows the casual programmer // to simply set the PWM duty cycle and be up and running instantly. buf[0] = _PRE_SCALE_REG; buf[1] = 0x5; write(my_fd, buf, 2); // load pre_scale register // Set MODE1 register to awaken so that pre_scale takes effect // Restart=0, ExtClk=0, AutoIncr=0, Sleep=0, Sub1-3=0, AllCall=1 buf[0] = _MODE1_REG; buf[1] = MODE1_REG_DEFAULT; // awaken chip write(my_fd, buf, 2); usleep(500); // insert delay; internal oscillator may // need up to 500 us to restart & stabilize return 1; // success } else return -1; // failed! ..since my_fd is uninitialized } // Hard Reset - be very careful! This fn will quite possibly reset all PCA9685 // slaves on the specified I2C bus channel. It may even reset non PCA9685 slaves // if the PCA9685's control word for reset matches theirs. int PWM_Board:: hard_reset(void) { int retcode, file_desc; char filename[20], buf[10], adapter_no[4]; // The minor I2C device number below is hardcoded below, but it is // better to dynamically determine this no. strcpy(adapter_no, "1"); // Copy "/path/i2cdevicename" to filename strcpy(filename, "/dev/i2c-"); strcat(filename, adapter_no); // Open i2c device file using above filename file_desc = open(filename, O_RDWR); if (file_desc < 0) { // ERROR - could not open the I2C device file return file_desc; } // If I2C device file opened OK, then link the device addr to the // file descriptor for subsequent use. retcode = ioctl(file_desc, I2C_SLAVE_FORCE, GENERAL_CALL_ADDR); if (retcode < 0) { // ERROR - ioctl call failed return retcode; } // Else I2C bus devices are now ready for general access buf[0] = SW_RESET; // command to reset device write(file_desc, buf, 1); return 0; }