-
schaut mal bei www.avrfreaks.net vorbei. Da ist ein sehr guter Artikel zu dem Thema. Letzendlich installiert man ein Betriebssystem, das die verschieden Threads(Prozess)/Tasks(Aufgabe) oder wie auch immer ihr die nennen wollt verwaltet. (Will mich hier nicht festlegen, aber ich glaube als Task bezeichnet man ein Programm das mehrere Prozesse haben kann) Grundsätzlich werden die Befehle nacheinander verarbeitet. (Es gibt zwar auch IC's mit mehreren Prozessoren aber ich glaub nicht in dieser Preisklasse)
-
OK ich hab nochmal nach der ct gesucht: es ist die 6/2004(8.3.2004) ab Seite 248.
Hier könnt ihr euch den kompletten Quellcode für den MSP430 downloaden, kann man aber sicher auch mim AVR realisieren:
ftp://ftp.heise.de/pub/ct/listings/0406-248.zip
Hier ist die komplette Linkseite von heise.de:
http://www.heise.de/ct/04/06/links/248.shtml
Wer die C't daheim hat hat Glück, denn da wird der Code erklärt.
Es wird hier natürlich kein echtes Betriebssystem programmiert, ich muss zugeben "Scheduler"(recycle) passt deutlich besser. Es ist wie recycle sagt ein Programm, das per Timer verschiedenen Pseudo-Threads verschieden viel Rechenzeit zuschaufelt.
-
MultiThreading Kernels
Also
schaut mal unter http://instruct1.cit.cornell.edu/courses/ee476/
und dann unter : AT90 MCU realtime operating systems
da gibts ne Menge MultiThREADING KERNELS
Thread bedeutet alle Funktionen (die Threads eben) laufen in einem gemeinsamen Speicherbereich (da der AVR oder sonstiger MC keine Memory Managment Unit besitzt um die Prozesse voneinaner zu schützen)
diese Threads werden aber Preemtiv (also vom Kernel selbst(zBsp:durch TimerINT) umgeschaltet und alles was zum Thread gehört (Stack,Register) wind gesichert und wenn der Thread wieder aktiv wird , wieder zürückgeholt !(Context Switching)
-Jeder Thread hat seinen eigenen Stack und seine eigenen Register
usw....
-
Dort findet man zum Beispiel das:
Code:
;***** Simple time-based scheduler ********
;
;The goal is to allow several tasks to execute at a regular rate
;without making a HUGE timer ISR. The timer 0 interrupt just
;decrements counters which are tested in a loop to dispatch
;subroutines. Timer 0 ticks at a mSec rate in this code.
;Task descriptions:
; task 1: blink LED 0 2/sec
; task 2: blink LED 1 1/sec
; task 3: Detect button 0 and change task 1 blink rate if the button is down
;
;********************************
;You will need to CHANGE this path
,nolist
.include "c:\avrtools\appnotes\8515def.inc"
.list
;********************************
;define registers
.def save =r1
.def temp =r16 ;temporary register
.def LED =r17 ;the actual LED value to display
.def reload =r18 ;holds timer reload value
;Specify the interval for each task to execute:
;since we are toggling LED state on each task entry,
;the task times are 1/2 the desired blink times
.equ t1 =250 ;250 mSec
.equ t2 =125 ;125 mSec - Task2 will need to have another counter
.equ t3 =30 ;30 mSec interval for the keyboard scanner
;********************************
;RAM locations:
;Task execution time intervals
.dseg
time1: .byte 1
time2: .byte 1
time3: .byte 1
;Task2 state variable needed to
;count 4 times the 1/4 second secheduled time
tsk2c: .byte 1
;message from task3 to task1 to change rate
tsk3m: .byte 1
;*******************************
; Initialization
.cseg
.org $0000
rjmp RESET ;reset entry vector
reti
reti
reti
reti
reti
reti
rjmp TIMER
reti
reti
reti
reti
reti
RESET:
ldi Temp, LOW(RAMEND) ;setup stack pointer
out SPL, Temp
ldi Temp, HIGH(RAMEND)
out SPH, Temp
;set up the PORTs
ser Temp ;set PORTB to be
out DDRB,Temp ;all outputs
ldi Temp, 0xff ;turn off LEDs
out PortB, Temp
clr Temp ;set PORTD to be
out DDRD,Temp ;all inputs
;set up timer 0 for 1 mSec ticks
ldi Temp,exp2(TOIE0);enable timer interrupt
out TIMSK, Temp
ldi Temp, 3 ;prescale timer by 64
out TCCR0, Temp
ldi Reload,256-62 ;preload timer since
out TCNT0, Reload ;62.5 x (64x.25) microSec = 1.0 mSec.
;initialize task timers
ldi temp, t1 ;mSec
sts time1, temp
ldi temp, t2
sts time2, temp
ldi temp, t3
sts time3, temp
;initialize LED state
ldi LED, 0xff ;all LEDs off
;initialize task2 state variable (4 times thru equals 1/2 sec)
ldi temp, 4
sts tsk2c, temp
;initalize task3 message to zero (don't modify task1 rate)
ldi temp, 0
sts tsk3m, temp
;Start the clock ticking
sei ;enable all interrupts
******************************************************************
;Now start scheduling events.
;This is the main program loop.
;All tasks are subroutines called from here when
;their respective timers reach zero.
Sched:
tsk1: lds temp, time1 ;test for first task ready
tst temp
brne tsk2 ;if not then skip it
rcall Task1
tsk2: lds temp, time2 ;test for second task ready
tst temp
brne tsk3 ;if not then skip it
rcall Task2
tsk3: lds temp, time3 ;test for third task ready
tst temp
brne Sched ;if not then skip it
rcall Task3
rjmp Sched
;*****************************************************************
;The three actual tasks:
;******************************
;LED 0 2/sec (but modified by Task 3)
Task1: lds temp, tsk3m ;get the message from task 3
tst temp ;and test it for "fast/normal"
brne t1fast
;if we get here the message from task 3 ="normal rate"
ldi temp, t1 ;reinit time counter
sts time1, temp
rjmp t1blk
;if we get here the message from task 3 ="fast rate"
t1fast: ldi temp, t1 ;reinit time counter
lsr temp ;divide time by four
lsr temp
sts time1, temp
;now toggle the LED
t1blk: mov temp, LED ;isolate the zero bit
andi temp, 0b00000001; and invert it
andi LED, 0b11111110
com temp
andi temp, 0b00000001
or LED, temp
out PORTB, LED
ret ;go back to scheduler
;******************************
;LED 1 1/sec
Task2: ldi temp, t2 ;reinit time counter
sts time2, temp
lds temp, tsk2c ;find out if 4 counts have occured
dec temp ;so that we can count to 1/2 second
sts tsk2c, temp
tst temp
brne t2exit ;if not then leave
;if we get here, then 1/2 second has passed and we
;should toggle the LED
mov temp, LED ;isolate the zero bit
andi temp, 0b00000010; invert it
andi LED, 0b11111101; then combine it with LED again
com temp
andi temp, 0b00000010
or LED, temp
out PORTB, LED
ldi temp, 4 ;and reset the state variable
sts tsk2c, temp
t2exit: ret ;go back to scheduler
;******************************
;Button detect and modify Task 1 blink rate
Task3: ldi temp, t3 ;reinit time counter
sts time3, temp
in temp, PIND ;get all the buttons
com temp ;convert a button-down to a one
andi temp, 0b00000001;isolate button 0
brne t3modt
;if we get here then set message to "normal" task1 rate
ldi temp, 0 ;set message value
sts tsk3m, temp ;and store it
rjmp t3exit
;if we get here then set message to "fast" task1 rate
t3modt: ldi temp, 1 ;set message value
sts tsk3m, temp ;and store it
t3exit: ret ;go back to scheduler
;**************************************************************
;timer 0 ISR (timer-zero overflow)
;Enters every 1.0 mSec
TIMER: in save, SREG
out TCNT0, Reload ; keeps clock ticking at 1 mSec
push temp ; use temp in ISR
;update each of the three timers
;for the three tasks
;BUT if the count is zero don't do anything until the process
;resets it
lds temp, time1
tst temp
breq t0t2
dec temp
sts time1, temp
t0t2: lds temp, time2
tst temp
breq t0t3
dec temp
sts time2, temp
t0t3: lds temp, time3
tst temp
breq t0end
dec temp
sts time3, temp
t0end: pop temp
out SREG, save
reti ;back to backgound tasks
;**************************************************************
Ist glaub für Mega32
-
Und in C sieht es so aus:
Code:
//Dup the function of sched1.asm
//used as an example in the program organization doc.
#include <90s8515.h>
//timeout values for each task
#define t1 250
#define t2 125
#define t3 60
//the three task subroutines
void task1(void); //blink at 2 or 8 Hz
void task2(void); //blink at 1 Hz
void task3(void); //detect button and modify task 1 rate
void initialize(void); //all the usual mcu stuff
unsigned char reload; //timer 0 reload to set 1 mSec
unsigned char time1, time2, time3; //timeout counters
unsigned char tsk2c; //task 2 counter to get to 1/2 second
unsigned char tsk3m; //task 3 message to task 1
unsigned char led; //light states
//**********************************************************
//timer 0 overflow ISR
interrupt [TIM0_OVF] void timer0_overflow(void)
{
//reload to force 1 mSec overflow
TCNT0=reload;
//Decrement the three times if they are not already zero
if (time1>0) --time1;
if (time2>0) --time2;
if (time3>0) --time3;
}
//**********************************************************
//Entry point and task scheduler loop
void main(void)
{
initialize();
//main task scheduler loop
while(1)
{
if (time1==0) task1();
if (time2==0) task2();
if (time3==0) task3();
}
}
//**********************************************************
//Task subroutines
//Task 1
void task1(void)
{
time1=t1; //reset the task timer
if (tsk3m != 0) time1 >>= 2; //check for task 3 message
//toggle the zeros bit
if ((led & 0x01) == 0)
led = led | 0x01;
else
led = led & 0xfe;
PORTB = led;
}
//*******************************
//Task 2
void task2(void)
{
time2=t2; //reset the task timer
if (--tsk2c == 0) //have we waited 1/2 second?
{
tsk2c = 4; //reload the 1/2 sec counter
//toggle the ones bit
if ((led & 0x02) == 0)
led = led | 0x02;
else
led = led & 0xfd;
PORTB = led;
}
}
//*******************************
//Task 3
void task3(void)
{
time3=t3; //reset the task timer
tsk3m = ~PIND & 0x01; //generate the message for task 1
}
//**********************************************************
//Set it all up
void initialize(void)
{
//set up the ports
DDRD=0x00; // PORT D is an input
DDRB=0xff; // PORT B is an ouput
PORTB=0;
//set up timer 0
reload=256-62; //value for 1 Msec
TCNT0=reload;
TIMSK=2; //turn on timer 0 overflow ISR
TCCR0=3; //prescalar to 64
//init the LED status (all off)
led=0xff;
//init the task timers
time1=t1;
time2=t2;
time3=t3;
//init the task 2 state variable
//for four ticks
tsk2c=4;
//init the task 3 message
//for no message
tsk3m=0;
//crank up the ISRs
#asm
sei
#endasm
}
-
Nach einigem Halbwissen werde ich erst mal ein bisschen Aufklärung betreiben ;)
In der Betriebssystemtechnik unterscheidet man grundsätzlich bei Multitasking-BS Prozesse und Threads.
Der wesentliche Unterschied ist, dass Prozesse in einem eigenem Addressraum ablaufen, Threads hingegen nicht. Threads laufen im Kontext von Prozessen. Aufgeweicht wird das Ganze durch shared Memory, den sich Prozesse teilen können.
Was ein Multitasking-Betriebssystem für Atemls AVR angeht, so muss man sich das in der Tat nicht als ein grosses BS ala Linux vorstellen.
Im einfachsten Fall ist das ein simpler Round-Robin-Scheduler, der in festen Zeitscheiben die Threads aufruft. Basta. Ein paar Programmzeilen, das war es.
Das Ganze kann man dann beliebig um die Üblichen Zutaten erweitern: variable Zeitscheiben, feste Prioritäten, variable Prioritäten, Interprozesskommunikation (Semaphore, shared Memory, etc).
Auch wenn man es etwas komplexer macht, so kostet das auch nur Rechenzeit in der Gegend von maximal einigen Prozent.
Ob das Ganze Sinn macht, muss jeder selbst wissen. Die weitaus meisten Controller-Anwendungen in der Industrie funktionieren nach einer Studie aus diesem Jahr immer noch nach dem guten alten Hauptschleife-Flags-Interrupt-Prinzip, wo in der Hauptschleife im Wesentlichen die Flags ausgewertet werden und der Controller dann die entsprechenden Routinen ausführt. In den entsprechenden Interrups werden die Flags dann je nach Ereignis gesetzt.
Vorteile macht ein Multitasking-System sicher, wenn man mit mehreren Leuten relativ unabhängig voneinander an einem Projekt arbeitet.
Und es macht sicherlich auch unabhängiger von der Hardware, wenn es ein komplexeres System ist, welches stärker abstrahiert.
-
wenn die microprozessoren noch schneller sind und mehr speicher haben machts mehr sinn. wenn 3 prozesse gleichzeitig laufen, braucht man 3 mal so viel ram
-
Hallo Freunde
hab mal genau gewusst was der Unterschied zwischen Multitasking und Multithreading ist. Und zwar über Apples altes Mc68k basierendes Betriebssystem. Dort war es so, daß die Programmierrichtilinien verlangten Code so zu schreiben, dass wenn ein Unterprogramm ausgeführt war man keine Bits gesetzt haben durfte die dem eigenen Programm die Kontrolle über den Prozessor auschließßlich gewährten, also Interrupts maskieren. Das Betriebssystem speicherte die requests der einzelnen tasks selber auf seinen eigenen Stack, und so hat ein Interrupt dann eine Routine des Betriebssystems ausgelöst die der nächsten Task in Stack die Kontroller über den Computer gibt. Hat jetzt ein Programmierer diese maskable interrupts maskiert konnte er den Rechner ohne zeitlliche Begrenzung für sein Programm benutzen. Als dann bei Apple echtes Multitasking kam konnte das Betriebssystem über einen Timer und einem nicht maskable Interuupt die Kontrolle wieder übernehmen, hat die Inhalte aller Register auf das Stack gerettet und die nächste Task begann immer damit ihre Registerinhalte vom Stack zu holen und einzulesen.
Diese Beschreibung zeigt wie man in jedem Prozessor Multitasking einführen kann. Ich kenne den AVR noch nicht, aber ich vermute in Analogie es gibt dort eine Möglichkeit über einen Hardware Timer einen Soft Interrupt auszulösen der einen Zeiger auf eine Interrupt-Service Rotine verweist. Diese Rotine müßte dann wie oben beschrieben die "Laufzeitumgebung der alten Task auf den Stack zu pushen, für die nächste Routine die "Laufzeit" Umgebung wieder einrichten und den Programm Counter auf den letzten Wert, oder letzten Wert plus eins setzen. Natürlich nicht vergessen den Timer zu reseten und neu zu starten.
Wichtig erscheint mir aber der Hinweis, dass wenn eine Task eine "Echtzeit" Aufgabe bearbeitet die Möglichkeit haben muß das Interrupt des Taskmanagers zu maskieren und erst dann wieder entmaskieren wenn das zeitkritische erledigt ist. Gerade im embedded Bereich, und das sind wir hier ja.
Beim Mu
-
vielleicht waere ein microprozessor mit linux besser geeignet (gibts fertig zu kaufen). die sind viel schneller als ein avr, haben viel mehr ram und mehr flash. sind nur etwas teurer.
-
Hallo hrrh
Du weisst schon das wir über Routinen sprechen die nur wenige Zeilen lang sind? Übrigens werden in der Industrie viele sogenannte RTOS eingesetzt die damit werben extrem effizient und extrem kleines footprint zu haben?! Und das in sehr kostensensitiven Anwendungen, z.B 68hc05!?