Psoc and I2C with PCA9685

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
pamac_1655886
Level 2
Level 2
First like received

I am trying to port the library provided by Adafruit (https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library)  to use it in my project. 

   

This library is intended to be used with PCA9685 chip, which provides 16 pwm outputs by the use of the I2C protocol. 

   

I already have a board from adafruit and tested with an arduino and their library, it works fine. I tried to do the same with psoc but I guess I don't ported it well because it is not working (nothing happens when I try to make a servo to work.

   

I attach you the project so you can check my code and give me some ideas. I am using the first address (6 different modules can be added. The first one is 0x60, it is wrong in my project but result is the same fixing it).

0 Likes
19 Replies
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

At first: set your I2C component to Byte mode or your target gets bad data.

   

You are omitting the MasterSendStop() API.

   

Byte I2C interface is quite simple: After setting up the component (no Enable() needed) and starting it you use

   

    I2C_MasterSendStart(DeviceAddress,I2C_WRITE_XFER_MODE);    // Initialize a transaction for writing
    I2C_MasterWriteByte(Register);                // Indicate which register you want to write to
    I2C_MasterWriteByte(Value);                // Write to register
    I2C_MasterSendStop();                    // End of transaction

   

When you want to read from a device you use (example for reading two bytes

   

    I2C_MasterSendStart(DeviceAddress,I2C_WRITE_XFER_MODE);    // Initialize a transaction for writing
    I2C_MasterWrite(Register);                // Indicate which register you want to write to
    I2C_MasterSendRestart(DeviceAddress,I2C_READ_XFER_MODE);
    I2C_MasterReadByte(I2C_ACK_DATA);            // Read from register
    I2C_MasterReadByte(I2C_NAK_DATA);            // Read from register, last byte is NAKed
    I2C_MasterSendStop();                    // End of transaction

   

Not too difficult. Keep in mind that most of the APIs (except those for reading a byte) return a status byte which, when non-zero indicate an error condition, do not continue when an error occured. Use that status information at least while testing your connection.

   

 

   

Bob

0 Likes
pamac_1655886
Level 2
Level 2
First like received

Thank you, Bob. I will try and post what I find

0 Likes
lock attach
Attachments are accessible only for community members.
pamac_1655886
Level 2
Level 2
First like received

I have made some changes, define the words you use for write/read operations and tested a bit but it seems to hang as soon as the program go into:

   

PWM_begin()>PWM_reset()>PWM_write8()>MOTOR_I2CMasterWriteByte(d)(HERE PROGRAM STOPS):

   

 

   

void PWM_write8(uint8_t reg, uint8_t d) {
  MOTOR_I2CMasterSendStart(_addr, I2C_WRITE_XFER_MODE);
  MOTOR_I2CMasterWriteByte(reg);
  MOTOR_I2CMasterWriteByte(d);   <------ HERE STOPS
  MOTOR_I2CMasterSendStop();
}

   

 

   

I don't know if I should activate something else. I also changed to byte mode I2C block.

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Change your PWM_write8() to

   

uint8_t PWM_write8(uint8_t reg, uint8_t d)
{uint8_t Result;
    
    if((Result = MOTOR_I2CMasterSendStart(_addr, I2C_WRITE_XFER_MODE))) return Result;
    if((Result = MOTOR_I2CMasterWriteByte(reg))) return Result;
    if((Result = MOTOR_I2CMasterWriteByte(d))) return Result;
    if((Result = MOTOR_I2CMasterSendStop())) return Result;
    return Result;
}

   


and check the returned result for != 0. There are descriptions in the associated .h file for error reasons.

   

 

   

Bob

0 Likes

Ok, I tested. It returns at first if. I2CMasterSendStart with 0x02 response code. If I remove Pullup resistors, I get a 0x04 code. I guess: MOTOR_I2C_MSTR_ERR_LB_NAK and MOTOR_I2C_MSTAT_ERR_ARB_LOST

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Removing of the pullups will not work at all. Try reducing I2C speed to 100kbps. use 2.2k pullups.

   

First start address NAKed could mean wrong address. Do you set _addr??? Do you call  PWM_ServoDriver() ???

   

 

   

Bob

0 Likes

Yes, I call PWM_ServoDriver. First thing in my code. I could replicate the 0x02 error. I have just got 0x04 since I post my previous message. 

   

I modified data rate to 100 and set slew rate to "Slow". I found those parameters in the datasheet. But this IC also supports 400 and fast rate. 

   

 

   

I double checked datasheet to see if the address was ok. I think it is. I have all address entries to 0, so it should be 0x40.

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

I call PWM_ServoDriver. First thing in my code.   Quite sure? main() reads

   

    CyGlobalIntEnable; /* Enable global interrupts. */
    PWM_begin();
    PWM_ServoDriver();

   

And in PWM_begin() you get the device out of sleep mode but using a wrong I2C address.

   

 

   

Bob

0 Likes
pamac_1655886
Level 2
Level 2
First like received

Yes, I fixed it. I saw it also when you pointed about the address. I have been reworking the library a bit basing on libraries for python I found on the internet and now I get a first error of 0x02 (NACK?) and then 0x04 always.

   

I don't have time today to check something else. Tomorrow I will try to test something else and I will post my updates. I willt ry also setting up another pioneer psoc as slave with address 0x40 and see what happens. 

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Are you using a -042 BLE Pioneer board?

   

Use P0_4 and P0_5 for I2C and provide your own 2k2 pullup resistors. The ohter pins are connected to FRam with an address 0x42 which will collide with your chip or with an internal UART-USB bridge which will destroy your sda-line data.

   

 

   

Bob

0 Likes
pamac_1655886
Level 2
Level 2
First like received

Thank you bob. I got it working!. The board I was using (adafruit original) has 2 pullup resistors (10K) and that seems to be a bit too much for pionneer psoc board. I replaced them by 2.2k as you suggested and it started to work. As I said I tested with 4.7k jumping the 10k resistors but this value seems to be still too high.

   

I have to include the library in my project so I will change the code a bit to make it more readable (variable and function names) and also including some comments. After that I will update this topic with a copy of the project with a small example of use. Maybe it is useful for anyone who wants to test this IC.

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Congratulations!

   

 

   

Bob

0 Likes
pamac_1655886
Level 2
Level 2
First like received

Sorry for rescuing such an old post. I am still working on this, My company has had other priorities all this time. I am trying to finish it right now. 

   

 

   

We already have our final pcb board designed and prototyped. I am able to communicate to the first PCA9685 device (2 I2C slaves) with 0x40 address. 

   

It works while I don't try to communicate to the second one. As soon as I try this communication, neither the first one nor the second one works.

   

 

   

We are using the same core as my pioneer kit. CY8C4247LQI-BL483 56-QFN and pins we use from it are SCL P0[5] 25, and SDA P0[4] 24. 2.2K resistors for pull up.

   

 

   

Using 0x41 for the second device in I2C (The address we configured by its input pins) I get a fail in writeByte function. 0x02 MOTOR_I2C_MSTR_ERR_LB_NACK. If I set, for example, 0x42-0x45, I get a fail in SendStart function. also a 0x02. Can you give me any clue with this information? Or what should I check? 

   

Is there something related to close connection to one slave device before talking to another in I2C?

   

 

   

EDIT: I tried removing the call to the first I2C device. So the only one initialized and use is the seond one. I also get the 0x02 error in writeByte function.

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Is the call to MasterSendStart() returning an error? Or the (required to close a connection) the MasterSendStop?

   

What is the length of the signal cable to the second PCA9685?

   

 

   

Bob

0 Likes
pamac_1655886
Level 2
Level 2
First like received

The wire is very short. It is a smd pcb. We have tested with an osciloscope and sda and scl are reaching the second i2c device. Write8 function doesn' return any fail now when I do the initializacion. this is the code fot that:

   

 

   

   

void PWM_setAllPWM(uint8_t on, uint8_t off){
    uint8_t test_var = 0x00;
   
   test_var =  PWM_write8(ALLLED_ON_L,  on&0xff);
   test_var = PWM_write8(ALLLED_ON_H,  on>>8);
   test_var = PWM_write8(ALLLED_OFF_L, off&0xff);
   test_var = PWM_write8(ALLLED_OFF_H, off>>8);
   test_var = test_var;
}

   

void PWM_begin(void) {
 //uint8_t mode1;
 //MOTOR_Start();
 //MOTOR_Enable();
 //CyDelay(2000);
 PWM_setAllPWM(0,0);
 PWM_reset();
 PWM_setPWMFreq(FREQ);
 
}

   

void PWM_reset(void) {
 uint8_t test_var = 0x00;
 test_var = PWM_write8(PCA9685_MODE1, 0x00);
 test_var = PWM_write8(PCA9685_MODE2, 0x04);
 test_var = test_var;
 CyDelay(5);
}

   

   

 

   

Read&write function:

   

 

   

   

uint8_t PWM_write8(uint8_t reg, uint8_t d) {
  uint8_t result = 0x00;

   

 if((result = MOTOR_I2CMasterSendStart(_addr, I2C_WRITE_XFER_MODE)))
    return result;
 if((result = MOTOR_I2CMasterWriteByte(reg)))
    return result;
 if((result = MOTOR_I2CMasterWriteByte(d)))
    return result;
 if((result = MOTOR_I2CMasterSendStop()))
    return result;
 return result;   
}

   

uint8_t PWM_read8(uint8_t reg) {
  uint8_t response = 0x00;
  MOTOR_I2CMasterSendStart(_addr, I2C_WRITE_XFER_MODE);
  MOTOR_I2CMasterWriteByte(reg);
  MOTOR_I2CMasterSendRestart(_addr, I2C_READ_XFER_MODE);
  //MOTOR_I2CMasterSendStop();
  response =  MOTOR_I2CMasterReadByte(I2C_NAK_DATA);
  MOTOR_I2CMasterSendStop();

   

  return response;
}

   

   

 

   

The response in every test_var during initializacion of second i2c device is 0x00. 

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Just for de-bugging: In PWM_read8() check all the returned values. To make debugging easier I would suggest to call a routine when an error occurs and set a breakpoint there. So you will see when looking at the call stack where the error comes from.

   

Are you doing any interrupt driven accesses to I2C?

   

What I am missing from datasheet of  PCA chip: there is no read register sequence, only a "Read all registers using the Auto-Increment". Might be a problem?

   

I would suggest you to connect a logic analyzer to SDA and SCL, most are able to give a communication protocol that will help to isolate the problem.

   

 

   

Bob

0 Likes

I will try the logic analyzer and debug code. As soon as I get some answer with it I will post it. thank you

0 Likes
pamac_1655886
Level 2
Level 2
First like received

Well, after running some tests it seems that the PCA9685 had a couple of pads causing a short and thats why pwm couldn't work. Now I have both working without problems. Thank you Bob.

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

You are always welcome.

   

 

   

Bob

0 Likes