Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

Problema servocomando

Elettronica lineare e digitale: didattica ed applicazioni

Moderatori: Foto UtenteBrunoValente, Foto UtenteDirtyDeeds, Foto Utentecarloc, Foto UtenteIsidoroKZ, Foto Utenteg.schgor

0
voti

[61] Re: Problema servocomando

Messaggioda Foto Utentefantamed » 18 nov 2013, 12:26

ciao,ho cominciato a descrivermi il codice per verificare se ho capito i passaggi e il significato di quello che non conoscevo,volevo quindi verificare se sto interpretando bene quello che leggo.potresti darci un'occhio ? :roll:
inizio dal settaggio:

#define PL1 RB0 // puls1
#define PL2 RB1 // puls2
#define PL3 RB2 // puls3
#define PL4 RB3 // puls4
#define LED RA0 //led rete
#define SER1 RB4 //segnale servo 1
#define SER2 RB5 //segnale servo 2

#define numServo 2 // imposto che i servo sono 2

#define byteIntTime numServo*2 // imposto i byte di IntTime a 4 che sono i primi 4 periodi per il movimento dei servo ,e cosi? il segno * è un per(x) ?

#define maxIntNumber byteIntTime*8 // potresti descrivermelo tu?

#define valueLeft 131 // posizione dx 45°
#define valueRight 6 // posizione sx 45°
#define valueCenter 69 // posizione centrale

volatile unsigned char intNumber; //dichiaro la variabile volatile (cioe che puo cambiare) di tipo char per il conteggio interrupt

volatile unsigned char intTime[byteIntTime]; //dichiaro un'array volatile di tipo char,di dimensione 16,ovvero 15 indici, poiche si parte dallo 0

void settaggio(void)
{
OPTION=0b10000010;
INTCON=0b10100000;

TRISA=0b11111110; //domanda : metti a 1 per i disturbi ?,ma come mai da RA0 fino a RA7,va bene anche cosi (TRISA=0b00011110;)

TRISB=0b00001111; //tanto gli ultimi 3 bit non esistono nel 16f84A,quindi chiudo con res.di pull up da RA1 a RA4//

LED=0;
SER1=0;
SER2=0;

TMR0=131; // assegno al timer0 il valore 131 ,ovvero mmancano 124 cicli dei 255 per andare in overflow

intNumber=0; // assegno alla variabile di conteggio intrrupt il valore 0

}

grazie Buona giornata
Avatar utente
Foto Utentefantamed
30 5
Frequentatore
Frequentatore
 
Messaggi: 110
Iscritto il: 8 nov 2013, 13:44

0
voti

[62] Re: Problema servocomando

Messaggioda Foto Utentec1b8 » 18 nov 2013, 12:51

C'è qualche imprecisione in quello che hai scritto.

fantamed ha scritto:#define byteIntTime numServo*2 // imposto i byte di IntTime a 4 che sono i primi 4 periodi per il movimento dei servo ,e cosi? il segno * è un per(x) ?

Esatto, il simbolo '*' è una moltiplicazione, in questo caso moltiplica per 2.
byteIntTime assume quindi il valore di numServo moltiplicato per 2, numServo vale 2 quindi 2*2=4

fantamed ha scritto:#define maxIntNumber byteIntTime*8 // potresti descrivermelo tu?

maxIntNumber contiene il numero massimo di interrupt, deve valere 16, perché 16 è il numero di interrupt che ci aspettiamo per completare un ciclo di 20ms. Calcolato così non va bene perché varrebbe 4*8=32. Meglio definirlo come 16 a costante. Chiedo scusa per l'errore, spero sia l'unico.

fantamed ha scritto:volatile unsigned char intNumber; //dichiaro la variabile volatile (cioe che puo cambiare) di tipo char per il conteggio interrupt

volatile non significa che può cambiare, tutte le variabili possono cambiare. volatile significa che la variabile è utilizzata e modificata sia in interrupt che nel normale flusso del programma.

fantamed ha scritto:volatile unsigned char intTime[byteIntTime]; //dichiaro un'array volatile di tipo char,di dimensione 16,ovvero 15 indici, poiche si parte dallo 0

No, dichiaro un array di dimensione 4, ovvero di 4 elementi. Il primo indice di un qualsiasi array è lo 0, quindi l'ultimo indice è identificato dal numero elementi meno 1 (nel nostro caso quindi da 3). Non puoi dire array di dimensione 4 ovvero 3 indici (o come hai scritto di dimensione 16 ovvero 15 indici). L'array di dimensione n ha n indici, il primo è lo 0 l'ultimo è n-1, ma il numero di indici è uguale alla dimensione.

fantamed ha scritto:TRISA=0b11111110; //domanda : metti a 1 per i disturbi ?,ma come mai da RA0 fino a RA7,va bene anche cosi (TRISA=0b00011110;)

Sinceramente mi sono preoccupato solo che RA0 fosse una uscita, va bene come hai scritto tu.

fantamed ha scritto:TMR0=131; // assegno al timer0 il valore 131 ,ovvero mmancano 124 cicli dei 255 per andare in overflow

intNumber=0; // assegno alla variabile di conteggio intrrupt il valore 0

Si, attento però che l'overflow si calcola sul numero 256 e non su 255.

Ciao.
Fabio
Avatar utente
Foto Utentec1b8
3.595 3 8 13
G.Master EY
G.Master EY
 
Messaggi: 1770
Iscritto il: 15 gen 2009, 15:23

0
voti

[63] Re: Problema servocomando

Messaggioda Foto Utentefantamed » 18 nov 2013, 22:48

:ok: fabio,grazie per le correzioni,ho sistemato le didascalie e rifatto i conti

Una cosa non inerente, come mai accanto alla mia foto c'e scritto online?

programma 8-[

Codice: Seleziona tutto
#include <PIC.h>                  // contiene i nomi mnemonici di registri e porte

__CONFIG(0x3FF9);               //configurazione fuses

#include "settaggioservo2.h"         // file che contiene il settaggio delle porte

void interrupt ISR (void)
{
   if (T0IF==1)            // se si e verificato un'interrupt
    {                             
      if (intNumber>=byteIntTime) {      // se il numero di interrupt e magg.o uguale a 4   
         TMR0=100;                                // imposto il timer0 a 100  (perche?)
      } else {                   
         SER1=0;                        //azzero servo1
         SER2=0;                        // azzero servo2
         TMR0=intTime[intNumber];    //assegno al tiemr0 il valore  4 che si incrementa fino a maxIntNumber 16
         if (intNumber==0) SER1=1;      // se siamo nel primo periodo 
         if (intNumber==2) SER2=1;       // se siamo nel secondo periodo
      }
      intNumber++;             //incremento di 1 il numero di interrupt
      if (intNumber==maxIntNumber)   // se gli interrupt sono arrivati a 15
         intNumber=0;        // azzero il conteggio degli interrupt per ricontare da 0
      T0IF=0;                   //  azzero il flag per nuovo interrupt
   }
}


void setServo(unsigned char servo, unsigned char time)   //Routine per movimento servo
{
   servo--;                    // decremento servo di 1 quindi da 2 diventa 1
   servo=servo<<1;         // assegno a servo il valore di servo con shift a sinistra cioe diventa 2
   intTime[servo]=time;                     // ??..
   servo++;                           // incremento servo di 1
   intTime[servo]=200-time;              // ??..
}

void main(void)
{
   settaggio();
   
   setServo(1, valueLeft);          // inizio con il servo1 in posizione tutto dx (131)
   setServo(2, valueCenter);      // inizio con il servo2 in posizione centrale (69)
   while(1) {
   }
}

abbi pazienza ma non riesco mai a postare il codice in ordine :mrgreen:
buona serata O_/
Avatar utente
Foto Utentefantamed
30 5
Frequentatore
Frequentatore
 
Messaggi: 110
Iscritto il: 8 nov 2013, 13:44

0
voti

[64] Re: Problema servocomando

Messaggioda Foto Utentec1b8 » 19 nov 2013, 0:03

Ti ho riscritto i commenti nel codice, correggendone qualcuno:
Codice: Seleziona tutto
    #include <PIC.h>                // contiene i nomi mnemonici di registri e porte

    __CONFIG(0x3FF9);               //configurazione fuses

    #include "settaggioservo2.h"    // file che contiene il settaggio delle porte

    void interrupt ISR (void)
    {
        if (T0IF==1)                // se si e verificato un'interrupt del Timer0
        {                             
            if (intNumber>=byteIntTime) {   // se il numero di interrupt e magg.o uguale a 4   
                TMR0=100;                   // imposto il timer0 a 100: dal 5 al 16 interrupt
                                            // si eseguono solo attese di 1250us senza impostare
                                            // nessun tipo di segnale
            } else {                   
                SER1=0;                     // azzero servo1
                SER2=0;                     // azzero servo2
                TMR0=intTime[intNumber];    // assegno al tiemr0 il valore di durata dell'attuale
                                            // interrupt. Ricordiamoci che:
                                            // 1° interrut = durata Ton servo1
                                            // 2° interrupt = durata per completare 2500us servo1
                                            // 3° interrut = durata Ton servo2
                                            // 4° interrupt = durata per completare 2500us servo2
                if (intNumber==0) SER1=1;   // se siamo nel primo interrupt siamo nel Ton servo1
                if (intNumber==2) SER2=1;   // se siamo nel terzo interrupt siamo nel Ton servo2
            }
            intNumber++;                    //incremento di 1 il numero di interrupt
            if (intNumber==maxIntNumber)    // se gli interrupt sono arrivati a 16
                intNumber=0;                // azzero il conteggio degli interrupt per ricontare da 0
            T0IF=0;                         //  azzero il flag per nuovo interrupt
        }
    }


    //Routine per impostare pisizione di un servo
    // riceve 2 parametri:
    // char servo = numero del servo da impostare. Può valere 1 o 2
    // char time = durata del Ton che si desidera impostare al servo
    //                imdicato nel primo parametro
    void setServo(unsigned char servo, unsigned char time)   
    {
        servo--;                        // decremento servo di 1 quindi:
                                        // - se valeva 1 adesso diventa 0
                                        // - se valeva 2 adesso diventa 0
        servo=servo<<1;                 // assegno a servo il valore di servo con shift a sinistra
                                        // cioè moltiplico per 2:
                                        // - se routine richiamata con servo che valeva 1 adesso vale 0
                                        // - se routine richiamata con servo che valeva 2 adesso vale 2
        intTime[servo]=time;            // salvo il tempo Ton richiesto nella posizione dell'interrupt
        servo++;                        // incremento servo di 1
        intTime[servo]=200-time;        // Calcolo (200-time) e salvo il temponecessario a completare 2500us
                                        // per il servo
    }

    void main(void)
    {
        settaggio();
       
        setServo(1, valueLeft);         // inizio con il servo1 in posizione tutto dx (131)
        setServo(2, valueCenter);       // inizio con il servo2 in posizione centrale (69)
        while(1) {
        }
    }
Fabio
Avatar utente
Foto Utentec1b8
3.595 3 8 13
G.Master EY
G.Master EY
 
Messaggi: 1770
Iscritto il: 15 gen 2009, 15:23

0
voti

[65] Re: Problema servocomando

Messaggioda Foto Utentefantamed » 19 nov 2013, 12:54

perdonami,non riesco a capire questo:
c1b8 ha scritto: servo--; // decremento servo di 1 quindi:
// - se valeva 1 adesso diventa 0
// - se valeva 2 adesso diventa 0 :?:

qui diventa 1
c1b8 ha scritto:servo=servo<<1; // assegno a servo il valore di servo con shift a sinistra
// cioè moltiplico per 2:
// - se routine richiamata con servo che valeva 1 adesso vale 0
// - se routine richiamata con servo che valeva 2 adesso vale 2 :?:

qui se moltiplico per 2 diventa 4
c1b8 ha scritto:intTime[servo]=time; // salvo il tempo Ton richiesto nella posizione dell'interrupt
servo++; // incremento servo di 1
intTime[servo]=200-time; // Calcolo (200-time) e salvo il temponecessario a completare 2500us // per il servo

questo pezzo non riesco a capirlo ,potresti spiegarmi cosa accade esattamente,sento che i miei unici 2 neuroni litigano ma non capiscono :lol: :lol: :lol: :lol:

Buona giornata
Avatar utente
Foto Utentefantamed
30 5
Frequentatore
Frequentatore
 
Messaggi: 110
Iscritto il: 8 nov 2013, 13:44

0
voti

[66] Re: Problema servocomando

Messaggioda Foto Utentec1b8 » 19 nov 2013, 15:26

Provo a spiegartela.

Abbiamo detto che per ogni servo abbiamo bisogno di generare 2 interrupt, uno per la durata del Ton richiesto ed uno per il tempo necessario ad ottenere 2500us.
Alcuni esempi:
1) Vogliamo posizionare il servo1 a 1000us (tutto a sx). Abbiamo bisogno di creare un interrupt della durata di 1000us (Ton) ed uno della durata di 2500-1000=1500us
2) Vogliamo posizionare il servo1 a 1500us (centro). Abbiamo bisogno di creare un interrupt della durata di 1500us (Ton) ed uno della durata di 2500-1500=1000us
3) Vogliamo posizionare il servo1 a 2000us (tutto a dx). Abbiamo bisogno di creare un interrupt della durata di 2000us (Ton) ed uno della durata di 2500-2000=500us

L'idea è quella di memorizzare questi due tempi in 2 elementi del nostro array intTime. 2 elementi per ogni servo, quindi in totale 4 elementi.
In questo modo andiamo a memorizzare i nostri dati in questo modo:
intTime[0]=tempo di Ton del servo1. Nel primo elemento salviamo il tempo Ton del servo1
intTime[1]=tempo di (2500-Ton) del servo1. Nel secondo elemento salviamo il tempo 2500-Ton del servo1
intTime[2]=tempo di Ton del servo2. Nel terzo elemento salviamo il tempo Ton del servo2
intTime[3]=tempo di (2500-Ton) del servo2. Nel quarto elemento salviamo il tempo 2500-Ton del servo2

La nostra routine setServo riceve il numero di servo che vogliamo impostare, questo numero può valere 1 o 2. La prima cosa che deve fare questa routine è calcolare quali elementi dell'array modificare in funzione del numero di servo passatogli.
Se gli passiamo il servo 1 deve modificare gli elementi 0 e 1.
Se gli passiamo il servo 2 deve modificare gli elementi 2 e 3.
Come effettua questo calcolo:
- il primo elemento da modificare è uguale a (servo-1)*2. Infatti:
se gli passiamo 1 la routine esegue: (1-1)*2=0 ----> che è proprio il primo elemento del servo1
se gli passiamo 2 la routine esegue: (2-1)*2=2 ----> che è proprio il primo elemento del servo2
Questo calcolo viene eseguito dalle seguenti istruzioni:
Codice: Seleziona tutto
            servo--;          // esegue la (servo-1)
            servo=servo<<1;   // moltiplica per 2 il risultato della precedente


- il secondo elemento dell'array da modificare è uguale al primo elemento (quello calcolato poco prima) +1.
Questa semplice somma è eseguita da:
Codice: Seleziona tutto
            servo++;                        // incremento servo di 1


Abbiamo così calcolato i 2 indici dell'array da modificare. Ma come vanno modificati?
- Il primo elemento deve assumere il valore della posizione Ton che vogliamo il servo assuma. Questo tempo viene passato alla routine come parametro di nome time. Per valorizzare il primo elemento basta quindi scrivere, subito dopo aver calcolato l'indice del primo elemento e prima di calcolare il secondo indice:
Codice: Seleziona tutto
            intTime[servo]=time;  // salvo il tempo Ton richiesto


- il secondo elemento deve contenere la diffrenza tra 2500 e Ton, se il nostro Ton è in time basta fare 2500-time.
Attenzione però che in time non abbiamo scritto ad esempio 1000 ma il valore da assegnare al timer per avere 1000us. Quindi per un Ton di 1000us in time scriviamo 131, per un Ton di 1500us in time scriviamo 69, e avanti così. Nel secondo elemento non possiamo quindi scrivere bruttalmente 2500-time ma dobbiamo scrivere il valore che si andrebbe a scrivere su timer0 per avere un tempo di 2500-Ton:
- con Ton=1000 (timer=time=131) abbiamo 2500-1000=1500 che convertito per il timer0 vale 69
- con Ton=1500 (timer=time=69) abbiamo 2500-1500=1000 che convertito per il timer0 vale 131
- con Ton=2000 (timer=time=6) abbiamo 2500-2000=500 che convertito per il timer0 vale 194
come vedi la somma dei 2 valori da sempre come risultato 200:
- 131+69=200
- 69+131=200
- 6+194=200
Ecco che il secondo elemento è calcolato nel seguente modo:
Codice: Seleziona tutto
            intTime[servo]=200-time;        // Calcolo (200-time) e salvo il tempo necessario a completare 2500us


Spero sia più chiaro.
Fabio
Avatar utente
Foto Utentec1b8
3.595 3 8 13
G.Master EY
G.Master EY
 
Messaggi: 1770
Iscritto il: 15 gen 2009, 15:23

0
voti

[67] Re: Problema servocomando

Messaggioda Foto Utentefantamed » 21 nov 2013, 1:35

scusa fabio mi perdo con il TMR0,mi confonde.

potresti rispiegarmi l'interrupt e timer0 di questo programma?

Quello che deve fare il programma lo ho capito,ma poi mi confondo quando devo utilizzare il TMR0
Grazie Buona notte
Avatar utente
Foto Utentefantamed
30 5
Frequentatore
Frequentatore
 
Messaggi: 110
Iscritto il: 8 nov 2013, 13:44

1
voti

[68] Re: Problema servocomando

Messaggioda Foto Utentec1b8 » 21 nov 2013, 12:52

Questo è il segnale che dobbiamo generare.


Nel grafico ho ipotizzato che il servo1 sia inizialmente posizionato a sx e quindi si sposti al centro, mentre il servo2 è tutto posizionato a dx. Naturalmente non cambia nulla del nostro discorso se i servo fossero posizionati in modo diverso.

Come vedi quello che dobbiamo garantire è che tra l'inizio di un Ton ed il successivo, per ogni servo, ci siano 20ms. Dal momento che il nostro timer è uno solo, per garantire questi 20ms per ogni servo facciamo in modo che il Ton del servo2 inizi sempre 2,5ms dopo l'inizio del Ton del servo1.
In questo modo dobbiamo solo preoccuparci che il Ton del servo1 inizi ogni 20ms.

Il Ton del servo2 deve iniziare sempre 2,5ms dopo l'inizio del Ton del servo1, qualsiasi sia la durata del Ton del servo1. Dobbiamo quindi conoscere 2 tempi per il servo1: il tempo di Ton e la differenza tra 2,5ms e Ton.
Quando servo1 è tutto a sx abbiamo Ton=1ms e differenza=2,5-1=1,5ms
Quando servo1 è al centro abbiamo Ton=1,5ms e differenza=2,5-1,5=1ms
Stesso discorso viene fatto per il servo2, anche per lui abbiamo bisogno di conoscere 2 tempi.

La nostra routine di interrupt fa esattamente questo:
- al primo interrupt accende il segnale del servo1 (siamo all'inizio del Ton del servo1) e si mette in attesa per la durata del Ton stesso (nel nostro caso imposta il timer per attendere 1ms).
- al secondo interrupt (siamo alla fine del Ton, cioè dopo quel 1ms impostato al primo interrupt), spegne il segnale di Ton e attende il tempo che serve a completare i 2,5ms. Imposta cioè il timer per attendere 2,5-1=1,5ms
- al terzo interrupt (dopo 1,5ms dal precedente) siamo all'inizio del Ton del servo2. Accende il Ton del servo2 ed attende 2ms (durata del Ton)
- al quarto interrupt spegne Ton e attende il tempo necessario a completare i 2,5ms. Attende cioè 2,5-2=0,5ms
- al quinto interrupt (dopo i 0,5ms) non deve controllare altri servo ma eseguire solo un ritardo per arrivare a 20ms. Per fare questo la routine imposta il timer per eseguire un'attesa di 1,25ms. In questo modo completa i 20ms dopo altri 12 interrupt (2,5ms del servo1+2,5ms del servo2+1,25ms*12 attese=20ms)

I tempi Ton servo1, 2,5-Ton1, Ton servo2 e 2,5-Ton2 sono memorizzati in un array in modo che la routine di interrupt non bedda calcolarli e perdere inutilmente tempo.

Vediamo come calcolare i valori da salvare nell'array per i tempi desiderati.

Il Timer0 del PIC genera in Interrupt quando il suo valore subisce un overflow, ovvero quando il valore del timer viene incrementato ed in conseguenza di questo incremento abbia bisogno di superare il valore massimo ammesso dal timer (abbia cioè bisogno di superare il valore 255).
Una modalità di incremento del Timer è automatica e prevede un incremento ogni impulso di clock del sistema diviso per il valore del prescaler.
Facciamo un esempio. Se abbiamo un quarzo di 4MHz, il clock di sistema per un PIC vale quarzo/4 ossia vale 1MHz. Questo significa che fornendo al Timer direttamente il clock di sistema, il timer si incrementa ad una frequenza di 1MHz ovvero si incrementa di 1 ogni microsecondo.
E' possibile dividere ulteriormente questa frequenza attraverso il prescaler. Se impostiamo il prescaler ad 8, il timer si incrementa di uno ad ogni 8us (frequenza di incremento pari a 1MHz/8).
Per ottenere quindi un overflow (=interrupt) dopo 1ms abbiamo bisogno che il nostro timer vada in overflow dopo 1000/8=125 incrementi (1000 è il tempo in microsecondi che vogliamo ottenere e 8 il tempo in microsencondi di incremento del timer).
L'overflow però si ottiene quando il timer passa da valore 255 a 256 (che non esiste e quindi quando passa da 255 a 0).
Se dobbiamo fargli assumere il valore 256 dopo 125 incrementi singifica che dobbiamo caricare il timer con il valore 256-125=131 per ottenere 1ms di ritardo.
- per 1,5ms di ritardo abbiamo: 1500/8=187,5 ----> 256-187,5=68,5 che arrotondiamo a 68 o 69
- per 2ms di ritardo abbiamo: 2000/8=250 ----> 256-200=6

L'intervallo di tempo necessario ad arrivare a 2,5ms si calcola di conseguenza:
- Ton=1ms ----> 2500-1000=1500 ----> 1500/8=187,5 ----> 256-187,5=68,5
- Ton=1,5ms ----> 2500-1500=1000 ----> 1000/8=125 ----> 256-125=131

Una semplificazione è che il valore da caricare sul timer per ottenere il completamento dei 2,5ms si ottiene anche dalla più semplice differenza 200-Valore_del_Ton:
- Ton=1ms ----> Valore_del_Ton=131 ----> differenza=200-131=69
- Ton=1,5ms ----> Valore_del_Ton=69 ----> differenza=200-69=131
Fabio
Avatar utente
Foto Utentec1b8
3.595 3 8 13
G.Master EY
G.Master EY
 
Messaggi: 1770
Iscritto il: 15 gen 2009, 15:23

0
voti

[69] Re: Problema servocomando

Messaggioda Foto Utentefantamed » 21 nov 2013, 16:05

scusa la cocciutaggine questo e il punto che non riesco a farmi entrare in testa

c1b8 ha scritto:E' possibile dividere ulteriormente questa frequenza attraverso il prescaler. Se impostiamo il prescaler ad 8, il timer si incrementa di uno ad ogni 8us (frequenza di incremento pari a 1MHz/8).


4000/4= 1000 cioe 1MHZ
poi

prescaler 1/8 = 1000/8=125 (cioe 8us?)
Avatar utente
Foto Utentefantamed
30 5
Frequentatore
Frequentatore
 
Messaggi: 110
Iscritto il: 8 nov 2013, 13:44

0
voti

[70] Re: Problema servocomando

Messaggioda Foto Utentec1b8 » 21 nov 2013, 16:15

Frequenza di 1MHz uguale a un periodo di 1/1000000=1us
Frequenza di 1MHz/8 = 1000kHz/8=125kHz, quindi un periodo di 1/125000=8us
Fabio
Avatar utente
Foto Utentec1b8
3.595 3 8 13
G.Master EY
G.Master EY
 
Messaggi: 1770
Iscritto il: 15 gen 2009, 15:23

PrecedenteProssimo

Torna a Elettronica generale

Chi c’è in linea

Visitano il forum: MSN [Bot] e 15 ospiti