<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=134.169.116.105</id>
	<title>Mikrocontroller.net - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=134.169.116.105"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/134.169.116.105"/>
	<updated>2026-04-10T21:47:30Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FPGA_Soft_Core&amp;diff=92020</id>
		<title>FPGA Soft Core</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FPGA_Soft_Core&amp;diff=92020"/>
		<updated>2016-02-23T16:34:21Z</updated>

		<summary type="html">&lt;p&gt;134.169.116.105: Bedeutung von soft core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Ein soft core (engl. wörtlich &amp;quot;weicher Kern&amp;quot;, sinngemäß &amp;quot;Software-Kern&amp;quot;) ist ein Prozessor, ein Mikrocontroller oder ein digitaler Signalprozessor, welche als virtuelle Einheit in einem FPGA oder ASIC-Design integriert wird. Somit kann jeder beliebige Prozessor zum Chip hinzugefügt werden, wobei dieser in einem ASIC de facto zu einem hard core (engl. harter Kern) wird. In FPGA besteht dieser aus reiner Anwenderlogik, die dazu entsprechend konfiguriert wird. Alternativ wird er als echter hard core auf dem Chip platziert oder bei der Überführung eines FPGA-designs in einen ASIC praktisch zu einem hard core gemacht.&lt;br /&gt;
&lt;br /&gt;
Im Regelfall werden soft cores in FPGAs dazu verwendet, komplizierte Aufgaben zu erledigen, die eine klassische state machine überfordern oder ineffektiv werden ließen, die aber gleichzeitig nicht zu hohe Anforderungen an die Geschwindigkeit stellen. Oft werden SCs im Nachhinein in FPGAs integriert, wenn sich im Zuge einer Erweiterung des Funktionsumfangs herausstellt, dass die Aufgaben komplexer werden und intelligente Verwaltung gefordert wird.&lt;br /&gt;
&lt;br /&gt;
=== Vorteile von Soft Cores gegenüber hard cores ===&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Flexible Anwendung, das FPGA &#039;&#039;&#039;kann&#039;&#039;&#039; bei Bedarf mit einem SoftCore versehen werden; es wird im Vorhinein jedoch keine Chipfläche für einen eventuell ungenutzen Hard Core verschwendet (Kostenvorteil)&lt;br /&gt;
* Einige Softcore sind sehr flexibel konfigurierbar (16/32 Bit Datenpfade, Zusatzmodule, Spezielle Anwenderbefehle etc.) &lt;br /&gt;
* Kleine Softcores können je nach Anwendung mehrfach im FPGA parallel platziert und verwendet werden, dadurch erhöht sich die Datenverabreitungskapazität (z.&amp;amp;nbsp;B. ein RX Prozessor und ein TX-Prozessor)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Geringere Geschwindigkeit&lt;br /&gt;
* Höhere Wärmeverlustleistung&lt;br /&gt;
&lt;br /&gt;
=== Eigenschaften===&lt;br /&gt;
Ein soft core wird gern für etwas komplexere Aufgaben verwendet, bei denen es nicht auf allerhöchste Geschwindigkeit ankommt, z.&amp;amp;nbsp;B. eine Displayansteuerung oder die Konfiguration von Peripheriebausteinen über I2C.&lt;br /&gt;
&lt;br /&gt;
Die erreichbaren Taktfrequenzen sind stark vom FPGA-Typ und der FPGA-Auslastung (freie Logikressourcen) abhängig. Mit Softcores können daher typischerweise maximal Datendurchsatzraten in der Größenordnung von 10-20Mbps realisiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Typen ===&lt;br /&gt;
Eine große Anzahl frei verfügbarer Soft-CPUs gibt es auf [http://www.opencores.org/browse.cgi/filter/category_microprocessor OpenCores.org]. Die vorhandene Palette reicht von Nachbauten vorhandener Prozessoren, wie des AVR, PIC oder MIPS, bis hin zu eigenen Entwicklungen, wie dem &amp;quot;16 Bit Microcontroller&amp;quot; (c16), der speziell für FPGAs optimiert ist und mit einem eigenen RTOS kommt. Ein anderer ist z.&amp;amp;nbsp;B. der 32-bit OpenRisc 1000, für den eine Version der GNU Tools für die Software Entwicklung zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
Hier soll ein kleiner Überblick über vorhandene Softcores entstehen, für welche Softwarunterstützung existiert und welche Leistungen zu erwarten sind.&lt;br /&gt;
Es nützt einem oft der tollste Softcore nicht, wenn er sich nicht entsprechend programmieren läßt. Für LatticeMico32, MicroBlaze und NIOS ist jeweils ein Systembuilder verfügbar. Damit lassen sich leicht und übersichtlich Prozessor und Peripherie konfigurieren.&lt;br /&gt;
&lt;br /&gt;
== 8 Bit Soft Cores ==&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;softcores&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Name&lt;br /&gt;
!optimiert für&lt;br /&gt;
!Quellcode&lt;br /&gt;
!max. Programmspeicher&lt;br /&gt;
!Programmierung&lt;br /&gt;
!Toolchain&lt;br /&gt;
!Leistung&lt;br /&gt;
!Lizenz/Preis&lt;br /&gt;
!Weblink&lt;br /&gt;
|-&lt;br /&gt;
|AX8&lt;br /&gt;
|entfällt&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|2k (8k)&lt;br /&gt;
|asm, C, Basic, [[Ada]]...&lt;br /&gt;
|GCC/WinAVR, ...&lt;br /&gt;
|wie AT90S2313 mit 30-50MHz&lt;br /&gt;
|&lt;br /&gt;
|[http://www.opencores.org/project,ax8 OpenCores]&lt;br /&gt;
|-&lt;br /&gt;
|T51&lt;br /&gt;
|entfällt&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|64k&lt;br /&gt;
|asm, Basic, C, ...&lt;br /&gt;
|sdcc, MCS-Basic, ...&lt;br /&gt;
|single cycle 8051, z.&amp;amp;nbsp;B. 40 MHz auf Spartan3&lt;br /&gt;
|&lt;br /&gt;
|[http://www.opencores.org/projects.cgi/web/t51/overview OpenCores]&amp;lt;BR&amp;gt;[[T51-Core]]&lt;br /&gt;
|-&lt;br /&gt;
|8051 IP Core&lt;br /&gt;
|entfällt&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|64k&lt;br /&gt;
|asm, Basic, C, ...&lt;br /&gt;
|sdcc, MCS-Basic, ...&lt;br /&gt;
|1-4 cycle 8051&lt;br /&gt;
|open IP-Core&lt;br /&gt;
|[http://www.oregano.at/ip/8051.htm Oregano Systems]&lt;br /&gt;
|-&lt;br /&gt;
|LatticeMico8&lt;br /&gt;
|Lattice&lt;br /&gt;
|ja, Verilog &amp;amp; VHDL&lt;br /&gt;
|&lt;br /&gt;
|asm, C&lt;br /&gt;
|GNU Toolchain&lt;br /&gt;
|&lt;br /&gt;
|open IP-core&lt;br /&gt;
|[http://www.latticesemi.com/products/intellectualproperty/referencedesigns/8bitmicrocontrollermico8.cfm Lattice]&lt;br /&gt;
|-&lt;br /&gt;
|PicoBlaze&lt;br /&gt;
|Xilinx&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|2k (1024 Befehle)&lt;br /&gt;
|asm (DOS/ dosbox)&lt;br /&gt;
|&lt;br /&gt;
|lt. Xilinx 100 MIPS&lt;br /&gt;
|Xilinx Reference Design License&lt;br /&gt;
|[http://www.xilinx.com/picoblaze Xilinx]&lt;br /&gt;
|-&lt;br /&gt;
|PacoBlaze&lt;br /&gt;
|entfällt&lt;br /&gt;
|ja, Verilog&lt;br /&gt;
|wie PicoBlaze&lt;br /&gt;
|asm (geschrieben in Java)&lt;br /&gt;
|KCAsm&lt;br /&gt;
|wie Picoblaze&lt;br /&gt;
|modifizierte BSD Lizenz&lt;br /&gt;
|[http://bleyer.org/pacoblaze/ PacoBlaze]&lt;br /&gt;
|-&lt;br /&gt;
|Panda&lt;br /&gt;
|entfällt&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|64k&lt;br /&gt;
|asm&lt;br /&gt;
|HASM, Builder SW&lt;br /&gt;
|je nach Konfiguration&lt;br /&gt;
|Free IP, Boost&lt;br /&gt;
|[http://www.logicsolutions.ch/Download.htm LogicSolutions]&lt;br /&gt;
|-&lt;br /&gt;
|Proteus&lt;br /&gt;
|entfällt&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|64k&lt;br /&gt;
|asm&lt;br /&gt;
|HASM, Builder SW&lt;br /&gt;
|je nach Konfiguration&lt;br /&gt;
|Free IP, Boost&lt;br /&gt;
|[http://www.logicsolutions.ch/Download.htm LogicSolutions]&lt;br /&gt;
|-&lt;br /&gt;
|embedded Z8 (UB8820)&lt;br /&gt;
|XILINX, ASIC syn.&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|64k&lt;br /&gt;
|asm&lt;br /&gt;
|[http://john.ccac.rwth-aachen.de:8000/as/ Makroassembler AS]&lt;br /&gt;
|je nach Konfiguration&lt;br /&gt;
|Free IP&lt;br /&gt;
|[http://www.dynamic-technologies.de/papa/z8/current.tgz embeddedZ8 site]&lt;br /&gt;
|-&lt;br /&gt;
|bo8&lt;br /&gt;
|&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|n*64k&lt;br /&gt;
|asm&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|Creative Commons&lt;br /&gt;
|[http://www.mikrocontroller.net/articles/8bit-CPU:_bo8 bo8]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Picoblaze ===&lt;br /&gt;
&lt;br /&gt;
Dabei handelt es sich um einen sehr kleinen, aber dennoch sehr leistungsfähigen Microprozessor.&lt;br /&gt;
&lt;br /&gt;
Eigenschaften:&lt;br /&gt;
&lt;br /&gt;
*nur 76-93 Slices Ressourcenbedarf&lt;br /&gt;
*16 8-Bit Register&lt;br /&gt;
*1024 Befehle Programmspeicher (Version für Spartan-II nur 256 Befehle)&lt;br /&gt;
*18-Bit Befehle, RISC (Version für Spartan-II hat 16-Bit Befehle)&lt;br /&gt;
*&#039;&#039;&#039;Alle&#039;&#039;&#039; Befehle dauern zwei Takte&lt;br /&gt;
*Interruptunterstützung&lt;br /&gt;
*8-Bit IO-Bus&lt;br /&gt;
*Indirekte Addressierung möglich&lt;br /&gt;
*Programmspeicher durch Bankumschaltung erweiterbar&lt;br /&gt;
*Assembler als Kommandozeilenprogramm verfügbar&lt;br /&gt;
*Entwicklungsumgebung pBlazeIDE verfuegbar mit integriertem Assembler&lt;br /&gt;
*Schneller Programmdownload über JTAG während der Entwicklung&lt;br /&gt;
*37..102 MIPS, abhängig von der FPGA-Familie&lt;br /&gt;
&lt;br /&gt;
=== AX8 ===&lt;br /&gt;
Der AX8-Core entspricht fast einem Atmel AT90S2313. Ihm fehlen noch Hardwareeinheiten wie EEPROM, Watchdog und die verschiedenen Powerdown-Modi.&lt;br /&gt;
&lt;br /&gt;
* Ressourcenbedarf: ~ 1400 Xilinx-Slices&lt;br /&gt;
* erreichbare Geschwindigkeit auf einem Spartan3-1000: 50 MHz&lt;br /&gt;
* als Toolchain kann problemlos WinAVR o.ä. verwendet werden&lt;br /&gt;
&lt;br /&gt;
Die ROM-Beschreibung muss angepasst werden, damit XST einen BlockRAM generieren kann (ein passendes hex2rom gibts hier: [http://www.mikrocontroller.net/topic/91160 hex2rom]). &lt;br /&gt;
&lt;br /&gt;
Der Core lässt sich leicht mit Speicher (8KByte ROM), einem EEPROM (bis zu 64KByte), zwei Ports und einem Watchdog erweitern. Damit entspricht der AX8 dann einem AT90S8535, bis auf die anderen Timer/Counter und die fehlende SPI-Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
Für Entwicklungszwecke lässt sich der ROM-Inhalt - wie beim Picoblaze - per JTAG-Schnittstelle updaten. Somit kann neue Software getestet werden, ohne das Design neu zu synthetisieren. Dafür benötigt man die Werkzeuge hex2svf, svf2xsvf und xsvfplayer. Im FPGA füllt dann eine State-Machine am JTAG-Port einen DualPort-RAM (ROM :-))&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu neueren AVR-Controllern wie dem ATMega8, fehlen dem AX8-Core Befehle wie MOVW, MUL und FMUL. Außerdem fehlen Hardwareeinheiten, wie I2C und erweiterte Timer. Die größte Beschränkung aber ist der maximale Programmspeicherplatz von 8KByte. Mehr Speicher macht größere Änderungen am VHDL-Code des Cores erforderlich.&lt;br /&gt;
&lt;br /&gt;
=== AVR8 ===&lt;br /&gt;
&lt;br /&gt;
The AVR8 Soft Processor core is an AVR instruction set compatible processor that is optimized to run on the Butterfly Flight or Butterfly Light FPGA hardware. It is based on the &#039;&#039;&#039;ATmega103&#039;&#039;&#039; processor and includes a full toolchain to compile and run C code developed with avr-gcc.&lt;br /&gt;
&lt;br /&gt;
http://gadgetfactory.net/gf/project/avr_core/&lt;br /&gt;
&lt;br /&gt;
=== Panda ===&lt;br /&gt;
Es steht eine grafische Builder SW zur Verfügung um ein System inklusive Peripheriern zu generieren, ohne VHDL schreiben zu müssen. Einige Peripherieren (Timer, Ports, etc.) sind bereits integriert, eigene Peripherien können in die SW integriert werden.&lt;br /&gt;
Der Prozessor ist stark konfigurierbar (z.&amp;amp;nbsp;B. Ein/Ausschalten einzelner Instruktionen).&lt;br /&gt;
&lt;br /&gt;
=== Proteus ===&lt;br /&gt;
Der Prozessor kann durch über 20 Generics perfekt an das Projekt angepasst werden. Es lassen sich beispielsweise Wortbreite, Instruktionen, Pipelining und Register anpassen. Es gibt auch eine JTAG Debugging Software zum Prozessor, die es auch ermöglicht mehrere Prozessoren in einem Chip zu Debuggen. Auch benutzerdefinierte Instruktionen werden unterstützt.&lt;br /&gt;
Proteus hat für Daten und Instruktionen zwei separate Wishbone Interfaces und ist zu 100% Herstellerunabhängig. In seiner kleinsten Konfiguration benötigt Proteus weniger als 300 LUTs.&lt;br /&gt;
&lt;br /&gt;
=== embedded Z8 (UB8820/UB8840) ===&lt;br /&gt;
Frei konfigurierbarer Mikrokontroller Core der sich an der Architekture des UB8820/... orientiert (damit dem z8 entsprechend)&lt;br /&gt;
&lt;br /&gt;
Eigenschaften:&lt;br /&gt;
&lt;br /&gt;
*konfigurierbare Anzahl der Registersets&lt;br /&gt;
&lt;br /&gt;
=== bo8 ===&lt;br /&gt;
Die CPU &#039;&#039;&#039;bo8&#039;&#039;&#039; ist Teil eines auf &#039;&#039;mikrocontroller.net&#039;&#039;&amp;amp;nbsp; vorgestellten Gesamt-Projekts. Sie hat einen vollständigen Befehlssatz mit 256 OpCodes. Die Berechnung der Dauer von Befehlsfolgen durch Abzählen von Zyklen ist sehr einfach. Die CPU kann eine unbestimmte Anzahl von 64KByte-Seiten adressieren. Nachteilig sind die fehlenden Interrupts und die bisher fehlenden Aussagen zur maximalen Taktfrequenz.&lt;br /&gt;
&lt;br /&gt;
== 16 Bit Soft Cores ==&lt;br /&gt;
=== Übersicht ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;softcores&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Name&lt;br /&gt;
!optimiert für&lt;br /&gt;
!Quellcode&lt;br /&gt;
!Programmierung&lt;br /&gt;
!Toolchain&lt;br /&gt;
!Leistung&lt;br /&gt;
!Lizenz/Preis&lt;br /&gt;
!Weblink&lt;br /&gt;
|-&lt;br /&gt;
|NEO430&lt;br /&gt;
|entfällt&lt;br /&gt;
|Ja, VHDL&lt;br /&gt;
|asm, C, C++, ..&lt;br /&gt;
|MSPGCC (Windows, Linux/Cygwin)&lt;br /&gt;
|5-15 cycles per instruction&lt;br /&gt;
|LGPL&lt;br /&gt;
|[http://www.opencores.org/project,neo430 neo430 @ OpenCores.org]&lt;br /&gt;
|-&lt;br /&gt;
|OpenMSP430&lt;br /&gt;
|FPGA &amp;amp; ASIC&lt;br /&gt;
|Ja, Verilog&lt;br /&gt;
|asm, C, C++, ..&lt;br /&gt;
|MSPGCC (Windows, Linux/Cygwin)&lt;br /&gt;
|1-6 cycles per instruction&lt;br /&gt;
|BSD&lt;br /&gt;
|[http://opencores.org/project,openmsp430 OpenMSP430 @ OpenCores.org]&lt;br /&gt;
|-&lt;br /&gt;
|TG68&lt;br /&gt;
|&lt;br /&gt;
|Ja, VHDL&lt;br /&gt;
|asm, C, C++, ..&lt;br /&gt;
|GCC, (68k-compatible)&lt;br /&gt;
|&lt;br /&gt;
|LGPL&lt;br /&gt;
|[http://opencores.org/project,tg68 tg68 @ OpenCores.org]&lt;br /&gt;
|-&lt;br /&gt;
|UCore&lt;br /&gt;
|Altera&lt;br /&gt;
|VHDL&lt;br /&gt;
|Assembler&lt;br /&gt;
|Assembler, Emulator&lt;br /&gt;
|single cycle per instruction&lt;br /&gt;
|&lt;br /&gt;
|Artikel [http://www.mikrocontroller.net/articles/16/32Bit_Computer/Konsole] HP [http://www.goldmomo.de]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== NEO430 ===&lt;br /&gt;
* MSP430-ISA-kompatibel (verwendet MSPGCC Compiler), 16-bit RISC&lt;br /&gt;
* UART/SPI/Wishbone Bootloader&lt;br /&gt;
* Verschiedene Beispielprogramme&lt;br /&gt;
* Hardware (anpassbar): Timer, SPI &amp;amp; UART, Parallel IO, Wishbone Interface, ...&lt;br /&gt;
* Leistung: 5..15 Taktzyklen pro Instruktion, ~145MHz (Altera Cyclone IV EP4CE22F17C6N)&lt;br /&gt;
* Recourcenbedarf: ~1400 LE&#039;s auf Altera Cyclone IV&lt;br /&gt;
* Datenblatt (auf opencores.org): [http://opencores.org/websvn,filedetails?repname=neo430&amp;amp;path=%2Fneo430%2Ftrunk%2Fdoc%2FNEO430.pdf NEO430.pdf]&lt;br /&gt;
&lt;br /&gt;
=== OpenMSP430 ===&lt;br /&gt;
* MSP430 kompatibel (verwendet MSPGCC Compiler), 16-bit RISC&lt;br /&gt;
* FPGA und ASIC erprobt&lt;br /&gt;
* Hardware (anpassbar): 6x16 Hardware Multiplier, Watchdog, GPIO, TimerA, generic templates&lt;br /&gt;
* two-wire Serial Debug Interface mit Unterstützung des MSPGCC GNU Debugger (GDB)&lt;br /&gt;
* Leistung: 1..6 Taktzyklen pro Instruktion&lt;br /&gt;
* Recourcenbedarf: Xilinx: 1650 LUTs / Altera: 1550 LEs / ASIC: 8k gate&lt;br /&gt;
* Datenblatt (auf opencores.org): [http://opencores.org/websvn,filedetails?repname=openmsp430&amp;amp;path=%2Fopenmsp430%2Ftrunk%2Fdoc%2FopenMSP430.pdf openMSP430.pdf]&lt;br /&gt;
&lt;br /&gt;
=== TG68 ===&lt;br /&gt;
* 68000 kompatible&lt;br /&gt;
* Recourcenbedarf: ~2700 Xilinx Slices, ~4000 LC&#039;s auf Altera Cyclone II&lt;br /&gt;
&lt;br /&gt;
== 32 Bit Soft Cores ==&lt;br /&gt;
=== Übersicht ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;softcores&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Name&lt;br /&gt;
!optimiert für&lt;br /&gt;
!Quellcode&lt;br /&gt;
!Programmierung&lt;br /&gt;
!Toolchain&lt;br /&gt;
!Leistung&lt;br /&gt;
!Lizenz/Preis&lt;br /&gt;
!Weblink&lt;br /&gt;
|-&lt;br /&gt;
|HiCoVec Vektorprozessor&lt;br /&gt;
|Xilinx Spartan3A&lt;br /&gt;
|ja, VHDL&lt;br /&gt;
|asm, C&lt;br /&gt;
|GCC, HiCoVec Assembler&lt;br /&gt;
|Flexible Vektoreinheit&lt;br /&gt;
|GPL&lt;br /&gt;
|[http://ees.informatik.hs-augsburg.de/hicovec HS-Augsburg]&lt;br /&gt;
|-&lt;br /&gt;
|LatticeMico32&lt;br /&gt;
|Lattice, siehe aber soc-lm32&lt;br /&gt;
|ja, Verilog&lt;br /&gt;
|asm, C, C++&lt;br /&gt;
|GCC, MSB&lt;br /&gt;
|&lt;br /&gt;
|open IP-core&lt;br /&gt;
|[http://www.latticesemi.com/products/intellectualproperty/ipcores/mico32/ Lattice]&lt;br /&gt;
|-&lt;br /&gt;
|Leon&lt;br /&gt;
|&lt;br /&gt;
|Ja, VHDL&lt;br /&gt;
|asm, C, C++&lt;br /&gt;
|GCC (SPARC-compatible)&lt;br /&gt;
|&lt;br /&gt;
|GPL&lt;br /&gt;
|[http://www.gaisler.com Gaisler Research]&lt;br /&gt;
|-&lt;br /&gt;
|MicroBlaze&lt;br /&gt;
|Xilinx&lt;br /&gt;
|Nein&lt;br /&gt;
|asm, C, C++&lt;br /&gt;
|GCC, EDK&lt;br /&gt;
|&lt;br /&gt;
|EDK $500/Jahr&lt;br /&gt;
|[http://www.xilinx.com/products/design_resources/proc_central/microblaze.htm Xilinx]&amp;lt;BR&amp;gt;[http://de.wikipedia.org/wiki/MicroBlaze MicroBlaze bei Wikipedia]&lt;br /&gt;
|-&lt;br /&gt;
|MicroBlaze MCS&lt;br /&gt;
|Xilinx&lt;br /&gt;
|Nein&lt;br /&gt;
|asm, C, C++&lt;br /&gt;
|GCC, SDK&lt;br /&gt;
|&lt;br /&gt;
|kostenfrei&lt;br /&gt;
|[http://www.xilinx.com/tools/mb_mcs.htm Xilinx]&lt;br /&gt;
|-&lt;br /&gt;
|NIOS II&lt;br /&gt;
|Altera&lt;br /&gt;
|Nein&lt;br /&gt;
|asm, C, C++&lt;br /&gt;
|GCC, SOPC, EDS&lt;br /&gt;
|&lt;br /&gt;
|mit Nios II Eval-Kit ab $400, oder $3000 - Nios II/e core free (12/2009)&lt;br /&gt;
|[http://www.altera.com/nios2 Altera]&lt;br /&gt;
|-&lt;br /&gt;
|OpenRISC&lt;br /&gt;
|&lt;br /&gt;
|Ja, Verilog&lt;br /&gt;
|asm, C, C++, ..&lt;br /&gt;
|GCC&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|[http://www.opencores.org/projects.cgi/web/or1k/overview OpenCores]&lt;br /&gt;
|-&lt;br /&gt;
|Plasma&lt;br /&gt;
|&lt;br /&gt;
|Ja, VHDL&lt;br /&gt;
|asm, C, C++&lt;br /&gt;
|GCC (MIPS-compatible)&lt;br /&gt;
|&lt;br /&gt;
|GPL&lt;br /&gt;
|[http://www.opencores.org/ OpenCores.org]&lt;br /&gt;
|-&lt;br /&gt;
|MAIS&lt;br /&gt;
|&lt;br /&gt;
|Ja, VHDL&lt;br /&gt;
|asm, C, C++&lt;br /&gt;
|GCC &lt;br /&gt;
|&lt;br /&gt;
|Creative Commons CC BY-NC 3.0 with exception commercial applicants have to pay a licence fee &lt;br /&gt;
|[http://www.dossmatik.de/mais-cpu.html]&lt;br /&gt;
|-&lt;br /&gt;
|Propeller 2&lt;br /&gt;
|&lt;br /&gt;
|Ja, VHDL&lt;br /&gt;
|C,&lt;br /&gt;
|Parallax IDE&lt;br /&gt;
|&lt;br /&gt;
|GPL3&lt;br /&gt;
|[http://www.parallax.com]&lt;br /&gt;
|-&lt;br /&gt;
|Zylin CPU (ZPU)&lt;br /&gt;
|&lt;br /&gt;
|Ja, VHDL&lt;br /&gt;
|asm, C, C++&lt;br /&gt;
|GCC&lt;br /&gt;
|&lt;br /&gt;
|FreeBSD/GPL&lt;br /&gt;
|[http://opensource.zylin.com/zpu.htm Zylin.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Einen ersten Eindruck von derLeistungsfähigkeit der 32 Bit SoftCores gibt z.&amp;amp;nbsp;B. &lt;br /&gt;
die [http://www.gaisler.com/doc/Evaluation_of_synthesizable_CPU_cores.pdf Master-Arbeit &amp;quot;Evaluation of synthesizable CPU cores&amp;quot;] aus dem Jahr 2004. &lt;br /&gt;
Diese Vergleicht den Leon 2, MicroBlaze und OpenRISC 1200 miteinander.&lt;br /&gt;
&lt;br /&gt;
=== Lattice Mico32 und soc-lm32 ===&lt;br /&gt;
&lt;br /&gt;
Mico32 ist eine 32 Bit pipelined RISC CPU. Die steht unter einer OpenSource&lt;br /&gt;
Lizenz und liegt komplett im Verilog Quelltext vor. Sowohl die CPU Architektur als auch der Resourcenbedarf ist vergleichbar mit dem MicroBlaze von Xilinx, ist allerdings eine eigenständige Entwicklung von Lattice.&lt;br /&gt;
&lt;br /&gt;
* Recourcenbedarf: ~1600 Slices (Lattice/Xilinx)&lt;br /&gt;
* Erreicht ca. 80-116MHz auf ECP2 und XP2 Devices von Lattice und ca. 100 MHz auf Spartan3 Generation FPGAs,&lt;br /&gt;
* Konfigurierbare D- und I-Caches (aus BRAM oder Distributed RAM)&lt;br /&gt;
* 2 Wishbone-Interfaces: Für Daten-Load&amp;amp;Store und Instruction-Fetch&lt;br /&gt;
* Systembuilder mit automatisch erstellten Wishbone Arbitern&lt;br /&gt;
* Keine Begrenzung in Anzahl der benutzten Wishbone Busse&lt;br /&gt;
* Kompletter GNU Toolchain aus binutils, gcc und gdb&lt;br /&gt;
* Eclipse Entwicklungsumgebung, gemanagte C und C++ Entwicklung&lt;br /&gt;
* Automatische Treibererstellung durch den Systembuilder&lt;br /&gt;
* In Lattice Bausteinen gleichzeitige Benutzung des Logikanalyzers und Debuggers&lt;br /&gt;
&lt;br /&gt;
Neben dem Original, das man von [http://www.latticesemi.com/products/intellectualproperty/ipcores/mico32/index.cfm Lattice] herunterladen kann, und dessen Toolchain erstmal auf Windows mit Lattice FPGAs ausgelegt ist, gibt es noch [http://www.das-labor.org/wiki/Soc-lm32 soc-lm32]. soc-lm32 ist eine Portierung auf Altera und Xilinx Bausteine und benutzt einen Makefile-basierten Workflow.&lt;br /&gt;
&lt;br /&gt;
Eine von Lattice in Auftrag gegebene Portierung von uC-Linux ist verfügbar&lt;br /&gt;
(http://www.theobroma-systems.com/mico32/).&lt;br /&gt;
&lt;br /&gt;
Nachteile: bis jetzt gibt es keine Version mit FPU (kann über Wishbone als Peripherie angeflanscht werden) oder MMU.&lt;br /&gt;
&lt;br /&gt;
=== Leon ===&lt;br /&gt;
&lt;br /&gt;
Dieser Prozessorkern ist komplett SPARC V8 kompatibel -- beliebige SPARC Compiler können verwendet werden (z.&amp;amp;nbsp;B. bcc, ). Neben dem Kern selbst steht auch eine breite Auswahl an Peripherie in Form von VHDL-Komponenten zur Vefügung. Als On-Chip Interconnect kommt AMBA (AHB + APB) zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
* Recourcenbedarf: ~4000 Xilinx Slices, 10 BRAMs (minimal-konfiguration)&lt;br /&gt;
* Erreicht ca. 50 MHz auf Spartan3 Generation FPGAs&lt;br /&gt;
&lt;br /&gt;
=== MicroBlaze &amp;amp; Nachbauten ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MicroBlaze&#039;&#039;&#039; ist ein Core der von der Firma Xilinx speziell für deren FPGAs zur Verfügung gestellt wird. Er ist u.a. mit der XPS-Software instanziier- und konfigurierbar. Die Softwareentwicklung erfolgt in C mittels der Software EDK.&lt;br /&gt;
&lt;br /&gt;
Microblaze ist im Gegensatz zu PicoBlaze kommerziell und erfordert eine Lizenz. Allerdings hat Xilinx beginnend mit der ISE 13.4. einen leichtgewichtigen Microblaze - MicroBlaze MCS - veröffentlicht der kostenfrei für alle Xilinx FPGA&#039;s (mglw außer&lt;br /&gt;
den prähistorischen Relikten wie Spartan XL?!) verwendbar ist:&lt;br /&gt;
&lt;br /&gt;
http://www.xilinx.com/tools/mb_mcs.htm&lt;br /&gt;
&lt;br /&gt;
Dieser wird mit dem CoreGen statt dem EDK generiert, zum programmieren (C/C++) stellt Xilinx ein SDK bereit.&lt;br /&gt;
&lt;br /&gt;
=== Propeller ===&lt;br /&gt;
Komplettes Open Source Paket aus SoftCore, IDE und Debugging des Propeller 2 uCs:&lt;br /&gt;
http://de.wikipedia.org/wiki/Parallax_Propeller&lt;br /&gt;
http://www.parallax.com/microcontrollers/propeller-1-open-source&lt;br /&gt;
&lt;br /&gt;
=== ZPU ===&lt;br /&gt;
http://www.mikrocontroller.net/articles/ZPU:_Softcore_Implementierung_auf_Spartan-3_FPGA&lt;br /&gt;
&lt;br /&gt;
[[Category:FPGA und Co]]&lt;/div&gt;</summary>
		<author><name>134.169.116.105</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Multitasking&amp;diff=85407</id>
		<title>Multitasking</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Multitasking&amp;diff=85407"/>
		<updated>2014-10-27T13:03:05Z</updated>

		<summary type="html">&lt;p&gt;134.169.116.105: /* System ohne real-time OS und Entwicklung in ASM */ Typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Multitasking bedeutet ein quasi paralleles Ausführen von mehreren Prozessen auf einem Prozessor.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Da eine echte parallele Ausführung von mehreren Prozessen (Programmen, Funktionen) auf einem einzelnen CPU-Kern nicht möglich ist, wird ein &amp;quot;Trick&amp;quot; verwendet. Dabei werden die einzelnen Prozesse jeweils nur für kurze Zeit (1..50 ms) bearbeitet und danach auf einen anderen Prozess umgeschaltet. Man spricht auch von einer verschachtelten Bearbeitung (engl. interleaving).&lt;br /&gt;
&lt;br /&gt;
Das Herz jedes Multitasking-Systems ist der Scheduler. Dieses Programm beinhaltet einen Algorithmus, der überprüft, welcher Prozess als nächstes die CPU (also Rechenzeit) zugeteilt bekommt. Es gibt verschiedene Schedulingalgorithmen:&lt;br /&gt;
&lt;br /&gt;
* First come first served: Teilt den Prozessen in der Reihenfolge Rechenzeit zu,  in der sie rechenbereit werden&lt;br /&gt;
* Shortest Job first: Der Prozess mit der kürzesten Rechenzeit wird als erstes bearbeitet. Dazu muss die Rechenzeit natürlich im Voraus bekannt sein&lt;br /&gt;
* Shortest remaining time next: Der Prozess mit der kürzesten verbleibenden Rechenzeit wird jeweils als nächstes bearbeitet. Auch hier muss diese Zeit natürlich bekannt sein&lt;br /&gt;
* Round Robin: Alle Prozesse bekommen eine gleich große Zeitscheibe zugeteilt. Der Scheduler lässt jeden Prozess für die Dauer einer Zeitscheibe rechnen, und übergibt die CPU dann an den nächsten Prozess&lt;br /&gt;
* Priority Scheduling: Anders als beim Round Robin Verfahren sind die Prozesse hier nicht gleichwertig. Prozesse haben Prioritäten, der Scheduler sorgt dafür, dass höher priorisierte Prozesse bevorzugt behandelt werden&lt;br /&gt;
&lt;br /&gt;
Natürlich sind Scheduler in freier Wildbahn nicht immer so einfach zu charakterisieren, da sie oftmals komplizierte Hybriden der genannten Techniken implementieren. Die Scheduler der &amp;quot;echten&amp;quot; Betriebsysteme (Windows, Linux, MacOS, *BSD) sind im Prinzip prioritäten-basierende Round Robin Scheduler. Generell hat ein Betriebsystem 2 Möglichkeiten, Multitasking zu realisieren, kooperativ oder präemptiv.&lt;br /&gt;
&lt;br /&gt;
== Kooperatives Multitasking ==&lt;br /&gt;
&lt;br /&gt;
Beim kooperativen Multitasking gibt der Scheduler die Kontrolle komplett an den Prozess ab. D.h., das Betriebsystem ist darauf angewiesen, dass der Prozess die Kontrolle wieder abgibt. Geschieht das nicht, wird der Scheduler nicht wieder aufgerufen und damit auch kein anderer Prozess mehr ausgeführt - das System &amp;quot;hängt&amp;quot;. Das OS ist also auf die [http://de.wikipedia.org/wiki/Kooperation Kooperation] der Prozesse angewiesen. Bekannte Beispiele für Betriebssysteme mit kooperativem Multitasking sind Windows 3.x und MacOS vor Version 10.&lt;br /&gt;
&lt;br /&gt;
Dennoch ist kooperatives Multitasking keineswegs überholt oder schlecht. Gerade im Bereich der Mikrocontroller und Echtzeitanwendungen gibt es viele Argumente, die für ein kooperatives Multitasking sprechen: Kooperatives Multitasking ist deterministischer (zeitlich und logisch vorhersagbar). Es ist besser simulierbar, d.h. für ein gegebenes System ist leichter nachweisbar, dass es funktioniert. Da es sich um geschlossene Systeme handelt, tritt das Problem, dass &amp;quot;irgendein&amp;quot; Prozess das System anhält, nicht auf. Es laufen ja im Gegensatz zum PC nicht &amp;quot;irgendwelche&amp;quot; Prozesse, sondern nur die, deren Korrektheit (hoffentlich) verifiziert &amp;amp; validiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Ein einfaches Beispiel für den AVR ===&lt;br /&gt;
&lt;br /&gt;
Hier soll ein einfaches Beispiel den Weg in die Programmierung von parallel bearbeiteten Aufgaben zeigen.&lt;br /&gt;
&lt;br /&gt;
Wichtigster Grundsatz ist die Herangehensweise! Viele Programmieranfänger haben damit Schwierigkeiten, was u.a. an den schlecht vermittelten Grundlagen liegt. Oft sieht man Funktionen zum Warten in Form von&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
while(1) {&lt;br /&gt;
    PORTD ^= (1&amp;lt;&amp;lt;PD0);&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um beispielsweise eine [[LED]] blinken zu lassen. Will man dann noch andere Dinge erledigen, wundert sich der Programmierer, warum der Mikrocontroller so langsam reagiert, trotz 16 MHz Taktfrequenz.&lt;br /&gt;
&lt;br /&gt;
==== Einfacher Ansatz ====&lt;br /&gt;
&lt;br /&gt;
Stellen wir uns vor, wir wollen drei Dinge gleichzeitig tun.&lt;br /&gt;
&lt;br /&gt;
* Eine Taste abfragen&lt;br /&gt;
* Eine LED blinken lassen, in Abhängigkeit der gedrückten Taste&lt;br /&gt;
* Daten vom UART empfangen und zum PC zurücksenden&lt;br /&gt;
&lt;br /&gt;
Ein einfacher Ansatz für die drei Dinge sieht etwa so aus. Die Beispiele wurden mit [[WinAVR]] Version 20081006 in der Optimierungsstufe -Os kompiliert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, erster Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6864 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
&lt;br /&gt;
    PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    if (taste) &lt;br /&gt;
        _delay_ms(1000);    // 1 s warten&lt;br /&gt;
    else&lt;br /&gt;
        _delay_ms(100);     // 0,1 s warten&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)));            // Warte auf empfangenes Zeichen vom UART&lt;br /&gt;
    tmp = UDR;&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));           // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
    UDR = tmp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 1;              // PC0 ist Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    &lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        taste = taste_lesen();&lt;br /&gt;
        led_blinken(taste);&lt;br /&gt;
        uart_lesen();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man das Programm nun laufen lässt, wird man feststellen dass&lt;br /&gt;
&lt;br /&gt;
* das Hyperterminal sehr langsam reagiert und bisweilen Zeichen verschluckt&lt;br /&gt;
* die LED auf Tastendrücke nur dann reagiert, wenn man per Hyperterminal Zeichen eingibt&lt;br /&gt;
&lt;br /&gt;
Dieser Ansatz ist also untauglich. Egal wie schnell unser AVR auch ist, er reagiert sehr langsam.&lt;br /&gt;
&lt;br /&gt;
==== Verbesserter Ansatz ====&lt;br /&gt;
&lt;br /&gt;
Will man mehrere Dinge gleichzeitig bearbeiten, muss man die Aufgaben in kleinste Häppchen zerteilen. Diese kleinsten Häppchen werden dann verschachtelt abgearbeitet, also ein Häppchen von Aufgabe A, ein Häppchen von Aufgabe B, ein Häppchen von Aufgabe C.&lt;br /&gt;
&lt;br /&gt;
Das Auslesen der Taste geht immer sehr schnell, kein Ansatz zum optimieren. Das Blinken der LED dauer entweder 1s oder 100ms, eine Ewigkeit für einen Mikrocontroller! Hier muss man was ändern. Am schlimmsten ist die UART-Nutzung. Der AVR wartet solange, bis ein Zeichen empfangen wurde! Das kann ewig dauern! Unser Programm steht! Das darf nicht sein!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, zweiter Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6468 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
    static uint16_t zaehler=0;&lt;br /&gt;
&lt;br /&gt;
    if (taste) {&lt;br /&gt;
        if (zaehler&amp;gt;=999) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        if (zaehler&amp;gt;=99) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    zaehler++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    if((UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC))) {                // empfangenes Zeichen abholbereit im UART ?&lt;br /&gt;
        tmp = UDR;&lt;br /&gt;
        while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));       // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
        UDR = tmp;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 1;              // PB0 ist Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    &lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        taste = taste_lesen();&lt;br /&gt;
        led_blinken(taste);&lt;br /&gt;
        uart_lesen();&lt;br /&gt;
        _delay_ms(1);       // 1 ms warten&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses Programm reagiert &#039;&#039;&#039;ganz&#039;&#039;&#039; anders! Schnell wie der Wind und vollkommen unabhängig von anderen, parallel laufenden Prozessen. Warum ist das so ?&lt;br /&gt;
&lt;br /&gt;
Die einzelnen kleinen Häppchen sind verdaulicher als die grossen. Die maximale Durchlaufzeit der einzelnen Funktionen ist drastisch reduziert. Anstatt in der LED-Ausgabe einmal 1000 ms zu warten wird nun 1000x1ms gewartet. Zwischendurch werden aber 1000 mal die anderen Prozesse bearbeitet. Echte Demokratie sozusagen. Noch viel besser ist die Handhabung des UARTs. Anstatt eine Ewigkeit auf ein ankommendes Zeichen zu warten, wird nur dann etwas bearbeitet, wenn auch wirklich etwas zur Bearbeitung vorliegt. Klingt eigentlich logisch. Also nur dann, wenn schon ein Zeichen empfangen wurde wird es auch bearbeitet, ansonsten geht es zurück zur Hauptschleife. Das ist eigentlich der ganze &amp;quot;Trick&amp;quot; eines kooperativen Multitaskings. Auch wenn die Verwendung von _delay_ms(1) noch ein kleiner Schönheitsfehler ist, den die Profis lieber mit einem [[Timer]] erledigen, so wird das Prinzip klar.&lt;br /&gt;
&lt;br /&gt;
*Prozesse eines kooperativen Multitaskingsystems warten nicht auf das Eintreten von Ereignissen, sondern bearbeiten nur bereits eingetretene Ereignisse.&lt;br /&gt;
*Grössere Aufgaben werden in kleine Teilaufgaben zerlegt, welche nur durch mehrfaches Aufrufen der Funktion abgearbeitet werden. Das erreicht man meist am besten mit einer [[statemachine |State machine]].&lt;br /&gt;
*Prozesse eines kooperativen Multitaskings haben eine garantierte, maximale Durchlaufzeit, welche möglichst klein ist.&lt;br /&gt;
&lt;br /&gt;
Damit ähneln die Prozesse einem [[Interrupt]], auch wenn sie als ganz normale Funktionen ausserhalb eines Interrupts ausgeführt werden. An diesem Beispiel erkennt man die Vor- und Nachteile des kooperativen Multitaskings&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* einfacher Scheduler mit geringster CPU Belastung&lt;br /&gt;
* Deterministische Arbeitsweise, damit einfach prüfbar und strenges Timing möglich&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* eine andere Programmierweise zur Zerlegung größerer Aufgaben in kleine Teilaufgaben muss manuell vorgenommen werden&lt;br /&gt;
&lt;br /&gt;
==== Verbesserter Ansatz mit Timer ====&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss noch einmal die verbesserte Version mit Timer. Diese hat mehrere Vorteile.&lt;br /&gt;
&lt;br /&gt;
* Das Zeitraster der Hauptschleife ist exakt, unabhängig von der Laufzeit der Aufgaben, weil der Timer unabhängig eine feste Interruptfrequenz generiert. Im vorherigen Beispiel war das Zeitraster die Summe aus Laufzeit aller Funktionen/Tasks und dem _delay_ms(1).&lt;br /&gt;
* CPU-Rechenleistung wird zu 100% in der Abarbeitung der Task verwendet und nicht für nutzlose Warteschleifen verschwendet.&lt;br /&gt;
* Es kann leicht im realen System geprüft werden, ob die Laufzeit der Tasks klein genug ist, um den Anforderungen des Timers zu genügen.&lt;br /&gt;
&lt;br /&gt;
Diese Überprüfung kann an zwei Stellen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
* Am Ende der Hauptschleife nach Abarbeitung aller Ausgaben. Wenn hier die Variable flag_1ms schon wieder aktiv ist, dauerte die Abarbeitung länger als 1ms. Wenn man ein sehr strenges Timing sicherstellen möchte, ist das ein Fehler, der erkannt und signalisiert werden kann.&lt;br /&gt;
* In der ISR. Wenn hier die Variable immer noch aktiv ist, wurde sie von der Hauptschleife noch nicht erkannt und zurück gesetzt. Das ist definitiv ein Fehler, denn jetzt würde ohne Fehlererkennung ein Timerdurchlauf von der Hauptschleife verschluckt werden. Diese Prüfung ist etwas nachgiebiger, weil zwischenzeitlich ein Durchlauf der Hauptschleife mehr als 1ms, jedoch nicht länger als 2ms dauern darf. Siehe auch den Abschnitt [[Interrupt#Zeitverhalten_eines_Timerinterrupts]]. Sinnvollerweise nutzt man nur eine der beiden Prüfungen, nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, dritter Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6468 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0 und PB1&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
#include &amp;quot;avr/interrupt.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
    static uint16_t zaehler=0;&lt;br /&gt;
&lt;br /&gt;
    if (taste) {&lt;br /&gt;
        if (zaehler&amp;gt;=999) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        if (zaehler&amp;gt;=99) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    zaehler++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    if((UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC))) {                // empfangenes Zeichen abholbereit im UART ?&lt;br /&gt;
        tmp = UDR;&lt;br /&gt;
        while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));       // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
        UDR = tmp;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t flag_1ms;&lt;br /&gt;
 &lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 3;              // PB0/1 sind Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
    // Timer 0 initialisieren, CTC, Presacler 64&lt;br /&gt;
&lt;br /&gt;
    TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01) | (1&amp;lt;&amp;lt;CS00);&lt;br /&gt;
    OCR0  = 56;      // 1ms&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;OCIE0);&lt;br /&gt;
&lt;br /&gt;
    // Interrupts global freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (flag_1ms) {&lt;br /&gt;
            flag_1ms=0;&lt;br /&gt;
            taste = taste_lesen();&lt;br /&gt;
            led_blinken(taste);&lt;br /&gt;
            uart_lesen();&lt;br /&gt;
            if (flag_1ms) {&lt;br /&gt;
                // Laufzeit der Tasks &amp;gt;1ms, Fehlersignalisierung&lt;br /&gt;
                // PB1 auf HIGH, Programm stoppen&lt;br /&gt;
                PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
                while(1);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Interruptserviceroutine für Timer 0&lt;br /&gt;
// hier 1ms&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_COMP_vect) {&lt;br /&gt;
    if (flag_1ms) {&lt;br /&gt;
        // Laufzeit der Tasks &amp;gt;2ms, Fehlersignalisierung&lt;br /&gt;
        // PB1 auf HIGH, Programm stoppen&lt;br /&gt;
        PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
        while(1);&lt;br /&gt;
    }&lt;br /&gt;
    flag_1ms = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Message passing Framework ====&lt;br /&gt;
&lt;br /&gt;
Im vorangegangenen Abschnitt wird erklärt, wie man die einzelnen &amp;quot;Tasks&amp;quot; in kleine Häppchen Zerlegen kann und diese alle innerhalb der Main Loop aufruft.&lt;br /&gt;
Dieses kooperative System hat aber noch einige Nachteile:&lt;br /&gt;
&lt;br /&gt;
* Alle Häppchen werden gleich oft aufgerufen und nicht nur bei Bedarf&lt;br /&gt;
* Für die Timeouts gibt es noch keine befriedigende Lösung&lt;br /&gt;
&lt;br /&gt;
Wenn man diese beiden Nachteile auch noch lösen möchte, wird das ganze noch ein klein wenig komplizierter. Da man das Grundprinzip aber für viele Mikrocontroller Projekte immer wieder verwenden kann, lohnt es sich und man kann die Entwicklung für diese Art des Multitasking in einem Framework zusammenfassen.&lt;br /&gt;
&lt;br /&gt;
Ein Framework, das sind einige Dateien, die den Rahmen (Frame=Rahmen) für ein Programm bilden und den Teil enthalten, den man immer wieder braucht.&lt;br /&gt;
&lt;br /&gt;
===== Message =====&lt;br /&gt;
&lt;br /&gt;
Die Basis des Frameworks bildet die &amp;quot;Message&amp;quot;. Immer wenn wir für einen unserer &amp;quot;Tasks&amp;quot; etwas zu tun haben, schicken wir eine &amp;quot;Message&amp;quot;. Die &amp;quot;Messages&amp;quot; werden in eine Warteschlange einsortiert und der Reihe nach abgearbeitet. Dadurch kommt ein Task der viel zu tun hat (viele Messages bekommt) öfter dran, als ein &amp;quot;Task&amp;quot; der nicht so viel zu tun hat. Außerdem kann eine Message noch Daten enthalten (z.B. ein empfangenes Zeichen). So können die einzelnen Tasks sogar Daten austauschen.&lt;br /&gt;
&lt;br /&gt;
===== Message Receiver =====&lt;br /&gt;
&lt;br /&gt;
Unsere &amp;quot;Tasks&amp;quot; werden immer dann aufgerufen, wenn Arbeit für sie da ist. Das wissen wir, weil sie eine Message empfangen sollen. Deshalb heißen die &amp;quot;Tasks&amp;quot; ab jetzt &amp;quot;Message Receiver&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===== Timeout =====&lt;br /&gt;
&lt;br /&gt;
In diesem System wird jeder Message Receiver aufgerufen, wenn jemand Arbeit für ihn hat und er deshalb eine Message bekommt. Was aber, wenn keine Message kommt, oder ein Message Receiver selbst aktiv werden soll?&lt;br /&gt;
&lt;br /&gt;
Aus diesem Grund braucht es die Timeouts. Mit Hilfe &amp;lt;u&amp;gt;eines&amp;lt;/u&amp;gt; Hardware Timers wird eine &amp;quot;Systemzeit&amp;quot; programmiert. Jeder Message Receiver kann die Zeit angeben, wann er wieder aufgerufen werden muss. Das Framework verwaltet alle Timer und sendet den Message Receivern eine &amp;quot;Timeout&amp;quot; message, wenn ihre Zeit gekommen ist.&lt;br /&gt;
&lt;br /&gt;
===== Beispiel =====&lt;br /&gt;
Hier der Sourcecode eines solchen Framework [[Datei:ACF.zip]]&lt;br /&gt;
Das Framework implementiert die Message Warteschlange und die Timer Warteschlange. Die Prozessor spezifischen Dinge sind in der Datei &amp;quot;ACF_Hal.c&amp;quot; zusammengefasst und für Linux Desktop und atMega128 implementiert.&lt;br /&gt;
In dieser Datei kann man das ganze auch auf andere Prozessoren anpassen.&lt;br /&gt;
&lt;br /&gt;
Ein &amp;quot;main&amp;quot; sieht dann z.B. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ACF.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv)&lt;br /&gt;
{&lt;br /&gt;
    ACF_init();&lt;br /&gt;
    ACF_loop();&lt;br /&gt;
    return 0; // we will never arrive here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tracing =====&lt;br /&gt;
Es gibt noch einen weiteren Grund, sich ein &amp;quot;Framework&amp;quot; zu erarbeiten, oder ein fertiges Framework zu verwenden. Da der Ablauf der Software und der Aufruf aller Teile vom Framework bestimmt wird, kann das Framework auch einen sehr detaillierten Trace über das Verhalten des Codes anfertigen. Das Framework aus vorstehendem Beispiel enthält bereits entsprechenden Code.&lt;br /&gt;
&lt;br /&gt;
Solche Traces von laufendem Code können gerade dann sehr hilfreich sein, wenn viele Dinge gleichzeitig ablaufen (und das war schließlich der Sinn des ganzen).&lt;br /&gt;
&lt;br /&gt;
Nachstehendes Bild zeigt den Trace eines Reglers, der mit dem Framework realisiert wurde.&lt;br /&gt;
[http://www.mikrocontroller.net/attachment/74409/ablauf.png Sequenzdiagramm]&lt;br /&gt;
&lt;br /&gt;
== Präemptives Multitasking ==&lt;br /&gt;
&lt;br /&gt;
Beim präemptiven Multitasking gibt das OS die Kontrolle zu keinem Zeitpunkt auf. Ein Prozess, der gerade die CPU nutzt, kann jederzeit wieder vom Betriebssystem unterbrochen werden. Daher muss bei der Entwicklung für ein präemptives System immer damit gerechnet werden, dass ein Prozess &#039;&#039;&#039;jederzeit&#039;&#039;&#039; unterbrochen werden kann. Das kann z.&amp;amp;nbsp;B. zu Problemen beim Zugriff auf limitierte Betriebsmittel führen. Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Prozess A sucht freien Speicher und findet einen freien Block&lt;br /&gt;
* Prozess B wird vom Scheduler gestartet und sucht ebenfalls einen Speicherblock. Der gefundene Block wird von Prozess B reserviert und benutzt&lt;br /&gt;
* Der Scheduler teilt wieder Prozess A die CPU zu. Prozess A wird fortgeführt, d.h. er reserviert jetzt den im letzten Systemcall gefundenen Speicherblock&lt;br /&gt;
Jetzt haben also beide Prozesse den gleichen Speicherblock reserviert. Entweder arbeiten jetzt beide Prozesse mit dem gleichen Speicher, und überschreiben daher gegenseitig die Daten, oder das Betriebsystem hat etwas gemerkt und zieht die Notbremse. In jedem Fall passieren schreckliche Dinge. Sowas nennt man eine Race-Condition.&lt;br /&gt;
&lt;br /&gt;
Die Lösung nennt sich Semaphore: Dieser Mechanismus wird vom Betriebsystem bereitgestellt und erlaubt es einem Prozess eine bestimmte Ressource zu sperren. Wenn also Prozess A aus obigem Beispiel Speicher haben möchte, setzt er vor Beginn der sogenannten &amp;quot;Kritischen Sektion&amp;quot; einen Semaphor für &amp;quot;Speicher reservieren&amp;quot;. Dieser Semaphor wird erst wieder aufgehoben, sobald Prozess A den Speicher für sich reserviert hat. Wenn der Prozess B zwischendurch gestartet wird und ebenfalls versucht den Semaphor zu setzen, wird er solange warten müssen, bis Prozess A den Semaphor wieder freigibt. Speziell für derartige Locking Mechanismen bieten die meisten Prozessoren sogenannte TAS-Befehle (Test And Set), die in einem Prozessorbefehl eine Variable testen und je nach Ergebnis setzen können. Das ist nötig um das Setzen von Semaphoren unteilbar (atomar) zu machen. Könnte der Scheduler das Setzen eines Semaphors unterbrechen, wäre ja der ganze Aufwand umsonst.&lt;br /&gt;
&lt;br /&gt;
Präemptive Multitasking Systeme sind sehr flexibel und kommen mit einer Vielzahl an Tasks klar. Amok laufende Prozesse können das System bei korrekter Implementierung nicht blockieren. Damit aber das System crash-sicher ist, muss es Systemresourcen geben, die nur der Scheduler verteilen kann (z.&amp;amp;nbsp;B. kein anderer Prozess darf in den Speicherbereich des Schedulers schreiben; kein anderer Prozess darf den Timerinterrupt des Schedulers ändern). Diese Möglichkeiten sind in Mikrocontrollern normalerweise gar nicht vorhanden, wodurch dieser Vorteil des Präemptiven MT weniger ins Gewicht fällt. Beispiele für Systeme mit präemptivem Multitasking sind Linux, *BSD  und Windows XP.&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* sehr flexibel in der Verwaltung von dynamisch ausgeführten Prozessen&lt;br /&gt;
* einzelne Prozesse können einfach linear programmiert werden, ohne die Aufgabe in kleine Teile zerlegen zu müssen&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Der Scheduler ist aufwändiger und benötigt mehr CPU-Zeit&lt;br /&gt;
* Höherer Resourcenbedarf zu Verwaltung des Systems und Bereitstellung der Semaphore etc.&lt;br /&gt;
* nicht streng deterministisch, somit kann kein festes Timing garantiert werden&lt;br /&gt;
* nicht explizit debug- und prüfbar, da die Prozesse nicht fest gekoppelt sind&lt;br /&gt;
&lt;br /&gt;
== Multithreading ==&lt;br /&gt;
&lt;br /&gt;
Multithreading ist eine meist softwarebasierende Möglichkeit moderner Betriebssysteme, innerhalb eines Prozesses mehrere Tasks (threads) parallel auszuführen. Der Vorteil dieser weiteren Unterteilung ist, dass sich die Threads eines Tasks den Speicherbereich teilen können und eine Aufteilung in logische nebeneinander laufende Teile möglich ist. Je nach Betriebssystem kann der Übergang von Multithreading zu Multiprocessing fliessend bis starr sein. &lt;br /&gt;
&lt;br /&gt;
Das Hyperthreading eines Intel Pentium 4 folgt dem Konzept des Multithreadings auf Hardwarebasis und teilt den CPU-Kern zeitlich in zwei logische Prozessoren ein.&lt;br /&gt;
&lt;br /&gt;
== Umsetzung auf Prozessoren ==&lt;br /&gt;
Unabhängig davon, ob Multitasking oder -threading auf einem Prozessor konkret unterstützt wird, lässt es sich immer in Form von Software realisieren. Dies wird in modernen Systemen durch das OS geleistet, das standardisierte Funktionen und Strukturen zur Verfügung stellt. Besonders C++ bietet ein stark abstrahiertes Programmiermodell und Methoden-Set an, um effektiv untereinander kompatible Programmmodule erstellen zu können. Nutzt man diese nicht, wie z.B. bei der Programmierung in C, müssen Strukturen manuell erzeugt und gehandhabt werden, was aufwändiger ist, aber auch geringeren overhead bewirkt. Das Programm ist dann fast immer erheblich kleiner, in den meisten Fällen strukturell einfacher, bezüglich komplizierter Änderungen jedoch auch unflexibler und träger. &lt;br /&gt;
&lt;br /&gt;
Bei Mikrocontrollern findet man je nach Komplexität und Struktur der Appliation praktisch alle denkbaren Kombinationen:&lt;br /&gt;
&lt;br /&gt;
=== System mit real-time OS und Entwicklung in C++ ===&lt;br /&gt;
* Programmentwicklung stark an abstrakte Interfaces und Standards gebunden&lt;br /&gt;
* Starke Abängigkeit an das OS im Bezug auf RT-Funktionalität&lt;br /&gt;
* Innerhalb gleicher OS-Landschaft gut portierbar&lt;br /&gt;
* Sehr geringe Abhängigkeit vom Prozessortyp&lt;br /&gt;
* Multitasking muss über OS-Schicht ausprogrammiert werden&lt;br /&gt;
* Multithreading muss/kann durch Programmierleistung optimiert werden&lt;br /&gt;
* Laufzeiteffizienz ist niedrig durch relativ hohen Anteil des OS-Bedarfs&lt;br /&gt;
* Datendurchsatz regelt sich selbst und ist gleichmässig am relativen Maximum&lt;br /&gt;
* Programmiereffizienz ist niedrig durch sehr viel Arbeit an Formalismen&lt;br /&gt;
* Planungsaufwand ist noch überschaubar&lt;br /&gt;
* Planungseffizienz ist relaiv hoch durch viele Standards&lt;br /&gt;
* Erweiterung um komplexe Module einfach möglich, Timing regelt sich selber&lt;br /&gt;
&lt;br /&gt;
=== System mit real-time OS und Entwicklung in C ===&lt;br /&gt;
* Programmentwicklung stark an Interfaces und weniger an Standards&lt;br /&gt;
* Starke Abängigkeit an das OS im Bezug auf RT-Funktionalität&lt;br /&gt;
* Innerhalb aber auch ausserhalb gleicher OS-Landschaft gut portierbar&lt;br /&gt;
* Geringe Abhängig von dem Prozessortyp&lt;br /&gt;
* Multitasking muss über OS-Schicht und eigene Strukturen programmiert werden&lt;br /&gt;
* Multithreading muss/kann durch Programmierleistung optimiert werden&lt;br /&gt;
* Laufzeiteffizienz ist höher durch geringeren Anteil des OS-Bedarfs&lt;br /&gt;
* Datendurchsatz regelt sich selbst und ist gleichmässig am relativen Maximum&lt;br /&gt;
* Programmiereffizienz ist höher durch weniger Arbeit mit Formalien&lt;br /&gt;
* Planungsaufwand ist etwas höher, je nach Applikation&lt;br /&gt;
* Planungseffizienz ist relaiv niedriger durch weniger Standards&lt;br /&gt;
* Erweiterung mit akzeptablem Aufwand möglich, Timing muss beachtet werden&lt;br /&gt;
&lt;br /&gt;
=== System ohne real-time OS und Entwicklung in C ===&lt;br /&gt;
* Programmentwicklung stark an Interfaces und kaum an Standards gebunden&lt;br /&gt;
* Ausprägung und Gestaltung der RT-Funktionalität absolut frei&lt;br /&gt;
* nur ausserhalb von OS-Landschaft portierbar, dafür prinziepiell sehr gut&lt;br /&gt;
* Stärkere Abhängig von dem Prozessortyp, kann die Portierbarkeit einschränken&lt;br /&gt;
* Multitasking muss auschlieslich durch eigene Strukturen programmiert werden&lt;br /&gt;
* Multithreading muss durch umständliche Programmierung ermöglicht werden werden&lt;br /&gt;
* Laufzeiteffizienz kann sehr hoch sein, ist aber stark von der Progr. abhängig&lt;br /&gt;
* Datendurchsatz muss selbst ins Maximum gesteuert werden, das aber höher liegt&lt;br /&gt;
* Programmiereffizienz ist hoch, dank Wegfall von Konventionen&lt;br /&gt;
* Planungsaufwand ist stark Applikations abhängig, gfs sehr viel grösser&lt;br /&gt;
* Planungseffizienz ist gering, da RT Konzept selber optmiert werden muss&lt;br /&gt;
* Erweiterung nur möglich, wenn Timing weitestgehend überarbeitet wird&lt;br /&gt;
&lt;br /&gt;
=== System ohne real-time OS und Entwicklung in ASM ===&lt;br /&gt;
* Programmentw. nur an physische Interfaces gebunden und frei von Standards&lt;br /&gt;
* Ausprägung und Gestaltung der RT-Funktionalität absolut frei&lt;br /&gt;
* nur innerhalb der Prozessorlandschaft gut portierbar&lt;br /&gt;
* Volle Abhängig vom Prozessortyp, Portierbarkeit auf andere aufwändig&lt;br /&gt;
* Multitasking muss auschlieslich durch eigene Strukturen programmiert werden&lt;br /&gt;
* Multithreading muss durch umständliche Programmierung ermöglicht werden werden&lt;br /&gt;
* Laufzeiteffizienz sehr hoch, jedoch stark von Programmierung abhängig&lt;br /&gt;
* Datendurchsatz muss selbst ins Maximum gesteuert werden, das aber höher liegt&lt;br /&gt;
* Programmiereffizienz ist hoch, dank Wegfall von Konventionen&lt;br /&gt;
* Planungsaufwand ist stark applikationsabhängig, ggfs. sehr viel grösser&lt;br /&gt;
* Planungseffizienz sehr gering, RT Konzept nur für einfache System machbar&lt;br /&gt;
* Erweiterung nur möglich, wenn Timing komplett überarbeitet wird&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Pr%E4emptives_Multitasking Präemptives Multitasking] bei [http://de.wikipedia.org Wikipedia]&lt;br /&gt;
* [http://www.femtoos.org/ Femto OS], ein ultrakompaktes Mulitaskingbetriebssystem für kleine Mikrocontroller&lt;br /&gt;
*[http://www.freertos.org/ FreeRTOS], ein freies Echtzeitbetriebssystem für Mikrocontroller&lt;br /&gt;
* [http://w3.ualg.pt/~rmarcel/Get%20by%20Without%20an%20RTOS.pdf Get by Without an RTOS] Ein schönes Beispiel wie man ohne ein RTOS auch Multitasking hinbekommt. &lt;br /&gt;
* [[TNKernel]], freier Multitasking-Kernel. &lt;br /&gt;
* [http://embeddedgurus.com/state-space/2010/04/i-hate-rtoses/ i-hate-rtoses] Blog zum Thema RTOS&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Betriebssysteme]]&lt;/div&gt;</summary>
		<author><name>134.169.116.105</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Multitasking&amp;diff=85406</id>
		<title>Multitasking</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Multitasking&amp;diff=85406"/>
		<updated>2014-10-27T13:02:43Z</updated>

		<summary type="html">&lt;p&gt;134.169.116.105: /* System ohne real-time OS und Entwicklung in C */ Typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Multitasking bedeutet ein quasi paralleles Ausführen von mehreren Prozessen auf einem Prozessor.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Da eine echte parallele Ausführung von mehreren Prozessen (Programmen, Funktionen) auf einem einzelnen CPU-Kern nicht möglich ist, wird ein &amp;quot;Trick&amp;quot; verwendet. Dabei werden die einzelnen Prozesse jeweils nur für kurze Zeit (1..50 ms) bearbeitet und danach auf einen anderen Prozess umgeschaltet. Man spricht auch von einer verschachtelten Bearbeitung (engl. interleaving).&lt;br /&gt;
&lt;br /&gt;
Das Herz jedes Multitasking-Systems ist der Scheduler. Dieses Programm beinhaltet einen Algorithmus, der überprüft, welcher Prozess als nächstes die CPU (also Rechenzeit) zugeteilt bekommt. Es gibt verschiedene Schedulingalgorithmen:&lt;br /&gt;
&lt;br /&gt;
* First come first served: Teilt den Prozessen in der Reihenfolge Rechenzeit zu,  in der sie rechenbereit werden&lt;br /&gt;
* Shortest Job first: Der Prozess mit der kürzesten Rechenzeit wird als erstes bearbeitet. Dazu muss die Rechenzeit natürlich im Voraus bekannt sein&lt;br /&gt;
* Shortest remaining time next: Der Prozess mit der kürzesten verbleibenden Rechenzeit wird jeweils als nächstes bearbeitet. Auch hier muss diese Zeit natürlich bekannt sein&lt;br /&gt;
* Round Robin: Alle Prozesse bekommen eine gleich große Zeitscheibe zugeteilt. Der Scheduler lässt jeden Prozess für die Dauer einer Zeitscheibe rechnen, und übergibt die CPU dann an den nächsten Prozess&lt;br /&gt;
* Priority Scheduling: Anders als beim Round Robin Verfahren sind die Prozesse hier nicht gleichwertig. Prozesse haben Prioritäten, der Scheduler sorgt dafür, dass höher priorisierte Prozesse bevorzugt behandelt werden&lt;br /&gt;
&lt;br /&gt;
Natürlich sind Scheduler in freier Wildbahn nicht immer so einfach zu charakterisieren, da sie oftmals komplizierte Hybriden der genannten Techniken implementieren. Die Scheduler der &amp;quot;echten&amp;quot; Betriebsysteme (Windows, Linux, MacOS, *BSD) sind im Prinzip prioritäten-basierende Round Robin Scheduler. Generell hat ein Betriebsystem 2 Möglichkeiten, Multitasking zu realisieren, kooperativ oder präemptiv.&lt;br /&gt;
&lt;br /&gt;
== Kooperatives Multitasking ==&lt;br /&gt;
&lt;br /&gt;
Beim kooperativen Multitasking gibt der Scheduler die Kontrolle komplett an den Prozess ab. D.h., das Betriebsystem ist darauf angewiesen, dass der Prozess die Kontrolle wieder abgibt. Geschieht das nicht, wird der Scheduler nicht wieder aufgerufen und damit auch kein anderer Prozess mehr ausgeführt - das System &amp;quot;hängt&amp;quot;. Das OS ist also auf die [http://de.wikipedia.org/wiki/Kooperation Kooperation] der Prozesse angewiesen. Bekannte Beispiele für Betriebssysteme mit kooperativem Multitasking sind Windows 3.x und MacOS vor Version 10.&lt;br /&gt;
&lt;br /&gt;
Dennoch ist kooperatives Multitasking keineswegs überholt oder schlecht. Gerade im Bereich der Mikrocontroller und Echtzeitanwendungen gibt es viele Argumente, die für ein kooperatives Multitasking sprechen: Kooperatives Multitasking ist deterministischer (zeitlich und logisch vorhersagbar). Es ist besser simulierbar, d.h. für ein gegebenes System ist leichter nachweisbar, dass es funktioniert. Da es sich um geschlossene Systeme handelt, tritt das Problem, dass &amp;quot;irgendein&amp;quot; Prozess das System anhält, nicht auf. Es laufen ja im Gegensatz zum PC nicht &amp;quot;irgendwelche&amp;quot; Prozesse, sondern nur die, deren Korrektheit (hoffentlich) verifiziert &amp;amp; validiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Ein einfaches Beispiel für den AVR ===&lt;br /&gt;
&lt;br /&gt;
Hier soll ein einfaches Beispiel den Weg in die Programmierung von parallel bearbeiteten Aufgaben zeigen.&lt;br /&gt;
&lt;br /&gt;
Wichtigster Grundsatz ist die Herangehensweise! Viele Programmieranfänger haben damit Schwierigkeiten, was u.a. an den schlecht vermittelten Grundlagen liegt. Oft sieht man Funktionen zum Warten in Form von&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
while(1) {&lt;br /&gt;
    PORTD ^= (1&amp;lt;&amp;lt;PD0);&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um beispielsweise eine [[LED]] blinken zu lassen. Will man dann noch andere Dinge erledigen, wundert sich der Programmierer, warum der Mikrocontroller so langsam reagiert, trotz 16 MHz Taktfrequenz.&lt;br /&gt;
&lt;br /&gt;
==== Einfacher Ansatz ====&lt;br /&gt;
&lt;br /&gt;
Stellen wir uns vor, wir wollen drei Dinge gleichzeitig tun.&lt;br /&gt;
&lt;br /&gt;
* Eine Taste abfragen&lt;br /&gt;
* Eine LED blinken lassen, in Abhängigkeit der gedrückten Taste&lt;br /&gt;
* Daten vom UART empfangen und zum PC zurücksenden&lt;br /&gt;
&lt;br /&gt;
Ein einfacher Ansatz für die drei Dinge sieht etwa so aus. Die Beispiele wurden mit [[WinAVR]] Version 20081006 in der Optimierungsstufe -Os kompiliert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, erster Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6864 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
&lt;br /&gt;
    PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    if (taste) &lt;br /&gt;
        _delay_ms(1000);    // 1 s warten&lt;br /&gt;
    else&lt;br /&gt;
        _delay_ms(100);     // 0,1 s warten&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)));            // Warte auf empfangenes Zeichen vom UART&lt;br /&gt;
    tmp = UDR;&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));           // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
    UDR = tmp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 1;              // PC0 ist Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    &lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        taste = taste_lesen();&lt;br /&gt;
        led_blinken(taste);&lt;br /&gt;
        uart_lesen();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man das Programm nun laufen lässt, wird man feststellen dass&lt;br /&gt;
&lt;br /&gt;
* das Hyperterminal sehr langsam reagiert und bisweilen Zeichen verschluckt&lt;br /&gt;
* die LED auf Tastendrücke nur dann reagiert, wenn man per Hyperterminal Zeichen eingibt&lt;br /&gt;
&lt;br /&gt;
Dieser Ansatz ist also untauglich. Egal wie schnell unser AVR auch ist, er reagiert sehr langsam.&lt;br /&gt;
&lt;br /&gt;
==== Verbesserter Ansatz ====&lt;br /&gt;
&lt;br /&gt;
Will man mehrere Dinge gleichzeitig bearbeiten, muss man die Aufgaben in kleinste Häppchen zerteilen. Diese kleinsten Häppchen werden dann verschachtelt abgearbeitet, also ein Häppchen von Aufgabe A, ein Häppchen von Aufgabe B, ein Häppchen von Aufgabe C.&lt;br /&gt;
&lt;br /&gt;
Das Auslesen der Taste geht immer sehr schnell, kein Ansatz zum optimieren. Das Blinken der LED dauer entweder 1s oder 100ms, eine Ewigkeit für einen Mikrocontroller! Hier muss man was ändern. Am schlimmsten ist die UART-Nutzung. Der AVR wartet solange, bis ein Zeichen empfangen wurde! Das kann ewig dauern! Unser Programm steht! Das darf nicht sein!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, zweiter Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6468 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
    static uint16_t zaehler=0;&lt;br /&gt;
&lt;br /&gt;
    if (taste) {&lt;br /&gt;
        if (zaehler&amp;gt;=999) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        if (zaehler&amp;gt;=99) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    zaehler++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    if((UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC))) {                // empfangenes Zeichen abholbereit im UART ?&lt;br /&gt;
        tmp = UDR;&lt;br /&gt;
        while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));       // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
        UDR = tmp;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 1;              // PB0 ist Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    &lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        taste = taste_lesen();&lt;br /&gt;
        led_blinken(taste);&lt;br /&gt;
        uart_lesen();&lt;br /&gt;
        _delay_ms(1);       // 1 ms warten&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses Programm reagiert &#039;&#039;&#039;ganz&#039;&#039;&#039; anders! Schnell wie der Wind und vollkommen unabhängig von anderen, parallel laufenden Prozessen. Warum ist das so ?&lt;br /&gt;
&lt;br /&gt;
Die einzelnen kleinen Häppchen sind verdaulicher als die grossen. Die maximale Durchlaufzeit der einzelnen Funktionen ist drastisch reduziert. Anstatt in der LED-Ausgabe einmal 1000 ms zu warten wird nun 1000x1ms gewartet. Zwischendurch werden aber 1000 mal die anderen Prozesse bearbeitet. Echte Demokratie sozusagen. Noch viel besser ist die Handhabung des UARTs. Anstatt eine Ewigkeit auf ein ankommendes Zeichen zu warten, wird nur dann etwas bearbeitet, wenn auch wirklich etwas zur Bearbeitung vorliegt. Klingt eigentlich logisch. Also nur dann, wenn schon ein Zeichen empfangen wurde wird es auch bearbeitet, ansonsten geht es zurück zur Hauptschleife. Das ist eigentlich der ganze &amp;quot;Trick&amp;quot; eines kooperativen Multitaskings. Auch wenn die Verwendung von _delay_ms(1) noch ein kleiner Schönheitsfehler ist, den die Profis lieber mit einem [[Timer]] erledigen, so wird das Prinzip klar.&lt;br /&gt;
&lt;br /&gt;
*Prozesse eines kooperativen Multitaskingsystems warten nicht auf das Eintreten von Ereignissen, sondern bearbeiten nur bereits eingetretene Ereignisse.&lt;br /&gt;
*Grössere Aufgaben werden in kleine Teilaufgaben zerlegt, welche nur durch mehrfaches Aufrufen der Funktion abgearbeitet werden. Das erreicht man meist am besten mit einer [[statemachine |State machine]].&lt;br /&gt;
*Prozesse eines kooperativen Multitaskings haben eine garantierte, maximale Durchlaufzeit, welche möglichst klein ist.&lt;br /&gt;
&lt;br /&gt;
Damit ähneln die Prozesse einem [[Interrupt]], auch wenn sie als ganz normale Funktionen ausserhalb eines Interrupts ausgeführt werden. An diesem Beispiel erkennt man die Vor- und Nachteile des kooperativen Multitaskings&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* einfacher Scheduler mit geringster CPU Belastung&lt;br /&gt;
* Deterministische Arbeitsweise, damit einfach prüfbar und strenges Timing möglich&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* eine andere Programmierweise zur Zerlegung größerer Aufgaben in kleine Teilaufgaben muss manuell vorgenommen werden&lt;br /&gt;
&lt;br /&gt;
==== Verbesserter Ansatz mit Timer ====&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss noch einmal die verbesserte Version mit Timer. Diese hat mehrere Vorteile.&lt;br /&gt;
&lt;br /&gt;
* Das Zeitraster der Hauptschleife ist exakt, unabhängig von der Laufzeit der Aufgaben, weil der Timer unabhängig eine feste Interruptfrequenz generiert. Im vorherigen Beispiel war das Zeitraster die Summe aus Laufzeit aller Funktionen/Tasks und dem _delay_ms(1).&lt;br /&gt;
* CPU-Rechenleistung wird zu 100% in der Abarbeitung der Task verwendet und nicht für nutzlose Warteschleifen verschwendet.&lt;br /&gt;
* Es kann leicht im realen System geprüft werden, ob die Laufzeit der Tasks klein genug ist, um den Anforderungen des Timers zu genügen.&lt;br /&gt;
&lt;br /&gt;
Diese Überprüfung kann an zwei Stellen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
* Am Ende der Hauptschleife nach Abarbeitung aller Ausgaben. Wenn hier die Variable flag_1ms schon wieder aktiv ist, dauerte die Abarbeitung länger als 1ms. Wenn man ein sehr strenges Timing sicherstellen möchte, ist das ein Fehler, der erkannt und signalisiert werden kann.&lt;br /&gt;
* In der ISR. Wenn hier die Variable immer noch aktiv ist, wurde sie von der Hauptschleife noch nicht erkannt und zurück gesetzt. Das ist definitiv ein Fehler, denn jetzt würde ohne Fehlererkennung ein Timerdurchlauf von der Hauptschleife verschluckt werden. Diese Prüfung ist etwas nachgiebiger, weil zwischenzeitlich ein Durchlauf der Hauptschleife mehr als 1ms, jedoch nicht länger als 2ms dauern darf. Siehe auch den Abschnitt [[Interrupt#Zeitverhalten_eines_Timerinterrupts]]. Sinnvollerweise nutzt man nur eine der beiden Prüfungen, nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, dritter Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6468 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0 und PB1&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
#include &amp;quot;avr/interrupt.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
    static uint16_t zaehler=0;&lt;br /&gt;
&lt;br /&gt;
    if (taste) {&lt;br /&gt;
        if (zaehler&amp;gt;=999) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        if (zaehler&amp;gt;=99) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    zaehler++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    if((UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC))) {                // empfangenes Zeichen abholbereit im UART ?&lt;br /&gt;
        tmp = UDR;&lt;br /&gt;
        while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));       // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
        UDR = tmp;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t flag_1ms;&lt;br /&gt;
 &lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 3;              // PB0/1 sind Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
    // Timer 0 initialisieren, CTC, Presacler 64&lt;br /&gt;
&lt;br /&gt;
    TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01) | (1&amp;lt;&amp;lt;CS00);&lt;br /&gt;
    OCR0  = 56;      // 1ms&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;OCIE0);&lt;br /&gt;
&lt;br /&gt;
    // Interrupts global freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (flag_1ms) {&lt;br /&gt;
            flag_1ms=0;&lt;br /&gt;
            taste = taste_lesen();&lt;br /&gt;
            led_blinken(taste);&lt;br /&gt;
            uart_lesen();&lt;br /&gt;
            if (flag_1ms) {&lt;br /&gt;
                // Laufzeit der Tasks &amp;gt;1ms, Fehlersignalisierung&lt;br /&gt;
                // PB1 auf HIGH, Programm stoppen&lt;br /&gt;
                PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
                while(1);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Interruptserviceroutine für Timer 0&lt;br /&gt;
// hier 1ms&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_COMP_vect) {&lt;br /&gt;
    if (flag_1ms) {&lt;br /&gt;
        // Laufzeit der Tasks &amp;gt;2ms, Fehlersignalisierung&lt;br /&gt;
        // PB1 auf HIGH, Programm stoppen&lt;br /&gt;
        PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
        while(1);&lt;br /&gt;
    }&lt;br /&gt;
    flag_1ms = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Message passing Framework ====&lt;br /&gt;
&lt;br /&gt;
Im vorangegangenen Abschnitt wird erklärt, wie man die einzelnen &amp;quot;Tasks&amp;quot; in kleine Häppchen Zerlegen kann und diese alle innerhalb der Main Loop aufruft.&lt;br /&gt;
Dieses kooperative System hat aber noch einige Nachteile:&lt;br /&gt;
&lt;br /&gt;
* Alle Häppchen werden gleich oft aufgerufen und nicht nur bei Bedarf&lt;br /&gt;
* Für die Timeouts gibt es noch keine befriedigende Lösung&lt;br /&gt;
&lt;br /&gt;
Wenn man diese beiden Nachteile auch noch lösen möchte, wird das ganze noch ein klein wenig komplizierter. Da man das Grundprinzip aber für viele Mikrocontroller Projekte immer wieder verwenden kann, lohnt es sich und man kann die Entwicklung für diese Art des Multitasking in einem Framework zusammenfassen.&lt;br /&gt;
&lt;br /&gt;
Ein Framework, das sind einige Dateien, die den Rahmen (Frame=Rahmen) für ein Programm bilden und den Teil enthalten, den man immer wieder braucht.&lt;br /&gt;
&lt;br /&gt;
===== Message =====&lt;br /&gt;
&lt;br /&gt;
Die Basis des Frameworks bildet die &amp;quot;Message&amp;quot;. Immer wenn wir für einen unserer &amp;quot;Tasks&amp;quot; etwas zu tun haben, schicken wir eine &amp;quot;Message&amp;quot;. Die &amp;quot;Messages&amp;quot; werden in eine Warteschlange einsortiert und der Reihe nach abgearbeitet. Dadurch kommt ein Task der viel zu tun hat (viele Messages bekommt) öfter dran, als ein &amp;quot;Task&amp;quot; der nicht so viel zu tun hat. Außerdem kann eine Message noch Daten enthalten (z.B. ein empfangenes Zeichen). So können die einzelnen Tasks sogar Daten austauschen.&lt;br /&gt;
&lt;br /&gt;
===== Message Receiver =====&lt;br /&gt;
&lt;br /&gt;
Unsere &amp;quot;Tasks&amp;quot; werden immer dann aufgerufen, wenn Arbeit für sie da ist. Das wissen wir, weil sie eine Message empfangen sollen. Deshalb heißen die &amp;quot;Tasks&amp;quot; ab jetzt &amp;quot;Message Receiver&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===== Timeout =====&lt;br /&gt;
&lt;br /&gt;
In diesem System wird jeder Message Receiver aufgerufen, wenn jemand Arbeit für ihn hat und er deshalb eine Message bekommt. Was aber, wenn keine Message kommt, oder ein Message Receiver selbst aktiv werden soll?&lt;br /&gt;
&lt;br /&gt;
Aus diesem Grund braucht es die Timeouts. Mit Hilfe &amp;lt;u&amp;gt;eines&amp;lt;/u&amp;gt; Hardware Timers wird eine &amp;quot;Systemzeit&amp;quot; programmiert. Jeder Message Receiver kann die Zeit angeben, wann er wieder aufgerufen werden muss. Das Framework verwaltet alle Timer und sendet den Message Receivern eine &amp;quot;Timeout&amp;quot; message, wenn ihre Zeit gekommen ist.&lt;br /&gt;
&lt;br /&gt;
===== Beispiel =====&lt;br /&gt;
Hier der Sourcecode eines solchen Framework [[Datei:ACF.zip]]&lt;br /&gt;
Das Framework implementiert die Message Warteschlange und die Timer Warteschlange. Die Prozessor spezifischen Dinge sind in der Datei &amp;quot;ACF_Hal.c&amp;quot; zusammengefasst und für Linux Desktop und atMega128 implementiert.&lt;br /&gt;
In dieser Datei kann man das ganze auch auf andere Prozessoren anpassen.&lt;br /&gt;
&lt;br /&gt;
Ein &amp;quot;main&amp;quot; sieht dann z.B. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ACF.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv)&lt;br /&gt;
{&lt;br /&gt;
    ACF_init();&lt;br /&gt;
    ACF_loop();&lt;br /&gt;
    return 0; // we will never arrive here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tracing =====&lt;br /&gt;
Es gibt noch einen weiteren Grund, sich ein &amp;quot;Framework&amp;quot; zu erarbeiten, oder ein fertiges Framework zu verwenden. Da der Ablauf der Software und der Aufruf aller Teile vom Framework bestimmt wird, kann das Framework auch einen sehr detaillierten Trace über das Verhalten des Codes anfertigen. Das Framework aus vorstehendem Beispiel enthält bereits entsprechenden Code.&lt;br /&gt;
&lt;br /&gt;
Solche Traces von laufendem Code können gerade dann sehr hilfreich sein, wenn viele Dinge gleichzeitig ablaufen (und das war schließlich der Sinn des ganzen).&lt;br /&gt;
&lt;br /&gt;
Nachstehendes Bild zeigt den Trace eines Reglers, der mit dem Framework realisiert wurde.&lt;br /&gt;
[http://www.mikrocontroller.net/attachment/74409/ablauf.png Sequenzdiagramm]&lt;br /&gt;
&lt;br /&gt;
== Präemptives Multitasking ==&lt;br /&gt;
&lt;br /&gt;
Beim präemptiven Multitasking gibt das OS die Kontrolle zu keinem Zeitpunkt auf. Ein Prozess, der gerade die CPU nutzt, kann jederzeit wieder vom Betriebssystem unterbrochen werden. Daher muss bei der Entwicklung für ein präemptives System immer damit gerechnet werden, dass ein Prozess &#039;&#039;&#039;jederzeit&#039;&#039;&#039; unterbrochen werden kann. Das kann z.&amp;amp;nbsp;B. zu Problemen beim Zugriff auf limitierte Betriebsmittel führen. Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Prozess A sucht freien Speicher und findet einen freien Block&lt;br /&gt;
* Prozess B wird vom Scheduler gestartet und sucht ebenfalls einen Speicherblock. Der gefundene Block wird von Prozess B reserviert und benutzt&lt;br /&gt;
* Der Scheduler teilt wieder Prozess A die CPU zu. Prozess A wird fortgeführt, d.h. er reserviert jetzt den im letzten Systemcall gefundenen Speicherblock&lt;br /&gt;
Jetzt haben also beide Prozesse den gleichen Speicherblock reserviert. Entweder arbeiten jetzt beide Prozesse mit dem gleichen Speicher, und überschreiben daher gegenseitig die Daten, oder das Betriebsystem hat etwas gemerkt und zieht die Notbremse. In jedem Fall passieren schreckliche Dinge. Sowas nennt man eine Race-Condition.&lt;br /&gt;
&lt;br /&gt;
Die Lösung nennt sich Semaphore: Dieser Mechanismus wird vom Betriebsystem bereitgestellt und erlaubt es einem Prozess eine bestimmte Ressource zu sperren. Wenn also Prozess A aus obigem Beispiel Speicher haben möchte, setzt er vor Beginn der sogenannten &amp;quot;Kritischen Sektion&amp;quot; einen Semaphor für &amp;quot;Speicher reservieren&amp;quot;. Dieser Semaphor wird erst wieder aufgehoben, sobald Prozess A den Speicher für sich reserviert hat. Wenn der Prozess B zwischendurch gestartet wird und ebenfalls versucht den Semaphor zu setzen, wird er solange warten müssen, bis Prozess A den Semaphor wieder freigibt. Speziell für derartige Locking Mechanismen bieten die meisten Prozessoren sogenannte TAS-Befehle (Test And Set), die in einem Prozessorbefehl eine Variable testen und je nach Ergebnis setzen können. Das ist nötig um das Setzen von Semaphoren unteilbar (atomar) zu machen. Könnte der Scheduler das Setzen eines Semaphors unterbrechen, wäre ja der ganze Aufwand umsonst.&lt;br /&gt;
&lt;br /&gt;
Präemptive Multitasking Systeme sind sehr flexibel und kommen mit einer Vielzahl an Tasks klar. Amok laufende Prozesse können das System bei korrekter Implementierung nicht blockieren. Damit aber das System crash-sicher ist, muss es Systemresourcen geben, die nur der Scheduler verteilen kann (z.&amp;amp;nbsp;B. kein anderer Prozess darf in den Speicherbereich des Schedulers schreiben; kein anderer Prozess darf den Timerinterrupt des Schedulers ändern). Diese Möglichkeiten sind in Mikrocontrollern normalerweise gar nicht vorhanden, wodurch dieser Vorteil des Präemptiven MT weniger ins Gewicht fällt. Beispiele für Systeme mit präemptivem Multitasking sind Linux, *BSD  und Windows XP.&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* sehr flexibel in der Verwaltung von dynamisch ausgeführten Prozessen&lt;br /&gt;
* einzelne Prozesse können einfach linear programmiert werden, ohne die Aufgabe in kleine Teile zerlegen zu müssen&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Der Scheduler ist aufwändiger und benötigt mehr CPU-Zeit&lt;br /&gt;
* Höherer Resourcenbedarf zu Verwaltung des Systems und Bereitstellung der Semaphore etc.&lt;br /&gt;
* nicht streng deterministisch, somit kann kein festes Timing garantiert werden&lt;br /&gt;
* nicht explizit debug- und prüfbar, da die Prozesse nicht fest gekoppelt sind&lt;br /&gt;
&lt;br /&gt;
== Multithreading ==&lt;br /&gt;
&lt;br /&gt;
Multithreading ist eine meist softwarebasierende Möglichkeit moderner Betriebssysteme, innerhalb eines Prozesses mehrere Tasks (threads) parallel auszuführen. Der Vorteil dieser weiteren Unterteilung ist, dass sich die Threads eines Tasks den Speicherbereich teilen können und eine Aufteilung in logische nebeneinander laufende Teile möglich ist. Je nach Betriebssystem kann der Übergang von Multithreading zu Multiprocessing fliessend bis starr sein. &lt;br /&gt;
&lt;br /&gt;
Das Hyperthreading eines Intel Pentium 4 folgt dem Konzept des Multithreadings auf Hardwarebasis und teilt den CPU-Kern zeitlich in zwei logische Prozessoren ein.&lt;br /&gt;
&lt;br /&gt;
== Umsetzung auf Prozessoren ==&lt;br /&gt;
Unabhängig davon, ob Multitasking oder -threading auf einem Prozessor konkret unterstützt wird, lässt es sich immer in Form von Software realisieren. Dies wird in modernen Systemen durch das OS geleistet, das standardisierte Funktionen und Strukturen zur Verfügung stellt. Besonders C++ bietet ein stark abstrahiertes Programmiermodell und Methoden-Set an, um effektiv untereinander kompatible Programmmodule erstellen zu können. Nutzt man diese nicht, wie z.B. bei der Programmierung in C, müssen Strukturen manuell erzeugt und gehandhabt werden, was aufwändiger ist, aber auch geringeren overhead bewirkt. Das Programm ist dann fast immer erheblich kleiner, in den meisten Fällen strukturell einfacher, bezüglich komplizierter Änderungen jedoch auch unflexibler und träger. &lt;br /&gt;
&lt;br /&gt;
Bei Mikrocontrollern findet man je nach Komplexität und Struktur der Appliation praktisch alle denkbaren Kombinationen:&lt;br /&gt;
&lt;br /&gt;
=== System mit real-time OS und Entwicklung in C++ ===&lt;br /&gt;
* Programmentwicklung stark an abstrakte Interfaces und Standards gebunden&lt;br /&gt;
* Starke Abängigkeit an das OS im Bezug auf RT-Funktionalität&lt;br /&gt;
* Innerhalb gleicher OS-Landschaft gut portierbar&lt;br /&gt;
* Sehr geringe Abhängigkeit vom Prozessortyp&lt;br /&gt;
* Multitasking muss über OS-Schicht ausprogrammiert werden&lt;br /&gt;
* Multithreading muss/kann durch Programmierleistung optimiert werden&lt;br /&gt;
* Laufzeiteffizienz ist niedrig durch relativ hohen Anteil des OS-Bedarfs&lt;br /&gt;
* Datendurchsatz regelt sich selbst und ist gleichmässig am relativen Maximum&lt;br /&gt;
* Programmiereffizienz ist niedrig durch sehr viel Arbeit an Formalismen&lt;br /&gt;
* Planungsaufwand ist noch überschaubar&lt;br /&gt;
* Planungseffizienz ist relaiv hoch durch viele Standards&lt;br /&gt;
* Erweiterung um komplexe Module einfach möglich, Timing regelt sich selber&lt;br /&gt;
&lt;br /&gt;
=== System mit real-time OS und Entwicklung in C ===&lt;br /&gt;
* Programmentwicklung stark an Interfaces und weniger an Standards&lt;br /&gt;
* Starke Abängigkeit an das OS im Bezug auf RT-Funktionalität&lt;br /&gt;
* Innerhalb aber auch ausserhalb gleicher OS-Landschaft gut portierbar&lt;br /&gt;
* Geringe Abhängig von dem Prozessortyp&lt;br /&gt;
* Multitasking muss über OS-Schicht und eigene Strukturen programmiert werden&lt;br /&gt;
* Multithreading muss/kann durch Programmierleistung optimiert werden&lt;br /&gt;
* Laufzeiteffizienz ist höher durch geringeren Anteil des OS-Bedarfs&lt;br /&gt;
* Datendurchsatz regelt sich selbst und ist gleichmässig am relativen Maximum&lt;br /&gt;
* Programmiereffizienz ist höher durch weniger Arbeit mit Formalien&lt;br /&gt;
* Planungsaufwand ist etwas höher, je nach Applikation&lt;br /&gt;
* Planungseffizienz ist relaiv niedriger durch weniger Standards&lt;br /&gt;
* Erweiterung mit akzeptablem Aufwand möglich, Timing muss beachtet werden&lt;br /&gt;
&lt;br /&gt;
=== System ohne real-time OS und Entwicklung in C ===&lt;br /&gt;
* Programmentwicklung stark an Interfaces und kaum an Standards gebunden&lt;br /&gt;
* Ausprägung und Gestaltung der RT-Funktionalität absolut frei&lt;br /&gt;
* nur ausserhalb von OS-Landschaft portierbar, dafür prinziepiell sehr gut&lt;br /&gt;
* Stärkere Abhängig von dem Prozessortyp, kann die Portierbarkeit einschränken&lt;br /&gt;
* Multitasking muss auschlieslich durch eigene Strukturen programmiert werden&lt;br /&gt;
* Multithreading muss durch umständliche Programmierung ermöglicht werden werden&lt;br /&gt;
* Laufzeiteffizienz kann sehr hoch sein, ist aber stark von der Progr. abhängig&lt;br /&gt;
* Datendurchsatz muss selbst ins Maximum gesteuert werden, das aber höher liegt&lt;br /&gt;
* Programmiereffizienz ist hoch, dank Wegfall von Konventionen&lt;br /&gt;
* Planungsaufwand ist stark Applikations abhängig, gfs sehr viel grösser&lt;br /&gt;
* Planungseffizienz ist gering, da RT Konzept selber optmiert werden muss&lt;br /&gt;
* Erweiterung nur möglich, wenn Timing weitestgehend überarbeitet wird&lt;br /&gt;
&lt;br /&gt;
=== System ohne real-time OS und Entwicklung in ASM ===&lt;br /&gt;
* Programmentw. nur an physische Interfaces gebunden und frei von Standards&lt;br /&gt;
* Ausprägung und Gestaltung der RT-Funktionalität absolut frei&lt;br /&gt;
* nur innerhalb der Prozessorlandschaft gut portierbar&lt;br /&gt;
* Volle Abhängig vom Prozessortyp, Portierbarkeit auf andere aufwändig&lt;br /&gt;
* Multitasking muss auschlieslich durch eigene Strukturen programmiert werden&lt;br /&gt;
* Multithredding muss durch umständliche Programmierung ermöglicht werden werden&lt;br /&gt;
* Laufzeiteffizienz sehr hoch, jedoch stark von Programmierung abhängig&lt;br /&gt;
* Datendurchsatz muss selbst ins Maximum gesteuert werden, das aber höher liegt&lt;br /&gt;
* Programmiereffizienz ist hoch, dank Wegfall von Konventionen&lt;br /&gt;
* Planungsaufwand ist stark applikationsabhängig, ggfs. sehr viel grösser&lt;br /&gt;
* Planungseffizienz sehr gering, RT Konzept nur für einfache System machbar&lt;br /&gt;
* Erweiterung nur möglich, wenn Timing komplett überarbeitet wird&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Pr%E4emptives_Multitasking Präemptives Multitasking] bei [http://de.wikipedia.org Wikipedia]&lt;br /&gt;
* [http://www.femtoos.org/ Femto OS], ein ultrakompaktes Mulitaskingbetriebssystem für kleine Mikrocontroller&lt;br /&gt;
*[http://www.freertos.org/ FreeRTOS], ein freies Echtzeitbetriebssystem für Mikrocontroller&lt;br /&gt;
* [http://w3.ualg.pt/~rmarcel/Get%20by%20Without%20an%20RTOS.pdf Get by Without an RTOS] Ein schönes Beispiel wie man ohne ein RTOS auch Multitasking hinbekommt. &lt;br /&gt;
* [[TNKernel]], freier Multitasking-Kernel. &lt;br /&gt;
* [http://embeddedgurus.com/state-space/2010/04/i-hate-rtoses/ i-hate-rtoses] Blog zum Thema RTOS&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Betriebssysteme]]&lt;/div&gt;</summary>
		<author><name>134.169.116.105</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Multitasking&amp;diff=85405</id>
		<title>Multitasking</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Multitasking&amp;diff=85405"/>
		<updated>2014-10-27T13:02:00Z</updated>

		<summary type="html">&lt;p&gt;134.169.116.105: /* System mit real-time OS und Entwicklung in C */ Typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Multitasking bedeutet ein quasi paralleles Ausführen von mehreren Prozessen auf einem Prozessor.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Da eine echte parallele Ausführung von mehreren Prozessen (Programmen, Funktionen) auf einem einzelnen CPU-Kern nicht möglich ist, wird ein &amp;quot;Trick&amp;quot; verwendet. Dabei werden die einzelnen Prozesse jeweils nur für kurze Zeit (1..50 ms) bearbeitet und danach auf einen anderen Prozess umgeschaltet. Man spricht auch von einer verschachtelten Bearbeitung (engl. interleaving).&lt;br /&gt;
&lt;br /&gt;
Das Herz jedes Multitasking-Systems ist der Scheduler. Dieses Programm beinhaltet einen Algorithmus, der überprüft, welcher Prozess als nächstes die CPU (also Rechenzeit) zugeteilt bekommt. Es gibt verschiedene Schedulingalgorithmen:&lt;br /&gt;
&lt;br /&gt;
* First come first served: Teilt den Prozessen in der Reihenfolge Rechenzeit zu,  in der sie rechenbereit werden&lt;br /&gt;
* Shortest Job first: Der Prozess mit der kürzesten Rechenzeit wird als erstes bearbeitet. Dazu muss die Rechenzeit natürlich im Voraus bekannt sein&lt;br /&gt;
* Shortest remaining time next: Der Prozess mit der kürzesten verbleibenden Rechenzeit wird jeweils als nächstes bearbeitet. Auch hier muss diese Zeit natürlich bekannt sein&lt;br /&gt;
* Round Robin: Alle Prozesse bekommen eine gleich große Zeitscheibe zugeteilt. Der Scheduler lässt jeden Prozess für die Dauer einer Zeitscheibe rechnen, und übergibt die CPU dann an den nächsten Prozess&lt;br /&gt;
* Priority Scheduling: Anders als beim Round Robin Verfahren sind die Prozesse hier nicht gleichwertig. Prozesse haben Prioritäten, der Scheduler sorgt dafür, dass höher priorisierte Prozesse bevorzugt behandelt werden&lt;br /&gt;
&lt;br /&gt;
Natürlich sind Scheduler in freier Wildbahn nicht immer so einfach zu charakterisieren, da sie oftmals komplizierte Hybriden der genannten Techniken implementieren. Die Scheduler der &amp;quot;echten&amp;quot; Betriebsysteme (Windows, Linux, MacOS, *BSD) sind im Prinzip prioritäten-basierende Round Robin Scheduler. Generell hat ein Betriebsystem 2 Möglichkeiten, Multitasking zu realisieren, kooperativ oder präemptiv.&lt;br /&gt;
&lt;br /&gt;
== Kooperatives Multitasking ==&lt;br /&gt;
&lt;br /&gt;
Beim kooperativen Multitasking gibt der Scheduler die Kontrolle komplett an den Prozess ab. D.h., das Betriebsystem ist darauf angewiesen, dass der Prozess die Kontrolle wieder abgibt. Geschieht das nicht, wird der Scheduler nicht wieder aufgerufen und damit auch kein anderer Prozess mehr ausgeführt - das System &amp;quot;hängt&amp;quot;. Das OS ist also auf die [http://de.wikipedia.org/wiki/Kooperation Kooperation] der Prozesse angewiesen. Bekannte Beispiele für Betriebssysteme mit kooperativem Multitasking sind Windows 3.x und MacOS vor Version 10.&lt;br /&gt;
&lt;br /&gt;
Dennoch ist kooperatives Multitasking keineswegs überholt oder schlecht. Gerade im Bereich der Mikrocontroller und Echtzeitanwendungen gibt es viele Argumente, die für ein kooperatives Multitasking sprechen: Kooperatives Multitasking ist deterministischer (zeitlich und logisch vorhersagbar). Es ist besser simulierbar, d.h. für ein gegebenes System ist leichter nachweisbar, dass es funktioniert. Da es sich um geschlossene Systeme handelt, tritt das Problem, dass &amp;quot;irgendein&amp;quot; Prozess das System anhält, nicht auf. Es laufen ja im Gegensatz zum PC nicht &amp;quot;irgendwelche&amp;quot; Prozesse, sondern nur die, deren Korrektheit (hoffentlich) verifiziert &amp;amp; validiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Ein einfaches Beispiel für den AVR ===&lt;br /&gt;
&lt;br /&gt;
Hier soll ein einfaches Beispiel den Weg in die Programmierung von parallel bearbeiteten Aufgaben zeigen.&lt;br /&gt;
&lt;br /&gt;
Wichtigster Grundsatz ist die Herangehensweise! Viele Programmieranfänger haben damit Schwierigkeiten, was u.a. an den schlecht vermittelten Grundlagen liegt. Oft sieht man Funktionen zum Warten in Form von&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
while(1) {&lt;br /&gt;
    PORTD ^= (1&amp;lt;&amp;lt;PD0);&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um beispielsweise eine [[LED]] blinken zu lassen. Will man dann noch andere Dinge erledigen, wundert sich der Programmierer, warum der Mikrocontroller so langsam reagiert, trotz 16 MHz Taktfrequenz.&lt;br /&gt;
&lt;br /&gt;
==== Einfacher Ansatz ====&lt;br /&gt;
&lt;br /&gt;
Stellen wir uns vor, wir wollen drei Dinge gleichzeitig tun.&lt;br /&gt;
&lt;br /&gt;
* Eine Taste abfragen&lt;br /&gt;
* Eine LED blinken lassen, in Abhängigkeit der gedrückten Taste&lt;br /&gt;
* Daten vom UART empfangen und zum PC zurücksenden&lt;br /&gt;
&lt;br /&gt;
Ein einfacher Ansatz für die drei Dinge sieht etwa so aus. Die Beispiele wurden mit [[WinAVR]] Version 20081006 in der Optimierungsstufe -Os kompiliert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, erster Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6864 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
&lt;br /&gt;
    PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    if (taste) &lt;br /&gt;
        _delay_ms(1000);    // 1 s warten&lt;br /&gt;
    else&lt;br /&gt;
        _delay_ms(100);     // 0,1 s warten&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)));            // Warte auf empfangenes Zeichen vom UART&lt;br /&gt;
    tmp = UDR;&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));           // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
    UDR = tmp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 1;              // PC0 ist Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    &lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        taste = taste_lesen();&lt;br /&gt;
        led_blinken(taste);&lt;br /&gt;
        uart_lesen();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man das Programm nun laufen lässt, wird man feststellen dass&lt;br /&gt;
&lt;br /&gt;
* das Hyperterminal sehr langsam reagiert und bisweilen Zeichen verschluckt&lt;br /&gt;
* die LED auf Tastendrücke nur dann reagiert, wenn man per Hyperterminal Zeichen eingibt&lt;br /&gt;
&lt;br /&gt;
Dieser Ansatz ist also untauglich. Egal wie schnell unser AVR auch ist, er reagiert sehr langsam.&lt;br /&gt;
&lt;br /&gt;
==== Verbesserter Ansatz ====&lt;br /&gt;
&lt;br /&gt;
Will man mehrere Dinge gleichzeitig bearbeiten, muss man die Aufgaben in kleinste Häppchen zerteilen. Diese kleinsten Häppchen werden dann verschachtelt abgearbeitet, also ein Häppchen von Aufgabe A, ein Häppchen von Aufgabe B, ein Häppchen von Aufgabe C.&lt;br /&gt;
&lt;br /&gt;
Das Auslesen der Taste geht immer sehr schnell, kein Ansatz zum optimieren. Das Blinken der LED dauer entweder 1s oder 100ms, eine Ewigkeit für einen Mikrocontroller! Hier muss man was ändern. Am schlimmsten ist die UART-Nutzung. Der AVR wartet solange, bis ein Zeichen empfangen wurde! Das kann ewig dauern! Unser Programm steht! Das darf nicht sein!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, zweiter Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6468 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
    static uint16_t zaehler=0;&lt;br /&gt;
&lt;br /&gt;
    if (taste) {&lt;br /&gt;
        if (zaehler&amp;gt;=999) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        if (zaehler&amp;gt;=99) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    zaehler++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    if((UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC))) {                // empfangenes Zeichen abholbereit im UART ?&lt;br /&gt;
        tmp = UDR;&lt;br /&gt;
        while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));       // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
        UDR = tmp;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 1;              // PB0 ist Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    &lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        taste = taste_lesen();&lt;br /&gt;
        led_blinken(taste);&lt;br /&gt;
        uart_lesen();&lt;br /&gt;
        _delay_ms(1);       // 1 ms warten&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses Programm reagiert &#039;&#039;&#039;ganz&#039;&#039;&#039; anders! Schnell wie der Wind und vollkommen unabhängig von anderen, parallel laufenden Prozessen. Warum ist das so ?&lt;br /&gt;
&lt;br /&gt;
Die einzelnen kleinen Häppchen sind verdaulicher als die grossen. Die maximale Durchlaufzeit der einzelnen Funktionen ist drastisch reduziert. Anstatt in der LED-Ausgabe einmal 1000 ms zu warten wird nun 1000x1ms gewartet. Zwischendurch werden aber 1000 mal die anderen Prozesse bearbeitet. Echte Demokratie sozusagen. Noch viel besser ist die Handhabung des UARTs. Anstatt eine Ewigkeit auf ein ankommendes Zeichen zu warten, wird nur dann etwas bearbeitet, wenn auch wirklich etwas zur Bearbeitung vorliegt. Klingt eigentlich logisch. Also nur dann, wenn schon ein Zeichen empfangen wurde wird es auch bearbeitet, ansonsten geht es zurück zur Hauptschleife. Das ist eigentlich der ganze &amp;quot;Trick&amp;quot; eines kooperativen Multitaskings. Auch wenn die Verwendung von _delay_ms(1) noch ein kleiner Schönheitsfehler ist, den die Profis lieber mit einem [[Timer]] erledigen, so wird das Prinzip klar.&lt;br /&gt;
&lt;br /&gt;
*Prozesse eines kooperativen Multitaskingsystems warten nicht auf das Eintreten von Ereignissen, sondern bearbeiten nur bereits eingetretene Ereignisse.&lt;br /&gt;
*Grössere Aufgaben werden in kleine Teilaufgaben zerlegt, welche nur durch mehrfaches Aufrufen der Funktion abgearbeitet werden. Das erreicht man meist am besten mit einer [[statemachine |State machine]].&lt;br /&gt;
*Prozesse eines kooperativen Multitaskings haben eine garantierte, maximale Durchlaufzeit, welche möglichst klein ist.&lt;br /&gt;
&lt;br /&gt;
Damit ähneln die Prozesse einem [[Interrupt]], auch wenn sie als ganz normale Funktionen ausserhalb eines Interrupts ausgeführt werden. An diesem Beispiel erkennt man die Vor- und Nachteile des kooperativen Multitaskings&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* einfacher Scheduler mit geringster CPU Belastung&lt;br /&gt;
* Deterministische Arbeitsweise, damit einfach prüfbar und strenges Timing möglich&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* eine andere Programmierweise zur Zerlegung größerer Aufgaben in kleine Teilaufgaben muss manuell vorgenommen werden&lt;br /&gt;
&lt;br /&gt;
==== Verbesserter Ansatz mit Timer ====&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss noch einmal die verbesserte Version mit Timer. Diese hat mehrere Vorteile.&lt;br /&gt;
&lt;br /&gt;
* Das Zeitraster der Hauptschleife ist exakt, unabhängig von der Laufzeit der Aufgaben, weil der Timer unabhängig eine feste Interruptfrequenz generiert. Im vorherigen Beispiel war das Zeitraster die Summe aus Laufzeit aller Funktionen/Tasks und dem _delay_ms(1).&lt;br /&gt;
* CPU-Rechenleistung wird zu 100% in der Abarbeitung der Task verwendet und nicht für nutzlose Warteschleifen verschwendet.&lt;br /&gt;
* Es kann leicht im realen System geprüft werden, ob die Laufzeit der Tasks klein genug ist, um den Anforderungen des Timers zu genügen.&lt;br /&gt;
&lt;br /&gt;
Diese Überprüfung kann an zwei Stellen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
* Am Ende der Hauptschleife nach Abarbeitung aller Ausgaben. Wenn hier die Variable flag_1ms schon wieder aktiv ist, dauerte die Abarbeitung länger als 1ms. Wenn man ein sehr strenges Timing sicherstellen möchte, ist das ein Fehler, der erkannt und signalisiert werden kann.&lt;br /&gt;
* In der ISR. Wenn hier die Variable immer noch aktiv ist, wurde sie von der Hauptschleife noch nicht erkannt und zurück gesetzt. Das ist definitiv ein Fehler, denn jetzt würde ohne Fehlererkennung ein Timerdurchlauf von der Hauptschleife verschluckt werden. Diese Prüfung ist etwas nachgiebiger, weil zwischenzeitlich ein Durchlauf der Hauptschleife mehr als 1ms, jedoch nicht länger als 2ms dauern darf. Siehe auch den Abschnitt [[Interrupt#Zeitverhalten_eines_Timerinterrupts]]. Sinnvollerweise nutzt man nur eine der beiden Prüfungen, nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, dritter Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6468 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0 und PB1&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
#include &amp;quot;avr/interrupt.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
    static uint16_t zaehler=0;&lt;br /&gt;
&lt;br /&gt;
    if (taste) {&lt;br /&gt;
        if (zaehler&amp;gt;=999) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        if (zaehler&amp;gt;=99) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    zaehler++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    if((UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC))) {                // empfangenes Zeichen abholbereit im UART ?&lt;br /&gt;
        tmp = UDR;&lt;br /&gt;
        while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));       // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
        UDR = tmp;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t flag_1ms;&lt;br /&gt;
 &lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 3;              // PB0/1 sind Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
    // Timer 0 initialisieren, CTC, Presacler 64&lt;br /&gt;
&lt;br /&gt;
    TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01) | (1&amp;lt;&amp;lt;CS00);&lt;br /&gt;
    OCR0  = 56;      // 1ms&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;OCIE0);&lt;br /&gt;
&lt;br /&gt;
    // Interrupts global freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (flag_1ms) {&lt;br /&gt;
            flag_1ms=0;&lt;br /&gt;
            taste = taste_lesen();&lt;br /&gt;
            led_blinken(taste);&lt;br /&gt;
            uart_lesen();&lt;br /&gt;
            if (flag_1ms) {&lt;br /&gt;
                // Laufzeit der Tasks &amp;gt;1ms, Fehlersignalisierung&lt;br /&gt;
                // PB1 auf HIGH, Programm stoppen&lt;br /&gt;
                PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
                while(1);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Interruptserviceroutine für Timer 0&lt;br /&gt;
// hier 1ms&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_COMP_vect) {&lt;br /&gt;
    if (flag_1ms) {&lt;br /&gt;
        // Laufzeit der Tasks &amp;gt;2ms, Fehlersignalisierung&lt;br /&gt;
        // PB1 auf HIGH, Programm stoppen&lt;br /&gt;
        PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
        while(1);&lt;br /&gt;
    }&lt;br /&gt;
    flag_1ms = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Message passing Framework ====&lt;br /&gt;
&lt;br /&gt;
Im vorangegangenen Abschnitt wird erklärt, wie man die einzelnen &amp;quot;Tasks&amp;quot; in kleine Häppchen Zerlegen kann und diese alle innerhalb der Main Loop aufruft.&lt;br /&gt;
Dieses kooperative System hat aber noch einige Nachteile:&lt;br /&gt;
&lt;br /&gt;
* Alle Häppchen werden gleich oft aufgerufen und nicht nur bei Bedarf&lt;br /&gt;
* Für die Timeouts gibt es noch keine befriedigende Lösung&lt;br /&gt;
&lt;br /&gt;
Wenn man diese beiden Nachteile auch noch lösen möchte, wird das ganze noch ein klein wenig komplizierter. Da man das Grundprinzip aber für viele Mikrocontroller Projekte immer wieder verwenden kann, lohnt es sich und man kann die Entwicklung für diese Art des Multitasking in einem Framework zusammenfassen.&lt;br /&gt;
&lt;br /&gt;
Ein Framework, das sind einige Dateien, die den Rahmen (Frame=Rahmen) für ein Programm bilden und den Teil enthalten, den man immer wieder braucht.&lt;br /&gt;
&lt;br /&gt;
===== Message =====&lt;br /&gt;
&lt;br /&gt;
Die Basis des Frameworks bildet die &amp;quot;Message&amp;quot;. Immer wenn wir für einen unserer &amp;quot;Tasks&amp;quot; etwas zu tun haben, schicken wir eine &amp;quot;Message&amp;quot;. Die &amp;quot;Messages&amp;quot; werden in eine Warteschlange einsortiert und der Reihe nach abgearbeitet. Dadurch kommt ein Task der viel zu tun hat (viele Messages bekommt) öfter dran, als ein &amp;quot;Task&amp;quot; der nicht so viel zu tun hat. Außerdem kann eine Message noch Daten enthalten (z.B. ein empfangenes Zeichen). So können die einzelnen Tasks sogar Daten austauschen.&lt;br /&gt;
&lt;br /&gt;
===== Message Receiver =====&lt;br /&gt;
&lt;br /&gt;
Unsere &amp;quot;Tasks&amp;quot; werden immer dann aufgerufen, wenn Arbeit für sie da ist. Das wissen wir, weil sie eine Message empfangen sollen. Deshalb heißen die &amp;quot;Tasks&amp;quot; ab jetzt &amp;quot;Message Receiver&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===== Timeout =====&lt;br /&gt;
&lt;br /&gt;
In diesem System wird jeder Message Receiver aufgerufen, wenn jemand Arbeit für ihn hat und er deshalb eine Message bekommt. Was aber, wenn keine Message kommt, oder ein Message Receiver selbst aktiv werden soll?&lt;br /&gt;
&lt;br /&gt;
Aus diesem Grund braucht es die Timeouts. Mit Hilfe &amp;lt;u&amp;gt;eines&amp;lt;/u&amp;gt; Hardware Timers wird eine &amp;quot;Systemzeit&amp;quot; programmiert. Jeder Message Receiver kann die Zeit angeben, wann er wieder aufgerufen werden muss. Das Framework verwaltet alle Timer und sendet den Message Receivern eine &amp;quot;Timeout&amp;quot; message, wenn ihre Zeit gekommen ist.&lt;br /&gt;
&lt;br /&gt;
===== Beispiel =====&lt;br /&gt;
Hier der Sourcecode eines solchen Framework [[Datei:ACF.zip]]&lt;br /&gt;
Das Framework implementiert die Message Warteschlange und die Timer Warteschlange. Die Prozessor spezifischen Dinge sind in der Datei &amp;quot;ACF_Hal.c&amp;quot; zusammengefasst und für Linux Desktop und atMega128 implementiert.&lt;br /&gt;
In dieser Datei kann man das ganze auch auf andere Prozessoren anpassen.&lt;br /&gt;
&lt;br /&gt;
Ein &amp;quot;main&amp;quot; sieht dann z.B. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ACF.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv)&lt;br /&gt;
{&lt;br /&gt;
    ACF_init();&lt;br /&gt;
    ACF_loop();&lt;br /&gt;
    return 0; // we will never arrive here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tracing =====&lt;br /&gt;
Es gibt noch einen weiteren Grund, sich ein &amp;quot;Framework&amp;quot; zu erarbeiten, oder ein fertiges Framework zu verwenden. Da der Ablauf der Software und der Aufruf aller Teile vom Framework bestimmt wird, kann das Framework auch einen sehr detaillierten Trace über das Verhalten des Codes anfertigen. Das Framework aus vorstehendem Beispiel enthält bereits entsprechenden Code.&lt;br /&gt;
&lt;br /&gt;
Solche Traces von laufendem Code können gerade dann sehr hilfreich sein, wenn viele Dinge gleichzeitig ablaufen (und das war schließlich der Sinn des ganzen).&lt;br /&gt;
&lt;br /&gt;
Nachstehendes Bild zeigt den Trace eines Reglers, der mit dem Framework realisiert wurde.&lt;br /&gt;
[http://www.mikrocontroller.net/attachment/74409/ablauf.png Sequenzdiagramm]&lt;br /&gt;
&lt;br /&gt;
== Präemptives Multitasking ==&lt;br /&gt;
&lt;br /&gt;
Beim präemptiven Multitasking gibt das OS die Kontrolle zu keinem Zeitpunkt auf. Ein Prozess, der gerade die CPU nutzt, kann jederzeit wieder vom Betriebssystem unterbrochen werden. Daher muss bei der Entwicklung für ein präemptives System immer damit gerechnet werden, dass ein Prozess &#039;&#039;&#039;jederzeit&#039;&#039;&#039; unterbrochen werden kann. Das kann z.&amp;amp;nbsp;B. zu Problemen beim Zugriff auf limitierte Betriebsmittel führen. Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Prozess A sucht freien Speicher und findet einen freien Block&lt;br /&gt;
* Prozess B wird vom Scheduler gestartet und sucht ebenfalls einen Speicherblock. Der gefundene Block wird von Prozess B reserviert und benutzt&lt;br /&gt;
* Der Scheduler teilt wieder Prozess A die CPU zu. Prozess A wird fortgeführt, d.h. er reserviert jetzt den im letzten Systemcall gefundenen Speicherblock&lt;br /&gt;
Jetzt haben also beide Prozesse den gleichen Speicherblock reserviert. Entweder arbeiten jetzt beide Prozesse mit dem gleichen Speicher, und überschreiben daher gegenseitig die Daten, oder das Betriebsystem hat etwas gemerkt und zieht die Notbremse. In jedem Fall passieren schreckliche Dinge. Sowas nennt man eine Race-Condition.&lt;br /&gt;
&lt;br /&gt;
Die Lösung nennt sich Semaphore: Dieser Mechanismus wird vom Betriebsystem bereitgestellt und erlaubt es einem Prozess eine bestimmte Ressource zu sperren. Wenn also Prozess A aus obigem Beispiel Speicher haben möchte, setzt er vor Beginn der sogenannten &amp;quot;Kritischen Sektion&amp;quot; einen Semaphor für &amp;quot;Speicher reservieren&amp;quot;. Dieser Semaphor wird erst wieder aufgehoben, sobald Prozess A den Speicher für sich reserviert hat. Wenn der Prozess B zwischendurch gestartet wird und ebenfalls versucht den Semaphor zu setzen, wird er solange warten müssen, bis Prozess A den Semaphor wieder freigibt. Speziell für derartige Locking Mechanismen bieten die meisten Prozessoren sogenannte TAS-Befehle (Test And Set), die in einem Prozessorbefehl eine Variable testen und je nach Ergebnis setzen können. Das ist nötig um das Setzen von Semaphoren unteilbar (atomar) zu machen. Könnte der Scheduler das Setzen eines Semaphors unterbrechen, wäre ja der ganze Aufwand umsonst.&lt;br /&gt;
&lt;br /&gt;
Präemptive Multitasking Systeme sind sehr flexibel und kommen mit einer Vielzahl an Tasks klar. Amok laufende Prozesse können das System bei korrekter Implementierung nicht blockieren. Damit aber das System crash-sicher ist, muss es Systemresourcen geben, die nur der Scheduler verteilen kann (z.&amp;amp;nbsp;B. kein anderer Prozess darf in den Speicherbereich des Schedulers schreiben; kein anderer Prozess darf den Timerinterrupt des Schedulers ändern). Diese Möglichkeiten sind in Mikrocontrollern normalerweise gar nicht vorhanden, wodurch dieser Vorteil des Präemptiven MT weniger ins Gewicht fällt. Beispiele für Systeme mit präemptivem Multitasking sind Linux, *BSD  und Windows XP.&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* sehr flexibel in der Verwaltung von dynamisch ausgeführten Prozessen&lt;br /&gt;
* einzelne Prozesse können einfach linear programmiert werden, ohne die Aufgabe in kleine Teile zerlegen zu müssen&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Der Scheduler ist aufwändiger und benötigt mehr CPU-Zeit&lt;br /&gt;
* Höherer Resourcenbedarf zu Verwaltung des Systems und Bereitstellung der Semaphore etc.&lt;br /&gt;
* nicht streng deterministisch, somit kann kein festes Timing garantiert werden&lt;br /&gt;
* nicht explizit debug- und prüfbar, da die Prozesse nicht fest gekoppelt sind&lt;br /&gt;
&lt;br /&gt;
== Multithreading ==&lt;br /&gt;
&lt;br /&gt;
Multithreading ist eine meist softwarebasierende Möglichkeit moderner Betriebssysteme, innerhalb eines Prozesses mehrere Tasks (threads) parallel auszuführen. Der Vorteil dieser weiteren Unterteilung ist, dass sich die Threads eines Tasks den Speicherbereich teilen können und eine Aufteilung in logische nebeneinander laufende Teile möglich ist. Je nach Betriebssystem kann der Übergang von Multithreading zu Multiprocessing fliessend bis starr sein. &lt;br /&gt;
&lt;br /&gt;
Das Hyperthreading eines Intel Pentium 4 folgt dem Konzept des Multithreadings auf Hardwarebasis und teilt den CPU-Kern zeitlich in zwei logische Prozessoren ein.&lt;br /&gt;
&lt;br /&gt;
== Umsetzung auf Prozessoren ==&lt;br /&gt;
Unabhängig davon, ob Multitasking oder -threading auf einem Prozessor konkret unterstützt wird, lässt es sich immer in Form von Software realisieren. Dies wird in modernen Systemen durch das OS geleistet, das standardisierte Funktionen und Strukturen zur Verfügung stellt. Besonders C++ bietet ein stark abstrahiertes Programmiermodell und Methoden-Set an, um effektiv untereinander kompatible Programmmodule erstellen zu können. Nutzt man diese nicht, wie z.B. bei der Programmierung in C, müssen Strukturen manuell erzeugt und gehandhabt werden, was aufwändiger ist, aber auch geringeren overhead bewirkt. Das Programm ist dann fast immer erheblich kleiner, in den meisten Fällen strukturell einfacher, bezüglich komplizierter Änderungen jedoch auch unflexibler und träger. &lt;br /&gt;
&lt;br /&gt;
Bei Mikrocontrollern findet man je nach Komplexität und Struktur der Appliation praktisch alle denkbaren Kombinationen:&lt;br /&gt;
&lt;br /&gt;
=== System mit real-time OS und Entwicklung in C++ ===&lt;br /&gt;
* Programmentwicklung stark an abstrakte Interfaces und Standards gebunden&lt;br /&gt;
* Starke Abängigkeit an das OS im Bezug auf RT-Funktionalität&lt;br /&gt;
* Innerhalb gleicher OS-Landschaft gut portierbar&lt;br /&gt;
* Sehr geringe Abhängigkeit vom Prozessortyp&lt;br /&gt;
* Multitasking muss über OS-Schicht ausprogrammiert werden&lt;br /&gt;
* Multithreading muss/kann durch Programmierleistung optimiert werden&lt;br /&gt;
* Laufzeiteffizienz ist niedrig durch relativ hohen Anteil des OS-Bedarfs&lt;br /&gt;
* Datendurchsatz regelt sich selbst und ist gleichmässig am relativen Maximum&lt;br /&gt;
* Programmiereffizienz ist niedrig durch sehr viel Arbeit an Formalismen&lt;br /&gt;
* Planungsaufwand ist noch überschaubar&lt;br /&gt;
* Planungseffizienz ist relaiv hoch durch viele Standards&lt;br /&gt;
* Erweiterung um komplexe Module einfach möglich, Timing regelt sich selber&lt;br /&gt;
&lt;br /&gt;
=== System mit real-time OS und Entwicklung in C ===&lt;br /&gt;
* Programmentwicklung stark an Interfaces und weniger an Standards&lt;br /&gt;
* Starke Abängigkeit an das OS im Bezug auf RT-Funktionalität&lt;br /&gt;
* Innerhalb aber auch ausserhalb gleicher OS-Landschaft gut portierbar&lt;br /&gt;
* Geringe Abhängig von dem Prozessortyp&lt;br /&gt;
* Multitasking muss über OS-Schicht und eigene Strukturen programmiert werden&lt;br /&gt;
* Multithreading muss/kann durch Programmierleistung optimiert werden&lt;br /&gt;
* Laufzeiteffizienz ist höher durch geringeren Anteil des OS-Bedarfs&lt;br /&gt;
* Datendurchsatz regelt sich selbst und ist gleichmässig am relativen Maximum&lt;br /&gt;
* Programmiereffizienz ist höher durch weniger Arbeit mit Formalien&lt;br /&gt;
* Planungsaufwand ist etwas höher, je nach Applikation&lt;br /&gt;
* Planungseffizienz ist relaiv niedriger durch weniger Standards&lt;br /&gt;
* Erweiterung mit akzeptablem Aufwand möglich, Timing muss beachtet werden&lt;br /&gt;
&lt;br /&gt;
=== System ohne real-time OS und Entwicklung in C ===&lt;br /&gt;
* Programmentwicklung stark an Interfaces und kaum an Standards gebunden&lt;br /&gt;
* Ausprägung und Gestaltung der RT-Funktionalität absolut frei&lt;br /&gt;
* nur ausserhalb von OS-Landschaft portierbar, dafür prinziepiell sehr gut&lt;br /&gt;
* Stärkere Abhängig von dem Prozessortyp, kann die Portierbarkeit einschränken&lt;br /&gt;
* Multitasking muss auschlieslich durch eigene Strukturen programmiert werden&lt;br /&gt;
* Multithredding muss durch umständliche Programmierung ermöglicht werden werden&lt;br /&gt;
* Laufzeiteffizienz kann sehr hoch sein, ist aber stark von der Progr. abhängig&lt;br /&gt;
* Datendurchsatz muss selbst ins Maximum gesteuert werden, das aber höher liegt&lt;br /&gt;
* Programmiereffizienz ist hoch, dank Wegfall von Konventionen&lt;br /&gt;
* Planungsaufwand ist stark Applikations abhängig, gfs sehr viel grösser&lt;br /&gt;
* Planungseffizienz ist gering, da RT Konzept selber optmiert werden muss&lt;br /&gt;
* Erweiterung nur möglich, wenn Timing weitestgehend überarbeitet wird&lt;br /&gt;
&lt;br /&gt;
=== System ohne real-time OS und Entwicklung in ASM ===&lt;br /&gt;
* Programmentw. nur an physische Interfaces gebunden und frei von Standards&lt;br /&gt;
* Ausprägung und Gestaltung der RT-Funktionalität absolut frei&lt;br /&gt;
* nur innerhalb der Prozessorlandschaft gut portierbar&lt;br /&gt;
* Volle Abhängig vom Prozessortyp, Portierbarkeit auf andere aufwändig&lt;br /&gt;
* Multitasking muss auschlieslich durch eigene Strukturen programmiert werden&lt;br /&gt;
* Multithredding muss durch umständliche Programmierung ermöglicht werden werden&lt;br /&gt;
* Laufzeiteffizienz sehr hoch, jedoch stark von Programmierung abhängig&lt;br /&gt;
* Datendurchsatz muss selbst ins Maximum gesteuert werden, das aber höher liegt&lt;br /&gt;
* Programmiereffizienz ist hoch, dank Wegfall von Konventionen&lt;br /&gt;
* Planungsaufwand ist stark applikationsabhängig, ggfs. sehr viel grösser&lt;br /&gt;
* Planungseffizienz sehr gering, RT Konzept nur für einfache System machbar&lt;br /&gt;
* Erweiterung nur möglich, wenn Timing komplett überarbeitet wird&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Pr%E4emptives_Multitasking Präemptives Multitasking] bei [http://de.wikipedia.org Wikipedia]&lt;br /&gt;
* [http://www.femtoos.org/ Femto OS], ein ultrakompaktes Mulitaskingbetriebssystem für kleine Mikrocontroller&lt;br /&gt;
*[http://www.freertos.org/ FreeRTOS], ein freies Echtzeitbetriebssystem für Mikrocontroller&lt;br /&gt;
* [http://w3.ualg.pt/~rmarcel/Get%20by%20Without%20an%20RTOS.pdf Get by Without an RTOS] Ein schönes Beispiel wie man ohne ein RTOS auch Multitasking hinbekommt. &lt;br /&gt;
* [[TNKernel]], freier Multitasking-Kernel. &lt;br /&gt;
* [http://embeddedgurus.com/state-space/2010/04/i-hate-rtoses/ i-hate-rtoses] Blog zum Thema RTOS&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Betriebssysteme]]&lt;/div&gt;</summary>
		<author><name>134.169.116.105</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Multitasking&amp;diff=85404</id>
		<title>Multitasking</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Multitasking&amp;diff=85404"/>
		<updated>2014-10-27T13:01:21Z</updated>

		<summary type="html">&lt;p&gt;134.169.116.105: /* System mit real-time OS und Entwicklung in C++ */ Typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Multitasking bedeutet ein quasi paralleles Ausführen von mehreren Prozessen auf einem Prozessor.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Da eine echte parallele Ausführung von mehreren Prozessen (Programmen, Funktionen) auf einem einzelnen CPU-Kern nicht möglich ist, wird ein &amp;quot;Trick&amp;quot; verwendet. Dabei werden die einzelnen Prozesse jeweils nur für kurze Zeit (1..50 ms) bearbeitet und danach auf einen anderen Prozess umgeschaltet. Man spricht auch von einer verschachtelten Bearbeitung (engl. interleaving).&lt;br /&gt;
&lt;br /&gt;
Das Herz jedes Multitasking-Systems ist der Scheduler. Dieses Programm beinhaltet einen Algorithmus, der überprüft, welcher Prozess als nächstes die CPU (also Rechenzeit) zugeteilt bekommt. Es gibt verschiedene Schedulingalgorithmen:&lt;br /&gt;
&lt;br /&gt;
* First come first served: Teilt den Prozessen in der Reihenfolge Rechenzeit zu,  in der sie rechenbereit werden&lt;br /&gt;
* Shortest Job first: Der Prozess mit der kürzesten Rechenzeit wird als erstes bearbeitet. Dazu muss die Rechenzeit natürlich im Voraus bekannt sein&lt;br /&gt;
* Shortest remaining time next: Der Prozess mit der kürzesten verbleibenden Rechenzeit wird jeweils als nächstes bearbeitet. Auch hier muss diese Zeit natürlich bekannt sein&lt;br /&gt;
* Round Robin: Alle Prozesse bekommen eine gleich große Zeitscheibe zugeteilt. Der Scheduler lässt jeden Prozess für die Dauer einer Zeitscheibe rechnen, und übergibt die CPU dann an den nächsten Prozess&lt;br /&gt;
* Priority Scheduling: Anders als beim Round Robin Verfahren sind die Prozesse hier nicht gleichwertig. Prozesse haben Prioritäten, der Scheduler sorgt dafür, dass höher priorisierte Prozesse bevorzugt behandelt werden&lt;br /&gt;
&lt;br /&gt;
Natürlich sind Scheduler in freier Wildbahn nicht immer so einfach zu charakterisieren, da sie oftmals komplizierte Hybriden der genannten Techniken implementieren. Die Scheduler der &amp;quot;echten&amp;quot; Betriebsysteme (Windows, Linux, MacOS, *BSD) sind im Prinzip prioritäten-basierende Round Robin Scheduler. Generell hat ein Betriebsystem 2 Möglichkeiten, Multitasking zu realisieren, kooperativ oder präemptiv.&lt;br /&gt;
&lt;br /&gt;
== Kooperatives Multitasking ==&lt;br /&gt;
&lt;br /&gt;
Beim kooperativen Multitasking gibt der Scheduler die Kontrolle komplett an den Prozess ab. D.h., das Betriebsystem ist darauf angewiesen, dass der Prozess die Kontrolle wieder abgibt. Geschieht das nicht, wird der Scheduler nicht wieder aufgerufen und damit auch kein anderer Prozess mehr ausgeführt - das System &amp;quot;hängt&amp;quot;. Das OS ist also auf die [http://de.wikipedia.org/wiki/Kooperation Kooperation] der Prozesse angewiesen. Bekannte Beispiele für Betriebssysteme mit kooperativem Multitasking sind Windows 3.x und MacOS vor Version 10.&lt;br /&gt;
&lt;br /&gt;
Dennoch ist kooperatives Multitasking keineswegs überholt oder schlecht. Gerade im Bereich der Mikrocontroller und Echtzeitanwendungen gibt es viele Argumente, die für ein kooperatives Multitasking sprechen: Kooperatives Multitasking ist deterministischer (zeitlich und logisch vorhersagbar). Es ist besser simulierbar, d.h. für ein gegebenes System ist leichter nachweisbar, dass es funktioniert. Da es sich um geschlossene Systeme handelt, tritt das Problem, dass &amp;quot;irgendein&amp;quot; Prozess das System anhält, nicht auf. Es laufen ja im Gegensatz zum PC nicht &amp;quot;irgendwelche&amp;quot; Prozesse, sondern nur die, deren Korrektheit (hoffentlich) verifiziert &amp;amp; validiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Ein einfaches Beispiel für den AVR ===&lt;br /&gt;
&lt;br /&gt;
Hier soll ein einfaches Beispiel den Weg in die Programmierung von parallel bearbeiteten Aufgaben zeigen.&lt;br /&gt;
&lt;br /&gt;
Wichtigster Grundsatz ist die Herangehensweise! Viele Programmieranfänger haben damit Schwierigkeiten, was u.a. an den schlecht vermittelten Grundlagen liegt. Oft sieht man Funktionen zum Warten in Form von&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
while(1) {&lt;br /&gt;
    PORTD ^= (1&amp;lt;&amp;lt;PD0);&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um beispielsweise eine [[LED]] blinken zu lassen. Will man dann noch andere Dinge erledigen, wundert sich der Programmierer, warum der Mikrocontroller so langsam reagiert, trotz 16 MHz Taktfrequenz.&lt;br /&gt;
&lt;br /&gt;
==== Einfacher Ansatz ====&lt;br /&gt;
&lt;br /&gt;
Stellen wir uns vor, wir wollen drei Dinge gleichzeitig tun.&lt;br /&gt;
&lt;br /&gt;
* Eine Taste abfragen&lt;br /&gt;
* Eine LED blinken lassen, in Abhängigkeit der gedrückten Taste&lt;br /&gt;
* Daten vom UART empfangen und zum PC zurücksenden&lt;br /&gt;
&lt;br /&gt;
Ein einfacher Ansatz für die drei Dinge sieht etwa so aus. Die Beispiele wurden mit [[WinAVR]] Version 20081006 in der Optimierungsstufe -Os kompiliert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, erster Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6864 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
&lt;br /&gt;
    PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    if (taste) &lt;br /&gt;
        _delay_ms(1000);    // 1 s warten&lt;br /&gt;
    else&lt;br /&gt;
        _delay_ms(100);     // 0,1 s warten&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)));            // Warte auf empfangenes Zeichen vom UART&lt;br /&gt;
    tmp = UDR;&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));           // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
    UDR = tmp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 1;              // PC0 ist Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    &lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        taste = taste_lesen();&lt;br /&gt;
        led_blinken(taste);&lt;br /&gt;
        uart_lesen();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man das Programm nun laufen lässt, wird man feststellen dass&lt;br /&gt;
&lt;br /&gt;
* das Hyperterminal sehr langsam reagiert und bisweilen Zeichen verschluckt&lt;br /&gt;
* die LED auf Tastendrücke nur dann reagiert, wenn man per Hyperterminal Zeichen eingibt&lt;br /&gt;
&lt;br /&gt;
Dieser Ansatz ist also untauglich. Egal wie schnell unser AVR auch ist, er reagiert sehr langsam.&lt;br /&gt;
&lt;br /&gt;
==== Verbesserter Ansatz ====&lt;br /&gt;
&lt;br /&gt;
Will man mehrere Dinge gleichzeitig bearbeiten, muss man die Aufgaben in kleinste Häppchen zerteilen. Diese kleinsten Häppchen werden dann verschachtelt abgearbeitet, also ein Häppchen von Aufgabe A, ein Häppchen von Aufgabe B, ein Häppchen von Aufgabe C.&lt;br /&gt;
&lt;br /&gt;
Das Auslesen der Taste geht immer sehr schnell, kein Ansatz zum optimieren. Das Blinken der LED dauer entweder 1s oder 100ms, eine Ewigkeit für einen Mikrocontroller! Hier muss man was ändern. Am schlimmsten ist die UART-Nutzung. Der AVR wartet solange, bis ein Zeichen empfangen wurde! Das kann ewig dauern! Unser Programm steht! Das darf nicht sein!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, zweiter Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6468 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
    static uint16_t zaehler=0;&lt;br /&gt;
&lt;br /&gt;
    if (taste) {&lt;br /&gt;
        if (zaehler&amp;gt;=999) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        if (zaehler&amp;gt;=99) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    zaehler++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    if((UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC))) {                // empfangenes Zeichen abholbereit im UART ?&lt;br /&gt;
        tmp = UDR;&lt;br /&gt;
        while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));       // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
        UDR = tmp;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 1;              // PB0 ist Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
    &lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        taste = taste_lesen();&lt;br /&gt;
        led_blinken(taste);&lt;br /&gt;
        uart_lesen();&lt;br /&gt;
        _delay_ms(1);       // 1 ms warten&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses Programm reagiert &#039;&#039;&#039;ganz&#039;&#039;&#039; anders! Schnell wie der Wind und vollkommen unabhängig von anderen, parallel laufenden Prozessen. Warum ist das so ?&lt;br /&gt;
&lt;br /&gt;
Die einzelnen kleinen Häppchen sind verdaulicher als die grossen. Die maximale Durchlaufzeit der einzelnen Funktionen ist drastisch reduziert. Anstatt in der LED-Ausgabe einmal 1000 ms zu warten wird nun 1000x1ms gewartet. Zwischendurch werden aber 1000 mal die anderen Prozesse bearbeitet. Echte Demokratie sozusagen. Noch viel besser ist die Handhabung des UARTs. Anstatt eine Ewigkeit auf ein ankommendes Zeichen zu warten, wird nur dann etwas bearbeitet, wenn auch wirklich etwas zur Bearbeitung vorliegt. Klingt eigentlich logisch. Also nur dann, wenn schon ein Zeichen empfangen wurde wird es auch bearbeitet, ansonsten geht es zurück zur Hauptschleife. Das ist eigentlich der ganze &amp;quot;Trick&amp;quot; eines kooperativen Multitaskings. Auch wenn die Verwendung von _delay_ms(1) noch ein kleiner Schönheitsfehler ist, den die Profis lieber mit einem [[Timer]] erledigen, so wird das Prinzip klar.&lt;br /&gt;
&lt;br /&gt;
*Prozesse eines kooperativen Multitaskingsystems warten nicht auf das Eintreten von Ereignissen, sondern bearbeiten nur bereits eingetretene Ereignisse.&lt;br /&gt;
*Grössere Aufgaben werden in kleine Teilaufgaben zerlegt, welche nur durch mehrfaches Aufrufen der Funktion abgearbeitet werden. Das erreicht man meist am besten mit einer [[statemachine |State machine]].&lt;br /&gt;
*Prozesse eines kooperativen Multitaskings haben eine garantierte, maximale Durchlaufzeit, welche möglichst klein ist.&lt;br /&gt;
&lt;br /&gt;
Damit ähneln die Prozesse einem [[Interrupt]], auch wenn sie als ganz normale Funktionen ausserhalb eines Interrupts ausgeführt werden. An diesem Beispiel erkennt man die Vor- und Nachteile des kooperativen Multitaskings&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* einfacher Scheduler mit geringster CPU Belastung&lt;br /&gt;
* Deterministische Arbeitsweise, damit einfach prüfbar und strenges Timing möglich&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* eine andere Programmierweise zur Zerlegung größerer Aufgaben in kleine Teilaufgaben muss manuell vorgenommen werden&lt;br /&gt;
&lt;br /&gt;
==== Verbesserter Ansatz mit Timer ====&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss noch einmal die verbesserte Version mit Timer. Diese hat mehrere Vorteile.&lt;br /&gt;
&lt;br /&gt;
* Das Zeitraster der Hauptschleife ist exakt, unabhängig von der Laufzeit der Aufgaben, weil der Timer unabhängig eine feste Interruptfrequenz generiert. Im vorherigen Beispiel war das Zeitraster die Summe aus Laufzeit aller Funktionen/Tasks und dem _delay_ms(1).&lt;br /&gt;
* CPU-Rechenleistung wird zu 100% in der Abarbeitung der Task verwendet und nicht für nutzlose Warteschleifen verschwendet.&lt;br /&gt;
* Es kann leicht im realen System geprüft werden, ob die Laufzeit der Tasks klein genug ist, um den Anforderungen des Timers zu genügen.&lt;br /&gt;
&lt;br /&gt;
Diese Überprüfung kann an zwei Stellen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
* Am Ende der Hauptschleife nach Abarbeitung aller Ausgaben. Wenn hier die Variable flag_1ms schon wieder aktiv ist, dauerte die Abarbeitung länger als 1ms. Wenn man ein sehr strenges Timing sicherstellen möchte, ist das ein Fehler, der erkannt und signalisiert werden kann.&lt;br /&gt;
* In der ISR. Wenn hier die Variable immer noch aktiv ist, wurde sie von der Hauptschleife noch nicht erkannt und zurück gesetzt. Das ist definitiv ein Fehler, denn jetzt würde ohne Fehlererkennung ein Timerdurchlauf von der Hauptschleife verschluckt werden. Diese Prüfung ist etwas nachgiebiger, weil zwischenzeitlich ein Durchlauf der Hauptschleife mehr als 1ms, jedoch nicht länger als 2ms dauern darf. Siehe auch den Abschnitt [[Interrupt#Zeitverhalten_eines_Timerinterrupts]]. Sinnvollerweise nutzt man nur eine der beiden Prüfungen, nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Multitasking Demo, dritter Versuch&lt;br /&gt;
&lt;br /&gt;
ATmega32 @ 3,6468 MHz&lt;br /&gt;
&lt;br /&gt;
LED + 1KOhm Vorwiderstand an PB0 und PB1&lt;br /&gt;
Taster nach GND an PA0&lt;br /&gt;
UART an RXD und TXD&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;avr/io.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util/delay.h&amp;quot;&lt;br /&gt;
#include &amp;quot;avr/interrupt.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t taste_lesen(void) {&lt;br /&gt;
    if (PINA &amp;amp; (1&amp;lt;&amp;lt;PA0))&lt;br /&gt;
        return 1;&lt;br /&gt;
    else &lt;br /&gt;
        return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led_blinken(uint8_t taste) {&lt;br /&gt;
    static uint16_t zaehler=0;&lt;br /&gt;
&lt;br /&gt;
    if (taste) {&lt;br /&gt;
        if (zaehler&amp;gt;=999) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        if (zaehler&amp;gt;=99) {&lt;br /&gt;
            PORTB ^= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
            zaehler=0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    zaehler++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_lesen(void) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
    if((UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC))) {                // empfangenes Zeichen abholbereit im UART ?&lt;br /&gt;
        tmp = UDR;&lt;br /&gt;
        while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)));       // Warte auf freien Sendepuffer vom UART&lt;br /&gt;
        UDR = tmp;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t flag_1ms;&lt;br /&gt;
 &lt;br /&gt;
int main(void) {&lt;br /&gt;
    int8_t taste;&lt;br /&gt;
&lt;br /&gt;
    // IOs initialisieren&lt;br /&gt;
    &lt;br /&gt;
    PORTA = 1;              // Pull Up für PA0&lt;br /&gt;
    DDRB  = 3;              // PB0/1 sind Ausgang&lt;br /&gt;
&lt;br /&gt;
    // UART initialisieren&lt;br /&gt;
    &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
    // Timer 0 initialisieren, CTC, Presacler 64&lt;br /&gt;
&lt;br /&gt;
    TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01) | (1&amp;lt;&amp;lt;CS00);&lt;br /&gt;
    OCR0  = 56;      // 1ms&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;OCIE0);&lt;br /&gt;
&lt;br /&gt;
    // Interrupts global freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
    // Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while (1) {&lt;br /&gt;
        if (flag_1ms) {&lt;br /&gt;
            flag_1ms=0;&lt;br /&gt;
            taste = taste_lesen();&lt;br /&gt;
            led_blinken(taste);&lt;br /&gt;
            uart_lesen();&lt;br /&gt;
            if (flag_1ms) {&lt;br /&gt;
                // Laufzeit der Tasks &amp;gt;1ms, Fehlersignalisierung&lt;br /&gt;
                // PB1 auf HIGH, Programm stoppen&lt;br /&gt;
                PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
                while(1);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Interruptserviceroutine für Timer 0&lt;br /&gt;
// hier 1ms&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_COMP_vect) {&lt;br /&gt;
    if (flag_1ms) {&lt;br /&gt;
        // Laufzeit der Tasks &amp;gt;2ms, Fehlersignalisierung&lt;br /&gt;
        // PB1 auf HIGH, Programm stoppen&lt;br /&gt;
        PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
        while(1);&lt;br /&gt;
    }&lt;br /&gt;
    flag_1ms = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Message passing Framework ====&lt;br /&gt;
&lt;br /&gt;
Im vorangegangenen Abschnitt wird erklärt, wie man die einzelnen &amp;quot;Tasks&amp;quot; in kleine Häppchen Zerlegen kann und diese alle innerhalb der Main Loop aufruft.&lt;br /&gt;
Dieses kooperative System hat aber noch einige Nachteile:&lt;br /&gt;
&lt;br /&gt;
* Alle Häppchen werden gleich oft aufgerufen und nicht nur bei Bedarf&lt;br /&gt;
* Für die Timeouts gibt es noch keine befriedigende Lösung&lt;br /&gt;
&lt;br /&gt;
Wenn man diese beiden Nachteile auch noch lösen möchte, wird das ganze noch ein klein wenig komplizierter. Da man das Grundprinzip aber für viele Mikrocontroller Projekte immer wieder verwenden kann, lohnt es sich und man kann die Entwicklung für diese Art des Multitasking in einem Framework zusammenfassen.&lt;br /&gt;
&lt;br /&gt;
Ein Framework, das sind einige Dateien, die den Rahmen (Frame=Rahmen) für ein Programm bilden und den Teil enthalten, den man immer wieder braucht.&lt;br /&gt;
&lt;br /&gt;
===== Message =====&lt;br /&gt;
&lt;br /&gt;
Die Basis des Frameworks bildet die &amp;quot;Message&amp;quot;. Immer wenn wir für einen unserer &amp;quot;Tasks&amp;quot; etwas zu tun haben, schicken wir eine &amp;quot;Message&amp;quot;. Die &amp;quot;Messages&amp;quot; werden in eine Warteschlange einsortiert und der Reihe nach abgearbeitet. Dadurch kommt ein Task der viel zu tun hat (viele Messages bekommt) öfter dran, als ein &amp;quot;Task&amp;quot; der nicht so viel zu tun hat. Außerdem kann eine Message noch Daten enthalten (z.B. ein empfangenes Zeichen). So können die einzelnen Tasks sogar Daten austauschen.&lt;br /&gt;
&lt;br /&gt;
===== Message Receiver =====&lt;br /&gt;
&lt;br /&gt;
Unsere &amp;quot;Tasks&amp;quot; werden immer dann aufgerufen, wenn Arbeit für sie da ist. Das wissen wir, weil sie eine Message empfangen sollen. Deshalb heißen die &amp;quot;Tasks&amp;quot; ab jetzt &amp;quot;Message Receiver&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===== Timeout =====&lt;br /&gt;
&lt;br /&gt;
In diesem System wird jeder Message Receiver aufgerufen, wenn jemand Arbeit für ihn hat und er deshalb eine Message bekommt. Was aber, wenn keine Message kommt, oder ein Message Receiver selbst aktiv werden soll?&lt;br /&gt;
&lt;br /&gt;
Aus diesem Grund braucht es die Timeouts. Mit Hilfe &amp;lt;u&amp;gt;eines&amp;lt;/u&amp;gt; Hardware Timers wird eine &amp;quot;Systemzeit&amp;quot; programmiert. Jeder Message Receiver kann die Zeit angeben, wann er wieder aufgerufen werden muss. Das Framework verwaltet alle Timer und sendet den Message Receivern eine &amp;quot;Timeout&amp;quot; message, wenn ihre Zeit gekommen ist.&lt;br /&gt;
&lt;br /&gt;
===== Beispiel =====&lt;br /&gt;
Hier der Sourcecode eines solchen Framework [[Datei:ACF.zip]]&lt;br /&gt;
Das Framework implementiert die Message Warteschlange und die Timer Warteschlange. Die Prozessor spezifischen Dinge sind in der Datei &amp;quot;ACF_Hal.c&amp;quot; zusammengefasst und für Linux Desktop und atMega128 implementiert.&lt;br /&gt;
In dieser Datei kann man das ganze auch auf andere Prozessoren anpassen.&lt;br /&gt;
&lt;br /&gt;
Ein &amp;quot;main&amp;quot; sieht dann z.B. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;ACF.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv)&lt;br /&gt;
{&lt;br /&gt;
    ACF_init();&lt;br /&gt;
    ACF_loop();&lt;br /&gt;
    return 0; // we will never arrive here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Tracing =====&lt;br /&gt;
Es gibt noch einen weiteren Grund, sich ein &amp;quot;Framework&amp;quot; zu erarbeiten, oder ein fertiges Framework zu verwenden. Da der Ablauf der Software und der Aufruf aller Teile vom Framework bestimmt wird, kann das Framework auch einen sehr detaillierten Trace über das Verhalten des Codes anfertigen. Das Framework aus vorstehendem Beispiel enthält bereits entsprechenden Code.&lt;br /&gt;
&lt;br /&gt;
Solche Traces von laufendem Code können gerade dann sehr hilfreich sein, wenn viele Dinge gleichzeitig ablaufen (und das war schließlich der Sinn des ganzen).&lt;br /&gt;
&lt;br /&gt;
Nachstehendes Bild zeigt den Trace eines Reglers, der mit dem Framework realisiert wurde.&lt;br /&gt;
[http://www.mikrocontroller.net/attachment/74409/ablauf.png Sequenzdiagramm]&lt;br /&gt;
&lt;br /&gt;
== Präemptives Multitasking ==&lt;br /&gt;
&lt;br /&gt;
Beim präemptiven Multitasking gibt das OS die Kontrolle zu keinem Zeitpunkt auf. Ein Prozess, der gerade die CPU nutzt, kann jederzeit wieder vom Betriebssystem unterbrochen werden. Daher muss bei der Entwicklung für ein präemptives System immer damit gerechnet werden, dass ein Prozess &#039;&#039;&#039;jederzeit&#039;&#039;&#039; unterbrochen werden kann. Das kann z.&amp;amp;nbsp;B. zu Problemen beim Zugriff auf limitierte Betriebsmittel führen. Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Prozess A sucht freien Speicher und findet einen freien Block&lt;br /&gt;
* Prozess B wird vom Scheduler gestartet und sucht ebenfalls einen Speicherblock. Der gefundene Block wird von Prozess B reserviert und benutzt&lt;br /&gt;
* Der Scheduler teilt wieder Prozess A die CPU zu. Prozess A wird fortgeführt, d.h. er reserviert jetzt den im letzten Systemcall gefundenen Speicherblock&lt;br /&gt;
Jetzt haben also beide Prozesse den gleichen Speicherblock reserviert. Entweder arbeiten jetzt beide Prozesse mit dem gleichen Speicher, und überschreiben daher gegenseitig die Daten, oder das Betriebsystem hat etwas gemerkt und zieht die Notbremse. In jedem Fall passieren schreckliche Dinge. Sowas nennt man eine Race-Condition.&lt;br /&gt;
&lt;br /&gt;
Die Lösung nennt sich Semaphore: Dieser Mechanismus wird vom Betriebsystem bereitgestellt und erlaubt es einem Prozess eine bestimmte Ressource zu sperren. Wenn also Prozess A aus obigem Beispiel Speicher haben möchte, setzt er vor Beginn der sogenannten &amp;quot;Kritischen Sektion&amp;quot; einen Semaphor für &amp;quot;Speicher reservieren&amp;quot;. Dieser Semaphor wird erst wieder aufgehoben, sobald Prozess A den Speicher für sich reserviert hat. Wenn der Prozess B zwischendurch gestartet wird und ebenfalls versucht den Semaphor zu setzen, wird er solange warten müssen, bis Prozess A den Semaphor wieder freigibt. Speziell für derartige Locking Mechanismen bieten die meisten Prozessoren sogenannte TAS-Befehle (Test And Set), die in einem Prozessorbefehl eine Variable testen und je nach Ergebnis setzen können. Das ist nötig um das Setzen von Semaphoren unteilbar (atomar) zu machen. Könnte der Scheduler das Setzen eines Semaphors unterbrechen, wäre ja der ganze Aufwand umsonst.&lt;br /&gt;
&lt;br /&gt;
Präemptive Multitasking Systeme sind sehr flexibel und kommen mit einer Vielzahl an Tasks klar. Amok laufende Prozesse können das System bei korrekter Implementierung nicht blockieren. Damit aber das System crash-sicher ist, muss es Systemresourcen geben, die nur der Scheduler verteilen kann (z.&amp;amp;nbsp;B. kein anderer Prozess darf in den Speicherbereich des Schedulers schreiben; kein anderer Prozess darf den Timerinterrupt des Schedulers ändern). Diese Möglichkeiten sind in Mikrocontrollern normalerweise gar nicht vorhanden, wodurch dieser Vorteil des Präemptiven MT weniger ins Gewicht fällt. Beispiele für Systeme mit präemptivem Multitasking sind Linux, *BSD  und Windows XP.&lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* sehr flexibel in der Verwaltung von dynamisch ausgeführten Prozessen&lt;br /&gt;
* einzelne Prozesse können einfach linear programmiert werden, ohne die Aufgabe in kleine Teile zerlegen zu müssen&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
* Der Scheduler ist aufwändiger und benötigt mehr CPU-Zeit&lt;br /&gt;
* Höherer Resourcenbedarf zu Verwaltung des Systems und Bereitstellung der Semaphore etc.&lt;br /&gt;
* nicht streng deterministisch, somit kann kein festes Timing garantiert werden&lt;br /&gt;
* nicht explizit debug- und prüfbar, da die Prozesse nicht fest gekoppelt sind&lt;br /&gt;
&lt;br /&gt;
== Multithreading ==&lt;br /&gt;
&lt;br /&gt;
Multithreading ist eine meist softwarebasierende Möglichkeit moderner Betriebssysteme, innerhalb eines Prozesses mehrere Tasks (threads) parallel auszuführen. Der Vorteil dieser weiteren Unterteilung ist, dass sich die Threads eines Tasks den Speicherbereich teilen können und eine Aufteilung in logische nebeneinander laufende Teile möglich ist. Je nach Betriebssystem kann der Übergang von Multithreading zu Multiprocessing fliessend bis starr sein. &lt;br /&gt;
&lt;br /&gt;
Das Hyperthreading eines Intel Pentium 4 folgt dem Konzept des Multithreadings auf Hardwarebasis und teilt den CPU-Kern zeitlich in zwei logische Prozessoren ein.&lt;br /&gt;
&lt;br /&gt;
== Umsetzung auf Prozessoren ==&lt;br /&gt;
Unabhängig davon, ob Multitasking oder -threading auf einem Prozessor konkret unterstützt wird, lässt es sich immer in Form von Software realisieren. Dies wird in modernen Systemen durch das OS geleistet, das standardisierte Funktionen und Strukturen zur Verfügung stellt. Besonders C++ bietet ein stark abstrahiertes Programmiermodell und Methoden-Set an, um effektiv untereinander kompatible Programmmodule erstellen zu können. Nutzt man diese nicht, wie z.B. bei der Programmierung in C, müssen Strukturen manuell erzeugt und gehandhabt werden, was aufwändiger ist, aber auch geringeren overhead bewirkt. Das Programm ist dann fast immer erheblich kleiner, in den meisten Fällen strukturell einfacher, bezüglich komplizierter Änderungen jedoch auch unflexibler und träger. &lt;br /&gt;
&lt;br /&gt;
Bei Mikrocontrollern findet man je nach Komplexität und Struktur der Appliation praktisch alle denkbaren Kombinationen:&lt;br /&gt;
&lt;br /&gt;
=== System mit real-time OS und Entwicklung in C++ ===&lt;br /&gt;
* Programmentwicklung stark an abstrakte Interfaces und Standards gebunden&lt;br /&gt;
* Starke Abängigkeit an das OS im Bezug auf RT-Funktionalität&lt;br /&gt;
* Innerhalb gleicher OS-Landschaft gut portierbar&lt;br /&gt;
* Sehr geringe Abhängigkeit vom Prozessortyp&lt;br /&gt;
* Multitasking muss über OS-Schicht ausprogrammiert werden&lt;br /&gt;
* Multithreading muss/kann durch Programmierleistung optimiert werden&lt;br /&gt;
* Laufzeiteffizienz ist niedrig durch relativ hohen Anteil des OS-Bedarfs&lt;br /&gt;
* Datendurchsatz regelt sich selbst und ist gleichmässig am relativen Maximum&lt;br /&gt;
* Programmiereffizienz ist niedrig durch sehr viel Arbeit an Formalismen&lt;br /&gt;
* Planungsaufwand ist noch überschaubar&lt;br /&gt;
* Planungseffizienz ist relaiv hoch durch viele Standards&lt;br /&gt;
* Erweiterung um komplexe Module einfach möglich, Timing regelt sich selber&lt;br /&gt;
&lt;br /&gt;
=== System mit real-time OS und Entwicklung in C ===&lt;br /&gt;
* Programmentwicklung stark an Interfaces und weniger an Standards&lt;br /&gt;
* Starke Abängigkeit an das OS im Bezug auf RT-Funktionalität&lt;br /&gt;
* Innerhalb aber auch ausserhalb gleicher OS-Landschaft gut portierbar&lt;br /&gt;
* Geringe Abhängig von dem Prozessortyp&lt;br /&gt;
* Multitasking muss über OS-Schicht und eigene Strukturen programmiert werden&lt;br /&gt;
* Multithredding muss/kann durch Programmierleistung optimiert werden&lt;br /&gt;
* Laufzeiteffizienz ist höher durch geringeren Anteil des OS-Bedarfs&lt;br /&gt;
* Datendurchsatz regelt sich selbst und ist gleichmässig am relativen Maximum&lt;br /&gt;
* Programmiereffizienz ist höher durch weniger Arbeit mit Formalien&lt;br /&gt;
* Planungsaufwand ist etwas höher, je nach Applikation&lt;br /&gt;
* Planungseffizienz ist relaiv niedriger durch weniger Standards&lt;br /&gt;
* Erweiterung mit akzeptablem Aufwand möglich, Timing muss beachtet werden&lt;br /&gt;
&lt;br /&gt;
=== System ohne real-time OS und Entwicklung in C ===&lt;br /&gt;
* Programmentwicklung stark an Interfaces und kaum an Standards gebunden&lt;br /&gt;
* Ausprägung und Gestaltung der RT-Funktionalität absolut frei&lt;br /&gt;
* nur ausserhalb von OS-Landschaft portierbar, dafür prinziepiell sehr gut&lt;br /&gt;
* Stärkere Abhängig von dem Prozessortyp, kann die Portierbarkeit einschränken&lt;br /&gt;
* Multitasking muss auschlieslich durch eigene Strukturen programmiert werden&lt;br /&gt;
* Multithredding muss durch umständliche Programmierung ermöglicht werden werden&lt;br /&gt;
* Laufzeiteffizienz kann sehr hoch sein, ist aber stark von der Progr. abhängig&lt;br /&gt;
* Datendurchsatz muss selbst ins Maximum gesteuert werden, das aber höher liegt&lt;br /&gt;
* Programmiereffizienz ist hoch, dank Wegfall von Konventionen&lt;br /&gt;
* Planungsaufwand ist stark Applikations abhängig, gfs sehr viel grösser&lt;br /&gt;
* Planungseffizienz ist gering, da RT Konzept selber optmiert werden muss&lt;br /&gt;
* Erweiterung nur möglich, wenn Timing weitestgehend überarbeitet wird&lt;br /&gt;
&lt;br /&gt;
=== System ohne real-time OS und Entwicklung in ASM ===&lt;br /&gt;
* Programmentw. nur an physische Interfaces gebunden und frei von Standards&lt;br /&gt;
* Ausprägung und Gestaltung der RT-Funktionalität absolut frei&lt;br /&gt;
* nur innerhalb der Prozessorlandschaft gut portierbar&lt;br /&gt;
* Volle Abhängig vom Prozessortyp, Portierbarkeit auf andere aufwändig&lt;br /&gt;
* Multitasking muss auschlieslich durch eigene Strukturen programmiert werden&lt;br /&gt;
* Multithredding muss durch umständliche Programmierung ermöglicht werden werden&lt;br /&gt;
* Laufzeiteffizienz sehr hoch, jedoch stark von Programmierung abhängig&lt;br /&gt;
* Datendurchsatz muss selbst ins Maximum gesteuert werden, das aber höher liegt&lt;br /&gt;
* Programmiereffizienz ist hoch, dank Wegfall von Konventionen&lt;br /&gt;
* Planungsaufwand ist stark applikationsabhängig, ggfs. sehr viel grösser&lt;br /&gt;
* Planungseffizienz sehr gering, RT Konzept nur für einfache System machbar&lt;br /&gt;
* Erweiterung nur möglich, wenn Timing komplett überarbeitet wird&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Pr%E4emptives_Multitasking Präemptives Multitasking] bei [http://de.wikipedia.org Wikipedia]&lt;br /&gt;
* [http://www.femtoos.org/ Femto OS], ein ultrakompaktes Mulitaskingbetriebssystem für kleine Mikrocontroller&lt;br /&gt;
*[http://www.freertos.org/ FreeRTOS], ein freies Echtzeitbetriebssystem für Mikrocontroller&lt;br /&gt;
* [http://w3.ualg.pt/~rmarcel/Get%20by%20Without%20an%20RTOS.pdf Get by Without an RTOS] Ein schönes Beispiel wie man ohne ein RTOS auch Multitasking hinbekommt. &lt;br /&gt;
* [[TNKernel]], freier Multitasking-Kernel. &lt;br /&gt;
* [http://embeddedgurus.com/state-space/2010/04/i-hate-rtoses/ i-hate-rtoses] Blog zum Thema RTOS&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Betriebssysteme]]&lt;/div&gt;</summary>
		<author><name>134.169.116.105</name></author>
	</entry>
</feed>