Library for interfacing with the NXP PCA9685 PWM controller over an I2C connection. Designed with the Adafruit breakout board (of the same name) in mind.

Committer:
dimavb
Date:
Sat Jun 22 17:24:38 2019 +0000
Revision:
5:d44f88fa27a1
Parent:
4:3bcda7deb098
init

Who changed what in which revision?

UserRevisionLine numberNew contents of line
el13cj 2:8017239aa374 1 #ifndef PCA9685_LIBRARY_CPP
el13cj 2:8017239aa374 2 #define PCA9685_LIBRARY_CPP
el13cj 2:8017239aa374 3
el13cj 2:8017239aa374 4
el13cj 0:aa965d6b1f8f 5 #include "mbed.h"
el13cj 0:aa965d6b1f8f 6 #include "PCA9685.h"
el13cj 0:aa965d6b1f8f 7 #include "definitions.h"
el13cj 0:aa965d6b1f8f 8
el13cj 2:8017239aa374 9
dimavb 5:d44f88fa27a1 10 PCA9685::PCA9685(uint8_t i2c_address, I2C *i2c_object, float frequency) :
el13cj 4:3bcda7deb098 11
el13cj 4:3bcda7deb098 12 i2c_addr(i2c_address),
el13cj 4:3bcda7deb098 13 freq(frequency),
el13cj 4:3bcda7deb098 14 i2c(i2c_object)
el13cj 4:3bcda7deb098 15
el13cj 0:aa965d6b1f8f 16 {
el13cj 0:aa965d6b1f8f 17
el13cj 0:aa965d6b1f8f 18 }
el13cj 0:aa965d6b1f8f 19
el13cj 4:3bcda7deb098 20 void PCA9685::init(void)
el13cj 4:3bcda7deb098 21 {
el13cj 4:3bcda7deb098 22
el13cj 0:aa965d6b1f8f 23 reset();
el13cj 4:3bcda7deb098 24
el13cj 1:9d6633a308ba 25 uint8_t prescale = (uint8_t) (OSC_CLOCK / (4096 * PWM_SCALER * freq)) - 1;
el13cj 4:3bcda7deb098 26
el13cj 0:aa965d6b1f8f 27 write_8(MODE1, 0x21); //0010 0001 : AI ENABLED
el13cj 0:aa965d6b1f8f 28 write_8(MODE2, 0x07); //0000 0111 : NOT INVRT, CHANGE ON STOP, TOTEM POLE, \OE = 1, LEDn = HIGH IMP
el13cj 4:3bcda7deb098 29
el13cj 0:aa965d6b1f8f 30 set_prescale(prescale);
el13cj 4:3bcda7deb098 31
el13cj 0:aa965d6b1f8f 32 }
el13cj 0:aa965d6b1f8f 33
el13cj 4:3bcda7deb098 34 void PCA9685::reset(void)
el13cj 4:3bcda7deb098 35 {
el13cj 4:3bcda7deb098 36
el13cj 0:aa965d6b1f8f 37 write_8(MODE1,0x00);
el13cj 4:3bcda7deb098 38
el13cj 0:aa965d6b1f8f 39 }
el13cj 0:aa965d6b1f8f 40
el13cj 4:3bcda7deb098 41 void PCA9685::set_prescale(uint8_t prescale)
el13cj 4:3bcda7deb098 42 {
el13cj 4:3bcda7deb098 43
el13cj 0:aa965d6b1f8f 44 uint8_t oldmode = read_8(MODE1);
el13cj 0:aa965d6b1f8f 45 uint8_t newmode = (oldmode&0x7F) | 0x10; // set the sleep bit
el13cj 0:aa965d6b1f8f 46
el13cj 0:aa965d6b1f8f 47 write_8(MODE1, newmode); // send the device to sleep
el13cj 0:aa965d6b1f8f 48 wait_ms(5);
el13cj 0:aa965d6b1f8f 49 write_8(PRESCALE, prescale); // set the prescaler
el13cj 0:aa965d6b1f8f 50 write_8(MODE1, oldmode);
el13cj 0:aa965d6b1f8f 51 wait_ms(5);
el13cj 0:aa965d6b1f8f 52 write_8(MODE1, oldmode | 0xa1); // wake up the device
el13cj 4:3bcda7deb098 53
el13cj 0:aa965d6b1f8f 54 }
el13cj 0:aa965d6b1f8f 55
el13cj 0:aa965d6b1f8f 56
el13cj 0:aa965d6b1f8f 57 //NB REQUIRES AUTO-INCREMENT MODE ENABLED
el13cj 0:aa965d6b1f8f 58 //0 <= pwm_output <= 15
el13cj 0:aa965d6b1f8f 59 //0 <= (count_on || count_off) <= 4095
el13cj 4:3bcda7deb098 60 void PCA9685::set_pwm_output(int pwm_output, uint16_t count_on, uint16_t count_off)
el13cj 4:3bcda7deb098 61 {
el13cj 4:3bcda7deb098 62
el13cj 0:aa965d6b1f8f 63 char msg[5];
el13cj 0:aa965d6b1f8f 64
el13cj 0:aa965d6b1f8f 65 msg[0] = LED0_ON_L + (4 * pwm_output);
el13cj 0:aa965d6b1f8f 66 msg[1] = count_on;
el13cj 0:aa965d6b1f8f 67 msg[2] = count_on >> 8;
el13cj 0:aa965d6b1f8f 68 msg[3] = count_off;
el13cj 0:aa965d6b1f8f 69 msg[4] = count_off >> 8;
el13cj 0:aa965d6b1f8f 70
dimavb 5:d44f88fa27a1 71 i2c->write(i2c_addr, msg, 5);
el13cj 4:3bcda7deb098 72
el13cj 3:a3410f2f061d 73 }
el13cj 3:a3410f2f061d 74
el13cj 4:3bcda7deb098 75 void PCA9685::set_pwm_output_on_0(int pwm_output, uint16_t count_off)
el13cj 4:3bcda7deb098 76 {
el13cj 4:3bcda7deb098 77
el13cj 3:a3410f2f061d 78 char msg[3];
el13cj 4:3bcda7deb098 79
el13cj 3:a3410f2f061d 80 msg[0] = LED0_ON_L + 2 + (4 * pwm_output);
el13cj 3:a3410f2f061d 81 msg[1] = count_off;
el13cj 3:a3410f2f061d 82 msg[2] = count_off >> 8;
el13cj 4:3bcda7deb098 83
dimavb 5:d44f88fa27a1 84 i2c->write(i2c_addr,msg,3);
el13cj 3:a3410f2f061d 85
el13cj 0:aa965d6b1f8f 86 }
el13cj 0:aa965d6b1f8f 87
el13cj 0:aa965d6b1f8f 88
el13cj 0:aa965d6b1f8f 89 //NB REQUIRES AUTO-INCREMENT MODE ENABLED
el13cj 0:aa965d6b1f8f 90 //0 <= pwm_output <= 15
el13cj 4:3bcda7deb098 91 void PCA9685::set_pwm_duty(int pwm_output, float duty_cycle)
el13cj 4:3bcda7deb098 92 {
el13cj 4:3bcda7deb098 93
el13cj 4:3bcda7deb098 94 if (duty_cycle > 1.0) {
el13cj 4:3bcda7deb098 95 duty_cycle = 1.0;
el13cj 4:3bcda7deb098 96 }
el13cj 4:3bcda7deb098 97 if (duty_cycle < 0.0) {
el13cj 4:3bcda7deb098 98 duty_cycle = 0.0;
el13cj 4:3bcda7deb098 99 }
el13cj 4:3bcda7deb098 100
el13cj 0:aa965d6b1f8f 101 uint16_t count_off = (uint16_t) (duty_cycle * 4095);
el13cj 0:aa965d6b1f8f 102 uint16_t count_on = 0x0000;
el13cj 4:3bcda7deb098 103
el13cj 0:aa965d6b1f8f 104 set_pwm_output(pwm_output, count_on, count_off);
el13cj 4:3bcda7deb098 105
el13cj 0:aa965d6b1f8f 106 }
el13cj 0:aa965d6b1f8f 107
el13cj 0:aa965d6b1f8f 108
el13cj 0:aa965d6b1f8f 109 //NB REQUIRES AUTO-INCREMENT MODE ENABLED
el13cj 0:aa965d6b1f8f 110 //0 <= pwm_output <= 15
el13cj 4:3bcda7deb098 111 void PCA9685::set_pwm_pw(int pwm_output, float pulse_width_us)
el13cj 4:3bcda7deb098 112 {
el13cj 4:3bcda7deb098 113
el13cj 0:aa965d6b1f8f 114 float period_us = (1e6/freq);
el13cj 4:3bcda7deb098 115
el13cj 0:aa965d6b1f8f 116 float duty = pulse_width_us/period_us;
el13cj 4:3bcda7deb098 117
el13cj 0:aa965d6b1f8f 118 set_pwm_duty(pwm_output, duty);
el13cj 4:3bcda7deb098 119
el13cj 0:aa965d6b1f8f 120 }
el13cj 4:3bcda7deb098 121
el13cj 0:aa965d6b1f8f 122
el13cj 4:3bcda7deb098 123 void PCA9685::update(void)
el13cj 4:3bcda7deb098 124 {
el13cj 4:3bcda7deb098 125
dimavb 5:d44f88fa27a1 126 i2c->stop();
el13cj 0:aa965d6b1f8f 127
el13cj 0:aa965d6b1f8f 128 }
el13cj 4:3bcda7deb098 129
el13cj 0:aa965d6b1f8f 130
el13cj 0:aa965d6b1f8f 131
el13cj 4:3bcda7deb098 132 void PCA9685::write_8(uint8_t reg, uint8_t msg)
el13cj 4:3bcda7deb098 133 {
el13cj 4:3bcda7deb098 134
el13cj 0:aa965d6b1f8f 135 char send[2]; //Store the address and data in an array
el13cj 0:aa965d6b1f8f 136 send[0] = reg;
el13cj 0:aa965d6b1f8f 137 send[1] = msg;
dimavb 5:d44f88fa27a1 138 i2c->write(i2c_addr, send, 2);
el13cj 4:3bcda7deb098 139
el13cj 0:aa965d6b1f8f 140 }
el13cj 0:aa965d6b1f8f 141
el13cj 4:3bcda7deb098 142 uint8_t PCA9685::read_8(uint8_t reg)
el13cj 4:3bcda7deb098 143 {
el13cj 4:3bcda7deb098 144
el13cj 0:aa965d6b1f8f 145 char send[1] ;
el13cj 0:aa965d6b1f8f 146 send[0] = reg;
dimavb 5:d44f88fa27a1 147 i2c->write(i2c_addr, send, 1);
el13cj 0:aa965d6b1f8f 148 char recieve[1];
dimavb 5:d44f88fa27a1 149 i2c->read(i2c_addr, recieve, 1);
el13cj 0:aa965d6b1f8f 150 return recieve[0];
el13cj 4:3bcda7deb098 151
el13cj 4:3bcda7deb098 152 }
el13cj 0:aa965d6b1f8f 153
el13cj 4:3bcda7deb098 154 int PCA9685::convert_pwm_value(float pulse_width_us, float period_us)
el13cj 4:3bcda7deb098 155 {
el13cj 0:aa965d6b1f8f 156
el13cj 0:aa965d6b1f8f 157 int result;
el13cj 0:aa965d6b1f8f 158 float interim;
el13cj 4:3bcda7deb098 159
el13cj 0:aa965d6b1f8f 160 interim = ((pulse_width_us / period_us) * 4095); //scale the pulse width to a 12-bit scale
el13cj 0:aa965d6b1f8f 161 result = (int) interim; //round the value to the nearest integer
el13cj 4:3bcda7deb098 162
el13cj 0:aa965d6b1f8f 163 return result;
el13cj 4:3bcda7deb098 164
el13cj 4:3bcda7deb098 165 }
el13cj 2:8017239aa374 166
el13cj 2:8017239aa374 167 #endif