Thread-Alternativen für eingebettete Systeme
Ich studiere derzeit Elektrotechnik. Aufgrund der Pandemien wurde mein Unterricht unterbrochen und ich nutze diese Zeit, um mehr über Elektronik und Programmierung zu lernen.
Ich versuche derzeit, eine Pic16f628aund eine generische Digitalanzeige zu verwenden, um eine Digitaluhr mit einigen Funktionen zu erstellen. Die Sache ist, dass ich während der Ausführungszeit auf ein Menü zugreifen musste, indem ich eine Taste drückte, während die Uhr angezeigt wurde. Normalerweise würde ich einen Thread für die Uhranzeige aufrufen und der Haupt-Thread würde nach Eingaben suchen, aber aufgrund der Einfachheit des Bildcontrollers kann ich die Ressource nicht verwenden.
Mein C-Code (der noch nicht speziell für pic implementiert wurde) sieht ungefähr so aus:
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();
}
}
Während des Sleep () müsste der Controller in der Lage sein, nach neuen Eingaben zu suchen und entsprechend zu handeln.
Eine Alternative war jedoch, eine Zustandsmaschine zum Speichern eines Tastendrucks zu verwenden und die Schlaffunktion in 4 oder 8 Intervalle zu unterteilen.
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);
Es könnte getan werden, aber ich würde mich freuen, wenn ich dem Projekt nicht mehr Komplexität hinzufügen müsste, zum Beispiel eine weitere Zustandsmaschine. Was kann ich in Thermos von Software tun, um diese Funktionalität zu implementieren?
Antworten
Threading ist ein übergeordnetes Konzept als die Programmierung von Mikrocontrollern. Einfach ausgedrückt, Threads werden als Scheduler implementiert, der Timer-Interrupts verwendet, wodurch wiederum der Programmzähler + der Stapelzeiger usw. gespeichert und auf verschiedene Speicherorte gesetzt werden. Es ist also durchaus möglich und einfach, ein ähnliches Konzept mithilfe von Interrupts zu implementieren - mit dem Vorteil, dass Sie anstelle von generischem Multithreading spezielle Interrupts erhalten.
Dies ist ungefähr der einzig sinnvolle Weg, dies mit einem eingeschränkten Legacy 8 Bitter wie PIC zu tun, das in Bezug auf die Stapelverwendung äußerst begrenzt ist. Vergessen Sie die Verwendung von Thread-Bibliotheken, auch von solchen, die für Mikrocontroller geschrieben wurden. Das wird nur zu übermäßigem Aufblähen und Komplexität führen, für nichts gewonnen. Im Allgemeinen ist es eine schlechte Idee, PC-Programmierkonzepte in die eingebettete Welt zu ziehen.
Was Sie sollten tun, ist Ihre Schaltfläche Scannen in einem zyklischen Timer - Interrupt zu setzen , die einmal pro 10 ms oder so ausgeführt wird . Innerhalb des Interrupts rufen Sie die Schaltflächen ab und vergleichen die gelesene Schaltfläche zum Entprellen mit der vorherigen. Das Ergebnis wird in einer Variablen gespeichert, die mit dem Hauptprogramm geteilt wird volatileund als Rennbedingungen deklariert und vor diesen geschützt ist. Da Sie nur innerhalb der Interrupts in die Variable schreiben, ist es möglicherweise ausreichend geschützt, um sicherzustellen, dass die Lesevorgänge 8 Bit betragen. Sie müssen sie jedoch zerlegen, um sicherzugehen. Mehr Infos dazu hier: Verwendung von Volatile in der Embedded C-Entwicklung .
Verwenden Sie Interrupts
Sie möchten beim Drücken einer Taste Code ausführen? Verwenden Sie einen Pin-Change-Interrupt
Sie möchten etwas in einem festgelegten Intervall tun? Verwenden Sie einen Timer-Interrupt
In gewisser Weise führt die Hardware des Mikrocontrollers einen "Thread" aus, der die Interruptquellen überwacht, und führt für jedes Ereignis einen "Rückruf" oder eine Interruptroutine aus.
Das Hauptprogramm wird während der Ausführung des Interrupts automatisch angehalten.
Eine übliche Methode zum Teilen von Daten zwischen Interrupts und volatileHauptcode besteht darin, globale Variablen zu verwenden und Interrupts vorübergehend zu deaktivieren, wenn Daten von diesen Globals gelesen werden, wenn sie größer als die Wortgröße des Controllers sind (fast immer auf einem 8-Bit-Controller).
Ich würde wahrscheinlich eine kooperative Multitasking-Bibliothek vorschlagen. Eines, das ich in der Vergangenheit verwendet habe, ist Protothreads:http://www.dunkels.com/adam/pt/
Jede anständige kooperative Multitasking-Bibliothek hilft dabei, die implizite Zustandsmaschine zu abstrahieren, die erforderlich ist, um den Überblick zu behalten.
Viel Glück.
Es gibt im Allgemeinen verschiedene Ansätze für Multitasking, wenn es um eingebettete Systeme geht:
- Polling oder kooperatives Multitasking : Alles wird in einer Endlosschleife ausgeführt. Die Aufgaben sind so konzipiert, dass sie so wenig Zeit wie möglich benötigen und so schnell wie möglich zur Hauptausführung zurückkehren, um Verzögerungen zu vermeiden. Beachten Sie, dass Aufgaben, die für diese Architektur geeignet sind, möglicherweise nicht das sind, was Sie im Hinblick auf ein übergeordnetes Konzept denken würden. Beispielsweise könnte in Ihrer Anwendung eine Aufgabe
update_displayund eine andere Aufgabe sein,check_buttonund Sie würden eine Schleife erstellen, wie z.
while(1){
check_buttons();
update_display();
sleep(0.1); //seconds
}
Interrupts : Alle möglichen Eingänge, die mit Hardware-Interrupts verbunden sind, und die Hauptausführung bleiben für Dinge übrig, die nicht auf Interrupt gesetzt werden können (möglicherweise nichts. In diesem Fall wird der Mikrocontroller normalerweise in einen Ruhemodus versetzt, um den Stromverbrauch zu reduzieren. Einzelheiten dazu Dies geschieht normalerweise in Abhängigkeit von dem jeweils verwendeten Mikrocontroller und Compiler.
RTOS : Abhängig von der Leistung des Mikrocontrollers kann möglicherweise ein Echtzeitbetriebssystem (RTOS) ausgeführt werden, das möglicherweise eine API zum Erstellen von Aufgaben oder sogar Threads enthält. Dies hängt von der Anwendung und den Hardwarefunktionen ab und sollte für Schulungsbeispiele nicht erforderlich sein (oder ratsam sein, imo).
Bedenken Sie auch, dass ein weiterer wichtiger Teil bei der Entscheidung über die Gesamtarchitektur der Anwendung die Aufteilung der Aufgaben und deren Zusammenarbeit ist. Eines der verwendeten Paradigmen sind die State Machines (der Link führt zur allgemeinen Wikipedia-Seite, die möglicherweise überwältigend ist. Einfachere Ressourcen für die eingebettete Programmierung finden Sie in Ihrem Lehrbuch oder bei Google).
Meistens haben 8-Bit-Geräte eine begrenzte Quelle. Ich denke, eine einfachere Lösung ist eine bessere Lösung in 8-Bit-PICs.
Sie können Hardware-Timer für 2 verschiedene Aufgaben erstellen. Setzen Sie ein Flag und überprüfen Sie das Flag in Ihrer Endlosschleife, erledigen Sie die Aufgabe und setzen Sie das Flag zurück. Verwenden Sie keine Verzögerungen. Diese Methode garantiert, dass Ihre Aufgaben in Ihrer Endlosschleife erledigt werden, wenn Ihre Flaggen hoch sind.
Sie müssen jedoch wissen, dass die Aufgaben nicht genau zu dem Zeitpunkt ausgeführt werden, zu dem die Flags hochgefahren sind. Wenn zwei Flags gleichzeitig gesetzt wurden, können Sie nicht wissen, welches zuerst ausgeführt wird. Weil Sie nicht wissen, wo in der Endlosschleife. Für nicht zeitkritische Schnittstellenanwendungen ist dies jedoch meistens in Ordnung .