praktikumhandleiding

Commentaren

Transcriptie

praktikumhandleiding
Practicum Handleiding IN2305-II
M. Dufour, A.J.C. van Gemund
Embedded Software Lab
Faculteit Elektrotechniek, Wiskunde en Informatica, TU Delft
November 2006
Voorwoord
Het practicum bij IN2305-II Embedded Programming heeft als doel je hands-on ervaring bij te
brengen met het programmeren van embedded programma’s die tijd-kritisch, I/O-intensief en concurrent zijn. Omdat er in totaal drie IN2305 practica zijn is het practicum bewust klein gehouden.
Verspreid over drie dagdelen zul je een serie aaneengesloten kleine opdrachten uitvoeren, welke
leiden tot het ontwerp van een simpele “cruise control” applicatie.
Deze handleiding is in zijn eerste editie, geschreven in de aanloop naar de eerste editie van het
vak IN2305-II. Veel van de gebruikte hardware (motoropstellingen) en software (uC/OS/X32 port,
debugger) is daarnaast onlangs, speciaal voor het practicum, door de schrijvers ontwikkeld. De X32
betreft het afstudeerwerk van Sijmen Woutersen. Ter ondersteuning van genoemde software heeft
hij deze meerdere malen uitgebreid en verbeterd.
De schrijvers hopen dat het resultaat van hun inspanningen bijdraagt tot een leuk en leerzaam
practicum. We bedanken degenen die hebben bijgedragen aan dit resultaat: Sijmen Woutersen,
voor het ontwikkelen van de X32 soft core en toolset, die ook in andere projecten worden gebruikt,
en voor het helpen debuggen van de uC/OS/X32 port; en de huidige student-assistenten Joost
Heijkoop en Thomas Schaap voor het geven van waardevolle feedback op deze eerste versie van de
handleiding.
M. Dufour en A.J.C. van Gemund
Delft, November 2006
i
Inhoudsopgave
1 Inleiding
1
2 Hardware
2.1 FPGA Bordje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 X32 Soft Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 Signalen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
2
2
3
3 Software
3.1 uC/OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Monitor Taak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 X32 Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
4
4
4
4 Opdracht
5
5 Dagdeel 1
6
6 Dagdeel 2
7
7 Dagdeel 3
8
A Practicum Software
10
A.1 Installatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
A.2 Gebruik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
A.3 X32 Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
B UNIX Tutorial
12
B.1 UNIX Commando’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
B.2 Output Redirection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
C X32 Tutorial
15
D uC/OS Tutorial
16
D.1 Gebruik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
D.2 uC/OS Functies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
ii
1
Inleiding
Bij dit practicum zul je, verspreid over drie dagdelen, stap-voor-stap een “cruise control” applicatie
ontwikkelen. Een cruise control systeem stelt de bestuurder van een auto in staat om automatisch
een bepaalde snelheid (“cruising speed”) aan te houden [1]. Afhankelijk van wind, (wrijvings-)weerstand en zwaartekracht (denk aan een heuvel) stuurt het systeem constant de motor bij (“regelt”
het de motor), zodanig dat de snelheid van de auto min of meer constant blijft (Figuur 1.1).
Figuur 1.1: Een cruise control systeem
Om de echte situatie zo dicht mogelijk te benaderen, gebruik je bij het practicum “embedded
hardware” om een motortje aan te sturen, waaraan een klein aluminium “wiel” is bevestigd
(Figuur 1.2). De effecten van wind etc. zul je zelf na moeten bootsen, door het wiel met de hand
af te remmen (of door het extra snelheid mee te geven).
De embedded hardware bestaat uit een FPGA bordje met enkele knoppen, leds en een display
waarmee je getallen kunt weergeven. Het FPGA bordje is aangesloten op de motor, en ontvangt
hiervan een signaal dat de huidige snelheid aangeeft (het “sensor pad”); het stuurt een signaal
terug dat het vermogen naar de motor bepaalt (het “actuator pad”). De knoppen op het bordje
gebruik je om de autobesturing na te bootsen, inclusief cruise control functie; het display gebruik
je om genoemde signalen weer te geven.
Figuur 1.2: De hardware zoals bij het practicum
Embedded systemen worden traditioneel geprogrammeerd in C. In geval van harde tijdslimieten
(“real-time constraints”) wordt er hierbij veelal gebruik gemaakt van een real-time kernel, welke
het eenvoudiger maakt om meerdere taken “tegelijk” uit te voeren en te synchroniseren, zodat
aan deze tijdslimieten wordt voldaan. Doordat programma’s eenvoudiger worden en explicieter
met synchronisatie omgaan, worden ze ook leesbaarder en gemakkelijker te “porten” naar andere
platforms. Een en ander leidt er toe dat je bij dit practicum een C programma zult ontwikkelen,
dat gebruik maakt van een real-time kernel. De real-time kernel die je zult gebruiken is uC/OS, de
kernel die in het boek bij het vak wordt besproken.
2
Hardware
Op het FPGA bordje bevindt zich een 32-bits “soft core” (processor), genaamd X32 [2]. Aan de
motor is een sensor (“encoder”) bevestigd, die de huidige snelheid van het wiel doorgeeft aan het
FPGA bordje, en een actuator, waarmee het FPGA bordje het vermogen naar de motor kan regelen
(Figuur 1.2).
1
2.1
FPGA Bordje
Het FPGA bordje dat je zult gebruiken is een Xilinx Spartan-3 Starter Board (Figuur 2.1). De
FPGA chip op dit bordje bevat 400,000 configureerbare gates en draait met een kloksnelheid van
50 MHz. Op het bordje is een geheugen module aanwezig met 1 MB RAM en een flash geheugen
module met daarin de huidige hardware beschrijving. Telkens wanneer je het FPGA bordje reset
wordt de FPGA chip automatisch volgens deze beschrijving geconfigureerd.
Het bordje bevat een keur aan knopjes, leds, switches en I/O poorten (seriele poort, VGA out,
PS/2 en wat algemene poorten), welke vanzelfsprekend allen door de FPGA chip “gebruikt” kunnen
worden. Er is ook een “seven-segment display” (SSD) aanwezig, waarmee je (maximaal) 16-bits
getallen weer kunt geven. Verder is er een speciale “JTAG” aansluiting aanwezig om, vanaf een
host PC, een hardware beschrijving in het flash geheugen te schrijven.
Figuur 2.1: Xilinx Spartan-3 Starter Board
2.2
X32 Soft Core
De X32 soft core is een experimentele 32-bit processor, ontwikkeld door een afstudeerder aan het
Embedded Software Lab [2]. Het heeft een eenvoudige instructie set, gebaseerd op het “intermediate” bytecode formaat van de LCC C-compiler. Dit bytecode formaat is stack-gebaseerd (vgl. Java
en Python bytecode), waardoor de meeste operaties worden uitgevoerd m.b.v. een stack in het
geheugen, in plaats van via registers. Om bijvoorbeeld twee getallen op te tellen, worden deze
getallen eerst op de stack gezet, waarna een optel instructie ze vervangt door hun som.
Het stack-gebaseerde karakter van de instructie set heeft als gevolg dat er meerdere instructies
nodig zijn voor simpele operaties, zoals optellen, en dat er constant met het geheugen gecommuniceerd moet worden (i.v.m. de stack). De X32 is dan ook niet ontwikkeld voor snelheid, maar om
de software chain eenvoudiger te maken door bytecode direct uit te voeren. De snelheid waarmee
instructies op het gebruikte FPGA bordje, dat zoals gezegd op 50 MHz loopt, worden uitgevoerd
ligt rond de 4 MIPS (miljoen instructies per seconde).
Omdat het in real-time systemen vaak belangrijk is om zeer snel te reageren op bepaalde “events”
(denk aan een “meltdown”), kun je op de X32 aan iedere interrupt softwarematig een bepaalde prioriteit toekennen. Interrupts met een hogere prioriteit kunnen interrupts met een lagere prioriteit
onderbreken. De ISR van de interrupt met de lagere prioriteit wordt hierbij stopgezet, en weer
voortgezet wanneer de betreffende interrupt de hoogste prioriteit heeft van alle “wachtende” interrupts (interrupts kunnen dus “genest” zijn).
In Appendix C vind je uitleg over hoe je de X32 kunt gebruiken. Voor een meer uitgebreide
behandeling verwijzen we je naar de X32 Programmers Manual [2].
2
2.3
Signalen
De encoder stuurt twee signalen naar het FPGA bordje, waaruit af te leiden is met welke snelheid
en in welke richting het wiel draait (het “sensor pad”). Het FPGA bordje stuurt een signaal terug,
dat het vermogen naar de motor aangeeft (het “actuator pad”; Figuur 1.2). Als je naar de kabel
tussen het FPGA bordje en de motor kijkt, zie je dan ook vier pinnetjes (een wordt gebruikt voor
aarde).
De signalen die de encoder genereert noemen we a en b. Beide zijn in feite hetzelfde, maar
onderling in fase verschoven, signaal (Figuur 2.2). Een fase verschil van 90 of -90 graden geeft de
richting aan waarin het wiel draait. De encoder geeft een puls voor ieder bepaald aantal minuten
dat het wiel draait. De snelheid van het wiel kan afgeleid worden door het aantal pulsjes per
tijdseenheid te tellen.
Figuur 2.2: Voorbeeld a en b signalen (tijdens afremmen)
De X32 is ingesteld om op iedere “flank” van a en b (d.w.z., telkens wanneer a of b verandert),
een interrupt te genereren. Tijdens het practicum zul je hier gebruik van maken om een software
“decoder” te schrijven. De decoder telt het aantal (gecombineerde) puls “cycli”, en zorgt dat er
een error ledje aan gaat wanneer er een fout optreedt (bijv. doordat er een flank wordt gemist).
Op maximale snelheid zitten er maar enkele microseconden tussen opeenvolgende interrupts.
Omdat de X32 op ongeveer 4 MIPS loopt, betekent dit dat de software decoder de pulsjes vanaf
een bepaalde snelheid niet meer bij kan houden en het error ledje aan gaat. Verderop in het
practicum zul je daarom de software decoder vervangen door een kant-en-klare hardware (VHDL)
decoder, die meegeleverd wordt met de X32 en die de signalen gemakkelijk bij kan houden.
Het signaal dat het FPGA bordje naar de motoropstelling terug stuurt noemen we m. Dit is een
zogeheten “pulse width modulated” (PWM) signaal. Een PWM signaal bestaat uit een blokgolf
met een vaste periode, maar met een variabele puls breedte (Figuur 2.3). Omdat het (net zoals het
decoderen van a en b) te ”duur” is om een (hoog-frequent) PWM signaal in software te realiseren,
gebruik je bij het practicum een kant-en-klare hardware PWM generator, die eveneens meegeleverd
wordt met de X32.
Figuur 2.3: Voorbeeld m signaal (tijdens optrekken)
In Appendix C vind je uitleg over hoe je met beide signalen en genoemde hardware componenten
kunt omgaan.
3
Software
Tijdens het practicum gebruik je de uC/OS real-time kernel, welke is aangepast (“geport”) om op
de X32 te draaien [4]. Je krijgt hierbij een kant-en-klare “monitor” taak kado, waarmee je uC/OS
applicaties in real-time kunt bekijken en zelfs op een rudimentaire manier kunt beinvloeden. Er is
ook een meer geavanceerde monitor beschikbaar, in de vorm van een debugger.
3
3.1
uC/OS
uC/OS [4] is de real-time kernel die besproken wordt in het boek bij het vak. Het biedt de typische
functionaliteit die je verwacht van een simpele real-time kernel, zoals multitasking, een “delay”
functie, semaforen, queues en mailboxes.
uC/OS is op twee belangrijke manieren beperkt. Allereerst mag je twee taken niet dezelfde prioriteit geven. Hierdoor kan de kernel eenvoudiger bepalen welke taak er aan de beurt is. Daarnaast
heeft uC/OS vrijwel geen voorzieningen op het gebied van interrupt afhandeling. Hierdoor ben je
genoodzaakt dit via de standaard (low-level) X32 library te doen, en in iedere interrupt service
routine (ISR) aan uC/OS door te geven dat het tijdelijk geen context switch mag uitvoeren (dit is
immers niet wenselijk in een ISR [6]).
In Appendix D vind je meer details over het gebruiken van ISR’s in uC/OS applicaties en een
overzicht van de uC/OS functies die je bij het practicum kunt gebruiken.
3.2
Monitor Taak
Een monitor stelt je in staat om in het geheugen van een programma rond te kijken, en dit eventueel
op plekken aan te passen, terwijl het programma draait. Het bevat daarnaast een “sense” functie,
waarmee je kunt laten zien hoe een programma variabele in de tijd verandert.
Een monitor werkt in principe op (unsigned) byte niveau. Om een variabele in het geheugen
te bekijken, is het dus handig om te weten hoeveel bytes een bepaald type variabele inneemt, en
hoe (signed) waarden worden opgeslagen. Daarnaast heeft een monitor in principe geen inzicht in
de oorspronkelijke C code. Dit betekent dat je de adressen van variabelen zelf dient uit te zoeken,
bijvoorbeeld met behulp van de “debug output” bij je programma. Dit is een overzicht dat je kunt
laten genereren tijdens het compileren, en dat ondermeer aangeeft waar elke C variabele zich in het
geheugen bevindt.
Een monitor kan op verschillende manieren met programma’s gecombineerd worden. In geval
van een real-time kernel ligt het echter voor de hand om hier een low-priority taak voor te gebruiken.
De kernel zorgt er dan voor dat de monitor alleen wordt geexecuteerd wanneer het verder niets te
doen heeft.
Bij het practicum krijg je een kant-en-klare monitor taak kado. Deze gebruik je om een aantal
variabelen (parameters van de controller) te optimaliseren, zonder het programma telkens opnieuw
te hoeven compileren en uploaden.
3.3
X32 Debugger
Hoewel een monitor nuttig kan zijn in eenvoudige situaties, wordt het al gauw onhandig en langzaam
in het gebruik. Enkele problemen zijn dat het erg low-level is, je de relatie met de C source code
zelf moet leggen en een monitor zelf aardig wat geheugen kan innemen (doordat deze eveneens op
de embedded hardware draait). Bovendien kun je weliswaar het geheugen bekijken en veranderen,
maar eigenlijk zou je ook het programma zelf willen kunnen bekijken en (meer direct) beinvloeden.
Een “debugger” is in een meer geavanceerde tool, die je in staat stelt om programma executie
te “bevriezen”, wanneer het een bepaalde instructie bereikt (een “break point”). Hierdoor kun je
een programma gecontroleerd uitvoeren, bijv. door van C regel naar C regel te “springen”. Telkens
wanneer het programma stil staat, kun je nu rustig (vgl. een monitor) bekijken wat er in het
geheugen veranderd is.
Een goede debugger heeft nog enkele andere belangrijke eigenschappen. In de eerste plaats
kun je deze volledig aan de hand van de oorspronkelijke C code gebruiken. Dit betekent dat
aangegeven is waar in de C code het programma zich “bevindt”, en dat je gewoon de naam van een
variabele kunt gebruiken, zonder het adres te hoeven opzoeken. Daarnaast kan een goede debugger
C expressies (inclusief assignment) voor je evalueren. Dit betekent ondermeer dat je de waarde van
een variabele simpelweg kunt veranderen met een commando zoals a = *b+2.
4
Tijdens het practicum kun je eventueel gebruik maken van een grafische debugger, die ontwikkeld
is om C programma’s op de X32 te debuggen [5]. Deze draait “remote” vanaf de host PC, en communiceert met de X32 bootloader om break points te zetten en in het geheugen te lezen/schrijven.
Het kan C expressies evalueren (inclusief assignment) en assembly weergeven. Daarnaast kun je er
grafisch break points mee (aan/uit) zetten door met de muis voor de betreffende C regel te klikken.
In Appendix A vind je uitleg over hoe je de X32 debugger kunt opstarten en gebruiken.
4
Opdracht
De opdracht voor het practicum beschrijven we aan de hand van het volgende schema, dat globaal
weergeeft hoe het uiteindelijke systeem er uit dient te zien:
Figuur 4.1: Een schema van het uiteindelijke systeem
De opdracht bestaat grofweg uit twee onderdelen: het implementeren van een controller (zie
het midden van het schema) en het afhandelen van de interface met de bestuurder (de drie controls
links in het schema).
De controller regelt het signaal dat naar de motoropstelling gaat (via throttle), op basis van
een door de gebruiker opgegeven cruising speed of setpoint (setpoint) en de huidige snelheid van
het wiel (speed). Om de huidige snelheid te bepalen, dien je een software “decoder” te schrijven,
die het aantal a en b cycli bijhoudt (count). De snelheid (speed) is gelijk aan de verandering in
count per uC/OS time slice. Omdat zal blijken dat een software decoder niet snel genoeg is, zul je
deze in tweede instantie vervangen door een hardware (VHDL) decoder.
Het afhandelen van de interface vormt een belangrijk deel van de opdracht, met name omdat de
X32 versie die we bij het practicum gebruiken geen ingebouwde functionaliteit heeft om knoppen
te “debouncen”. Hierdoor dien je dit zelf in software te doen, en wel op een “correcte” manier.
Een foute manier is om “polling” (periodiek uitlezen) te gebruiken, omdat dit een onnodige CPU
belasting geeft. Bovendien dient het niet mis te gaan wanneer de bestuurder meerdere knoppen
tegelijk of vlak na elkaar indrukt. Dit laatste probleem zul je oplossen door het afhandelen van
verschillende knoppen “in parallel” te doen.
Een tweede reden waarom de interface een belangrijk deel van de opdracht vormt, is dat je
ook een “autorepeat” functie dient in te bouwen. Om het eenvoudig te maken de snelheid aan
te passen, verhoogt of verlaagt deze functie throttle automatisch iedere uC/OS time slice. De
functie wordt geactiveerd wanneer de bestuurder een bepaalde knop langer dan 500 ms ingedrukt
houdt, en gedeactiveerd wanneer de knop weer wordt los gelaten.
Om het gemakkelijker voor student-assistenten te maken om je werk na te kijken, dien je in
je programma dezelfde namen als in bovenstaand schema te gebruiken; daarnaast zijn er enkele
precieze eisen omtrent de interface naar de bestuurder. Van links naar rechts dienen de vier knoppen
op het FPGA bordje (Figuur 2.1) de volgende functies te hebben:
5
• Een “reset” functie; hiermee wordt de motor uitgezet, het SSD gereset en het programma
afgesloten.
• Een “(dis)engage” functie; hiermee wordt cruise control mode aan (uit) gezet. Wanneer deze
aan gezet wordt, wordt setpoint gelijkgesteld aan speed.
• Een “decrement” functie; hiermee wordt throttle verlaagd, of setpoint, wanneer cruise
control mode aan staat.
• Een “increment” functie; hiermee wordt throttle verhoogd, of setpoint, wanneer cruise
control mode aan staat.
Het meest linkse ledje geeft aan wanneer er een fout optreedt bij het verwerken van a en b. In dit
geval gaat het ledje voor een duur van 100 ms aan, waarna het automatisch weer uit gaat. Het
ledje ernaast geeft weer of het programma zich in cruise control mode bevindt.
Op de linker- en rechterhelft van het SSD zijn respectievelijk speed en throttle te zien, beiden
naar rechts geshift om in 8 bits te passen.
5
Dagdeel 1
Gedurende dit eerste dagdeel ontwikkel je het basis framework voor de applicatie, waaraan je
gedurende het tweede en derde dagdeel respectievelijk debouncing en een cruise control mode toevoegt. Dit houdt in dat de applicatie na vandaag reageert op het indrukken van knoppen, dat
de sensor en actuator paden werken (m.a.w., dat er signalen van en naar de motoropstelling gestuurd kunnen worden), dat deze signalen op het SSD worden weergegeven, en dat het ”error” ledje
tijdelijk aan gaat wanneer er een fout optreedt op het sensor pad.
Voordat je met het practicum kunt beginnen, dien je in te loggen op je GNU/Linux account, en
de practicum software te downloaden en installeren. In Appendix A vind je installatie instructies
voor de practicum software en een overzicht van de de programma’s die je na installatie kunt
gebruiken. In Appendix B, C en D vind je tutorials voor het gebruik van respectievelijk UNIX, de
X32 soft core en uC/OS, en een overzicht van de belangrijkste functies/commando’s. We gaan er
in het vervolg van uit dat je deze appendices aandachtig hebt bestudeerd.
Opdracht 1a Na deze opdracht kun je met behulp van de knoppen throttle bijhouden; deze
wordt op het SSD weergegeven (via een periodieke uC/OS taak), en in de vorm van een PWM
signaal naar de motor gestuurd.
• Schrijf een programma dat exit naar de bootloader, zodra de “reset” knop wordt ingedrukt.
Dit houdt in dat je een ISR isr buttons definiëert, waarin je de standaard C exit functie
gebruikt.
• Houd vanuit de ISR een (integer) throttle bij, die verlaagd respectievelijk opgehoogd kan
worden met de “decrement” en “increment” knop en geef deze weer op het SSD. (Gebruik
eventueel de standaard C printf functie om een tussenstap te maken.) Het probleem van
bouncing wordt nu duidelijk!
• Creëer een periodieke taak die iedere uC/OS time slice throttle naar het SSD schrijft.
Verwijder de betreffende code uit de ISR, zodat deze zo kort mogelijk blijft.
• Update de PWM puls breedte in de periodieke taak met de huidige waarde van throttle.
Stel de periode van het PWM signaal bij aanvang van het programma in op 1024 (dit correspondeert met 1024 klok-cycli van de FPGA chip; zie Appendix C).
6
Opdracht 1b Na deze opdracht bepaalt de applicatie speed, m.b.v. een software decoder (Sectie
2.3), en geeft het deze weer op het SSD. Het error ledje gaat tijdelijk aan wanneer er een probleem
optreedt op het sensor pad.
• Definiëer een ISR isr decode die wordt aangeroepen wanneer a of b verandert (Appendix C).
De ISR leest de huidige toestand van a en b uit, en verlaagt of verhoogt count, afhankelijk van
in welke richting het wiel draait. Geef de betreffende interrupts een hoge prioriteit. (Omdat
de signalen een hoge frequentie kunnen hebben is het belangrijk om snel te reageren.)
• Zorg dat de periodieke taak speed berekent en deze op de linkerhelft van het SSD weergeeft.
(Zowel het actuator als het sensor pad wordt nu netjes gevisualiseerd.)
• Creëer een “error” taak, die het error ledje voor 100 ms aan zet, wanneer er een probleem
is met a en b. (Hierdoor zou er fout geteld kunnen worden, waardoor er verkeerde commando’s naar de motor gestuurd zouden kunnen worden.) Een goede indicatie hiervoor is
dat de ISR een toestands-overgang detecteert waarbij beide signalen tegelijk veranderen. Gebruik een semafoor voor de communicatie tussen de ISR en de taak, en zorg dat deze gereset
wordt wanneer het ledje weer uit gaat. (Bij een fout wordt de ISR vaak een aantal keren
aangeroepen.) Gebruik hiervoor code als volgt (we zullen hier later een nettere oplossing
voor zien):
while(OSSemAccept(sem err) > 0); // reset semafoor
Verifieer dat het error ledje aan (en ook weer uit!) gaat wanneer je de motoropstelling aan of
uit zet (hierbij treden altijd illegale toestands-overgangen van a en b op).
• Voer throttle op en constateer dat het error ledje op een bepaald moment aan gaat. Op dit
moment wordt de ISR (geschreven in C, op een niet al te snelle processor!) te “traag” om alle
a en b events nog te kunnen verwerken. Hierdoor is het programma alleen nog maar bezig
deze (high-priority) signalen te verwerken, en lopen de knoppen en het SSD vast.
• Houd het wiel even tegen (zodat de frequentie van a en b afneemt) en verlaag ondertussen
throttle, totdat het programma weer normaal werkt.
6
Dagdeel 2
Dit tweede dagdeel staat in het teken van het debouncen van de knoppen; daarnaast voeg je een
“autorepeat” feature toe voor de “increment” en “decrement” knoppen, om het gemakkelijker te
maken throttle aan te passen.
Opdracht 2a Na deze opdracht worden de knoppen parallel gedebounced, maar nog met dezelfde
(lelijke) while-lus als voor de error taak.
• Creëer een aparte taak voor elke knop (heb je eerdere ervaring met C, of maak je het jezelf
graag moeilijk, dan kun je de code voor deze taken eventueel samenvoegen in een enkel “task
body”); definiëer hiernaast evenzoveel semaforen, voor de communicatie tussen de ISR en de
taken.
• Laat isr buttons de taak behorende bij een knop wekken, wanneer de knop wordt ingedrukt
of losgelaten.
• Gebruik de uC/OS delay functie om tenminste 20 ms (de lengte van een uC/OS time slice)
te wachten, voordat een gewekte taak de toestand van de betreffende knop uitleest. Het
bouncen duurt voor het gebruikte FPGA bordje altijd korter, waardoor nu altijd de juiste
waarde wordt uitgelezen.
7
• Gebruik de oplossing (while lus) die we eerder hebben gehanteerd voor de error taak, om het
effect van bouncing tegen te gaan.
Opdracht 2b Hoewel de while lussen het bouncing probleem oplossen, wil je met name in een
real-time systeem geen onnodige overhead veroorzaken. Herhaaldelijk een semafoor ophogen, om
deze vervolgens weer te resetten is dus geen nette oplossing.
Een andere mogelijkheid om bouncing tegen te gaan bestaat uit het tijdelijk uit zetten van
de (hardware) interrupt voor knoppen. Hiermee is het echter lastig tot een correcte oplossing te
komen, omdat alle knoppen deze interrupt delen, en het dus mis kan gaan wanneer er meerdere
knoppen (vrijwel) tegelijkertijd gebruikt worden.
Een correcte, nette oplossing “simuleert” softwarematig een aparte interrupt voor iedere knop.
De ISR houdt hierbij een aparte “interrupt enable flag” bij voor iedere knop, en zet deze uit zodra
de betreffende knop wordt ingedrukt; de betreffende taak zet deze weer aan, wanneer het het
debouncing proces voltooid heeft.
• Definiëer een (integer) “interrupt enable register” en reserveer hierin voor iedere knop een
aparte ”interrupt enable flag” (bit).
• Gebruik de C logische operatoren (^, &, | en ~) om te bepalen welke knoppen er ingedrukt
of losgelaten zijn (ten opzichte van de eerdere situatie). Zorg dat de betreffende interrupts
vervolgens uitgezet worden.
• Gebruik de betreffende semaforen om de taken te wekken die behoren bij de knoppen die
“nieuw” ingedrukt of losgelaten zijn. Zorg dat deze taken de juiste interrupt flag weer aan
zetten, wanneer ze het debouncing proces voltooid hebben.
• Je kunt de while lus in de error task eventueel ook vervangen. De overhead hiervan is echter
minder problematisch, omdat errors sowieso niet voor horen te komen.
Opdracht 2c Na deze opdracht kun je, door de “decrement” of “increment” knop langer dan
500 ms ingedrukt te houden, throttle automatisch elke timeslice laten verlagen of verhogen (“autorepeat”).
• Definiëer twee globale variabelen voor de toestand van de “decrement” en “increment” knoppen na debouncen, en update deze vanuit de betreffende taken.
• Definiëer twee taken met bijbehorende semaforen. Zorg dat de juiste taak gewekt wordt op
het moment dat de 500 ms ingaan.
• Zorg dat de betreffende taak 500 ms wacht (zolang de betreffende knop ingedrukt blijft), en
dat het vervolgens elke time slice throttle automatisch aanpast.
7
Dagdeel 3
Tijdens dit laatste dagdeel implementeer je de controller. Dit doe je met behulp van een algemeen
bruikbaar controle algoritme, een zogeheten PID-algoritme. Hiervan stel je de drie (P, I en D)
parameters zelf in m.b.v. een monitor taak (Sectie 3.2). Tenslotte maak je met gnuplot een plaatje
van het gedrag van de controller om te laten zien dat deze goed werkt.
Opdracht 3a Allereerst rest ons nog het gebruiken van de hardware component voor het decoderen van a en b. Na deze opdracht gaat het error ledje alleen nog maar aan bij het aan/uit
zetten van de motoropstelling.
• Vervang de C decoder door de meegeleverde VHDL decoder (Appendix C). Gebruik de “error”
interrupt die deze genereert om het error ledje aan te sturen.
8
Opdracht 3b Na deze opdracht heb je een werkende (maar simpele) controller, met bijbehorende
knoppen functionaliteit en een ledje dat aangeeft of de applicatie zich in cruise control mode bevindt.
• Voeg een cruise control mode toe. Laat het cruise control ledje aangeven of de applicatie zich
in cruise control mode bevindt. Sla de huidige snelheid op wanneer cruise control mode aan
gezet wordt (setpoint = speed).
• Definiëer een cruise control functie, en roep deze vanuit de periodieke taak aan. Voeg hieraan
een simpele controller toe, bijv. als volgt:
eps = setpoint - speed;
throttle += eps/4;
Verifieer dat je nu een werkende controller hebt, door (voorzichtig) wat met het wiel te
spelen.
• Zorg dat je in cruise control mode met de “decrement” en “increment” knoppen setpoint
kunt aanpassen. Voor de veiligheid dient de autorepeat functie in cruise control mode uit te
staan.
Opdracht 3c Na deze opdracht heb je de simpele controller vervangen door een meer geavanceerde controller. Je hebt de drie (PID) parameters hiervan met behulp van een monitor
ingesteld.
• Vervang de simpele controller door de volgende, meer algemeen bruikbare controller. Gebruik
integers voor de parameters (de X32 ondersteunt geen floating-point getallen):
throttle
D * (eps
lastlast
last eps
+= (P * ((eps - last eps) + I * eps +
- 2 * last eps + lastlast eps)))/64;
eps = last eps;
= eps;
• Voeg de monitor (Sectie 3.2) toe aan de applicatie: “include” de header file monitor.h en voeg
een low-priority “monitor taak” toe die de functie run monitor (zie monitor.c) aanroept.
Start de applicatie weer en maak jezelf bekend met de beschikbare commando’s. (Let op: de
X32 debugger werkt niet tegelijk met de monitor!)
• Zoek het fysieke adres van de throttle variabele op in het bestand cc.dbg, en “sense” de 4
bytes op dit adres, terwijl je de “decrement” en “increment” knoppen bedient.
• Stel de PID variabelen in met behulp van de de monitor taak. Begin hierbij met P, onderzoek
daarna het effect van I en vervolgens D.
Opdracht 3d Na deze opdracht kun je het gedrag van de simpele en geavanceerde controller
grafisch met elkaar vergelijken, m.b.v. het fameuze UNIX gnuplot programma.
• Gebruik printf om vanuit de periodieke taak iedere 100 ms vier getallen uit te printen: een
indicatie van de tijd, setpoint, throttle en speed. De output van de applicatie ziet er nu
als volgt uit:
0 18 120 100
1 18 67 80
..
9
• Test de (simpele en geavanceerde) controllers een aantal seconden, en gebruik “standard
output redirection” (Appendix B) om de gegenereerde output in twee bestanden op te slaan.
Verwijder “ongewenste” regels uit deze files.
• Gebruik een gnuplot script (Appendix B) om de data in elk bestand grafisch weer te geven,
met commando’s als volgt:
plot "filename" using 1:2 title "setpoint" with lines, \
"filename" using 1:3 title "throttle" with lines, \
"filename" using 1:4 title "speed" with lines
pause -1
Referenties
[1] K. Nice, How Cruise Control Systems Work
http://auto.howstuffworks.com/cruise-control.htm
[2] S. Woutersen; X32 Programmers Manual (Draft)
http://x32.ewi.tudelft.nl/x32progman.pdf
[3] A.J.C. van Gemund, IN2305 Resource Page
http://www.st.ewi.tudelft.nl/~gemund/Courses/In2305/Resources
[4] M. Dufour, Running uC/OS on the X32 Soft Core
http://www.st.ewi.tudelft.nl/~gemund/Courses/In2305/Resources/ucosx32.pdf
[5] M. Dufour, X32-debug: A Remote Source-Level Debugger for the X32 Soft Core (draft)
http://www.st.ewi.tudelft.nl/~gemund/Courses/In2305/Resources/x32db.pdf
[6] D.E. Simon, An Embedded Software Primer, ISBN 020161569, Addison-Wesley
[7] M. Stonebank, UNIX Tutorial for Beginners
http://www.ee.surrey.ac.uk/Teaching/Unix
[8] G. van Rossum et al., The Python Programming Language Website
http://python.org
[9] T. Williams et al., The Gnuplot Homepage
http://www.gnuplot.info
A
Practicum Software
In deze appendix vind je uitleg over hoe de practicum software te installeren en gebruiken. Zie
Appendix B voor uitleg over de gebruikte UNIX commando’s.
A.1
Installatie
Om de practicum software te installeren, dien je allereerst in te loggen op je GNU/Linux account.
Download vervolgens de in2305 package.tgz “tarball” van de IN2305 Resource Page [3], en pak
deze uit met het volgende commando:
tar -zxf in2305 package.tgz
De practicum software is nu geinstalleerd in een nieuw aangemaakte in2305 directory.
10
A.2
Gebruik
Om de software te kunnen gebruiken, ga je de in2305 directory binnen en “source” je een script
met practicum instellingen:
cd in2305
source setup in2305
Herhaal dit telkens wanneer je de software vanuit een andere shell wil gebruiken of opnieuw inlogt op je account!
Wanneer je nu het ls commando gebruikt, zie je onder andere de volgende directories:
x32: de X32 soft core, inclusief bootloader programma (VHDL)
x32-tools: X32 compiler tools en instructie-set simulator (C)
ucos: X32 versie van de uC/OS real-time kernel (C)
x32db: de grafische, C-level X32 debugger (Python)
doc: X32, uC/OS en X32 debugger documentatie (PDF)
In de cruise directory zijn enkele voorbereidingen getroffen voor de cruise control applicatie.
Deze gaan er vanuit dat je hiervoor een bestand genaamd cc.c gebruikt. Gebruik je favoriete UNIX
editor om dit bestand aan te maken (als je nog geen favoriete editor hebt dan kun je kate gebruiken):
cd cruise
kate cc.c
Wanneer je tijdens het practicum je programma wil testen, dan kun je het compileren, uploaden naar de FPGA, een terminal programma opstarten en het programma starten, met een
enkel “make” commando:
make cc
Wanneer er tijdens dit proces iets mis gaat (een programma loopt bijvoorbeeld vast), dan is
het vaak een goed idee om het FPGA bordje hardwarematig te resetten (zet hierbij altijd eerst
de motoropstelling uit). De X32 is daarnaast zo ingesteld dat je een softwarematige reset kunt
veroorzaken, door alle vier de knoppen tegelijk in te drukken (Figuur 2.1).
A.3
X32 Tools
Om het practicum af te ronden heb je geen X32 specifieke commando’s nodig. Voor de volledigheid
vind je hier een overzicht van impliciet (via make) gebruikte en overige X32 specifieke commando’s.
• x32-term: Dit is een eenvoudig terminal programma, dat als alternatief voor minicom (Appendix B) dient. Het bevat weinig functionaliteit, maar voldoende voor gebruik bij het
practicum. Als je het een “s” argument mee geeft, geeft het automatisch het “start program” commando aan de X32 bootloader (Appendix C).
• x32-upload: Dit programma communiceert met de X32 bootloader om een uitvoerbaar
(*.ce) bestand (standaard vanaf adres 0) te uploaden. Je kunt het programma hierbij automatisch laten starten, of een terminal programma gebruiken om zelf het “start program”
commando aan de bootloader te geven.
• x32-debug: Dit commando start de X32 debugger (Sectie 3.3). Als argument geef je het de
naam van een uitvoerbaar (*.ce) bestand mee. De debugger voert nu (voor de zekerheid)
11
make uit voor het bestand, uploadt het naar de FPGA, zet een initieel break point op de main
functie en start het programma.
(Om de debugger thuis te gebruiken dien je naast de practicum software de volgende “dependencies” te installeren: python, pyqt, qscintilla en eventueel pyqt-qscintilla.)
• x32-sim: Dit is een instructie-set simulator voor de X32. Een instructie-set simulator is met
name handig tijdens de ontwikkeling van een processor zelf (voor applicaties kun je beter een
monitor of debugger gebruiken). Een simulator kan echter ook handig zijn als je thuis geen
FPGA bordje hebt. Je kunt dan weliswaar niet met hardware (zoals knoppen, het SSD en de
motoropstelling) communiceren, maar toch verschillende aspecten van je programma testen.
• lcc-x32: Dit is een versie van de LCC compiler, omgebouwd om X32 bytecode te genereren
(normaal gesproken wordt dit alleen intern gebruikt), en debugging output t.b.v. de X32
debugger. De LCC compiler heeft vergelijkbare command-line opties met GCC. Enkele voorbeelden hiervan zie je in cruise/Makefile; een volledig overzicht krijg je door het zonder
argumenten op te starten.
B
UNIX Tutorial
GNU/Linux is een moderne variant van het aloude UNIX besturingssysteem. In deze appendix vind
je een overzicht van de bij het practicum gebruikte en enkele overige nuttige UNIX commando’s,
en een introductie in het “redirecten” van de output van deze commando’s.
B.1
UNIX Commando’s
Op de gemiddelde UNIX computer vind je een overvloed aan handige tools. Een groot aantal hiervan
is gestandaardiseerd, inclusief de manier waarop je argumenten/input meegeeft; veel andere tools
(zoals gnuplot) hebben daarnaast een enkele bron, waardoor ze op de meeste UNIX systemen
hetzelfde werken.
De volgende commando’s heb je nodig bij de installatie van de practicum software, het practicum
zelf of kunnen hierbij van pas komen. Zie [7] voor een meer uitgebreide UNIX tutorial.
• cd: Hiermee verander je de huidige directory (vgl. cd in DOS).
• ls: Hiermee print je een lijst van bestanden in de huidige directory (vgl. dir in DOS);
door een “-l” optie mee te geven zie je ook allerlei eigenschappen van deze bestanden, zoals
grootte.
• cat: Hiermee print je de inhoud van een of meerdere text bestanden achter elkaar naar het
scherm (vgl. type in DOS).
• less: Hiermee print je de inhoud van een text bestand, maar word je bij elke pagina gevraagd
om op bijv. spatie te drukken om verder te gaan, en kun je achterwaarts scrollen.
• grep: Hiermee zoek je in de inhoud van een of meerdere text bestanden, naar regels waarin
een bepaalde string (of reguliere expressie) voorkomt. Standaard worden deze regels uitgeprint, voorafgegaan door de naam van het betreffende bestand, maar de output kan op
allerlei manieren aangepast worden. De volgende opties zijn erg handig:
-R: zoek recursief in directories (bijv. grep -R .).
-i: negeer (“ignore”) verschillen tussen hoofd- en kleine letters
-l: print alleen de namen van bestanden die de string/expressie bevatten, niet de betreffende
regels
12
-v: laat alleen bestanden/regels zien die de string/expressie niet bevatten
-n: print voorafgaand aan iedere regel het regelnummer
• diff: Hiermee print je het “verschil” tussen twee text bestanden uit. Dit is met name handig
om grotere (programma) bestanden met elkaar te vergelijken. Het verschil (“diffje”) kun je
opslaan in een bestand (Sectie B.2) en later weer “toepassen”, ook al is het originele bestand
ondertussen op andere plekken veranderd. Dit maakt het met name geschikt voor (online)
samenwerking. Vaak wordt de “-u” optie gebruikt, om verschillen in een standaard, leesbaar
formaat uit te printen.
• tar: Hiermee kun je bestanden in/uitpakken, al dan niet met behulp van compressie. De “-z”
optie geeft aan dat gzip (een andere UNIX tool) gebruikt moet worden voor (de)compressie.
De “-c” en “-x” opties geven respectievelijk compressie en decompressie aan. De “-f” optie
geeft aan dat er een bestands naam volgt. Om bijvoorbeeld alles in de huidige directory in
en vervolgens weer uit te pakken kun je de volgende commando’s geven:
tar -zcf bestand.tgz *
tar -zxf bestand.tgz
• source: Hiermee kun je een script binnen de huidige shell uitvoeren (normaal gesproken
wordt er voor het uitvoeren van een script een aparte shell opgestart). Dit betekent met
name dat omgevingsvariabelen (“environment variables”) bewaard blijven nadat het script is
uitgevoerd.
• man: Hiermee kun je informatie over een UNIX commando opvragen. Bijv. man diff geeft
een beschrijving van het diff commando en een overzicht van de beschikbare opties.
• make: Dit is een zeer nuttige tool, waarmee je bijvoorbeeld voor C/C++ projecten kunt
aangeven hoe een onderdeel van andere onderdelen afhangt, en hoe het opnieuw gecompileerd
moet worden, wanneer deze andere onderdelen veranderen. Het gebruik van make kan veel tijd
(en toetsaanslagen) besparen, doordat programma’s nu “incrementeel” gecompileerd worden
en je minder (low-level) commando’s hoeft te gebruiken. Om make te kunnen gebruiken, dien
je een bestand genaamd Makefile in de huidige directory aan te maken, met entries als volgt:
cc.ce: cc.c monitor.c
lcc-x32 -g cc.c
Deze twee regels geven aan dat het commando lcc-x32 -g cc.c uitgevoerd moet worden,
zodra cc.c of monitor.c veranderd is en make verzocht wordt om cc.ce te updaten. Dit
verzoek kan expliciet zijn (make cc.ce) of impliciet, doordat een ander onderdeel weer van
cc.ce afhangt.
• minicom: Dit is een veelgebruikt serieel communicatie programma, met veel meer mogelijkheden dan x32-term. Het practicum setup script zorgt ervoor dat je het enkel hoeft op te
starten om met het FPGA bordje te communiceren.
• gnuplot: Met dit programma kun je op allerlei manieren 2d en 3d functies en datasets visualiseren. Het vormt een uitstekende combinatie met een programmeertaal als Python, omdat
je hiermee gemakkelijk met ad hoc datasets kunt omgaan. Resulterende plaatjes kunnen
geëxporteerd worden in PostScript formaat, zodat je ze gemakkelijk in scripties kunt gebruiken. Je kunt het interactief gebruiken door het simpelweg op te starten:
gnuplot
13
Je kunt ook een aantal commando’s opslaan in een bestand en deze uitvoeren door de bestandsnaam mee te geven:
gnuplot bestand
Met het plot commando genereer je 2d plaatjes. Het volgende geeft een simpele functie
weer:
plot x**3
Je kunt plot ook meerdere functies of datasets meegeven, waarbij je deze met komma’s
achter elkaar zet:
plot x**3, x, "bestand"
Dit commando plot twee functies en data uit een text bestand in hetzelfde plaatje. Er wordt
aangenomen dat het text bestand is opgedeeld in een aantal kolommen. (Standaard worden
hiervan de eerste twee kolommen genomen.)
Bij iedere functie/dataset kun je een aantal opties meegeven, bijv. als volgt:
plot sqrt(x) title "wortel", "bestand" using 1:3 with lines
De “title” optie voegt een titel toe voor de betreffende functie/dataset. De “using” optie
geeft aan welke twee kolommen van het aangegeven bestand gebruikt worden. De “with
lines” optie zorgt ervoor dat data punten met lijntjes worden verbonden.
Om een specifiek gebied te bekijken, kun je commando’s als volgt gebruiken:
set grid; set xtics 1; set ytics 1
set xrange [-2:2]; set yrange [-2:2]
Met het splot commando genereer je 3d plaatjes. Het volgende voorbeeld laat wat interessante mogelijkheden zien:
set xrange [-2:2]; set yrange [-2:2]
set hidden3d; set contour
splot x**2+y**2, 2*sin(x+y)
Als je nu het output venster selecteert, kun je het plaatje in 3d roteren m.b.v. de linker
muisknop en in- en uitzoomen m.b.v. de middelste muisknop.
B.2
Output Redirection
Het is een interessant feit dat UNIX en C vrijwel tegelijkertijd op Bell Labs zijn ontwikkeld, en
dat ARPANET in hetzelfde jaar (1969) het licht zag. Er is hoe dan ook iets aan UNIX en C
waardoor ze na de relatieve “eeuwigheid” (in computer jaren) sinds hun inceptie nog steeds door
IT professionals gebruikt worden, en zelfs door het GNU Project en Linux aan populariteit winnen.
Blijkbaar hebben de ontwikkelaars van UNIX en C intuitief aangevoeld hoe je software schrijft die
overeind blijft, zelfs in een snel veranderende omgeving.
Een belangrijk deel van de filosofie achter UNIX zit hem in de manier waarop programma’s
(interfaces) opgedeeld zijn in kleinere programma’s (interfaces). Het idee is dat ieder onderdeel
14
gespecialiseerd is in het uitvoeren van een enkele, simpele taak. Ingewikkelde opties, of opties die
niet absoluut noodzakelijk zijn, zijn hierbij uit den boze. Grotere programma’s worden gemaakt
door dergelijke “eenvoudige” onderdelen te combineren, veelal via simpele text-gebaseerde protocollen. Eenvoudige interfaces en hergebruik van veelal algemene, gespecialiseerde programma’s
zorgen ervoor dat de globale complexiteit makkelijker te beheersen is.
De beschreven filosofie komt duidelijk naar voren bij de standaard UNIX tools. Tools als grep,
make en gnuplot zijn extreem gespecialiseerd en kunnen eenvoudig vanaf de command-line met
elkaar gecombineerd worden om willekeurig ingewikkelde taken uit te voeren.
De meeste UNIX tools accepteren input via zogeheten “standard input”, en printen hun output
via zogeheten “standard output”. Het eerste correspondeert in principe met het toetsenbord, en
het laatste met het scherm. We kunnen de output van het ene programma echter ook als input van
een ander programma gebruiken, met behulp van een zogeheten “pipe”. Het volgende commando
gebruikt bijvoorbeeld twee pipes (aangegeven door “|”) om te scrollen door alle bestands namen
in de huidige directory (en subdirectories) waarin een bepaalde (case-insensitive) string voorkomt:
ls -l | grep -iR cpp | less
Om de inhoud van een bestand als standard input te gebruiken kun je veelal ook een argument
gebruiken, maar het volgende is nu wellicht fraaier (uniq en sort zijn standaard UNIX tools):
cat filename | uniq | sort
Vanzelfsprekend kun je ook de output van een programma in een bestand opslaan. Hiervoor
gebruik je het “>” karakter. Het volgende plakt respectievelijk drie bestanden aan elkaar en slaat
het “verschil” tussen twee bestanden op in een derde bestand:
cat a b c > d
diff -u e f > g
C
X32 Tutorial
In deze appendix vind je uitleg over hoe je op de X32 met beschikbare hardware (VHDL) componenten en peripherals (zoals knoppen en het SSD) kunt omgaan. Voor een meer uitgebreide
behandeling van de X32 verwijzen we je naar de X32 Programmers Manual [2].
Om te beginnen dien je de X32 “header file” in je C programma te “includen”. Hierin worden enkele belangrijke geheugen adressen en interrupt nummers gedefiniëerd. Daarnaast vind je er
enkele handige macro’s en functies om met interrupts om te gaan. Je kunt dit alles gebruiken door
het volgende bovenin je programma te zetten:
#include <x32.h>
Mocht je de header file zelf willen bekijken, dan kun je deze in de x32-tools/lib-x32 directory
vinden.
Voor memory-mapped I/O gebruik je een vast gedeelte van het geheugen. Het begin hiervan
wordt aangegeven door een integer pointer genaamd peripherals. Om een bepaald (memorymapped) adres te gebruiken, tel je hier een voorgedefiniëerde offset bij op. Het volgende schrijft
bijvoorbeeld een waarde naar het SSD:
peripherals[PERIPHERAL DISPLAY] = 0x1234;
Tijdens het practicum gebruik je de volgende offsets om een (peripheral) waarde te lezen:
15
PERIPHERAL BUTTONS: de toestand van de knoppen (een bit per knop)
PERIPHERAL ENGINE DECODED: de stand van de teller van de VHDL decoder
De volgende offsets gebruik je om een waarde te schrijven:
PERIPHERAL
PERIPHERAL
PERIPHERAL
PERIPHERAL
LEDS: de configuratie van de ledjes (een bit per ledje)
DISPLAY: het getal op het SSD
DPC1 PERIOD: de periode van het PWM signaal
DPC1 WIDTH: de puls breedte van het PWM signaal
Om een bepaalde interrupt af te vangen, stel je een ISR en een prioriteit in, en enable je de
betreffende interrupt. Hiervoor gebruik je drie verschillende macros:
SET INTERRUPT VECTOR(INTERRUPT BUTTONS, isr buttons);
SET INTERRUPT PRIORITY(INTERRUPT BUTTONS, 10);
ENABLE INTERRUPT(INTERRUPT BUTTONS);
In dit voorbeeld dient er een functie genaamd isr buttons te zijn, zonder argumenten of return
type. Om er voor te zorgen dat de X32 interrupts ook daadwerkelijk afhandelt, dien je interrupts
daarnaast “globaal” aan te zetten:
ENABLE INTERRUPT(INTERRUPT GLOBAL);
Let op: bij gebruik van uC/OS dien je het aanzetten van de “globale” interrupt over te laten
aan uC/OS zelf (zie Appendix D).
Tijdens het practicum kun je gebruik maken van de volgende interrupt (IRQ) nummers:
INTERRUPT
INTERRUPT
kan afgaan
INTERRUPT
INTERRUPT
INTERRUPT
INTERRUPT
BUTTONS: er is een knop ingedrukt of losgelaten
OVERFLOW: het resultaat van een arithmetische operatie past niet in 32 bits (let op: deze
in standaard functies zoals printf!)
DIVISION BY ZERO: er wordt door 0 gedeeld
ENGINE A: signaal a is veranderd (“flank”)
ENGINE B: signaal b is veranderd (“flank”)
ENGINE ERROR: de VHDL decoder heeft een error gedetecteerd
Voor de prioriteit van een bepaalde interrupt kun je een getal van 1 tot 15 kiezen. In feite kun je
ook een veel groter getal dan 15 nemen, maar omwille van portabiliteit is dit niet verstandig. Een
groter getal betekent een hogere prioriteit. Je kunt meerdere interrupts dezelfde prioriteit geven;
welke interrupt voorrang krijgt is dan echter ongedefiniëerd.
D
uC/OS Tutorial
In deze appendix vind je uitleg over hoe je uC/OS kunt gebruiken en een overzicht van de bij het
practicum gebruikte uC/OS functies.
D.1
Gebruik
Het gebruik van uC/OS is het duidelijkst te illustreren aan de hand van een uitgewerkt voorbeeld.
Het volgende programma definiëert een simpele taak, die één maal per seconde “hello, world” naar
16
het scherm schrijft:
#include <ucos.h>
#define PRIORITY 10
#define STACK SIZE 1024
int stack[1024];
void task(void *data) {
while(TRUE) {
printf("hello, world\n");
OSTimeDly(50);
}
}
void main() {
OSInit();
OSTaskCreate(task, (void *)0, (void *)stack, PRIORITY);
OSStart();
}
In de volgende sectie vind je uitleg over de bij het practicum gebruikte uC/OS functies. Hier
volgen nog een aantal belangrijke opmerkingen over het gebruik van uC/OS.
• Prioriteiten zijn “omgekeerd” ten opzichte van de X32; een lager getal betekent in dit geval
een hogere prioriteit.
• Verschillende taken mogen niet dezelfde prioriteit hebben.
• Omdat interrupts grotendeels buiten uC/OS om gaan, dien je aan het begin van iedere ISR
OSIntEnter() te plaatsen, en aan het eind OSIntExit(); hiermee geef je aan dat uC/OS
tijdelijk geen context switch mag uitvoeren.
• Taken, semaforen en andere uC/OS datastructuren dien je pas na het aanroepen van OSInit
te initialiseren.
• Het aanzetten van INTERRUPT GLOBAL (Appendix C) dien je aan uC/OS over te laten (meer
specifiek, aan OSStart; zie volgende sectie).
D.2
uC/OS Functies
Het volgende geeft een (niet-alfabetisch) overzicht van de bij dit practicum gebruikte uC/OS functies. Zie [4] en/of de ucos directory voor een volledig overzicht.
• OSTaskCreate: Creëert een nieuwe taak. Als argumenten geef je respectievelijk het adres van
een functie mee, welke als “task body” fungeert, een initieel argument voor deze functie, een
pointer naar een “working stack” en een prioriteit. Voor de prioriteit kun je een getal van 1
tot 62 kiezen. Let op dat je het initiele argument en de stack pointer “cast” door er (void
*) voor te zetten.
Het volgende creëert een taak, met 7 als initieel argument:
OSTaskCreate(some func, (void *)7, (void *)stack, PRIORITY)
17
• OSTimeDly: Zet de betreffende taak tijdelijk op non-actief. Als argument geef je het aantal
time ticks mee, waarna de taak weer actief dient te worden. Bij het practicum is uC/OS zo
ingesteld dat er 20 ms tussen opeenvolgende time ticks zitten.
Het volgende laat een taak “wachten” tot de eerstvolgende time tick:
OSTimeDly(1);
• OSSemCreate: Creëert een semafoor. Als argument geef je de initiele waarde van de “semafoor counter” mee. Het return type is een pointer naar een algemeen type voor uC/OS
data structuren:
OS EVENT *sem;
sem = OSSemCreate(0);
• OSSemPost: “Post” naar een semafoor. Als er taken zijn die op de semafoor “penden” wordt
er naar de taak met de hoogste prioriteit gewisseld. Als deze er niet zijn wordt de semafoor
counter met 1 opgehoogd.
OSSemPost(sem);
• OSSemPend: “Pend” op een semafoor. Als argumenten geef je de betreffende semafoor mee,
een timeout waarde en het adres van een variabele waar een eventuele timeout error mee
aangegeven kan worden. Als timeout waarde gebruik je bij dit practicum WAIT FOREVER,
waardoor een timeout error niet kan optreden:
UBYTE err;
OSSemPend(sem, WAIT FOREVER, &err);
• OSSemAccept: Verlaagt een semafoor counter als deze groter dan 0 is en retourneert de originele counter waarde.
OSSemAccept(sem);
• OSInit: Initialiseert uC/OS en creëert een “idle task” met de laagst mogelijke prioriteit (63).
Deze taak draait wanneer alle andere taken non-actief zijn, waarbij het een oneindige lus
uitvoert (“busy loop”).
• OSStart: Start het multitasken, door te wisselen naar de taak met de hoogste prioriteit en
de interrupts aan te zetten, m.b.v. INTERRUPT GLOBAL (Appendix C).
18

Vergelijkbare documenten