Pages: [1]   Go Down
Author Topic: Gestione Interrupt  (Read 927 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Salve, avrei bisogno di un aiuto per capire come gestire il segnale di interrupt nel mio codice.
Praticamente sto realizzando un robottino, questo quando rileva un ostacolo a 20cm, blocca le ruote e gira la testa di 360° scansionando l'area con il sensore ad ultrasuoni. Una volta che rileva un altro ostacolo deve fare la stessa cosa, ma ruotando la testa nel senso opposto, questo per evitare che si attorciglino i cavi.
Siccome sto usando un servo motore (modificato per ruotare completamente) per sapere quando la testa ha fatto il giro completo ho applicato una specie di pulsante, che quando si chiude significa che ha completato il giro, quindi porta il pin di input al livello basso e manda il segnale di interrupt.

Questo è il codice, dove purtroppo non sto riuscendo a gestire per bene il senso di rotazione, perchè fa un po come gli piace:

Code:

#include <Servo.h>

int pinServoDX = 8;
int pinServoSX = 9;
int pinServoSens = 10;

int inizio = 2;
volatile int avanza;

volatile int conta_interrupt = 1;
volatile int rotazione = 1;  // 0 = DX   1 = SX

int distanza;

int triggerP = 6;
int echoP = 7;

Servo servoDX;
Servo servoSX;  
Servo servoSens;
 
void ruota_sensore();
void leggi_distanza();
void inverti_rotazione();
 
void setup()
{
  servoDX.attach(pinServoDX);
  servoSX.attach(pinServoSX);
  
  rotazione = 1;
  avanza = 1;
  distanza = 0;
  
  pinMode(triggerP, OUTPUT );
  pinMode(echoP, INPUT );
  pinMode(inizio, INPUT);
  digitalWrite(inizio, HIGH);
  attachInterrupt(1, inverti_rotazione, RISING );

  Serial.begin( 9600 );
}

void loop()
{
    leggi_distanza();
  
    if(distanza > 20 && digitalRead(inizio) == 0 && avanza == 1)
    {
        Serial.print(digitalRead(2));
        Serial.print(" - AVANZO - ");
        servoDX.attach(pinServoDX);
        servoSX.attach(pinServoSX);
        servoDX.write(1);
        servoSX.write(250);
      }
      else
      {
        Serial.print(digitalRead(2));
        Serial.print(" - NON AVANZO - ");
        ruota_sensore();
      }
}

void leggi_distanza()
{
  digitalWrite(triggerP, LOW );
  digitalWrite(triggerP, HIGH );
  delayMicroseconds( 10 );
  digitalWrite(triggerP, LOW );
  
  long durata = pulseIn(echoP, HIGH );
  
  distanza = durata / 58;
  
  Serial.print("dist: ");
  Serial.println(distanza);
}

void ruota_sensore()
{
      servoDX.detach();
      servoSX.detach();

      avanza = 0;
      
      servoSens.attach(pinServoSens);
              
      if(rotazione == 1)
      {
          servoSens.write(1);
          Serial.print("senso orario ");
      }
      else
      {
        servoSens.write(250);
        Serial.print("senso antiorario ");
      }
}

void inverti_rotazione()
{
      if(conta_interrupt == 2)
     {
       avanza = 1;
       conta_interrupt = 1;
      
       rotazione = !rotazione;
    }
    else
      conta_interrupt ++ ;    
}
Logged

0
Offline Offline
Faraday Member
**
Karma: 31
Posts: 2908
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Non ho dedicato molto tempo alla lettura del codice, quindi può essere che ci sono altri motivi che mi sfuggono.
L'implementazione della funzione utente void inverti_rotazione() mi sembra troppo contorta.

Se ho capito bene c'è uno switch a destra ed uno a sinistra connessi entrambe allo stesso piedino di interrupt.
Quindi:

Code:
#define TURN_DX       1
#define TURN_SX       -1
volatile int currentRotation = TURN_DX;


void inverti_rotazione()
{
     if (currentRotation == TURN_DX) {

         currentRotation = TURN_SX;

     } else {

         currentRotation = TURN_DX;

     }
}

oppure meno comprensibile:



Code:
#define BIT_STATE_ROTATION       0

// XXX da cambiare se si vuole salvare altri bit di stato
#define BIT_STATE_XXX                 1
#define BIT_STATE_XXX                 2
// fino a BIT_STATE_XXX                 7 puoi salvare 8 stati, 16 se la variabile store_state è grande 16 bit
 

#define __bit_is_set(var, bit)   (var & _BV(bit))
#define __bit_is_clear(var, bit) (!(var & _BV(bit)))

volatile byte store_states = 0x0;

void inverti_rotazione()
{
   store_state ^= _BV(BIT_STATE_ROTATION);   // ad ogni chiamata il bit 0 di store_state cambia stato, se è 0 diventa uno e viceversa
}

// per testare il bit usa
if (__bit_is_set(store_state, BIT_STATE_ROTATION)) {
    // il bit 0 di store_state è acceso
}
// oppure
if (__bit_is_clear(store_state, BIT_STATE_ROTATION)) {
    // il bit 0 di store_state è spento
}

Comunque la logica è giusta, se sta girando a destra e si chiude lo switch e arrivato a fine corsa destra, se invece stava girando a sinistra....ecc


Ciao.
Logged

AvrDudeQui front end per avrdude https://gitorious.org/avrdudequi/pages/Home

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

In realtà è solo una molla posizionata al centro sulla base ed un'altra posizionata sulla testa. Quando ruota la testa di 360° le molle si toccano chiudendo il circuito.
Inoltre l'avevo già definita così la funzione inverti_rotazione, questo è il codice:

Code:
[ ... ]

// quando il circuito è chiuso sul pin ho 0 altrimenti 1

digitalWrite(inizio, HIGH); // pin dell'interrupt
attachInterrupt(1, inverti_rotazione, FALLING );

[ ... ]

void inverti_rotazione()
{
   avanza = 1;
   rotazione = !rotazione;
}

Solo che alla prima accensione l'interruttore è chiuso, quindi il pin è a 0. Non appena si apre il pin passa a 1 e si verifica l'interrupt?!?! Ma non dovrebbe verificarsi quando passa da 1 a 0 se dichiaro FALLING ?

Logged

0
Offline Offline
Faraday Member
**
Karma: 31
Posts: 2908
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Si però, il cambio di stato di un microswitch non netto, tra il tempo in cui si passa da contatto aperto a chiusi si possono verificare (si verifica) dei cambi di stato repentini, quindi una seguenza di fronti di salita e discesa che solitamente un interrupt di un microcontroller rileva tutti.

Quindi l'ideale sarebbe trasformare i sensori da meccanici ad ottici, che non soffrono di queste incertezze, oppure cercare in qualche modo di rallentare il codice presente nella funzione utente, cioè una sorta di debounce, che però monopolizza la CPU per il tempo di ritardo imposto. Se questo è compatibile con l'applicazione prima di cambiare stato alla variabile leggi 20 volte il pin fai una media e se la media risulta un dato vero allora cambi stato alla variabile diversamente si è trattato ancora di un rimbalzo.

Ciao.
Logged

AvrDudeQui front end per avrdude https://gitorious.org/avrdudequi/pages/Home

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Si però, il cambio di stato di un microswitch non netto, tra il tempo in cui si passa da contatto aperto a chiusi si possono verificare (si verifica) dei cambi di stato repentini, quindi una seguenza di fronti di salita e discesa che solitamente un interrupt di un microcontroller rileva tutti.

Quindi l'ideale sarebbe trasformare i sensori da meccanici ad ottici, che non soffrono di queste incertezze, oppure cercare in qualche modo di rallentare il codice presente nella funzione utente, cioè una sorta di debounce, che però monopolizza la CPU per il tempo di ritardo imposto. Se questo è compatibile con l'applicazione prima di cambiare stato alla variabile leggi 20 volte il pin fai una media e se la media risulta un dato vero allora cambi stato alla variabile diversamente si è trattato ancora di un rimbalzo.

Ciao.
Potresti essere più esplicito?

Come sensore ottico potrei usare una fotoresistenza ed un led che la illumina?
« Last Edit: June 30, 2013, 08:51:56 am by walter4991 » Logged

0
Offline Offline
Faraday Member
**
Karma: 31
Posts: 2908
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Potresti essere più esplicito?

Come sensore ottico potrei usare una fotoresistenza ed un led che la illumina?

ni, cioè potresti anche tentare quella strada, ma le fotoresistenze sono sensibili alla luce.

Come sensori di fine corsa tipicamente si usano i sensori a forcella, dove in un unico corpo di plastica si trovano montati l'uno di fronte all'altro
un emettitore ad infrarossi e un ricevitore infrarossi, interrompendo il fascio interponento qualcosa nel mezzo rilevi il fine corsa.
Si usano spesso nelle spampanti per rilevare la mancanza carta.

Cerca "sensori ottici a forcella" con google e clicca su immagini, credo che anche RobotItaly abbia qualcosa.

Ciao.
Logged

AvrDudeQui front end per avrdude https://gitorious.org/avrdudequi/pages/Home

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Usando la fotoresistenza (magari la inserisco in un tubicino) ed il led: se incontro un ostacolo blocco i motori, ruoto il la testa e nel frattempo accendo il led, quando la testa tornerà in posizione illuminerà la fotoresistenza.
Se il valore letto dalla fotoresistenza è maggiore di tot allora inverto la rotazione per il rilevamento del prossimo ostacolo.

Tutto questo senza usare interrupt. E' giusto come ragionamento? C'è qualche inconveniente?
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sono riuscito a risolvere senza usare gli interrupt!
Praticamente prima ho misurato quanto tempo ci mette all'incirca per completare il giro, supponiamo 1 sec, successivamente ho fatto avviare il motorino per 700ms, dopo di che procedo a passi di 2 ms finché non chiudo il circuito! smiley-grin

Code:
#include <Servo.h>
 
Servo myservo;  // create servo object to control a servo
 
int senso = 0;
int cont = 0;
int rotazione = 0;
 
void setup()
{
  myservo.attach(10);
  pinMode(inizio, INPUT);
  digitalWrite(inizio, HIGH);
}
 
void loop()
{
    if(rotazione == 0)
      senso = 1;
    else
      senso = 250;
 
     start = millis();
   
     myservo.attach(10);
     
    if(cont == 0)
    {
       for(int i=0;i<10;i++)
       {
          myservo.write(senso);
          delay(70);
        }
      cont ++;
    }
   
    while(digitalRead(inizio)!=0)
    {
        myservo.write(senso);
        delay(2);
    }
   
    rotazione = !rotazione;
    cont = 0;
   
    myservo.detach();
}
Logged

0
Offline Offline
Shannon Member
****
Karma: 131
Posts: 10474
:(){:|:&};:
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Tutto questo senza usare interrupt. E' giusto come ragionamento? C'è qualche inconveniente?

l'nconveniente rispetto all'interrupt è di perdere delle letture. Non ho letto il resto del discorso, quindi sta a te decidere/osservare se è un comportamento accettabile
Logged

sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Bhè l'apertura e chiusura di un interruttore meccanico all'interno di un ciclo while (che è in "ascolto" solo per questo interruttore) viene rilevata tranquillamente.
Logged

0
Offline Offline
Shannon Member
****
Karma: 131
Posts: 10474
:(){:|:&};:
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

mentre sei nel while. ma non metre, ad esempio, sei nel  delay(70);
Logged

sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

Pages: [1]   Go Up
Jump to: