Please, i need code info, for speed regulation of a dc motor

Hi,

I want to regulate the speed of a dc Motor by reading the back emf voltage. I can read the back emf voltage by ADC, but i don't know how to use this info in my code.

I know how back emf works. I searched, but i couldn't find code to read - experiment.

Please do you have a link or any code info?

Thank you!

I can read the back emf voltage by ADC

Are you sure? How? With some motor controllers, this is not even possible.

I don't use a motot controller.
I 've set Arduino PWM 62500Hz, every 5mS stop for 2mS, then i count the bemf and restart the PWM.

I don't use a motot controller.

Well, that is certainly a problem!

Why?

The Arduino output pins cannot power a motor, nor can the 5V regulator (in most cases). You can destroy the Arduino just by trying.

Sorry, I confused.
The hardware is ok. I have an H-Bridgeand is working fine.

The only that i need is help on software.
I cannot find code for experiment on the relation between BEMF and PWM.

billys7:
I cannot find code for experiment on the relation between BEMF and PWM.

Well if you know the relationship between BEMF and PWM, and this....

I know how back emf works

.... indicates that you do, write the code yourself. Why do you expect that the code's already out there? As a minimum, do the design of the program in pseudo-code to get the logic straight, then try to convert that to actual code, and ask for help if you get stuck.

Do you have the logic sorted in your head?

Some concrete code and circuit details are necessary if you want people to help,
otherwise everyone's just guessing the details.

As I see it you may already have some hardware, or not, for reading the EMF,
you have an Hbridge so you are reversing the motor (?). You want to regularly
sample the back EMF when the bridge is inactive to sense the rotor speed?

You know this means dropping the drive to the motor for long enough for the inductive
currents to subside and closing one switch on the bridge to provide a reference and
measure the voltage on the other arm, then re-enable the bridge. In theory you
can do this on every PWM cycle if using a low enough PWM frequency and employing
mixed decay mode

MarkT:
Some concrete code ....

That's something this simple civil engineer can relate to....

Here is the part of ADC and PWM of my code:

byte Speed;
word BEMF[16], BEMF_AVG, BEMFS;// Value to store analog result

//----------------------------------Timers Setup--------------------------------------------------------------
TCCR1A = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
TCCR1B = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
TCCR1C = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
TCNT1H = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
TCNT1L = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
OCR1AH = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
OCR1AL = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
OCR1BH = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
OCR1BL = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
TIMSK1 = 0x00; //Μηδενισμός Timer1 πριν τη χρήση
TIFR1 = 0x00; //Μηδενισμός Timer1 πριν τη χρήση

OCR1A = 4; // compare match register 16MHz/256/15625Hz Στόχος το 15625Hz
TCCR1B |= (1 << WGM12); // CTC mode, Normal port operation, OC1A/OC1B disconnected.
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
//----------------------------------PWM Setup------------------------------------------------------------------------
TCCR0A = 0x00; //Μηδενισμός Timer0 πριν τη χρήση
TCCR0B = 0x00; //Μηδενισμός Timer0 πριν τη χρήση
TCNT0 = 0x00; //Μηδενισμός Timer0 πριν τη χρήση
OCR0A = 0x00; //Μηδενισμός Timer0 πριν τη χρήση
OCR0B = 0x00; //Μηδενισμός Timer0 πριν τη χρήση
TIMSK0 = 0x00; //Μηδενισμός Timer0 πριν τη χρήση
TIFR0 = 0x00; //Μηδενισμός Timer0 πριν τη χρήση

TCCR0A = 0xA3; //10100011 Fast PWM Mode(non-inverting)Clear OC0A on Compare Match, set OC0A at BOTTOM,
TCCR0B = 0x01; //00000001 No Prescaling PWM 62.500Hz FastPWM 8-bit Top = 255 BOTTOM - MAX
//TCCR0A = 0xA1; //10100001 PWM, Phase Correct Clear OC0A on Compare Match when up-counting. Set OC0A on Compare Match when down-counting.
//TCCR0B = 0x01; //00000001 No Prescaling PWM 31.372,55Hz PWM = fclk_I/O / (N * 510)
//The N variable represents the prescale factor (1, 8, 64, 256, or 1024).

//------------------------------End Timers Setup---------------------------------------------------------------------

//----------------------------------ADC Setup----------------------------------------------------------------------

ADCSRA = 0x00;
ADMUX = 0x00;
ADCSRB = 0x00;

ADMUX = 0x45; //01000101 = Avcc(+5v) as voltage reference, 10-bit,ADC5
//ADCSRA = 0xEF; //11101111 = Power up the ADC,Start Conversio, ADC Enable, Auto Trigger, Enable ADC Interrupt,

//--------------Main Loop--------------------------------------------------------------------------------------------
void loop()
{

Serial.print("Bemf = “);
Serial.print(BEMFS);
Serial.print(” S = “);
Serial.print(Speed);
Serial.print(” OC = ");
Serial.println(OCR0B);

}
//----------------End Main Loop--------------------------------------------------------------------------------------

//===============================================================================================
// Interrupts
//===============================================================================================

ISR(TIMER1_COMPA_vect) //interrupt 15.625Hz κάθε 64μS
{
BEMF_Counter++; //Αυξάνει το μετρητή κατά 1 κάθε 64μS

//-------------------------Έναρξη μέτρησης BEMF-----------------------------------------------------------------------------------------------
// Χρόνος που απαιτείται = 25mS + 2mS + 2,25mS = 29,25mS Περίπου 34 φορές το δευτερόλεπτο
if (BEMF_Counter >= 391) //Αν BEMF_Counter >= 25mS (391 χ 64μS)
{TCCR0A &= ~(1<<COM0B1); //Σταμάτησε PWM στο OCR0B,
if ((BEMF_Counter >= 423)&&((PORTD & 32) == 0)) //Μετά από 2mS((423-391)*64μS) και εάν το PWM στην D5 έχει σταματήσει
{ADCSRA = 0xEF;} //Ξεκίνα τη μέτρηση του BEMF
}
//-------------------------Τέλος έναρξης μέτρησης BEMF----------------------------------------------------------------------------------------
TCNT1 = 0x00;
TIFR1 = 0x00; //Timer1 INT Flag Reg: Clear Timer Overflow Flag
}

//---------------------------Interrupt service routine for the BEMF and update power-------------------------------------------------------------
ISR(ADC_vect){

BEMF[w] = ADCL | (ADCH << 8 ); //Πρέπει να διαβάσει πρώτα τη low για να ‘κλειδώσει’ η τιμή 10-bit αποτέλεσμα
if (++w >= 15) //Για 15 δείγματα περίπου 1,86mS με 2,25 mS
{w=0; ADCSRA &= ~(1<<ADEN); } //Τέλος μέτρησης

BEMF_AVG = (BEMF[2]+BEMF[3]+BEMF[4]+BEMF[5]+BEMF[6]+BEMF[7]+BEMF[8]+BEMF[9]+BEMF[10]+BEMF[11]+BEMF[12]+BEMF[13]+BEMF[14])/13;
BEMF_Counter = 0; //Μηδένισε τον BEMF_Counter
TCCR0A |= (1<<COM0B1); //Ξεκίνα PWM στο OCR0B
BEMFS = BEMF_AVG/2.9;
// Back-EMF Code
if (BEMFS > 254){BEMFS = 255;}
if ((BEMFS>Speed)&& (OCR0B > 0 )){OCR0B–;}
if ((BEMFS<Speed)&& (OCR0B < 255)){OCR0B++;}
if ((BEMFS==Speed)||(BEMFS == 0)){OCR0B = Speed;}
//if (BEMFS>Speed){if (OCR0B-- <1 ){OCR0B = 0; }}
//if (BEMFS<Speed){if (OCR0B++ >254){OCR0B = 255;}}

}
//-----------------------End Interrupt service routine for the BEMF and update power-----------------------------------------------------
//===============================================================================================
// End Interrupts
//===============================================================================================

The variable “Speed” depends on another part of the code, and the values which takes are from 0 to 255.
The way i am using interrupt on Timer1 is because i will also use it for other purposes.

About the circuit:
Each end of the motor’s pins has a resistor of 8k2. On the other end of the resistors, these resistors are connected together,
also with the arduino’s pin A5 and with a third resistor of 8k2. The other end of the third resistor is connected to the ground, as a voltage divider. In parallel with the third resistor a capacitor 100nF. Maximum applied voltage 15V.

My problems are :
-I don’t have an oscilloscope to check the timings,
-If, with my fingers, forced the motor to stop, without decreasing the voltage, the BEMF is not 0, and is not increasing the OCR0B enough.
-Very important is the capacitor, parallel with the third resistor, which i don’t know how to calculate the value.

Any thoughts or suggestions are welcome.

Please edit your post and put the code in code tags “<> button”

jremington:
Please edit your post and put the code in code tags “<> button”

How can i do this ?

Here is the part of ADC and PWM of my code:

byte Speed;
word BEMF[16], BEMF_AVG, BEMFS;// Value to store analog result

//----------------------------------Timers Setup--------------------------------------------------------------
 TCCR1A = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 TCCR1B = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 TCCR1C = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 TCNT1H = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 TCNT1L = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 OCR1AH = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 OCR1AL = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 OCR1BH = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 OCR1BL = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 TIMSK1 = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 TIFR1   = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
   
 OCR1A = 4;                          // compare match register 16MHz/256/15625Hz Στόχος το 15625Hz
 TCCR1B |= (1 << WGM12);     // CTC mode, Normal port operation, OC1A/OC1B disconnected.
 TCCR1B |= (1 << CS12);        // 256 prescaler
 TIMSK1 |= (1 << OCIE1A);     // enable timer compare interrupt
//----------------------------------PWM    Setup------------------------------------------------------------------------  
 TCCR0A = 0x00;            //Μηδενισμός Timer0 πριν τη χρήση
 TCCR0B = 0x00;            //Μηδενισμός Timer0 πριν τη χρήση
 TCNT0  = 0x00;            //Μηδενισμός Timer0 πριν τη χρήση
 OCR0A  = 0x00;            //Μηδενισμός Timer0 πριν τη χρήση
 OCR0B  = 0x00;            //Μηδενισμός Timer0 πριν τη χρήση
 TIMSK0 = 0x00;            //Μηδενισμός Timer0 πριν τη χρήση
 TIFR0   = 0x00;            //Μηδενισμός Timer0 πριν τη χρήση
 
 
 TCCR0A = 0xA3;            //10100011 Fast PWM Mode(non-inverting)Clear OC0A on Compare Match, set OC0A at BOTTOM,
 TCCR0B = 0x01;            //00000001 No Prescaling PWM 62.500Hz FastPWM 8-bit Top = 255 BOTTOM - MAX
 //TCCR0A = 0xA1;         //10100001 PWM, Phase Correct Clear OC0A on Compare Match when up-counting. Set OC0A on Compare Match when down-counting.
 //TCCR0B = 0x01;         //00000001 No Prescaling PWM 31.372,55Hz  PWM = fclk_I/O / (N * 510)
                                  //The N variable represents the prescale factor (1, 8, 64, 256, or 1024).

//------------------------------End Timers Setup---------------------------------------------------------------------

//----------------------------------ADC Setup----------------------------------------------------------------------  

 ADCSRA = 0x00;
 ADMUX  = 0x00;
 ADCSRB = 0x00;

 ADMUX  = 0x45;                       //01000101 = Avcc(+5v) as voltage reference, 10-bit,ADC5
 //ADCSRA = 0xEF;                    //11101111 = Power up the ADC,Start Conversio, ADC Enable, Auto Trigger, Enable ADC Interrupt,

//--------------Main Loop--------------------------------------------------------------------------------------------
void loop()
{
 
  Serial.print("Bemf = ");
  Serial.print(BEMFS);  
  Serial.print(" S = ");
  Serial.print(Speed);
  Serial.print(" OC = ");
  Serial.println(OCR0B);
 
   
}
//----------------End Main Loop--------------------------------------------------------------------------------------

//===============================================================================================
//                                   Interrupts
//===============================================================================================

ISR(TIMER1_COMPA_vect)   //interrupt 15.625Hz κάθε 64μS
{
  BEMF_Counter++;           //Αυξάνει το μετρητή κατά 1 κάθε 64μS
 


//-------------------------Έναρξη μέτρησης BEMF-----------------------------------------------------------------------------------------------
//         Χρόνος που απαιτείται = 25mS + 2mS + 2,25mS = 29,25mS Περίπου 34 φορές το δευτερόλεπτο
if (BEMF_Counter >= 391)                                           //Αν BEMF_Counter >= 25mS (391 χ 64μS)
  {TCCR0A &= ~(1<<COM0B1);                                        //Σταμάτησε PWM στο OCR0B,
if ((BEMF_Counter >= 423)&&((PORTD & 32) == 0))                   //Μετά από 2mS((423-391)*64μS) και εάν το PWM στην D5 έχει σταματήσει
  {ADCSRA = 0xEF;}                                                //Ξεκίνα τη μέτρηση του BEMF
  }    
//-------------------------Τέλος έναρξης μέτρησης BEMF----------------------------------------------------------------------------------------
TCNT1 = 0x00;
TIFR1 = 0x00;          //Timer1 INT Flag Reg: Clear Timer Overflow Flag
}

//---------------------------Interrupt service routine for the BEMF and update power-------------------------------------------------------------
ISR(ADC_vect){                                            
 
   BEMF[w] = ADCL | (ADCH << 8 );                 //Πρέπει να διαβάσει πρώτα τη low για να 'κλειδώσει' η τιμή    10-bit αποτέλεσμα
   if (++w >= 15)                                          //Για 15 δείγματα περίπου 1,86mS με 2,25 mS
     {w=0; ADCSRA &= ~(1<<ADEN);     }          //Τέλος μέτρησης
                                                           
                                                           
   BEMF_AVG = (BEMF[2]+BEMF[3]+BEMF[4]+BEMF[5]+BEMF[6]+BEMF[7]+BEMF[8]+BEMF[9]+BEMF[10]+BEMF[11]+BEMF[12]+BEMF[13]+BEMF[14])/13;
   BEMF_Counter = 0;                                      //Μηδένισε τον BEMF_Counter
   TCCR0A |= (1<<COM0B1);                             //Ξεκίνα PWM στο OCR0B
   BEMFS = BEMF_AVG/2.9;
//            Back-EMF Code    
   if (BEMFS > 254){BEMFS = 255;}
   if ((BEMFS>Speed)&& (OCR0B > 0  )){OCR0B--;}
   if ((BEMFS<Speed)&& (OCR0B < 255)){OCR0B++;}    
   if ((BEMFS==Speed)||(BEMFS == 0)){OCR0B = Speed;}
   //if (BEMFS>Speed){if (OCR0B-- <1  ){OCR0B = 0;  }}
   //if (BEMFS<Speed){if (OCR0B++ >254){OCR0B = 255;}}
   
 }
//-----------------------End Interrupt service routine for the BEMF and update power-----------------------------------------------------
//===============================================================================================
//                               End Interrupts
//===============================================================================================

The variable “Speed” depends on another part of the code, and the values which takes are from 0 to 255.
The way i am using interrupt on Timer1 is because i will also use it for other purposes.

About the circuit:
Each end of the motor’s pins has a resistor of 8k2. On the other end of the resistors, these resistors are connected together,
also with the arduino’s pin A5 and with a third resistor of 8k2. The other end of the third resistor is connected to the ground, as a voltage divider. In parallel with the third resistor a capacitor 100nF. Maximum applied voltage 15V.

My problems are :
-I don’t have an oscilloscope to check the timings,
-If, with my fingers, forced the motor to stop, without decreasing the voltage, the BEMF is not 0, and is not increasing the OCR0B enough.
-Very important is the capacitor, parallel with the third resistor, which i don’t know how to calculate the value.

Any thoughts or suggestions are welcome.

billys7:
How can i do this ?

When you got to this forum you obviously ignored the "read this first before posting"
sticky threads completely. Go and read them and you'll know how to use the forum...

Ah good - its there now....

After experiments, seems like the hardware measures are ok.
I extend the time of the PWM to work for one second, and then stops for one second.
My multimeter measure the voltage, increasing and decreasing. Everything was ok.

But the serial port of arduino saw me, that PWM never stops and Bemf measure was not 0 when the PWM was off

Some observations:

You are setting up various machine registers at top level. Don't do it this way, the values
will probably be trashed by the Arduino initialization code. Top level statements get executed
early, you should be doing your setup in setup() - that's why its there.

This is the wrong way to update a 16 bit timer register:

 TCNT1H = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση
 TCNT1L = 0x00;            //Μηδενισμός Timer1 πριν τη χρήση

The hardware has interlocks that require a particular assembler instruction
sequence to update the 16 bit registers atomically - in particular they must
be written in the right sequence. Rather than do it your self use the provided
macros/compiler-smarts that get it right for you:

 TCNT1 = 0x0000;            //Μηδενισμός Timer1 πριν τη χρήση

Your code seems to just use interrupt driven ADC conversions to measure the
back EMF. This can't work, you must make sure the H-bridge is quiescent before
taking an analog reading or you won't be seeing the back EMF at all, but just the
H-bridge driving the motor to either rail.

Thank you for your answer.

In the first interrupt i stop the PWM by setting TCCR0A &= ~(1<<COM0B1);, and it is working.

//===============================================================================================
//                                   Interrupts
//===============================================================================================

ISR(TIMER1_COMPA_vect)   //interrupt 15.625Hz κάθε 64μS
{
  BEMF_Counter++;           //Αυξάνει το μετρητή κατά 1 κάθε 64μS
 


//-------------------------Έναρξη μέτρησης BEMF-----------------------------------------------------------------------------------------------
//         Χρόνος που απαιτείται = 25mS + 2mS + 2,25mS = 29,25mS Περίπου 34 φορές το δευτερόλεπτο
if (BEMF_Counter >= 391)                                           //Αν BEMF_Counter >= 25mS (391 χ 64μS)
  {TCCR0A &= ~(1<<COM0B1);                                        //Σταμάτησε PWM στο OCR0B,
if ((BEMF_Counter >= 423)&&((PORTD & 32) == 0))                   //Μετά από 2mS((423-391)*64μS) και εάν το PWM στην D5 έχει σταματήσει
  {ADCSRA = 0xEF;}                                                //Ξεκίνα τη μέτρηση του BEMF
  }    
//-------------------------Τέλος έναρξης μέτρησης BEMF----------------------------------------------------------------------------------------
TCNT1 = 0x00;
TIFR1 = 0x00;          //Timer1 INT Flag Reg: Clear Timer Overflow Flag
}

I think that the problem is here:
I don’t know if i have set it ok.

I want to take the 15 samples, to make the average of the 13, and when it will finish it, then to update the OCR0B.

//---------------------------Interrupt service routine for the BEMF and update power-------------------------------------------------------------
ISR(ADC_vect){                                            
 
   BEMF[w] = ADCL | (ADCH << 8 );                 //Πρέπει να διαβάσει πρώτα τη low για να 'κλειδώσει' η τιμή    10-bit αποτέλεσμα
   if (++w >= 15)                                          //Για 15 δείγματα περίπου 1,86mS με 2,25 mS
     {w=0; ADCSRA &= ~(1<<ADEN);     }          //Τέλος μέτρησης
                                                           
                                                           
   BEMF_AVG = (BEMF[2]+BEMF[3]+BEMF[4]+BEMF[5]+BEMF[6]+BEMF[7]+BEMF[8]+BEMF[9]+BEMF[10]+BEMF[11]+BEMF[12]+BEMF[13]+BEMF[14])/13;
   BEMF_Counter = 0;                                      //Μηδένισε τον BEMF_Counter
   TCCR0A |= (1<<COM0B1);                             //Ξεκίνα PWM στο OCR0B
   BEMFS = BEMF_AVG/2.9;
//            Back-EMF Code    
   if (BEMFS > 254){BEMFS = 255;}
   if ((BEMFS>Speed)&& (OCR0B > 0  )){OCR0B--;}
   if ((BEMFS<Speed)&& (OCR0B < 255)){OCR0B++;}    
   if ((BEMFS==Speed)||(BEMFS == 0)){OCR0B = Speed;}
   //if (BEMFS>Speed){if (OCR0B-- <1  ){OCR0B = 0;  }}
   //if (BEMFS<Speed){if (OCR0B++ >254){OCR0B = 255;}}
   
 }
//-----------------------End Interrupt service routine for the BEMF and update power-----------------------------------------------------
//===============================================================================================
//                               End Interrupts
//===============================================================================================