Jump to content
ELFORUM - Forumul electronistilor
Sign in to follow this  
Guest Bogdan Sig

Help, Comanda unui servomotor cu PWM (Atmega 32)

Recommended Posts

Guest Bogdan Sig

Salut am si eu un proiect. Comandarea unui servomotor(Futaba s3000) prin PWM. Consta ca la rotirea unui potentiometru sa se roteasca si servomotorul de exemplu rotesc spre dreapta sa se roteasca de la 90 gr la 180 si spre stanga de la 90 gr la 0 ca in videoul acesta

. Putem folosii orice uC, eu am ales Atmel mega32. Am realizat si o schema in Proteus, problema ar fi codul. Am gasit un cod care face asta dar trebuie adaptat la schema care o am si asta nu inteleg cum intra comanda ce se intampla, conectarea la pini. Codul e folosit tot la Atmega 32.Ledul acela l-am pus cu intentia de a sta aprins pe tot parcursul functionarii. Schema facuta in Proteus : http://www.fast-files.com/getfile.aspx?file=112110 #include#include#includevolatile int servo_value;int delay( void );int main( void ){ADCSRA |= 1 << ADEN;ADCSRA |= 1 << ADIE;sei();ADCSRA |= 1 << ADPS2;ADMUX |= 1 << REFS0;TCCR1A |= 1 << COM1A1 | 1 << COM1A0 | 1 << WGM11;TCCR1B |= 1 << WGM13 | 1 << WGM12 | 1 << CS10;ICR1 = 19999;DDRD |= 1 << PIND5;servo_value = 18059;ADCSRA |= 1 << ADSC;while(1){OCR1A = servo_value;//delay();//OCR1A = 19519;//delay();}}int delay( void ){ _delay_ms( 100 );return 1;}ISR( ADC_vect ){servo_value = (-1.427175 * ADC + 19519);ADCSRA |= 1 << ADSC;} 

Share this post


Link to post
Share on other sites

Nu e corect spus PWM, caci ai de a face cu PPM. Semnalul pentru controlul servo este cu perioada de 20ms (50Hz) si ai pulsul care variaza intre 1 si 2ms. Cu un semnal PWM ar trebui sa variezi factorul de umplere de la 5 la 10% in pasi mici, practic imposibil cu solutia aleasa.

 

Posted Image

 

Dar schema ai la ce ai facut? Nu am sa descarc din linkul tau. E de evitat genul asta de postare de schema. Nu toti avem licente de Proteus ca sa deschidem schema ta.

 

Din pacate nu lucrez cu Atmega, dar poate gasim noi problema ta.

Edited by thunderer

Share this post


Link to post
Share on other sites

si asta nu inteleg cum intra comanda ce se intampla, conectarea la pini.

N-am proteus si nici n-am de gand sa-l cumpar, asa ca nu pot vedea schema, asa ca vorbesc "teorie". Cum nici cu atmelurile n-am facut nimic, teoria e cam abstracta, da'... In principiu, din cate m-am prins eu, codul postat foloseste o intrare ADC ca sa citeasca resistenta si un timer ca sa genereze pulsurile PPM in functie de ce citeste ADC-ul.

ADCSRA |= 1 << ADEN;ADCSRA |= 1 << ADIE;sei();ADCSRA |= 1 << ADPS2;ADMUX |= 1 << REFS0;

Bucata asta a folosit la configurarea ADC-ului. Urmeaza configurarea timerului: 

TCCR1A |= 1 << COM1A1 | 1 << COM1A0 | 1 << WGM11;TCCR1B |= 1 << WGM13 | 1 << WGM12 | 1 << CS10;ICR1 = 19999;DDRD |= 1 << PIND5;

Ca sa afli ce pini sa folosesti, iei binisor foaia de catalog a controllerului si citesti despre ADC si timere. Presupun cu iesirea PPM e pe pinul D5, da' e numai o presupunere. Te las pe tine s-o verifici.Urmeaza o initializare a valorii pentru servo; valoarea va fi actualizata permanent prin citirea valorii rezistentei cu ajutorul ADC-ului.

servo_value = 18059;
ADCSRA |= 1 << ADSC;

Habar n-am ce a facut comanda anterioara, da' presupun ca porneste achizitia ADC.Bucla urmatoare (infinita) se ocupa cu scrierea valorii curente (masurate de ADC) in timer.

while(1){OCR1A = servo_value;//delay();//OCR1A = 19519;//delay();}

De ADC si timer se ocupa modulele corespunzatoare din controller. Schimbul de date intre modulele astea si programul tau se poate face in programul principal, asa cum se intampla cu actualizarea timerului, sau se face "atunci cand e nevoie" coordonat de sistemul de intreruperi - cand a terminat ce avea de facut, modulul hardware activeaza intreruperea corespunzatoare si "intrerupe" controllerul din ce facea. Dupa "tratarea intreruperii" programul se reia de unde a fost intrerupt. Un astfel de sistem e folosit in cazul ADC-ului - functia urmatoare e functia de tratare a intreruperilor de la ADC. Cand achizitia ADC e terminata (achizitia nu e instantanee, are nevoie de ceva timp ca sa fie realizata), intreruperea e activata si programul "intra" in functia asta. In functia e citita valoarea achizitionata si e pornita o noua achizitie, dupa care programul principal se reia, urmand sa fie intrerupt din nou cand achizitia proaspat pornita se termina. 

 ISR( ADC_vect ){servo_value = (-1.427175 * ADC + 19519);ADCSRA |= 1 << ADSC;}

Spor!

Edited by Liviu M

Share this post


Link to post
Share on other sites
Guest Bogdan Sig

@ thunderer

Am atasat poza cu schema facuta de mine. @Liviu M

Acum m-am apucat de citit datele din catalog amanuntit, eu trebuie sa fac prin comanda PWM.

Am codul de la de la un proiect realizat anul trecut de cineva a folosit PIC12F675, insa nu ma pot duce tot cu acelasi :(. Am atasat schema si semnalul PWM de pe osciloscop.

 

 

#include <pic.h>

#define Semnal  GPIO2

 

// Declarare functii

void Init();

unsigned int AdcRead();

 

int tmr1ms = 0xF82F; //65535-2000=63535 (F82F)

int AdcValue;

int timerValue ;//val ce o depun in TMR1 H si L

 

void main()

{

  Init();

 

  while(1)

  {

 

  }

 

;}

void Init()

{

 

  ANSEL  = 0b0010001;    // Fosc/8 , ANS0

  ADCON0 = 0b00000001; // channel 00, right justified

  TRISIO = 0b11111011;   // GPI02 output

  GPIO  = 0x00;  //GPIO output

  INTCON = 0b01100000;     //GiE,PEIE

  T1CON = 0b00000101;    //prescaler 1:1

  OPTION = 0b11000111;    //prescaler 256

  TMR0 = 0xB3;   // 256-78+1=179

  T0IE = 1;   //enable interrupts timer0

  GIE = 1;   //general enable interrupts

  T0IF = 0; //flag timer0

  TMR1IF = 0;    //flag-ul timeri1

  ADCON0 |= 0x02;  

}

 

unsigned int AdcRead()

{

  int HIGH=0;

 

  GODONE  = 1;     // Enable Go/Done

 

 

 

  if (GODONE=1)

  {

 

  HIGH = ADRESH; //incarca continutul din ADRESH in HIGH

  HIGH = HIGH*4+24;  

 

  }

 

  return HIGH;

}

 

void interrupt Isr(void)

{

  if((T0IE&T0IF)==1)  //If Timer0 Interrupt

  {

  AdcRead();  

  AdcValue = AdcRead();

  timerValue = tmr1ms + AdcValue;    //valoarea de 2 ms + valorea potentiometrului

  TMR1IE=0;     //enable interrupts timer1

  TMR1H = timerValue>>8; //deplaseaza valoarea cu 8 biti la dreapta

  TMR1L = timerValue;   //incarca valoarea

 

  Semnal = 1; // semnal PWM

 

  TMR1IE=1; //enable interuupts timer1 

  T0IF = 0; //reset flag timer0

  TMR0 = 0xB3;

 

 

 

  }

 

  if((TMR1IE&TMR1IF)==1)  //If Timer1 Interrupt

  {

  Semnal = 0; //semnal PWM

  TMR1IF = 0;   // reset flag timer1

 

  }

 

}

Edited by Bogdan Sig

Share this post


Link to post
Share on other sites

Eu as zice sa te opresti la o schema + un cod si sa te stradui sa intelegi ce se intampla acolo.

Ti-am zis mai sus sa citesti foaia de catalog - in special modulele ADC si Timer - ca sa determini pe ce pini trebuie sa conectezi rezistenta si comanda servo-ului.

Dupa cum arata schema ta, inca nu le-ai aflat (de exemplu, m-as astepta sa fie un pin cu ADC in nume pentru rezistenta).

Share this post


Link to post
Share on other sites

Cursorul potentiometrului trebuie conectat la unul din porturile PA0..7, iar pentru comanda efectiva a motorului cu semnal PWM vei folosi unul din porturile PD4 sau PD5 (daca folosesti OCR1A, respectiv OCR1B)

In rest, ce a scris LiviuM este valabil.

Share this post


Link to post
Share on other sites

Nu am avut rabdare sa analizez codul dar remarc unele ciudatenii.

 

Fac un singur exemplu:

TCCR1A |= 1 << COM1A1 | 1 << COM1A0 | 1 << WGM11;

de ce setezi COM1A0 ? nu cumva vrei sa fie PWM normal (neinversat?). Atunci ar trebui sa stergi OC1A la atingere prag.

(Clear OC1A/OC1B on compare match, set OC1A/OC1B at BOTTOM)
 
O alta observatie, dincolo de precedenta operatorilor, ar fi bine sa scrii ceva de genul:
TCCR1A |= (1<<COM1A1)|(1<<COM1A0)|(1<<WGM11);

De fapt,

TCCR1A |= (1<<COM1A1)|(1<<WGM11);

Apoi, nu stiu daca este atat de "urgent" sa generezi o intrerupere la sfarsitul fiecarei conversii, caci cred ca oricum aceasta apare mai repede decat semnalul tau PWM si nu prea vad sensul.

 

Apoi, valorile pentru impulsul minin si maxim ar trebui sa le definesti undeva si apoi sa lucrezi cu numele lor nu cu numere.

Exemplu:

servo_value = (-1.427175 * ADC + 19519);

ai putea sa definesti 

#define servo_minim 19519

si apoi ceva de genul:

servo_value = (coeficient* ADC + servo_minim);

Apoi orice ajustare va fi simpla, iar pentru cei care vor citi codul, va fi mai clar. Ar trebui ca cineva sa dedice 15 minute sa refaca tot calculul tau si eventual in timpul acesta putea scrie programul de la zero, daca era mai clar iti raspundea in 20 secunde.

Observ ca ai folosit un coeficient negativ (probabil compensezi PWM inversat). De ce ? Nu e mai simplu sa gandesti totul in pozitiv ? Comanzi unghiuri negative ?

 

Ma grabesc acum sa ies si nu am timp, dar maine cred ca iti prezint o solutie.

Edited by one

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.Terms of Use si Guidelines