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

Mi è venuto un flash, perchè uso un pò Atmel Studio e programmazione ISP e ogni tanto Arduino, quando trovo già tutto fatto come librerie, ecc.

Ma posso scrivere codice in C come faccio sull'IDE Atmel Studio "mischiato" a quello di Arduino sull'IDE Arduino? :roll_eyes:

Gli #INCLUDE di Arduino mi pare si rifacciano alle stesse librerie di AVR Studio.

Mi sto sbagliando a confondermi? :smiley:

Sí, l'IDE di arduino ha tutte le librerie standart incluse nel pacchetto. Inoltre a quelle altre specifiche per Arduino. Alcune cose sono semplificate per non creare problemi alla utente previsto di Arduino.

Ma posso scrivere codice in C come faccio sull'IDE Atmel Studio "mischiato" a quello di Arduino sull'IDE Arduino? smiley-roll-blue

Non Ti capisco.

Ciao Uwe

Hai ragione, ho scritto un pò criptico.

Ti faccio un esempio:

Per chiamare un Interrup esterno, su Arduino uso l'istruzione:

attachInterrupt(interrupt, function, mode),

mentre su AVR Studio, in C "puro", uso la funzione sie() nel main, e la chimata all'interrupt (fuori dal main e dal while) con l'istruzione

ISR (INT0_vect)
{
qualchecosa;
}

Se uso quuest'ultima istruzione sull' IDE di Arduino (invece che attach o detachInterrupt del Reference code) che succede? La riconosce?

Tutti due (Arduino e Atmel Studio) usano lo stesso compilatore percui dovrebbe funzionare anche in Arduino. La funzione di Arduino semplifica la programmazione perché non devi conoscere troppo a fondo il controller.

Ciao Uwe

Ok, il punto è che voglio usare tutti i pin di Arduino, come faccio in C-Atmel, come Interrupt interni fuori dalla funzione loop, cosi mi evito (in molti casi) di dovermi rivolgere ad una libreria tipo RTOS o qualche semaforo vario.

Ogni sensore, ogni Input, mi attiva un interrupt interno codifi ato fuori dal loop.

Certo che puoi gestire ISR dal codice di Arduino.
Basta aprire il core di Arduino, o alcune librerie sia di Arduino che di terze parti (ad esempio anche diverse delle mie) per vedere che puoi gestire una ISR. Puoi anche farlo direttamente dentro ad uno sketch. Basta ovviamente sapere come fare (quali vettori usare).

Abbi pazienza Leo, ma se io copio pari pari un pezzo di codice da AVR studio, fatto in C (rispettanto main() = setup() e while(1) = loop()) e lo butto sull'IDE di Arduino, funziona?

BaBBuino:
Abbi pazienza Leo, ma se io copio pari pari un pezzo di codice da AVR studio, fatto in C (rispettanto main() = setup() e while(1) = loop()) e lo butto sull'IDE di Arduino, funziona?

Sembra di si, è una prova che non avevo mai fatto però adesso ho provato a compilare questo semplicissimo sorgente dall'IDE 1.0.4, non mi ha dato nessun errore, l'eseguibile è grosso solo 184 byte, è ancora da verificare se poi gira sul serio :slight_smile:

int a;

int main()
{
 a = 1;
while(1)
 {
  a++; 
 }  
}

Bene! Se posso integrare in un unico IDE i vantaggi delle 3 cazzate che servono per Arduino (più tutto il mondo di librerie che c'è in giro) con il C atmel per funzioni un pò più critiche, sono un uomo felice! :smiley:

Cioè, non del tutto. Era meglio il contrario, se Arduino girava sull'IDE AVR Studio, infinitamente più completo...

BaBBuino:
Bene! Se posso integrare in un unico IDE i vantaggi delle 3 cazzate che servono per Arduino (più tutto il mondo di librerie che c'è in giro) con il C atmel per funzioni un pò più critiche, sono un uomo felice! :smiley:

Ho provato adesso a far girare quel piccolo programma, si pianta subito, presumibilmente perché mancano le inizializzazioni richieste dal core di Arduino e che sono indispensabile per preparare l'ambiente di lavoro per wiring, che comunque è presente quando compili tramite IDE.
Però la cosa non cambia la possibilità di poter scrivere direttamente in C ANSI all'interno dello sketch, tieni presente che setup e loop sono l'equivalente di :

int main()

// codice per inizializzare le variabili, le periferiche e/o eseguire una sola volta

while(1)
{
 // codice da eseguire all'infinito.
}

Ovvero cambia solo il modo con cui viene scritta la main e non la sua sostanza, all'interno della setup puoi scrivere tutto il codice C ANSI, e usare tutte le funzioni della avrlibc, che vuoi, stessa cosa vale per la loop() che devi vedere come la while(1), ovviamente nulla vieta di mischiare le comodità di wiring con il codice C ANSI puro, però devi fare attenzione a come riassegni le risorse interne per evitare conflitti con l'uso che ne fa wiring, p.e se vuoi usare la millis(), o il delay, non devi toccare il timer 0.

Si, ok. Le due funzioni principali so che sono equivalenti perchè prima conoscevo AVR e DOPO ho conosciuto Arduino, quindi ho riconosciuto subito il main() e il while(1) nel setup() e loop().

Per le risorse è scontato che devo prestare attenzione, proprio perchè non è trasparente e scontato sapere quali risorsa utilizza una semplice istruzione di Wiring. Mi devo fare un pò di cultura "dietro le quinte" di Arduino.

Cmq stasera provo un pò di cose e vedo se funzionano. Più che altro mi interessa gestire i pin digitali come Interrupt sul CHANGE per quel discorso di evitare robe tipo RTOS, e conservare un buon flusso del programma anche con sensori multipli, senza 2000 delay() infilati da tutte le parti.

BaBBuino:
per quel discorso di evitare robe tipo RTOS, e conservare un buon flusso del programma anche con sensori multipli, senza 2000 delay() infilati da tutte le parti.

Volendo c'è il LeOS che ti semplifica la vita se devi gestire dei task che devono essere eseguiti periodicamente.

astrobeed:

BaBBuino:
per quel discorso di evitare robe tipo RTOS, e conservare un buon flusso del programma anche con sensori multipli, senza 2000 delay() infilati da tutte le parti.

Volendo c'è il LeOS che ti semplifica la vita se devi gestire dei task che devono essere eseguiti periodicamente.

Non dirmelo! Lo avevo adocchiato la settimana scorsa e quando ho iniziato a leggere la storia degli upgrade, hanno hacherato il sito di Leo e mi sono preso il virus della Polizia! :~

si può scrivere in C puro ma non si può forzare la comilazione ad usare il C puro, in quanto il main aggiunto dall'editor include varie classi, quindi C++.

hanno hacherato il sito di Leo

non ne sapevo nulla, leo? ti fai fregare così?

lesto:
non ne sapevo nulla, leo? ti fai fregare così?

E' da un paio di mesi che mi attaccano il sito. Ho aggiunto alcuni plugin ma il problema di fondo è la sicurezza intrinseca di WordPress, veramente bassa.
Stavo infatti pensando a migrare tutto su Joomla, che è un pò più robusto di WP mantenendo una certa facilità di gestione.

BaBBuino:
Non dirmelo! Lo avevo adocchiato la settimana scorsa e quando ho iniziato a leggere la storia degli upgrade, hanno hacherato il sito di Leo e mi sono preso il virus della Polizia! :~

Mi spiace; non me ne ero neanche accorto perché avendo Linux non prendevo i malware che mi avevano inserito nel codice del sito, che colpiscono solo i sistemi Windows.

Comunque se non ti "fidi" del mio sito, puoi prendere il codice del leOS anche da GitHub:

Dentro al pacchetto trovi anche un PDF che ti spiega come usarlo.

lesto:
si può scrivere in C puro ma non si può forzare la comilazione ad usare il C puro, in quanto il main aggiunto dall'editor include varie classi, quindi C++.

hanno hacherato il sito di Leo

non ne sapevo nulla, leo? ti fai fregare così?

ni, cioè andrebbe riscritto diversamente.
Se il file ha estensione ".c" il compilatore è gcc quindi C, se il file ha estensione ".cpp" il compilatore è g++ quindi C++, se il file ha estensione ".s", viene chiamato l'assembler "as". Alla fine tutti i file oggetto compilati vengono collegati a formare un eseguibile in formato ".elf" e il lavoro sporco lo fa g++ con il flag per linkare. Il linguaggio o meglio il compilatore fornisce degli strumenti per permettere al programmatore di mischiare codice c, c++ e asm durante la fase di linking.

Rimanendo in tema arduino con file sorgente multipli ".ino" (sempre se ancora permesso, con pde era così) il qualificatore "static" usato per isolare le variabili in un solo modulo perde di significato, in quanto tutti i file ".ino", (con pde era così) vengono miscelati insieme, in un unico file e poi compilato. Mentre usando l'estensione .c o .cpp tutte le caratteristiche del C/C++ sono presenti, quindi anche "static", "extern" ecc.

Approfitto della occasione per sollevare una questione su cui non ho ancora indagato, principalmente perchè non uso C++ in embedded.
La questione è:
In ambito non embedded spesso si usano le classi statiche o meglio sigleton, queste sono delle classi che possono essere instanziate solo una volta, al prossimo tentativo di istanzazione si ottiene la precedente istanza o meglio un puntatore, quindi si deve usare "new", con queste classi non serve il costruttore di copia implicito fornito dal compilatore e quindi lo si esclude scrivendo una pragma o altra direttiva fornita dal complitatore in uso.

Con arduino si crea istanza locale, quindi nello stack, ma il costruttore di copia spesso non serve, ad esempio alla classe Serial non serve il costruttore di copia, ne quello implicito ne quello specifico, allora sarebbe meglio poterlo disabilitare no. Il costruttore di copia interviene quando su una istanza di classe uso l'operatore "=", cioè così String myString = yourString;. Ovviamente nella classe String di arduino il costruttore di copia è necessario e sicuramente i programmatori hanno sovrascritto il costruttore di copia implicito specificandone uno esplicitamente.

Tutto sto discorso potrebbe essere inutile se si scoprisse che il costruttore di copia è disabilitato di default, cosa sensata in ambito embedded con risorse limitate. Altra cosa che si potrebbe scoprire è che per quelle istanza su cui non viene adoperato il costruttore di copia tramite l'operatore "=" il compilatore ottimizza per dimensione, quindi lo toglio dal codice. L'esperienza precedenti con la capacità di ottimizzare mi fanno pensare che l'ottimizzazione fa fuori tutto ciò che non è necessario.

Ciao.

lesto:
si può scrivere in C puro ma non si può forzare la comilazione ad usare il C puro, in quanto il main aggiunto dall'editor include varie classi, quindi C++.

hanno hacherato il sito di Leo

non ne sapevo nulla, leo? ti fai fregare così?

Ma le inizializzazioni del C le scriverei nel setup(). Main non lo scrivo ed uso da nessuna parte. Non va bene cosi?

il main non lo scrivi perchè lo aggiunge di nascosto l'ide. insieme ad un alro bel pò di chicche, come inizializzazioni varie, Serial (hai mai incluso la HardwareSerial.h?)

Main di Arduino

#include <Arduino.h>

int main(void)
{
	init();

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

La init() inizializza alcuni registri del micro in base alle caratteristiche di quest'ultimo

void init()
{
	// this needs to be called before setup() or some functions won't
	// work there
	sei();
	
	// on the ATmega168, timer 0 is also used for fast hardware pwm
	// (using phase-correct PWM would mean that timer 0 overflowed half as often
	// resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(TCCR0A) && defined(WGM01)
	sbi(TCCR0A, WGM01);
	sbi(TCCR0A, WGM00);
#endif  

	// set timer 0 prescale factor to 64
#if defined(__AVR_ATmega128__)
	// CPU specific: different values for the ATmega128
	sbi(TCCR0, CS02);
#elif defined(TCCR0) && defined(CS01) && defined(CS00)
	// this combination is for the standard atmega8
	sbi(TCCR0, CS01);
	sbi(TCCR0, CS00);
#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
	// this combination is for the standard 168/328/1280/2560
	sbi(TCCR0B, CS01);
	sbi(TCCR0B, CS00);
#elif defined(TCCR0A) && defined(CS01) && defined(CS00)
	// this combination is for the __AVR_ATmega645__ series
	sbi(TCCR0A, CS01);
	sbi(TCCR0A, CS00);
#else
	#error Timer 0 prescale factor 64 not set correctly
#endif

	// enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
	sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
	sbi(TIMSK0, TOIE0);
#else
	#error	Timer 0 overflow interrupt not set correctly
#endif

	// timers 1 and 2 are used for phase-correct hardware pwm
	// this is better for motors as it ensures an even waveform
	// note, however, that fast pwm mode can achieve a frequency of up
	// 8 MHz (with a 16 MHz clock) at 50% duty cycle

#if defined(TCCR1B) && defined(CS11) && defined(CS10)
	TCCR1B = 0;

	// set timer 1 prescale factor to 64
	sbi(TCCR1B, CS11);
#if F_CPU >= 8000000L
	sbi(TCCR1B, CS10);
#endif
#elif defined(TCCR1) && defined(CS11) && defined(CS10)
	sbi(TCCR1, CS11);
#if F_CPU >= 8000000L
	sbi(TCCR1, CS10);
#endif
#endif
	// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
	sbi(TCCR1A, WGM10);
#elif defined(TCCR1)
	#warning this needs to be finished
#endif

	// set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
	sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
	sbi(TCCR2B, CS22);
#else
	#warning Timer 2 not finished (may not be present on this CPU)
#endif

	// configure timer 2 for phase correct pwm (8-bit)
#if defined(TCCR2) && defined(WGM20)
	sbi(TCCR2, WGM20);
#elif defined(TCCR2A) && defined(WGM20)
	sbi(TCCR2A, WGM20);
#else
	#warning Timer 2 not finished (may not be present on this CPU)
#endif

#if defined(TCCR3B) && defined(CS31) && defined(WGM30)
	sbi(TCCR3B, CS31);		// set timer 3 prescale factor to 64
	sbi(TCCR3B, CS30);
	sbi(TCCR3A, WGM30);		// put timer 3 in 8-bit phase correct pwm mode
#endif

#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */
	sbi(TCCR4B, CS42);		// set timer4 prescale factor to 64
	sbi(TCCR4B, CS41);
	sbi(TCCR4B, CS40);
	sbi(TCCR4D, WGM40);		// put timer 4 in phase- and frequency-correct PWM mode	
	sbi(TCCR4A, PWM4A);		// enable PWM mode for comparator OCR4A
	sbi(TCCR4C, PWM4D);		// enable PWM mode for comparator OCR4D
#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */
#if defined(TCCR4B) && defined(CS41) && defined(WGM40)
	sbi(TCCR4B, CS41);		// set timer 4 prescale factor to 64
	sbi(TCCR4B, CS40);
	sbi(TCCR4A, WGM40);		// put timer 4 in 8-bit phase correct pwm mode
#endif
#endif /* end timer4 block for ATMEGA1280/2560 and similar */	

#if defined(TCCR5B) && defined(CS51) && defined(WGM50)
	sbi(TCCR5B, CS51);		// set timer 5 prescale factor to 64
	sbi(TCCR5B, CS50);
	sbi(TCCR5A, WGM50);		// put timer 5 in 8-bit phase correct pwm mode
#endif

#if defined(ADCSRA)
	// set a2d prescale factor to 128
	// 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
	// XXX: this will not work properly for other clock speeds, and
	// this code should use F_CPU to determine the prescale factor.
	sbi(ADCSRA, ADPS2);
	sbi(ADCSRA, ADPS1);
	sbi(ADCSRA, ADPS0);

	// enable a2d conversions
	sbi(ADCSRA, ADEN);
#endif

	// the bootloader connects pins 0 and 1 to the USART; disconnect them
	// here so they can be used as normal digital i/o; they will be
	// reconnected in Serial.begin()
#if defined(UCSRB)
	UCSRB = 0;
#elif defined(UCSR0B)
	UCSR0B = 0;
#endif
}

sei() è una macro definita nel file interrupt.h della toolchain avr

# define sei()  __asm__ __volatile__ ("sei" ::)

e abilita tutti gli interrupt.
Dovrebbe essere tra le prime istruzioni disassemblando un qualsiasi firmware di Arduino.
Mi potreste dare conferma?

In un firmware per mcu Atmel all'inizio c'è la jump table dei vettori di interrupt. Dopo di questi inizia il codice vero e proprio. C'è effettivamente una "sei" ad un certo punto.