Hi Guys,
I am struggling to get a PCA9685 I2C PWM module to work on a Nucleo_F446ZE board - my first venture into I2C in this world, and the PCA9685 is new to me. Using MBed Studio.
I did get the servo working using PWMOut, so I think servo and board are working okay. The PCA9685 module is untested but I have no reason to think it is faulty.
As this is a learning exercise for me I am doing it ‘on-the-chip’, ‘straight line’ and not invoking any library code classes, but I have cut and pasted from a few classes on this website to help me with learning - Big thanks to Paulo Sanna
I am fairly sure I have the hookup, addressing and I2C setup working as the I2C write()s are returning 0. I did try an invalid I2C address and it hung on the write(), so I think the issue is in what I am actually writing to the module, and not the basic setup.
In the config of the chip it seemed to me I only needed to be concerned about the MODE1 register (Ox00) and the PRE_SCALE register (0xFE).
The configuration seems to me reasonably straightforward and I think I understand most of it, except the sections on sleep mode and restart where I am not 100% comfortable. I am also not sure if the auto increment bit needs to be set for the multi-byte write() to function properly. I suspect not but I set it anyway.
When I run it nothing happens - no servo sweep at all.
Any tips in diagnosing the issues or help as to what could be wrong would be appreciated.
Thanks - Peter
Diagnostic output with notes
====================start================
==Reset==
Write (hex values): 0 0 Write returned: 0
==set PWM==
Write for Read: 0 Write() returned: 0
Read() returned: 0 Value= 0
Write (hex values): 0 10 Write returned: 0 <Set Sleep bit 4>
Sleep 5 ms
Write (hex values): fe 79 Write returned: 0 <Set PRE_SCALE to 121>
Write (hex values): 0 0 Write returned: 0
Sleep 5 ms
Write (hex values): 0 a1 Write returned: 0 <Set restart, AI, All Call>
==Loop== No 1
Write 5 (dec values): 6 0 0 205 0 Write() returned: 0 <Servo 0 on 0 Off 205>
Write 5 (dec values): 6 0 0 154 1 Write() returned: 0 <Servo 0 on 0 off 410>
==Loop== No 2
Write 5 (dec values): 6 0 0 205 0 Write() returned: 0
Write 5 (dec values): 6 0 0 154 1 Write() returned: 0
==Loop== No 3
Write 5 (dec values): 6 0 0 205 0 Write() returned: 0
Write 5 (dec values): 6 0 0 154 1 Write() returned: 0
==Loop== No 4
Write 5 (dec values): 6 0 0 205 0 Write() returned: 0
Write 5 (dec values): 6 0 0 154 1 Write() returned: 0
Code
// test PCA9685 module with servo
#include "mbed.h"
#define PCA9685_MODE1 0x0
#define PCA9685_PRESCALE 0xFE
#define TIMING_BLOCK_START 0x06
int addr8bit = 0x40 << 1; // default 8-bit I2C address, 0x80
uint8_t retVal ;
I2C i2c(I2C_SDA , I2C_SCL); // create an I2C object using specified pins
void write8(uint8_t address, uint8_t data)
{
char cmd[2];
cmd[0] = address;
cmd[1] = data;
printf ("Write (hex values): %x %x ", cmd[0], cmd[1]) ;
retVal = i2c.write(addr8bit, cmd, 2);
printf (" Write returned: %d \n", retVal) ;
}
char read8(char address)
{
printf ("Write for Read: %x ", address) ;
retVal = i2c.write(addr8bit, &address, 1);
printf (" Write() returned: %d \n", retVal) ;
char rtn;
retVal = i2c.read(addr8bit, &rtn, 1);
printf ("Read() returned: %x Value= %x \n", retVal, rtn) ;
return rtn;
}
void reset(void)
{
write8(PCA9685_MODE1, 0x0);
}
void setPrescale(uint8_t prescale)
{
uint8_t oldmode = read8(PCA9685_MODE1);
uint8_t newmode = (oldmode & 0x7F) | 0x10; // sleep
write8(PCA9685_MODE1, newmode); // go to sleep
printf ("Sleep 5 ms \n") ;
ThisThread::sleep_for(chrono::milliseconds(5)) ;
write8(PCA9685_PRESCALE, prescale); // set the prescaler
write8(PCA9685_MODE1, oldmode);
printf ("Sleep 5 ms \n") ;
ThisThread::sleep_for(chrono::milliseconds(5)) ;
write8(PCA9685_MODE1, oldmode | 0xa1);
}
void setPWMFreq(float freq)
{
float prescaleval = 25000000;
prescaleval /= 4096;
prescaleval /= freq;
uint8_t prescale = floor(prescaleval + 0.5) - 1;
setPrescale(prescale);
}
// function to set servo number (0-15) on and off count values
// Range 0-4095 count for 1 ms = 205 count for 2 ms = 410
void setServoOnOff(uint8_t num, uint16_t on, uint16_t off)
{
char cmd[5] ; //
cmd[0] = TIMING_BLOCK_START + (4 * num) ; // address of the low byte of servo
cmd[1] = on; // low byte of on value
cmd[2] = (on >> 8); // high byte of on
cmd[3] = off; // low byte of off
cmd[4] = (off >> 8); // high byte of off
// note: passing cmd with no index passes an array pointer - required
printf ("Write 5 (dec values): %d %d %d %d %d ", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]) ;
retVal = i2c.write(addr8bit, cmd, 5); // write the address and 4 bytes
printf ("Write() returned: %d \n", retVal) ;
}
int main()
{
printf("====================start================ \n") ;
i2c.frequency(400000) ; // set the bus frequency
printf("==Reset== \n") ;
reset() ;
printf("==set PWM== \n") ;
setPWMFreq(50.0) ;
uint8_t loopCount = 1 ;
while (true)
{
printf("==Loop== No %d \n", loopCount ) ;
setServoOnOff(0, 0, 205) ;
ThisThread::sleep_for(chrono::milliseconds(300)) ; // let servo move
setServoOnOff(0, 0, 410) ;
ThisThread::sleep_for(chrono::milliseconds(300)) ; // let servo move
loopCount ++ ;
if (loopCount > 4)
return 0 ;
}
}