scrittura registri microcontrollore e gestione interrupt

Salve a tutti,
io provengo da una lunga esperienza di programmazzione dei microcontrollori MEGA AVR nell'ambiente mikropascal pro for avr.
La mia tecnica di programmazzione fa largamente uso di impostazioni nei registri del microcontrollore, difatti scrivento opportunamente nei registri si impostano in maniera egregia le funzionalita' implementatte nei microcontrollori della famiglia megaavr quali i timer gli interrupt il funzionamento degli adc e delle interfaccie spi ecc..ecc.. .
Nell'ambiente mikropascal da me conosciuto esistono delle variabili per ogni registro del microcontrollore o al limite esiste la possibilita' di scrivere puntando direttamente l'indirizzo fisico del registro, nell'ambiente di sviluppo di arduino e' ancora possibile fare ste cose e cioe' scrivere nei registri cosi da impostare il funzionamento delle varie periferiche del microcontrollore?
se si come?

Sempicemente usando i nomi definiti da Atmel (ora Microchip) nel datasheet della MCU ...

Esempi di righe valide ...

TIMSK2 &= ~(1<<TOIE2);

SREG &= ~(1<<SREG_I);

_WD_CONTROL_REG = ((1<<_WD_CHANGE_BIT) | (1<<WDE));

Considera che NON sei affatto limitato alle funzioni Arduino che vedi nel reference (come tu temi nella tua presentazione), ma hai l'accesso a tutto quello che vuoi, a tutte le possibiltà offerte dal C/C++ ed, in più, hai a disposizuone la AVR libc.

Guglielmo

L'ambiente di sviluppo di Arduino altro non è che un framework c/c++ per AVR, il suo nome originale è wiring, questo significa che in realtà Arduino si programma in C/C++.
Tutto quello che facevi con mickropascal non solo è fattibile ma hai ancora più possibilità in quanto il C è il linguaggio ideale per la programmazione low level.
In pratica puoi accedere a qualunque registro macchina degli AVR semplicemente usando il nome mnemonico,
quello indicato sul data sheet degli AVR, p.e. se vuoi scrivere direttamente sul PORT B del Atmega328 il valore 0 puoi farlo con la seguente riga di codice "PORTB = 0x00;".
Per gli interrupt devi scrivere le necessarie ISR e abilitare i relativi flag nei registri, attenzione che Arduino di suo gestisce in automatico vari interrupt, p.e. quello del timer 0, pertanto nel momento in cui si ridefiniscono le relative ISR alcune funzionalità possono andare perse.

Perfetto, quindi non c'e' il limite che io avevo rilevato leggendo velocemente un po di Language reference.
Adesso mi serve capire meglio come si fa in pratica ad associare una routine ad un interrupt, dal Language reference vedo che e' disponibile la tecnica per linkare la routine ad un pin di interrupt attraverso l'istruzione attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE); ma se io volessi usare un interrupt generato da un evento interno tipo quello di fine trasmissione SPI in che modo lo posso linkare visto che l'istruzione attachinterrupt vuole come parametro un pin?
Oltre a questo volevo sapere come si gestisce l'accesso diretto alla memoria e cioe' come si legge e/o si scrive nella RAM e magari anche nella eprom e dove sta spiegato come si lavora con i puntatori.
Grazie in anticipo per le informazioni che mi saprete dare

oops, dimenticavo di ringraziarvi esplicitamente per le indicazioni!

avevo non letto una parte dell'istruzione attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE); e leggendo meglio ho riolevato che al posto di digitalPinToInterrupt(interruptPin) posso mettere direttamente il numero di interrupt che e' indicato nel datasheet del microcontrollore, quindi il problema del link routine interrrupt e' risolto resta da chiarire la questione accesso alla memoria e puntatori.
Roberto

La attachInterrupt() è specifica per i pin dotati di interrupt dedicato, 1 e 2 nel caso di Arduino basato su Atmega 328, ovvero interrupt 0 e 1.
Per tutti gli altri interrupt ti ho già spiegato che occorre abilitare il relativo flag nel registro preposto allo scopo, quale dipende dalla periferica e devi fare riferimento al data sheet, e scrivere la relativa ISR usando come nome funzione ISR e come argomento il nome predefinito del vettore di interrupt, il compilatore poi si occupa a gestire in modo corretto il richiamo alla relativa funzione per ogni interrupt.
Per esempio se voglio gestire un interrupt legato all'evento OCA sul timer 1 devo abilitare il flag nel regisro e poi serve una funzione con nome "ISR(TIMER1_COMPA_vect)"

Esempio di codice per gli eventi OCA e OCB del timer 1

void Timer_Init()
{
  // init timer 1 e timer 2
  cli();                         // disable global interrupts
  TCCR1A = 0;                    // reset Timer 1
  TCCR1B = 0;
  TCCR1B = 0b00001000 + CkSel_1; // set prescaler e modo CTC
  OCR1A = 65000;                 // OC A preset
  OCR1B = 32500;                 // OC B preset
  TIMSK1 = 0b00000110;      // enable Timer1 interrupt OCA e OCB
  sei();                         // enable global interrupts:
}


/// --------------------------
/// ISR Timer 1 OC A
/// --------------------------
ISR(TIMER1_COMPA_vect)
{
  ENC1_st ^= 1;
  if (ENC1_CwCcw)
  {
    if (ENC1_st) sbi(PORTH, 5);
    else cbi(PORTH, 5);
  }
  else
  {
    if (ENC1_st) sbi(PORTH, 6);
    else cbi(PORTH, 6);
  }
}
/// --------------------------
/// ISR Timer 1 OC B
/// --------------------------
ISR(TIMER1_COMPB_vect)
{
  if (ENC1_CwCcw)
  {
    if (ENC1_st) sbi(PORTH, 6);
    else cbi(PORTH, 6);
  }
  else
  {
    if (ENC1_st) sbi(PORTH, 5);
    else cbi(PORTH, 5);
  }
}

in allegato ho messo la pagina del datasheet dove sono riportati i dati sugli interrupt, noto che quello che tu chiami ISR(TIMER1_COMPA_vect) in realta' viene scritto come TIMER1_COMPA, intendi che va sempre comunque aggiunto il suffisso _vect? e comunque se avessi scritto ISR(0x0016) funzionava lo stesso? (i.e. TIMER1_COMPA_vect e' una variabile che nell'ambiente di compilazione vale 0x0016?)

Tutti i vettori di interrupt sono definiti del file "avrinterruptsname.h", fa parte del compilatore avrgcc, quindi fai riferimento a questo per vedere come sono implementati.
Non puoi mettere direttamente l'address nella funzione ISR perché si aspetta un vettore, predefinto nel compilatore, e il valore è quello per il relativo indice e non l'address assoluto del interrupt.
Tutti i nomi parametri per la ISR hanno il postfisso "_vect", dovrebbe servire per evitare ambiguità con i nomi, inoltre ritengo sia una "stupidaggine" mettere l'address assoluto nella ISR perché in questo modo non sai a quale interrupt corrisponde senza dover andare a vedere la relativa tabella, molto meglio un nome mnemonico, tanto poi il compilatore lo converte nel reale valore assoluto, nel codice compilato non ci sono nomi astratti, solo valori assoluti, o relativi, per i vari salti all'interno del codice macchina.

Si certo, l'indirizzo assoluto on e' quello reale della routine di interrupt (io pensavo che il compilatore lo aggiustasse) perche' mi sa che dipenda da come le routines di interrupt vengano "spiazzate" in relazione ad altre impostazioni (dimensione bootloader?), comunque quello a cui volevo arrivare era sapere che esistono dei files nei quali a seconda del microcontrollore vengano memorizzate delle variabili, questi files dove sono indicati?

Il vettore degli interrupt si trova a delle ben precise locazioni in memoria flash del micro, non dipende dal bootloader che si trova alla fine della flash, per per via dell'architettura harvard ha un suo adress bus separato da quello della memoria dati, la ram.
Su tutti gli AVR l'address 0 della flash, rammento che è organizzata sotto forma di word da 16 bit, contiene l'address per la routine post reset, a seguire tutti gli altri address per gli interrupt disponibili, quanti sono dipende dal modello della mcu e dalle periferiche che contiene.
L'address contenuto nella locazione 0 della flash viene stabilito dal compilatore, dipende da molti fattori, stessa cosa per tutti gli address seguenti relativi agli interrupt, è sempre il compilatore a stabilire dove si trova realmente la relativa ISR, la sola cosa imposta dal produttore è la posizione nella tabella degli interrupt del address a cui il programma deve saltare per trovare la relativa ISR.
P.e. sul Atmega328P, il micro usato sulla UNO, il vettore di interrupt parte dal adress 0x01 della flash e arriva fino al address 0x32 (complessivamente 50 word pari a 100 byte), dopo questo address è possibile iniziare ad allocare il codice del programma, in questo caso nel address 0x00 viene scritto il valore 0x033 che è l'indirizzo da cui parte il codice da eseguire.
Nel caso di avrgcc il primo address dove si trova il codice eseguibile non è 0x33 perché il compilatore usa altre locazioni della flash per metterci cose sue pertanto il codice eseguibile inizia più avanti, se non mi ricordo male tipicamente dal address 0x80.
Quanto sopra vale, in generale, per tutti i micro/mcu dei vari produttori, salvo casi particolari.
Per sapere quali file contengono la configurazione, gli alias, etc, del compilatore è indispensabile studiare la documentazione del avrgcc e delle avrlibc, quest'ultime sono molto importanti perché contengono moltissime funzioni specifiche gli avr, p.e. la gestione delle periferiche, non fanno parte del reference di Arduino ma sono parte integrante del compilatore.
Per farla breve, se vuoi usare il lato semplice di Arduino ti basta il suo reference, se vuoi usare al 100% il processore montato sulla scheda che stai usando devi studiare il suo datasheet, le varie application note, imparare il C, studiare la documentazione del compilatore e delle avrlibic.
Tieni presente che Pascal è un figlio del C, il relativo interprete/compilatore solitamente è scritto in C, ci sono molte affinità ma anche molte differenze.

astrobeed:
La attachInterrupt() è specifica per i pin dotati di interrupt dedicato, 1 e 2 nel caso di Arduino basato su Atmega 328, ovvero interrupt 0 e 1.

Forse volevi dire pin 2 e 3 ;).

Si hai ragione, pin 2 e 3. sulla UNO. :slight_smile:

ok, per lavorare con i microcontrollori ho iniziato dai datasheet ed in particolare dalla parte descrittiva delle varie periferiche del microcontrollore ed i relativi registri, la parte organizzativa della memoria e i vari lock bit ecc.. li ho trascurati per questo ricordavo male la posizione delle routine di interrupt.
Ringrazio per i chiarimenti, resta aperta la questione puntatori, la affrontero' col tempo.
Un saluto a tutti

Per i puntatori devi fare riferimento al C, la loro gestione dipende dal linguaggio e dal compilatore, non dal processore.

Ma soprattutto, se vuoi lavorare a livello così basso, forse fai meglio a lasciare perdere Arduino e a passare direttamente ad Atmel Studio.

Giusto per chiudere il discorso interrupt, qui trovi la documentazione della avrlibc con tutte le macro dedicate alla gestione interrupt, i nomi dei vari vettori per tutti gli AVR e le eventuali opzioni.

SukkoPera:
forse fai meglio a lasciare perdere Arduino e a passare direttamente ad Atmel Studio.

Dal punto di vista stesura del codice non cambia nulla tra l'IDE di Arduino e Atmel Studio, intendo su come scriverlo non come editor dello stesso, quello che fai con Atmel Studio lo fai pure con l'IDE di Arduino.
La vera differenza tra i due ambienti la fa l'editor, di gran lunga migliore quello di Atmel Studio, però più complesso da usare, e i vari tool di sviluppo, test e debug presenti in Atmel Studio.
Se vuoi usare al 100% il micro presente su una board Arduino nulla vieta di farlo anche tramite IDE, se vuoi puoi anche includere del codice assembly, se necessario, nello sketch, p.e. la libreria per i WS2812 di Adafruit è scritta quasi completamente in assembly sebbene contenuta in un file .cpp.

Certo, ma ho trovato molto poco, in allegato. e soprattutto non ci sono esempi.
Dove posso trovare qualcosa in piu'?

Non troverai (quasi) nulla su i puntatori nel reference di Arduino perché parla di Wiring, non del C/C++, i puntatori fanno parte del linguaggio e devi guardare i testi che insegnano a programmare in C, attenzione che ho detto "insegnano a programmare" perché se ti limiti al reference manual del C/C++ anche li trovi ben poco riguardo a come si usano i puntatori.