[Advanced] Programmazione Arduino in Bare Metal

Perfetto..... hai inventato la millis() ( quella dè noantri )

Come dice il proverbio.... chi semina, coglie
e tu ora stai seminando

e come disse il sommo... Non ragioniam di lor, ma guarda e passa

Brunello:
Perfetto..... hai inventato la millis() ( quella dè noantri )

Guglielmo

MauroTec:
L'alcolista non ammette di avere un problema, quando lo fa c'è ancora speranza di smettere, BaBBuino e un softwarista e lo nega, quindi...
Spetta a noi riportarlo sulla retta via della risposta in frequenza.
Ti ricordi quanto è sinuosa una rete di zobel, con la sua bella bobbinona, ti ricordi la guerra intrapresa nei confronti della terza armonica, non vorrai mica dargliela vinta.
:smiley:

Povero me... :confused:

Brunello:
Perfetto..... hai inventato la millis() ( quella dè noantri )

Come dice il proverbio.... chi semina, coglie
e tu ora stai seminando

e come disse il sommo... Non ragioniam di lor, ma guarda e passa

Del resto il Bare Metal è rendere le cose semplici assai complicate! :smiley:

BaBBuino:

ISR(TIMER1_OVF_vect)  // Routine dell'INTERRUPT che si attiva all'overflow del Timer1

{ conteggio_1++; conteggio_2++; conteggio_3++;
  if (conteggio_1 == TEMPO_LED_1){   // se...
  //bitSet(PINB, 3); // toggle LED_1
  digitalWrite(LED_1, !digitalRead(LED_1));  // inverto la condizione del LED ad ogni "giro"
...




I contatori vengono poi utilizzati per dividere il numero degli Overflow e "togglare" i 3 LED

Tutto bello. Ma perchè usare la digitalWrite() in una ISR ? Non rallenta il tutto? Quindi, visto il post iniziale, non sarebbe meglio usare direttamente i registri ?

Si, ma quello era un esempio per il nabbo di turno..

Lì (per gli Advanced) avremmo dovuto scrivere

PORTB = 0b00100000; // accendo il PORTB5 che è il pin 13 di Arduino

oppure, ancora meglio;

bitSet(PortB, 5);

Questo perchè la prima istruzione agisce su tutti i bit del PORTB e magari ci sono porte di cui non vuoi alterare il valore.

Si può fare anche così, cioè un bel bitwise di tipo OR:

PORTB = (PORTB | 0b00100000);

che è ancora più corto scritto così:

PORTB |= 0b00100000;

In questo modo si crea una maschera (MASK) che viene applicata al PORTB, così si cambiano solo i valori dove nella maschera ci sono degli "uni", lasciando inalterato il resto.

Per mettere a zero, stesse considerazioni:

O così:

PORTB = 0b00000000;

Oppure:

PORTB &= 0b11011111; // AND di PORTB con se stesso e la maschera

Con l'AND invece che con l'OR, ma a maschera invertita: dove c'è uno ZERO si tira a zero quel bit, dove c'è UNO lascia inalterato.

E poi c'è la solita manipolazione atomica*:

bitClear(PORTB, 5);

*atomico = manipolazione diretta del singolo bit, no maschera.

Ho dato un'occhiata "dentro" la digitalWrite();

In effetti non è che sia il massimo per semplicità e velocità!


void digitalWrite(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *out;

if (port == NOT_A_PIN) return;

// If the pin that support PWM output, we need to turn it off
// before doing a digital write.
if (timer != NOT_ON_TIMER) turnOffPWM(timer);

out = portOutputRegister(port);

uint8_t oldSREG = SREG;
cli();

if (val == LOW) {
*out &= ~bit;
} else {
*out |= bit;
}

SREG = oldSREG;
}

x iscrizione

BaBBuino:
Ho dato un'occhiata "dentro" la digitalWrite(); In effetti non è che sia il massimo per semplicità e velocità!

Soprattutto porta via molto tempo la parte che riporta da numero pin shield -> pin/port della specifica scheda/mcu.
Ben 3 funzioni !! Non credo però si possa "semplificare".

Allora, il SysTic lo abbiamo con la ISR del Timer in overflow 1000 volte al secondo (assomiglia alla millis(), ma è meglio!!)

ISR(TIMER1_OVF_vect) // Routine dell'INTERRUPT che si attiva all'overflow del Timer1
{
SysTic++; // Incremento il SysTic ad ogni Overflow del Timer (che avviene 1000 volte/sec)

switch(SysTic)
{
case 60000:
SysTic = 0;
}
TCNT1 = 0xC17F; // Ricarico dentro il Timer il numero decimale 49535 ed esco
}

Poi diamo un limite al conteggio del SysTic, dopo di chè c'è il reset.

Ho scelto 60.000:

  1. Perchè è il limite di un ciclo per il nostro NOANTROS, 60.000 cicli = 1 minuto

  2. E poi perchè sta bello preciso dentro un int senza segno, e non ci complica la vita

Ho usato il costrutto switch perchè nei PIC è più veloce dell' if. Lascio a volenterosi il test con AVRstudio, la scrittura del codice C ed il pescaggio della finestra con l'Assembly, per verificare la differenza di istruzioni tra switch e if.

Quindi il SysTic va a tappo una volta al minuto e riparte da zero e conta 60.000 eventi con la risoluzione del millesimo di secondo. Direi più che sufficiente per un sistema RTOS dei poveri! :smiley:

Ora creeremo lo Scheduler con una funzione che chiameremo... Scheduler()! Ma va? :smiley:

I Task:

void Task_1()
{
PINB = 0b00100000; // Toggle PORTB5 = D13 di Arduino
}

void Task_2()
{
PINB = 0b00010000; // Toggle PORTB4 = D12 di Arduino
}

void Task_3()
{
PINB = 0b00001000; // Toggle PORTB3 = D11 di Arduino
}

void Task_4()
{
PINB = 0b00000100; // Toggle PORTB2 = D10 di Arduino
}

I Task sono tutti uguali, ma nulla vieta di fare robe più complesse.

Unica CONSTRAIN (OKKIO!) la durata del singolo Task non deve superare l'intervallo più breve tra un Task e l'altro.

Si ma qua non va bene eh! :slightly_frowning_face:

Doveva essere un thread dove le menti illuminate del forum si sarebbero riunite per fare cagnara, fare "soffoco", darsi fastidio a vicenda. Insomma parlare di algoritmi a 29 bit + 1, discutere dei famosi "registri fantasma", intavolare discussioni se sia meglio spostare i bit a sinistra o spostarli a destra, analizzare istruzioni atomiche, verificare e tradurre in firmware il paradosso del gatto-morto-dentro-la-scatola-che-però-non-è-morto (Schroedinger). In una frase: portare avanti l'Umanità! :drooling_face:

E invece GNiente con la GN maiuscola...

Mi trovo qua, a parlare da solo come i matti... :cry:

... ma no, tranquillo, continua, continua a parlare .... (i matti vanno assecondati) ...

Guglielmo :grin: :grin: :grin:

Ma! Forse è quel Bare Metal nel titolo che frena la gente.
Inoltre, a parte un gruppo di utenti esperti, di cui io non faccio parte, il livello di programmazione è basso, e la filosofia di Arduino non incoraggia ad approfondire.
Ci sono le funzioni di I/O Digital e Analog, si applicano e stop.
Franco

Giracchiando su internet per un lcd i2c mi sono imbattuto su questa libreria SoftwareI2C:
http://playground.arduino.cc/Main/SoftwareI2CLibrary
per avere I2C su altri pin. Logicamente non I2C hw quindi meno performante. La libreria chiede di definire i pin non come numero da shield ma come port+bit
E la libreria è scritta in assembly !!

Suggerisco questo libro.

  1. Per chi non è Advanced: Si può anche iniziare a scrivere qualcosa. Gli esempi sono corti e ben commentati

  2. Lasciamo stare l'Assembly (non Assembler) altrimenti diventiamo matti sul serio

  3. Bene anche il libro, se porta qualche spunto interessante. Se lo hai già, cosa aspetti ad aprirlo e postare qualcosa qua? :smiley:

  4. Si può anche far finta di dire qualcosa di teNNico, L'importante è partecipare! :smiley:

Non so se è in topic ma è baremettalling, qualcuno ha provato ChibiOS con Arduino?
Ci sono degli esempi per la Mega.

bigjohnson:
Non so se è in topic ma è baremettalling, qualcuno ha provato ChibiOS con Arduino?
Ci sono degli esempi per la Mega.

Si, l'ho visto. Gli RTOS hanno quello che si chiama footprint (impronta del piede) ovvero quanta Flash e quanta RAM occupano, solo così per campare. E Arduino non è che abbia così tante risorse (più che altro la RAM), quindi alla fine, spesso, occupa quasi più l'RTOS che il programma che vogliamo far funzionare. Quindi l'impiego di questi sistemi - su Arduino - va valutato caso per caso.

Nel caso, va molto bene FreeRTOS.

Quindi il SysTic va a tappo una volta al minuto e riparte da zero e conta 60.000 eventi con la risoluzione del millesimo di secondo. Direi più che sufficiente per un sistema RTOS dei poveri! :smiley:

Ora creeremo lo Scheduler con una funzione che chiameremo... Scheduler()! Ma va? :smiley:

Mi è venuta l'idea di modificare la ISR SysTick. Se invece che incrementare una variabile, decrementassimo
il valore del timer di ogni task?
Per fare ciò dovremmo avere una lista di task, per semplicità un array di puntatori a funzione generica.
Serve anche un contenitore dove inserire:

  • il valore dell'intervallo di tempo (ulong taskTimerInterval)
  • Un bool: AutoReload|NoAutoReload
  • Un Bool: Start|Stop
  • Servirebbe anche un modo per riavviare il timer
  • Ci possiamo anche mettere un byte per la priorità
  • Puntatore a funzione generica TFPointer
    Focalizziamo l'attenzione su taskTimerInterval, supponiamo sia imposta a 200 ms

La ISR SysTick su tutti i task decrementa la variabile taskTimerInterval di una unita, quindi avremo che dopo 200 esecuzioni taskTimerInteval raggiunge il valore zero.
Se taskTimerInterval == 0 inserisci TFpointer nello scheduler; Si ma in quale posizione?

Vantaggi:
In questo modo sicuramente evitiamo le if (millis() > o < ecc che porta sempre confusione e side effect, ma cosa altrettanto importante cessa di esistere il millenium bug, cioè mancando la variabile SysTick non c'è rischio che questa vada in overflow.

Svantaggi:
Occorre una struttura o una classe al fine di creare un oggetto task.
Non possiamo sapere da quanto tempo il micro è in funzione.
Possiamo solo gestire task con tempo relativo, perché manca la variabile SysTick.
La ISR deve masticare task, lo scheduler deve masticare task.

Posso confermare che una linked list dinamica di puntatori a struttura task funziona bene su arduino a patto di non fare ricorso a String o ad altro che alloca memoria dinamica. Inoltre la linked list è molto rapida a spostare .
Sarebbe meglio un array di task definito a tempo di compilazione ma si deve trovare il modo di spostare elementi il più velocemente possibile

Ciao.

EWWIWA è intervenuto qualcuno con qualcosa di serio! :smiley:

Mauro allora i Softwaristi non sono "cosi" inutili! :smiley:

Cmq quando un Hardwarista ed un Sofwarista si uniscono, accadono cose prodigiose... :o :roll_eyes:
...tipo che anche un Blink smette di funzionare! :smiley:

Mauro appena hoi un pò di tempo butto giù del codice (che provvederai a correggere) e lo testiamo. :grin: