Gestione PCINT attraverso PCICR

Ciao a tutti...

Volevo porre una domnda da nubbio...

Io ho scritto un po di codice che volevo condividere con voi ma non so se posso farlo qui sul forum o se devo postarlo in un link apposito.

Grazie..

posta quì se hai problemi, se è corretto allora il posto giusto è il playground, magari con un paio di righe di spiegazione

ps. l'user sul playground è lo stesso che hai sul forum, le 2 cose son collegate

lesto:
posta quì se hai problemi, se è corretto allora il posto giusto è il playground, magari con un paio di righe di spiegazione

Ok ora lo so commentando poi lo condivido.

Ecco qui le parti di codice modificate e create...

Come dicevo, mi sono trovato nella neccessiàa di dove gestire gli interrup PCINT abbinati ai pin del micro.

Ho scritto una rutine che potrebbe essere utile e sono qui a condividere con voi la soluzione che ho trovato.

Specifico che il micro che uso è il atmega1284p quindi il pin_arduino.h è modificato ma coerente con il core è la versione 1.0.1

Modifiche a pin_arduino.h

//modifice allo standard
#define digitalPinToPCICR(p)    (((p) >= 0 && (p) < NUM_DIGITAL_PINS) ? (&PCICR) : ((uint8_t *)0))
// modificato per avere tutti i PIN
//#define digitalPinToPCICRbit(p) (((p) <= 7) ? 1 : (((p) <= 15) ? 3 : (((p) <= 23) ? 2 : 0)))
#define digitalPinToPCICRbit(p) (((p) < 8) ? (PCIF1)    : (((p) < 16) ? (PCIF3)    : (((p) < 24) ? (PCIF2)    : (PCIF0))))
//modificato per avere tutti i PIN
//#define digitalPinToPCMSK(p)    (((p) <= 7) ? (&PCMSK2) : (((p) <= 13) ? (&PCMSK0) : (((p) <= 21) ? (&PCMSK1) : ((uint8_t *)0))))
#define digitalPinToPCMSK(p)    (((p) <= 7) ? (&PCMSK1) : (((p) <= 15) ? (&PCMSK3) : (((p) <= 23) ? (&PCMSK2) : (&PCMSK0))))
// modificato per ottenere la maschera corretta
// #define digitalPinToPCMSKbit(p) ((p) % 8)
#define digitalPinToPCMSKbit(p) digitalPinToBitMask(p)

Poi ho creato il file interrupt.h:

#ifndef INTERRUPT_H_
#define INTERRUPT_H_

#include <inttypes.h>
#include "../wiring_private.h"

#ifdef __cplusplus
extern "C"{
#endif

void attachInterruptPcint(const uint8_t pinNumero, void (*userFunc)(void));//, int mode);

void detachInterruptPcint(const uint8_t pinNumero);

#ifdef __cplusplus
} // extern "C"
#endif

#endif /* INTERRUPT_H_ */

e in fine ho scritto questo interrupt.c

#include "interrupt.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "pins_arduino.h"

// definisce i bit massimi per il micro in uso
#define BIT_MAX 8
// trova il numro di gruppo PCINT attraverso lo standard di "pin_arduino.h"
#define PCIF_MAX (NUM_DIGITAL_PINS+1)/8

// questo arrai serve solo per indicizzare il puntatore alle funzioni da richiamare
static volatile voidFuncPtr intFunc[PCIF_MAX][BIT_MAX];

// abilta la il PCINT in base al pin e alla funzione da richiamare
void attachInterruptPcint( const uint8_t pinNumero,void (*userFunc)(void))
{
	uint8_t bit_pcicr=digitalPinToPCICRbit(pinNumero);
	uint8_t bit_pcmsk=digitalPinToPCMSKbit(pinNumero);
	volatile uint8_t *pcicr=digitalPinToPCICR(pinNumero);
	volatile uint8_t *pcmsk=digitalPinToPCMSK(pinNumero);

	//salva il puntatore alla funzione
	intFunc[bit_pcicr][(uint8_t)(log(bit_pcmsk)/log(2))]=userFunc;

	//setta i registri PCICR e il relativo PCMSK con il bit ricavato dal pin
	uint8_t oldSREG = SREG;
		cli();
		*pcmsk |= bit_pcmsk;
		*pcicr |= _BV(bit_pcicr);
	SREG = oldSREG;
}

//elimina la gestione dell'interrupt
void detachInterruptPcint( const uint8_t pinNumero )
{
	uint8_t bit_pcicr=digitalPinToPCICRbit(pinNumero);
	uint8_t bit_pcmsk=digitalPinToPCMSKbit(pinNumero);
	volatile uint8_t *pcicr=digitalPinToPCICR(pinNumero);
	volatile uint8_t *pcmsk=digitalPinToPCMSK(pinNumero);

	//verifica se è già stato cancellato
	if (intFunc[bit_pcicr][(uint8_t)(log(bit_pcmsk)/log(2))])
	{
		//porta il puntatore alla funzione a NULL
		intFunc[bit_pcicr][(uint8_t)(log(bit_pcmsk)/log(2))]=NULL;
			
		//cancella i registri per disabilitare l'interrupt
		uint8_t oldSREG = SREG;
			cli();
			*pcmsk &= ~bit_pcmsk;
			//se tutto il PCMSK inerente è a zero disabilita il controllo su PCIRC
			if (*pcmsk==0) {
				*pcicr &= ~_BV(bit_pcicr);
			}			
		SREG = oldSREG;
	}		
}

ISR(PCINT0_vect)
{
	//cicla per tutti gli 8 bit collegati al relativo PCINT
	//se vi è un interrupt in attesa interrompe il cilo per la ripresa successiva
	for (uint8_t j=0;(j<BIT_MAX)&& ((PCIFR & _BV(PCIF0))==0);j++)
	{
		// se è un interrupt con funzione chiama la relativa funzione
		if (intFunc[PCIF0][j])
		{
			intFunc[PCIF0][j]();
		}
	}
}
ISR(PCINT1_vect)
{
	for (uint8_t j=0;(j<BIT_MAX)&& ((PCIFR & _BV(PCIF1))==0);j++)
	{
		if (intFunc[PCIF1][j])
		{
			intFunc[PCIF1][j]();
		}
	}
}
ISR(PCINT2_vect)
{
	for (uint8_t j=0;(j<BIT_MAX)&& ((PCIFR & _BV(PCIF2))==0);j++)
	{
		if (intFunc[PCIF2][j])
		{
			intFunc[PCIF2][j]();
		}
	}
}
ISR(PCINT3_vect)
{
	for (uint8_t j=0;(j<BIT_MAX)&& ((PCIFR & _BV(PCIF3))==0);j++)
	{
		if (intFunc[PCIF3][j])
		{
			intFunc[PCIF3][j]();
		}
	}
}
ISR(BADISR_vect)
{
	// se viene eseguita è un errore di programmazione
}

Spero che tutto ciò possa essere utile a qualcuno per non dover riscrivere tutto e penso che sia una buona idea per eventuali implementazioni nel core successivi.

Ciao.

interrupt.h (443 Bytes)

interrupt.c (2.82 KB)

bravo, mi ci sono imbattuto perche' cercavo info proprio su PCINT.
Comunque c'e' una libreria apposita gia' fatta che fa usare su tutti i pin gli interrupt.

Hai il tuo primo Karma :wink:

Mi spieghi questo?

log(bit_pcmsk)/log(2)

Non l'ho capito, ma sicuramente è per mio difetto (sono le 6 di mattina ed ho dormito solo 4 ore. :sleeping: )

credo sia per fare il logaritmo in base 2 di bit_pcmsk, quindi per trovare l'esponente, ovvero la posizione del bit ad 1 nel byte

(non esistendo log base 2 ma solo log base E, devi usare il cambio di base dei logaritmi, ovvero dividere log base E per il log base E della base che vuoi otenere Logarithm - Wikipedia)

edit: IMHO, sapendo che i float e i log sono inefficientissimi per mancanza di FPU, un for che fa bitshift e verifica il risultato è preferbile, in oltre non rischi roblemi derivanti dall'arrotondamento. Inzomma, bella soluzione sulla carta, ma si dimostra difficile da leggere e error-prone.

Argh.. l'avevo detto che il sonno mi impediva di capire.... non ci avevo proprio pensato :sweat_smile: :sweat_smile: