Någon som har PCA9685 + Servo som vill testa min STM32 kod?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
DanielM
Inlägg: 2189
Blev medlem: 5 september 2019, 14:19:58

Någon som har PCA9685 + Servo som vill testa min STM32 kod?

Inlägg av DanielM »

Hej!

Jag har en 12-bit PWM servostyrare vid namn PCA9685 + en liten blå 5V servo. Kina så klart. Men det sägs att att dom ska fungera.
Jag har STM32 F401RE som jag vill exprimentera på. Men jag fick inte PCA9685 fungera med STM32 bibliotek. Så därför tog jag friheten att skriva om Adafruit's PCA9685 C++ bibliotek till STM32 C kod.

Men det fungerar ändå inte. Nu misstänker jag att min servo's PWM kabel inte får någon signal. Jag menar, jag har skrivit av detta
https://github.com/adafruit/Adafruit-PW ... Driver.cpp
https://github.com/adafruit/Adafruit-PW ... voDriver.h

Till rent C för just STM32 men jag får det inte att fungera.
Jag har tyvärr inget oscilloskop i närheten för tillfället. Min fråga till er om man kan efterfråga om någon kodgranskning eller test och verifiering av er? Fungerar denna kod för er, så då vet jag att antingen är min servo sönder eller så är min PCA9685 sönder.

Exempelkod i STM32:

Kod: Markera allt

#include "main.h"
#include "PCA9685/PCA9685.h"

#define SERVOMIN  125 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  575 // this is the 'maximum' pulse length count (out of 4096)

PCA9685_I2C i2c;

long map(long x, long in_min, long in_max, long out_min, long out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
int angleToPulse(int ang){

   int pulse = map(ang,0, 180, SERVOMIN,SERVOMAX);// map angle of 0 to 180 to Servo min and Servo max
   return pulse;
}

void main(){

// Find I2C address
uint8_t address;
  for(uint8_t i = 0; i < 128; i++){
	  if(HAL_I2C_IsDeviceReady(&hi2c1, i, 5, 10) == HAL_OK){
	  	  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 1);
	  	  address = i;
	  	  break;
	  }
  }

  PCA9685_begin(&i2c, &hi2c1, address, 0);
  PCA9685_setPWMFreq(&i2c, 50);  // Analog servos run at ~50 Hz updates

    while (1)
    {
    	  for( int angle = 0; angle<181; angle++){
    	    HAL_Delay(1);
    	    PCA9685_setPWM(&i2c, 0, 0, angleToPulse(angle) );
    	  }
    }
}
PCA9685.c

Kod: Markera allt

#include "PCA9685.h"

static uint8_t read8(PCA9685_I2C *i2c, uint8_t addr) {
	uint8_t pData[] = { addr };
	if(HAL_I2C_Master_Transmit(i2c->hi2c, i2c->address, pData, 1, 10) != HAL_OK)
		return 0;
	if(HAL_I2C_Master_Receive(i2c->hi2c, i2c->address, pData, 1, 10) != HAL_OK)
		return 0;

	return pData[0];
}

static void write8(PCA9685_I2C *i2c, uint8_t addr, uint8_t d) {
	uint8_t pData[2] = { addr, d };
	if(HAL_I2C_Master_Transmit(i2c->hi2c, i2c->address, pData, 2, 10) != HAL_OK)
		return; // For debugging tool in STM
}

void PCA9685_setOscillatorFrequency(PCA9685_I2C *i2c, uint32_t freq) {
	i2c->oscillator_freq = freq;
}

uint32_t PCA9685_getOscillatorFrequency(PCA9685_I2C *i2c) {
	return i2c->oscillator_freq;
}

void PCA9685_writeMicroseconds(PCA9685_I2C *i2c, uint8_t num, uint16_t Microseconds) {
	float pulse = Microseconds;
	float pulselength = 1000000; // 1,000,000 us per second

	// Read prescale
	uint16_t prescale = PCA9685_readPrescale(i2c);

	// Calculate the pulse for PWM based on Equation 1 from the datasheet section
	// 7.3.5
	prescale += 1;
	pulselength *= prescale;
	pulselength /= i2c->oscillator_freq;
	pulse /= pulselength;
	PCA9685_setPWM(i2c, num, 0, pulse);
}

void PCA9685_setPin(PCA9685_I2C *i2c, uint8_t num, uint16_t val, bool invert) {
	// Clamp value between 0 and 4095 inclusive.
	if(val > 4095)
		val = 4095;
	if (invert) {
		if (val == 0) {
			// Special value for signal fully on.
			PCA9685_setPWM(i2c, num, 4096, 0);
		} else if (val == 4095) {
			// Special value for signal fully off.
			PCA9685_setPWM(i2c, num, 0, 4096);
		} else {
			PCA9685_setPWM(i2c, num, 0, 4095 - val);
		}
	} else {
		if (val == 4095) {
			// Special value for signal fully on.
			PCA9685_setPWM(i2c, num, 4096, 0);
		} else if (val == 0) {
			// Special value for signal fully off.
			PCA9685_setPWM(i2c, num, 0, 4096);
		} else {
			PCA9685_setPWM(i2c, num, 0, val);
		}
	}
}

void PCA9685_setPWM(PCA9685_I2C *i2c, uint8_t num, uint16_t on, uint16_t off) {
	uint8_t pData[5] = { PCA9685_LED0_ON_L + 4 * num, on, on >> 8, off, off >> 8 };
	if(HAL_I2C_Master_Transmit(i2c->hi2c, i2c->address, pData, 5, 10) != HAL_OK)
		return; // For debugging tool in STM
}

uint8_t PCA9685_getPWM(PCA9685_I2C *i2c, uint8_t num) {
	uint8_t Size = PCA9685_LED0_ON_L + 4 * num;
	uint8_t pData[Size];
	if(HAL_I2C_Master_Receive(i2c->hi2c, i2c->address, pData, Size, 10) != HAL_OK)
		return 0;
	return pData[0];
}

uint8_t PCA9685_readPrescale(PCA9685_I2C *i2c) {
	return read8(i2c, PCA9685_PRESCALE);
}

void PCA9685_setOutputMode(PCA9685_I2C *i2c, bool totempole) {
	uint8_t oldmode = read8(i2c, PCA9685_MODE2);
	uint8_t newmode;
	if (totempole) {
		newmode = oldmode | MODE2_OUTDRV;
	} else {
		newmode = oldmode & ~MODE2_OUTDRV;
	}
	write8(i2c, PCA9685_MODE2, newmode);
}

void PCA9685_setPWMFreq(PCA9685_I2C *i2c, float freq) {

	// Range output modulation frequency is dependant on oscillator
	if (freq < 1)
		freq = 1;
	if (freq > 3500)
		freq = 3500; // Datasheet limit is 3052=50MHz/(4*4096)

	float prescaleval = ((i2c->oscillator_freq / (freq * 4096.0)) + 0.5) - 1;
	if (prescaleval < PCA9685_PRESCALE_MIN)
		prescaleval = PCA9685_PRESCALE_MIN;
	if (prescaleval > PCA9685_PRESCALE_MAX)
		prescaleval = PCA9685_PRESCALE_MAX;
	uint8_t prescale = (uint8_t) prescaleval;

	uint8_t oldmode = read8(i2c, PCA9685_MODE1);
	uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep
	write8(i2c, PCA9685_MODE1, newmode);                          // go to sleep
	write8(i2c, PCA9685_PRESCALE, prescale); // set the prescaler
	write8(i2c, PCA9685_MODE1, oldmode);
	HAL_Delay(5);

	// This sets the MODE1 register to turn on auto increment.
	write8(i2c, PCA9685_MODE1, oldmode | MODE1_RESTART | MODE1_AI);

}

void PCA9685_setExtClk(PCA9685_I2C *i2c, uint8_t prescale) {
	uint8_t oldmode = read8(i2c, PCA9685_MODE1);
	uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep
	write8(i2c, PCA9685_MODE1, newmode); // go to sleep, turn off internal oscillator

	// This sets both the SLEEP and EXTCLK bits of the MODE1 register to switch to
	// use the external clock.
	write8(i2c, PCA9685_MODE1, (newmode |= MODE1_EXTCLK));
	write8(i2c, PCA9685_PRESCALE, prescale); // set the prescaler
	HAL_Delay(5);

	// clear the SLEEP bit to start
	write8(i2c, PCA9685_MODE1, (newmode & ~MODE1_SLEEP) | MODE1_RESTART | MODE1_AI);
}

void PCA9685_wakeup(PCA9685_I2C *i2c) {
	uint8_t sleep = read8(i2c, PCA9685_MODE1);
	uint8_t wakeup = sleep & ~MODE1_SLEEP; // set sleep bit low
	write8(i2c, PCA9685_MODE1, wakeup);
}

void PCA9685_sleep(PCA9685_I2C *i2c) {
	uint8_t awake = read8(i2c, PCA9685_MODE1);
	uint8_t sleep = awake | MODE1_SLEEP; // set sleep bit high
	write8(i2c, PCA9685_MODE1, sleep);
	HAL_Delay(5); // wait until cycle ends for sleep to be active
}

void PCA9685_reset(PCA9685_I2C *i2c) {
	write8(i2c, PCA9685_MODE1, MODE1_RESTART);
	HAL_Delay(10);
}

void PCA9685_begin(PCA9685_I2C *i2c, I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t prescale) {
	i2c->hi2c = hi2c;
	i2c->address = address;
	PCA9685_reset(i2c);
	if (prescale) {
		PCA9685_setExtClk(i2c, prescale);
	} else {
		// set a default frequency
		PCA9685_setPWMFreq(i2c, 1000);
	}
	// set the default internal frequency
	PCA9685_setOscillatorFrequency(i2c, FREQUENCY_OSCILLATOR);
}
PCA9685.h

Kod: Markera allt

#ifndef SRC_PCA9685_PCA9685_H_
#define SRC_PCA9685_PCA9685_H_

#include "main.h"
#include "stdbool.h"

// REGISTER ADDRESSES
#define PCA9685_MODE1 0x00      /**< Mode Register 1 */
#define PCA9685_MODE2 0x01      /**< Mode Register 2 */
#define PCA9685_SUBADR1 0x02    /**< I2C-bus subaddress 1 */
#define PCA9685_SUBADR2 0x03    /**< I2C-bus subaddress 2 */
#define PCA9685_SUBADR3 0x04    /**< I2C-bus subaddress 3 */
#define PCA9685_ALLCALLADR 0x05 /**< LED All Call I2C-bus address */
#define PCA9685_LED0_ON_L 0x06  /**< LED0 on tick, low byte*/
#define PCA9685_LED0_ON_H 0x07  /**< LED0 on tick, high byte*/
#define PCA9685_LED0_OFF_L 0x08 /**< LED0 off tick, low byte */
#define PCA9685_LED0_OFF_H 0x09 /**< LED0 off tick, high byte */
// etc all 16:  LED15_OFF_H 0x45
#define PCA9685_ALLLED_ON_L 0xFA  /**< load all the LEDn_ON registers, low */
#define PCA9685_ALLLED_ON_H 0xFB  /**< load all the LEDn_ON registers, high */
#define PCA9685_ALLLED_OFF_L 0xFC /**< load all the LEDn_OFF registers, low */
#define PCA9685_ALLLED_OFF_H 0xFD /**< load all the LEDn_OFF registers,high */
#define PCA9685_PRESCALE 0xFE     /**< Prescaler for PWM output frequency */
#define PCA9685_TESTMODE 0xFF     /**< defines the test mode to be entered */

// MODE1 bits
#define MODE1_ALLCAL 0x01  /**< respond to LED All Call I2C-bus address */
#define MODE1_SUB3 0x02    /**< respond to I2C-bus subaddress 3 */
#define MODE1_SUB2 0x04    /**< respond to I2C-bus subaddress 2 */
#define MODE1_SUB1 0x08    /**< respond to I2C-bus subaddress 1 */
#define MODE1_SLEEP 0x10   /**< Low power mode. Oscillator off */
#define MODE1_AI 0x20      /**< Auto-Increment enabled */
#define MODE1_EXTCLK 0x40  /**< Use EXTCLK pin clock */
#define MODE1_RESTART 0x80 /**< Restart enabled */
// MODE2 bits
#define MODE2_OUTNE_0 0x01 /**< Active LOW output enable input */
#define MODE2_OUTNE_1 0x02 /**< Active LOW output enable input - high impedience */
#define MODE2_OUTDRV 0x04 /**< totem pole structure vs open-drain */
#define MODE2_OCH 0x08    /**< Outputs change on ACK vs STOP */
#define MODE2_INVRT 0x10  /**< Output logic state inverted */

#define PCA9685_I2C_ADDRESS 0x40      /**< Default PCA9685 I2C Slave Address */
#define FREQUENCY_OSCILLATOR 25000000 /**< Int. osc. frequency in datasheet */

#define PCA9685_PRESCALE_MIN 3   /**< minimum prescale value */
#define PCA9685_PRESCALE_MAX 255 /**< maximum prescale value */

typedef struct{
	I2C_HandleTypeDef *hi2c;
	uint8_t address;
	uint32_t oscillator_freq;
}PCA9685_I2C;

void PCA9685_begin(PCA9685_I2C* i2c, I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t prescale);
void PCA9685_reset(PCA9685_I2C* i2c);
void PCA9685_sleep(PCA9685_I2C* i2c);
void PCA9685_wakeup(PCA9685_I2C* i2c);
void PCA9685_setExtClk(PCA9685_I2C* i2c, uint8_t prescale);
void PCA9685_setPWMFreq(PCA9685_I2C* i2c, float freq);
void PCA9685_setOutputMode(PCA9685_I2C* i2c, bool totempole);
uint8_t PCA9685_readPrescale(PCA9685_I2C* i2c);
uint8_t PCA9685_getPWM(PCA9685_I2C* i2c, uint8_t num);
void PCA9685_setPWM(PCA9685_I2C* i2c, uint8_t num, uint16_t on, uint16_t off);
void PCA9685_setPin(PCA9685_I2C* i2c, uint8_t num, uint16_t val, bool invert);
void PCA9685_writeMicroseconds(PCA9685_I2C* i2c, uint8_t num, uint16_t Microseconds);
uint32_t PCA9685_getOscillatorFrequency(PCA9685_I2C* i2c);
void PCA9685_setOscillatorFrequency(PCA9685_I2C* i2c, uint32_t freq);

#endif /* SRC_PCA9685_PCA9685_H_ */
DanielM
Inlägg: 2189
Blev medlem: 5 september 2019, 14:19:58

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av DanielM »

Nu fungerar det! Varsågod! :)

Kod: Markera allt

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "PCA9685/PCA9685.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define SERVOMIN  150 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  600 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN  600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates
#define SERVOS 1 // How many servos we have
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;

/* USER CODE BEGIN PV */
PCA9685_I2C i2c;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void setServoPulse(uint8_t n, float pulse) {

  float pulselength = 1000000;   // 1,000,000 us per second
  pulselength /= SERVO_FREQ;   // Analog servos run at ~60 Hz updates
  pulselength /= 4096;  // 12 bits of resolution
  pulse *= 1000000;  // convert input seconds to us
  pulse /= pulselength;
  PCA9685_setPWM(&i2c, n, 0, pulse);
}
uint8_t servonum = 0;
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

  PCA9685_begin(&i2c, &hi2c1, 0x80, 0);
  PCA9685_setOscillatorFrequency(&i2c, 27000000);
  PCA9685_setPWMFreq(&i2c, SERVO_FREQ);  // Analog servos run at ~50 Hz updates


  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    while (1)
    {

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    	// Drive each servo one at a time using setPWM()
    	  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    	    PCA9685_setPWM(&i2c, servonum, 0, pulselen);
    	  }

    	  HAL_Delay(500);
    	  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    		  PCA9685_setPWM(&i2c, servonum, 0, pulselen);
    	  }

    	  HAL_Delay(500);

    	  // Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!
    	  // The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior.
    	  for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
    		  PCA9685_writeMicroseconds(&i2c, servonum, microsec);
    	  }

    	  HAL_Delay(500);
    	  for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
    		  PCA9685_writeMicroseconds(&i2c, servonum, microsec);
    	  }

    	  HAL_Delay(500);

    	  servonum++;
    	  if (servonum >= SERVOS)
    		  servonum = 0; // Testing the first SERVOS servo channels

  }
  /* USER CODE END 3 */
}
guckrum
Inlägg: 1683
Blev medlem: 19 juni 2012, 09:04:27
Ort: Lund

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av guckrum »

Så därför tog jag friheten att skriva om Adafruit's PCA9685 C++ bibliotek till STM32 C kod.
Tänk på att koden har en licens som du bör respektera.
DanielM
Inlägg: 2189
Blev medlem: 5 september 2019, 14:19:58

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av DanielM »

För det första. Copyrightlagar gäller inte i Sverige ;)

För det andra så är Adafruit's kod BSD-licens. Vilket är en väldigt liberal licens.
https://github.com/adafruit/Adafruit-PW ... icense.txt

För det tredje så brukar jag mest bara hålla mig till öppen källkod.
Mjukvara som måste licensieras brukar man ofta få tag på ThePirateBay. Jag använder inte sådant för det är inte produktivt.
Janson1
Inlägg: 1351
Blev medlem: 1 december 2016, 09:06:02
Ort: Marks Kommun

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av Janson1 »

Vad var grundproblemet?
DanielM
Inlägg: 2189
Blev medlem: 5 september 2019, 14:19:58

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av DanielM »

Adressen. Jag gjorde en liten sökning på adressen och då fick jag fram 0 hela tiden, trots att det var en godkänd adress enligt STM32. Men den kunde inte tala med PCA9685. Så jag läste lite snabbt i databladet och fick fram att adressen skall vara 0x80.
Användarvisningsbild
JimmyAndersson
Inlägg: 26308
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av JimmyAndersson »

Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45264
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av TomasL »

Och du har brutit mot licensen, eftersom du inte har med copyright-notisen i din kod.
DanielM
Inlägg: 2189
Blev medlem: 5 september 2019, 14:19:58

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av DanielM »

TomasL skrev:Och du har brutit mot licensen, eftersom du inte har med copyright-notisen i din kod.
För det finns bara BSD licens där. :vissla:

JimmyAndersson skrev:>”Copyrightlagar gäller inte i Sverige”

???

Läs på...
https://riksarkivet.se/upphovsratt
https://sv.m.wikipedia.org/wiki/Upphovsrätt
https://euipo.europa.eu/ohimportal/sv/w ... f2d8d875c4
https://lagen.nu/1960:729

Där har du lite att börja med.
Nu missuppfattar du!
Copyright är en amerikansk lag. Upphovsrätt är en svensk lag.
Om det står © på en produkt i Sverige så kan man vara säker på att det finns dokumentation om upphovsrätten också. Men finns det bara © så är produkten inte upphovsrättskyddad.

En byråkratisk tjej förklarade detta för mig för 2 år sedan.
Rick81
Inlägg: 746
Blev medlem: 30 december 2005, 13:07:09

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av Rick81 »

Tror knappast någon bryr sig att du ändrar en adress angående licensen...
DanielM
Inlägg: 2189
Blev medlem: 5 september 2019, 14:19:58

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av DanielM »

Jag tror detta är klassiska "visa sig smart"-mentaliteten. Med tanke på att diskussionen inte var så lång i denna tråd så finns det utrymme för att diskutera licens då det är ett avancerat ämne. :wink:

Vi alla här kan väll redan erkänna nu att det fanns ingen © licens :D
ToPNoTCH
Inlägg: 4871
Blev medlem: 21 december 2009, 17:59:48

Re: Någon som har PCA9685 + Servo som vill testa min STM32 k

Inlägg av ToPNoTCH »

DanielM skrev:Adressen. Jag gjorde en liten sökning på adressen och då fick jag fram 0 hela tiden, trots att det var en godkänd adress enligt STM32. Men den kunde inte tala med PCA9685. Så jag läste lite snabbt i databladet och fick fram att adressen skall vara 0x80.
Då läste du databladet fel.

Adressen är=0x40.
Sedan lägger man en R/W bit efter adressen beroende på om man vill läsa (1) eller skriva (0).
Skriv svar