I'm trying to generate PWM signals for a stepper motor with a PCA9685 development board and an MCU (STM32F4). I'm able to write and read all commands to the chip via I2C — and verify that everything I send is transferred correctly. I'm using the internal oscillator of the chip.
Currently, the chip is configured to 50 Hz (Register 0xFE set to value 0x90). A logic analyzer shows a repetition frequency of 49.18 Hz. When I write values from 205 to 450 to the OFF register (address 0x08 and 0x09), I’m only getting a pulse width from 1.02 ms down to 0.7658 ms (yes the direction seems to decrease with increasing value). No matter how I adjust the OFF register value, I can't seem to achieve any pulse widths greater than ~1 ms (not 1.5 ms and not 2 ms).
Has anyone worked with the PCA9685 chip before? Do you have any recommendations on how to fix this or what to check?
Code Snipped of STM32F4:
int main(void)
{
//initialization
SystemClock_Config();
GPIO_Config();
//Reset PCA9685
I2C_config();
I2C_start();
I2C_addr(0x00);
I2C_write(0x06);
I2C_stop();
//Set Frequency
I2C_start();
I2C_addr(0x80);
I2C_write(0xFE);
I2C_write(0x90);
I2C_stop();
//Read current status
I2C_start();
I2C_addr(0x80);
I2C_write(0xFE);
I2C_start();
I2C_addr(0x80|1);
register_return_value = I2C_read(0);
I2C_stop();
//Read current status
I2C_start();
I2C_addr(0x80);
I2C_write(0x00);
I2C_start();
I2C_addr(0x80|1);
register_return_value = I2C_read(0);
I2C_stop();
//Write Value
I2C_start();
I2C_addr(0x80);
I2C_write(0x00);
I2C_write((register_return_value & ~(0x10)));
I2C_stop();
//Read current status
I2C_start();
I2C_addr(0x80);
I2C_write(0x00);
I2C_start();
I2C_addr(0x80|1);
register_return_value = I2C_read(0);
I2C_stop();
uint16_t pwm_value = 205;
//
I2C_start();
I2C_addr(0x80);
I2C_write(0x06);
I2C_write(0x00);
I2C_stop();
//
I2C_start();
I2C_addr(0x80);
I2C_write(0x07);
I2C_write(0x00);
I2C_stop();
//
I2C_start();
I2C_addr(0x80);
I2C_write(0x08);
I2C_write(0xFF & pwm_value);
I2C_stop();
//
I2C_start();
I2C_addr(0x80);
I2C_write(0x09);
I2C_write(0x0F & (pwm_value << 8));
I2C_stop();
}
-
\$\begingroup\$ Please add your full code! I know the PCA very well and will try to help but seeing everything you're doing would be very helpful. Also some O-Scope captures would be great. When you write a value higher than 405, does it just max out at ~1ms no matter how high you go or is there other behavior? \$\endgroup\$InBedded16– InBedded162025年07月09日 20:09:10 +00:00Commented Jul 9 at 20:09
-
\$\begingroup\$ The pulse width decreasing suggests you're configuring the registers incorrectly. Seeing the register values for registers 0x01, 0x02, and 0x06-0x09 would be what to check. \$\endgroup\$InBedded16– InBedded162025年07月09日 20:18:43 +00:00Commented Jul 9 at 20:18
-
\$\begingroup\$ When I input increasing values, I get a pulse width that first increases and then decreases, but it never exceeds 1.2 ms. I will add the code below. \$\endgroup\$ulix– ulix2025年07月09日 20:20:05 +00:00Commented Jul 9 at 20:20
-
\$\begingroup\$ @InBedded16 Registers 0x06 to 0x09 have the values I have configure them. 0x06=0x00; 0x07=0x00; 0x08=0xCD (for 205dec) and 0x09=0x00; \$\endgroup\$ulix– ulix2025年07月09日 20:27:02 +00:00Commented Jul 9 at 20:27
-
\$\begingroup\$ @InBedded16 sure you mean 0x01 and 0x02 not 0x00 and 0x01? either way: 0x01=0x04 and 0x02=0xe2; 0x00=0x01 \$\endgroup\$ulix– ulix2025年07月09日 20:29:39 +00:00Commented Jul 9 at 20:29
2 Answers 2
I2C_write(0x0F & (pwm_value << 8));
This line is shifting pwm_value in the wrong direction, so it will always write 0 to the high byte of the register. It should be:
I2C_write(0x0F & (pwm_value >> 8));
-
\$\begingroup\$ Thanks, just noticed that... should have noticed myself :) \$\endgroup\$ulix– ulix2025年07月10日 10:12:21 +00:00Commented Jul 10 at 10:12
The code writes PWM value incorrectly.
The register 8 is written correctly for the low byte, but register 9 gets always written as 0.
Basically after 255 the PWM value wraps back to 0 which would explain what you are experiencing.