PCA9685 I2C communication problem
Posted: Thu Jan 25, 2018 8:04 pm
hey guys,
I try to communicate with a PCA9685 16 bit PWM LED driver chip. I've read the docs, datasheet, even Arduino libs how they do it, everything. I'm using an ESP32.
Communication with other I2C devices is no problem, but this one drives me crazy
Resetting the chip is no problem, but setting the settings and outputs. The problem is the command of sending a '0x00' to adress '0x00'. After this, no matter what I send, is received as 0. I've already hooked up another device which only should read the data of the I2C bus, and it tells me, surprise: 0
I'm guessing this is because the ACK_VAL is the same as the value I want to transmit. But I have to transmit it
I'll get the same error/problem if I want to turn on/off the LEDs, because turning on the first LEDs is setting the LED0_ON_x to 4096 and LED0_OFF_x to 0 . This results into a transmission sequence of the following: 0x6, 0x0, 0x10, 0x0, 0x0
Now, please it's your turn for any suggestions
I try to communicate with a PCA9685 16 bit PWM LED driver chip. I've read the docs, datasheet, even Arduino libs how they do it, everything. I'm using an ESP32.
Communication with other I2C devices is no problem, but this one drives me crazy
Resetting the chip is no problem, but setting the settings and outputs. The problem is the command of sending a '0x00' to adress '0x00'. After this, no matter what I send, is received as 0. I've already hooked up another device which only should read the data of the I2C bus, and it tells me, surprise: 0
I'm guessing this is because the ACK_VAL is the same as the value I want to transmit. But I have to transmit it
I'll get the same error/problem if I want to turn on/off the LEDs, because turning on the first LEDs is setting the LED0_ON_x to 4096 and LED0_OFF_x to 0 . This results into a transmission sequence of the following: 0x6, 0x0, 0x10, 0x0, 0x0
Now, please it's your turn for any suggestions
Code: Select all
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
#define PCA9685_ADDR 0x40 /*!< slave address for PCA9685, offset definable with solder pads */
#define MODE1 0x00 // Mode register 1
#define MODE2 0x01 // Mode register 2
#define SUBADR1 0x02 // I2C-bus subaddress 1
#define SUBADR2 0x03 // I2C-bus subaddress 2
#define SUBADR3 0x04 // I2C-bus subaddress 3
#define ALLCALLADR 0x05 // LED All Call I2C-bus address
#define LED0 0x6 // LED0 start register
#define LED0_ON_L 0x6 // LED0 output and brightness control byte 0
#define LED0_ON_H 0x7 // LED0 output and brightness control byte 1
#define LED0_OFF_L 0x8 // LED0 output and brightness control byte 2
#define LED0_OFF_H 0x9 // LED0 output and brightness control byte 3
#define LED_MULTIPLYER 4 // For the other 15 channels
#define ALLLED_ON_L 0xFA // load all the LEDn_ON registers, byte 0 (turn 0-7 channels on)
#define ALLLED_ON_H 0xFB // load all the LEDn_ON registers, byte 1 (turn 8-15 channels on)
#define ALLLED_OFF_L 0xFC // load all the LEDn_OFF registers, byte 0 (turn 0-7 channels off)
#define ALLLED_OFF_H 0xFD // load all the LEDn_OFF registers, byte 1 (turn 8-15 channels off)
#define PRE_SCALE 0xFE // prescaler for output frequency
#define CLOCK_FREQ 25000000.0 // 25MHz default osc clock
static esp_err_t set_external_pwm_freq(uint16_t freq_hz)
{
esp_err_t ret;
/*
// https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/blob/master/Adafruit_PWMServoDriver.cpp
Adafruit Code result:
STARTING RESETTING CHIP
Resetting chip by writing to address: 0x00, value: 0x80
FINISHED RESETTING CHIP
STARTING SETTING FREUQENCY
Attempting to set freq 1000.00
Estimated pre-scale: 5.782
Final pre-scale: 6
Reading oldmode at address: 0x00, value: 0x00
Writing newmode to address: 0x00, value: 0x10
Writing prescaleval to address: 0xfe, value: 0x06
Writing oldmode to address: 0x00, value: 0x00
Writing oldmode | 0xa0 to address: 0x00, value: 0xa0
Mode now 0x00
FINISHED SETTING FREUQENCY
Turning on led 0
Setting PWM 0: 4096->0
Turning off led 0
Setting PWM 0: 0->4096
...
*/
uint8_t oldMode = 0x0;
uint8_t newMode;
uint8_t readBackMode = 0;
float tmpPrescale = (CLOCK_FREQ / 4096 / freq_hz);
uint8_t prescale_val = floor(tmpPrescale);
printf("XXXX_1: Prescale value %d (%.3f) (should be 6@1000 Hz)\n", prescale_val, tmpPrescale);
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
// request at register "MODE1", and read result into "oldMode"
// i2c_master_write_byte(cmd, PCA9685_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
// i2c_master_write_byte(cmd, MODE1, ACK_CHECK_EN); // select register
// i2c_master_write_byte(cmd, PCA9685_ADDR << 1 | READ_BIT, ACK_CHECK_EN);
// i2c_master_read_byte(cmd, oldMode, NACK_VAL);
// printf("XXXX_2: old mode: 0x%02x\n (should be 0x00)", oldMode);
newMode = (oldMode & 0x7F) | 0x10; // sleep
i2c_master_write_byte(cmd, PCA9685_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
// reset command to PCA9685 again
i2c_master_write_byte(cmd, MODE1, ACK_CHECK_EN); // select register
i2c_master_write_byte(cmd, 0x80, ACK_CHECK_EN); // restart cmd
// write new mode
printf("XXXX_4: write new mode: 0x%02x (should be 0x10)\n", newMode);
i2c_master_write_byte(cmd, MODE1, ACK_CHECK_EN); // select register
i2c_master_write_byte(cmd, 0x10, ACK_CHECK_EN); // set value (sleep)
// write prescaler
printf("XXXX_5: write prescaler: 0x%02x (should be 0x06)\n", prescale_val);
i2c_master_write_byte(cmd, PRE_SCALE, ACK_CHECK_EN); // select register
i2c_master_write_byte(cmd, 0x06, ACK_CHECK_EN); // multiplyer for PWM frequency
// [b]write old mode[/b]
printf("XXXX_6: write oldMode: 0x%02x (should be 0x00)\n", oldMode);
// uint8_t myData[2] = {MODE1, 0x0};
// i2c_master_write(cmd, myData, 2, ACK_CHECK_EN);
i2c_master_write_byte(cmd, MODE1, ACK_CHECK_EN); // select register
i2c_master_write_byte(cmd, 0x0, ACK_CHECK_EN); // set value (sleep)
printf("XXXX_7: Pause 5ms\n");
vTaskDelay(5 / portTICK_RATE_MS);
// re-wirte (oldMode | 0xa0)
printf("XXXX_8: write oldMode | 0xa0: 0x%02x (should be 0xa0)\n", oldMode|0xa0);
i2c_master_write_byte(cmd, MODE1, ACK_CHECK_EN); // select register
i2c_master_write_byte(cmd, 0xa0, ACK_CHECK_EN); // set value (sleep)
// i2c_master_write_byte(cmd, PCA9685_ADDR << 1 | READ_BIT, ACK_CHECK_EN);
// i2c_master_read_byte(cmd, readBackMode, NACK_VAL);
// printf("XXXX_9: read back mode now: 0x%02x\n", readBackMode);
// not used here
// i2c_master_write_byte(cmd, MODE2, ACK_CHECK_EN); // select register
// i2c_master_write_byte(cmd, 0x04, ACK_CHECK_EN); // totem pole (default)
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_EXAMPLE_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
printf("Finished setting external PWM\n");
return ret;
}
static esp_err_t led_manual(void)
{
esp_err_t ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, PCA9685_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
// led 1
i2c_master_write_byte(cmd, 0x6, ACK_CHECK_EN); // select register
i2c_master_write_byte(cmd, 0x0, ACK_CHECK_EN);
i2c_master_write_byte(cmd, 0x10, ACK_CHECK_EN); // turn
i2c_master_write_byte(cmd, 0x0, ACK_CHECK_EN);
i2c_master_write_byte(cmd, 0x0, ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_EXAMPLE_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}