Docs

Micro-controllers, wireless transmission and database

Head

PCA9685 with Blue Pill using STM32CubeIDE

Prerequisites

This project assumes you have already installed STM32CubeIDE. You need to have previously done a basic blink sketch with blue-pill using STM32CubeIDE. I have made a complete video from installing STM32CubeIDE to LED blink program. You can watch it by clicking this link. https://www.youtube.com/watch?v=kXg467nVd_A

Wiring Diagram

Diagram

STM32CubeIDE Settings

Click connectivity --> Click I2C1

For I2C select I2C

Additional code on top of STM32CubeIDE generated code

/* USER CODE BEGIN 0 */
#define PCA9685_ADDRESS 0x80
// Datasheet link --> https://cdn-shop.adafruit.com/datasheets/PCA9685.pdf
#define PCA9685_MODE1         0x0         // as in the datasheet page no 10/52
#define PCA9685_PRE_SCALE     0xFE        // as in the datasheet page no 13/52
#define PCA9685_LED0_ON_L     0x6         // as in the datasheet page no 10/52
#define PCA9685_MODE1_SLEEP_BIT      4    // as in the datasheet page no 14/52
#define PCA9685_MODE1_AI_BIT         5    // as in the datasheet page no 14/52
#define PCA9685_MODE1_RESTART_BIT    7    // as in the datasheet page no 14/52

void PCA9685_SetBit(uint8_t Register, uint8_t Bit, uint8_t Value)
{
  uint8_t readValue;
  // Read all 8 bits and set only one bit to 0/1 and write all 8 bits back
  HAL_I2C_Mem_Read(&hi2c1, PCA9685_ADDRESS, Register, 1, &readValue, 1, 10);
  if (Value == 0) readValue &= ~(1 << Bit);
  else readValue |= (1 << Bit);
  HAL_I2C_Mem_Write(&hi2c1, PCA9685_ADDRESS, Register, 1, &readValue, 1, 10);
  HAL_Delay(1);
}

void PCA9685_SetPWMFrequency(uint16_t frequency)
{
  uint8_t prescale;
  if(frequency >= 1526) prescale = 0x03;
  else if(frequency <= 24) prescale = 0xFF;
  //  internal 25 MHz oscillator as in the datasheet page no 1/52
  else prescale = 25000000 / (4096 * frequency);
  // prescale changes 3 to 255 for 1526Hz to 24Hz as in the datasheet page no 1/52
  PCA9685_SetBit(PCA9685_MODE1, PCA9685_MODE1_SLEEP_BIT, 1);
  HAL_I2C_Mem_Write(&hi2c1, PCA9685_ADDRESS, PCA9685_PRE_SCALE, 1, &prescale, 1, 10);
  PCA9685_SetBit(PCA9685_MODE1, PCA9685_MODE1_SLEEP_BIT, 0);
  PCA9685_SetBit(PCA9685_MODE1, PCA9685_MODE1_RESTART_BIT, 1);
}

void PCA9685_Init(uint16_t frequency)
{
  PCA9685_SetPWMFrequency(frequency); // 50 Hz for servo
  PCA9685_SetBit(PCA9685_MODE1, PCA9685_MODE1_AI_BIT, 1);
}

void PCA9685_SetPWM(uint8_t Channel, uint16_t OnTime, uint16_t OffTime)
{
  uint8_t registerAddress;
  uint8_t pwm[4];
  registerAddress = PCA9685_LED0_ON_L + (4 * Channel);
  // See example 1 in the datasheet page no 18/52
  pwm[0] = OnTime & 0xFF;
  pwm[1] = OnTime>>8;
  pwm[2] = OffTime & 0xFF;
  pwm[3] = OffTime>>8;
  HAL_I2C_Mem_Write(&hi2c1, PCA9685_ADDRESS, registerAddress, 1, pwm, 4, 10);
}

void PCA9685_SetServoAngle(uint8_t Channel, float Angle)
{
  float Value;
  // 50 Hz servo then 4095 Value --> 20 milliseconds
  // 0 degree --> 0.5 ms(102.4 Value) and 180 degree --> 2.5 ms(511.9 Value)
  Value = (Angle * (511.9 - 102.4) / 180.0) + 102.4;
  PCA9685_SetPWM(Channel, 0, (uint16_t)Value);
}
/* USER CODE END 0 */

  /* USER CODE BEGIN 2 */
  PCA9685_Init(50); // 50Hz for servo
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      PCA9685_SetServoAngle(5, 0);
      PCA9685_SetServoAngle(7, 36);
      PCA9685_SetServoAngle(9, 72);
      PCA9685_SetServoAngle(11, 108);
      PCA9685_SetServoAngle(13, 144);
      PCA9685_SetServoAngle(15, 180);
      HAL_Delay(1000);

      PCA9685_SetServoAngle(5, 36);
      PCA9685_SetServoAngle(7, 72);
      PCA9685_SetServoAngle(9, 108);
      PCA9685_SetServoAngle(11, 144);
      PCA9685_SetServoAngle(13, 180);
      PCA9685_SetServoAngle(15, 0);
      HAL_Delay(1000);

      PCA9685_SetServoAngle(5, 72);
      PCA9685_SetServoAngle(7, 108);
      PCA9685_SetServoAngle(9, 144);
      PCA9685_SetServoAngle(11, 180);
      PCA9685_SetServoAngle(13, 0);
      PCA9685_SetServoAngle(15, 36);
      HAL_Delay(1000);

      PCA9685_SetServoAngle(5, 108);
      PCA9685_SetServoAngle(7, 144);
      PCA9685_SetServoAngle(9, 180);
      PCA9685_SetServoAngle(11, 0);
      PCA9685_SetServoAngle(13, 36);
      PCA9685_SetServoAngle(15, 72);
      HAL_Delay(1000);

      PCA9685_SetServoAngle(5, 144);
      PCA9685_SetServoAngle(7, 180);
      PCA9685_SetServoAngle(9, 0);
      PCA9685_SetServoAngle(11, 36);
      PCA9685_SetServoAngle(13, 72);
      PCA9685_SetServoAngle(15, 108);
      HAL_Delay(1000);

      PCA9685_SetServoAngle(5, 180);
      PCA9685_SetServoAngle(7, 0);
      PCA9685_SetServoAngle(9, 36);
      PCA9685_SetServoAngle(11, 72);
      PCA9685_SetServoAngle(13, 108);
      PCA9685_SetServoAngle(15, 144);
      HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */