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.
PCA9685.cpp@2:8017239aa374, 2016-04-07 (annotated)
- Committer:
- el13cj
- Date:
- Thu Apr 07 20:12:00 2016 +0000
- Revision:
- 2:8017239aa374
- Parent:
- 1:9d6633a308ba
- Child:
- 3:a3410f2f061d
Added header guards in cpp file
Who changed what in which revision?
User | Revision | Line number | New 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 | |
el13cj | 0:aa965d6b1f8f | 10 | PCA9685::PCA9685(uint8_t i2c_address, I2C i2c_object, float frequency) : i2c_addr(i2c_address), freq(frequency), i2c(i2c_object) |
el13cj | 0:aa965d6b1f8f | 11 | { |
el13cj | 0:aa965d6b1f8f | 12 | //int two = 1 + 1; |
el13cj | 0:aa965d6b1f8f | 13 | |
el13cj | 0:aa965d6b1f8f | 14 | } |
el13cj | 0:aa965d6b1f8f | 15 | |
el13cj | 0:aa965d6b1f8f | 16 | void PCA9685::init(void) { |
el13cj | 0:aa965d6b1f8f | 17 | |
el13cj | 0:aa965d6b1f8f | 18 | reset(); |
el13cj | 0:aa965d6b1f8f | 19 | |
el13cj | 1:9d6633a308ba | 20 | uint8_t prescale = (uint8_t) (OSC_CLOCK / (4096 * PWM_SCALER * freq)) - 1; |
el13cj | 0:aa965d6b1f8f | 21 | |
el13cj | 0:aa965d6b1f8f | 22 | write_8(MODE1, 0x21); //0010 0001 : AI ENABLED |
el13cj | 0:aa965d6b1f8f | 23 | write_8(MODE2, 0x07); //0000 0111 : NOT INVRT, CHANGE ON STOP, TOTEM POLE, \OE = 1, LEDn = HIGH IMP |
el13cj | 0:aa965d6b1f8f | 24 | |
el13cj | 0:aa965d6b1f8f | 25 | set_prescale(prescale); |
el13cj | 0:aa965d6b1f8f | 26 | |
el13cj | 0:aa965d6b1f8f | 27 | |
el13cj | 0:aa965d6b1f8f | 28 | |
el13cj | 0:aa965d6b1f8f | 29 | } |
el13cj | 0:aa965d6b1f8f | 30 | |
el13cj | 0:aa965d6b1f8f | 31 | void PCA9685::reset(void) { |
el13cj | 0:aa965d6b1f8f | 32 | |
el13cj | 0:aa965d6b1f8f | 33 | write_8(MODE1,0x00); |
el13cj | 0:aa965d6b1f8f | 34 | |
el13cj | 0:aa965d6b1f8f | 35 | } |
el13cj | 0:aa965d6b1f8f | 36 | |
el13cj | 0:aa965d6b1f8f | 37 | void PCA9685::set_prescale(uint8_t prescale) { |
el13cj | 0:aa965d6b1f8f | 38 | |
el13cj | 0:aa965d6b1f8f | 39 | uint8_t oldmode = read_8(MODE1); |
el13cj | 0:aa965d6b1f8f | 40 | uint8_t newmode = (oldmode&0x7F) | 0x10; // set the sleep bit |
el13cj | 0:aa965d6b1f8f | 41 | |
el13cj | 0:aa965d6b1f8f | 42 | write_8(MODE1, newmode); // send the device to sleep |
el13cj | 0:aa965d6b1f8f | 43 | wait_ms(5); |
el13cj | 0:aa965d6b1f8f | 44 | write_8(PRESCALE, prescale); // set the prescaler |
el13cj | 0:aa965d6b1f8f | 45 | write_8(MODE1, oldmode); |
el13cj | 0:aa965d6b1f8f | 46 | wait_ms(5); |
el13cj | 0:aa965d6b1f8f | 47 | write_8(MODE1, oldmode | 0xa1); // wake up the device |
el13cj | 0:aa965d6b1f8f | 48 | |
el13cj | 0:aa965d6b1f8f | 49 | } |
el13cj | 0:aa965d6b1f8f | 50 | |
el13cj | 0:aa965d6b1f8f | 51 | |
el13cj | 0:aa965d6b1f8f | 52 | //NB REQUIRES AUTO-INCREMENT MODE ENABLED |
el13cj | 0:aa965d6b1f8f | 53 | //0 <= pwm_output <= 15 |
el13cj | 0:aa965d6b1f8f | 54 | //0 <= (count_on || count_off) <= 4095 |
el13cj | 0:aa965d6b1f8f | 55 | void PCA9685::set_pwm_output(int pwm_output, uint16_t count_on, uint16_t count_off) { |
el13cj | 0:aa965d6b1f8f | 56 | |
el13cj | 0:aa965d6b1f8f | 57 | char msg[5]; |
el13cj | 0:aa965d6b1f8f | 58 | |
el13cj | 0:aa965d6b1f8f | 59 | msg[0] = LED0_ON_L + (4 * pwm_output); |
el13cj | 0:aa965d6b1f8f | 60 | msg[1] = count_on; |
el13cj | 0:aa965d6b1f8f | 61 | msg[2] = count_on >> 8; |
el13cj | 0:aa965d6b1f8f | 62 | msg[3] = count_off; |
el13cj | 0:aa965d6b1f8f | 63 | msg[4] = count_off >> 8; |
el13cj | 0:aa965d6b1f8f | 64 | |
el13cj | 0:aa965d6b1f8f | 65 | i2c.write(ADDR, msg, 5); |
el13cj | 0:aa965d6b1f8f | 66 | |
el13cj | 0:aa965d6b1f8f | 67 | } |
el13cj | 0:aa965d6b1f8f | 68 | |
el13cj | 0:aa965d6b1f8f | 69 | |
el13cj | 0:aa965d6b1f8f | 70 | //NB REQUIRES AUTO-INCREMENT MODE ENABLED |
el13cj | 0:aa965d6b1f8f | 71 | //0 <= pwm_output <= 15 |
el13cj | 0:aa965d6b1f8f | 72 | void PCA9685::set_pwm_duty(int pwm_output, float duty_cycle) { |
el13cj | 0:aa965d6b1f8f | 73 | |
el13cj | 0:aa965d6b1f8f | 74 | if (duty_cycle > 1.0) { duty_cycle = 1.0; } |
el13cj | 0:aa965d6b1f8f | 75 | if (duty_cycle < 0.0) { duty_cycle = 0.0; } |
el13cj | 0:aa965d6b1f8f | 76 | |
el13cj | 0:aa965d6b1f8f | 77 | uint16_t count_off = (uint16_t) (duty_cycle * 4095); |
el13cj | 0:aa965d6b1f8f | 78 | uint16_t count_on = 0x0000; |
el13cj | 0:aa965d6b1f8f | 79 | |
el13cj | 0:aa965d6b1f8f | 80 | set_pwm_output(pwm_output, count_on, count_off); |
el13cj | 0:aa965d6b1f8f | 81 | |
el13cj | 0:aa965d6b1f8f | 82 | } |
el13cj | 0:aa965d6b1f8f | 83 | |
el13cj | 0:aa965d6b1f8f | 84 | |
el13cj | 0:aa965d6b1f8f | 85 | //NB REQUIRES AUTO-INCREMENT MODE ENABLED |
el13cj | 0:aa965d6b1f8f | 86 | //0 <= pwm_output <= 15 |
el13cj | 0:aa965d6b1f8f | 87 | void PCA9685::set_pwm_pw(int pwm_output, float pulse_width_us) { |
el13cj | 0:aa965d6b1f8f | 88 | |
el13cj | 0:aa965d6b1f8f | 89 | float period_us = (1e6/freq); |
el13cj | 0:aa965d6b1f8f | 90 | |
el13cj | 0:aa965d6b1f8f | 91 | float duty = pulse_width_us/period_us; |
el13cj | 0:aa965d6b1f8f | 92 | |
el13cj | 0:aa965d6b1f8f | 93 | set_pwm_duty(pwm_output, duty); |
el13cj | 0:aa965d6b1f8f | 94 | |
el13cj | 0:aa965d6b1f8f | 95 | } |
el13cj | 0:aa965d6b1f8f | 96 | |
el13cj | 0:aa965d6b1f8f | 97 | |
el13cj | 0:aa965d6b1f8f | 98 | void PCA9685::update(void) { |
el13cj | 0:aa965d6b1f8f | 99 | |
el13cj | 0:aa965d6b1f8f | 100 | i2c.stop(); |
el13cj | 0:aa965d6b1f8f | 101 | |
el13cj | 0:aa965d6b1f8f | 102 | } |
el13cj | 0:aa965d6b1f8f | 103 | |
el13cj | 0:aa965d6b1f8f | 104 | |
el13cj | 0:aa965d6b1f8f | 105 | |
el13cj | 0:aa965d6b1f8f | 106 | void PCA9685::write_8(uint8_t reg, uint8_t msg) { |
el13cj | 0:aa965d6b1f8f | 107 | |
el13cj | 0:aa965d6b1f8f | 108 | char send[2]; //Store the address and data in an array |
el13cj | 0:aa965d6b1f8f | 109 | send[0] = reg; |
el13cj | 0:aa965d6b1f8f | 110 | send[1] = msg; |
el13cj | 0:aa965d6b1f8f | 111 | i2c.write(i2c_addr, send, 2); |
el13cj | 0:aa965d6b1f8f | 112 | |
el13cj | 0:aa965d6b1f8f | 113 | } |
el13cj | 0:aa965d6b1f8f | 114 | |
el13cj | 0:aa965d6b1f8f | 115 | uint8_t PCA9685::read_8(uint8_t reg) { |
el13cj | 0:aa965d6b1f8f | 116 | |
el13cj | 0:aa965d6b1f8f | 117 | char send[1] ; |
el13cj | 0:aa965d6b1f8f | 118 | send[0] = reg; |
el13cj | 0:aa965d6b1f8f | 119 | i2c.write(i2c_addr, send, 1); |
el13cj | 0:aa965d6b1f8f | 120 | char recieve[1]; |
el13cj | 0:aa965d6b1f8f | 121 | i2c.read(i2c_addr, recieve, 1); |
el13cj | 0:aa965d6b1f8f | 122 | return recieve[0]; |
el13cj | 0:aa965d6b1f8f | 123 | |
el13cj | 0:aa965d6b1f8f | 124 | } |
el13cj | 0:aa965d6b1f8f | 125 | |
el13cj | 0:aa965d6b1f8f | 126 | int PCA9685::convert_pwm_value(float pulse_width_us, float period_us) { |
el13cj | 0:aa965d6b1f8f | 127 | |
el13cj | 0:aa965d6b1f8f | 128 | int result; |
el13cj | 0:aa965d6b1f8f | 129 | float interim; |
el13cj | 0:aa965d6b1f8f | 130 | |
el13cj | 0:aa965d6b1f8f | 131 | interim = ((pulse_width_us / period_us) * 4095); //scale the pulse width to a 12-bit scale |
el13cj | 0:aa965d6b1f8f | 132 | result = (int) interim; //round the value to the nearest integer |
el13cj | 0:aa965d6b1f8f | 133 | |
el13cj | 0:aa965d6b1f8f | 134 | return result; |
el13cj | 0:aa965d6b1f8f | 135 | |
el13cj | 2:8017239aa374 | 136 | } |
el13cj | 2:8017239aa374 | 137 | |
el13cj | 2:8017239aa374 | 138 | #endif |