Alternative di thread per sistemi embedded

Aug 16 2020

Attualmente sto studiando ingegneria elettrica. A causa delle pandemie, le mie lezioni sono state sospese e sto usando questo tempo per saperne di più sull'elettronica e sulla programmazione.

Attualmente sto cercando di utilizzare un Pic16f628adisplay digitale generico per costruire un orologio digitale con alcune caratteristiche. Il fatto è che avevo dovuto accedere a un menu premendo un pulsante in tempo di esecuzione, mentre l'orologio veniva visualizzato. Normalmente chiamerei un thread per la visualizzazione dell'orologio e il thread principale guarderebbe gli input, ma a causa della semplicità del controller pic non posso usare la risorsa.

Quindi, il mio codice C (non ancora implementato specificamente per pic) è qualcosa del genere:

void display_timer(){
  static struct current_time timer;
  static int is_time_set = 0;
  set_current_time(&timer, &is_time_set);

  while (is_time_set){
    system("clear");
    printf("########\n");
    printf("%d:%d:%d\n", timer.HOURS, timer.MINUTES, timer.SECONDS);
    printf("########\n");
    sleep(1);
    update_timer(&timer, &is_time_set);
  }
}
int main ()
{
  while (1){
    display_menu();

  }
}

Durante lo sleep (), il controller dovrebbe essere in grado di guardare nuovi input e agire di conseguenza.

Un'alternativa che pensavo fosse quella di utilizzare una macchina a stati per memorizzare la pressione di un pulsante, dividendo la funzione sleep in 4 o 8 intervalli, qualcosa del genere:

  while (is_time_set){
    system("clear");
    printf("########\n");
    printf("%d:%d:%d\n", timer.HOURS, timer.MINUTES, timer.SECONDS);
    printf("########\n");
    for (int i = 0; i<8; i++){
    if (state_machine_input == 1){state_machine_input = 0; break;}
    sleep(1/8);
    }
    update_timer(&timer, &is_time_set);

Potrebbe essere fatto, ma apprezzerei se non dovessi aggiungere più complessità al progetto, aggiungendo ad esempio un'altra macchina a stati. Cosa posso fare nei therms del software per implementare questa funzionalità?

Risposte

28 Lundin Aug 17 2020 at 07:04

Il threading è un concetto di livello superiore rispetto alla programmazione del microcontrollore. In poche parole, i thread sono implementati come uno scheduler che utilizza interrupt del timer, che a sua volta salva il contatore del programma + il puntatore dello stack, ecc. E li imposta in posizioni diverse. Quindi è abbastanza possibile e facile implementare un concetto simile usando gli interrupt, con il vantaggio di ottenere interrupt specializzati invece del multi-threading generico.

Questo è l'unico modo sensato per farlo con un 8 bitter legacy limitato come PIC, che è estremamente limitato quando si tratta di utilizzare lo stack. Dimentica di usare le librerie di thread, anche quelle scritte per microcontrollori. Ciò aggiungerà solo un eccesso di gonfiore e complessità, per niente guadagnato. In generale, è una cattiva idea trascinare i concetti di programmazione per PC nel mondo embedded.

Quello che dovresti fare è inserire la scansione dei pulsanti all'interno di un interrupt ciclico del timer che viene eseguito una volta ogni 10 ms circa. Dall'interno dell'interrupt, si interrogano i pulsanti e si confronta il pulsante letto con il precedente una volta, per scopi di debouncing. Il risultato di ciò viene memorizzato in una variabile condivisa con il programma principale, dichiarato come volatilee protetto dalle race condition. Dal momento che scrivi nella variabile solo dall'interno degli interrupt, potrebbe essere una protezione sufficiente per garantire che le letture siano 8 bit, ma devi disassemblare per essere sicuro. Maggiori informazioni al riguardo qui: Utilizzo di volatile nello sviluppo C incorporato .

15 Pelle Aug 17 2020 at 13:23

Usa le interruzioni

Vuoi eseguire del codice quando premi un pulsante? Usa un pin-change-interrupt

Vuoi fare qualcosa a un intervallo fisso? Usa un timer di interruzione

In un certo senso, l'hardware del microcontrollore esegue un "thread" che monitora le sorgenti di interrupt ed esegue una "callback" o routine di interrupt per ogni evento.

Il programma principale viene automaticamente messo in pausa durante l'esecuzione dell'interrupt.

Un modo comune per condividere i dati tra gli interrupt e il codice principale è attraverso le volatilevariabili globali e disabilitare temporaneamente gli interrupt durante la lettura dei dati da questi globali quando sono più della dimensione della parola del controller (quasi sempre su un controller a 8 bit)

11 AndrewLentvorski Aug 16 2020 at 23:38

Probabilmente suggerirei una libreria multitasking cooperativa. Uno che ho usato in passato è Protothreads:http://www.dunkels.com/adam/pt/

Qualsiasi libreria multitasking cooperativa decente aiuterà ad astrarre la macchina a stati implicita richiesta per tenere traccia delle cose.

In bocca al lupo.

6 bracco23 Aug 17 2020 at 12:53

Esistono in generale diversi approcci con il multitasking quando si tratta di sistemi embedded:

  • Polling o multitasking cooperativo : tutto viene svolto in un ciclo infinito e le attività sono progettate per richiedere il minor tempo possibile e tornare all'esecuzione principale il più velocemente possibile, per evitare ritardi. Nota che le attività adatte a questa architettura potrebbero non essere ciò a cui penseresti in termini di concetto di livello superiore, ad esempio nella tua applicazione potrebbe essere update_displayun'attività e un'altra attività potrebbe essere check_buttone dovresti costruire un ciclo come:
    while(1){
         check_buttons();
         update_display();
         sleep(0.1); //seconds
     }
  • Interrupt : tutti i possibili ingressi collegati agli interrupt hardware e l'esecuzione principale è lasciata per cose che non possono essere messe in interrupt (potrebbe non essere nulla, nel qual caso di solito il microcontrollore viene messo in modalità sleep per ridurre i consumi energetici. Dettagli su come questo di solito dipende dal particolare microcontrollore e compilatore utilizzato.

  • RTOS : a seconda della potenza fornita dal microcontrollore, potrebbe essere possibile eseguire un sistema operativo in tempo reale (RTOS), che potrebbe fornire API per creare attività o persino thread. Ciò dipende dall'applicazione e dalle capacità hardware e per esempi didattici non dovrebbe essere necessario (o consigliabile, imo)

Considera anche che un'altra parte importante nel decidere l'architettura complessiva dell'applicazione è la divisione in compiti e il modo in cui cooperano. Uno dei paradigmi utilizzati sono le macchine a stati (il collegamento è alla pagina generale di wikipedia che potrebbe essere travolgente, risorse più semplici specifiche per la programmazione incorporata si possono trovare sul tuo libro di testo o su google).

5 ExpinElectronics Aug 17 2020 at 06:15

Per lo più, i dispositivi a 8 bit hanno una sorgente limitata. Penso che una soluzione più semplice sia una soluzione migliore nei PIC a 8 bit.

È possibile creare timer hardware per 2 diverse attività. Imposta una bandiera e controlla la bandiera nel tuo ciclo infinito, esegui il compito e ripristina la bandiera. Non usare ritardi. Questo metodo garantisce di svolgere le tue attività nel tuo ciclo infinito, se il tuo flag è attivo.

Ma devi sapere che l'attività non viene eseguita nel momento esatto in cui viene visualizzato il flag. Se sono stati impostati due flag contemporaneamente non è possibile sapere quale viene eseguito per primo. Perché non sai dove nel ciclo infinito. Ma per lo più va bene per applicazioni di interfacciamento non critiche in termini di tempo.