Dubbio stupido: Posso scrivere codice C "puro" nell'IDE di Arduino?

Sei() è semplicemente l'"accensione" di TUTTI gli interrupt, con ISR poi si dichiara la funzione per il singolo Interrupt.

Potrebbe essere l'equivalente di Interrupts() di Arduino

Alura... domani proverò questo semplice programma...

#include <avr/io.h>
#include <stdint.h>            

#include <avr/interrupt.h>    


volatile uint8_t portB = 0xFF;     // metto le pullUp su tutte le uscite

int main(void)
{
    DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // azzero gli ingressi PB0, PB1, PB2 pin
    // e PB0,PB1,PB2 (pin PCINT0, PCINT1, PCINT2 ) settate come Input

    PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // PullUp sul gruppo di Porte B
   
    
    PCICR |= (1 << PCIE0);     // abilito PCIE0 a verificare il registro degli interrupt mascherabili PCMSK0    
    PCMSK0 |= (1 << PCINT0);   // setto su PCINT0 un  interrupt su state change (unico possibile sugli Interrupt mascherabili) 

    sei();                     // abilito tutti gli interrupts

    while(1)
    {
        qualcosa() //ma anche niente...
    }
}



ISR (PCINT0_vect) //routine di gestione del vettore Interrupt
{
    uint8_t changedbits;


    changedbits = PINB ^ portB;
    portB = PINB;

    
    if(changedbits & (1 << PB0))
    {
        /* PCINT0 è cambiato di livello */
    }
    
    if(changedbits & (1 << PB1))
    {
        /* PCINT1 è cambiato di livello */
    }

    if(changedbits & (1 << PB2))
    {
        /* PCINT2 è cambiato di livello */
    }

}

Proverò a mettere il codice main nel setup() di Arduino e il codice while nel loop() di Arduino. Il principio dovrebbe essere questo

Ci butto due pulsanti e un led e vedo se funzia. Ovviamente con AVR Studio funziona perfettamente...

Naturalmente userò i pin dell'ATmega in base alla mappatura del chip 328, e non Arduino, quindi i PCINT0, 1 e 2 (che dovrebbe significare PinChangeInterrupt) sono i piedini fisici 14,15 e 16 dell'Atmega 328.

BaBBuino:
Sei() è semplicemente l'"accensione" di TUTTI gli interrupt,

Visto che siamo a precisare, sei è un'istruzione che setta il flag "i" nel registro di stato SREG del microcontrollore :stuck_out_tongue:

[quote
Potrebbe essere l'equivalente di Interrupts() di Arduino
[/quote]
Senza il "potrebbe". Lo è.
Interrupts altro non è che un alias per sei().
Dal file Arduino.h:

#define interrupts() sei()
#define noInterrupts() cli()

BaBBuino:
Alura... domani proverò questo semplice programma...

Non so se funzionerà perché, come è stato detto, l'IDE di Arduino manipola i file sorgenti. Nello specifico, lo sketch lo ingloba in un altro file dove già è contenuta la funzione main().
Capito questo, puoi però benissimo adattare il codice allo stile di Arduino:

#include <avr/io.h>
#include <stdint.h>            

#include <avr/interrupt.h>    


volatile uint8_t portB = 0xFF;     // metto le pullUp su tutte le uscite

void setup()
{
    DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // azzero gli ingressi PB0, PB1, PB2 pin
    // e PB0,PB1,PB2 (pin PCINT0, PCINT1, PCINT2 ) settate come Input

    PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // PullUp sul gruppo di Porte B
   
    
    PCICR |= (1 << PCIE0);     // abilito PCIE0 a verificare il registro degli interrupt mascherabili PCMSK0    
    PCMSK0 |= (1 << PCINT0);   // setto su PCINT0 un  interrupt su state change (unico possibile sugli Interrupt mascherabili) 

    sei();                     // abilito tutti gli interrupts
}
 
void loop()
    {
        qualcosa() //ma anche niente...
    }
}



ISR (PCINT0_vect) //routine di gestione del vettore Interrupt
{
    uint8_t changedbits;


    changedbits = PINB ^ portB;
    portB = PINB;

    
    if(changedbits & (1 << PB0))
    {
        /* PCINT0 è cambiato di livello */
    }
    
    if(changedbits & (1 << PB1))
    {
        /* PCINT1 è cambiato di livello */
    }

    if(changedbits & (1 << PB2))
    {
        /* PCINT2 è cambiato di livello */
    }

}

Le ISR puoi includerle senza problemi negli sketch, io abitualmente lo faccio.

leo72:
Le ISR puoi includerle senza problemi negli sketch, io abitualmente lo faccio.

Vediamo di chiarire una volta per tutte questo dubbio.
Arduino usa il compilatore avrgcc abbinato alle avrlibc, che è lo stesso utilizzato da AVR Studio sotto forma di winavr oppure come toolchain Atmel, nel secondo caso ci sono varie migliorie e ottimizzazioni non presenti nel primo tool.
Nello sketch di Arduino è possibile usare esclusivamente l'ANSI C, non c'è nessun limite e nessuna controindicazione, anzi sarebbe meglio lavorare in questo modo, basta considerare la setup come la parte iniziale della main() e la loop come il ciclo infinito della main, questo giusto per inizializzare correttamente wiring e poterne sfruttare le relative funzioni.
Unica cosa a cui stare attenti è come si riprogrammano le varie periferiche rispetto a quanto prevede wiring, ovvero potete utilizzare tutte le periferiche come meglio credete, però poi non è detto che se usate le relative funzioni di wiring ottenete i risultati attesi.
Ci sono molti programmi per Arduino che sono scritti in mixed mode, ovvero parte C ANSI e parte Wiring in modo da ottimizzare al massimo le prestazioni, un esempio su tutti è MultiWii, noto software, molto diffuso, per quadricotteri che è realizzato in questo modo.

Era stato spiegato, credo da Astro, che l'IDE non copia e incolla il codice di seguito alla main, ma compila a parte il .ino e il main.c e poi li linka indicando il main compilato come punto di partenza.

Per vedere se è possibile sovrascrivere la main si potrebbe provare una cosa del genere.

#include <Arduino.h>

int main(void)
{
  init();

#if defined(USBCON)
  USBDevice.attach();
#endif

  Serial.begin(9600);

  setup();

  for (;;) {
    loop();
    if (serialEventRun) serialEventRun();
  }
  return 0;
}

void setup()
{
  Serial.print("Funziona");
}

void loop()
{
  ;
}

Compila, ma non l'ho provato su Arduino.

Domanda spero non OT o fuori luogo.

In arduino ci sono i comandi per gestire gli interrupt (come detto una semplificazione).
Perchè usare ISR? Ho visto librerie che le usano (forse una di LEO). Con la ISR è possibile fare cose non possibili con Interrupt() ?

nid69ita:
In arduino ci sono i comandi per gestire gli interrupt (come detto una semplificazione).

Si, ma sono molto limitati rispetto alle possibilità offerte dal 328, in realtà molti interrupt sono gestiti in modo invisibile all'utente da wiring, p.e. quelle usati dalle porte seriali, se vuoi accedere a tutte le funzionalità devi gestirle esternamente a wiring.

arduino fornisce solo la attachInterrupt che fornisce un modo pratico per ascoltare gli interrupt su due pin.

Con le ISR puoi "ascoltare" tutti i pin, i timer, etc..

PaoloP:
Era stato spiegato, credo da Astro, che l'IDE non copia e incolla il codice di seguito alla main, ma compila a parte il .ino e il main.c e poi li linka indicando il main compilato come punto di partenza.

L'IDE crea una cartella temporanea in cui riversa tutti i file usati dal programma, a cominciare da quelli usati dal core di Arduino per finire allo sketch dell'utente, che diventa un comune file .cpp che viene incluso nel progetto.
A questo punto passa tutto al compilatore, che unifica i vari file in un unico programma.
Quindi l'alchimia funziona per il semplice motivo che lo sketch "pippo.ino" diventa un comune file "pippo.cpp" e come tale viene incluso.

Puoi fare la prova aprendo il Blink e compilando.
In /tmp (o nella tua cartella temporanea di Windows) viene creata una cartella build9569824659248594287 (un numero a caso) e lì trovi tutti i file, compreso il file Blink.cpp, i file di Wiring, ecc...
Nei programmi più complessi trovi anche le copie delle eventuali librerie incluse.

leo72:
L'IDE crea una cartella temporanea in cui riversa tutti i file usati dal programma, a cominciare da quelli usati dal core di Arduino per finire allo sketch dell'utente, che diventa un comune file .cpp che viene incluso nel progetto.

Esatto, basta abilitare il modo verbose durante la compilazione per vedere tutti i passaggi intermedi che esegue l'IDE, sotto forma di righe di comando per il compilatore e il linker, per rendersi conto della cosa, poi se si da una sbirciatina nella cartella temporanea ai vari file le cose sono ancora più chiare :slight_smile:

astrobeed:
se si da una sbirciatina nella cartella temporanea ai vari file le cose sono ancora più chiare :slight_smile:

Ad esempio, questa è la cartella temporanea del progettino a cui sto lavorando in questi giorni:

[leo@desktop-hp build6920487833808324953.tmp]$ ls -la
totale 820
drwxr-xr-x  4 leo  users    980 13 mar 11.42 .
drwxrwxrwt 23 root root     500 13 mar 11.42 ..
-rw-r--r--  1 leo  users   1486 13 mar 11.25 CDC.cpp.d
-rw-r--r--  1 leo  users   2300 13 mar 11.25 CDC.cpp.o
-rw-r--r--  1 leo  users 310944 13 mar 11.42 core.a
-rw-r--r--  1 leo  users   1405 13 mar 11.25 HardwareSerial.cpp.d
-rw-r--r--  1 leo  users  32376 13 mar 11.25 HardwareSerial.cpp.o
-rw-r--r--  1 leo  users   1596 13 mar 11.25 HID.cpp.d
-rw-r--r--  1 leo  users   2300 13 mar 11.25 HID.cpp.o
-rw-r--r--  1 leo  users   1502 13 mar 11.25 IPAddress.cpp.d
-rw-r--r--  1 leo  users  13240 13 mar 11.25 IPAddress.cpp.o
drwxr-xr-x  3 leo  users    100 13 mar 11.25 leOS
-rw-r--r--  1 leo  users   1268 13 mar 11.25 main.cpp.d
-rw-r--r--  1 leo  users   3400 13 mar 11.25 main.cpp.o
-rw-r--r--  1 leo  users    155 13 mar 11.25 malloc.c.d
-rw-r--r--  1 leo  users  12960 13 mar 11.25 malloc.c.o
-rw-r--r--  1 leo  users    259 13 mar 11.25 new.cpp.d
-rw-r--r--  1 leo  users   5824 13 mar 11.25 new.cpp.o
drwxr-xr-x  3 leo  users     60 13 mar 11.25 PinChangeInt
-rw-r--r--  1 leo  users   1270 13 mar 11.25 Print.cpp.d
-rw-r--r--  1 leo  users  48472 13 mar 11.25 Print.cpp.o
-rw-r--r--  1 leo  users   6820 13 mar 11.42 serratura_elettronica_mega328.cpp
-rw-r--r--  1 leo  users   1363 13 mar 11.42 serratura_elettronica_mega328.cpp.d
-rw-r--r--  1 leo  users     13 13 mar 11.42 serratura_elettronica_mega328.cpp.eep
-rwxr-xr-x  1 leo  users  44051 13 mar 11.42 serratura_elettronica_mega328.cpp.elf
-rw-r--r--  1 leo  users  12802 13 mar 11.42 serratura_elettronica_mega328.cpp.hex
-rw-r--r--  1 leo  users  32524 13 mar 11.42 serratura_elettronica_mega328.cpp.o
-rw-r--r--  1 leo  users   1272 13 mar 11.25 Stream.cpp.d
-rw-r--r--  1 leo  users  33488 13 mar 11.25 Stream.cpp.o
-rw-r--r--  1 leo  users   1268 13 mar 11.25 Tone.cpp.d
-rw-r--r--  1 leo  users  18184 13 mar 11.25 Tone.cpp.o
-rw-r--r--  1 leo  users   1604 13 mar 11.25 USBCore.cpp.d
-rw-r--r--  1 leo  users   2308 13 mar 11.25 USBCore.cpp.o
-rw-r--r--  1 leo  users    620 13 mar 11.25 WInterrupts.c.d
-rw-r--r--  1 leo  users   6656 13 mar 11.25 WInterrupts.c.o
-rw-r--r--  1 leo  users    624 13 mar 11.25 wiring_analog.c.d
-rw-r--r--  1 leo  users   6068 13 mar 11.25 wiring_analog.c.o
-rw-r--r--  1 leo  users    610 13 mar 11.25 wiring.c.d
-rw-r--r--  1 leo  users   8868 13 mar 11.25 wiring.c.o
-rw-r--r--  1 leo  users    626 13 mar 11.25 wiring_digital.c.d
-rw-r--r--  1 leo  users  11668 13 mar 11.25 wiring_digital.c.o
-rw-r--r--  1 leo  users    622 13 mar 11.25 wiring_pulse.c.d
-rw-r--r--  1 leo  users   6780 13 mar 11.25 wiring_pulse.c.o
-rw-r--r--  1 leo  users    622 13 mar 11.25 wiring_shift.c.d
-rw-r--r--  1 leo  users   6188 13 mar 11.25 wiring_shift.c.o
-rw-r--r--  1 leo  users    157 13 mar 11.25 WMath.cpp.d
-rw-r--r--  1 leo  users   7572 13 mar 11.25 WMath.cpp.o
-rw-r--r--  1 leo  users    271 13 mar 11.25 WString.cpp.d
-rw-r--r--  1 leo  users  75836 13 mar 11.25 WString.cpp.o

leOS e PinChangeInt sono le cartelle delle 2 lib che utilizzo. Notare "serratura_elettronica_mega328.cpp", che altro non è se non il mio sketch .ino.

L'abbiamo detto tutti 8 volte che il main() = setup(), e il while() = loop()!

Il prossimo che lo ribadisce verrà picchiato dagli altri! :smiley:

Cmq nel pomeriggio faccio un pò di prove, e vediamo se quello che abbiamo detto poi funziona senza errori...

BaBBuino:
L'abbiamo detto tutti 8 volte che il main() = setup(), e il while() = loop()!

Il prossimo che lo ribadisce verrà picchiato dagli altri! :smiley:

il main() è il main(), ovvero una funzione OBBLIGATORIA, la prima richiamata in fase di esecuzione.
il setup() è una funzione richiamata dal main() di Arduino in cui è presente un ciclo for infinito - for(;:wink: - con al suo interno la chiamata a loop().

]:smiley: ]:smiley: ]:smiley:

BaBBuino:
L'abbiamo detto tutti 8 volte che il main() = setup(), e il while() = loop()!

No, setup e loop fanno parte della main(), l'analogia è che nella setup metti tutto quello che nella main sta prima del while(1) e in questo quello che è contenuto nella loop.

main.cpp dal core di Arduino:

#include <Arduino.h>

int main(void)
{
	init();

#if defined(USBCON)
	USBDevice.attach();
#endif
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}

In due addirittura...

Ora mi metto a piangere... =(

...e poi vi picchio! :smiley:

BaBBuino:
...e poi vi picchio! :smiley:

Ok, ti aspetto domani a mezzogiorno in piazza, intanto comincio a pulire la mazza ferrata , l'ultimo che ci ha sbattuto contro l'ha sporcata tutta di sangue :grin:

astrobeed:

BaBBuino:
...e poi vi picchio! :smiley:

Ok, ti aspetto domani a mezzogiorno in piazza, intanto comincio a pulire la mazza ferrata , l'ultimo che ci ha sbattuto contro l'ha sporcata tutta di sangue :grin:

questi giovani d'oggi non hanno più rispetto per le mazze altrui

Allora...

Ho messo il c0dice che ho postato ieri con qualche variazione:


#include <avr/io.h>
#include <avr/interrupt.h>


int main(void)
{  
    DDRB &= ~(1 << DDB0);     // Clear del pin PB0 pin e settatocome ingresso
    

    PORTB |= (1 << PORTB0);    // Attivo la PullUp su PB0
    


    PCICR |= (1 << PCIE0);    // setto PCIE0 ad abilitare il registro degli interrupts mascherabili
    PCMSK0 |= (1 << PCINT0);  // setto PCINT0 a triggerare quando c'è un CHANGE

    sei();                    // accendo tutti gli interrupts

    while(1)
    {
        /* qualcosa() ma anche niente */
    }
}



ISR (PCINT0_vect)
{
    /*  quà l'interrupt code */
}

Molto semplice quindi...

L'ho infilato in uno schetch abbastanza complesso già esistente.

In questo schetch ho già i due interrupts INT0 e INT1 usati per un encoder e avevo un input di una fotocellula attivato da uno schifido digitalread() in mezzo al loop e con dei delay() in mezzo.
Il punto è che la Fotocellula ha un'alta priorità, e infilarla in un digitalRead() intervallato da delay() vari, non è una bella cosa.

Da qui nasce l'esigenza di sfruttare gli interrupt mascherabili dell'ATMEGA. Inesistente con Wiring-Arduino, possibile con IDE Atmel e relativo codice C nativo.

Quindi la fotocellula, invece che un merdoso digitalRead(), attiva un Interrupt, per carità, mascherabile e con la limitazione del solo CHANGE, ma pur sempre un interrupt che non soggiace ai delay() vari, che sono nel loop() arduinesco.

I pin liberi rimasti (ho un Display grafico...) sono solo il 12 e il 13 (alias 18 e 19 dell' AT328). Questi pin sono anche PCINT4 e PCINT5, ovvero PinChangeInterrupt4 e 5.

Ho sostituito al codicetto più sopra il giusto PCINT, ovvero il 4 al posto dello 0 e il codice si compila senza errori.

Solo che quando la fotocellula si attiva, ovvero fa un CHANGE e richiama la routine ISR... invece che fare il codice interno, mi resetta il processore, che riparte da zero..
Ogni change, da 1 a 0 e poi anche da 0 a 1 mi resetta la MCU. Quindi il CHANGE su quel pin un qualcosa lo fa, ma non quello che dovrebbe!

Ho provato ad usare il pin successivo, il 13 Arduino, alias 19 AT328, alias PCINT5, ma il risultato identico...

Ho trascurato qualcosa?

Codice di prova, ho solo cambiato le porte...


#include <avr/io.h>
#include <avr/interrupt.h>


int main(void)
{  
    DDRB &= ~(1 << DDB4);     // Clear del pin PB4 pin e settatocome ingresso
    

    PORTB |= (1 << PORTB4);    // Attivo la PullUp su PB4
    


    PCICR |= (1 << PCIE0);    // setto PCIE0 ad abilitare il registro degli interrupts mascherabili
    PCMSK0 |= (1 << PCINT4);  // setto PCINT0 a triggerare quando c'è un CHANGE

    sei();                    // accendo tutti gli interrupts

    while(1)
    {
        /* qualcosa() ma anche niente */
    }
}



ISR (PCINT4_vect)
{
    /*  quà l'interrupt code */
}

Altra prova identica ma con PCINT5 invece di PCINT4 (spostando, ovviamente, sul nuovo piedino la fotocellula)


#include <avr/io.h>
#include <avr/interrupt.h>


int main(void)
{  
    DDRB &= ~(1 << DDB5);     // Clear del pin PB5 pin e settatocome ingresso
    

    PORTB |= (1 << PORTB5);    // Attivo la PullUp su PB5
    


    PCICR |= (1 << PCIE0);    // setto PCIE0 ad abilitare il registro degli interrupts mascherabili
    PCMSK0 |= (1 << PCINT5);  // setto PCINT5 a triggerare quando c'è un CHANGE

    sei();                    // accendo tutti gli interrupts

    while(1)
    {
        /* qualcosa() ma anche niente */
    }
}



ISR (PCINT5_vect)
{
    /*  quà l'interrupt code */
}

L'encoder usa gli INT0 e INT1 (Pin 2 e 3), ma non credo interferiscano con gli altri INT, visto che attachinterrupt() è un'istruzione selettiva usabile solo sui due INT non Mascherabili.

Che czz sbaglio?