Embedded systems
Pauwels D.
Embedded systems V1.0
Hoofdstuk
Inleiding Wat is een embedded systeem? We leven in een "embedded" wereld. We worden omringd door een groot aantal embedded systemen en in ons dagelijks leven zijn we vaak afhankelijk van het correct functioneren van deze gadgets. Embedded systemen zijn elektronische systemen waarin microprocessoren zijn verwerkt. De hoofdredenen waarom deze zijn gebruikt is het vereenvoudigen van het systeemontwerp, het voorzien van de gewenste flexibiliteit en het drukken van de kostprijs per systeem. Een microprocessor aan boord hebben betekent dat het verbeteren van tekortkomingen, het maken van aanpassingen en het toevoegen van nieuwe features slechts een kwestie is van het herschrijven of aanpassen van de software die het toestel bedient. De hedendaagse embedded systemen zijn zo goedkoop geworden dat ze bijna in elk toestel zitten dat we gebruiken in ons alledaags leven. Embedded systemen zijn doorgedrongen in onze televisie, radio en CD speler. Ook de afwasmachine of microgolfoven uit onze keuken, kredietkaartlezers, en toegangscontrole-systemen of Palm computers en GSM's bevatten allemaal een embedded systeem. In heel wat gevallen zijn we er ons zelfs niet van bewust dat een "computer" aanwezig is daardoor beseffen we dikwijls niet wat de impact van embedded systemen is op ons leven. Onze desktop PC waarmee we email versturen, tekstverwerking doen, speadsheets en databases raadplegen is ontworpen om verschillende toepassingen uit te voeren. Een embedded systeem is ontworpen om slechts een beperkt aantal taken uit te voeren zoals bv. er voor zorgen dat de toast niet aanbrandt of het instellen van de kookcyclus in de microgolfoven. Een embedded systeem kan dus niet voor iets ander worden gebruikt.
1.1 Wat is er uniek aan het ontwerp van embedded systemen? Het ontwerp van een embedded systeem vormt een uitdaging voor een elektronica ingenieur wat de betrouwbaarheid, performantie en systeemkost betreft.
Pauwels D.
Embedded systems V1.0
1
De betrouwbaarheidsvereisten zullen een grotere verantwoordelijkheid leggen bij de ontwerpers van het systeem, om fouten te elimineren en om de software zo te ontwerpen dat onverwachtte situaties of fouten getollereerd worden. Veel embedded systemen moeten 24 uur per dag, zeven dagen per week, 365 dagen per jaar werken. Men kan ze niet gewoon "rebooten" wanneer er iets verkeerd gaat. Daarom zijn soliede ontwerpmethoden en een grondige testfase heel belangrijk voor elk embedded systeem, dit wat de hardware- als de software-implementatie betreft. Door de behoefte om te communiceren met sensors, actuatoren, toetsenborden, display's enz. zal verondersteld worden van de programmeur dat hij (zij) een goede kennis heeft over hoe men op alternatieve manieren aan I/O kan doen, om vereisten qua snelheid, complexiteit en systeemkost op elkaar af te stemmen. Alhoewel we meestal (ahfankelijk van de gebruikte processor of controller) programmeren in een hogere programmeertaal voor een betere productiviteit, zal het toepassen van deze alternatieve methoden dikwijls aanleiding geven om af te dalen tot op het hardwareniveau van het systeem en te programmeren in assembler.
1.2 Hoe krachtig zijn embedded processoren? De embedded systemen die we vinden in de meeste consumer producten bevatten een microcontroller. De kracht van deze controllers is meestal vergelijkbaar met deze van de eerste PC's, maar krachtiger processoren worden steeds meer en meer gebruikt omdat ook de toepassingen die lopen op embedded sytemen steeds complexer worden. De grootste hoeveelheid embedded systemen die vandaag in productie zijn, zijn gebaseerd op 4-bit, 8-bit of 16-bit processoren. Alhoewel 32-bit processoren nu nog maar voor een relatief klein percentage meetellen voor het gebruik in embedded systemen stijgt hun gebruik voor deze doeleinden zeer snel. Ontwerpers van embedded systemen hebben vandaag de keuze uit een groot aanbod aan processoren en controllers. Vooral in het 8-bit segment kan de beschikbare variëteit zelfs een ervaren ontwerper soms overweldigen. Het selecteren van de meest geschikte controller voor een bepaalde toepassing is soms een moeilijke eerste stap. Het wordt er ook niet eenvoudiger op doordat er steeds nieuwe controllers op de markt komen.
1.3 Embedded systemen werken meestal "REAL-TIME". Real-time systemen verwerken 'events'. Events zijn gebeurtenissen die voorkomen aan systeeminputs en die andere gebeurtenissen veroorzaken aan systeemoutputs. Een voorbeeld van een event is het blokeren van een wiel van een wagen bij het remmen. De reactie van het ABS systeem (embedded systeem) hierop is het gepulseerd bekrachtigen van de remkracht zodat het wiel weer gaat draaien en er weer besturing van de wagen mogelijk is. Het hoofddoel van een real-time systeem is de responcetijd te minimaliseren. Het ABS systeem moet het blokeren van het wiel detecteren binnen enkele milliseconden; een vertraging van enkele seconden zou hier ontoelaatbaar en zelfs potentiëel dodelijk zijn. Wat real-time betekent voor een embedded systeem is afhankelijk van de toepassing, zo zal de besturing van een microgolfoven minder tijdskritisch zijn en zullen we tevreden zijn als de controller zijn responce zo snel mogelijk berekent, hij heeft immers geen echte deadline, de gebruiker of de installatie is niet in gevaar als de responce iets langer duurt. We spreken hier van soft real-time. Wanneer er wel een deadline is opgelegd aan het systeem, zoals in het geval van het ABS systeem spreken we van hard real-time systemen. Pauwels D.
Embedded systems V1.0
2
Hoofdstuk
Wat is een microcontroller? Iedereen heeft wellicht al wel de benaming 'microprocessor' gehoord, met als belangrijkste vertegenwoordiger de Pentium processor uit de Intel x86 reeks. Je vind deze term zeker in een aantal commerciële publicaties waarmee de PC-shops ons om de oren slaan. Men zou door al dat commerciële 'geschreeuw' bijna vergeten dat dit type van 'PC'-processor maar een klein stukje van de totale processormarkt vertegenwoordigd en dat er daarnaast ook nog een zeer groot en bloeiend segment is van embedded microprocessoren en microcontrollers. Dit zijn meteen ook termen die de modale PC gebruiker vreemd zijn! Wat is dan wel zo'n microcontroller?
2.1 Een typisch microprocessorsysteem. Om aan te geven wat een microcontroller is, gaan we eerst even het blokschema van een typisch embedded microprocessorsysteem van nabij bekijken. X-tal Osc.
POR
ADRES DECODER
ADRESBUS
CPU
ROM (FLASH) (EPROM) (EEPROM)
SRAM
PAR. POORT
TIMER SER. COUNTER POORT
DATABUS
Fig. 2.1.1. Een blokschema van een typisch microprocessorsysteem
Pauwels D.
Embedded systems V1.0
3
Op de figuur 2.1.1 zien we de CPU (een single chip CPU is een microprocessor) die is verbonden met de nodige geheugenchips (ROM en RAM) en met een aantal I/O chips, in dit geval parallelle poorten, seriële poorten en timers. De verbinding van de CPU met de andere componenten verloopt via een aantal bussen. Een bus is een verzameling van geleiders die signalen van een zelfde aard transporteren. De draden die de gegevens van en naar het geheugen vervoeren, wordt de DATABUS genoemd. De geleiders die het adres (locatienummer) vervoeren dat aangeeft waar er informatie gelezen of geschreven wordt, noemen we de ADRESBUS. Omdat we moeten kunnen lezen en of schrijven naar het geheugen, zijn er ook controlesignalen nodig, die we de CONTROLEBUS noemen. Aangezien het de processor is die de controlebus stuurt, hebben alle namen van de signalen betrekking op de functie die ze vanuit de processor krijgen. Zo wordt de 'read' lijn actief als de processor wil lezen, en de 'write' lijn als de processor wil schrijven. Wat zijn nu de nadelen van een klassiek microprocessorsysteem voor embedded toepassingen? MTBF. Alles gaat stuk! Niets is helaas voor de eeuwigheid. Het systeem dat je zo geduldig en met zoveel liefde hebt ontworpen zal het na een poos opgeven bij de klant. De kunst bestaat erin dat je systeem de klant overleeft, je levert dus best kwaliteit af, zo hou je gelukkige klanten tot op hun sterfbed. Een systeem bestaat meestal uit verschillende componenten die allemaal hun eigen "Failure rate" hebben. Deze FIT-rate ( FIT of Failures In Time) is de statistische waarde die voor een component aangeeft hoeveel failures of defecten de component zal hebben per miljard werkingsuren. De FIT rate van een component is een gegeven dat door de fabrikant ter beschikking kan worden gesteld voor elke component, of in het beste geval gegevens waaruit je de FIT rate kan berekenen.. Aan de hand van deze cijfers kan voor een bepaald systeem de MTBF worden bepaald. MTBF staat voor Mean Time Between Failure. De MTBF is geen exact gegeven, het is een voorspelling, waar van een systeem met een aantal componenten met een bepaalde FIT-rate, een gemiddelde werkingsduur wordt berekend.
Waarin:
n qi ri
het aantal verschillende componenten. hoeveelheid van de i de component. de FIT rate.
Hieruit blijkt dat voor een bepaalde FIT rate de betrouwbaarheid van het systeem afneemt als het aantal componenten toeneemt, iets wat je met wat boerenwijsheid ook al wel kon inschatten. In de volgende figuur 2.1.2 staat bv. de FIT-rate van een PowerPC 603r processor van Freescale weergegeven in functie van de junctietemperatuur met de voedingsspanning als parameter.
Pauwels D.
Embedded systems V1.0
4
Fig. 2.1.2. De FIT rate van een XPC 603e processor van Freescale.
Ontwerp van een PCB. Voor je fonkelnieuw embedded processorsysteem moet er helaas ook een PCB (Printed Circuit Board) worden ontworpen. Met de steeds hoger liggende clockfrequenties en pincount van de processoren is dit eveneens een niet zo voor de hand liggende en vaak een tijdrovende zaak. Sommige ontwerpers zullen, niet gehinderd door enige kennis ter zake, aanvoeren dat het tekenen van een PCB louter en alleen het omzetten is van schema verbindingen naar echte bedrading. Niets is minder waar! Het tekenen van een PCB is het met kennis van zaken creëren van een nieuwe component met eigen elektrische eigenschappen en met een aanzienlijk aandeel in het al dan niet slagen van een project! Zelfs bij de meest ervaren ontwerper, en bij het gebruik van de meest geavanceerde CAD pakketten zullen er waarschijnlijk een aantal designfouten in de eerste prototype PCB's van een complex systeem zitten. Dikwijls liggen deze problemen op het vlak van overspraak en wederzijdse koppelingen of signaalintegriteit. En ook op het vlak van EMC (Elektro Magnetische Compatibiliteit) kunnen er een aantal problemen aan het licht komen bij de eerste metingen op de prototypes. Het systeem kan immers te stoorgevoelig zijn, of kan zelf teveel storing veroorzaken. Dit heeft als gevolg dat er een aantal aanpassingen of zelfs een redesign van de PCB moet worden gedaan. Dit is een kostelijke zaak. Je kan dus maar beter deze complexe en grote PCB's vermijden.
Pauwels D.
Embedded systems V1.0
5
2.2 Een microcontrollersysteem. Wanneer we het blokschema van een microcontrollersysteem van fig 2.2.1 vergelijken met het blokschema van een processorsysteem van fig 2.1.1 dat is opgebouwd uit diskrete componenten dan merken we dat deze praktisch gelijkaardig zijn. Het grote verschil is echter dat bij een microcontroller het hele systeem op één chip is geïntegreerd, wat grote voordelen heeft tegenover een diskreet opgebouwd systeem. Omdat het hier gaat om een single-chip oplossing is de MTBF van het eindproduct meestal groter. Er moet meestal ook geen complexe PCB meer ontworpen worden. Uiteraard moet nog wel een PCB worden ontworpen voor je embedded toepassing, waar dan de microcontroller (single chip) in geïntegreerd wordt, maar deze is dan meestal veel minder complex dan bij een processorsysteem. Het systeem kan ook veel compacter worden gemaakt met een microcontroller dan met de diskrete oplossing, dit heeft samen met het vorige argument als gevolg dat het eindproduct economischer wordt. Op technisch vlak hebben de testingenieurs het ook gemakkelijker, immers daar de digitale bussen waar de snelle signalen over getransporteerd worden on-chip blijven zal het systeem veel sneller voldoen aan de gangbare EMC normen. Vandaar de gestadige groei van het aantal microcontrollers op de markt.
Fig 2.2.1. Een voorbeeld van een typisch microcontrollersysteem
In het 8-bit segment is de meest populaire en meest gebruikte architectuur die van de Intel 8051. Doordat deze architectuur wijd aanvaardt is door een zeer groot aantal gebruikers, ontwikkelen verschillende fabrikanten nog steeds controllers met nieuwe features gebaseerd op deze architectuur. Er zijn naar schatting ongeveer een 400-tal verschillende derivaten van een oorspronkelijke 8051 op de markt, en na 25 jaar komen er nog elke dag bij. Men schat dat de helft van alle gebruikte microcontrollers gebaseerd zijn op deze architectuur.
Pauwels D.
Embedded systems V1.0
6
2.3 Wat is er op de markt? Er is tegenwoordig een groot assortiment aan verschillende microcontrollers terug te vinden op de markt. In de figuur 2.3.1 wordt aangegeven wat de verdeling is tussen 4-bit, 8-bit, 16-bit en 32-bit controllers. Zoals blijkt zijn 64-bit en andere (DSP, 128-bit, …) tegenwoordig nog te verwaarlozen ten opzichte van de rest. Types van gebruikte controllers in embedded systemen 50
4-bit
40
8-bit
30
16-bit
20
32-bit
10
64-bit
0
Andere
1999-2000
Fig. 2.3.1. Verdeling van de meest gebruikte architecturen naar verwerkingsbreedte.
Wat opvalt is dat het overgrote deel van de huidige embedded systemen op de markt voorzien zijn van een 8-bit of 16-bit controller, terwijl het 32-bit segment nog relatief klein is. Een andere trend die we opmerken in de volgende figuur 2.3.2 is dat door de steeds groeiende complexiteit van embedded systemen er een omgekeerde tendens is in het gebruik van controllers voor nieuwe ontwerpen. Hier is de 32-bit controller het snelst groeiende segment.
Types van controllers gebruikt voor nieuwe ontwerpen 60 40 20 0
1999-2000
4-bit 8-bit 16-bit 32-bit 64-bit Andere
Fig. 2.3.2. Gebruik van controllers in nieuwe ontwerpen.
Het komt er in de praktijk op neer dat voor een bepaalde toepassing steeds de meest geschikte controller moet gekozen worden uit het globale aanbod. Dit rekening houdend met een aantal aspecten zoals: reeds beschikbare tools voor de ontwikkeling (compilers,simulatoren, debuggers, enz…), reeds bestaande software bibliotheken, ervaring van de ingenieurs met bepaalde controllerreeksen, beschikbaarheid van componenten, economische gegevens enz. Dit is meestal geen gemakkelijke keuze.
Pauwels D.
Embedded systems V1.0
7
2.4 Een voorbeeld uit elke categorie. 4-Bit controller: bv. OKI MSM63184 Deze familie van controllers worden gebruikt voor 'low-end' toepassingen, zoals calculators, telefoons (geen GSM's of DECT toestellen…), uurwerken (in 'DIE' te verkrijgen), spelletjes, speelgoed, fietskilometertellers, thermostaat, enz… Dit type heeft ingebouwde 640-dot matrix LCD drivers, wat ideaal is voor een aantal van de genoemde toepassingen. Andere types hebben een ingebouwde 'melody' module waarmee tonen, toonlengten en tempo's kunnen opgewekt worden: ideaal voor speelgoed. Een eigenschap van deze reeks controllers is dat ze low power devices zijn. Ze kunnen werken op een zeer lage voedingsspanning van 0,9V tot 2,7V en dit bij een stroomverbruik van 15µA in actieve toestand! Dit is ideaal voor batterijgevoede toepassingen. Een gevolg van dit zeer laag stroomverbruik is dat ze met een lage clockfrequentie werken, in dit geval 32KHz tot enkele honderden KHz. Bij 32KHz geeft dit een uitvoeringstijd van 61µS per instructie, zeker iets waar rekening moet mee gehouden worden. Een eigenschap van de meeste 4-bit controllers is dat ze slechts in mask ROM uitvoering of in OTP (One Time Programmable) ROM uitvoering beschikbaar zijn, en dat ze meestal van een hardware stack voorzien zijn. Hun kostprijs zit voor de meest performante controller uit deze reeks rond 3.5€ voor grote aantallen. 8-Bit controller: bv. SAB 80C517 De groep van 8-bit controllers worden gebruikt in medium end toepassingen zoals: harddisk controle, keyboards, PLC's (vb. LOGO van SIEMENS), sensoren, stappenmotorsturingen, subsystemen (vb. display units), enz. De hier weergegeven SAB 80C517 van Infineon (vroeger SIEMENS) is een erg veelzijdige controller die een heleboel verschillende I/O mogelijkheden heeft meegekregen, vandaar zijn toepassing in voornamelijk industriële systemen. Deze controller is geen low power type, actief (@12MHz en 5V) verbruikt hij 40mA stroom, maar hij beschikt over een idle mode waar hij 15mA verbruikt en een power down mode waar hij maar 50µA verbruikt. De systeemclock moet tussen 3.5 en 16MHz liggen, het is dus geen statisch ontwerp. De SAB80C517 is een controller die meestal ROM-less gebruikt wordt, alhoewel hij door de fabrikant ook met mask ROM kan geleverd worden. Dit is een nadeel, er moet immers steeds een extern programmageheugen worden toegevoegd. De meeste fabrikanten integreren tegenwoordig minstens enkele Kbytes FLASH ROM in hun controllers die dan ISP (In System Programmable) worden. Dit is een algemene trend. De SAB80C517 is een controller die is afgeleid van de Intel 8051, waar heel wat nieuwe periferie-elementen werden aan toegevoegd. Het grote voordeel van deze familie is dat er reeds immense hoeveelheden software zijn geschreven voor deze controller-core die binnen de familie upward compatibel en dus herbruikbaar is. Naast deze periferie valt ook de hardware Division/Multiplication unit op, die 32-bit/16-bit bewerkingen en 16-bit*16-bit bewerkingen kan maken los van de CPU, wat een enorme tijdswinst kan opleveren bij wiskundige bewerkingen. De kostprijs voor dit type controller zit rond 8€ voor grote aantallen.
Pauwels D.
Embedded systems V1.0
8
Fig. 2.4.1. Blokschema van de C517 controller van Infineon.
Pauwels D.
Embedded systems V1.0
9
16-bit controller: bv. XC167 van Infineon. 16-bit controllers worden gebruikt in high-end toepassingen, dit zijn meestal allemaal rekenintensieve toepassingen zoals: PID motorsnelheidsregelingen (servo-controllers), PLC's (S7 reeks van SIEMENS), industriële controlesystemen, management van verbrandingsmotoren, ABS systemen, enz. Het is zo dat industriële systemen meer en meer in staat moeten zijn om te communiceren met elkaar of met een subsysteem, hier vormt de CAN bus (industriëel- en automotive communicatie netwerk) een uitstekende oplossing. Deze chip bezit: een clockgenerator met PLL en verschillende types intern geheugen zoals 128Kbytes programma Flash, programma RAM en data RAM.
Fig. 2.4.2. Blokschema van de XC167 controller van Infineon.
Pauwels D.
Embedded systems V1.0
10
32-Bit controller: bv. de MB91F361 van Fujitsu. Dit is een voorbeeld uit een reeks van controllers voor zeer rekenintensieve taken zoals: laseren high end inktjet printers, communicatie producten (routers, bridges…), high end gaming producten, muziek instrumenten, GPS systemen, next generation dashboards… Zijn clocksnelheid ligt tussen 16 en 64MHz en is onder programmacontrole instelbaar, wat nuttig is om het stroomverbruik te beperken wanneer dit nodig is. Verder valt de massale hoeveelheid intern geheugen op (512 Kbytes Flash en 16Kbytes RAM) wat nuttig is voor het stockeren van een (real time) OS, wat bij 32-bit controllers meestal het geval is. Merk ook op dat deze controller beschikt over een volledige 32-bit externe bus interface zodat hij een groot extern geheugen kan aanspreken (max. 4Gbyte). Hij is dan ook ruim behuisd, met zijn 208 pins TQFP verpakking. De externe bus interface maakt de grens tussen een microcontroller en een embedded microprocessor zeer vaag. De microcontroller heeft hier in tegenstelling met een embedded processor echter nog altijd het voordeel single-chip te kunen werken.
Fig. 2.4.3. Blokschema van de MB91F361 controller van Fujitsu.
Pauwels D.
Embedded systems V1.0
11
Hoofdstuk
8051 embedded controller hardware 8051,C517,ADµ µC832 De 8051 familie is een verzameling van controllers met dezelfde interne basisstructuur. De oorspronkelijke 8051 controller werd ontwikkeld door Intel en kwam omstreeks 1980 op de markt. Door het grote succes van deze µC ( microcontroller) voor industriële- en consumertoepassingen zijn er een groot aantal chip fabrikanten die onder licentie ook deze 8051µC gaan produceren. Men schat dat de helft van alle embedded systemen worden bestuurd door een controller uit de 8051 familie. Sommige van deze fabrikanten zoals PHILIPS, INFINEON (C517), DALLAS, ATMEL, ANALOG DEVICES (ADµC832), SILICON LABORATORIES enz. voegen aan deze µC nog interne componenten toe, terwijl ze de originele “kern µC” of CPU-CORE behouden. Dit heeft het voordeel dat bestaande software kan worden behouden, omdat de CPU-CORE alle originele hardware onderdelen bevat, terwijl de fabrikant toch eigen periferie componenten kan aanbrengen voor bepaalde toepassingen. Ook het feit dat de meeste ontwerpers "second-sources" willen voor hun componenten is een reden voor dit succes. Zo zijn er op het ogenblik honderden verschillende µC’s die allemaal ‘8051 compatibel’ zijn, maar toch verschillen door hun specifieke extra’s zoals bv. extra parallelle of seriële poorten, I2C Bus, AD/DA converters, extra hoeveelheid intern geheugen .... Ook het tegenovergestelde doet zich voor; voor minimale toepassingen bevat de oorspronkelijke 8051 nog te veel mogelijkheden. Door een aantal parallelle poorten weg te laten, krijgt men een µC met minder aansluitpinnen en mogelijkheden, maar wel een µC die veel compacter is, bv. de ATMEL 89C5115 in een 28 pin SO behuizing. De ATMEL 89C5115 µC bestaat uit de 8051 CPU-CORE waarbij extra periferie werd toegevoegd en andere dan weer werd weggelaten. Dit heeft het voordeel dat men zeer compact en zeer economisch bepaalde toepassingen toch intelligentie kan verschaffen.
Pauwels D.
Embedded systems V1.0
12
3.1 De oorspronkelijke 8051 architectuur. De oorspronkelijke 8051µC bestaat uit: -Een 8-bit CPU ( Central Processing Unit ) die er voor zorgt dat de instructies worden opgehaald uit het geheugen, worden gedecodeerd en worden uitgevoerd. Deze CPU is geoptimaliseerd voor controletoepassingen ( bit bewerkingen). -4K bytes ROM. Voor programma opslag. -128 bytes interne RAM, voor het opslaan van variabelen, data, enz... -Twee 16 bit timer / counter circuits (TIMER0,TIMER1) -Een full duplex UART (Universele Asynchrone Receiver Transmitter) dit is een seriële poort voor commumicatie toepassingen (COM poort). - On chip clock oscillator. -4 acht bit parallel poorten. (P0,P1,P2,P3) waarvan ook twee poorten gebruikt kunnen worden als ADRES en DATA BUS om extern geheugen aan te spreken. Dit heeft als nadeel dat deze poorten dan hun functie als I/O poort verliezen. - Interrupt controller met twee interrupt niveau’s, die 5 interrupt bronnen kunnen verwerken. Dit is dus de basisstructuur die we bijna altijd zullen terugvinden in de hele 8051 familie, en die compatibel is voor alle componenten in deze controllerfamilie.
3.2 De externe systeemarchitectuur. In sommige van de 8051 compatibele controllers is geen (of onvoldoende) intern programmageheugen geïntegreerd, dit is bv. zo bij de C517 van Infineon of de 8031 van Intel, die helemaal geen intern programmageheugen bezitten In deze gevallen gebruiken de controllers uit deze reeks extern aangesloten geheugenchips op dezelfde manier als een microprocessor. Op deze manier kan men extern bijkomend programmageheugen, datageheugen of bijkomende periferiechips zoals: parallelle- of seriële poorten, toevoegen aan een systeem door ze te verbinden met de bussen van de microcontroller.
3.2.1 De externe geheugen ruimte bij de 8051 reeks. De 8051 verschilt van een traditionele processor doordat hij een gescheiden geheugenruimte heeft voor data- en programmacode. Het verschil tussen programma- en datageheugen is een heel bijzondere eigenschap van de 8051 familie. De totale geheugenruimte voor programmacode is 64Kbytes, en deze kan extern of gedeeltelijk intern (bv. 4Kbyte bij de 8051) gesitueerd zijn. Ook het externe datageheugen is maximaal 64Kbytes groot. De 8051 compatibele controllers hebben bijkomend ook nog minstens 128 bytes intern datageheugen. De externe memorymap van een 8051 controller wordt in figuur 3.2.1.1 weergegeven. Bij instruction fetches zal de controller instructies (code) steeds ophalen uit het code geheugen, dat intern of extern van de controller kan gelegen zijn. Extern codegeheugen wordt aangesproken onder twee voorwaarden: • wanneer de /EA (External Access) pin met een logisch laag niveau is verbonden of • wanneer de program counter (PC) een adres aangeeft dat groter is dan de interne geheugenruimte. Pauwels D.
Embedded systems V1.0
13
De bedoeling is dus dat bij ROM-less (zonder intern codegeheugen) controllers vb. C517 en 8031 de /EA-pin laag gemaakt wordt, om aan te geven dat instructies extern moeten opgehaald worden. Niet alleen voor instruction fetches kan het codegeheugen aangesproken worden, maar a.d.h.v. een MOVC A,@A+DPTR instructie kan ook data (bv. een tabel met constanten) die in dit codegeheugen staat gelezen worden. Wanneer het externe codegeheugen gelezen wordt, zal de /PSEN (Program Store Enable) lijn door de controller geactiveerd worden als leescontrolesignaal naar het geheugen toe. Merk op dat er geen voorzieningen zijn getroffen om naar het codegeheugen te schrijven vanuit de controller, de code zit immers in ROM geheugen! Codegeheugen
Datageheugen FFFFh
Externe code geheugenruimte
FFFFh
Externe data geheugenruimte
1000h Codegeheugen kan intern of extern zitten afhankelijk van de controller en de toestand van de EA-pin Fig. 3.2.1.1.
0FFFh
0000h
0000h
De externe memorymap van een 8051 controller.
Het externe datageheugen kan wel worden gelezen en geschreven, het moet immers geschikt zijn om variabelen te bevatten , het is dan ook uitgevoerd als RAM geheugen. Dit kan onder programmacontrole gelezen of geschreven worden met volgende instructies: • •
MOVX A,@DPTR en MOVX A,@Ri om data te lezen uit het geheugen en MOVX @DPTR,A en MOVX @Ri,A om data te schrijven naar het geheugen.
Waneer het externe datageheugen wordt gelezen of geschreven zal de controller respectievelijk de /RD of de /WR lijn activeren als controle signaal. Merk op dat de controller niet in staat is om uit het datageheugen instructies op te halen, daar hij hiervoor een ander leescontrolesignaal gebruikt.
Pauwels D.
Embedded systems V1.0
14
In sommige gevallen (bv. bij programma-ontwikkeling ) is het wenselijk dat een programma kan opgehaald (en uitgevoerd) worden uit het datageheugen, want daar kan men wel een programma in downloaden, het is immers schrijfbaar (RAM). Bij de 8051 reeks kan dit (het overlappen van data- en codegeheugenruimte) gedaan worden door de beide actief lage leescontrolesignalen (/RD en /PSEN) via een AND functie te combineren tot een nieuw actief laag leessignaal dat dan naar het fysische geheugen (RAM) met een bepaald geheugenbereik gaat. De externe verbindingen van een 8051 compatibele controller worden aangegeven in de figuur 3.2.1.2.
Fig. 3.2.1.2. Pinbeschrijving van een 8051 compatibele AT89s8252 in dil behuizing.
De vier I/O poorten worden hier weergegeven samen met hun alternatieve functies. In een basis 8051 systeem kunnen de vier poorten als 8-bit parallelle poorten gebruikt worden. Het is belangrijk te begrijpen dat wanneer een pin gebruikt wordt met zijn alternatieve functie deze pin zijn gewone I/O functie verliest. Wanneer bv. externe periferiechips of geheugen nodig zijn, worden poort 0 en poort 2 gebruikt met hun alternatieve functie nl. als businterface. Poort 0 wordt dan de 8-bit gemultiplexte adres/data bus en poort 2 krijgt de functie toegewezen van hoogste byte van de adresbus. Enkele pinnen van poort 3 worden controlelijnen zoals /RD (read) en /WR (write). Deze poortpinnen zijn dan verloren als I/O-pinnen en er blijven in dit geval niet veel pinnen meer over die kunnen gebruikt worden als vrij programmmeerbare I/O pin. Het probleem wordt nog groter indien we ook enkele externe interrupts willen verwerken via de interruptingangen van poort 3. Ook de seriële poort en de timeringangen zitten als alternatieve functie op deze poort 3.
Pauwels D.
Embedded systems V1.0
15
Het besluit is dan ook dat we de 8051 controllers met vier poorten liefst single-chip gebruiken en niet met een externe businterface daar we immers op deze manier de voordelen (I/O) van de controller verliezen.
Fig. 3.2..1.3.
C517 systeem met externe EPROM en SRAM.
Een 8051 compatibele controller met businterface gebruiken wordt pas interessant wanneer we kunnen beschikken over extra I/O poorten zoals bv. het geval is bij de C517 van Infineon, die beschikt over 8 poorten, zoals aangegeven in de figuur 3.2.1.3. De RST (reset) ingang moet bij power-up even hoog worden gehouden ( bij de C517 is dit het inverse signaal, dat dus laag moet gemaakt worden) om de controller zich te laten initialiseren en met de programmauitvoering te beginnen op locatie 0000h. De oscillator pinnen (XTAL1, XTAL2) worden gebruikt om een quarts kristal aan te sluiten op de inwendige clockoscillator. Andere controlelijnen zijn: • de adres latch enable (ALE) pin die gebruikt wordt om de adres/databus te demultiplexen, • de program store enable (/PSEN) pin om het externe codegeheugen aan te spreken en • de external access (/EA) pin die gebruikt wordt om de controller in het externe codegeheugen instructies te laten ophalen. Pauwels D.
Embedded systems V1.0
16
3.2.2 De bus architectuur. De volgende figuur 3.2.2.1 geeft de bus-structuur aan voor het aanspreken van externe geheugen- of periferie-elementen. We zien hier een 16 bit adresbus en een 8 bit databus, samen met een viertal controlesignalen. Om het aantal poortpinnen die gebruikt worden voor de adres-en databus te beperken worden de laagste byte van de adresbus en de data tijdgemultiplexed op dezelfde bus. Het Address Latch Enable (ALE) signaal wordt gebruikt om de laagste byte van het adres te latchen en zo deze twee types informatie te demultiplexen. Op deze manier kan een stabiele 16 bit adresbus worden aangeboden aan het systeem, tewijl de adres/databus daarna wordt gebruikt voor een datatransfert. De /RD en /WR lijnen controleren de datastroom van en naar de externe datageheugenruimte. De /PSEN lijn controleert de datastroom van de externe programmaruimte. In deze architectuur zijn er geen voorzieningen voor een gescheiden I/O ruimte (bv. voor andere periferiechips enz.) wat wil zeggen dat externe periferie in de geheugenruimte moet worden ondergebracht, we zeggen dat de periferie "memory-mapped" is. 8051 compatibele controller P2
MSB adresbus (A8-A15)
ALE
P0
16-bit adresbus
Adres latch LSB adres/ databus AD0-AD7
LSB adresbus A0-A7
databus
P3
RD WR PSEN
Fig. 3.2.2.1. Busstructuur van een 8051 compatibele controller.
3.3 De externe timing. 3.3.1 De controllerclock en de relatie tot systeemtiming. De controller systeemclock bepaalt alle timing-eigenschappen van het systeem en wordt opgewekt door een interne oscillator in de controller. De XTAL1 en XTAL2 pinnen zijn de ingang en de uitgang van een inverterende versterker die kan werken als een on-chip oscillator van het Pierce type. Hiertussen sluiten we een quartskristal aan waarvan de parallelresonantiefrequentie gelijk is aan de gewenste clockfrequentie. Hoe hoger de clockfrequentie wordt gekozen, hoe sneller de controller instructies zal uitvoeren, maar hoe hoger het stroomverbruik en hoe groter de EMI. De minimum frequentie is afhankelijk van de functionaliteit van het systeem, m.a.w. de controller Pauwels D.
Embedded systems V1.0
17
moet snel genoeg zijn om het systeem normaal te laten werken. De minimum frequentie is ook afhankelijk van de technologie waarmee de controller intern is opgebouwd (fully static?). Het bereik van de clockfrequentie wordt in de datasheet van de controller opgegeven en heeft meestal een maximum van enkele (tientallen) MHz. Men gebruikt in heel wat 8051 compatibele systemen een clock (en dus ook een quartskristal) van 11.059 MHz. Men gebruikt deze frequentie omdat ze intern in de controller kan worden afgedeeld naar een standaard bitrate (communicatiesnelheid) voor de seriële poort. Naast dit quartskristal worden er ook nog twee capaciteiten vanuit XTAL1 en XTAL2 verbonden naar de massa, zoals wordt aangegeven in de figuur 3.3.3.1. De datasheet van een parallel resonant kristal geeft de load capaciteit weer, wat de serieschakeling is van C1 en C2. De waarde van C1 en C2 ligt meestal tussen 30 en 40 pF. Het vergroten van de capaciteiten heeft als gevolg dat de start-up tijd van de oscillator stijgt tot het punt waar de oscillator niet meer start. De capaciteitswaarden verminderen heeft als gevolg dat de oscillator kan gaan oscilleren op een hogere harmonische (overtone) van de grondfrequentie. Daar dit een circuit is waar signalen worden gegenereerd van relatief hoge frequentie worden er ook enkele eisen gesteld aan de fysische layout van het circuit (EMI). De signaalbanen op de PCB (Printed Cicuit Board) die het kristal, de capaciteiten en de controller oscillatorpinnen verbinden, moeten zo kort en zo breed mogelijk zijn om parasitaire inductantie en weerstand te beperken. Het kristal en de capaciteiten moeten dus zo kort mogelijk bij de oscillatorpinnen van de controller geplaatst worden, terwijl de signaalbanen naar de oscillator moeten afgeschermd worden van andere signalen om overspraak te vermijden, dit doet men meestal met een massavlak of met een massaring rond de signaalbanen.
Fig. 3.3.3.1.
Oscillatorcircuit dat meestal wordt gebruikt bij controllers.
De systeemclock bepaalt de interne clockphases, states en machinecycli m.a.w. de snelheid waarmee externe bustransacties en instructies worden uitgevoerd. Een machine cyclus bestaat uit 6 toestanden (states) en is 12 oscillator periodes lang. Elke state is onderverdeeld in een phase1 (P1) en een phase2 (P2). Een machinecyclus bestaat dus uit 12 oscillatorperiodes die genummerd worden van S1P1 (State1, Phase1) tot S6P2. Rekenkundige en logische bewerkingen vinden typisch plaats gedurende phase1 en register naar register transferts gedurende phase2.
Pauwels D.
Embedded systems V1.0
18
Fig. 3.3.3.2. Relatie tussen de clock en de verschillende states en machinecycli.
De uitvoering van een één-cyclus instructie start op S1P2 (stijgende flank PSEN), wanneer de opcode wordt binnengelezen in het instructieregister. Als het een twee-byte instructie is, wordt de tweede byte gelezen tijdens S4 van dezelfde machinecyclus. Wanneer het over een één-byte instructie gaat, zal er ook tijdens S4 een byte gelezen worden (dit is de volgende opcode) maar deze wordt niet verder gebruikt. In elk geval is de uitvoering klaar op het einde van S6P2. De meeste 8051 instructies worden uitgevoerd in één machinecyclus, maar sommige doen er twee of vier cycli over.
Pauwels D.
Embedded systems V1.0
19
3.3.2 Timing van de buscontrolesignalen. De timing en de logische gedachtengang van de controlesignalen is belangrijk om de buswerking van een 8051 controller te begrijpen. De timingdiagramma's voor het lezen van een byte uit het programmageheugen en het lezen en het schrijven van een byte uit het datageheugen worden hier in figuur 3.3.2.1 weergegeven en spreken voor zich.
Fig. 3.3.2.1. Controlesignalen bij verschillende types van busacces.
Pauwels D.
Embedded systems V1.0
20
Alle buscycli starten met het aanbieden van de adresinfo op P0 en P2. Op de dalende flank van ALE wordt de info op P0 in de externe latch opgeslagen, het volledige 16 bit adres is nu stabiel tot het einde van de buscyclus. P0 kan nu veranderen van waarde en de data kan nu weergegeven of gelezen worden langs P0. Voor het ophalen van een instructiebyte uit het programmageheugen zal de stijgende flank van de /PSEN lijn de data (programmabyte) binnenlezen. In het geval het datageheugen wordt aangesproken zal de stijgende flank van het /RD signaal aangeven wanneer de data gelezen wordt, de data moet door het geheugen tegen deze tijd stabiel op de databus geplaatst zijn. Bij het schrijven naar het datageheugen zal de stijgende flank van /WR aangeven dat de data stabiel op de databus zit en dat de geheugencomponent deze mag latchen (lezen). Het ALE signaal wordt gebruikt om de adres/data info op P0 te demultiplexen, maar zelfs bij het uitvoeren van intern opgeslagen code zal dit signaal worden gegenereerd, het is dus een nuttig signaal voor debugdoeleinden, dat aangeeft of de controller daadwerkelijk code uitvoert. In single-chip toepassingen kan het ALE signaal, afhankelijk van het type van controller, softwarematig worden uitgeschakeld wat nuttig is voor het beperken van EMI, ALE wordt immers anders geactiveerd met een constante frequentie van 1/6 van de oscillatorclock wat aanleiding kan geven tot elektromagnetische straling.
3.4 I/O systemen. De 8051 gebaseerde controllers hebben naast twee interne timers meestal minstens vier parallelle poorten en één seriële poort als I/O. Zoals reeds eerder werd aangehaald heeft door het beperkte aantal pinnen van een behuizing (bv. 40 pins dip) bijna elke pin een alternatieve functie toegewezen gekregen. Het nadeel is dat wanneer deze pinnen met hun alternatieve functie worden gebruikt, ze natuurlijk niet tegelijk als gewone I/O pinnen kunnen gebruikt worden. Eigenlijk kan dit wel, maar dit kan nogal vreemde gevolgen hebben, de gebruiker moet dan wel weten wat hij doet. Na een RESET zijn de vier poorten (I/O pinnen ) allemaal geconfigureerd als Input pinnen, en zijn de alternatieve functies uitgeschakeld. Over de verschillende I/O blokken die in de controllers aanwezig (kunnen) zijn zullen we het hebben in het hoofdstuk 'Interne bouwstenen van een microcontroller'.
Pauwels D.
Embedded systems V1.0
21
Hoofdstuk
4
Software 4.1 8051 software basics. Er bestaan verschillende programmeertalen voor de 8051 compatibele controllers. Ze kunnen geprogrammeerd worden in assembler of in één van de verschillende beschikbare 'hogere' programmeertalen zoals C, BASIC, PL/M of Forth. Om de verschillende mogelijkheden te kunnen begrijpen, is het belangrijk om machinetaal, assembler en de hogere programmeertaal te begrijpen. Machinetaal bestaat uit getallen die de microcontroller interpreteert als commando's en data die aangeven hoe een taak moet uitgevoerd worden. Een voorbeeld hiervan is de sequentie 02h,12h,34h in het programmageheugen. Deze getallen worden door de controller geïnterpreteerd als een sprong (02h, de operation code of opcode) naar een geheugenlocatie 1234h (de operand of data). De instructies voor een 8051 controller kunnen bestaan uit 1, 2 of 3 bytes, afhankelijk van de hoeveelheid data die nodig is in de instructie. Al de programma's in de verschillende programmeertalen die beschikbaar zijn, moeten eerst worden omgezet naar machinetaal vooraleer ze bruikbaar zijn voor de controller. Elke instructie die kan uitgevoerd worden door de controller heeft een unieke opcode (eerste getal). Daar opcodes nogal moeilijk zijn om mee om te gaan voor de programmeur worden ze voorgesteld door een engelstalige afkorting of samentrekking van een aantal woorden (Mnemonic) die aangeeft wat de instructie doet. Dit noemen we een assembler instructie. De instructie die hoort bij de vorige getalsequentie is LJMP 1234h. De assemblerinstructie is de vorm die bruikbaar is voor de programmeur, tewijl de machinetaal door de controller wordt gebruikt. De instructieset zijn alle mogelijke instructies (op het laagste niveau) die een controller kan uitvoeren en die dus ter beschikking staan van de programmeur om een programma te schrijven. De 8051 instructieset is geoptimaliseerd voor 8-bit en single bit controletoepassingen. Zo zijn er heel wat instructies die toelaten 1-bit variabelen (Booliaanse variabelen) te manipuleren. Deze bitvariabelen kunnen bits zijn van interne poorten of van verschillende special-function registers of, bits uit interne bit-aanspreekbare RAM locaties voor het gebruik als flags. Dit is nuttig voor heel wat controle- en logische functies die Booliaanse bewerkingen vereisen. Dit wordt duidelijk in de volgende delen van de tekst. Assembler programma's laten totale controle toe over uitvoeringssnelheid en geheugengebruik, dit betekent dat de programmacode compacter kan gemaakt worden dan bij een hogere programmeertaal en de uitvoeringssnelheid perfect kan gecontroleerd worden.
Pauwels D.
Embedded systems V1.0
22
Wanneer men over weinig geheugen beschikt en de timing kritisch is kan het aangeraden zijn om in assembler te programmeren. Het grote nadeel van assembler is dat de programmeur elk detail van het programma voor zijn rekening moet nemen, inclusief geheugen allocatie voor code en data, registergebruik, en de controle over de opslag van variabelen. Hogere programmeertalen verlossen de programmeur van de details die gepaard gaan met assemblerprogrammatie. Geheugen allocatie, beveiliging van variabelen, stackgebruik en vele andere details worden afgehandeld door de compiler. Het nadeel hiervan is echter dat de programmeur de controle verliest over deze details. Gewoonlijk zal de machinecode die resulteert van een programma, dat werd gemaakt in een hogere programmeertaal, beduidend groter zijn en trager werken dan de code uit een assembler programma dat dezelfde taak uitvoert. De keuze van de taal is afhankelijk van de aard van het project. Een programma schrijven op een succesvolle manier, in om het even welke taal vereist echter een grondige kennis van de controller en zijn periferiebouwstenen en van het totale embedded systeem. Hierdoor is het schrijven van embedded software een uitdagender en dikwijls veel complexer geheel dan het schrijven van desktop software.
4.2 De interne 8051 familie architectuur. De CPU van de 8051 familie is een 8-bit processor die o.a. bestaat uit volgende delen: een instructiedecoder, een arithmatic section met hierin de ALU of Arithmatic Logic Unit (rekenkundige en logische eenheid), een program controller, een timing- en controledeel en een array van registers (8-bit geheugenlocaties in de controller) die aanspreekbaar zijn door de programmeur. Elke programma-instructie wordt door de instructie decoder ontcijferd, en de juiste interne timing signalen worden door de timing/controle-eenheid gegenereerd voor het controleren van de interne werking van de verschillende delen van de CPU. De Timing/Controle eenheid zorgt voor de syncronisatie van de dataflow in en uit de CPU. Hij coördineert de verplaatsing van data op de interne en externe bussen van de microcontroller en hij genereert de /PSEN, /RD en /WR signalen waarvan eerder sprake. De arithmatic section zorgt ervoor dat de data op de juiste manier bewerkt wordt en bestaat uit de ALU ( Arithmatic & Logic Unit), ACCU of A werkregister, B hulpregister, en PSW (Processor Status Word). De ALU is verantwoordelijkvoor het rekenkundig bewerken van bytes. Hij handelt de optelling, aftrekking, vermenigvuldiging, deling en de logische bewerkingen af zoals 'and'en en 'or'en van data. De ALU zit intern in de CPU en staat niet rechtstreeks onder controle van de programmeur. Het enige deel van de CPU waar de programmeur directe controle over heeft zijn de registers. Registers zijn in dit geval byte-brede latches (geheugenlocaties) die gebruikt worden om data te stockeren en te manipuleren. De registers bij de 8051 familie bevatten: • een accumulatorregister (ACCU of gewoon A) dat ingangsdata bevat voor de rekenkundige bewerkingen die met de ALU worden uitgevoerd. De ACCU is ook het register dat het resultaat bevat na een rekenkundige bewerking met de ALU, • een 16 bit datapointer register (DPTR), dat gebruikt wordt voor het aanwijzen van data die in het externe geheugen zitten en die moeten worden gemanipuleerd,
Pauwels D.
Embedded systems V1.0
23
•
het PSW is een geheugenplaats (register) met een speciale functie. Het bestaat uit een aantal bits die informatie vasthouden over de processor status, of m.a.w. over de instructies die reeds uitgevoerd werden door de µC.
Fig. 4.2.1. Het processorstatusregister van een 8051 controller.
De CY bit (CarrY flag), is een bit die aangeeft of er een overflow of underflow conditie is opgetreden, tijdens een vorige bewerking. De carry-bit wordt ook gebruikt als accumulator bij Booleaanse (bit) bewerkingen. De RS0 en RS1 bits worden gebruikt om één van de vier registerbanken te selecteren zoals aangegeven in de tabel onderaan in figuur 4.2.1. De AC (Auxiliary Carry) wordt gebruikt bij BCD bewerkingen en geeft een overflow aan van de LS nibble naar de MS nibble. De P bit geeft de pariteit aan van de accu. P=1 bij een oneven aantal 1's in de accu. De twee bits F0 en F1 zijn general purpose flagbits voor de gebruiker. •
een reeks general-purpose registers (registerbanken) voor opslag van data.
De program controller controleert de sequentie waarin de instructies worden uitgevoerd door de CPU. Een 16-bit Program Counter (PC) register bevat steeds het adres van de volgende uit te voeren instructie. Het PC register wordt beïnvloed door de spronginstructies. Al deze registers zitten in het inwendige datageheugen van de microcontroller. De registers van een 8051 controller bestaan uit twee soorten registers, de special-function registers (SFR's) en de registerbanken.
Pauwels D.
Embedded systems V1.0
24
4.2.1 De interne geheugen architectuur. De 8051 familie heeft intern een aantal datageheugenlocaties (RAM) die het mogelijk maken de controllers single-chip te gebruiken. Het interne datageheugen is opgedeeld in twee functionele blokken. Eén blok van 128 bytes data RAM en één blok van 128 mogelijke Special Function Registers ook wel SFR’s genoemd. De interne geheugenindeling wordt aangegeven in de figuur4.2.1.1.
Fig. 4.2.1.1. Intern datageheugen van een 8051 controller.
Het data RAM gedeelte bestaat op zijn beurt uit twee delen: de gewone interne geheugenlocaties voor het opslaan van data en een deel geheugenlocaties die we registers noemen. Dit is een blok van general purpose registers die gegroepeerd zijn als 4 banken van acht registers die we R0 tot R7 noemen. De registers van dit registerblok dienen voor het opslaan van tijdelijke gegevens, het zijn de normale processorregisters. Zie figuur 4.2.1.4 . De SFR’s zijn geheugenlocaties met een speciale functie in de controller zoals de CPU registers (bv. de ACCU, B,het PSW en pointers zoals SP, en de datapointer). Of het zijn geheugenlocaties waarvan de geheugencellen gebruikt worden om de op de chip aanwezige I/O componenten te besturen. Zo zijn oa. de poorten ook SFR's. De data die in deze SFR’s geplaatst wordt, wordt door de bijhorende I/O component (bv, een timer) gebruikt om zijn werking te bepalen. Op deze manier worden de interne periferiecomponenten via software instelbaar. Een belangrijke eigenschap van de SFR's is hun resetwaarde. Na een power-up of reset van de 8051 wordt in de meeste SFR's een specifieke opstartwaarde geplaatst. Dit is belangrijk, want zo kan men interne periferie uitschakelen om te voorkomen dat er na het opstarten onvoorspelbare fouten zouden optreden. Zo worden bv. de poorten na een reset allemaal op een logisch 1 niveau (input mode) gezet, iets waar men in de software rekening moet mee houden.
Pauwels D.
Embedded systems V1.0
25
De SFR's nemen meestal ook niet alle 128 beschikbare geheugenadressen in. De niet gebruikte adressen kunnen echter niet gebruikt worden voor dataopslag, de geheugencellen op deze adressen bestaan gewoon niet. Dit wordt aangegeven in figuur 4.2.1.2.
Fig. 4.2.1.2. SFR's bij een 8051 compatibele AT89S8252.
Microcontrollers hebben als voordeel t.o.v. processoren dat hun SFR's intern zitten en dat ze kunnen worden aangesproken als CPU registers. Een controller kan de SFR's dus snel aanpassen en kan ook met SFR's rekenen, iets wat geen enkele processor kan. Om al de interne geheugenlocaties (datalocaties, registers en SFR's) aan te spreken zijn er 256 adressen van 00h tot FFh voorzien. Om deze twee soorten geheugenlocaties, data of SFR's, aan te spreken wordt er gebruik gemaakt van instructies met “directe en indirecte” adressering. Bij directe adressering staat het adres van de geheugenlocatie waarop de instructie betrekking heeft in de operand (na de op-code) van de instructie zelf. De hoogste 128 bytes interne locaties (80h tot FFh) zijn voorzien voor de SFR’s. Voor de SFR’s kan enkel de directe adressering gebruikt worden. Bij indirecte adressering staat in de operand van de instructie het adres van een andere interne geheugenlocatie, zeg maar register, en die geheugenplaats bevat op haar beurt het adres van de geheugenplaats die we willen aanspreken met de instructie. Dit register kan dus beschouwd worden als een pointer naar een interne locatie. Dit lijkt zeer omslachtig doch voor veel toepassingen o.a. het aanspreken van tabellen is het een zeer handige adresseermethode.
Pauwels D.
Embedded systems V1.0
26
De laagste 128 bytes, van de interne data RAM, met de adressen 00H tot 7FH kunnen volgens beide adresseermethodes aangesproken worden. De meeste varianten van de 8051 famile hebben net zoals de 8052 (een 8051 compatibele opvolger) nog 128 bytes extra intern datageheugen. Er zijn in de architectuur echter maar 256 interne geheugenlocaties voorzien en deze zijn reeds door de vorige blokken ingenomen. Als oplossing voor dit probleem heeft men de 128 extra geheugenlocaties op dezelfde interne adressen geplaatst als de SFR's, dus van 80h tot FFh. Zie figuur 4.2.1.3. Er is toch geen conflict tussen beide overlappende delen doordat men ervoor zorgde dat de extra locaties enkel via indirecte adressering kunnen aangesproken worden, waar dit voor de SFR's, die op dezelfde adressen zitten, met directe adressering het geval was.
Fig. 4.2.1.3. Intern datageheugen van een 8052 compatibele controller
Waarom wordt er nu een onderscheid gemaakt tussen de gewone interne RAM locaties en de registers? De lengte (het aantal bytes) van de opcode is heel belangrijk. De opcode van een instructie is de bitcombinatie in de instructie, die aangeeft over welke instructie het gaat. Hoe langer de opcode, hoe meer tijd de CPU nodig heeft om de instructie uit het geheugen te halen en uit te voeren. Voor een µC waar het programma in het interne codegeheugen (bv. Flash of eprom) zit is dit ook heel belangrijk, want deze hoeveelheid geheugen is beperkt. De fabrikanten besteden dan ook de nodige aandacht aan het inkorten van opcode’s. Ideaal zijn instructies die 1 byte lang zijn, de CPU kan ze dan in één maal ophalen. Dit is onmogelijk voor instructies die het adres bevatten van een interne geheugenlocatie, want dit adres neemt zelf al één byte in beslag! Om dit op te vangen geeft men bij een 8051 compatibele µC acht geheugenlocaties een drie bit adres. Pauwels D.
Embedded systems V1.0
27
Dit laat toe om in een 8-bit opcode, waar er dan nog 5 bits vrij zijn, ook nog de aard van de instructie te omschrijven. De instructies die dit 3 bit adres gebruiken kunnen dus enkel voor die 8 geheugenlocaties gebruikt worden. Deze 8 geheugenlocaties behoren tot de set van 128 locaties die zowel direct als indirect adresseerbaar zijn. Ze behouden echter ook ten allen tijde nog hun normaal 8 bit adres. Omdat deze instructies veel gebruikte instructies zijn, ze zijn immers snel, had men ze graag laten inwerken op meer geheugenlocaties, en dit zonder de opcode te verlengen. Dit kan enkel als meer adresbits gebruikt worden, waardoor er dan weer minder bits ter beschikking staan om de aard van de instructie aan te geven. De oplossing voor dit probleem is de extra adresbits niet in de opcode te stoppen, maar ze in een SFR onder te brengen. Bij een 8051 compatibele CPU zitten er zo 2 extra adresbits (RS1 en RS0) in het PSW register ( Processor Status Word ). De programeur kan deze 2 extra adresbits aanpassen, en er kan op deze manier met deze 2 bits een selectie gemaakt worden tussen 4 groepen (BANKEN) van telkens 8 geheugenlocaties met een 3-bit adres nl. registers R0 tot R7 . Deze 4 BANKEN van telkens 8 registers zitten onderaan in de laagste 128 databytes. De 8 registers hebben ook telkens dezelfde naam gekregen, R0 tot R7. De registerstructuur in de laagste 128 interne databytes wordt weergegeven in figuur 4.2.1.4 waar ook de 4 registerbanken terug te vinden zijn.
Fig. 4.2.1.4. Registerstructuur in de laagste 128 interne geheugenlocaties.
Merk op dat na de vier registerbanken (vanaf adres 20h) er 16 bytes datageheugen zitten die ook op bitbasis (flags) kunnen aangesproken worden met het aangegeven bitadres. Dit kan via één van de bitmanipulatie-instructies (bit set, bit clear, bit and, bit or, bit complement, bit Pauwels D.
Embedded systems V1.0
28
test) die typisch zijn aan deze controller. Ze kunnen echter ook ten alle tijde als een gewone byte worden aangesproken. Het deel van 30h tot 7Fh (scratch pad area) kan gebruikt worden voor het opslaan van data en variabelen.
4.2.2 Programma- flow en controle. Zoals reeds gezegd, zijn alle microprocessoren essentiëel sequentiële machines. Ze kijken naar een lijst van instructies (machinecode) en voeren ze één na één uit tot de voedingsspanning wegvalt of tot ze softwarematig gestopt worden (low power gebruik). Het 16-bit register dat aangeeft waar de volgende uit te voeren instructie opgeslagen is noemt men de Program Counter (PC). De inhoud van de PC wordt bij het begin van elke buscyclus gebruikt om de adresbus te sturen. Bij het uitvoeren van de instructie zal de PC aangepast worden zodat het adres van de volgende instructie aangegeven wordt. Dit kan het adres zijn van een instructie die in het codegeheugen volgt op de instructie die wordt uitgevoerd. Maar bij spronginstructies is dit het adres waar naartoe gesprongen moet worden. Na een RESET wordt de PC op 0000h gezet. Op dit fysisch adres moet dus het programma starten.
4.2.3 De systeemstack. De STACK van een µP of µC systeem is een deel van het RAM geheugen dat gebruikt wordt voor het tijdelijk opslaan van gegevens, en heeft een FILO (first-in, last-out) structuur. De stack van een i8051 compatibele microcontroller kan alleen in interne RAM geplaatst worden. De gegevens die in de stack bewaard worden zijn de terugkeeradressen bij: een subroutineoproep via een CALL instructie, of bij een interrupt-routine oproep via interruptverwerking. Bij de stack hoort een Stack Pointer (SP), wat ook een SFR is. Deze SP geeft de eerst volgende vrije plaats aan in de STACK (RAM geheugen) waar de controller een terugkeeradres kan bewaren bij een CALL of INTERRUPT. Na RESET staat de SP op 07H (default waarde). De SP wordt automatisch geïncrementeerd, voordat data wordt gestockeerd op de plaats waar de SP naar wijst ( pre-increment ) bij een call instructie, de SP wordt gedecrementeerd nadat de data werd gelezen ( post-decrement ) tijdens een return ( RET,RETI) instructie. Dit mechanisme verloopt trouwens volledig automatisch! Als programmeur moet u hiervoor niets ondernemen. De stack kan zich overal in interne RAM bevinden, en moet dus niet starten op adres 08H. De programmeur kan dit veranderen door in de SP een andere waarde te plaatsen a.d.h.v. een MOV SP,# xx instructie, waar xx staat voor het adres waar de stack moet starten. Dit automatische mechanisme bij een subroutine-call wordt aangegeven in fig 4.2.3.1. De programmeur kan ook zelf data op de stack plaatsen, en data van stack halen, door gebruik te maken van resp. PUSH en POP instructies. Hij moet dit echter doen met de nodige omzichtigheid, immers de stack heeft een FILO structuur. Data-elementen die in een bepaalde volgorde op stack worden geplaatst moeten er in de omgekeerde volgorde weer worden afgehaald. Indien de programmeur in een subroutine, of in een interruptroutine, (terugkeeradres staat telkens op stack) data-elementen op de stack plaatst, moeten deze er weer worden afgehaald vooraleer de subroutine of interruproutine wordt verlaten. Pauwels D.
Embedded systems V1.0
29
Op deze manier zal de RETurn instructie het correcte terugkeeradres van de stack kunnen halen en op de juiste plaats terugkeren in de onderbroken routine. In het andere geval zullen twee databytes van stack worden gehaald en zullen deze door de controller worden aanzien als het terugkeeradres. De controller maakt dan een sprong naar een willekeurig programma-adres. Onnodig te zeggen dat dit tot zeer ergerlijke fouten en willekeurig gedrag van het systeem kan leiden.
Externe controllergeheugen met instructies
Adres van de instructies 16-bits
instructie n instructie n+1 instructie n+2 instructie n+3 call subroutine instructie n+5 instructie n+6
XXYY
9
Stack-pointer register (wijst naar intern geheugen in de controller)
Interne controllergeheugen
SP 8-bit SP na reset
1
SP+1
3
SP+1
2
opslaan LSB
4
opslaan MSB
YY XX
5
07h 08h 09h 0Ah 0Bh
PC = subroutine
Program counter 16-bit
Jump to XXYY
PChigh
PClow
subroutine
6
Jump to subroutine
7
PChigh =XX SP-1
8
PClow =YY SP-1
return
Fig. 4.2.3.1. Voorbeeld van stack-werking bij een CALL en RETurn instructie.
Pauwels D.
Embedded systems V1.0
30
In de figuur 4.2.3.1 wordt aangegeven hoe het stackmechanisme werkt bij een CALL- en een RETurn instructie. Als vertreksituatie nemen we aan dat de SP nog op de resetwaarde 07h staat en dat de controller instructies uitvoert in een hoofdroutine en zo bij de 'CALLsubroutine' instructie komt. De controller zal een routine op het adres 'subroutine' moeten gaan uitvoeren, maar eerst zal de SP met één worden verhoogd en zo wijzen (1) naar de volgende vrije locatie 08h op de stack. Dan zal de controller de low byte van het adres van de volgende uit te voeren instructie (YY) op stack plaatsen (2), deze haalt hij uit de PC. De volgende actie is de SP weer met één verhogen tot locatie 09h (3) en ook de high byte (XX) van het adres op stack plaatsen (4). De PC kan nu worden geladen met het adres 'subroutine' (5) zodat de controller op dit adres de volgende instructie gaat ophalen (sprong naar subroutine). Bij het beëindigen van de subroutine zal de RETurn instructie als gevolg hebben dat de high byte van de PC zal geladen worden met de inhoud van de geheugenlocatie waar de SP naar wijst (7) en dat de SP daarna met één wordt verminderd. Daarna wordt op het adres waar de SP naar wijst de byte opgehaald die in de low byte van de PC wordt geplaatst (8) waarna de SP weer met één wordt verminderd. De SP staat nu op de waarde die hij had bij aanvang. Doordat de PC nu is geladen met het terugkeeradres (XXYY) dat op stack werd geplaatst bij de CALL, zal de vogende instructie op dit adres worden opgehaald, de controller is m.a.w. terug naar de hoofdroutine gesprongen. Dit mechanisme verloopt volledig automatisch bij een CALL en RET instructie en de programmeur moet hiervoor geen acties ondernemen.
Pauwels D.
Embedded systems V1.0
31
4.3 8051 assembler. 4.3.1 Adressering. Om de instructieset van de controller te begrijpen, moet men eerst nagaan hoe de controller data in het systeem kan aanspreken of adresseren. De algemene vorm van een instructie, met tussen vierkante haakjes een parameter die niet steeds aanwezig is of [OPTIONEEL] wordt hier aangegeven. MNEMONIC [BESTEMMING],[BRON] MNEMONIC:
de omschrijving van wat de instructie doet. vb: MOV = VERPLAATS ADD = TEL OP...
BESTEMMING:
het register waarop de bewerking wordt uitgevoerd, of de bestemming van het te verplaatsen getal, soms optioneel. vb: 030H = adres register 30H A = de accumulator R0 = verkort adresseerbaar register, ....
BRON:
het register of het getal dat aangeeft van waar de tweede operand moet komen, of wat de tweede operand is. Deze parameter is niet steeds aanwezig (optioneel).
MOV MOVC MOVX
A Rn DIRECT @Ri @DPTR DPTR C BIT
,A ,Rn ,DIRECT ,@Ri ,@DPTR ,#GETAL 8/16 BIT ,@A+DPTR ,@A+PC ,C ,BIT
MOV: Mnemonic voor de verplaats instructie tussen registers of bits. MOVC: Mnemonic voor de verplaats instructie met het externe codegeheugen. MOVX: Mnemonic voor de verplaats instructie met het externe datageheugen. EERSTE KOLOM: Alle mogelijke bestemmingen. TWEEDE KOLOM: Alle mogelijke bronnen. Niet alle combinaties tussen de kolommen zijn mogelijk. Om na te gaan wat welke mogelijkheden toegelaten zijn moet de instructieset geraadpleegd worden.
Pauwels D.
Embedded systems V1.0
32
Verklaring van bronnen en bestemmingen: A:
accumulator (rekenregister)
Rn:
werkregister uit de huidig geselecteerde bank (twee bits in PSW)
direct:
een 8 bit hex getal dat het adres is van een direct adresseerbaar register vb: 15H
@Ri:
i kan 0 of 1 zijn. Die twee registers kunnen gebruikt worden voor indirecte adressering. @ wil zeggen dat indirecte adressering gebruikt wordt.
DPTR:
de Data PionTeR is het enige register dat door de CPU als een 16 bit register kan gebruikt worden. Het bestaat uit twee 8 bit registers (DPH en DPL), die afzonderlijk ook aangesproken kunnen worden. Voor sommige instructies worden ze als een geheel gezien. De DPTR is het enige register dat met een instructie aangepast of geladen kan worden als 16 bit register.
@DPTR:
de DPTR is het enige register dat gebruikt kan worden voor indirecte adressering met een 16 bit adres. Dit is ook de enige manier om gegevens uit het external data- of code memory op te halen. Dit is een omslachtige werkwijze, aangezien eerst de datapointer geladen moet worden met het adres van de variabele, alvorens via de indirecte adressering de variabele gelezen of geschreven kan worden. Als bron of bestemming in de CPU kan alleen de accu gebruikt worden.
@A+DPTR: idem voorgaande, maar bij de waarde in de DPTR, wordt voor de duur van de instructie, de waarde van de accu opgeteld. Dit is een vorm van geïndexeerde adressering. @A+PC:
PC staat voor Program Counter of programmateller. Dit 16 bit register wordt door de CPU gebruikt om bij te houden op welk adres de volgende opcode gelezen moet worden. Deze adressering werkt enkel met code memory. Dit is een vorm van geïndexeerde adressering.
#getal:
Wordt het '#' teken gebruikt, voor een getal, dan wordt het getal als een numerische waarde gezien, in het andere geval zal de assembler het getal als een adres zien (directe adressering). Het getal is normaal 8 bit, alleen voor het laden van de DPTR, wordt een 16 bit getal toegelaten.
C:
carry vlag in de PSW.
BIT:
elke bit die via bitadressering bereikbaar is.
Pauwels D.
Embedded systems V1.0
33
Enkele voorbeelden kunnen dit duidelijk maken. MOV A,#03FH
;plaats in de accu het getal 3FH
Het getal dat in de accu moet komen staat expliciet als operand in de binaire instructie. We spreken in dat geval van immediate adressering. Getallen mogen in verschillende notaties ingegeven worden: 030H = 030 of 030D =
030O =
het getal 30 HEXADECIMAAL (16 tallig talstelsel)
het getal 30 in het decimale talstelsel. Het vertaalprogramma (ASSEMBLER) zal dit getal wel eerst omzetten naar hexadecimaal, alvorens het in de instructie te plaatsen. 030 of 030D wordt dan 01EH. De omrekening zie je enkel in de opcode, ze is niet merkbaar in de broncode (naam.ASM file), en ook niet in de listing (naam.LST). octaal talstelsel. Verder verloopt de verwerking zoals bij de decimale getallen. Het getal wordt 18H.(Dit talstelsel passen we niet toe.)
000100001B = binair talstelsel. Dezelfde redenering is van toepassing zoals bij de decimale getallen. Het getal wordt 21H. Merk op dat er voor elk getal een extra 0 wordt ingegeven. De 0 heeft geen numerische betekenis, en is enkel nodig om de assembler een foutloze omzetting te laten uitvoeren. De assembler herkent getallen alleen wanneer ze met een cijfer tussen 0-9 beginnen. Voor hexadecimale getallen die beginnen met de letters A-F is de extra 0 noodzakelijk. Uit gewoonte plaatsen we de 0 er overal bij.
MOV A,03FH
;neemt een kopie van de inhoud van het ;register met adres 3FH naar de accumulator
Alle adressen tussen 00H en FFH zijn toegelaten. Deze registers bevinden zich in de controller. Voor de onderste 128 registers is er geen verwarring mogelijk omdat zij slechts een keer in de controller aanwezig zijn. Voor de adressen 80H-FFH zijn er bij de meeste 8051 compatibelen (vanaf 8052) per adres twee registers aanwezig (zie figuur 4.2.1.3). Omdat het adres van het bronregister expliciet in de instructie is opgenomen spreken we van directe adressering. Voor de mogelijke talstelsels die gebruikt mogen worden bij de ingave verwijzen we naar de immediate adressering. MOV A,Rn
;zet in de accumulator de inhoud van ;een verkort adresseerbaar register.
In de figuur 4.2.1.4 kan je zien dat er 8 registers zijn waarop de verkorte adressering van toepassing is: R0 t.e.m. R7. Ze worden ook wel een registerbank genoemd. De verkorte adressering geeft kortere (en dus ook snellere) opcodes dan andere vormen van directe adressering. In de controller zijn er 4 registerbanken aanwezig. De verkorte adressering is slechts van toepassing op één bank. In de PSW zijn er twee bits die bepalen welke bank dat is. De PSW is opgenomen in figuur 4.2.1. Pauwels D.
Embedded systems V1.0
34
Bovendien zijn er instructies die alleen gebruikt kunnen worden met de verkorte adresseerbare registers (R0-R7). Samen met de accumulator en het PSW register vormen deze registers de meest gebruikte registers in een programma.
MOV A,@Ri
;in Ri (i=0 of 1), staat een adres dat verwijst naar het eigenlijke ;register dat we wensen te lezen.
Dit type van instructies wordt vooral gebruikt voor het adresseren van tabellen. Het wordt indirecte adressering genoemd omdat het adres van het te lezen/schrijven register niet expliciet in de instructie voorkomt. Wel wordt de plaats aangegeven waar dat adres te vinden is. Er zijn zo maar twee mogelijke plaatsen: R1 of R0 van de huidig geselecteerde registerbank. De vier instructies die werden besproken geven bijna alle mogelijke manieren aan om gegevens tussen registers te verplaatsen. Om dit te illustreren hebben we MOVE instructies gebruikt. Ook bij de andere instructies zijn dezelfde adresseringsmethoden mogelijk. Bij het maken van een programma in assembler kan je best met de instructielijst controleren of de instructie die je wenst te gebruiken wel bestaat. In de volgende drie voorbeelden geven we aan hoe het mogelijk is gegevens van en naar het externe geheugen te verplaatsen. Hier kán enkel indirecte adressering gebruikt worden via het 16 bits DPTR register. Omdat het externe adres 16 bit groot is, kan R0 of R1 niet meer gebruikt worden. Die zijn immers maar 8 bit groot. Daarom zijn er in de controller 2 SFR’s DPH en DPL beschikbaar die samen een 16 bit DPTR register vormen. Ze zijn zowel afzonderlijk, als twee 8 bit SFR registers bruikbaar, of als één 16 bit SFR. Sommige instructies gebruiken enkel de 16 bit samenvoeging. Die zijn herkenbaar door dat in de mnemonic de afkorting DPTR gebruikt wordt. Om ze als 8 bit registers aan te spreken kan men alle die instructies gebruiken die van toepassing zijn op SFR’s (directe adressering). MOVX MOVX
A,@DPTR @DPTR,A
;lezen van het externe data RAM geheugen. ;schrijven naar het externe RAM geheugen.
In beide gevallen bevat de DPTR het adres van de externe RAM locatie die gelezen/geschreven wordt. Het lezen of schrijven naar externe RAM kan enkel via de accumulator gebeuren.
MOVC
A,@A+DPTR
;lezen van het codegeheugen.
Het externe programmageheugen kan enkel gelezen worden via de accumulator. Vermits het een ROM geheugen is, heeft het geen zin er naar te schrijven. Er zijn dan ook geen hardware mogelijkheden om dat te doen.
Pauwels D.
Embedded systems V1.0
35
4.3.2 De 8051 instructieset. De 8051 instructieset is geoptimaliseerd voor 8-bit controletoepassingen. Er zijn enkele adresseringsmethodes voor het snel aanspreken van variabelen in interne RAM locaties. De instructieset is ontworpen om op een vlotte manier bit-variabelen te manipuleren voor controlesystemen en systemen waar veel logische bewerkingen moeten gebeuren. Een overzicht van de 8051 instructieset kan een indruk geven van de verschillende mogelijkheden. Rekenkundige bewerkingen.
Tabel 4.3.2.1 8051 Instructies voor rekenkundige bewerkingen.
De 8051 CPU core is accumulator gebaseerd, dit heeft gevolgen voor de instructieset. De meeste bewerkingen kunnen enkel met deze ACCU (A) gebeuren, wat als gevolg heeft dat er in een programma ook heel wat instructies zullen zitten die eerst de nodige data in de ACCU plaatsen en daarna het resultaat weer terug in geheugenlocaties of registers copieëren. Merk op dat elke byte van het inwendige datageheugen kan worden geïncrementeerd of gedecrementeerd zonder gebruik van de ACCU. De DAA is bedoeld voor BCD bewerkingen. Bij BCD bewerkingen moet een ADD of ADDC altijd direct worden gevolgd door een DAA instructie, deze zorgt ervoor dat het resultaat naar BCD vorm wordt gecorrigeerd. De DAA instructie converteert geen binair getal naar BCD vorm. De DAA instructie heeft enkel zin als tweede stap na de optelling van twee BCD bytes! In de instructieset kan voor iedere instructie in detail worden nagegaan wat het effect is op de flags in het PSW. Pauwels D.
Embedded systems V1.0
36
Logische bewerkingen.
Tabel 4.3.2.2 8051 Instructies voor logische bewerkingen.
De instructies die logische bewerkingen uitvoeren op bytes (AND, OR, Exclusive OR, NOT) voeren deze uit op een bit-met-bit basis. De meeste instructies spreken voor zich. De SWAP instructie verwisselt de high en low nibbles van de ACCU, wat nuttig kan zijn bij BCD bewerkingen. De ondersteuning in de instructieset voor de BCD bewerkingen is het gevolg van het feit dat heel wat embedded systemen nog altijd LED zeven segment displays gebruiken als aanduiding. Dit zijn instructies die minder worden gevonden op een processor, omdat ze daar minder relevant zijn.
Pauwels D.
Embedded systems V1.0
37
Data transfert instructies. Interne dataRAM.
Tabel 4.3.2.3 8051 Instructies voor interne datatransferts.
Dit zijn de instructies voor het verplaatsen van data in het interne datageheugen. Merk op dat de SFR's enkel via directe adressering en de bovenste 128 bytes data RAM (indien ze aanwezig zijn) enkel via indirecte adressering dienen aangesproken te worden. Bij de 8051 controllers zit de stack in interne RAM, en deze groeit naar hoger liggende adressen. PUSH en POP instructies kunnen enkel directe adressering gebruiken om de byte aan te duiden die op stack moet bewaard worden of die van stack moet gehaald worden. De stack zelf wordt aangesproken met indirecte adressering via het SP register. Dit betekent dat de stack ook in de hoogste 128 bytes datageheugen gelocaliseerd kan worden. Extene RAM.
Tabel 4.3.2.4 8051 Instructies voor externe datatransferts in dataspace.
Pauwels D.
Embedded systems V1.0
38
Dit zijn instructies voor het aanspreken van het externe datageheugen (/RD en /WR worden geactiveerd bij het uitvoeren van deze instructies). Hier kan enkel indirecte adressering gebruikt worden. Men heeft de keuze om een één-byte adres, @Ri, te gebruiken waar Ri ofwel R0 of R1 van de geselecteerde registerbank kan zijn . Ofwel gebruikt men een twee-byte adres,@DPTR. Het nadeel van een 16-bit adres te gebruiken als er maar enkele Kbytes externe RAM aanwezig zijn in het systeem is, dat er steeds een 16 bit adres gebruikt wordt. Dus alle 8 bits van poort 2 worden als adresbus gebruikt. Wanneer een 8-bit adres gebruikt wordt moet de programmeur op poort 2 zelf de nodige resterende adresbits aanbieden, maar kunnen de overige bits van poort 2 gebruikt worden voor een andere toepassing. Extern programmageheugen, lookup tabellen.
Tabel 4.3.2.5 8051 Instructies voor externe datareads uit code space.
Dit zijn de enige twee instructies die kunnen gebruikt worden voor het lezen van data uit het (externe) programmageheugen. Indien het externe programmageheugen wordt aangesproken zal het controlesignaal PSEN geactiveerd worden als lees-strobe. Ze worden meestal gebruikt voor het lezen van gegevens uit een lookup tabel. Merk op dat de instructies enkel toe laten dat er gelezen wordt en dat er geen gegevens kunnen geschreven worden naar programmageheugen. Booleaanse (bit) instructies.
Tabel 4.3.2.6 8051 Instructies voor bit-bewerkingen.
Pauwels D.
Embedded systems V1.0
39
De 8051 controllers beschikken over een complete Booleaanse (single-bit) processor. De interne data RAM bevat 128 afzonderlijk adresseerbare bits, en in de SFR's kunnen ook 128 adresseerbare bits voorkomen ondergebracht in de verschillende registers de de hardware besturen. Alle poorten zijn bitadresseerbaar en elke poortpin kan als een afzonderlijke single-bit poort beschouwd worden. De instructies die deze bits aanspreken zijn niet alleen conditionele sprong-instructies, maar een complete set instructies zoals move, set, clear, complement, OR en AND instructies. Dit is typisch voor de 8051 en één van zijn grote troeven, de meeste andere processor- of controllerfamilies kunnen enkel op bytebasis hun poorten aanspreken. Een bitvariabele kan enkel via directe adressering worden aangesproken, waarbij bitadressen 00h tot 7Fh in de data RAM zitten en bitadressen 80h tot FFh in de SFR's. Sprong instructies. Onconditionele spronginstructies.
Tabel 4.3.2.7 8051 Jump (branch) instructies.
In deze tabel wordt enkel een 'JMP addr ' aangegeven, maar er bestaan er eigenlijk drie nl. SJMP, AJMP en LJMP. Ze verschillen enkel in het bereik van het sprongadres. JMP is een 'generische' mnemonic die door de programmeur kan gebruikt worden wanneer de gegenereerde code voor hem om het even is. Bij de SJMP instructie wordt het sprongadres als een relatieve offset geëncodeerd t.o.v. het startadres van de instructie die volgt op de SJMP. De instructie is twee byte lang en bestaat uit de opcode en de relatieve offsetbyte, die een complement 2 getal is. Het sprongbereik is dus beperkt van -128 tot +127 bytes relatief t.o.v. de instructie die volgt op de SJMP. De AJMP instructie encodeert het sprongadres als een 11-bit constante. De instructie is twee bytes lang en bestaat uit de opcode, die al 3 van de 11 adresbits bevat en een byte die de 8 laagste adresbits bevat. Bij het uitvoeren van deze instructie worden deze 11 bits gewoon in de 11 laagste bits van de PC geplaatst, en de 5 hoogste bits blijven onveranderd. Het sprongadres ligt dus in dezelfde 2K blok als de instructie die volgt op de AJMP. De LJMP instructie encodeert het sprongadres als een 16-bit constante. De instructie is drie bytes lang en bestaat uit een opcodebyte gevolgd door twee adresbytes. Het sprongadres kan dus om het even waar liggen in het 64K programmageheugenbereik. In asasembler zal de programmeur het sprongadres steeds op dezelfde manier aangeven, als een label of als een 16-bit constante. De assembler zal het sprongadres dan in het correcte formaat in de gegeven instructie plaatsen. Indien het sprongadres te ver gelegen is voor een bepaalde spronginstructie, zal de assembler dit aangeven met een 'destination out of range' boodschap.
Pauwels D.
Embedded systems V1.0
40
De JMP@ A+DPTR instructie supporteert de 'case' functie. Het sprongadres wordt berekend bij de uitvoering van de instructie als de som van de DPTR en de accu. De DPTR wordt op het startadres van de sprongtabel geplaatst en de accu krijgt de offsetwaarde in de tabel. In de tabel wordt er ook maar één 'CALL' instructie aangegeven, maar er zijn ook hier meerdere call instructies nl. LCALL en ACALL. Deze verschillen in de manier waarop het sprongadres wordt geëncodeerd. CALL is de generische mnemonic die kan gebruikt worden als het voor de programmeur om het even is welke binaire instructie er gegenereerd wordt. De LCALL instructie gebruikt een 16-bit adresformaat waardoor het sprongadres in het volledige 64K programmageheugen kan gelegen zijn. De ACALL instructie gebruikt maar een 11-bit adres, waardoor het sprongadres in hetzelfde 2K blok moet gelegen zijn als het startadres van de instructie die volgt op de ACALL. Subroutines moeten worden afgesloten met een RET instructie die terugspringt naar de instructie na de CALL. RETI wordt gebruikt voor het beëndigen van een interrupt service routine. Het verschil met een RET instructie is dat de RETI instructie aan het interrupt controlesysteem aangeeft dat de interrupt die afgehandeld wordt klaar is. Indien er geen interruptafhandeling bezig is, is een RETI functioneel hetzelfde als een RET instructie. Conditionele spronginstructies.
Tabel 4.3.2.8 8051 Conditionele spronginstructies.
Zoals men kan opmerken, kan bij deze instructies alleen relatieve offsetadressering worden toegepast, waar er in de instructie maar 1 byte wordt voorzien om het sprongadres op een relatieve manier aan teduiden t.o.v. het adres van de volgende instructie. Dit betekent een sprongbereik van -128 tot +127 bytes.
Pauwels D.
Embedded systems V1.0
41
4.4 De taal voor programma-ontwikkeling. Er wordt wel eens gezegd, dat men zichzelf niet als een echte embedded programmeur mag beschouwen indien men geen assembler kan schrijven. Dit is misschien een beetje overdreven, maar het is zeker zo dat bij de meeste embedded systemen er een deel van de code in assembler is geschreven. Dit komt doordat de hogere programmeertalen of HLL's (High Level Languages) meestal ervan uit gaan dat dingen zoals de stack reeds zijn geïnitialiseerd. Omdat function-calls in elke programmeertaal de stack gebruiken, die in RAM geheugen is ondergebracht, kan men bv. geen C functie oproepen die de stack initialiseert of die bv. de onchip chipselect logica instelt voor de RAM. Dit moet gewoon gebeuren in eerder uitgevoerde startupcode die dan in assembler is geschreven. Het is wel zo dat tegenwoordig de meeste C-compilers zelf voor een bepaalde controller in een bepaald systeem (waarvan de omgeving beschreven werd in de IDE van de compiler) een voorstel van assembler startupcode genereren. De startupcode moet meestal echter nog worden "gefinetuned" door de programmeur. Een tweede reden waarom er nog assembler geschreven wordt is snelheid. De beste code optimisers zijn nog steeds niet zo goed als een programmeur in het genereren van de snelste code, en dit zal zeker nog een tijdje zo blijven. Dit kan men duidelijk zien in de voorbeelden uit de volgende paragraaf. Hogere programmeertalen voor embedded toepassingen worden wel steeds gesofisticeerder en efficiënter en ze zijn beter in staat om de typische hardware van een embedded systeem beter te ondersteunen. De keuze van een programmeertaal wordt, zoals elke engineering tool, bepaald door een compromis tussen kostprijs, eenvoud in gebruik en gebruiksnut. Voor heel eenvoudige projecten kan een eenvoudige assembler reeds volstaan. Veel kleine projecten zijn helemaal niet sneller afgewerkt door een HLL te gebruiken. Sommige fabrikanten zoals bv. Atmel, stellen via hun website gratis assemblers ter beschikking voor hun controllers. Maar de kostprijs is niet noodzakelijk de bepalende factor in de keuze voor een assembler. Soms is assembler de enige manier om alles snel genoeg te laten verlopen in een systeem. Wanneer het grootste deel van de code bestaat uit het aanpassen van bits van I/O poorten in reactie op events, of het laden van timerwaardes dan gaat het grootste deel van de code bestaan uit dingen zoals het berekenen van timerwaardes. De eigenlijke code is dan redelijk eenvoudig en assembler is hier op zijn plaats. Het nadeel is dat voor het onderhouden van de code de programmeurs in de toekomst ook de instructieset van de controller moeten aanleren. Wanneer de complexiteit van een project en tijd voor het coderen zelf groter wordt, begint assembler minder aantrekkelijk te worden voor het project. De tijd die nodig is voor het debuggen van de code wordt groter en de mogelijkheid om code te schrijven voor meerdere controllers zonder een nieuwe taal te moeten leren wordt belangrijk. Alhoewel dit laatste argument meestal wegvalt bij simpele contollers. De HLL's hebben voor deze controllers meestal een aantal beperkingen, zoals het ontbreken van floating-point mogelijkheden, wat de programma-overdraagzaamheid beperkt. Voor de meeste controllers zijn er ook instructies nodig, om de specifieke hardware van de controller aan te spreken, die dan moeilijk overdraagzaam zijn. Een aantal dingen die moeten bekeken worden bij de keuze van een taal (compiler) worden nu kort aangegeven.
Pauwels D.
Embedded systems V1.0
42
Processor- of controllersupport. C is de meest universele programmeertaal die beschikbaar is voor de meeste processoren en controllers. PL/M en Pascal zijn bijna helemaal uitgestorven en BASIC is maar voorhanden voor een beperkt aantal controllers. Zoals reeds aangehaald moet men er rekening mee houden dat HLL versies voor controllers niet noodzakelijk alle mogelijkheden van een taal ondersteunen. Sommige dingen die normaal zijn voor een HLL op een PC of workstation zijn nu eenmaal niet mogelijk op een microcontroller van €2,5. Wat ook zeer belangrijk is bij de aanschaf van een compiler voor een controllerfamilie is dat hij de controllerversie ondersteunt die men daadwerkelijk gebruikt. Een compiler voor de 8051 familie moet bv. aangepaste code genereren voor rekenkundige routines wanneer we een C517 variant uit deze familie gebruiken, deze heeft immers een eigen 32-bit multiply/divide unit wat de uitvoeringstijd van deze programma's enkele grootteordes verkleint. Emulator support. De meeste emulatoren (controller hardware-simulators) kunnen inplaats van hex adressen de labels uit de source code van het programma weergeven (source level debug). Men kan inplaats van te "single-steppen" door de machinecode direct door een C instructie stappen, of zelfs door complete functies alsof het statements waren. Op dezelfde manier kunnen breakpoints worden gezet in de code. Dit kan het debuggen enorm vereenvoudigen. De emulator software heeft hiervoor echter een tabel nodig waarin aangegeven staat welk label met welk adres overeenkomt. Men moet nagaan of de compiler een fileformaat kan genereren dat compatibel is met de emulator. Ideaal is dat de emulator de data weergeeft in het correcte formaat en dat hij char, int en andere types van variabelen herkent. Grootte van de code (codegeheugen). Sommige compilers zijn zeer inefficiënt in het gebruik van ROM (Flash) en RAM geheugengrootte. Ga na of de compiler er niet voor zorgt dat er een enorme stijging van de hardware kosten zal volgen, voor het extra benodigde geheugen. Hetzelfde geldt ook voor de snelheid van de gegenereerde code. Ga na wat de grootte is van de gebruikte libraries en of ze wel in het beschikbare geheugen kunnen, een C printf functie in een controllertoepassing kan aanleiding geven tot een plotse stijging van 50Kbyte in code! Programma optimalisatie. Optimaliserende compilers proberen de efficiëntste code te genereren en kunnen het verschil maken tussen een toepassing die werkt of niet werkt. Een optimaliserende compiler elimineert de overbodige machine code. Bijvoorbeeld, als de code een variabele test om te zien of een bit is geset en daarna dezelfde variabele test om een andere bit te checken, dan zou een niet optimaliserende compiler de variabele twee keer kunnen lezen. Een optimaliserende compiler zal de variabele maar één keer lezen, in een register bewaren en wetende dat de variabele ondertussen niet is veranderd, dit register gebruiken voor de tweede bittest. Opgelet, dit kan ook voor problemen zorgen! Indien de variabele geen gewone RAM locatie is maar een hardwareregister, dat door de interne hardware los van het programma kan aangepast worden. Een aanpassing van een bit door de interne hardware kan zo ongedetecteerd blijven in de geoptimaliseerde versie van het programma en voor problemen zorgen. Dit is de reden waarom optimalisatie meestal kan uitgeschakeld worden voor bepaalde delen van de code. Meestal laten ze ook toe om memory-mapped hardware te definiëren zodat deze anders wordt bekeken dan gewone RAM. Sommige compilers, meestal bij controllers, laten toe te optimaliseren naar codegrootte of naar snelheid, maar meestal kan je dit alleen selecteren voor het hele programma.
Pauwels D.
Embedded systems V1.0
43
Assembler support. De meeste toepassingen vragen nog steeds assembler voor dingen zoals initialisatie, interrupts of snelle I/O. De compiler zou het moeten mogelijk maken om gemakkelijk assemblerfiles (mogelijks als inline code) te includen in de software. Een ander probleem bij controllers is dat de parameterpassing door de zeer beperkte stackruimte niet via de stack gebeurt maar via een gebied in (externe) RAM. Dit is meestal een overlay, die gedeeld wordt met andere functies. De HLL compiler moet op dit gebied een goed gedefiniëerde interface hebben, zeker naar assemblerroutines toe. De programmeur moet immers goed weten op welke manier variabelen worden doorgegeven. Debugging tools. Wanneer tools worden gekocht, moet er ook aandacht worden besteed aan het feit dat het systeem moet worden gedebugd. Bij het gebruik van controllers ondervindt men de meeste beperkingen hierrond. Sommige debuggersoftware voor controllers maakt gebruik van de on-chip seriële poort voor communicatie met de host PC. Als in de toepassing deze poort gebruikt wordt voor communicatie met andere controllers van het systeem, moet er een andere oplossing gevonden worden. Een emulator kan dan de oplossing zijn. Een emulator is echter wegens economische redenen niet altijd ter beschikking, of kan om andere redenen niet gebruikt worden. Denk bv. maar aan een embedded systeem dat om zijn werking te kunnen testen, bevestigd moet zijn op een (bewegende) robotarm. Een emulator is hier niet bruikbaar. In gevallen zoals deze is het ook nuttig om over een simulator te beschikken, die toelaat grote delen van de code te testen vanop de ontwerptafel. In een echte omgeving, met motoren en schakelende relais, waar de meeste embedded sytemen terecht komen, komen de meeste beperkingen van een simulator aan het licht, want omgevingsfactoren kunnen niet mee worden gesimuleerd. Indien er geen emulatoren of simulatoren kunnen gebruikt worden moet de ontwerper creatief zijn en naar andere oplossingen zoeken. Een nieuwe trend is echter de benodigde debughardware mee te intergreren in de controller of processor, de emulator wordt als het ware mee ingebouwd in de controller! Deze speciale hardware wordt wel eens een Background Debug Module (BDM) genoemd. Via een dedicated of standaard (JTAG) interface met een PC kan dan eenvoudig debug informatie worden bekomen uit een werkend systeem.
Pauwels D.
Embedded systems V1.0
44
4.4.1 8051 en een hogere programmeertaal. Voor de 8051 familie zijn er verschillende programmeertalen beschikbaar van verschillende fabrikanten. De meest gebruikte hogere programmeertaal is uiteraard C. Als voorbeeld wordt eenzelfde kleine routine voor de 8051 controller (een kleine en eenvoudige controller) geprogrammeerd in C, in BASCOM BASIC en in assembler. Het is aan de lezer om conclusies te trekken over de meest geschikte omgeving om te programmeren. Een voorbeeld van een zeer eenvoudig programma in C wordt in figuur 4.4.4.1 gegeven.
Fig. 4.4.1.1 Programmavoorbeeld in C.
Pauwels D.
Embedded systems V1.0
45
De assemblerlisting van de uitvoerbare code die de compiler hiervan maakte wordt hierna weergegeven, deze kan bekomen worden door de IntelHEX file die door de compiler werd gegenereerd te disassembleren naar een assemblerfile, in de meeste gevallen wordt er bij het compileren echter ook een assembler listfile aangemaakt. De vergelijking tussen de C-sourcefile en de gegenereerde code geeft de lezer een idee van de efficiëntie (?) van een doorsnee C-compiler voor 8051 controllers. Rigel\Reads51\Work\_blink\a01.xsf C:\Rigel\Reads51\Include\pcx51.inc 2 3 00000 3 4 00000 extern code __add12 4 5 00000 extern code __add21 5 6 00000 extern code __add1n 6 7 00000 extern code __sub12 7 8 00000 extern code __and12 8 9 00000 extern code __or12 9 10 00000 extern code __incwp 10 11 00000 extern code __addsp 11 12 00000 extern code __getb1p 12 13 00000 extern code __getb1p0 13 14 00000 extern code __getb1pu 14 15 00000 extern code __getb1pu0 15 16 00000 extern code __getb1s 16 17 00000 extern code __getb1s0 17 18 00000 extern code __getb1su 18 19 00000 extern code __getb1su0 19 20 00000 extern code __getb2s 20 21 00000 extern code __getb2s0 21 22 00000 extern code __getb2su 22 23 00000 extern code __getb2su0 23 24 00000 extern code __getw1m 24 25 00000 extern code __getw1p0 25 26 00000 extern code __getw1p 26 27 00000 extern code __getw1s0 27 28 00000 extern code __getw1s 28 29 00000 extern code __getw2m 29 30 00000 extern code __getw2p 30 31 00000 extern code __getw2s 31 32 00000 extern code __putwp1 32 33 00000 extern code __point1s 33 34 00000 extern code __point2s 34 35 00000 extern code __ne12 35 36 00000 extern code __ge12 36 37 00000 extern code __gt12 37 38 00000 extern code __uge12 38 39 00000 extern code __ugt12 39 40 00000 extern code __pushm ; not used 40 41 00000 extern code __pushp 41 42 00000 extern code __pushp0 42 43 00000 extern code __pushs 43 44 00000 extern code __push2 ; not used 44 45 00000 extern code __enterisr 45 46 00000 extern code __enter 46 47 00000 extern code __return 47 48 00000 extern code __returnisr 48 49 00000 extern code __return_n 49 50 00000 extern code __switch 50 51 00000 extern code __lneg1 51 52 00000 extern code __eq12 52 53 00000 extern code __lt12 53 54 00000 extern code __le12 54 55 00000 extern code __ult12 55 56 00000 extern code __ule12 56 57 00000 extern code __neg1 57 58 00000 extern code __rinc1 58 59 00000 extern code __rdec1 59 60 00000 extern code __asl12 60 61 00000 extern code __asr12 61 62 00000 extern code __udiv12 62 63 00000 extern code __umul12 63 64 00000 extern code __mul12 64 65 00000 extern code __div12 65 66 00000 extern code __mod12 66 67 00000 extern code __push1 67 68 00000 extern code __pop2 68 69 00000 extern code __pushab 69 70 00000 extern code __swap12 70 71 00000 extern code _ccargc 71 72 00000 72 73 00000 extern code __pushp 73 74 00000 extern code __pushs0 74 75 00000 extern code __dbl1 75 76 00000 extern code __putwm1 76 77 00000 extern code __prtocarry 77 78 00000 extern code __putw1s0 78 79 00000 extern code __putw1s 79 80 00000 extern code __putb1s0 80 81 00000 extern code __putb1s 81 82 00000 82 83 00000 extern code __call1 ; not used 83 84 00000 extern code __umod12 ; not implemented 84 85 00000 extern code __debug ; conditional use 85 87 00000 86 88 00000 C:\Rigel\Reads51\Include\Rc51Regs.inc
Pauwels D.
Embedded systems V1.0
46
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
3 00000 5 00000 6 00000 PRL data 0x00 7 00000 PRH data 0x01 8 00000 SRL data 0x02 9 00000 SRH data 0x03 10 00000 11 00000 MDL data 0x04 12 00000 MDH data 0x05 13 00000 CNT data 0x06 15 00000 MODFLAGREG data 0x07 16 00000 17 00000 SPL data 0x08 18 00000 SPH data 0x09 19 00000 BPL data 0x0A 20 00000 BPH data 0x0B 21 00000 22 00000 ARGCNT data 0x0C 23 00000 24 00000 ; --- 8051 registers --25 00000 ACC data 0xE0 26 00000 B data 0xF0 27 00000 DPH data 0x83 28 00000 DPL data 0x82 29 00000 IE data 0xA8 30 00000 IP data 0xB8 31 00000 P0 data 0x80 32 00000 P1 data 0x90 33 00000 P2 data 0xA0 34 00000 P3 data 0xB0 35 00000 PCON data 0x87 36 00000 PSW data 0xD0 37 00000 SBUF data 0x99 38 00000 SCON data 0x98 39 00000 SP data 0x81 40 00000 TCON data 0x88 41 00000 TH0 data 0x8C 42 00000 TH1 data 0x8D 43 00000 TL0 data 0x8A 44 00000 TL1 data 0x8B 45 00000 TMOD data 0x89 46 00000 47 00000 ; --- 8051 register bit fields --48 00000 ; IE 49 00000 EA bit 0xAF 50 00000 ES bit 0xAC 51 00000 ET1 bit 0xAB 52 00000 EX1 bit 0xAA 53 00000 ET0 bit 0xA9 54 00000 EX0 bit 0xA8 55 00000 ; IP 56 00000 PS bit 0xBC 57 00000 PT1 bit 0xBB 58 00000 PX1 bit 0xBA 59 00000 PT0 bit 0xB9 60 00000 PX0 bit 0xB8 61 00000 ; P3 62 00000 RD bit 0xB7 63 00000 WR bit 0xB6 64 00000 T1 bit 0xB5 65 00000 T0 bit 0xB4 66 00000 INT1 bit 0xB3 67 00000 INT0 bit 0xB2 68 00000 TXD bit 0xB1 69 00000 RXD bit 0xB0 70 00000 ; PSW 71 00000 CY bit 0xD7 72 00000 AC bit 0xD6 73 00000 F0 bit 0xD5 74 00000 RS1 bit 0xD4 75 00000 RS0 bit 0xD3 76 00000 OV bit 0xD2 77 00000 P bit 0xD0 78 00000 ; TCON 79 00000 TF1 bit 0x8F 80 00000 TR1 bit 0x8E 81 00000 TF0 bit 0x8D 82 00000 TR0 bit 0x8C 83 00000 IE1 bit 0x8B 84 00000 IT1 bit 0x8A 85 00000 IE0 bit 0x89 86 00000 IT0 bit 0x88 87 00000 ; SCON 88 00000 SM0 bit 0x9F 89 00000 SM1 bit 0x9E 90 00000 SM2 bit 0x9D 91 00000 REN bit 0x9C 92 00000 TB8 bit 0x9B 93 00000 RB8 bit 0x9A 94 00000 TI bit 0x99 95 00000 RI bit 0x98 96 00000 97 00000 ; macros to access the function argument stack in assembler 104 00000 107 00000 114 00000 115 00000 3 00000 C0Seg segment code 4 00000 CodeSeg segment code 5 00000 XDataSeg segment xdata 6 00000
Pauwels D.
Embedded systems V1.0
47
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
234 235 236 237 238
52 53 54 55 56
239 240 241 242 243 244 245
57 58 59 60 61 62 63
246 247 248 249 250
64 65 66 67 68
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
267 268 269 270 271
85 86 87 88 89
272 273 274
90 91 92
275 276 277 278 279
93 94 95 96 97
280 281
98 99
00000 ; --- external and public functions --00000 extern code _InitSerialPort0 00000 extern code _putc 00000 public _LedOff 00000 public _main 00000 public _LedOn 00000 00000 ; --- external and public variables --00000 00000 ; #line 1 // ------------- READS51 generated header -------------00000 ; #line 2 // module : a01.c 00000 ; #line 3 // created : 19:51:27, Monday, October 09, 2000 00000 ; #line 4 // Example routines for the textbook 00000 ; #line 5 // Programming and Interfacing the 8051 in C and Assembly 00000 ; #line 6 // by S. Yeralan and H. Emery 00000 ; #line 7 // (C) 2000, Rigel Press, www.rigelcorp.com 00000 ; #line 8 // ----------------------------------------------------00000 ; #line 9 00000 ; #line 10 00000 ; #line 11 // prototypes 00000 ; #line 12 /*"#C:\Rigel\Reads51\Include\Sio51.h"*/ 00000 ; #line 13 00000 ; #line 14 // prototypes 00000 ; #line 15 void PrtDec(int n); 00000 ; #line 16 void PrtHex(unsigned int u); 00000 ; #line 17 void putc(char c); 00000 ; #line 18 void puts(char *sz); 00000 ; #line 19 char getc(void); 00000 ; #line 20 void printf(char *sz); 00000 ; #line 21 void InitSerialPort0(int nMode); 00000 ; #line 22 00000 ; #line 23 00000 ; #line 24 // global definition of serial mode 00000 ; #line 25 00000 ; #line 26 // macros to access the function argument stack in assemb 00000 ; #line 27 00000 ; #line 28 00000 00000 ; #line 30 void LedOn(void){ 00000 ; ----------------------------------------------00000 rseg CodeSeg ; CHGSEG(2) 00000 _LedOn: ; DECLPUBm(LedOn) 00000 D0F0 pop b ; ENTER 00002 D0E0 pop acc 00004 120000 lcall __enter 0000220840 00007 ; #line 31 #asm 00007 C290 clr P1.0 00009 ; #line 34 } 00009 ; ----------------------------------------------00009 120000 lcall __return ; RETURN(0) 00004A0840 0000C ; #line 35 0000C ; #line 36 void LedOff(void){ 0000C ; ----------------------------------------------0000C _LedOff: ; DECLPUBm(LedOff) 0000C D0F0 pop b ; ENTER 0000E D0E0 pop acc 00010 120000 lcall __enter 0000220840 00013 ; #line 37 #asm 00013 D290 setb P1.0 00015 ; #line 40 } 00015 ; ----------------------------------------------00015 120000 lcall __return ; RETURN(0) 00004A0840 00018 ; #line 41 00018 ; #line 42 main(){ 00018 ; ----------------------------------------------00018 _main: ; DECLPUBm(main) 00018 750940 mov SPH, #0x40 ; ENTERMAIN(16384) 0001B 750800 mov SPL, #0x00 0001E 85090B mov BPH, SPH 00021 85080A mov BPL, SPL 00024 ; #line 43 int n; 00024 ; #line 44 00024 ; #line 45 // --- initialize serial port (9600 Baud) --00024 ; #line 46 InitSerialPort0(0); //DEF_SIO_MODE is defined in
00024 ; ----------------------------------------------00024 7402 mov a, #0x02 ; ADDSP(2) 00026 75F000 mov b, #0x00 00029 120000 lcall __addsp 0000470840 0002C 750000 mov PRL, #0x00 ; GETw1n(0) 0002F 750100 mov PRH, #0x00 00032 120000 lcall __push1 ; PUSH1 00002E0840 00035 750C01 mov ARGCNT, #0x01 ; ARGCNTn(1) 00038 120000 lcall _InitSerialPort0 ; CALLm(_InitSerialPort0) 0000370840 0003B 74FE mov a, #0xFE ; ADDSP(-2) 0003D 75F0FF mov b, #0xFF 00040 120000 lcall __addsp 0000470840 00043 ; #line 47 putc('\n'); 00043 ; ----------------------------------------------00043 75000A mov PRL, #0x0A ; GETw1n(10) 00046 750100 mov PRH, #0x00 00049 120000 lcall __push1 ; PUSH1 00002E0840 0004C 750C01 mov ARGCNT, #0x01 ; ARGCNTn(1) 0004F 120000 lcall _putc ; CALLm(_putc)
Pauwels D.
Embedded systems V1.0
48
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
0000480840 100 00052 74FE mov a, #0xFE ; ADDSP(-2) 101 00054 75F0FF mov b, #0xFF 102 00057 120000 lcall __addsp 0000470840 103 0005A ; #line 48 104 0005A ; #line 49 // endless loop 105 0005A ; #line 50 while(1) 106 0005A ; ----------------------------------------------107 0005A _1: ; LABm(_1:) 108 0005A _2: ; LABm(_2:) 109 0005A ; #line 51 { 110 0005A ; #line 52 LedOn(); 111 0005A ; ----------------------------------------------112 0005A 750C00 mov ARGCNT, #0x00 ; ARGCNTn(0) 113 0005D 120000 lcall _LedOn ; CALLm(_LedOn) 0000020040 114 00060 ; #line 53 putc('+'); 115 00060 ; ----------------------------------------------116 00060 75002B mov PRL, #0x2B ; GETw1n(43) 117 00063 750100 mov PRH, #0x00 118 00066 120000 lcall __push1 ; PUSH1 00002E0840 119 00069 750C01 mov ARGCNT, #0x01 ; ARGCNTn(1) 120 0006C 120000 lcall _putc ; CALLm(_putc) 0000480840 121 0006F 74FE mov a, #0xFE ; ADDSP(-2) 122 00071 75F0FF mov b, #0xFF 123 00074 120000 lcall __addsp 0000470840 124 00077 ; #line 54 for(n=0; n<100; n++); // waste some cycles 125 00077 ; ----------------------------------------------126 00077 750000 mov PRL, #0x00 ; GETw1n(0) 127 0007A 750100 mov PRH, #0x00 128 0007D 120000 lcall __putw1s0 ; PUTw1s(0) 00000D0840 129 00080 ; ----------------------------------------------130 00080 _4: ; LABm(_4:) 131 00080 120000 lcall __getw1s0 ; GETw1s(0) 0000400840 132 00083 750264 mov SRL, #0x64 ; GETw2n(100) 133 00086 750300 mov SRH, #0x00 134 00089 120000 lcall __ge12 ; JGE12n(_6) 00004E0840 135 0008C 5003 jnc $+5 136 0008E 02009D ljmp _6 009D020040 137 00091 ; ----------------------------------------------138 00091 _5: ; LABm(_5:) 139 00091 850A02 mov SRL, BPL ; POINT2s(0) 140 00094 850B03 mov SRH, BPH 141 00097 120000 lcall __incwp ; INCwp 0000290840 142 0009A ; #line 55 LedOff(); 143 0009A ; ----------------------------------------------144 0009A 020080 ljmp _4 ; JMPn(_4) 0080020040 145 0009D _6: ; LABm(_6:) 146 0009D ; ----------------------------------------------147 0009D 750C00 mov ARGCNT, #0x00 ; ARGCNTn(0) 148 000A0 12000C lcall _LedOff ; CALLm(_LedOff) 000C020040 149 000A3 ; #line 56 putc('0'); 150 000A3 ; ----------------------------------------------151 000A3 750030 mov PRL, #0x30 ; GETw1n(48) 152 000A6 750100 mov PRH, #0x00 153 000A9 120000 lcall __push1 ; PUSH1 00002E0840 154 000AC 750C01 mov ARGCNT, #0x01 ; ARGCNTn(1) 155 000AF 120000 lcall _putc ; CALLm(_putc) 0000480840 156 000B2 74FE mov a, #0xFE ; ADDSP(-2) 157 000B4 75F0FF mov b, #0xFF 158 000B7 120000 lcall __addsp 0000470840 159 000BA ; #line 57 for(n=0; n<100; n++); // waste some cycles 160 000BA ; ----------------------------------------------161 000BA 750000 mov PRL, #0x00 ; GETw1n(0) 162 000BD 750100 mov PRH, #0x00 163 000C0 120000 lcall __putw1s0 ; PUTw1s(0) 00000D0840 164 000C3 ; ----------------------------------------------165 000C3 _7: ; LABm(_7:) 166 000C3 120000 lcall __getw1s0 ; GETw1s(0) 0000400840 167 000C6 750264 mov SRL, #0x64 ; GETw2n(100) 168 000C9 750300 mov SRH, #0x00 169 000CC 120000 lcall __ge12 ; JGE12n(_9) 00004E0840 170 000CF 5003 jnc $+5 171 000D1 0200E0 ljmp _9 00E0020040 172 000D4 ; ----------------------------------------------173 000D4 _8: ; LABm(_8:) 174 000D4 850A02 mov SRL, BPL ; POINT2s(0) 175 000D7 850B03 mov SRH, BPH 176 000DA 120000 lcall __incwp ; INCwp 0000290840 177 000DD ; #line 58 } 178 000DD ; ----------------------------------------------179 000DD 0200C3 ljmp _7 ; JMPn(_7)
Pauwels D.
Embedded systems V1.0
49
362 363 364 365
180 181 182 183
366 367 368 369
184 185 186 187
370 371
188 189
372 373
190 191
00C3020040 000E0 _9: ; LABm(_9:) 000E0 ; #line 59 } 000E0 ; ----------------------------------------------000E0 02005A ljmp _1 ; JMPn(_1) 005A020040 000E3 _3: ; LABm(_3:) 000E3 74FE mov a, #0xFE ; ADDSP(-2) 000E5 75F0FF mov b, #0xFF 000E8 120000 lcall __addsp 0000470840 000EB ; ----------------------------------------------000EB 020018 ljmp _main ; RETMAIN 0018020040 000EE ; ----------------------------------------------000EE end ; ENDSEG(2)
Dit is de code die werd gegenereerd door de C compiler, 238 (EEh) bytes lang, niet meteen de meest geschikte oplossing indien men beschikt over een kleine hoeveelheid geheugen in het systeem. Als volgende omgeving werd BASCOM BASIC genomen.
Fig. 4.4.1.2 BASCOM BASIC programmavoorbeeld.
Pauwels D.
Embedded systems V1.0
50
Fig. 4.4.1.3 BASCOM compiler build rapport.
De gegenereerde code werd gedisassembleerd en geeft voorzien van commentaar dit resultaat. Uit het compiler report kunnen we afleiden dat er 199 bytes (C7h) code werden aangemaakt om het probleem op te lossen. ;===============================================================; ; Disassembled Using DIS8051 - (C)1989 Data Sync Engineering ; ;===============================================================; ORG 00000H ;STARTADRES IN CODEGEHEUGEN LJMP L004E ;SPRONG NAAR STARTUP CODE ORG 002CH ;STARTADRES VAN DE SUBROUTINES NA DE INTERRUPTVECTOREN ; L0036: CLR C ;GEEN BORROW GEBRUIKEN SUBB A,B ;VARIABELE MIN MAX TELLERWAARDE RET ; ;L003A: LCALL L0036 ;VARIABELE MIN MAX TELLERWAARDE JZ L0043 ;NUL ALS VARIABELE= MAX TELLERWAARDE JC L0043 ;ANDERS KLEINER EN AANGEVEN IN A CLR A ;IN A AANGEVEN DAT VARIABELE > MAX TELLERWAARDE RET L0043: MOV A,#001H ;VARIABELE IS KLEINER OF GELIJK AAN MAX TELLERWAARDE RET ; L0046: JNB TI,L0046 ;CHECK TRANSMIT BIT IN UART CLR TI ;ALS TI=1 DAT IS VORIG CHAR WEG, FLAG TERUG CLEAREN MOV SBUF,A ;NIEUWE CHAR IN TRANSMITBUFFER RET ; ; STARUP CODE INIT VAN DE SERIELE POORT EN ERROR VARIABELE L004E: MOV R0,#0FFH ;AANTAL BYTES INTERNE DATARAM CLR A ; L0051: MOV @R0,A ;INTERNE DATARAM OP NUL ZETTEN DJNZ R0,L0051 ;BLIJF LUSSEN TOT R0=0 MOV SP,#022H ;STACKPOINTER INITIALISEREN NA DE VARIABELEN CLR TR1 ;TIMER1 STOPPEN MOV SCON,#052H ;INIT UART MOV TMOD,#022H ;INIT BAUDRATE TIMER (TIMER1) MOV TH1,#0FDH SETB TR1 ;TIMER1 WEER STARTEN MOV 020H,#000H ;ERROR VARIABELE VOOR INTERN GEBRUIK CLEAREN
Pauwels D.
Embedded systems V1.0
51
L006A:
L006E:
; L0078: ;
L007D: L0080:
; L008C:
; L0095:
L0099:
; L00A3: ;
L00A8: L00AB:
; L00B7:
; L00C0: ; L00C5:
ORL SETB
08EH,#001H P1.0
;ALE LIJN DISABLEN ;LED AANSCHAKELEN
MOV MOV MOVC JZ LCALL INC SJMP
R0,#00BH A,R0 A,@A+PC L0078 L0046 R0 L006E
;CHAR OFFSET LADEN ;OM CHAR GEINDEXEERD OP TE HALEN ;TEN OPZICHTE VAN PC ;INDIEN NULL DAN GEDAAN ;ANDERS CHAR NAAR BUITEN STUREN ;VOLGENDE CHAR ;BLIJF LUSSEN
LJMP
L007D
;GEDAAN MET CHAR AFDRUKKEN
DB 02BH, 000H
;'+', NULL
MOV MOV MOV ACALL JNZ LJMP
021H,#000H B,#064H A,021H L003A L008C L0095
;VARIABELE OP NUL ZETTEN ;TELLERWAARDE =100 ;VARIABELE OPHALEN ;VARIABELE MIN TELLERWAARDE ;NIET NUL, DAN VARIABELE KLEINER OF GELIJK AAN TELLERWAARDE ;VARIABELE IS TELLERWAARDE
INC MOV JZ LJMP
021H A,021H L0095 L0080
;VARIABELE +1 ;CHECK VARIABELE ;OP OVERFLOW, INDIEN NUL DAN OVERFLOW ;VOLGENDE TEST
CLR
P1.0
;LED UITSCHAKELEN
MOV MOV MOVC JZ LCALL INC SJMP
R0,#00BH A,R0 A,@A+PC L00A3 L0046 R0 L0099
;CHAR OFFSET LADEN ;OM CHAR GEINDEXEERD OP TE HALEN ;TEN OPZICHTE VAN PC ;INDIEN NULL DAN GEDAAN ;ANDERS CHAR NAAR BUITEN STUREN ;VOLGENDE CHAR ;BLIJF LUSSEN
LJMP
L00A8
DB 030H, 000H
;'0', NULL
MOV MOV MOV ACALL JNZ LJMP
021H,#000H B,#064H A,021H L003A L00B7 L00C0
;VARIABELE OP NUL ZETTEN ;TELLERWAARDE =100 ;VARIABELE OPHALEN ;VARIABELE MIN TELLERWAARDE ;NIET NUL, DAN VARIABELE KLEINER OF GELIJK AAN TELLERWAARDE ;VARIABELE IS TELLERWAARDE
INC MOV JZ LJMP
021H A,021H L00C0 L00AB
;VARIABELE +1 ;CHECK VARIABELE ;OP OVERFLOW, INDIEN NUL DAN OVERFLOW ;VOLGENDE TEST
LJMP
L006A
;MAIN LOOP
CLR SJMP
EA L00C5
;EXIT CODE (ONNODIG HIER) ;DUMMY LOOP, PROGRAMMA IS AFGEWERKT EN GEEN O.S.
END
Pauwels D.
Embedded systems V1.0
52
In figuur 4.3.1.4 ziet men de assembler omgeving, waar op machine-instructieniveau wordt geprogrammeerd en waar de programmeur zelf alle nodige initialisaties van de hardware moet programmeren. De programmeur wordt hier verplicht om de instructieset van de controller onder de knie te krijgen, wat niet altijd eenvoudig is. Als besluit zouden we hierbij het volgende kunnen stellen: om een embedded systeem te programmeren wordt er van de programmeur altijd verwacht dat hij de specifieke controllerarchitectuur en zijn interne hardware door en door kent. Dit is immers wanneer er moet gedebugd worden (altijd dus) een eerste vereiste om tot een werkend systeem te komen, ongeacht in welke programmeertaal de controller wordt geprogrammeerd.
Fig. 4.3.1.4 Programmavoorbeeld in 8051 assembler.
Pauwels D.
Embedded systems V1.0
53
TEST
0090 007F 0020
0000 0000 02002C 002C 002C 78FF 002E E4 002F F6 0030 D8FD 0032 75817F 0035 C28E 0037 759852 003A 758922 003D 758DFD 0040 D28E
0042 D290 0044 742B 0046 1159 0048 1154 004A C290 004C 7430 004E 1159 0050 1154 0052 80EE
0054 7864 0056 D8FE 0058 22 0059 3099FD 005C C299 005E F599 0060 22
1 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
286 287 288 289 290 291 292 293 294 295 296 297
$nolist $list led stack var
bit p1.0 equ 07fh equ 020h
org ljmp
init:
clrmem:
main:
285 286 287 delay: delay1:
putc:
0000h init
org 002ch mov r0,#0ffh clr a
;aantal interne bytes dataram
mov djnz
;clear memory byte ;alle locaties
@r0,a r0,clrmem
mov sp,#stack clr tr1 mov s0con,#052h mov tmod,#022h mov th1,#0fdh setb tr1
;init stackpointer ;timer 1 stoppen ;init uart ;init baudrate timer (timer1)
setb led mov a,#'+' acall putc acall delay
;led aan ;'+' ;afdrukken ;delayloop
clr led mov a,#'0' acall putc acall delay sjmp main
;led uit ;'0' ;afdrukken ;delayloop
;timer 1 starten
;FOR-NEXT LUS mov djnz ret
r0,#100 r0,delay1
jnb ti0,putc clr ti0 mov s0buf,a ret
;1 microsec ;2 microsec ;2 microsec ;test transmitbuffer empty bit ;buffer vrij, dan flag clearen ;char in transmitbuffer en naar buiten
end
VERSION 1.2h ASSEMBLY COMPLETE, 0 ERRORS FOUND
Van de drie uitgewerkte versies is dit niet alleen het compactste programma met 96 bytes code (0060h), maar ook het snelste. Vergelijk de lege FOR-NEXT lus uit het BASCOM programma of de FOR lus uit het C programma, die honderd maal doorlopen wordt, maar met de delay routine uit het assemblerprogramma (203 microseconden).
Pauwels D.
Embedded systems V1.0
54
Hoofdstuk
Interne Periferie van een 8051 controller De 8051 familie bevat net zoals elke reeks van microcontrollers een hele variëteit aan periferiebouwstenen die intern (on-chip) aanwezig is. Zo zijn er op de oorspronkelijke 8051 vier 8-bit parallelle poorten, twee counter/timers en een seriële poort aanwezig. Afhankelijk van de variant uit deze familie zullen er nog extra periferiebouwstenen aanwezig zijn. Elk van deze periferiebouwstenen is gerelateerd met één of meerdere SFR's. Met andere woorden, de periferie wordt geïnitialiseerd door de bitsetting in de controle SFR's en dan kan de werking van de periferie bekeken worden via de statusbits in de overeenkomstige SFR's. In dit hoofdstuk wordt dieper ingegaan op de on-chip periferie die typisch is voor controllers en dan meer bepaald, maar niet exclusief, voor de 8051 reeks.
5.1 Parallelle poorten. Elke microcontroller heeft parallelle poorten ,die meestal 8-bit breed zijn, of minstens enkele bits die samen of individueel kunnen aangesproken worden. Microcontrollers gebruiken parallelle poorten om data (bits of bytes) binnen te lezen van een aantal pinnen waar de externe hardware informatie opzet, of om informatie naar buiten te sturen op een aantal pinnen waar externe hardware mee verbonden is. In het geval van de 8051 reeks kunnen de poorten als 8-bit poorten gebruikt worden, of als individuele bits. De poorten van de 8051 reeks werken principiëel zoals de meeste parallelle poorten van andere controllers of processoren. Er is echter één groot verschil. De poorten van de 8051 reeks hebben geen initialisatie nodig. Daarmee bedoelen we dat er geen configuratie van de poorten nodig is om te werken als input of als output, dit in tegenstelling met andere reeksen van controllers. De enige vereiste is dat voor een poortbit die men als input wenst te gebruiken er een 1 is geschreven in de poortlatch. Één en ander wordt later duidelijk.
Pauwels D.
Embedded systems V1.0
55
5.1.1 Parallelle poort-structuur en werking. De vier basispoorten van de 8051 reeks zijn bidirectioneel. Ze zijn opgebouwd uit een latch (SFR's P0 tot P3), een output driver en een input buffer. De output drivers van poorten 0 en 2, en de inputbuffers van poort 0, kunnen worden gebruikt om het eventuele externe geheugen aan te spreken. In deze toepassing zal poort 0 de low byte van het externe geheugenadres naar buiten sturen, en dit in de tijd gemultiplexed met de databyte die wordt gelezen of geschreven. Poort 2 stuurt de high byte van het externe geheugenadres naar buiten indien het over een 16-bit adres gaat. In de andere gevallen wordt op de pinnen van poort 2 de inhoud van het SFR P2 naar buiten gestuurd. Alle pinnen van poort 3, en indien er een TIMER2 aanwezig is in het systeem ook twee pinnen van poort 1, zijn multifunctioneel. Ze hebben m.a.w. niet alleen de functie van poortbit, maar kunnen ook gebruikt worden als input- of outputpin voor een andere interne periferiebouwsteen die aanwezig is in de controller. De verschillende mogelijke functies worden hieronder weergegeven.
Fig. 5.1.1.1 Alternatieve functie van de 8051 poortpinnen.
Deze alternatieve functies kunnen maar worden geactiveerd als de Port bitlatch in het poort SFR met een 1 is geschreven, m.a.w. als de poort in inputmode staat. Dit wordt schematisch aangegeven in de figuur 5.1.2.1. Na reset staan de alternatieve functies op een '1' niveau en kan de poortpin als parallelle I/O pin gebruikt worden. Indien een 8051 variant wordt gebruikt met meerdere parallelle poorten en meer interne periferieblokken (vb. een C517 van Infineon) moet hier dan nagegaan worden welke poortpinnen overeenkomen met welke alternatieve functie.
5.1.2
I/O configuraties.
In de figuur 5.1.2.1 wordt de typische bitlatch of portlatch en I/O buffer functioneel weergegeven voor de vier basispoorten van de 8051 reeks. De bitlatch (één bit van een poort SFR) wordt voorgesteld door een D-type flip-flop, waar een waarde van de interne bus wordt binnengeclocked bij een "write to latch" signaal van de CPU. De Q uitgang van de flip-flop wordt op de interne bus gezet als gevolg van een "read latch" signaal van de CPU.
Pauwels D.
Embedded systems V1.0
56
Het niveau van de poortpin zelf, wordt op de interne bus gezet bij een "read pin" signaal van de CPU. Sommige instructies die een poort lezen activeren het "read latch" en andere activeren het "read pin" controlesignaal. Dit kan in sommige gevallen een groot verschil zijn. Read Latch
Alternatieve output functie
D
Int.Bus
Q &
Port bitlatch
Write to Latch
CLK
Read pin
Port pindriver
Q
Alternatieve input functie
Fig. 5.1.2.1 Functionele voorstelling van de poortstructuur bij de 8051 familie.
Het port pindriver gedeelte uit de vorige figuur bestaat uit een aantal pFETs en een nFET, die er voor zorgen dat de poortpin een open drain structuur heeft met een interne pull-up die wordt gevormd door de interne pFET's. Dit wordt in de figuur 5.1.2.2 weergegeven.
Actief bij een ‘0’ naar ‘1’ flank
Weak pull-up
Actief als V-portpin > 1..1,4V
1 OSC. PERIOD OR
Delay
Laag niveau voor een '1' niveau op de poortpin
Fig. 5.1.2.2 Poortpindriver bij de 8051 familie. Pauwels D.
Embedded systems V1.0
57
De nFET zal niet geleiden bij een logisch '0' niveau op zijn gate, terwijl de pFET's dan juist wel zullen geleiden. De portdriver zal er voor zorgen dat P1 gedurende twee oscillatorperiodes wordt aangestuurd en gaat geleiden voor een uitgangstransitie van '0' naar '1'. Dit heeft als doel de parasitaire capaciteit, van het circuit dat is aangesloten op de poortpin, snel op te laden en dus de 'weak pull-up' P2 gedurende 2 clockperiodes te 'versterken' terwijl P2 altijd in geleiding is wanneer de nFET spert. De FET P3 zal slechts in geleiding gaan wanneer de spanning op de poortpin groter is dan 1 á 1,4V . Dit heeft ook gevolgen wanneer de poortpin als ingang staat geschakeld en wordt aangestuurd vanuit een open-collector of open-drain uitgang van een ander device. Zie onderstaande figuur 5.1.2.3. Wanneer de pin op een '0' niveau wordt gestuurd door het externe device zal de FET P3 niet geleiden en alleen de 'weak pull-up' P2 zal stroom voeren naar de poortpin. Een '0' naar '1' transitie op deze pin, die wordt gestuurd door het externe device, zal dus niet snel verlopen tot P3 ook mee in geleiding komt en de oplaadtijd zo ongeveer tien keer kleiner maakt. Dit wordt duidelijk in de grafiek in figuur 5.1.2.4.
Fig. 5.1.2.3 Effect van de parasitaire capaciteit van een pin als input geschakeld.
In het gebied A zal alleen P2 stroom leveren om de capaciteit op te laden tot ongeveer 1,4V. Vanaf dan zal (in gebied B) de FET P3 mee in geleiding komen en de capaciteit veel vlugger opladen tot een hoog niveau. V poortpin 5
A
Met P3
B
Zonder P3 4 3
P3 komt in geleiding
2 1 T (uS) 2
4
6
8
10
12
14
16
18
20
Fig. 5.1.2.4 Spanningsverloop op een poortpin bij een '0' naar '1' transitie via een OC driver. Pauwels D.
Embedded systems V1.0
58
De figuur 5.1.2.4 geeft aan dat er dus een zekere tijd (enkele machinecycli) nodig is om een logisch hoog niveau te bereiken bij een '0' naar '1' overgang, wat zeker niet uit het oog mag verloren worden wanneer externe devices worden aangesproken. Zeker in het geval van een aanzienlijke capacitieve belasting zoals bij het gebruik van flat-cable om een poort te verbinden met een extern device zoals bv. een LCD display. Meestal is het beter een actief lage sturing van externe devices te gebruiken, indien mogelijk, omdat de interne nFET dan stroom kan 'sinc'-en naar de massa. Deze toegelaten stroom is veel groter dan de stroom die bij een actief hoge sturing kan 'gesourced' worden door de pFET's (interne pull-up). Doordat de controller bij power-up of reset zijn poortpinnen allemaal op logisch '1' zet (als input) krijgen we het bijkomend voordeel dat bij een actief lage sturing alle uitgangen op 'niet actief' staan bij startup. In het geval er toch een actief hoge sturing moet gebruikt worden moet men er op letten dat de uitgangsspanning minstens 1,4V kan worden, op deze manier garanderen we dat ook FET P3 mee stroom kan leveren naar de poortpin. In het geval van het aansturen van een NPN transistor kunnen we in serie met de basis een diode opnemen om zo de uitgangsspanning tot op1,4V te kunnen brengen. Read Latch
Alternatieve output functie
Vcc
Vcc
Vcc
Interne Pull-up
Int.Bus Write to Latch
Read pin
D
Q &
Port bitlatch CLK
Actief hoge sturing
Q
Actief lage sturing
Alternatieve input functie
Fig. 5.1.2.5 Mogelijke configuraties voor het aansturen van externe devices via transistoren.
5.1.3 De read-Modify-write instructies en hun eigenschappen. Sommige instructies die data op een poort lezen, lezen de poortlatch waar andere instructies de poortpin lezen. Welke instructies lezen nu welke data? De instructies die de poortlatch lezen in plaats van de poortpinnen zijn diegenen die de poortdata lezen, ze eventueel aanpassen en dan terugschrijven naar de poort. Dit noemen we de 'read-modify-write' (RMW) instructies. De instructies die hieronder aangegeven worden in de tabel 5.1.3.1 zijn zulke RMW instructies. Wanneer de destination operand in de instructie een poort is, of een poortbit, zullen deze instructies de poortlatch lezen en niet de poortbit. Het is niet zo voor de hand liggend dat de laatste drie instructies ook RMW instructies zijn, maar toch is het zo. Ze lezen de poortbyte, alle 8 bits, passen de geadresseerde bit aan en schrijven de nieuwe byte terug naar de poortlatch. De reden waarom de RMW instructies de latch lezen en niet de poortpinnen is om een verkeerde interpretatie te vermijden bij het lezen van het spanningsniveau van een poortpin. Wanneer bv. zoals in de figuur 5.1.2.5 een NPN transistor wordt aangestuurd, zal voor een Pauwels D.
Embedded systems V1.0
59
logisch niveau '1' dat naar de pin is gestuurd er bij het lezen van de pin maar 0,7V (of in het geval van de bijkomende diode 1,4V) gezien worden door de ingangsbuffer, wat overeenkomt met een logisch '0' niveau, en wat het verkeerde logische niveau is. Het lezen van de latch levert in dit geval wel de logiche waarde op. RMWInstructie ANL ORL XRL JBC CPL INC DEC DJNZ MOV PX.Y,C CLR PX.Y SETB PX.Y
Omschrijving Logische AND bv. ANL P1,A Logische OR bv. ORL P2,A Logische EX-OR bv. XRL P3,A Jump als bit 1 is en clear bit bv. JBC P1.1, label Complement bit bv. CPL P3.0 Increment bv. INC P2 Decrement bv. DEC P3 Decrement and jump if not zero bv. DJNZ P2,label Move carrybit in bit Y van poort X Clear bit Y van poort X Set bit Y van poort X
Tabel 5.1.3.1 Read Modify write instructies bij 8051.
5.1.4 Voordelen van deze 'open-drain' poortdrivers. Door de open drain poortstructuur is het mogelijk verschillende poortpinnen die als uitgang zullen werken met elkaar door te verbinden. Dit kan zonder dat er beschadiging zal optreden van één van de poortdrivers wanneer de poortpinnen worden gestuurd op een verschillend logisch niveau. Bij controllers met een actieve push-pull uitgang is dit niet mogelijk! Deze eigenschap kan nuttig zijn bij het lezen van externe schakelaars, of keyboards. Ook kunnen op deze manier verschillende controllers via een parallelle poort gewoon worden verbonden met elkaar. We zullen deze twee voorbeelden even verder bespreken.
5.1.4.1
Keyboards en schakelaars…
Een groot aantal embedded producten (bijna allemaal) hebben een keyboard interface als input voor de gebruiker of moeten een aantal schakelaars lezen. Een keyboard kan zowel gebruikt worden voor het ingeven van numerische data als voor het selecteren van een werkingsmode van het toestel. Er bestaan voor het lezen van keyboards kant en klare oplossingen in chipvorm, maar een software aanpak voor keyboardscanning of het lezen van schakelaars heeft het voordeel dat de kostprijs van het product kleiner wordt en dit in ruil voor een kleine CPU overhead. De meeste keyboards bestaan uit een matrix van aansluitdraden waar op elk kruispunt een drukschakelaar is mee verbonden. De controller kan dan zeer eenvoudig bepalen welke schakelaar er is ingedrukt. De keyboardhardware basics. Bij een keyboard wordt meestal een drukschakelaar gebruikt, waarvan het sluiten eenvoudig kan worden gedetecteerd door volgend circuit. De pull-up weerstand zorgt ervoor dat er een logisch '1' signaal aan de uitgang staat wanneer de schakelaar open staat, en een logische '0' wanneer de schakelaar is gesloten. Pauwels D.
Embedded systems V1.0
60
De pull-up weerstand zit bij de 8051 familie principiëel reeds intern in de pindriver, maar meestal plaatst men extern toch nog een laagohmige pull-up weerstand (enkele KΩ) uit betrouwbaarheidsoverwegingen. +5V
Contactdender
Schakelaar open
Schakelaar open
+5V
Naar de controlleringang
Schakelaar dicht
GND
Schakelaar ingedrukt
t
Fig. 5.1.4.1.1 Interfacing van een schakelaar, met bounce, via een pull-up d
Helaas zijn schakelaars niet ideaal en genereren ze geen gedefinieerde '1' of '0' wanneer ze worden ingedrukt of losgelaten. Alhoewel een contact snel en stevig lijkt te sluiten, is dit voor de snelle controller een zeer trage gebeurtenis. Wanneer het contact wordt gesloten 'botst' het nog enkele keren als een bal. Dit botsen noemt men contactdender of bounce. Het effect hiervan wordt aangegeven in de vorige figuur. De contactdender zorgt voor meerdere pulsen op de controller-ingang en deze dender duurt typisch tussen de 5 en 30mS. Zie fig. 5.1.4.1.1. Wanneer meerdere schakelaars nodig zijn in een systeem kan men elke schakelaar op deze manier verbinden met een poortpin van de controller. Voor een groot aantal schakelaars zijn er al snel niet genoeg poortpinnen voorhanden. De meest efficiënte manier om schakelaars te verbinden met een controller is in de vorm van een twee-dimensionale matrix zoals aangegeven in de figuur 5.1.4.1.2. Dit levert winst op van poortpinnen vanaf 6 schakelaars. Scanlijnen (rijen)
Keyboard matrix
"0" 1 1 POORT X
1 1
AT89S8252 1 1 1
Leeslijnen (kolommen)
Fig. 5.1.4.1.2 Schakelaars verbonden in matrixvorm. Pauwels D.
Embedded systems V1.0
61
Het aantal schakelaars nodig in de matrix is natuurlijk afhankelijk van de toepassing. Elke rij wordt aangestuurd door een poortbit (output), terwijl elke kolom wordt omhoog getrokken door de (interne) pull-up van de poortpin (input) waarmee hij is verbonden. Uiteraard kan ook met elke kolom extern nog een extra pull-up weerstand worden verbonden. Keyboard scanning is het proces waarbij de controller op regelmatige tijdstippen naar de keyboard matrix kijkt om te zien of er een toets is gedrukt. Als er een toets werd ingedrukt is het aan de keyboard scanningsoftware om de dender weg te filteren en te bepalen welke toets er werd ingedrukt. Uiteraard hoeft het niet altijd te gaan om een keyboard dat op deze manier wordt verbonden, wanneer in een systeem een groot aantal contacten moet worden gelezen zoals bv. einderitschakelaars, contacten van relais,naderingsschakelaars enz. kan dit ook op deze manier. Een matrix (keyboard) scanning algoritme. Een matrix kan op verschillende manieren worden gelezen, een eenvoudig algoritme wordt hier aangegeven. In de vertrektoestand worden alle rijen (outputpinnen) op een logische '0' gezet. Wanneer er geen toets is gedrukt zullen alle kolommen (inputpinnen) als logisch '1' gelezen worden. Het indrukken van een toets (sluiten van een contact) heeft als gevolg dat één van de kolommen laag gemaakt wordt. Om te zien of een toets is ingedrukt moet de controller dus alleen kijken of er een inputpin (kolom) laag staat. Wanneer de controller gedetecteerd heeft dat er een toets is ingedrukt, moet hij nog bepalen welke toets het is. Dit mechanisme is heel eenvoudig. De controller stuurt een '0' op slechts één van de rijpinnen (outputpinnen). Als er een '0' wordt teruggevonden op de ingangspinnen (kolommen) dan weet de controller dat de ingedrukte toets zich bevindt op de met een '0' aangestuurde rij. Dit kan een probleem geven bij controllers die niet beschikken over een open-drain pindriver. Indien er immers twee toetsen worden ingedrukt op dezelfde kolom zal een rijpin die '0' is kunnen verbonden worden met een rijpin die op '1' gestuurd is. Dit geeft bij de 8051 familie geen probleem, maar bij een aantal controllers met een actieve (push-pull) uitgangstrap zal dit het defect van een pin veroorzaken. Als alle ingangen hoog blijven bevindt de ingedrukte toets zich niet op de geselecteerde rij en de controller maakt dan de volgende rij logisch '0' en het proces herhaalt zich. Wanneer de rij is bepaald kan de controller nagaan welke van de kolommen (inputpinnen) er '0' is, dit geeft de kolom aan van de ingedrukte toets. De tijd die de controller nodig heeft om deze handelingen uit te voeren is zeer klein (enkele µs) vergeleken met de minimale tijd dat een schakelaar wordt ingedrukt (enkele honderden ms), er wordt dan ook verondersteld dat de schakelaar gesloten blijft tijdens dit proces. Om de contactdender weg te filteren zal de controller het keyboard scannen met een bepaald tijdsinterval, typisch tussen de 20 ms en de 100ms, wat de debounce periode wordt genoemd. Een toets is pas echt ingedrukt wanneer hij tweemaal na elkaar ingedrukt wordt terug gevonden, anders kan het gaan om een stoorsignaal of om de contactdender. Soms is het nodig te reageren op een toets die wordt ingedrukt of die wordt losgelaten, en niet op een ingedrukt gehouden schakelaar. Een dalende flank van een ingedrukte toets heeft men na opeenvolgend een niet ingedrukte en twee ingedrukte toestanden, en een stijgende flank van een losgelaten toets heeft men na opeenvolgend een ingedrukte en twee niet ingedrukte toestanden. De rij- en kolominformatie van de toets kan dan worden omgezet naar een scancode die dan in een buffer kan worden geplaatst. Pauwels D.
Embedded systems V1.0
62
Buffering is handig omdat het voorkomt dat er toetsinfo verloren gaat wanneer de toepassing niet direct de toetsinfo kan verwerken. De buffergrootte is weer afhankelijk van de toepassing. Een 'keyflag' kan aangeven aan de toepassing dat er nog toetsinfo in de buffer aanwezig is Het scannen met een bepaald tijdsinterval kan worden opgestart door een periodische timerinterrupt die meestal in elk systeem wel aanwezig is als systeem-'time-tick'.
5.1.4.2
Parallelle communicatie tussen twee controllers.
Wanneer twee controllers op éénzelfde systeem (PCB) met elkaar moeten communiceren kan dit op verschillende manieren. De eenvoudigste manier, wat verbindingen betreft, is de controllers seriëel te laten communiceren. Indien er poortpinnen genoeg voorhanden zijn en indien de communicatie snel moet verlopen kan er ook een parallelle verbinding nodig zijn. Dit kan bij de 8051 familie zonder bijkomende hardware, zoals te zien is in de volgende figuur 5.1.4.2.1. Bidirectionele datalijnen
Poort x
Poort x
AT89S8252
AT89S8352
P 3.2/ int0
P 3.2/ int0 Bidirectionele handshakelijn
A
B
Fig. 5.1.4.2.1 Parallelle verbinding tussen twee 8051 compatibelen.
Door de open-drain structuur zullen de pindrivers niet worden beschadigd indien op dezelfde verbinding door de beide controllers een verschillend logisch signaal wordt gestuurd, de logische '0' is dominant. Naast de poorten waarlangs onderling bidirectioneel data wordt doorgegeven worden er ook nog twee pinnen doorverbonden die als alternatieve functie een interruptingang hebben. Deze vormen de handshakelijn waarmee een controller aangeeft aan de andere controller wanneer zijn data beschikbaar is. Als vertreksituatie zetten beide controllers hun datapoort als ingang en hun handshakelijn als interrupt ingang. Stel dat controller A data wil sturen naar controller B, en daarop een antwoord verwacht van controller B. Controller A zal dan zijn data op zijn datapoort zetten (output) zodat controller B deze kan lezen. Om aan te geven dat de data geldig is zal controller A zijn handshakepin omschakelen als output en op deze lijn een actief lage puls geven. De handshake-ingang (interruptingang) van controller B zorgt hiermee voor een interrupt bij controller B, die de data op zijn poort nu kan lezen en verwerken. Controller A kan nu zijn handshakepin omschakelen als interruptingang.
Pauwels D.
Embedded systems V1.0
63
Controller B schakelt na het lezen en verwerken van de data zijn handshake pin om als uitgang en zet zijn antwoord data op de datapoort. Dit terwijl ook nog de data van controller A aanwezig is. Dit kan conflicten geven bij andere controllerfamilies met actieve push-pull uitgangen, maar niet bij de 8051 familie. Controller B kan nu een actief lage puls op de handshakelijn zetten. Dit wekt een interrupt op in controller A die nu de antwoord-data kan lezen op zijn poort die hij eerst als input schakelt door er '1' niveau's naar te schrijven. Dit proces kan zich nu verder herhalen. Uit deze voorbeelden blijkt dat de 8051 familie een zeer interessante poortstructuur heeft die heel wat mogelijkheden geeft. Het is echer ook nog mogelijk de poortpinnen te gebruiken als I/O pin én als pin met een alternatieve functie tegelijkertijd. Op deze manier kunnen heel wat effecten worden bekomen die anders softwarematig moeten opgelost worden. Hierover volgt later nog meer. Tenslotte verwijzen we naar de Technical Manual van de gebruikte controller voor een uitvoerige beschrijving van de elektrische karakteristieken van elke poort. Dit is enkel van belang voor diegenen die een controller gaan toepassen in een eigen ontwerp. De beschreven poortstructuur is immers niet bij alle 8051 derivaten exact gelijk. Soms is het mogelijk om bv. meer stroom te sinken en er bv. direct LEDs mee aan te sturen. Soms is de structuur zelfs compleet verschillend zoals bij de C868 Infineon controller.
Pauwels D.
Embedded systems V1.0
64
5.2 Timers / Counters. Timers / counters zijn bouwstenen die principiëel kunnen gebruikt worden om 'events' te tellen of om tijdperiodes te meten. Deze bouwstenen zijn meestal geconfigureerd als 8-bit of 16-bit rimpeltellers, met één of meerdere ingangsbronnen voor het clocksignaal. Een carrybit die een rollover van 0FFFFh naar 0000h van de teller aangeeft is voorzien en kan meestal softwarematig worden bekeken, of een interrupt genereren. Indien we de teller clocken met een vaste en gekende systeemclock, of een ervan afgeleide frequentie, dan gebruiken we de tellerhardware als timer. De teller telt dan met vaste en gekende periode. Wanneer we een extern signaal als clocksignaal voor de tellerhardware gebruiken, al dan niet met een vaste frequentie, dan gebruiken we de tellerhardware als counter. Timers / counters kunnen bv. gebruikt worden om de pulsbreedte te meten van een pulssignaal aan de ingang van een timer/counter, om een pulstrein met een bepaalde frequentie en eventueel een complexe golfvorm te genereren op een uitgangspin, om een preciese vertragingstijd op te wekken in een programma enz. We zullen hier verder de basistimers van de 8051 familie bekijken en ook enkele timers die terug te vinden zijn in een aantal verschillende varianten uit deze familie. Deze zijn uitgebreid met extra's zoals capture- en compare mogelijkheden. Tegelijkertijd belichten we enkele van hun mogelijke toepassingen.
5.2.1 Timer 0 en 1 van de 8051 familie. De 8051 familie heeft standaard twee timers, TIMER0 en 1, die beiden individueel gecontroleerd, gelezen, geladen en geconfigureerd kunnen worden. De 8051 timers hebben zoals reeds vermeld een aantal algemene functies: • de tijd bijhouden en/of tijd meten tussen events • het tellen van de events zelf • het genereren van baudrates voor de seriële poort. De hardware van de twee timers wordt gecontroleerd en geconfigureerd door de special function registers TCON en TMOD, die ze samen delen. Elke timer heeft zelf ook nog twee SFR's die de eigenlijke teller vormen (TH0/TL0 en TH1/TL1). De hardwareblokken van de timers kunnen softwarematig op vier verschillende manieren (modes 0 tot 3) worden geconfigureerd.
5.2.1.1
TIMER modes.
MODE0 Door de TIMER/COUNTER in deze mode te schakelen configureren we hem als een 8bit TIMER/COUNTER met een 5 bit prescaler ( ÷32 ), dit is een 8048 compatibele mode. De 8048 is een oudere en nu niet meer gebruikte controllerfamilie. In deze mode is het telregister bestaande uit TH0 en TL0 als 13bit register geconfigureerd. Als het telregister overloopt van 1111111111111B naar 0000000000000B wordt de overflowflag TF0 geset. Deze kan gebruikt worden als interruptbron voor het genereren van een IRQ. In de figuur 5.2.1.1.1 zijn de bits ‘GATE’, TR0, C/T software bestuurbare bits die bij de timer-initialisatie kunnen ingesteld worden. Ook TL0 en TH0 zijn softwarematig aanpasbaar.
Pauwels D.
Embedded systems V1.0
65
TR0, Gate, C/T zijn softwarematig instelbare bits terwijl TL0, TH0 als byte aanspreekbaar en aanpasbaar zijn a.d.h.v. een MOV instructie
Fig. 5.2.1.1.1 Timer/Counter 0/1 in mode0 als 13bit Timer/Counter
De te tellen pulsen worden naar het telregister doorgelaten indien de TR0 bit (Timer Run-bit) op 1 staat, en wanneer GATE 0 werd gemaakt of wanneer GATE 1 is maar de INT0-pin 1 is gemaakt. De GATE-bit 1 maken laat toe om de INT0-pin te gebruiken als externe ‘gating-pin’ wat nuttig kan zijn om pulsbreedte metingen te doen. Mode 0 is hetzelfde voor TIMER0 en TIMER1. MODE1
Fig. 5.2.1.1.2 Timer/Counter 0/1 in mode1 als 16bit Timer/Counter.
Deze mode functioneert op dezelfde manier als mode0, het enige verschil is dat het telregister nu bestaat uit 16bits t.o.v. 13bits in mode0. Deze mode1 heeft dus een groter telbereik nl. van 0 tot 65535 i.p.v. 8191. Voor een systeem met een clockfrequentie van 12MHz resulteert dit in timermode in een telfrequentie van 1MHz, of een periodetijd van 1µs. Het resultaat hiervan is dus dat, als timer in mode0, een maximale tijd kan gemeten worden van 8192µs. of 8.192 ms, terwijl dat in mode1 oploopt tot 65536 µs of 65.536 ms.
Pauwels D.
Embedded systems V1.0
66
MODE2 In mode2 wordt de timer geconfigureerd als 8bit timer, het telregister is dus maar 8bit groot, met “reload” eigenschappen. In deze mode zal het telregister TL0 herladen worden vanuit het reload-register TH0 bij een telregister-overflow. De reload laat de waarde in het register TH0 onveranderd.
Fig. 5.2.1.1.3 Timer/Counter 0/1 in mode2 als 8bit timer/Counter met Auto-Reload
Deze mode kan gebruikt worden om periodisch bepaalde “events” lees interrupt-routines op te starten zonder tussenkomst ( polling ) van de CPU. Stel dat we een routine om de 250µs. wensen uit te voeren met een clockfrequentie van 12 MHz, dan volstaat het om de registers TH0 en TL0 te laden met de waarde 6, en de timer in mode2 te schakelen om hiermee om de 250 µs. een interrupt te laten opwekken. Immers het maximale bereik in deze mode2 is 256 , wat in timermode (@12MHz) overeenstemt met een maximale tijd van 256µs. Door nu reeds het telregister met 6 te preloaden zal 250 pulsen ( dus 250µs) later er een overflow met reload optreden. Deze overflow kan aanleiding geven tot het genereren van een interrupt indien de interruptlogica hiervoor werd geconfigureerd ( zie interrupts).
Pauwels D.
Embedded systems V1.0
67
MODE3
Fig. 5.1.1.1.4 Timer/Counter 0 in mode3 als twee 8bit timer/counters.
Deze mode heeft een verschillend effect op TIMER0 en TIMER1. TIMER1 behoud gewoon zijn waarde in deze mode, het effect is hetzelfde als TR1 op nul te zetten. Bij TIMER0 in mode 3 zal worden opgesplitst in twee 8bit timers gevormd door telregisters TH0 en TL0. TL0 gebruikt de TIMER0 controlebits C/T, GATE, TR0 en de INT0 en T0 pinnen, terwijl TH0 gebruik maakt van de TR1 bit en overflows aangeeft in de TF1 flag! Dus, TH0 controleert nu de “timer1” interrupt. Mode3 is voorzien voor toepassingen waar een extra 8bit timer nodig is. Terwijl TIMER0 in mode3 staat kan TIMER1 aan- en uitgeschakeld worden door hem in en uit mode3 te schakelen, TIMER1 kan hier gebruikt worden voor elke toepassing waar er geen interrupt-generatie nodig is.
Pauwels D.
Embedded systems V1.0
68
5.2.1.2 Besturingsregisters voor TIMER0/1 •
Special Function Register TMOD.
Dit register bestaat uit twee identieke vier-bits velden die instaan voor de configuratie van TIMER0 en TIMER1. Elke bit in dit register heeft een besturingsfunctie voor de timers. GATE: Gating controle. Wanneer deze op 0 staat zal timer0/1 geënabled zijn als de TR0 /TR1 bit geset is. Wanneer deze op 1 staat zal timer/counter 0/1 geënabled zijn alleen als de betreffende interrupt pin INT0/INT1 hoog is en als de TR0/TR1 bit geset is. C/T: Counter of Timer selectiebit. Voor timer-werking moet deze op nul staan, voor counter-werking moet deze op 1 staan. Deze bit bepaalt van waar de te tellen pulsen zullen komen, intern vanuit de systeem-clock ÷12, of extern van de T0 of T1 pin. MODEBITS M1,M0. Met de combinatie van deze twee bits kiezen we de werkmode van de timer/counter
De bits in dit register zijn niet individueel aanspreekbaar met SETB of CLR instructies, we moeten dus een hele byte ineens aanspreken. Dit doen we met een MOV instructie, zo zal vb. de instructie MOV TMOD,#00000010B timer0 in mode2 schakelen en timer1 in mode0.
Pauwels D.
Embedded systems V1.0
69
•
Special Function Register TCON
Twee bits uit dit register worden gebruikt om de timer/counter 0/1 aan of uit te zetten, dit zijn de bits TR0/TR1. De twee overige bits die in gebruik zijn voor timertoepassingen zijn TF0/TF1 dit zijn de timer-overflow flags die aangeven dat een timer/counter is overgelopen. Deze bits kunnen worden gebruikt om softwarematig de status van de timer te pollen, of om een interrupt te genereren.
De bits in dit register zijn wel individueel aanspreekbaar en kunnen dus met SETB en CLR instructies aangepast worden . Zo zal bv. de instructie SETB TR0 timer0 starten.
Pauwels D.
Embedded systems V1.0
70
5.2.2 Toepassingen van TIMER 0 en 1. Bij wijze van voorbeeld worden een aantal verschillende toepassingsmogelijkheden van TIMER0 en 1 geschetst, met hun specifieke eigenschappen.
5.2.2.1
Tijdsmetingen van events (eenmalige intervalmeting).
Bij tijdsmetingen van events worden timers meestal 'gated' gebruikt. De signaalpuls waarvan we de tijdsduur willen kennen wordt m.a.w. gebruikt als schakelsignaal om een clocksignaal met gekende periode door te schakelen naar het tellerregister, zolang de puls duurt. De teller zal worden geïncrementeerd zolang de puls aanwezig is. Indien we het tellerregister voorafgaandelijk clearen, kan nadat de puls verstreken is de tellertoestand gelezen worden. De tellerwaarde is dan een maat voor de verstreken tijd uitgedrukt in clockperiodes. Voor een gekende clockperiode kan hieruit dan de verstreken tijd worden berekend. Als clocksignaal wordt meestal het interne oscillatorsignaal gedeeld door 12 gebruikt, wat er op neer komt dat de teller iedere machinecyclus geïncrementeerd wordt. Indien gewenst kan ook een extern clocksignaal op de T0 of T1 ingang als tellerclock gebruikt worden. Men moet het te meten pulssignaal aanleggen aan de INT0/P3.2 ingang, die naast de 'gate'functie ook tegelijk als interruptingang kan aangeschakeld worden. Dit heeft het bijkomende voordeel dat bij het beëindigen van de puls (event) ook direct een interrupt kan worden gegenereerd, die de routine voor de tijdsmeting opstart.
OSC
Div 12
Set&Cleared door hardware C/T =0 TL0 (8 Bits)
T0 pin Extern event 5V Gnd
TH0 (8 Bits)
TF0
Interrupt
C/T =1 ‘1’ Gate
TR0 &
INT0 pin
Control
>1
Resetten voor intervalmeting
t INT0 kan gelijktijdig gebruikt worden als een interruptingang en als gate.
Fig. 5.2.2.1.1 Tijdsmeting van events via basistimers.
Wat is de meetonzekerheid, nauwkeurigheid en het bereik van de timer? 1µs onzekerheid
1µs onzekerheid
t
Timerincrements elke µs.
Fig. 5.2.2.1.2 Meetonzekerheid bij tijdsmeting van het 'gating'signaal.
Afhankelijk van de oscillatorfrequentie zal de teller met een bepaalde snelheid worden geïncrementeerd. Voor een clockfrequentie van 12 MHz is dit 1µs. Door het feit dat het Pauwels D.
Embedded systems V1.0
71
'event' (de te meten puls) asynchroon met het clocksignaal van de teller verloopt zal er een meetonzekerheid bestaan van maximum 1µs aan de beide flanken van de te meten puls. Dit resulteert in een onzekerheid van 1µs op de tijdsmeting van de puls. Dit is vooral nadelig bij een kleine pulsbreedte. De nauwkeurigheid van de oscillatorfrequentie bepaald ook deze van de pulsbreedte, dit is meestal geen probleem daar de oscillatorfrequentie meestal wordt afgeleid van een quartskristal met een nauwkeurigheid van enkele ppm. Het bereik van de timer wordt bepaald door de mode waarin de timer wordt geschakeld, het grootste bereik (65536 counts of 65,536ms@12MHz) wordt bekomen als 16 bit teller in mode1. Softwarematig kan echter het bereik van de timer worden uitgebreid bij eenzelfde resolutie (clockfrequentie). Dit kan door de timeroverflows interrupts te laten genereren (IRQ Timer) die dan softwarematig worden geteld. Op deze manier kunnen we toch een timer implementeren met een praktisch onbeperkt bereik en een hoge resolutie. Het werkingsprincipe wordt in de volgende figuur geïllustreerd. Teller register 16 bit
65556µs @12MHz
65556µs @12MHz
65556µs @12MHz
65556µs @12MHz
21845µs @12MHz
FFFFh
Timer overflow
C/T=0 TR0=1 GATE=1 RESET TH0,TL0
5555h
0000h
t IRQ Timer
IRQ Timer
IRQ Timer
IRQ Timer
Start meting
Stop: Gate dicht
Gatesignaal
t Gate tijd= 4x65536µs +21845 µs
IRQ gate
IRQ Timer : INC “COUNTER” IRQ gate : lees TH0, TL0 en “COUNTER” en bereken de tijd
Overflows tellen COUNTER
TH0
TL0
Fig. 5.2.2.1.3 Softwarematig uitbreiden van het bereik van de tijdsmeting.
Pauwels D.
Embedded systems V1.0
72
5.2.2.2
Genereren van periodische timeroverflows of 'timeticks'.
In de meeste systemen moeten er, naast een background taak, op periodische basis een aantal andere taken uitgevoerd worden. De meest gebruikelijke manier om dit te implementeren is deze taken als interruptroutine uit te voeren die periodisch wordt opgestart aan de hand van een timer interrupt. Er zijn een aantal mogelijkheden om dit te verwezenlijken met een timer. 16 bit TIMER (mode1) met software reload. Wanneer we een timer in mode1 schakelen en starten, zal hij bij elke machinecyclus geïncrementeerd worden en na 65536 counts via een overflow weer vanaf 0000h verder tellen. Door interrupts te laten genereren bij timeroverflow kunnen we zo periodisch om de 65.636 ms (@12MHz) een taak laten uitvoeren. Het nadeel van deze methode is dat we een vaste periode verkrijgen als tijdsbasis. Timer overflow FFFFh 16 bit timer (mode1) zonder reload
0000h
t 65536µs @12MHz
TimerIRQ
TimerIRQ
TimerIRQ
Fig. 5.2.2.2.1 Time tick van 65.636 ms.
Een oplossing voor dit probleem is het preloaden van de tellerregisters (THx,TLx) met een bepaalde waarde (bv. XXYY) en dan pas de teller te starten. Op deze manier zal de teller veel vlugger zijn eindwaarde bereiken en een interrupt genereren bij het overlopen naar 0000h. De tijd 'Tp' hiervoor nodig is (10000h-XXYYh)•12/f waar f de clockfrequentie voorstelt. Wanneer we nu in de interruptroutine die als gevolg van de overflow opgestart wordt de timer even stoppen om hem weer te herladen met de waarde XXYY en hem daarna weer starten, dan zal zich na de ingestelde periode weer een overflow voordoen. De periode wordt zo op een softwarematige manier aanpasbaar. De tijd tussen de overflows wordt gelijk aan Tp, plus de tijd nodig om de timer softwarematig te herladen, plus de interruptlatency tijd van de microcontroller. De interruptlatency tijd is de tijd die de controller nodig heeft na het genereren van de interruptrequest (IRQ) tot het uitvoeren van de eerste instructie in de interruptroutine. Deze tijd is afhankelijk van het type instructie die de controller uitvoert bij het opwekken van de IRQ. Niet alle instructies duren immers even lang. Dit heeft timingjitter als gevolg tussen de opeenvolgende timeroverflows. Voor een aantal toepassingen heeft dit geen gevolgen, maar indien we deze 'timeticks' (periodische interrupts) gebruiken voor het genereren van
Pauwels D.
Embedded systems V1.0
73
timingsignalen op poortpinnen van de controller, of voor het updaten van een RTC dan zal dit ook hier aanleiding geven tot timingjitter, wat hier zeker niet toelaatbaar is. Er moet dus een betere manier gevonden worden! Timer overflows FFFFh
XXYY “preload” Timing jitter! 0000h
t
Tp=(10000h-XXYYh) .12/f
TimerIRQ
TimerIRQ
Interruptroutine bij timeroverflow: stop timer: TR0=0 reload timer: TH0=XX TL0=YY start timer: TR0=1 “toepassing” bv. Sturen van poortpinnen RETI
TimerIRQ
TimerIRQ
TimerIRQ
Interrupt latency time = constant !!! timingjitter = geschikt als “timetick”
Accumulatie van timingfouten!! Fig. 5.2.2.2.2 Timingjitter bij software reload van de timer.
8 bit TIMER met autoreload. Het probleem dat zich stelt bij de vorige situatie kan alleen worden opgelost indien de timer kan herladen worden zonder de softwarematige tussenkomst van de controller. Dit kan bv. door de timer in 8-bit autoreload mode te schakelen. timeroverflows FFh
THx reload waarde
(100h-reload).12/f
t
00h TLx en THx laden
Start timer
timer IRQ
timer IRQ
timer IRQ
timer IRQ
timer IRQ
Fig. 5.2.2.2.3 Hardware reload van de timer. Pauwels D.
Embedded systems V1.0
74
De timingjitter is hier verdwenen en de periode tussen de interrupt requests wordt enkel nog bepaald door de clockfrequentie en de reloadwaarde van de timer. Deze mode heeft het voordeel dat ze wel kan gebruikt worden voor bv. het incrementeren van een RTC. Het nadeel van deze timermode is dat er bij de standaard 8051 timers (TIMER0 &1) slechts met een 8-bit teller kan gewerkt worden. De maximale periode tussen de interrupts wordt daardoor beperkt tot 256 keer de clockperiode (256µs @12MHz) wat voor sommige systemen veel te klein is. Sommige 8051 varianten hebben een bijkomende 16-bit timer (TIMER2) met autoreload mogelijkheden wat het probleem oplost. Dit euvel kan echter bij de basistimers TIMER0&1 ook softwarematig worden weggewerkt door in de interruptroutine het aantal gegenereerde IRQ's te tellen en pas na een bepaald aantal telkens de toepassing uit te voeren. Het volgende illustreert dit principe. 'COUNTER' is de variabele die na elke IRQ wordt gedecrementeerd en die wanneer hij nul wordt softwarematig weer wordt herladen met de waarde 'SKIP_IRQ', die ook bij de initialisatiefase van het systeem in deze variabele moet worden geladen. timerIRQ
Initialisatie: MOV COUNTER, #SKIP_IRQ
DJNZ COUNTER,BACK MOV COUNTER,#SKIP_IRQ
Toepassing
BACK: RETI
Fig. 5.2.2.2.4 Software verlenging van de tijd tussen software-events.
Op deze manier kunnen we praktisch een oplossing bieden voor de meeste toepassingen.
5.2.3 TIMER2 van de 8052 familie. (AT 89S8252) De meeste 8052 varianten (8051 plus enkele extra's) beschikken over een extra timer2, die voor elke controller er anders kan uitzien. De Atmel 89S8252 die 8051 compatibel is heeft o.a. deze extra Timer2. Timer2 is hier een 16 bit timer/counter die kan werken als timer of event counter. Timer2 heeft hier een capture en een auto-reload (up-down) mode. Aan de hand van deze timer is het bv. mogelijk grotere tijden tussen 'timeticks' te genereren zonder tussenkomst van de CPU.
5.2.3.1
'Missing or late pulse' detector.
Een andere mogelijke toepassing is een missing of late pulse detector die aangeeft dat een puls wegvalt uit een pulstrein, of dat de pulstrein helemaal wegvalt. Voor deze toepassing maken we gebruik van de T2EX pin van timer2. Langs deze pin kunnen we timer2 herladen via de dalende flank van een aangelegd signaal, in ons geval de te bewaken pulstrein. De EXEN2 schakelaar wordt hiervoor softwarematig gesloten. De timer wordt in autoreload mode geschakeld en geladen met een waarde XXYY. Pauwels D.
Embedded systems V1.0
75
Deze waarde is zodanig gekozen dat wanneer een pulstrein met bepaalde eigenschappen op de T2EX pin wordt gezet, de timer nooit een overflow genereert. Indien er een puls wegvalt of te laat is, zal de timer overlopen en een IRQ genereren en kan het programma gepast reageren. Indien we de pulstrein afleiden van een nuldoorgangsdetectie van de netspanning, de pulsen komen dan om de 10 ms, dan kunnen we op deze manier bv. detecteren dat de netspanning wegvalt. Grotere tijd tussen ‘timeticks’ zonder tussenkomst van de CPU Toepassing bv. ‘missing’ of ‘late’ puls detector
Sync. van de ‘reload’ met de dalende flank Fig. 5.2.3.1.1 16-bit reload van timer 2 van een AT89S8252 via de T2EX pin .
Timer overflow
FFFFh
TL2,TH2 laden
Reload XXYY
TR2= ‘on’ EXEN2= ‘on’
0000h
T2EX pin
ok Vanaf hier detectie
Geen overflow IRQ
Overflow IRQ
Niet ok
t
ok t
Puls ontbreekt
Fig. 5.2.3.1.2 Missing pulse detectie via Timer2 van een AT89S8252. Pauwels D.
Embedded systems V1.0
76
5.2.3.2
Absolute positiemeting a.d.h.v. lijnsensoren of hoekencoders.
Timer2 kan bij de AT89S8252 ook als up-down counter worden geschakeld, waarbij de 'direction' pin (P1.1) aangeeft in welke richting geteld wordt. Op de T2 pin (P1.0) kan dan een extern signaal worden aangelegd waarvan we de pulsen kunnen tellen. Op deze manier kunnen we een absolute positiemeting doen a.d.h.v. een incrementele lineaire- of hoek-encoder. Deze encoders leveren twee 90º verschoven blokgolfvormige signalen, waarbij elke alternantie overeenkomt met een bepaalde lineaire- of hoekverplaatsing. De fase van het 'B' signaal t.o.v. het 'A' signaal (voorijlend of naijlend) is afhankelijk van de bewegingsrichting van de encoder. Zo zijn er bv. hoekencoders op de markt met 100 tot enkele duizenden pulsen per omwenteling! Bij een lijnsensor (lineaire encoder) is een resolutie van 20 pulsen per mm verplaatsing (1 puls per 50 micrometer) courant, denk hierbij maar aan de sensoren die worden gebruikt in de low cost inktjet printers.
B ccw B cw
A
Fig. 5.2.3.2.1 Hoekencoder met een quadratuur gecodeerd uitgangssignaal.
B
D
Q
Direction T2EX pin
Clk A
Count T2 pin
Fig. 5.2.3.1.2 Omzetting van quadratuursignalen in count en direction signalen.
Door de signalen A en B aan te leggen aan een D-FF halen we uit deze signalen de 'direction' en 'count' informatie die we respectievelijk kunnen aanleggen aan de ingangen T2EX en T2 van timer2. Op deze manier kunnen we de absolute positie bijhouden met de timer, indien we deze preloaden met de waarde 7FFFh. Dit is onze 'home' positie (nulpositie), de positie waarvan we vertrekken met de meting. We kunnen nu evenveel pulsen (de helft van het 16bit bereik of 32768pulsen) naar boven als naar beneden tellen. Op deze manier kunnen we de absolute positie van onze encoder bijhouden zonder verdere tussenkomst van de CPU, dit voor een bereik van 1,6384 m in beide richtingen bij een resolutie van 50 µm!
Pauwels D.
Embedded systems V1.0
77
Indien dit bereik moet worden uitgebreid, kan dit op eenvoudige manier door softwarematig a.d.h.v. de underflows en overflows een bijkomende variabele (die de MSB vormt van het uiteindelijke bereik) te decrementeren of te incrementeren . FFFFh + TR2=’on’
‘0’ positie ‘home’
7FFFh
Preload
-
0000h
t
Direction
Up
Up
Down
t
Count
t +
-
+
Start beweging Fig. 5.2.3.1.3 Automatische positiebepaling via een timer met under/overflow reload.
underflow
overflow
P1.0
0000h
Direction (ook softwarematig te controleren =pin P1.1) Fig. 5.2.3.1.4 Timer2 van de AT89S8252 met under/overflow reload mogelijkheden.
Pauwels D.
Embedded systems V1.0
78
5.2.3.3
Periodemeting a.d.h.v. de capture mode.
Een 'capture' mode van een timer is een mode waarbij de tellerwaarde van een timer bij een bepaalde gebeurtenis (typisch een flank aan een controlepin van de timer) wordt gecopiëerd naar een capture-register. Dit capture-register kan dan door de CPU worden gelezen en bewaard in het geheugen. Bij een tweede 'capture' kan de CPU het capture-register opnieuw lezen en het verschil maken tussen de twee capture waarden. Het verschil tussen beide waarden is een maat voor het tijdsverschil (periode) tussen de captures (flanken op de controlepin). De CPU wordt op interruptbasis verwittigd van het capturemoment zodat deze op het gepaste moment het captureregister kan lezen. Tevens worden de telleroverflows op interruptbasis doorgegeven aan de CPU, zodat deze kunnen worden geteld voor een software-extentie van het tellerbereik (periode). Timer2 in CAPTURE mode toepassing: periode meting
Interrupts tellen voor software ‘teller extentie’
Fig. 5.2.3.3.1 Timer2 met capture mogelijkheden van de AT89S8252.
Capture2
FFFFh
E01fh overflow
Tellerwaarde Capture1 0000h
1713h Timeroverflow IRQ
t
T2EX Capture IRQ
Capture IRQ
t
time= (capture2 - capture1)x 12/f Fig. 5.2.3.3.2 Principe van periodemeting via de capture mode van een timer.
Pauwels D.
Embedded systems V1.0
79
5.2.4 Compare unit. Sommige varianten uit de 8051 familie zoals de Infineon C537 en C517 bezitten een compare-unit, die samen met een timer kan gebruikt worden. Een compare-unit vergelijkt een 16-bit getal dat is opgeslagen in het compareregister met de inhoud van een tellerregister. Wanneer de tellerwaarde de opgeslagen waarde bereikt, wordt een uitgangssignaal gegenereerd op een bijhorende poortpin. De inhoud van het compareregister kan eigenlijk worden gezien als een soort van 'time stamp' waarbij een bepaalde uitgang moet reageren op een welbepaalde manier (pin gaat naar '0' of '1'). Een variatie van deze comparewaarde verandert de golfvorm van een blokgolfsignaal op de poortpin. Op deze manier kan een PWM signaal worden opgewekt waarvan de duty-cycle softwarematig aanpasbaar is, of er kan een reeks gedefiniëerde blokgolfsignalen op een gecontroleerde manier worden gegenereerd. Deze twee verschillende modes worden nu verder besproken.
5.2.4.1
Opwekken van PWM signalen a.d.h.v. de compare unit.
Waneer in deze mode het tellerregister de comparewaarde bereikt zal het uigangssignaal veranderen van '0' naar '1' . Het wordt weer '0' gemaakt bij een timeroverflow. Deze mode is ideaal voor het genereren van pulsbreedte gemoduleerde signalen (PWM). PWM signalen kunnen op hun beurt gebruikt worden voor digitaal naar analoog conversie via een low pass filternetwerk, of via het gecontroleerde element zelf, denk maar aan de inductantie van een DC of AC motor. 16 bit compareregister
16 bit digitale comparator match
Port x.y S
Q
R 16 bit timer (autoreload)
Timer overflow
Fig. 5.2.4.1.1 Principe van het opwekken van een PWM signaal via een timer. Pauwels D.
Embedded systems V1.0
80
5.2.4.2
Opwekken van meerdere complexe timingsignalen.
De compare-unit kan ook gebruikt worden samen met software die adaptief de transities aan de uitgangspinnen bepaalt. Deze manier van werken wordt veel gebruikt voor het opwekken van signalen die geen vaste periode hebben zoals een PWM signaal, maar die wel heel precies en met een hoge resolutie moeten gecontroleerd worden zonder jitter. In deze mode kunnen de beide transities van het uitgangssignaal worden gecontroleerd.
Fig. 5.2.4.2.1 Opwekken van complexe signalen via een timer met compare unit zoals de Infineon C517.
Een softwarematige schrijfoperatie naar een uitgangspoort zal in deze mode geen direct gevolg hebben op de uitgangspinnen, de nieuwe waarde die naar de poort werd geschreven zal pas effectief op de uitgangspinnen worden gezet bij de volgende gelijkheid van de tellerwaarde en het compareregister. Bij deze gelijkheid (match) wordt een interrupt request doorgegeven aan de CPU. Men kan op deze manier dus kiezen of een pin een transitie moet maken van '0' naar '1' of van '1' naar '0' of dat het niveau ongewijzigd moet blijven bij de volgende 'comparematch'. In het blokschema fig. 5.2.4.2.1 wordt aangegeven dat het poortregister eigenlijk uit twee verschillende registers bestaat. Het register bovenaan (shadow latch) kan onder softwarecontrole worden geschreven, maar de geschreven waarde zal pas worden doorgegeven naar het uitgangsregister (output buffer) in responce op een comparator gelijkheid. De gebruiker maakt voor deze toepassing twee tabellen aan in het geheugen. Een tabel die de achtereenvolgende uitgangswaarden (pattern) weergeeft die op de poort moeten worden gegenereerd. En een tweede tabel met16 bit waarden (schedule) die aangeeft bij welke tellerwaarden de opeenvolgende patroonwaarden naar buiten moeten gestuurd worden. De CPU zal uit de patroontabel de eerste data ophalen en naar het shadowregister sturen en vervolgens de timinginfo ophalen uit de scheduletabel en deze naar het compareregister schrijven. De bijhorende timer wordt dan geïnitialiseerd en gestart. De uitgangspinnen zullen dan bij het bereiken van de comparewaarde automatisch worden aangepast. De CPU krijgt ook een IRQ binnen en kan nu in de bijhorende interruptroutine de waarden van het shadowregister en de comparewaarde aanpassen met nieuwe waarden uit de
Pauwels D.
Embedded systems V1.0
81
tabellen. De teller loop ondertussen verder en bij een volgende gelijkheid worden de poortpinnen opnieuw aangepast. Belangrijk hier is te begrijpen dat de opgewekte signalen worden opgewekt met een timing die door de timer wordt bepaald en dat alle signalen tegelijk van toestand kunnen veranderen zonder timingjitter. Volgende figuur 5.2.4.2.2 illustreert een en ander.
Fig. 5.2.4.2.2 Gebruik van twee tabellen voor het genereren van timingsignalen op poort5 van een C517.
Pauwels D.
Embedded systems V1.0
82
5.2.5 Watchdog timer (AT 89s8252). De meeste embedded systemen moeten zeer betrouwbaar zijn, het is niet mogelijk om te wachten tot er iemand het systeem reboot als de software hangt. Sommige systemen zijn zelfs niet toegankelijk voor personen. Als hun software ooit hangt, blijven ze voor altijd buiten werking. Voor andere systemen zou de snelheid waarmee het systeem door een persoon gereset wordt te traag zijn om de werking van het product in zijn geheel te garanderen. Een watchdog timer is een hardware timer die een detectie kan maken van softwareproblemen en de CPU kan resetten als ze optreden. Algemeen gezien is een watchdog timer gebaseeerd op een teller die vanaf een initiële waarde naar beneden telt tot nul. De embedded software stelt de initiële waarde in en herstart de teller op periodische basis. Als de teller ooit de waarde nul bereikt voordat de software hem herstart, wordt er verondersteld dat de software een probleem heeft en wordt de CPU gereset. Op dat ogenblik wordt de CPU en de embedded software die erop draait herstart op dezelfde manier als bij een hardware reset. De watchdogtimer is soms een chip extern van de controller, maar meestal zit hij intern in de controller. In ieder geval is de uitgang van de watchdogtimer verbonden met de reset ingang van de CPU. Onafhankelijke oscillator
Watchdogtimer
Reset CPU
Osc. Prog. prescaler
PS0,PS1,PS2 Power on
POF bit=’1' RST heeft geen invloed
WDTRST WD reset Write '1' (Power Failure)
WDTEN
Fig. 5.2.5.1 Functioneel blokschema van de AT89S8252 watchdogtimer.
Als vb. nemen we de watchdogtimer uit de AT89S8252 chip van Atmel. De watchdog timerperiode kunnen we softwarematig instellen a.d.h.v. de PS0..PS2 bits uit het WMCON register zoals aangegeven in de volgende tabel 5.2.5.1.
Pauwels D.
Embedded systems V1.0
83
Tabel 5.2.5.1 Mogelijke timerperiodes.
Stel dat de normale programmaloop ongeveer 26ms in beslag neemt, dan stellen we de watchdogtimer in op de eerst volgende grotere waarde, in ons geval 32ms. Dan zal in normale omstandigheden de watchdogtimer om de 26ms worden herstart en zal een watchdogtimer nooit de CPU resetten. Bij het aanschakelen van de voedingsspanning zal ook een POF-bit worden geset, die aangeeft dat er een powerfailure was. Deze bit wordt na de initialisatie van het systeem softwarematig gecontroleerd, en indien geset, geeft dit aan dat het systeem gereset werd door een power-up conditie. In de (oneindige) hoofdlus resetten we dan softwarematig deze bit en starten de normale procedures van de toepassing. Op het einde van de hoofdlus resetten we de watchdogtimer door de WDRST bit te setten, en blijven de lus uitvoeren. Power-on 'POF'='1'
Systeem initialisatie
N POF='1'?
Watchdog reset
nok
ok J
Error handler (error counter??)
Main loop POF='0"
Bv: programma-loop= 26ms watchdog=32 ms
normale hoofdprogramma (oneindige lus met bepaalde maximum-timing)
Shut down
Interrupt handler watchdog herstarten
Fig. 5.2.5.2 programmastructuur bij het gebruik van een watchdogtimer.
Pauwels D.
Embedded systems V1.0
84
Indien door een softwarefout de watchdog niet wordt herstart (oneindige lus door een slechte test in een subroutine, overvloed aan interrupts, deadlock bij multitasking…) dan wordt het systeem gereset en starten we weer bovenaan in de flowchart. In dit geval zal de 'POF' bit echter niet op '1' staan en kunnen we detecteren dat er een watchdog-reset is opgetreden. We kunnen dit afhandelen door een error handler uit te voeren die de situatie afhandelt of die in de meeste gevallen niet meer is dan een errorcounter die na een aantal watchdog resets het systeem naar een shutdown toestand brengt. Wanneer de watchdog timer intern de controller zit, zal hij niet automatisch opstarten maar moet dit bij de initialisatie van het programma gebeuren. Om te voorkomen dat hij per ongeluk door een 'bug' in het programma wordt uitgeschakeld kan de watchdogtimer meestal niet meer softwarematig uitgeschakeld wortden. Voor sommige producten wordt door de Duitse TUV veiligheidsreglementering (Technischer ÜberwachungsVerein) geëist dat een hardwarematige watchdogtimer extern van de controller zit, daar hij dan ook bij een fout in de initialisatie- software reeds kan optreden. Een watchdog timer is een zeer nuttige tool om een systeem te helpen herstellen van sommige eenmalige fouten. Daar het tegenwoordig een standaard is watchdogs aan te treffen in controllers is deze methode ook 'gratis' te implementeren. Wanneer men werkt aan een 'mission-critical' systeem zal gezond verstand en een veiligheidsreglementering vereisen dat er een watchdog gebruikt wordt. En zelf in andere gevallen is het een goed idee om een systeem op deze manier bedrijfszekerder te maken!
Pauwels D.
Embedded systems V1.0
85