Trasformare uscita PWM di Arduino in uscita analogica

Ciao a tutti voi.
Possiedo un Arduino Uno R3 che, con opportuno codice, mi emette in una delle uscite PWM un segnale di circa 25Khz.
Ho acquistato un azionamento BLDC per controllare la rotazione di un motore brushless.
Fin qui tutto ok… ma il problema è che l’azionamento accetta in ingresso una fequenza PWWM di max 1khz oppure un segnale analogico 0-5V.

Premessa: non chiedetemi di modificare il codice perchè non sono pratico di programmazione. Quindi l’unica soluzione che mi rimarrebbe è quella di trasformare appunto l’uscita digitale in analogica attraverso un filtro passa-basso.

Come faccio a trovare il valore di R e il valore di C per costruire il filtro passa-basso?

Metti il codice (usando gli appositi tag).
Cambiare la frequenza del PWM è questione di pochi secondi.

Eccomi qua…
Ho verificato meglio e lo sketch è stato creato per funzionare con due frequenze differenti:

31250 Hz
3906.25 Hz

NOTA: da un software è possibile scegliere tramite una spunta se utilizzare appunto o la frequenza da 31250 Hz oppure la frequenza da 3906.25 Hz.

A me basterebbe che entrambe le linee dello sketch che lavorano alla frequenza di 31250 Hz e 3906.25 lavorino a 976 Hz, in maniera che io possa utilizzare gli azionamenti che funzionano a 1Khz al massimo. Così da software se si seleziona la frequenza di 31250 Hz o 3906.25 Hz, deve uscire sempre la frequenza di 976 Hz.

Eccovi lo sketch

/*	Command input protocol	(always 5 bytes, beginning with 'X' character and ends with a XOR checksum)
	'X' 1 H L C				Set motor 1 position to High and Low value 0 to 1023
	'X' 2 H L C				Set motor 2 position to High and Low value 0 to 1023
	'X' 3 H L C				Set motor 1 P Proportional value to High and Low value
	'X' 4 H L C				Set motor 2 P Proportional value to High and Low value
	'X' 5 H L C				Set motor 1 I Integral value to High and Low value
	'X' 6 H L C				Set motor 2 I Integral value to High and Low value
	'X' 7 H L C				Set motor 1 D Derivative value to High and Low value
	'X' 8 H L C				Set motor 2 D Derivative value to High and Low value

	'X' 200 0 0 C			Send back over serial port both analogue feedback raw values
	'X' 201 0 0 C			Send back over serial port the current pid count
	'X' 202 0 0 C			Send back over serial port the firmware version (used for x-sim autodetection)
	'X' 203 M V C			Write EEPROM on address M (only 0 to 255 of 1024 Bytes of the EEPROM) with new value V
	'X' 204 M 0 C			Read EEPROM on memory address M (only 0 to 255 of 1024 Bytes of the EEPROM), send back over serial the value
	'X' 205 0 0 C			Clear EEPROM
	'X' 206 0 0 C			Reread the whole EEPRom and store settings into fitting variables
	'X' 207 0 0	C			Disable power on motor 1
	'X' 208 0 0	C			Disable power on motor 2
	'X' 209 0 0	C			Enable power on motor 1
	'X' 210 0 0	C			Enable power on motor 2
	'X' 211 0 0	C			Send all debug values
	
	EEPROM memory map
	00		empty eeprom detection, 111 if set, all other are indicator to set default
	01-02	minimum 1
	03-04	maximum 1
	05		dead zone 1
	06-07	minimum 2
	08-09	maximum 2
	10		dead zone 2
	11-12	P component of motor 1
	13-14	I component of motor 1
	15-16	D component of motor 1
	17-18	P component of motor 2
	19-20	I component of motor 2
	21-22	D component of motor 2
	23		pwm1 offset
	24		pwm2 offset
	25		pwm1 maximum
	26		pwm2 maximum
	27		pwm frequency divider (1,8,64)

	Pin out of arduino for H-Bridge

	Pin 10 - PWM1 - Speed for Motor 1. 
	Pin  9 - PWM2 - Speed for Motor 2.
	Pin  2 - INA1 - motor 1 turn 
	Pin  3 - INA2 - motor 1 turn
	Pin  4 - INB1 - motor 2 turn
	Pin  5 - INB2 - motor 2 turn




	Analog Pins

	Pin A0 - input of feedback positioning from motor 1
	Pin A1 - input of feedback positioning from motor 2

	As well 5v and GND pins tapped in to feed feedback pots too.

*/

#include <EEPROM.h>

//Some speed test switches for testers ;)
#define FASTADC  1 //Hack to speed up the arduino analogue read function, comment out with // to disable this hack

// defines for setting and clearing register bits
#ifndef cbi
	#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
	#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define LOWBYTE(v)   ((unsigned char) (v))								//Read
#define HIGHBYTE(v)  ((unsigned char) (((unsigned int) (v)) >> 8))
#define BYTELOW(v)   (*(((unsigned char *) (&v) + 1)))					//Write
#define BYTEHIGH(v)  (*((unsigned char *) (&v)))

#define   GUARD_MOTOR_1_GAIN   100.0     
#define   GUARD_MOTOR_2_GAIN   100.0

//Firmware version info
int firmaware_version_mayor=1;
int firmware_version_minor =4;

//360� option for flight simulators
bool turn360motor1 = false;
bool turn360motor2 = false;

int virtualtarget1;
int virtualtarget2;
int currentanalogue1 = 0;
int currentanalogue2 = 0;
int target1=512;
int target2=512;
int low=0;
int high=0;
unsigned long hhigh=0;
unsigned long hlow=0;
unsigned long lhigh=0;
unsigned long llow=0;
int buffer=0;
int buffercount=-1;
int commandbuffer[5]={0};
unsigned long pidcount	= 0;		// unsigned 32bit, 0 to 4,294,967,295
byte errorcount	= 0;		// serial receive error detected by checksum




// fixed DATA for direct port manipulation, exchange here each value if your h-Bridge is connected to another port pin
// This pinning overview is to avoid the slow pin switching of the arduino libraries
// 
//                  +-\/-+
//            PC6  1|    |28  PC5 (AI 5)
//      (D 0) PD0  2|    |27  PC4 (AI 4)
//      (D 1) PD1  3|    |26  PC3 (AI 3)
//      (D 2) PD2  4|    |25  PC2 (AI 2)
// PWM+ (D 3) PD3  5|    |24  PC1 (AI 1)
//      (D 4) PD4  6|    |23  PC0 (AI 0)
//            VCC  7|    |22  GND
//            GND  8|    |21  AREF
//            PB6  9|    |20  AVCC
//            PB7 10|    |19  PB5 (D 13)
// PWM+ (D 5) PD5 11|    |18  PB4 (D 12)
// PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM
//      (D 7) PD7 13|    |16  PB2 (D 10) PWM
//      (D 8) PB0 14|    |15  PB1 (D 9) PWM
//                  +----+
// 
int portdstatus				=PORTD; 	// read the current port D bit mask
int ControlPinM1Inp1		=2;			// motor 1 INP1 output, this is the arduino pin description
int ControlPinM1Inp2		=3;			// motor 1 INP2 output, this is the arduino pin description
int ControlPinM2Inp1		=4;			// motor 2 INP1 output, this is the arduino pin description
int ControlPinM2Inp2		=5;			// motor 2 INP2 output, this is the arduino pin description
int PWMPinM1				=10;		// motor 1 PWM output
int PWMPinM2				=9;			// motor 2 PWM output

// Pot feedback inputs
int FeedbackPin1			= A0;		// select the input pin for the potentiometer 1, PC0
int FeedbackPin2			= A1;		// select the input pin for the potentiometer 2, PC1
int FeedbackMax1			= 1021;		// Maximum position of pot 1 to scale, do not use 1023 because it cannot control outside the pot range
int FeedbackMin1			= 2;		// Minimum position of pot 1 to scale, do not use 0 because it cannot control outside the pot range
int FeedbackMax2			= 1021;		// Maximum position of pot 2 to scale, do not use 1023 because it cannot control outside the pot range
int FeedbackMin2			= 2;		// Minimum position of pot 2 to scale, do not use 0 because it cannot control outside the pot range
int FeedbackPotDeadZone1	= 0;		// +/- of this value will not move the motor		
int FeedbackPotDeadZone2	= 0;		// +/- of this value will not move the motor
float quarter1				= 254.75;
float quarter2				= 254.75;
float threequarter1			= 764.25;
float threequarter2			= 764.25;

//PID variables
int motordirection1		= 0;			// motor 1 move direction 0=brake, 1=forward, 2=reverse
int motordirection2		= 0;			// motor 2 move direction 0=brake, 1=forward, 2=reverse
int oldmotordirection1	= 0;
int oldmotordirection2	= 0;
double K_motor_1		= 1;
double proportional1	= 4.200;		//initial value
double integral1		= 0.400;
double derivative1		= 0.400;
double K_motor_2		= 1;
double proportional2	= 4.200;
double integral2		= 0.400;
double derivative2		= 0.400;
int OutputM1					= 0;
int OutputM2					= 0;
double integrated_motor_1_error = 0;
double integrated_motor_2_error = 0;
float last_motor_1_error		= 0;
float last_motor_2_error		= 0; 
int disable						= 1; //Motor stop flag
int pwm1offset					= 50;
int pwm2offset					= 50;
int pwm1maximum					= 255;
int pwm2maximum					= 255;
float pwm1divider				= 0.8039;
float pwm2divider				= 0.8039;
float pwmfloat					= 0;
int pwmfrequencydivider			= 1; //31kHz

byte debugbyte =0;				//This values are for debug purpose and can be send via
int debuginteger =0;			//the SendDebug serial 211 command to the X-Sim plugin
double debugdouble =0;	

void setPwmFrequency(int pin, int divisor) 
{
	byte mode;
	if(pin == 5 || pin == 6 || pin == 9 || pin == 10) 
	{
		switch(divisor) 
		{
			case 1: mode = 0x01; break;
			case 8: mode = 0x02; break;
			case 64: mode = 0x03; break;
			case 256: mode = 0x04; break;
			case 1024: mode = 0x05; break;
			default: return;
		}
		if(pin == 5 || pin == 6) 
		{
			TCCR0B = TCCR0B & 0b11111000 | mode;
		} 
		else 
		{
			TCCR1B = TCCR1B & 0b11111000 | mode;
		}
	} 
	else 
	{
		if(pin == 3 || pin == 11) 
		{
			switch(divisor) 
			{
				case 1: mode = 0x01; break;
				case 8: mode = 0x02; break;
				case 32: mode = 0x03; break;
				case 64: mode = 0x04; break;
				case 128: mode = 0x05; break;
				case 256: mode = 0x06; break;
				case 1024: mode = 0x7; break;
				default: return;
			}
			TCCR2B = TCCR2B & 0b11111000 | mode;
		}
	}
}

void setup()
{
	//Serial.begin(115200);   //Uncomment this for arduino UNO without ftdi serial chip
	Serial.begin(9600);  //Uncomment this for arduino nano, arduino with ftdi chip or arduino duemilanove
	portdstatus=PORTD;
	pinMode(ControlPinM1Inp1, OUTPUT);
	pinMode(ControlPinM1Inp2, OUTPUT);
	pinMode(ControlPinM2Inp1, OUTPUT);
	pinMode(ControlPinM2Inp2, OUTPUT);
	pinMode(PWMPinM1,		  OUTPUT);
	pinMode(PWMPinM2,		  OUTPUT);
	analogWrite(PWMPinM1,	  0);
	analogWrite(PWMPinM2,	  0);
	UnsetMotor1Inp1();
	UnsetMotor1Inp2();
	UnsetMotor2Inp1();
	UnsetMotor2Inp2();
	disable=1;
	//TCCR1B = TCCR1B & 0b11111100; //This is a hack for changing the PWM frequency to a higher value, if removed it is 490Hz
	setPwmFrequency(9, 1);
	setPwmFrequency(10, 1);
#if FASTADC
	// set analogue prescale to 16
	sbi(ADCSRA,ADPS2) ;
	cbi(ADCSRA,ADPS1) ;
	cbi(ADCSRA,ADPS0) ;
#endif
}

void WriteEEPRomWord(int address, int intvalue)
{
	int low,high;
	high=intvalue/256;
	low=intvalue-(256*high);
	EEPROM.write(address,high);
	EEPROM.write(address+1,low);
}

int ReadEEPRomWord(int address)
{
	int low,high, returnvalue;
	high=EEPROM.read(address);
	low=EEPROM.read(address+1);
	returnvalue=(high*256)+low;
	return returnvalue;
}

void WriteEEProm()
{
	EEPROM.write(0,111);
	WriteEEPRomWord(1,FeedbackMin1);
	WriteEEPRomWord(3,FeedbackMax1);
	EEPROM.write(5,FeedbackPotDeadZone1);
	WriteEEPRomWord(6,FeedbackMin2);
	WriteEEPRomWord(8,FeedbackMax2);
	EEPROM.write(10,FeedbackPotDeadZone2);
	WriteEEPRomWord(11,int(proportional1*10.000));
	WriteEEPRomWord(13,int(integral1*10.000));
	WriteEEPRomWord(15,int(derivative1*10.000));
	WriteEEPRomWord(17,int(proportional2*10.000));
	WriteEEPRomWord(19,int(integral2*10.000));
	WriteEEPRomWord(21,int(derivative2*10.000));
	if(pwm1offset > 180 || pwm2offset > 180 || pwm1maximum < 200 || pwm2maximum < 200)
	{
		pwm1offset=50;
		pwm2offset=50;
		pwm1maximum=255;
		pwm2maximum=255;
		pwm1divider=0.8039;
		pwm2divider=0.8039;
	}
	EEPROM.write(23,pwm1offset);
	EEPROM.write(24,pwm2offset);
	EEPROM.write(25,pwm1maximum);
	EEPROM.write(26,pwm2maximum);
	if(pwmfrequencydivider != 1 && pwmfrequencydivider != 8)
	{
		pwmfrequencydivider=1;
	}
	EEPROM.write(27,pwmfrequencydivider);
}

void ReadEEProm()
{
	int evalue = EEPROM.read(0);
	if(evalue != 111) //EEProm was not set before, set default values
	{
		WriteEEProm();
		return;
	}
	FeedbackMin1=ReadEEPRomWord(1);
	FeedbackMax1=ReadEEPRomWord(3);
	FeedbackPotDeadZone1=EEPROM.read(5);
	FeedbackMin2=ReadEEPRomWord(6);
	FeedbackMax2=ReadEEPRomWord(8);
	FeedbackPotDeadZone2=EEPROM.read(10);
	proportional1=double(ReadEEPRomWord(11))/10.000;
	integral1=double(ReadEEPRomWord(13))/10.000;
	derivative1=double(ReadEEPRomWord(15))/10.000;
	proportional2=double(ReadEEPRomWord(17))/10.000;
	integral2=double(ReadEEPRomWord(19))/10.000;
	derivative2=double(ReadEEPRomWord(21))/10.000;
	pwm1offset=EEPROM.read(23);
	pwm2offset=EEPROM.read(24);
	pwm1maximum=EEPROM.read(25);
	pwm2maximum=EEPROM.read(26);
	if(pwm1offset > 180 || pwm2offset > 180 || pwm1maximum < 200 || pwm2maximum < 200)
	{
		pwm1offset=50;
		pwm2offset=50;
		pwm1maximum=255;
		pwm2maximum=255;
		pwm1divider=0.8039;
		pwm2divider=0.8039;
		EEPROM.write(23,pwm1offset);
		EEPROM.write(24,pwm2offset);
		EEPROM.write(25,pwm1maximum);
		EEPROM.write(26,pwm2maximum);
	}
	else
	{
		pwmfloat=float(pwm1maximum-pwm1offset);
		pwm1divider=pwmfloat/255.000;
		pwmfloat=float(pwm2maximum-pwm2offset);
		pwm2divider=pwmfloat/255.000;
	}
	pwmfrequencydivider=EEPROM.read(27);
	if(pwmfrequencydivider != 1 && pwmfrequencydivider != 8)
	{
		pwmfrequencydivider=1;
		EEPROM.write(27,pwmfrequencydivider);
	}
	quarter1=float(FeedbackMax1-FeedbackMin1)/4.000;
	quarter2=float(FeedbackMax2-FeedbackMin2)/4.000;
	threequarter1=quarter1*3.000;
	threequarter2=quarter1*3.000;
	setPwmFrequency(9, pwmfrequencydivider);
	setPwmFrequency(10, pwmfrequencydivider);
}

void SendAnalogueFeedback(int analogue1, int analogue2)
{
	high=analogue1/256;
	low=analogue1-(high*256);
	Serial.write('X');
	Serial.write(200);
	Serial.write(high);
	Serial.write(low);
	high=analogue2/256;
	low=analogue2-(high*256);
	Serial.write(high);
	Serial.write(low);
}

void SendPidCount()
{
	unsigned long value=pidcount;
	hhigh=value/16777216;
	value=value-(hhigh*16777216);
	hlow=value/65536;
	value=value-(hlow*65536);
	lhigh=value/256;
	llow=value-(lhigh*256);
	Serial.write('X');
	Serial.write(201);
	Serial.write(int(hhigh));
	Serial.write(int(hlow));
	Serial.write(int(lhigh));
	Serial.write(int(llow));
	Serial.write(errorcount);
}






void SendDebugValues()
{
	//The double is transformed into a integer * 10 !!!
	int doubletransfere=int(double(debugdouble*10.000));
	Serial.write('X');
	Serial.write(211);
	Serial.write(debugbyte);
	Serial.write(HIGHBYTE(debuginteger));
	Serial.write(LOWBYTE(debuginteger));
	Serial.write(HIGHBYTE(doubletransfere));
	Serial.write(LOWBYTE(doubletransfere));
}

void SendFirmwareVersion()
{
	Serial.write('X');
	Serial.write('-');
	Serial.write('P');
	Serial.write('I');
	Serial.write('D');
	Serial.write(' ');
	Serial.write(48+firmaware_version_mayor);
	Serial.write('.');
	Serial.write(48+firmware_version_minor);
}

void EEPromToSerial(int eeprom_address)
{
	int retvalue=EEPROM.read(eeprom_address);
	Serial.write('X');
	Serial.write(204);
	Serial.write(retvalue);
}

void ClearEEProm()
{
	for(int z=0; z < 1024; z++)
	{ 
		EEPROM.write(z,255);
	}
}

void ParseCommand()
{
	if(commandbuffer[0]==1)			//Set motor 1 position to High and Low value 0 to 1023
	{
		target1=(commandbuffer[1]*256)+commandbuffer[2];
		disable=0;
		return;
	}
	if(commandbuffer[0]==2)			//Set motor 2 position to High and Low value 0 to 1023
	{
		target2=(commandbuffer[1]*256)+commandbuffer[2];
		disable=0;
		return;
	}



	if(commandbuffer[0]==200)		//Send both analogue feedback raw values
	{
		SendAnalogueFeedback(currentanalogue1, currentanalogue2);
		return;
	}
	if(commandbuffer[0]==201)		//Send PID count
	{
		SendPidCount();
		return;
	}
	if(commandbuffer[0]==202)		//Send Firmware Version
	{
		SendFirmwareVersion();
		return;
	}
	if(commandbuffer[0]==203)		//Write EEPROM
	{
		EEPROM.write(commandbuffer[1],uint8_t(commandbuffer[2]));
		return;
	}
	if(commandbuffer[0]==204)		//Read EEPROM
	{
		EEPromToSerial(commandbuffer[1]);
		return;
	}
	if(commandbuffer[0]==205)		//Clear EEPROM
	{
		ClearEEProm();
		return;
	}
	if(commandbuffer[0]==206)		//Reread the whole EEPRom and store settings into fitting variables
	{
		ReadEEProm();
		return;
	}
	if(commandbuffer[0]==207 || commandbuffer[0]==208)		//Disable power on both motor
	{
		analogWrite(PWMPinM1,	  0);
		UnsetMotor1Inp1();
		UnsetMotor1Inp2();
		analogWrite(PWMPinM2,	  0);
		UnsetMotor2Inp1();
		UnsetMotor2Inp2();
		disable=1;
		return;
	}
	if(commandbuffer[0]==209 || commandbuffer[0]==210)		//Enable power on both motor
	{
		analogWrite(PWMPinM1,	  128);
		UnsetMotor1Inp1();
		UnsetMotor1Inp2();
		analogWrite(PWMPinM2,	  128);
		UnsetMotor2Inp1();
		UnsetMotor2Inp2();
		disable=0;
		return;
	}


	if(commandbuffer[0]==211)		//Send all debug values
	{
		SendDebugValues();
		return;
	}
}

void FeedbackPotWorker()
{
	currentanalogue1 = analogRead(FeedbackPin1);
	currentanalogue2 = analogRead(FeedbackPin2);
	//Notice: Minimum and maximum scaling calculation is done in the PC plugin with faster float support
}

bool CheckChecksum() //Atmel chips have a comport error rate of 2%, so we need here a checksum
{
	byte checksum=0;
	for(int z=0; z < 3; z++)
	{
		byte val=commandbuffer[z];
		checksum ^= val;
	}
	if(checksum==commandbuffer[3]){return true;}
	return false;
}

void SerialWorker()
{
	while(Serial.available()) 
	{
		if(buffercount==-1)
		{
			buffer = Serial.read();
			if(buffer != 'X'){buffercount=-1;}else{buffercount=0;}
		}
		else
		{
			buffer = Serial.read();
			commandbuffer[buffercount]=buffer;
			buffercount++;
			if(buffercount > 3)
			{
				if(CheckChecksum()==true){ParseCommand();}else{errorcount++;}
				buffercount=-1;
			}
		}
	}
}

void CalculateVirtualTarget()
{
	if(turn360motor1==true)
	{
		virtualtarget1=target1;
		if(currentanalogue1 > int(threequarter1) && target1 < int(quarter1)){virtualtarget1+=FeedbackMax1;}
		else{if(currentanalogue1 < int(quarter1) && target1 > int(threequarter1)){virtualtarget1=0-FeedbackMax1-target1;}}
	}


	else
	{
		virtualtarget1=target1;
	}
	if(turn360motor2==true)
	{
		virtualtarget2=target2;
		if(currentanalogue2 > int(threequarter2) && target2 < int(quarter2)){virtualtarget2+=FeedbackMax2;}
		else{if(currentanalogue2 < int(quarter2) && target2 > int(threequarter2)){virtualtarget2=0-FeedbackMax2-target2;}}
	}
	else
	{
		virtualtarget2=target2;
	}
}

void CalculateMotorDirection()
{
	if(virtualtarget1 > (currentanalogue1 + FeedbackPotDeadZone1) || virtualtarget1 < (currentanalogue1 - FeedbackPotDeadZone1))
	{
		if (OutputM1 >= 0)  
		{                                    
			motordirection1=1;				// drive motor 1 forward
		}  
		else 
		{                                              
			motordirection1=2;				// drive motor 1 backward
			OutputM1 = abs(OutputM1);
		}
	}
	else
	{
		motordirection1=0;
	}

	if(virtualtarget2 > (currentanalogue2 + FeedbackPotDeadZone2) || virtualtarget2 < (currentanalogue2 - FeedbackPotDeadZone2))
	{
		if (OutputM2 >= 0)  
		{                                    
			motordirection2=1;				// drive motor 2 forward
		}  
		else 
		{                                              
			motordirection2=2;				// drive motor 2 backward
			OutputM2 = abs(OutputM2);
		}
	}
	else
	{
		motordirection2=0;
	}

	OutputM1 = constrain(OutputM1, -255, 255);
	OutputM2 = constrain(OutputM2, -255, 255);
}




int updateMotor1Pid(int targetPosition, int currentPosition)   
{
	float error = (float)targetPosition - (float)currentPosition; 
	float pTerm_motor_R = proportional1 * error;
	integrated_motor_1_error += error;                                       
	float iTerm_motor_R = integral1 * constrain(integrated_motor_1_error, -GUARD_MOTOR_1_GAIN, GUARD_MOTOR_1_GAIN);
	float dTerm_motor_R = derivative1 * (error - last_motor_1_error);                            
	last_motor_1_error = error;
	return constrain(K_motor_1*(pTerm_motor_R + iTerm_motor_R + dTerm_motor_R), -255, 255);
}

int updateMotor2Pid(int targetPosition, int currentPosition)   
{
	float error = (float)targetPosition - (float)currentPosition; 
	float pTerm_motor_L = proportional2 * error;
	integrated_motor_2_error += error;                                       
	float iTerm_motor_L = integral2 * constrain(integrated_motor_2_error, -GUARD_MOTOR_2_GAIN, GUARD_MOTOR_2_GAIN);
	float dTerm_motor_L = derivative2 * (error - last_motor_2_error);                            
	last_motor_2_error = error;

	return constrain(K_motor_2*(pTerm_motor_L + iTerm_motor_L + dTerm_motor_L), -255, 255);
}

void CalculatePID()
{
	OutputM1=updateMotor1Pid(virtualtarget1,currentanalogue1);
	OutputM2=updateMotor2Pid(virtualtarget2,currentanalogue2);
}

void SetPWM()
{
	//Calculate pwm offset and maximum
	pwmfloat=OutputM1;
	pwmfloat*=pwm1divider;
	pwmfloat+=float(pwm1offset); 
	OutputM1=pwmfloat;
	if(OutputM1 > pwm1maximum){OutputM1=pwm1maximum;}
	pwmfloat=OutputM2;
	pwmfloat*=pwm2divider;
	pwmfloat+=float(pwm2offset);
	OutputM2=pwmfloat;
	if(OutputM2 > pwm2maximum){OutputM2=pwm2maximum;}

	//Set hardware pwm
	if(motordirection1 != 0)
	{
		analogWrite(PWMPinM1, int(OutputM1));
	}
	else
	{
		analogWrite(PWMPinM1, 0);
	}
	if(motordirection2 != 0)
	{
		analogWrite(PWMPinM2, int(OutputM2));
	}



	else
	{
		analogWrite(PWMPinM2, 0);
	}
}

//Direct port manipulation, change here your port code

void SetMotor1Inp1()
{
	portdstatus |= 1 << ControlPinM1Inp1;
	PORTD = portdstatus;
}

void UnsetMotor1Inp1()
{
	portdstatus &= ~(1 << ControlPinM1Inp1);
	PORTD = portdstatus;
}

void SetMotor1Inp2()
{
	portdstatus |= 1 << ControlPinM1Inp2;
	PORTD = portdstatus;
}

void UnsetMotor1Inp2()
{
	portdstatus &= ~(1 << ControlPinM1Inp2);
	PORTD = portdstatus;
}

void SetMotor2Inp1()
{
	portdstatus |= 1 << ControlPinM2Inp1;
	PORTD = portdstatus;
}
 
void UnsetMotor2Inp1()
{
	portdstatus &= ~(1 << ControlPinM2Inp1);
	PORTD = portdstatus;
}

void SetMotor2Inp2()
{
	portdstatus |= 1 << ControlPinM2Inp2;
	PORTD = portdstatus;
}

void UnsetMotor2Inp2()
{
	portdstatus &= ~(1 << ControlPinM2Inp2);
	PORTD = portdstatus;
}




void SetHBridgeControl() //With direct port manipulation for speedup the arduino framework!
{
	//Motor 1
	if(motordirection1 != oldmotordirection1)
	{
		if(motordirection1 != 0)
		{
			if(motordirection1 == 1)
			{
				SetMotor1Inp1();
				UnsetMotor1Inp2();
			}
			else
			{
				UnsetMotor1Inp1();
				SetMotor1Inp2();
			}
		}
		else
		{
			UnsetMotor1Inp1();
			UnsetMotor1Inp2();
		}
		oldmotordirection1=motordirection1;
	}

	//Motor 2
	if(motordirection2 != oldmotordirection2)
	{
		if(motordirection2 != 0)
		{
			if(motordirection2 == 1)
			{
				SetMotor2Inp1();
				UnsetMotor2Inp2();
			}
			else
			{
				UnsetMotor2Inp1();
				SetMotor2Inp2();
			}
		}
		else
		{
			UnsetMotor2Inp1();
			UnsetMotor2Inp2();
		}
		oldmotordirection2=motordirection2;
	}
}









void loop()
{
	//Read all stored PID and Feedback settings
	ReadEEProm();
	//Program loop
	while (1==1) //Important hack: Use this own real time loop code without arduino framework delays
	{
		FeedbackPotWorker();
		SerialWorker();
		CalculateVirtualTarget();
		CalculatePID();
		CalculateMotorDirection();
		if(disable==0)
		{
			SetPWM();
			SetHBridgeControl();
		}
		pidcount++;
	}
}

Nel tuo sketch stai usando per il PWM i pin 9 e 10 e con le impostazioni dei prescaler disponibili non è possibile impostare la frequenza di 976Hz su quei pin, ma solo una tra le seguenti: 31372Hz, 3921Hz, 490Hz, 122Hz, 61Hz.
Dovresti quindi cambiarli ed usare i pin 3 ed 11, ma il pin 3 è già impegnato e quindi ti conviene usare la frequenza di 490Hz (vedrai che il driver va benissimo lo stesso) e lasciare invariati i pin assegnati.

Nel setup chiami due volte questa funzione che va ad impostare la frequenza sui pin selezionati.

setPwmFrequency(9, 1);
setPwmFrequency(10, 1);

Per impostare la frequenza a 490Hz devi chiamare la funzione passando il valore 64 come divisore oppure non chiamarla affatto visto che è il valore di default (commenta le due righe)

setPwmFrequency(9, 64);
setPwmFrequency(10, 64);

Quindi sostituisco i due numeri 1 con il numero 64 in entrambe le due righe, giusto?

Yes!

Ok tra un paio di giorni faro una prova e vi farò sapere.
Una curiosità: attualmente con la frequenza di 31250 Hz di Arduino i motori in DC brushed non producevano alcun sibilo…
Con la frequenza di 490 Hz i motori Brushless produrranno il fastidioso sibilo?

La soluzione di @cotestatnt va bene dal punto di vista software.
Una soluzione dal punto di vista hardware potrebbe essere quella di mettere un filtro L/C

C potrebbe essere 10nF che a 25kHz corrisponde ad una reattanza di 637 ohm
L potrebbe essere 47 mH 10mA che a 25kHz corrisponde ad una reattanza di 7382 ohm

Mi domando se mettere un’induttanza cosi, collegata dritta ad una uscita, gli faccia bene ai Mosfet dell’uscita…

Effettivamente si potrebbe mettere un diodo in parallelo con l’induttore con il catodo collegato verso l’uscita di Arduino ed un diodo zener da 5,6V con l’anodo collegato alla massa e il catodo collegato all’uscita di Arduino per evitare correnti inverse o extratensioni.
Oppure, visto che si deve prelevare un segnale a bassa potenza, si potrebbe provare anche con una rete R/C al posto di quella L/C.
Ad esempio su questo stesso forum era stata proposta la seguente soluzione.

Avevo pensato pure io ad un filtro passa-basso (RC) per trasformare il segnale PWM in uscita da Arduino in un segnale analogico, ma non saprei come calcolare i valori di R e C.

Se qualcuno volesse spiegarmi come si fa… sarebbe cosa gradita.
Non chiedetemi numeri particolari perchè non saprei cosa dirvi.
So soltanto il dato della frequenza.

Mettiamo ad esempio che io abbia appunto una frequenza di 31250 Hz e volessi trasformarla in analogica (0-5 Volt) con un filtro RC.
Come si calcolano in maniera EASY… i valori di R e C ?

Ti conviene calcolare il filtro passa-basso per una frequenza 10 volte inferiore a quella della PWM quindi a 3125 Hz.
Siccome per controllare il motore 3125 Hz sono sovrabbondanti, puoi calcolare il filtro passa-basso a 500 Hz (se l’azionamento accetta un PWM di 1000Hz non può essere in grado di variare la velocità del motore più velocemente di 500 volte al secondo). .
La reattanza capacitativa la calcoli con la formula Z=1/(6,28 x f x C) dove C è in Farard, se usi nF puoi usare la formula Z=1000000000/(6,28 x f x C)
Siccome la tua frequenza è 500 Hz, si ha Z=1000000000/(6,28 x 500 x C) quindi Z=318310/C e quindi C=318310/Z
Per limitare la corrente in uscita da Arduino, io metterei R6 da 2200ohm ed R1 da 4700ohm quindi puoi calcolare nel modo seguente i condensatori.
C5=318310/R6=318310/2200=145nF quindi ci puoi mettere un condensatore poliestere da 150nF
C1=3183105/R1=318310/4700=68nF quindi ci puoi mettere un condensatore poliestere da 68nF

Ciao salvogi, quindi vediamo se ho ben capito: dovendo utilizzare un azionamento che accetta in ingresso una frequenza PWM massima di 1Khz… per calcolare il filtro RC non devo considerare la frequenza di 31250 Hz ma bensì la frequenza di 500 Hz. Giusto?

Quindi se fosse così… e se io utilizzassi l’azionamento con ingresso massimo PWM di 1Khz, io dovrei far riferimento sempre alla frequenza di 500 Hz, anche se da Arduino io avessi in uscita ad esempio 5Khz…10Khz…50Khz…100Khz…?

Un filtro RC ha una pendenza bassa quindi, se la frequenza di PWM è 31250 Hz, ti conviene tagliare a 3125 Hz per ridurre l’ampiezza del segnale a 31250 Hz che inevitabilmente arriva sull’uscita.
Tuttavia tagliando a 3125 Hz, avresti la possibilità di variare la potenza del motore 3125 volte al secondo che a mio avviso sembrerebbe inutile. Tra l’altro, se il tuo azionamento accetta come pilotaggio un PWM massimo a 1000 Hz significa che al massimo sarà in grado di variare la potenza 500 volte al secondo quindi non perdi nulla se tagli a 500 Hz e riduci ulteriormente i 31250 Hz che inevitabilmente arriveranno all’azionamento.
Già tagliando a 3125 Hz, all’azionamento dovrebbe arrivare un segnale abbastanza pulito quindi non dovresti avere problemi se passa una piccola parte dei 31250 Hz.
Comunque dipende dalla tua applicazione, con che velocità devi variare la potenza del motore? Se ti basta 10 volte al secondo, potresti tagliare a 100Hz (per mantenere un certo margine di sicurezza).
Questa operazione sarebbe meglio farla con un DAC ma dovresti mettere mano al codice, probabilmente puoi riuscire anche solo con un filtro passa basso.

Ciao Salvogi, ottima spiegazione !!

Ciao cotestatnt,
ho modificato i due valori sostituendoli appunto con il valore 64 e riprogrammato l’Atmega:

setPwmFrequency(9, 64)
setPwmFrequency(10, 64)

ma purtroppo ho misurato la frequenza in uscita da Arduino e ottengo sempre le due frequenze 31250 Hz e 3906.25 Hz

Forse ci sfugge un piccolo particolare e cioè, il software di controllo installato nel computer ha una schermata dalla quale si DEVE settare una frequenza o l’altra (31250 o 3906).
Probabilmente si deve togliere o disabilitare qualche riga del codice affinchè le “spunte” che si selezionano da software, non influiscano con la frequenza che io intendo utilizzare e cioè 490 Hz.

@Jumpereset ho visto che legge i valori salvati anche dalla EEPROM.

Considerando che a te serve quello che è il valore di default, commenta o cancella tutto il blocco istruzioni all’interno della funzione setPwmFrequency() e tagli la testa al toro.
Cosi ovunque venga richiamata quella funzione non avrà alcun effetto sulle impostazioni del PWM.

void setPwmFrequency(int pin, int divisor) 
{
  ////// nessuna riga o commenta tutto
}

Scusate, da ignorante, ma se metto un normalissimo elettrolitico da 10uF non “ammorbidisco” le frequenze, tanto da “trasformarle” in una corrente continua?

Puoi leggere questo articolo

Si riferisce al filtraggio di un’onda sinusoidale, ma il principio vale anche per un’onda quadra.

Interessante l’effetto evidenziato da una figura a circa metà articolo, dove con un bottone blu puoi cambiare la costante RC e vedere cosa succede.

Ciao,
P.