ES1 Project 1: Microcontrollers Les 3: Eenvoudige externe hardware & hardware programmeren in C Door Hugo Arends, september 2012
Hardware programmeren in C Inmiddels ben je al aardig op gang gekomen met het programmeren van microcontrollers. Microcontrollers vragen echter om specifieke programmeerkennis. Je moet namelijk gebruik maken van de aanwezige hardware. Tijdens deze les leer je hoe dat gaat in de programmeertaal C. Maar we beginnen met de manier waarop je eenvoudige hardware kunt aansluiten op de microcontroller.
Externe hardware Voordat we zinvolle informatie van/naar PORTB kunnen lezen/schrijven, moet er hardware gekoppeld worden aan de microcontroller. In de meest eenvoudige vorm zijn dat LED’s en switches. LED’s Light Emitting Diodes zijn elektrische componenten die licht uitzenden zodra er een stroom in de juiste richting doorheen kan. →
Een manier om een LED aan te sluiten op een microcontroller staat hiernaast weergegeven. De LED zal aan gaan zodra de I/O pin logisch 0 gemaakt wordt.
+5V
Microcontroller Resistor
Hierbij spreken van ‘sinken’ aangezien de stroom de microcontroller in loopt. I/O pin
NB. Op een soortgelijke manier zijn de LED’s op de STK500 ook aangesloten.
I/O pin
Een LED kan pas werken als daar een drempelspanning overheen staat. Een waarde voor deze drempelspanning is bijvoorbeeld 0,7 V. In de datasheet van de LED vind je de exacte specificatie. Tevens vind je in de datasheet hoeveel stroom er maximaal door de LED mag lopen. Hoe meer stroom, des te meer licht. →
Stel dat in de datasheet van een LED de volgende gegevens te vinden zijn:
→
In de datasheet van de microcontroller zoeken we op hoe groot de spanning is als er een logische 0 op de uitgang staat. Dit vind je in de tabel met ‘Electrical Characteristics’. Hierin vinden we:
Met deze gegevens kunnen we de waarde van de weerstand uitrekenen. Ga als volgt te werk: →
Over de weerstand staat een spanning van:
Met de wet van Ohm berekenen we nu de weerstand, aangezien de spanning bekend is (3,6 V) en
Door Hugo Arends, september 2012
1
tevens de gewenste stroom bekend is (15 mA). →
Bereken de waarde van de weerstand:
NB. Let op met de hoeveelheid stroom die je sinked of sourced met een microcontroller. In de ‘Electrical Characteristics’ vind je hoeveel stroom een enkele I/O pin maximaal kan dissiperen en ook hoeveel een complete I/O port tegelijk aan stroom kan dissiperen. Switches Een eenvoudige manier om een signaal van buiten de microcontroller in te lezen gaat met switches. →
Een manier om een switch aan te sluiten op een microcontroller staat hiernaast weergegeven. Zodra de switch ingedrukt wordt heeft de I/O pin de waarde van een logische 0.
+5V
Microcontroller Pullup Resistor
De weerstand wordt een pull-up weerstand genoemd. Deze voorkomt dat de I/O pin gaat ‘zweven’ als de switch niet ingedrukt is.
I/O pin I/O pin
NB. Op een soortgelijke manier zijn de switches op de STK500 ook aangesloten.
→
Switch
De stroom die de I/O pin in gaat hoeft niet groot te zijn. Het spanningsniveau op de I/O pin zorgt er namelijk voor dat er een logische 1 wordt gelezen (zodra de schakelaar niet ingedrukt is). Een waarde die veelvuldig toegepast wordt is: Hierdoor wordt de maximale stroom die de I/O pin moet dissiperen:
→
Het kan ook anders. In de microcontroller zit namelijk ook internal pull-up weerstand aan iedere I/O port. Hiernaast staat dat schematisch weergegeven.
Microcontroller +5V
Opdracht: zoek in de datasheet op hoe je deze internal pull-up resistor aan kunt zetten.
Internal Pulllup Resistor Enable
I/O pin I/O pin
Zoek tevens op wat de waarde van deze weerstand is.
Switch
Een compleet overzicht van de werking van de I/O ports is te vinden in de datasheet. Bekijk bijvoorbeeld de figuren I/O Pin Equivalent Schematic en General Digital I/O. Tot slot nog een opmerking over het gebruik van switches. Switches hebben de eigenschap te 'denderen'. Dit betekent dat de contactveer bij het indrukken (en loslaten) van de schakelaar even heen en weer kan veren, zodat er eigenlijk een serie 1-en en 0-en achter elkaar verschijnt in plaats van één enkele 1-0 of 0-1 overgang. Dit verschijnsel kan enkele tientallen ms duren. Door de waarde van de ingang waaraan de schakelaar zit, een aantal keren achter elkaar te testen (b.v. met tussentijden van 30 ms), kan dit denderprobleem softwarematig worden omzeild.
Door Hugo Arends, september 2012
2
I/O en de microcontroller Een microcontroller is opgebouwd uit verschillende functionele bouwstenen. Kijken we naar het Blok Diagram in de datasheet van ATmega32A dan zien we bijvoorbeeld: Central Processing Unit (CPU) PORTn drivers/buffers en PORTn digital interfaces USART SPI EEPROM Oscillator En nog veel meer! Deze bouwstenen zijn met elkaar verbonden via de databus. Elke bouwsteen heeft één of meerder registers aan deze databus. Om te voorkomen dat alle register tegelijk met de databus verbonden zijn kunnen de registers via een vast adres geadresseerd worden. We hebben reeds gezien dat er aan bijvoorbeeld de digital interface PORTB drie registers gekoppeld zijn. Te weten PORTB, DDRB en PINB.
I/O programmeren Met deze kennis gaan we het project HelloWorld herschrijven en bekijken hoe de I/O slimmer geprogrammeerd kan worden. →
Open het project HelloWorld in AVR Studio.
→
Om te beginnen worden er een aantal header files ge-include. Dat is nodig, zodat de compiler weet waar de externe definities en functies gevonden kunnen worden.
→
In de main routine wordt eerst de richting van PB0 geset in DDRB. Het nadeel van deze manier van programmeren is dat op deze manier alle andere bits (PB1 t/m PB7) dus input worden gemaakt!
Daar gaan we stapsgewijs een oplossing voor vinden. Het doel is dat slechts één bit van ee register geset kan worden, zonder dat daarbij de andere bits aangepast worden. →
De oplossing is vrij eenvoudig: door de OR operator toe te passen.
→
Or operator Voorbeeld Uitleg
| 2 | 3 Het getal links van de operator (2) wordt bitwijs ge-or-ed met het getal rechts van de operator (3). Or-en we 2 met 3 dan krijgen we 3
→
(0b00000010) (0b00000011) (0b00000011)
Dezelfde instructie kunnen we ook in een verkorte notatie opschrijven.
Door Hugo Arends, september 2012
3
DDRB wordt dus ge-OR-ed met 0x01 en het resultaat staat in DDRB. In deze regel code wordt 0x01 het masker genoemd. Overal waar in het masker een één staat komt in het resultaat een één, overal waar in het masker een nul staat blijft het resultaat zoals het was! Vaak wordt deze regel code echter nog iets ander geschreven. Als we namelijk PB0 willen aanpassen, dan ligt het voor de hand een #define te gebruiken, zodat de code beter leesbaar is. →
Deze instructie geeft hetzelfde resultaat, maar we zien direct dat PB0 aangepast wordt.
Om de code te begrijpen tussen de haakjes, moet je weten hoe de shift operator werkt. →
Shift operator Voorbeeld Uitleg
<< 1 << 3 Het getal links van de operator (1) wordt het getal rechts van de operator (3) keer naar links geschoven. Schuiven we 1 drie keer naar links dan krijgen we 8
(0b00000001) (0b00001000)
In HelloWorld.c wordt het getal 1 dus PB0 keer naar links geschoven. Aangezien PB0 gedefinieerd is als 0, is de uitkomst dus 0x01 (en dit hadden we eerder zelf ingevuld). Opdracht: In welke file wordt PB0 gedefinieerd? En hoe worden PB1 t/m PB7 gedefinieerd? We kijken verder naar de rest van het programma. →
Hier wordt dezelfde methode gebruikt om alléén PB0 van PORTB te setten. Daarna volgt een functie call om een delay te realiseren van 500 ms.
→
In de volgende instructie worden twee nieuwe operatoren geïntroduceerd.
Eerst bekijken we de and operator. →
And operator Voorbeeld Uitleg
& 2 & 3 Het getal links van de operator (2) wordt bitwijs ge-and-ed met het getal rechts van de operator (3). And-en we 2 met 3 dan krijgen we 2
Door Hugo Arends, september 2012
(0b00000010) (0b00000011) (0b00000010)
4
Ook wordt de inverse operator toegepast. →
Inverse operator Voorbeeld Uitleg
~ ~(3) Het getal (3) achter de operator wordt bitwijs geïnverteerd. Inverteren we 3 dan krijgen we 252
→
(0b00000011) (0b11111100)
Het resultaat van deze instructie is dus dat alléén PB0 van register PORTB gereset wordt. De rest (PB1 t/m PB7) blijft ongewijzigd.
Een in dit programma nog niet toegepaste operator is de exclusive or. Hiermee kunnen we individuele bits inverteren. Dat kan handig zijn om bijvoorbeeld een LED te laten knipperen. Overal waar een één in het masker staat zal het corresponderende bit geïnverteerd worden, de rest blijft gelijk. →
Exor operator Voorbeeld Uitleg
^ 20 ^ 6 Het getal links van de operator (20) wordt bitwijs ge-exor-ed met het getal rechts van de operator (6). Xor-en we 20 met 6 dan krijgen we 18
→
(0b00010100) (0b00000110) (0b00010010)
Daarmee kunnen we het programma herschrijven tot:
Conclusie: Met deze manier van programmeren kunnen we met behulp van een leesbaar masker individuele bits in registers setten, resetten en inverteren.
Door Hugo Arends, september 2012
5
Opdracht 1 Start in AVR Studio een nieuw project met de naam AtoB. Verbind op de STK500 PORTA met de schakelaars en op PORTB de LED’s Realiseer de volgende functies: Alle pinnen van PORTB worden gedefinieerd als output. De volgende I/O pinnen van PORTA worden gedefinieerd als input: PA0, PA1, PA2 en PA3.
LED0 gaat aan zodra SW0, SW1, SW2 of SW3 ingedrukt wordt. LED1 gaat aan zodra SW0, SW1, SW2 en SW3 ingedrukt wordt. LED2 doet het geinverteerde van SW2. LED3 gaat aan zodra op de switches SW0, SW1, SW2 en SW3 het getal 6 ingedrukt wordt. LED4 gaat aan zodra er géén andere LED’s aan zijn.
→
TIP. Controleren of een schakelaar ingedrukt is (bijvoorbeeld SW2), kan met de volgende code (bedenk dat je een logische nul leest zodra een schakelaar op de STK500 ingedrukt is).
// SW2 pressed? if(PINA == 0b11111011) { … }
Een alternatieve manier is door een masker te gebruiken, waardoor alle andere switches genegeerd worden! Ga voor jezelf na hoe deze regel code werkt.
// SW2 pressed? if((PINA & (1<
Opdracht 2 Geef antwoord op de volgende theorievragen. Hardware (zie eerste figuur op pagina 1) 1.
Stel dat de doorlaatspanning van de LED 1,8 V bedraagt en de uitgangsspanning van de microcontroller 0,2 V. Hoe groot moet de weerstand zijn als er door de LED een stroom moet lopen van 10 mA?
2.
Bereken de maximale en de minimale stroom in de LED als deze licht geeft, rekening houdend met de spreiding van de componenten. Ga ervan uit dat de voedingsspanning (ook die van de microcontroller) 5 V 5% bedraagt. De weerstand heeft een waarde van 680 Ohm 2%. De LED heeft een doorlaatspanning van 0,7 V. Raadpleeg de datasheet van de microcontroller (electrical characteristics).
Software 1.
Laat zien dat de instructie PORTB = PORTB & ~0x80 alleen bit 7 van PORTB reset. Neem hierbij aan dat PORTB = 0xC3.
2.
Laat zien dat de instructie PORTB |= 0x80 alleen bit 7 van PORTB set. Neem hierbij aan dat PORTB = 0x5A.
3.
Laat zien dat de instructie PORTB ^= 0x01 alleen bit 0 van PORTB inverteert. Neem hierbij aan dat PORTB = 0xC3, resp. 0x5A.
Tijd over Vul opdracht 1 aan door de volgende extra functies te implementeren:
Alle I/O ports van PORTA worden gedefinieerd als input.
Door Hugo Arends, september 2012
6
LED5 gaat aan zodra er een oneven aantal switches worden ingedrukt. LED6 inverteert van waarde zodra SW6 ingedrukt en weer losgelaten wordt. LED7 knippert met een frequentie van ongeveer 2 Hz.
In deze onderwijspublicatie is géén auteursrechtelijk beschermd werk opgenomen.
Door Hugo Arends, september 2012
7