PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Multitasking im AVR-Studio in "C"!



madangdive
13.03.2010, 10:39
Brauche wieder einmal eure Hilfe!
Bin noch ein Anfänger, möchte aber mehrere Dinge parallel laufen lassen.
Was ich bräuchte ist, ein Grundgerüst, damit ich darauf aufbauen kann.
Anbei ein Übungsprogramm, von dem ich nicht weiss, ob der Ansatz so stimmt, oder komplett falsch ist.
Wo bekommen ich Info's bzw. kann mir jemand den Code anbei aktualisieren. Wäre euch dankbar!
Momentan (jetziges Programm ist noch länger wie dies im Anhang) läuft mein Programm nicht gut, d.h: es macht was es will. Glaube die Funkt. im "Compare-Match-Teil sind zu lang!?!?.
Vorab schon Mal vielen Dank!
Mad

//======================== Programmbeschreibung ========================
// 1.) Die Zeitkritischen Funkt. werden über einen Switch/Case-Schleife mittels "i_ext" gesteuert
// 2.) 1x pro Millisek. sollten jene Funktionen aufgerufen werden, die sich ausserhalb der Switch-Case-Schleife befinden.
// 3.)Im "main-Teil" sind nur zeitunkritische Funktionen!
//================================================== ===================

#include "definition.h"
#include "LCD_def.h"
#include "lcd_drv.c"

uint8_t Funktion_LED_1(uint16_t ms);
uint8_t Funktion_LED_2(uint16_t ms);
uint8_t Funktion_LED_3(uint16_t ms);

uint8_t Tastaturabfr(uint16_t ms);
uint8_t In_Out_Abfr(uint16_t ms);

extern uint8_t i_ext = 0; // Eventhandler (steuert die zeitkritischen Funktionen) dekl. & init

//************** Beginn zeitkritische Funktionen !!!**********
ISR (TIMER0_COMP_vect)
{
switch(i_ext) // Eventhändler zum steuern des Ablaufs!!!
{
case 10: Funktion_LED_1(100); break; // bestimmen der Blinkfrequenz Led 1
// case 11: Funktion_LED_2(100); break; // bestimmen der Blinkfrequenz Led 2
// case 12: Funktion_LED_3(100); break; // bestimmen der Blinkfrequenz Led 3
}
//Tastaturabfr_4x3(100); break; // längere Funktionen mit LCD Ausgabe
//In_Out_Abfr(100); break; // längere Funktionen mit LCD Ausgabe

}
//************** Ende zeitkritische Funktionen !!!**********

int main()
{

//************************************************** **********************
// Timer Konfig. (Compare Match) zählt bis xx-Wert hoch (zB.: 8) --> IR
//************************************************** **********************
TIMSK |= (1 << OCIE0); // IRQ bei Compare Match
OCR0 = 8; // Vergleichswert 8 einstellen => ca. 1000 Hz (1ms) --> (8000000/1024/8)
sei(); // IRQs enable (Interrupt global einschalten)
TCCR0 |= (1 << WGM01); // CTC-Modus (Vergleichmodus)
TCCR0 |= (1 << CS00) | (1 << CS02); // Prescaler: 1/1024 (zB.: CPU-Takt 8000000/ Teiler 1024)
//************************************************** **********************
// I2C & LCD Konfiguration
//************************************************** **********************
lcd_init(); // LCD initialisieren
// i2c_init(); // initialize I2C library
//************************************************** **********************
// Port Konfig.
//************************************************** **********************
DDRA = 0x00; // Eing. Def.
PORTA= 0xFF; // Alle Pin via Pullup auf High
DDRB = 0xFF; // Ausg. Def.
PORTB= 0xFF; // Alle Pin via Pullup auf High
lcd_pos(2,0);
lcd_text("Guten Tag!");
_delay_ms(1000);

while(1) //endlos
{
lcd_clear();
lcd_pos(2,0);
lcd_text("Step 1!");
_delay_ms(1000);// was mich stört sind, diese sinnlosen delay's

i_ext = 10; //Aufruf Zeitk.-Funktion_LED_1() im Compare-Match-Modus;
_delay_ms(1000);

lcd_pos(2,0);
lcd_text("Step 2!");
_delay_ms(1000);
i_ext = 0; // Zeitkritischen-Funktionsaufruf sperren

LCD_Ausg();
_delay_ms(1000);

//usw:
} // ENDE while(1);
return 0;
} ENDE main();
//************************************************** **********************
// ZEIT-Kritische Funktion 1 wird mit einer Variable (i_ext) gesteuert
//************************************************** **********************
uint8_t Funktion_LED_1 (uint16_t ms) // Funktionsaufruf mit Wertübergabe
{
static uint16_t i_zaehler = 0; // Zähler auf 0 setzen (einmalig)
if (i_zaehler >= ms) // Zählerstand > wie Wert der Funktion --> rein in die Schleife
{ PORTB ^= (1 << PB1); // Toggle Pin 2 von PORT B
i_zaehler = 0; // Zähler wieder auf 0
}
i_zaehler ++; // sonst erhöhe Zähler um 1
return 0; // zurück ins HP
}

//************************************************** **********************
// NICHT-Zeitkritische Funktion
//************************************************** **********************
void LCD_Ausg(void)
{
lcd_clear(); // kompl. LCD löschen!!!!
_delay_ms(30);
lcd_text(" Hallo wie geht ");
lcd_pos(2,0);
lcd_text(" es dir ");
lcd_pos(3,0);
lcd_text(" heute? ");
lcd_pos(4,0);
lcd_text(" gut/schlecht? ");
}

ChristophB
13.03.2010, 11:54
hi, also dinge paralel laufen lassen ist eher schlcht zu realisieren da der uC nicht mehrere befehle gleichzeitig bearbeiten kann.
Ich bin aus deiner Programmbeschreibung nicht ganz schlau geworden, aber wenn du alle paar ms einen Programmabschnitt ausführen möchtest könntest du eventuell mit einem timer arbeiten der in einem definierten Zeitabstand einen Interrupt ausführt. In dieser Interrupt routine könntest du dann Interrupts unterdrücken so das dieser Zeitkritische code vollständig ausgeführt wird.

hier noch 2 links die dir helfen könnten: http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmieren_mit_Interrupts

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR

Ich hoffe ich konnte dir helfen

Besserwessi
13.03.2010, 13:03
So richtig klassisches Multitasking macht man mit µCs eher selten. Es geht, ist aber realtiv aufwendig. Besser ist es oft direkt die einzelnen Tasks auf interrupts aufzuteilen. Damit hat man mehr Kontrolle was wann gemacht wird.

Eine Aufgabe kann man im hauptprogramm erledigen, der Rest in Interrupts. Wenn ein Task keinen Interrupt hat zu dem er natürlicherweise wegen der Hardware gehört, kann man halt einen Timerinterrupt nutzen und ihn da reinhängen.

mws-h
15.03.2010, 08:51
Zu dem Thema gibt es einiges im Netz. Ich hab z.B. sowas gefunden:
http://www.elektor.de/jahrgang/2010/februar/multitasking-fur-den-atmega.1227989.lynkx
http://www.controllersandpcs.de/pdfs/vmavr.pdf

Und ein interessantes Buch:
http://www.reliability.de/Systemprogrammierung/3895762180.html

chientech
21.03.2010, 21:28
Hi,
google mal nach AVRX.

mfg

Vitis
22.03.2010, 08:10
Am Einfachsten geht sowas über Statemachine

madangdive
28.03.2010, 12:12
hi vitis!
was ist eine statemachine, bzw. wie wird so etwas angewendet!
gruß mad

Babbage
29.03.2010, 15:50
Bisschen träge dieser Thread, dann werd ich mal meinen Senf dazu geben.

Also: den Vorschlag mit der Statemachine finde ich in deinem Fall am günstigsten. Du hast keine Zeitkritischen Dinge zu erledigen und extra AVRX zu verwenden finde ich da übertrieben.

Ich versuche mal Deinen code einfach mal was mit ein paar Kommentaren in eine Statemachine zu übersetzen





char SignalTimeIstUp=0; // soll nach 1000ms im Interrupt gesetzt werden

ISR (TIMER0_COMP_vect)
{
// irgendwie immer nach 1000ms setzen, könnte auch einmal/ms sein und dann ein Zähler sein der bis 1000 zählt,
// dann könnte man auch kleinere Wartezeiten wie die 30ms realisieren
SignalTimeIstUp=1;
}


// die Zeitroutine (ist nur ein Beispiel geht auch schöner)
char Time_is_over(void)
{
if( SignalTimeIstUp )
{
// Zeit ist um, Signal zurücksetzen
SignalTimeIstUp=0;
return 1;
}
return 0; // Zeit ist noch nicht um
}





int main()
{
// Init alles was du brauchst


char ZustandLCD=0; // Zustand der Statemachine
char SignalAusgabe=0; // Eventhändler zum steuern des Ablaufs!!!

while(1)
{
// Statemachine LCD
switch(ZustandLCD)
{
case 0:
if( Time_is_over() )
{
lcd_clear();
lcd_pos(2,0);
lcd_text("Step 1!");
ZustandLCD=1;
};
break;

case 1: // wieder warten bis 1000ms abgelaufen
if( Time_is_over() )
{
SignalAusgabe=1;
ZustandLCD=2;
};
break;

case 2:
if( Time_is_over() )
{
lcd_pos(2,0);
lcd_text("Step 2!");
ZustandLCD=3;
};
break;

case 3:
if( Time_is_over() )
{
LCD_Ausg(); // da in LCD_Ausg noch gewartet wird könnte man diesen in verschiede Zustände (States) aufteilen
ZustandLCD=0; // wieder vorne anfangen
};
break;
}

// Blinken

// hier bitte blinken je nach SignalAusgabe und Zeitpunkt, dies könnte auch eine weitere Statemachine sein


}
}

mano
29.03.2010, 17:54
Was Du suchst ist ein Scheduler. Im einfachsten Fall ist es eine einfach verkette Liste mit Funktionspointern. Man kann es aber ein wenig komplizierter machen, wenn man zwei Task quasi parallel laufen lassen möchte. Da muss man PC, SP und die Register wieder passend setzten...

Naja, eine Statemachine ist eigentlich für was anderes gedacht und seine Aufgaben auf verschiedene Interrupts zu verteile ist auch nicht das wahre. Die Interrupt-Routinen lieber klein halten und in der Run-Loop den unkritischen Rest abarbeiten.

Ich habe mal einen einfachen Scheduler als Anhang mit drangehängt. Der Code ist leider nicht viel dokumentiert, sondern war einfach mal zum testen geschrieben worden. Eventuell ist der komplette Code nicht ganz fehlerfrei, weil ich nicht mehr weiß was ich zum Schluss damit gemacht habe. Interessant ist "list" und "main". Das ganze ist übrigens in diesem Fall so ausgelegt, dass es immer ein Element in der Liste gibt.

Die zwei Links sind auch zu empfehlen:
GOS: einfacher preemptive multitasking scheduler
http://www.mikrocontroller.net/topic/74026

Wartezeiten effektiv (Scheduler)
http://www.mikrocontroller.net/topic/12176

Robotniks
29.03.2010, 18:37
Hi,

du könntest entweder RTOS bzw. Free RTOS oder http://www.femtoos.org/ benutzen oder die neue
C-Control Pro www.c-control.de dort ist ein echtes Multithreading im Interpreter eingebaut.


Grüße