MCCA1P2 opdracht 1.

© Harry Broeders.

Deze pagina is bestemd voor Technische Informatica studenten van de THRijswijk die de minor Elektrotechniek volgen.

Knight Rider.

Z'n 20 jaar geleden was de TV serie Knight Rider populair. In deze serie werd de hoofdrol gespeeld door een sprekende auto KITT genaamd. Deze auto was aan de voorkant voorzien van een rijtje rode LED's (lampjes) die in een bepaald patroon aan en uitgeschakeld werden. Deze rij LED's moesten een scanner voorstellen waarmee KITT de omgeving verkende. 

In deze practicumopgave gaan we zelf een rijtje LED's aansturen met een bepaald patroon met behulp van de HC11 microcontroller.

Voorbeeldprogramma.

THRSim11 versie 5.22c installeren (indien nodig).

Het voorbeeld programma ophalen.

Het voorbeeld programma compileren, linken en omzetten in het juiste formaat.

Het voorbeeld programma simuleren.

Het voorbeeld programma uitvoeren op de EVM.

Extra informatie.

In het programma wordt het C/C++ keyword volatile gebruikt. In de C++ standaard staat:

volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See intro.execution for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C.

Het woord volatile betekent "vluchtig" en wordt dus gebruikt om aan te geven dat de variabele ook buiten het programma om veranderd kan worden. Dit keyword zorgt ervoor dat de compiler geen optimalisaties toepast die ervan uitgaan dat de waarde van een variabele nog hetzelfde is als die variabele door het programma zelf niet veranderd is.

Voorbeeld van het gebruik van volatile: Output.

In het voorbeeld programma wordt een pointer p gebruikt die staat te wijzen naar een output poort:

byte* p=(byte*)0x1004;

Het type byte is in het begin van het programma als volgt gedefinieerd:

typedef unsigned char byte;

In de for lus van main() wordt telkens een nieuwe waarde naar deze output poort geschreven:

*p=c1|c2;

Een sterk optimaliserende compiler kan nu "denken": "De waarde die ik via de pointer wegschrijf wordt nooit meer gelezen dus ik kan dat schrijven ook wel achterwege laten." Dat is natuurlijk niet de bedoeling, wij willen de juiste LEDjes wel zien branden! De waarde waar de pointer naar wijst moet dus als vluchtig (volatile) worden gequalificeerd:

volatile byte* p=(byte*)0x1004;

Bij het compileren met gcc kun je een programma op verschillende manieren optimaliseren, zie hier, Bijvoorbeeld:

De versie van de gcc compiler die wij nu gebruiken (3.3.5) zal als je het keyword volatile bij de pointer p vergeet, bij het gebruik van de optie -Os of -O3 de LEDjes toch gewoon aansturen. Maar dat kan bij een nieuwe versie van gcc anders zijn!

Voorbeeld van het gebruik van volatile: Input.

Je kunt ook een pointer naar een input poort definiëren:

byte* p=(byte*)0x....;

De waarde van deze input poort kun je dan als volgt inlezen:

waarde=*p;

Als dit meerdere malen achter elkaar gebeurt (bijvoorbeeld in een lus) kan een sterk optimaliserende compiler "denken": "De waarde die ik via de pointer heb ingelezen heb ik zo meteen weer nodig. Ik kan deze waarde dus bewaren (bijvoorbeeld in een register) en hoef deze waarde dan de tweede keer niet opnieuw uit het geheugen te lezen." Dat is natuurlijk niet de bedoeling, wij willen de nieuwe waarde van de inputpoort inlezen! De waarde waar de pointer naar wijst moet dus als vluchtig (volatile) worden gequalificeerd:

volatile byte* p=(byte*)0x....;

De versie van de gcc compiler die wij nu gebruiken (3.3.5) zal als je het keyword volatile bij de pointer p vergeet, bij het gebruik van de optie -Os de input poort toch gewoon opnieuw inlezen. Bij het gebruik van de opties -O3 wordt het opnieuw inlezen van de input poort wel weggeoptimaliseerd!.

Voorbeeld van het gebruik van volatile: Tijdvertraging.

In het voorbeeldprogramma wordt een "lege" for lus gebruikt om een tijdvertraging te realiseren.

void wait() {
    word i;
    for (i=0; i<10000; ++i)
        /*empty*/;
}

Een sterk optimaliserende compiler kan nu "denken": "De waarde van i wordt toch niet gebruikt de hele for lus kan dus weggeoptimaliseerd worden (en vervolgens kan de hele functie wait() weggeoptimaliseerd worden)." Dat is natuurlijk niet de bedoeling, wij willen dat de functie wait() een tijdvertraging opleverd! De variabele i moet dus als vluchtig (volatile) worden gequalificeerd:

volatile word i;

De versie van de gcc compiler die wij nu gebruiken (3.3.5) zal als je het keyword volatile bij de variabele i vergeet, bij gebruik van de optie -Os of -O3 de lus niet helemaal weggeoptimaliseren. De variabele i wordt bij het gebruik van de optie -Os of -O3 echter telkens met 625 verhoogd in plaats van met 1. Vraag me niet waarom! De lus wordt dan dus maar 8x doorlopen (in plaats van 10000x).

Programmeren in C++.

Het voorbeeldprogramma is geschreven in C. Je kunt echter ook C++ programma's compileren en uitvoeren op de 68HC11. Het voorbeeld programma in C++ kun je vinden in de file: opdr1.cpp. Gcc kijkt dus naar de extensie van het bestand om te bepalen op de C of de C++ compiler aangeroepen moet worden.

Opdracht.