From 1251be5115f54138f1f17e6d2494dfbbd11e21e5 Mon Sep 17 00:00:00 2001 From: acki Date: Sat, 13 Dec 2025 18:33:40 +0100 Subject: [PATCH] POC multiple PCA9685 (use AI btw) --- Core/Inc/actio.h | 2 - Core/Src/actio.c | 113 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 80 insertions(+), 35 deletions(-) diff --git a/Core/Inc/actio.h b/Core/Inc/actio.h index 5de9a17..1f2fcf7 100644 --- a/Core/Inc/actio.h +++ b/Core/Inc/actio.h @@ -5,7 +5,6 @@ #include #define NOMBRE_SERVOS 16 -#define NOMBRE_POSITIONS_SERVOS 8 #define NOMBRE_POSITIONS_ASC 8 #define NOMBRE_RELAIS 3 @@ -23,7 +22,6 @@ typedef struct Actionneurs { bool enJeu; uint16_t armementTirette; uint16_t angleServo[NOMBRE_SERVOS]; - // uint16_t valeursPositionsServos[NOMBRE_SERVOS][NOMBRE_POSITIONS_SERVOS]; uint16_t positionAscenseur; uint16_t valeursPositionsAscenseur[NOMBRE_POSITIONS_ASC]; GPIO_TypeDef * relayPorts[NOMBRE_RELAIS]; diff --git a/Core/Src/actio.c b/Core/Src/actio.c index 83fa690..fb26692 100644 --- a/Core/Src/actio.c +++ b/Core/Src/actio.c @@ -3,10 +3,20 @@ #include "actio.h" +#include + /* ######################## PARTIE PCA9685 SERVO ########################### */ -#define PCA9685_ADDRESS 0x80 +#define PCA9685_COUNT 2 // number of boards +#define PCA9685_BASE_ADDR 0x40 // A0=A1=A2=0 +#define PCA9685_SERVOS_PER_BOARD 16 +#define PCA9685_TOTAL_SERVOS (PCA9685_COUNT * PCA9685_SERVOS_PER_BOARD) + +#define PCA9685_PWM_FREQ 50 // Hz +#define SERVO_MIN_PULSE 102 // 0.5 ms +#define SERVO_MAX_PULSE 512 // 2.5 ms + // 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 @@ -16,28 +26,38 @@ #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); -void PCA9685_SetPWMFrequency(uint16_t frequency); -void PCA9685_Init(uint16_t frequency); -void PCA9685_SetPWM(uint8_t Channel, uint16_t OnTime, uint16_t OffTime); +void PCA9685_SetBit(uint8_t Address, uint8_t Register, uint8_t Bit, uint8_t Value); +void PCA9685_SetPWMFrequency(uint8_t Address, uint16_t frequency); +void PCA9685_Init(uint8_t Address, uint16_t frequency); +void PCA9685_InitAllBoards(uint16_t frequency); +void PCA9685_SetPWM(uint8_t Address, uint8_t Channel, uint16_t OnTime, uint16_t OffTime); void PCA9685_SetServoAngle(uint8_t Channel, float Angle); -void PCA9685_ContinuousServoRun(uint8_t Channel); -void PCA9685_ContinuousServoStop(uint8_t Channel); +void PCA9685_ContinuousServoRun(uint8_t Channel); // 360 deg servo +void PCA9685_ContinuousServoStop(uint8_t Channel); // 360 deg servo extern I2C_HandleTypeDef hi2c1; -void PCA9685_SetBit(uint8_t Register, uint8_t Bit, uint8_t Value) +static inline void servo_to_pca(uint16_t servo, + uint8_t *addr, + uint8_t *channel) +{ + uint8_t board = servo / PCA9685_SERVOS_PER_BOARD; + *channel = servo % PCA9685_SERVOS_PER_BOARD; + *addr = PCA9685_BASE_ADDR + board; +} + +void PCA9685_SetBit(uint8_t Address, 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); + HAL_I2C_Mem_Read(&hi2c1, Address << 1, 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_I2C_Mem_Write(&hi2c1, Address << 1, Register, 1, &readValue, 1, 10); HAL_Delay(1); } -void PCA9685_SetPWMFrequency(uint16_t frequency) +void PCA9685_SetPWMFrequency(uint8_t Address, uint16_t frequency) { uint8_t prescale; if(frequency >= 1526) prescale = 0x03; @@ -45,59 +65,86 @@ void PCA9685_SetPWMFrequency(uint16_t frequency) // 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); + PCA9685_SetBit(Address, PCA9685_MODE1, PCA9685_MODE1_SLEEP_BIT, 1); + HAL_I2C_Mem_Write(&hi2c1, Address, PCA9685_PRE_SCALE, 1, &prescale, 1, 10); + PCA9685_SetBit(Address, PCA9685_MODE1, PCA9685_MODE1_SLEEP_BIT, 0); + PCA9685_SetBit(Address, PCA9685_MODE1, PCA9685_MODE1_RESTART_BIT, 1); } -void PCA9685_Init(uint16_t frequency) +void PCA9685_Init(uint8_t Address, uint16_t frequency) { - PCA9685_SetPWMFrequency(frequency); // 50 Hz for servo - PCA9685_SetBit(PCA9685_MODE1, PCA9685_MODE1_AI_BIT, 1); + PCA9685_SetPWMFrequency(Address, frequency); // 50 Hz for servo + PCA9685_SetBit(Address, PCA9685_MODE1, PCA9685_MODE1_AI_BIT, 1); } -void PCA9685_SetPWM(uint8_t Channel, uint16_t OnTime, uint16_t OffTime) +void PCA9685_InitAllBoards(uint16_t frequency) +{ + for (uint8_t i = 0; i < PCA9685_COUNT; i++) { + PCA9685_Init(PCA9685_BASE_ADDR + i, frequency); + } +} + +void PCA9685_SetPWM(uint8_t Address, 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); + uint8_t pwm[4] = { + OnTime & 0xFF, + OnTime >> 8, + OffTime & 0xFF, + OffTime >> 8 + }; + HAL_I2C_Mem_Write(&hi2c1, Address << 1, registerAddress, 1, pwm, 4, 10); } void PCA9685_SetServoAngle(uint8_t Channel, float Angle) { - float Value; // 50 Hz servo then 4095 Value --> 20 milliseconds // Centieme de radians // 0 rad --> 0.5 ms(102.4 Value) and 2 PI radians --> 2.5 ms(511.9 Value) - Value = (Angle * (511.9 - 102.4) / (/*100 **/ 3.141592)) + 102.4; - PCA9685_SetPWM(Channel, 0, (uint16_t)Value); + uint8_t addr, ch; + servo_to_pca(Channel, &addr, &ch); + + float pwm = (Angle * (SERVO_MAX_PULSE - SERVO_MIN_PULSE) + / (float)M_PI) + SERVO_MIN_PULSE; + + if (pwm < SERVO_MIN_PULSE) pwm = SERVO_MIN_PULSE; + if (pwm > SERVO_MAX_PULSE) pwm = SERVO_MAX_PULSE; + + PCA9685_SetPWM(addr, ch, 0, (uint16_t)pwm); } void PCA9685_ContinuousServoRun(uint8_t Channel) { - PCA9685_SetPWM(Channel, 0, 511); + uint8_t addr, ch; + servo_to_pca(Channel, &addr, &ch); + + PCA9685_SetPWM(addr, ch, 0, 511); } void PCA9685_ContinuousServoStop(uint8_t Channel) { - PCA9685_SetPWM(Channel, 0, 307); + uint8_t addr, ch; + servo_to_pca(Channel, &addr, &ch); + + PCA9685_SetPWM(addr, ch, 0, 307); } void PCA9685_LEDOff(uint8_t Channel) { - PCA9685_SetPWM(Channel, 0, 0); + uint8_t addr, ch; + servo_to_pca(Channel, &addr, &ch); + + PCA9685_SetPWM(addr, ch, 0, 0); } void PCA9685_LEDOn(uint8_t Channel) { - PCA9685_SetPWM(Channel, 0, 1000); + uint8_t addr, ch; + servo_to_pca(Channel, &addr, &ch); + + PCA9685_SetPWM(addr, ch, 0, 1000); } /* ######################## PARTIE TMC2209 STEPPER ########################### */ @@ -175,7 +222,7 @@ void step(uint32_t steps, uint32_t delay_us) extern Actionneurs act; void initActionneurs(){ - PCA9685_Init(50); + PCA9685_InitAllBoards(PCA9685_PWM_FREQ); HAL_GPIO_WritePin(DIR_PORT, DIR_PIN, GPIO_PIN_SET); TMC2209_init(); act.etatLedArmement = 0;