Faculteit Ingenieurswetenschappen Vakgroep TFCG Microsystems Lab Voorzitter: Prof. Dr. ir. Andr´e Van Calster
Optimalisatie van de communicatie- en meetomgeving van een aanstuurbord voor bistabiele displays door Jochen Verbrugghe
Promotor: Prof. Dr. ir. A. Van Calster Scriptiebegeleiders: ir. A. Mont´e en Prof. Dr. ir. J. Doutreloigne
Scriptie ingediend tot het behalen van de academische graad van Burgerlijk ingenieur in de computerwetenschappen
Academiejaar 2006–2007
Faculteit Ingenieurswetenschappen Vakgroep TFCG Microsystems Lab Voorzitter: Prof. Dr. ir. Andr´e Van Calster
Optimalisatie van de communicatie- en meetomgeving van een aanstuurbord voor bistabiele displays door Jochen Verbrugghe
Promotor: Prof. Dr. ir. A. Van Calster Scriptiebegeleiders: ir. A. Mont´e en Prof. Dr. ir. J. Doutreloigne
Scriptie ingediend tot het behalen van de academische graad van Burgerlijk ingenieur in de computerwetenschappen
Academiejaar 2006–2007
Voorwoord Van alle opties van de richting computerwetenschappen, is bij ingebedde systemen het meest plaats voor elektronica. Het was dan ook mijn bedoeling om in mijn scriptie een combinatie te vinden van zowel ontwikkeling van software als ontwerp van de onderliggende elektronica. Dat was het geval met dit onderwerp. Een extra uitdaging was het laten samenwerken van de verschillende softwaresystemen, geschreven in zowel het hoogniveau C# als het lage C/assembler niveau met daartussen een handvol C++. Een woord van dank gaat uit naar ir. Ann Mont´e voor een goede en leuke samenwerking. Ook bedank ik Prof. Dr. ir Jan Doutreloigne voor de begeleiding en uitleg.
Toelating tot bruikleen “De auteur geeft de toelating deze scriptie voor consultatie beschikbaar te stellen en delen van de scriptie te kopi¨eren voor persoonlijk gebruik. Elk ander gebruik valt onder de beperkingen van het auteursrecht, in het bijzonder met betrekking tot de verplichting de bron uitdrukkelijk te vermelden bij het aanhalen van resultaten uit deze scriptie.”
Jochen Verbrugghe, mei 2007
ii
Optimalisatie van de communicatie- en meetomgeving van een aanstuurbord voor bistabiele displays door Jochen Verbrugghe Scriptie ingediend tot het behalen van de academische graad van burgerlijk ingenieur in de computerwetenschappen Academiejaar 2006–2007 Promotor: Prof. Dr. ir. A. Van Calster Scriptiebegeleiders: ir. A. Mont´e en Prof. Dr. ir. J. Doutreloigne Faculteit Ingenieurswetenschappen Universiteit Gent Vakgroep TFCG Microsystems Lab Voorzitter: Prof. Dr. ir. Andr´e Van Calster
Samenvatting In het kader van een doctoraat werd een aanstuurbord ontwikkeld om een bistabiel display aan te sturen. Vanaf een PC kunnen beelden worden afgebeeld. In deze scriptie wordt het seri¨ele communicatiekanaal vervangen door een USB-verbinding en wordt de rol van de PC uitgebreid. Het nieuwe grafisch controleprogramma biedt totale controle over het bord. Verder wordt ook een methode voor semi-automatische vermogenmeting uitgewerkt die toelaat het vermogen te meten dat het display dissipeert bij het afbeelden van een beeld.
Trefwoorden USB, C#, oscilloscoop automatisatie, 8051
Optimization of the Communication and Test Setup for a Bistable Display Motherboard Jochen Verbrugghe Supervisor(s): Ann Mont´e, Jan Doutreloigne Abstract—This article discusses three enhancements to a bistable display motherboard. First, the serial PC-motherboard link is upgraded to a modern USB interface. Second, a graphical PC front end is developed to provide rich functionality, yet is easy to use. Third, a semi-automatic power measurement setup, integrated with the PC front end, is presented to evaluate power consumption during a display cycle. Keywords—USB, .NET, C, oscilloscope control
I. I NTRODUCTION
B
ISTABLE displays keep their image when no voltage source is connected. This makes them an excellent choice for battery-driven appliances with low refresh rates. The TFCG department has developed advanced chips that generate the complex waveforms that drive these displays. A motherboard contains a microcontroller chip, 128 KiB flash memory, an FPGA and the drivers. Via the microcontroller, a PC can upload grayscale images to the flash which are used by the FPGA to control the driver chips. A MAX232 converts between serial and CMOS levels. To enable more efficient testing, a new graphical PC front end and new microcontroller code is realised. A USB communication link ensures a high-speed connection. A method for power measurement is developed for semi-automatic testing and is integrated with the new user interface.
Fig. 1. Schematic of the USB module.
the PC and one in the microcontroller. A. PC front end
The FT232R chip comes with a native Win32 driver. As the front end is written C# 2.0, the PC software is divided in 2 parts: 1. the DLL FT232RWrapper that wraps the native driver in an object-oriented C# class; 2. the application BDControl which provides a user interface (UI) and implements all functionality. II. USB COMMUNICATION LINK 24/05/2007 21:33:44 D:\Data\Documents\BC3\Thesis\Verslag\Diagramma\usbmodule.sch (Sheet: 1/1) BDControl (figure 2) has a highly object-oriented structure. The microcontroller DS89C420 communicates with the PC Together with a layered communication model, it is easily modiusing its integrated serial port. It does not sport an USB fied to allow other communication means and additional actions. stack. Consequently, we need a USB/serial converter chip: the Its main classes can be summarized as following. FT232R by FTDI. As the development of a new printed cir- • Image and project management: these classes model images cuit board is costly, a modular solution is presented. Because and projects. They provide functionality such as saving and level conversion is integrated with the FT232R, the MAX232 is loading from disk, importing and setting up the actions. dropped and the electronics for USB communication, placed on • GUI classes: the UI is comprised of several custom controls, a separate assembly, are plugged into the socket of the MAX232. derived from the C# class UserControl. Their main function The circuit schematic is shown in figure 1. LEDs D1 and D2 is to accept user input and present results. They contain almost light up when data is received or transmitted. The other com- no business logic but serve only as glue logic. Four child winponents are resetting the chip on power-up or are decoupling dows are implemented: a project navigator, an image visualizer, signals. a log view and a power measurement control interface. • Action classes: due to their specific dependent nature, e.g. III. S OFTWARE storing a project is essentially just storing 16 images, actions A user can perform actions, defined as interactions with the can be implemented as a class hierarchy. display. Possible actions are uploading an image or project to • Communication classes: the lowest level of communication the flash, downloading and displaying it. A project is a collec- is controlled by the native FT232R driver. One level up is the tion of 16 images. The communication is adhering to a sim- wrapper mentioned above. This class implements the commuple protocol: ASCII commands initiate an action, the micro- nication primitives such as sending or receiving n bytes. On the controller acknowledges, parameters are sent and the action is highest level, an abstract communicator takes care of processing carried out. Because of this sequential structure, they are imple- commands such as awaiting an acknowledge. This class is used mented as communicating finite state machines (CFSM), one in by the derived action classes.
Fig. 2. BDControl.
B. Microcontroller code The microcontroller code, written in C, consists of 4 state machines: a main FSM calls the FSMs that implement the 3 actions ‘store image’, ‘load image’, ‘display image’. During normal operation, the controller is idle: it goes into a power saving mode. When a character is received, an interrupt is triggered and the state of the main FSM is changed accordingly in the interrupt service routine (ISR). This state subsequently calls the correct FSM. It is this approach that makes idle mode possible, as no data polling of the serial port buffers is required. Also the ISR is sufficiently short, only one variable is changed. To avoid race conditions, serial interrupts are masked during action execution. The flash chip has to be programmed on a sector-by-sector basis. 128 bytes and addresses are loaded and simultaneously programmed. Therefore, during execution of the ‘store action’, data is received from the PC in blocks of 128 bytes. They are buffered by the microcontroller and then copied to the flash memory, which is accessed as external memory. The end of a program cylce (≈ 10 ms) can be detected by data polling: during a program cycle an attemped read of the last byte loaded will result in the complement of the loaded data on I/O7. Once the programming has been completed, true data is valid on all outputs. De ‘load action’ merely sends an image back to the PC, in one chunk. To display an image, the microcontroller needs to transfer data to the FPGA. A simple protocol is used: a reset pulse is given, data is put on the databus and a data available signal is set. This signal instructs the FPGA to cache the data. The microcontroller is running at 32 MHz, while the FPGA is running at 4 MHz. In order to meet setup and hold time constraints, and to allow the FPGA to capture the data, additional wait cycles are inserted in the code. IV. S EMI - AUTOMATIC POWER MEASUREMENT The driver chips use 6 voltage lines: 85 V, 60 V, 55 V, 45 V, 35 V and 5 V. Here, a method is presented to measure power is one
line. Power is dissipated while displaying an image. To measure the dissipation, the voltage drop across a small shunt resister is measured which gives us the current flowing through the line. The formula P = V I gives us the power. As the voltage drop is time-dependent, the average and peak power will be computed. The voltage drop waveform is captured using the dualchannel Tektronix THS720A oscilloscope. A reset puls of the display’s driving signals is used as a trigger. Two kind of measurements are defined: 1. Global measurement: measurement during a full display cycle. This is around 250 ms. 2. Local measurement: measurement during a shorter time, the window. As the scope only samples 2500 data points, a global measurement is only useful to extract windows. Local measurements with a sufficiently short window give accurate results. BDControl has been adapted to provide this new functionality. A dedicated class sets up the scope via a serial connection and initiates a display action. The received curve is processed and results are presented to the user. An example of a global and local measurement of the 60 V line is given in figures 3 and 4.
Fig. 3. Example of a global measurement.
Fig. 4. Example of a local measurement.
Average and peak power are 99.3 mW and 274.3 mW with a window length of 50 µs. V. C ONCLUSION To be able to measure power indicates that all vital components function correctly: the USB communication link, the front end BDControl and the computer controlled oscilloscope. A sufficiently short window ensures accurate results. R EFERENCES [1] Michael J. Pont, Embedded C, Addison Wesley Publishing Company, 2002. [2] Thomas W. Schultz, C and the 8051, Pagefree Publishing, 2004. [3] Jeroen Hoet, Automatiseren van een meetopstelling voor het opmeten van het vermogenverbruik in beeldschermen, Ghent University, Master Thesis, 2006.
Inhoudsopgave 1 Inleiding
1
Systeembeschrijving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
1.1.1
Belangrijkste componenten . . . . . . . . . . . . . . . . . . . . . . . .
3
1.2
Doel van deze scriptie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.3
Structuur van het werk
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.4
Datamodel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.1
2 USB-communicatie
6
2.1
Van serieel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.2
. . .naar USB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
3 Software
10
3.1
Ontwerpomgeving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
3.2
Concept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
3.2.1
Details van de basisacties . . . . . . . . . . . . . . . . . . . . . . . . .
12
3.2.2
PC-software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
3.3
De wrapper FT232RDevice . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
3.4
De grafische front-end BDControl . . . . . . . . . . . . . . . . . . . . . . . . .
22
3.4.1
Klassen voor project- en beeldbeheer . . . . . . . . . . . . . . . . . . .
25
3.4.2
GUI-klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
3.4.3
Actieklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
34
3.4.4
Communicatieklassen . . . . . . . . . . . . . . . . . . . . . . . . . . .
39
3.4.5
Hulpklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
44
Microcontrollercode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46
3.5.1
Implementatie van de acties . . . . . . . . . . . . . . . . . . . . . . . .
50
3.5.2
Mini DS89C420 programmer . . . . . . . . . . . . . . . . . . . . . . .
54
3.5
iii
Inhoudsopgave
iv
4 Semi-automatische vermogenmeting
59
Opzet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
4.1.1
Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
4.1.2
Globale en lokale metingen . . . . . . . . . . . . . . . . . . . . . . . .
61
4.2
Uitbreiding van BDControl . . . . . . . . . . . . . . . . . . . . . . . . . . . .
62
4.3
Illustratie van de methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
65
4.4
Voorbeeld . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
65
4.1
5 Besluit
68
A Klassendiagram
69
B Inhoud van de cdrom
71
C Installatie-instructies
72
Bibliografie
73
Lijst van figuren
75
Lijst van tabellen
77
Hoofdstuk 1
Inleiding Binnen de groep TFCG worden geavanceerde driverchips ontwikkeld voor het aansturen van bistabiele displays. Deze beeldschermen behouden hun inhoud na het wegvallen van de voedingsspanning. Ze zijn zeer vermogeneffici¨ent en dus uitermate geschikt voor batterijgevoede draagbare toepassingen zoals bijv. PDA of e-book. Een nadeel is echter dat vrij hoge spanningen (tot 100 V) vereist zijn om de rijen en kolommen te adresseren. Er wordt dan ook nog steeds gezocht naar methodes om het vermogenverbruik verder te verlagen. Dit gebeurt zowel door het optimaliseren van de hardware als van de software. Wat betreft het hardwaregedeelte is er een voorlopig optimum bereikt. Aan de software zijde kan er echter nog heel wat geoptimaliseerd worden. Onder software verstaan we enerzijds het verzorgen van communicatie tussen de verschillende componenten als het uitvoeren van de nodige berekeningen als het aansturen van het display vanaf een PC. Voorlopig werd een testbordje gemaakt waarop alle aanstuurelektronica ge¨ıntegreerd is en dat eveneens de nodige componenten bevat om de communicatie met de buitenwereld te verzorgen. Hierop bevindt zich ook een FPGA en de bijhorende programmeerlogica waarmee de nieuwe software onmiddellijk kan getest worden.
1.1
Systeembeschrijving
Deze sectie beschrijft het oorspronkelijk systeem waarmee verder ontwikkeld werd. Figuur 1.1 toont een blokschema. We onderscheiden drie grote componenten: het bord met eigenlijke bistabiel display en wat perifere elektronica; een aanstuurbord dat de stuur- en communicatie-elektronica bevat; een PC die een front-end biedt voor de gebruiker.
1
Hoofdstuk 1. Inleiding
2
communicatie 6 aansturing
PC
aanstuurbord
bistabiel display
Figuur 1.1: Overzicht van het systeem.
drivers
Flash μC RS232
FPGA
Figuur 1.2: Structuur van het aanstuurbord.
Een meer gedetailleerd beeld van het aanstuurbord is te zien in figuur 1.2. De 4 drivers werden ontwikkeld bij TFCG. Zij generen complexe golfvormen om het display aan te sturen. Daarbij wordt gebruik gemaakt van 6 voedingsspanningen: 85 V, 60 V, 55 V, 45 V, 35 V en 5 V. Voor meer informatie wordt de lezer verwezen naar [9]. E´en of meerdere beelden (zie 1.4) kunnen worden opgeslagen in het flashgeheugen. De microcontroller verzorgt de communicatie met de PC en kan beelden doorsturen naar de FPGA die op zijn beurt de drivers aanstuurt. Op het bord is een startknop, resetknop en een 4-wegs jumper aanwezig. Afbeelden van een beeld gebeurt door een druk op de startknop. De jumper bepaalt het beeldnummer.
Hoofdstuk 1. Inleiding
3
De microcontroller heeft 2 ingebouwde seri¨ele poorten. De communicatie met de PC verloopt dus logischerwijs via het RS232-protocol. Assemblercode zorgt voor de communicatie met de PC, het schrijven en lezen van het flashgeheugen en het doorsturen van data naar de FPGA. De PC front-end is een eenvoudig opdrachtregelprogramma dat louter beelden stuurt naar de microcontroller.
1.1.1
Belangrijkste componenten
Deze componenten vormen het kloppend hart van de schakeling. Microcontroller De DS89C420 van Maxim/Dallas Semiconductors[13] is qua instructieset identiek met de alom bekende 80C51. De processorkern is echter opnieuw ontworpen waardoor instructies tot 12 keer sneller uitgevoerd worden. Dit leidt tot een typische snelheidswinst van 10 bij gelijke trilfrequentie van het kristal. De kristalfrequentie op het aanstuurbord is 32 MHz. De eigenschappen die hier van belang zijn, zijn de twee seri¨ele poorten, de drie 16-bit timers, het 16 KiB on-chip flashgeheugen en de energiebesparende modus (slaaptoestand). Verder is de chip in-system programmeerbaar en is hij relatief goedkoop. Flashgeheugen De Atmel AT29C2010A is een in-system programmeerbaar en wisbaar ROM (PEROM), dat gebruik maakt van flashtechnologie. Van de 128 KiB grote opslagruimte wordt slechts de onderste 64 KiB gebruikt omdat de adresbus van de microcontroller maar 16 bit breed is.1 De toegangstijd bij lezen (≤ 70 ns) is klein genoeg om dat zonder wachttijden te doen. Schrijven gebeurt sector-per-sector: 128 bytes worden in maximaal 10 ms geschreven, wat ook voldoende kort is. Er is geen hoge voedingsspanning vereist voor het schrijven: 5 V is voldoende. Het vermogenverbruik is typisch 275 mW als de chip geselecteerd (het chip select signaal is geasserteerd) is. Bij een inactieve chip bedraagt de standbystroom slechts 100 µA. Field-Programmable Gate Array (FPGA) Een FPGA is een programmeerbare chip. De FPGA op het aanstuurbord is de Altera Cyclone EP1C6[1]. Zijn functie bestaat erin uit configuratiedata en beeldpixels de correcte stuursig1 Er kan meer geheugen aangesproken worden door met banken te werken. 64 KiB is echter ruim voldoende voor deze toepassing.
Hoofdstuk 1. Inleiding
4
nalen te genereren voor de drivers. Hij wordt aangestuurd door een 4 MHz klok, gegenereerd door de kristaloscillator SG531.
1.2
Doel van deze scriptie
Het doel van dit werk situeert zich op drie vlakken: in eerst instantie wordt de seri¨ele interface tussen PC en microcontroller vervangen door een moderne USB-verbinding. Naast de veel hogere overdrachtssnelheid is USB ook veel gebruiksvriendelijker dan seri¨ele poorten (bv. kettingen van apparaten vormen). Dat sommige PC’s al niet meer uitgerust worden met een seri¨ele poort, is daar een indicatie van. Verder is het de bedoeling dat een nieuwe, gebruiksvriendelijke front-end wordt ontwikkeld. Die moet heel wat meer functionaliteit bieden dan het opdrachtregelprogramma: naast beelden doorsturen moet ook het opsporen van fouten in de microcontrollercode een stuk makkelijker worden. Ook het initi¨eren van gebruikersacties zoals het laten afbeelden van een beeld moet mogelijk zijn. Een grafische interface is uiteraard gewenst. Ten derde moet het mogelijk gemaakt worden een semi-automatische vermogenmeting te doen aan de voedingslijnen. Dit gebeurt met een digitale oscilloscoop die bestuurd wordt vanaf de PC. ‘Semi-automatisch’ wijst op het feit dat de gebruikers zelf een probe moet plaatsen op de gewenste voedingslijn.
1.3
Structuur van het werk
Deze tekst stelt oplossingen voor voor de hierboven beschreven opgaven. Eerst wordt in hoofdstuk 2 de communicatie via USB mogelijk gemaakt. Hoofdstuk 3 beschrijft de frontend op PC en de code in de microcontroller en hoofdstuk 4 geeft een oplossing voor de vermogenmeting weer. Er wordt afgerond met een besluit.
1.4
Datamodel
Voor een goed begrip van de tekst worden hier enkele termen eenduidig gedefinieerd. Een en ander wordt verduidelijkt in figuur 1.3 Beeld Een beeld bestaat uit 4 bytes aan configuratiegegevens die de FPGA gebruikt om de golfvormen te cre¨eren;
Hoofdstuk 1. Inleiding
5
beeld 15 0xF000
beeld 14 0xE000 0x0124
naam 0x0104
0x2000 beelddata beeld 1 0x1000
0x0004 configuratie
0x0000
beeld 0 0x0000 project
beeld
Figuur 1.3: Het datamodel: schema van project en beeld en alignatie op 4 KiB grenzen.
de eigenlijke visuele data: 256 bytes die een grijswaardebeeld voorstellen op een 16 x 16
raster; 32 bytes voor de naam van het beeld. Dit zijn 32 ASCII-karakters.
Project
Een verzameling van zestien beelden vormt een project.
In het 64 KiB flashgeheugen worden de beelden opgeslagen op 4 KiB grenzen. Beelden kunnen dus theoretisch vergroot worden tot 4 KiB . Later volgt daarover meer.
Hoofdstuk 2
USB-communicatie Dit hoofdstuk beschrijft het aanpassen van de aanstuurelektronica. De seri¨ele interface tussen de PC en het bord moet plaats maken voor een snelle USB-oplossing.
2.1
Van serieel. . .
Een deel van het schema van het aanstuurbord is te zien in de volgende figuur:
RxD TxD
Figuur 2.1: Circuitschema van de oorspronkelijke seri¨ele communicatie.
6
Hoofdstuk 2. USB-communicatie
7
De microcontroller communiceert via zijn UART1 met de PC. De RS232-specificatie voorziet in een aantal signalen (TxD, RxD, DTR, DSR, DCD, . . .) waarvan hier enkel de datasignalen TxD en RxD worden gebruikt. De andere signalen dienen voor hardware-handshake en zijn hier overbodig. De poortpinnen voor TxD en RxD zijn respectievelijk P3.1 en P3.0. De bekende MAX232 zorgt voor de spanningsomzetting tussen RS232- en CMOS-niveaus.
2.2
. . .naar USB
Omdat de microcontroller niet beschikt over een ingebouwde USB-stack, zal de eigenlijke communicatie serieel moeten blijven: er moet dus een USB/serieel conversie plaats vinden. De FT232R van Future Technology Devices (FTDI) is hiervoor een geschikte chip.
3. Block Diagram
Page 5
De FT232R-chip
3.1 Block Diagram (Simplified) VCC
3V3OUT
USBDP
USBDM
3.3 Volt LDO Regulator
USB Transceiver with Integrated Series Resistors and 1.5K Pull-up
VCCIO
FIFO TX Buffer 128 bytes
Serial Interface Engine ( SIE )
USB Protocol Engine
To USB Transceiver Cell
OCSI (optional)
UART Controller with Programmable Signal Inversion and High Drive
UART FIFO Controller
TXD RXD RTS# CTS# DTR# DSR# DCD# RI# CBUS0 CBUS1 CBUS2 CBUS3 CBUS4
Internal EEPROM
USB DPLL
OSCO (optional)
Baud Rate Generator
48MHz
3V3OUT
FIFO RX Buffer 256 bytes Internal 12MHz Oscillator
Clock Multiplier / Divider
48MHz
24 MHz 12 MHz 6 MHz
RESET#
RESET GENERATOR
To USB Transceiver Cell
TEST GND
Figure 1 - FT232R Block Diagram
Figuur 2.2: Blokschema van de FT232R.
3.2 Functional Block Descriptions
Deze interfacebouwsteen integreert een volledige USB-naar-serieel omzetter en vergt bijna 3.3V LDO Regulator - The 3.3V LDO Regulator generates the 3.3V reference voltage for driving the USB transceiver
cell output buffers. It requires an external decoupling capacitor attached to the 3V3OUT regulator output pin. It geen externe componenten. De seri¨ ele signalen zijntoalbevan CMOS-niveau door de ge¨ ıntegreerde also provides 3.3V power to the 1.5kΩ internal pull up resistor on USBDP. The main function of this block is to power 1 USB Transceiver and the Reset Generator Cells rather than to power external logic. However, external circuitry the Universal Asynchronous Receiver/Transmitter requiring a 3.3V nominal supply at a current of around than 50mA could also draw its power from the 3V3OUT pin, if required.
USB Transceiver - The USB Transceiver Cell provides the USB 1.1 / USB 2.0 full-speed physical interface to the USB cable. The output drivers provide 3.3V level slew rate control signalling, whilst a differential receiver and two single ended receivers provide USB data in, SEO and USB Reset condition detection. This Cell also incorporates internal USB series resistors on the USB data lines, and a 1.5kΩ pull up resistor on USBDP. USB DPLL - The USB DPLL cell locks on to the incoming NRZI USB data and provides separate recovered clock and data signals to the SIE block.
Hoofdstuk 2. USB-communicatie
8
spanningsomzetter. De interne klok kan naar buiten gebracht worden. Verder zijn een EEPROM voor configuratiegevens, klokcircuit en USB-weerstanden ge¨ıntegreerd op de chip. De klokuitgang kan gebruikt worden om een microprocessor of externe logica te synchroniseren. De weerstanden, gedefinieerd in de USB-specificatie ([8]), zijn seri¨ele of pull-up weerstanden op de USB-datalijnen. Verder zijn drivers voor de gangbare PC-platformen gratis beschikbaar. Figuur 2.2 toont een blokschema waarop de genoemde eigenschappen te zien zijn. Voor meer informatie wordt de lezer verwezen naar [7]. Opzet Het ontwerpen en maken van een nieuw PCB kost veel tijd en geld. Daarom is voor een modulaire opzet gekozen: de vereiste elektronica wordt opgebouwd op een kleine printplaat die in het voetje van de MAX232 geplugd wordt. Naast de voedingsspanning en massa moeten enkel de TxD- en RxD-signalen doorverbonden worden. Figuur 2.3 toont het complete schema van de module.
Figuur 2.3: Het schema van de USB-module.
De chip is zo geconfigureerd dat LED’s D1 en D2 oplichten als er data verstuurd of ontvangen wordt. C1 en C2 ontkoppelen de USB-bus van stoorpulsen en voorkomen zo beschadiging van de USB-host. C3 sluit stoorpulsen op de referentiespanning (3V3OUT) kort naar massa. Als
Hoofdstuk 2. USB-communicatie
9
de schakeling op de PC wordt aangesloten, zal het signaal RESET hoog worden en wordt de chip ge¨ınitialiseerd. Even later is het systeem klaar voor communicatie. De voeding wordt volledig betrokken uit het bord (dit noemt men een self-powered USB-device). De host moet dus geen vermogen leveren. Op figuur 2.4 is een foto van het opgebouwde prototype te zien. De FT232R wordt enkel geleverd in een SSOP of QFN behuizing; daarom werd de chip eerst op een adapterprintje naar DIL gesoldeerd.
Figuur 2.4: Een foto van het opgebouwde prototype.
Hoofdstuk 3
Software Dit hoofdstuk beschrijft de software aan beide uiteinden van de USB-kabel: de front-end BDControl wordt voorgesteld en de nieuwe code voor de microcontroller beschreven.
3.1
Ontwerpomgeving
Pc-programma
De ontwikkeling van het programma gebeurde onder Windows XP. De
driver van de USB-chip wordt geleverd in de vorm van een Win32 DLL. Een voor de hand liggende taalkeuze is daardoor C++. De taal C# werd echter gekozen als ontwikkeltaal voor de PC-frontend. Dit heeft een aantal redenen: .NET framework : er kan gebruik gemaakt worden van het .NET framework van Mi-
crosoft. Deze bibliotheken kapselen (een groot deel van) de Win32 API in onder een object-ge¨orienteerde vorm. Verder zijn er een groot aantal nuttige klassen aanwezig die het programmeren sterk versnellen.1 Automatisch geheugenbeheer : de garbage collector laat toe om zich te focussen op de
algoritmes en niet op het geheugenbeheer. Excepties: fouten worden in de wereld van de Win32 API kenbaar gemaakt door
HRESULTs, boolese teruggeefcodes, integers, . . . Dit maakt het programmeren heel omslachtig. Het .NET framework gebruikt het C#-exceptiemechanisme voor een uniform model voor foutenrapportage. RAD: door de opgesomde (en andere) voordelen leent C# zich sterk tot Rapid Appli-
cation Development. 1 Zogenaamd managed C++ kan wel deze bibliotheken aanspreken. Dit weegt echter niet op tegen de overige voordelen van C#.
10
Hoofdstuk 3. Software
11
De ontwikkelomgeving is Visual Studio 2005, een IDE waarvan een gratis Express Edition beschikbaar is. Dit softwarepakket biedt een ge¨ıntegreerde omgeving: code schrijven, compileren, linken, debuggen en grafische interfaces ontwerpen via drag-and-drop kan zonder een externe tool. De nieuwe refactoringmogelijkheden in de laatste versie bieden een snelle manier om code structureel te veranderen. Microcontrollercode
De microcontroller kan geprogrammeerd worden in C of assembler.
Er werd geopteerd voor C. Dit laat toe op een overzichtelijke wijze code te schrijven, zonder aan snelheid in te boeten. De firma Keil biedt de C51 optimaliserende compiler en bijhorende IDE µVision gratis ter evaluatie aan[2]. Deze evaluatieversie heeft enkele beperkingen, maar die zijn niet van belang voor dit project. Heel handig is de mogelijkheid tot uitvoeren van de code in een simulator. De ontwikkeltijd verkort hierdoor aanzienlijk. Voor het programmeren van de microcontroller is de Microcontroller Toolkit[6] van Dallas gebruikt.
3.2
Concept
De grafische front-end dient in een aantal functionaliteiten te voorzien: het is een afstandsbediening waarmee het display bestuurd wordt. Daarnaast biedt het een makkelijke interface voor de gebruiker: beelden en projecten kunnen op schijf opgeslagen worden en doorgestuurd worden naar het display. Een grafische voorstelling van de beelden is te zien en configuratiegegevens kunnen onmiddelijk aangepast worden. In wat nu volgt wordt een hoogniveauvoorstelling gegeven van de interactie tussen PC en microcontroller. De PC stuurt (na input van de gebruiker) de microcontroller aan via zogenaamde ‘acties’. Mogelijke acties zijn: opslaan van beelden of projecten in het flashgeheugen; ophalen van beelden of projecten uit het flashgeheugen; laten afbeelden van een beeld.
Deze acties zijn niet onafhankelijk: het opslaan of ophalen van een project komt neer op 16 keer een beeld opslaan of ophalen. We kunnen bijgevolg 3 basisacties bepalen: opslaan, ophalen en laten afbeelden van een beeld. De onderlinge afhankelijkheid van bovenstaande acties zal later handig zijn bij de implementatie in het controleprogramma.
Hoofdstuk 3. Software
12
Bij elke basisactie verloopt de communicatie tussen PC en microcontroller volgens een vast protocol. Figuur 3.1 toont een sequentiediagram. Eerst wordt een eenvoudige handshake uitgevoerd. De communicatie verloopt als volgt: 1. De PC stuurt een ASCII-karakter. Dit karakter bepaalt welke actie uitgevoerd wordt en is tevens het eerste bericht van de handshake. 2. De microcontroller antwoordt met een ‘A’. De ack geeft aan dat de controller klaar is om de actie uit te voeren en vervolledigt de handshake. 3. Afhankelijk van het soort actie worden nu een aantal parameters doorgestuurd en wordt de actie afgewerkt. Telkens zal de microcontroller een ack terugsturen. Als de PC na 5 seconden geen antwoord ontvangt van de microcontroller, treedt een time out op en wordt de actie afgebroken. In de volgende tabel zijn de basisacties, hun ASCII-karakter en hun bijhorende parameters samengevat. Actie
ASCII-karakter
Parameters
Opslaan van een beeld
S
index (1 byte), datalengte (2 bytes)
Ophalen van een beeld
L
index (1 byte)
Afbeelden van een beeld
D
index (1 byte)
Deze protocolstructuur leent zich uitstekend om ge¨ımplementeerd te worden als communicerende toestandsmachines. Verder is het duidelijk dat op eenvoudige wijze acties kunnen toegevoegd worden.
3.2.1
Details van de basisacties
Elke basisactie wordt hier uitvoerig beschreven en de toestandsmachines worden uitgewerkt. Een beeld opslaan in het flash Figuur 3.2 toont het sequentiediagram voor de communicatie. Een Store start de actie. Parameters zijn het beeldnummer (0 → 15) en de lengte van het beeld. Van dit 16 bits getal wordt de laagste byte eerst verstuurd. Na bevestiging verzendt de PC de beelddata in blokken van 128 bytes. De reden is dat het flashgeheugen beschreven moet worden in sectoren van 128 bytes. Het laatste blok kan echter minder data bevatten. Dit wordt dan door de microcontroller aangevuld met nullen tot een volledig blok alvorens het weggeschreven wordt.
Hoofdstuk 3. Software
13
PC
µC
handshake
‘A’
actiespecifiek
parameters
Figuur 3.1: Sequentiediagram van de communicatie bij uitvoering van een basisactie.
De afgeleide toestandsmachines zijn te zien in figuur 3.3. De transities aan de PC-kant gebeuren na ontvangst van een A, de time out transities zijn niet te zien. Aan de zijde van microcontroller zijn de overgangen impliciet, i.e. na voltooiing van een bewerking. De eerste toestand START STORE UART is geen toestand van de weergegeven FSM, maar van een FSM in de hoofdlus van de microcontrollercode. Vanuit deze hoofd-FSM worden de andere opgeroepen. Meer uitleg volgt daarover in 3.5. Voor de eigenlijke implementatie van deze toestandsmachines wordt de lezer verwezen naar de delen over PC- en microcontrollercode. Een beeld ophalen uit het flash Het ophalen van een beeld is analoog (figuur 3.4). Als parameters wordt het beeldnummer en de lengte meegegeven. Omdat het volledige flashgeheugen sequentieel gelezen kan worden, stuurt de microcontroller alle beelddata onmiddellijk naar de PC. De tweede A zorgt er voor dat de PC in de juiste toestand komt. De toestandsmachines (figuur 3.5) zijn analoog aan het vorige geval en behoeven weinig verdere uitleg. Dezelfde opmerkingen blijven evenwel geldig. Een beeld afbeelden op het display Dit is de eenvoudigste basisactie. Het beeldnummer is de enige parameter. Sequentiediagram en toestandsmachines zijn te zien in figuur 3.6 en 3.7.
Hoofdstuk 3. Software
14 PC
µC
‘S’ ‘A’ , , ‘A’ <128 bytes blok> ‘A’ < ≤128 bytes blok> ‘A’
Figuur 3.2: Sequentiediagram van de communicatie bij opslaan.
3.2.2
PC-software
De PC-software bestaat grosso modo uit 2 componenten: de object-ge¨ ori¨entieerde wrapper-DLL FT232RDevice die de functionaliteit van de FT232R
driver inkapselt; de grafische front-end BDControl, die de acties implementeert en een gebruiksvriende-
lijke interface voor de gebruiker aanbiedt. De volgende secties beschrijven deze componenten. Verder uitleg is ook beschikbaar in de commentaar bij de code.
Hoofdstuk 3. Software
15
PC
microcontroller
INIT_STORE
SEND_CONFIG
START_STORE_UART
AWAIT_CONFIG
SEND_LAST_BLOCK
SEND_BLOCK
RECEIVE_LAST_BLOCK
RECEIVE_BLOCK
FINISH
Figuur 3.3: Toestandsmachines voor de uitvoering van een ‘opslaan’ actie.
PC
µC
‘L’ ‘A’ , , ‘A’
Figuur 3.4: Sequentiediagram van de communicatie bij ophalen van een beeld.
Hoofdstuk 3. Software
16
PC
microcontroller
INIT_LOAD
SEND_CONFIG
FINISH
GET_DATA
START_LOAD_UART
AWAIT_CONFIG
SEND
Figuur 3.5: Toestandsmachines voor de uitvoering van een ‘laad’ actie.
PC
µC
‘D’ ‘A’ ‘A’
Figuur 3.6: Sequentiediagram van de communicatie bij afbeelden van een beeld.
Hoofdstuk 3. Software
17
PC INIT_DISPLAY
microcontroller DISPLAY
FINISH
START_DISPLAY
AWAIT_CONFIG
DISPLAY
Figuur 3.7: Toestandsmachines voor de uitvoering van een ‘afbeeld’ actie.
Hoofdstuk 3. Software
3.3
18
De wrapper FT232RDevice
Deze C#-DLL kapselt de functies in die ge¨exporteerd worden door FTD2XX.DLL, de driver van de USB-chip onder Windows. Die driver is een typische Win32 driver en biedt geen objectge¨ori¨entieerde interface aan. Zoals gebruikelijk bij het programmeren onder het Win32-model, worden objecten (in een niet object-ge¨ori¨entieerde zin, zoals bestanden, vensters, mutexen) aangeduid met een handle die bij elke functieaanroep moet worden meegegeven. De klasse FT232RDevice kapselt zo een handle in. Een instantie van die klasse stelt dan de convertorchip FT232R voor, waarop methoden kunnen worden opgeroepen. Deze component is een pure C# klasse. Een gedeeltelijk klassendiagram is te zien in figuur 3.8. De ingekapselde handle is een private field van de klasse. IDisposable FT232RDevice Sealed Class
Fields ev handle status Methods CloseDevice ClrDtr CyclePort Dispose GetDeviceDescriptors GetEventHandle GetNumberOfDevices GetQueueStatus OpenDevice (+ 1 overload) Purge ReadData ResetPort SetBaudRate SetChars SetDataCharacteristics SetDivisor SetDtr SetEventNotification WriteData
NativeMethods Static Class
Methods FT_Close FT_ClrDtr FT_CreateDeviceInfoList FT_CyclePort FT_GetDeviceInfoList FT_GetQueueStatus FT_ListDevices (+ 1 overload) FT_Open FT_Purge FT_Read FT_ResetPort FT_SetBaudRate FT_SetChars FT_SetDataCharacteristics FT_SetDivisor FT_SetDtr FT_SetEventNotification FT_Write
Nested Types
Figuur 3.8: Vereenvoudigd klassendiagram van de wrapper en de geneste klasse NativeMethods.
FT232RDevice bevat een statische klasse NativeMethods die de DLL-functies van de driver importeert. Het is daarbij belangrijk dat hun argumenten correct worden gemarshald zodat
Hoofdstuk 3. Software
19
hun (Win32) types correct worden afgebeeld op C# types2 . Verder kent C# geen pointers3 , die worden echter heel courant gebruikt bij Win32 programmeren. .NET voorziet dan ook in een speciale namespace die interop services aanbiedt: System.Interop. Voorbeeld Een concreet voorbeeld van een ge¨ımporteerde functie: een aantal bytes lezen uit de ontvangstbuffer van de chip. De declaratie van de functie FT Read in het C headerbestand van de driver is: FT_STATUS WINAPI FT_Read( FT_HANDLE ftHandle, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpdwBytesReturned );
De parameters: ftHandle is een pointer naar de handle van een open device, lpBuffer een pointer naar een gealloceerde buffer waarin de data wordt geplaatst, nBufferSize de buffergrootte en pBytesReturned is een pointer naar een integer waarin het aantal effectief gelezen bytes teruggegeven wordt. Er worden dus 3 pointers en 1 integer meegegeven. De functie retourneert een FT STATUS, dit is een enumeratie met statuscodes. De corresponderende importdeclaratie in NativeMethods ziet er als volgt uit: [DllImport("FTD2XX.dll", EntryPoint = "FT_Read")] internal static extern uint FT_Read( IntPtr ftHandle, byte[] lpBuffer, uint nBufferSize, out uint lpdwBytesReturned );
De DLL die de functie exporteert en het entrypoint worden bepaald door het attribuut DllImport, dat een aanroep in unmanaged code door de interop services aanduidt. Het sleutelwoord extern geeft aan dat de methode elders ge¨ımplementeerd wordt (in casu in de driver). De complexiteit van het marshallen wordt in dit voorbeeld al duidelijk: de 3 pointers worden op 3 verschillende manieren meegegeven. Ten eerste wordt de structuur (struct) IntPtr voor de handle gebruikt. Deze is speciaal ontworpen om te gebruiken tussen talen 2 3
of correcter: .NET CLR types pointers zijn wel mogelijk in een zogenaamde unsafe context
Hoofdstuk 3. Software
20
die wel en geen pointers ondersteunen. De buffer is ge¨ımplementeerd als een array, waarvan de naam eigenlijk een pointer is naar het eerste element. Het argument lpdwBytesReturned is een outputwaarde die door de aangeroepen functie wordt ingevuld; daarvoor is in C# een speciaal sleutelwoord out gedefinieerd. Dit heeft als effect dat een parameter by reference wordt doorgegeven, gelijkaardig aan een pointer. De integer nBufferSize kan als een gewone uint gemarshald worden. De klasse FT232RDevice moet nu enkel nog een methode aanbieden die deze import oproept. Dat is in dit geval de volgende methode: public byte[] ReadData(int numBytesToRead) { uint bytesRead; byte[] buffer = new byte[numBytesToRead];
status = (FTStatus)NativeMethods.FT_Read(handle, buffer, (uint)numBytesToRead, out bytesRead ); if (status != FTStatus.FT_OK) throw new IOException(status.ToString());
if (bytesRead != (uint)numBytesToRead) throw new IOException("Unable to read all data from buffer");
return buffer; }
De functie zal proberen numBytesToRead bytes te lezen uit de chip en geeft deze terug als een byte-array. Nadat deze buffer gealloceerd is, wordt de ge¨ımporteerde functie opgeroepen via NativeMethods. Als de oproep geslaagd is en er effectief numBytesToRead bytes gelezen zijn, retourneert de functie de data. Bij fouten wordt een exceptie gegenereerd. Elke functie die door de driver FTD2XX.DLL ge¨exporteerd wordt, is op een analoge manier ingekapseld.
Hoofdstuk 3. Software
21
De statische functie GetDeviceDescriptors() geeft een lijst van DeviceDescriptors terug. Een descriptor bevat de beschrijving, het serienummer en het uniek device nummer (bepaald door Windows) van een chip. De functie OpenDevice opent een FT232RDevice door de gelijknamige functie in de native driver op te roepen en de handle als IntPtr te bewaren. Time out Zoals vermeld in 3.2 treedt er na 5 seconden een time out op als de PC geen antwoord krijgt van de microcontroller. Om dit te implementeren is gebruik gemaakt van het threadsynchronisatieprimitief semafoor of in Microsoft-jargon event. Dit is niet te verwarren met het gelijknamige gebeurtenismodel in .NET. Uit de documentatie[5]: Event wait handles are not events in the sense usually meant by that word in the .NET Framework. There are no delegates or event handlers involved. The word ‘event’ is used to describe them because they have traditionally been referred to as operating-system events, and because the act of signaling the wait handle indicates to waiting threads that an event has occurred. Besturingssysteemevents worden in .NET ingekapseld in de EventWaitHandle klasse. In een typisch scenario blokkeren een of meerdere threads op een EventWaitHandle totdat een andere thread de Set methode aanroept. Daarop zal ´e´en van de wachtende threads zijn uitvoering verderzetten4 . De acties worden asynchroon uitgevoerd door middel van een BackgroundWorker (zie 3.4.4) object. Deze klasse maakt het mogelijk om eenvoudig multithreaded programma’s te maken. Naast deze workerthread, wordt de driver zelf ook op een thread uitgevoerd. Er kan een event aangemaakt worden dat gezet wordt wanneer er data ontvangen wordt. De workerthread zal blokkeren op dit event totdat de driverthread het event zet (signaled), of tot er een bepaalde tijd verlopen is. De klasse FT232RDevice bevat een EventWaitHandle object (zie figuur 3.8). De constructor bevat de volgende code: ev = new EventWaitHandle(false, EventResetMode.AutoReset, "FT232RXEvent");
Er wordt dus een EventWaitHandle object ge¨ınstantieerd, dat niet gezet is, automatisch wordt gezet na vrijgave van een thread, en dat de naam FT232RXEvent draagt. Dit object bevat een property van het type SafeWaitHandle dat een handle is naar het onderliggende besturingssysteemevent. SafeWaitHandle is dus effectief een wrapper voor Win32 events. Andere code 4 Dit beschrijft automatic reset events. Voor meer informatie wordt de lezer verwezen naar elk goed .NET boek.
Hoofdstuk 3. Software
22
kan dit object opvragen met de methode GetEventHandle. Om de driver het aangemaakte event de laten gebruiken wordt de driverfunctie FT SetEventNotification opgeroepen: status = (FTStatus)NativeMethods.FT_SetEventNotification( handle, FTEventMask.FT_EVENT_RXCHAR, ev.SafeWaitHandle );
De driver zal het event dus zetten als er data ontvangen wordt. De time out kan eenvoudig gespecifieerd worden bij het blokkeren van een thread. Stel dat ev het event voorstelt, dan zal de thread die de volgende functie uitvoert blokkeren tot er een karakter ontvangen is of tot er 5 seconden verstreken zijn: ev.WaitOne(5000, false)
Daarbij is de false enkel van belang in een synchronisatiecontext, die hier niet gebruikt wordt. De rest van de methodes komen rechtstreeks overeen met de ge¨ımporteerde functies. Zij doen uiteraard sterk denken aan functies om een echte seri¨ele poort aan te spreken.
3.4
De grafische front-end BDControl
BDControl is een grafisch programma dat de gebruiker een interface aanbiedt voor het besturen van een bistabiel display. Het implementeert de acties vermeld in 3.2 en laat toe om georganiseerd om te gaan met beelden en projecten: beelden en projecten kunnen opgeslagen worden op schijf; beelden kunnen ge¨ımporteerd worden uit een standaard ASCII-bestand; random grijswaardebeelden worden automatisch gegenereerd.
De acties worden asynchroon, op een aparte thread, uitgevoerd om de gebruikersinterface niet te verstoren. Verder biedt het programma enkele handige features: een logboek, opslaan van gebruikersinstellingen (positie van de deelvensters, standaardbeeldconfiguratie, ...), ... Figuur 3.9 toont een screenshot van de applicatie. In de gebruikersinterface onderscheiden we naast het menu en de werkbalken meteen al een aantal vensters: de navigator links toont
Hoofdstuk 3. Software
23
Figuur 3.9: Screenshot van BDControl.
het huidige project, met daarin de 16 (al dan niet lege) beelden. Onderaan is het logboek te zien in het outputvenster. Het middelste venster toont het huidig geselecteerde beeld en zijn configuratieparameters. Rechts is de uitbreiding voor vermogenmeting te zien. Alle mogelijk opdrachten zijn zowel als knop op de werkbalken, als ingang in het menu of als toetsenbordsnelkoppeling aanwezig. Uitbreidbaarheid Het programma is zo geschreven dat het eenvoudig uitbreidbaar is. Het object-ge¨ori¨enteerde C# biedt daarvoor een krachtige basis, maar een intelligente opdeling van de functionaliteit is minstens even belangrijk. Acties: in 3.4.3 is te lezen dat de acties als een klassenhi¨erarchie zijn ge¨ımplementeerd:
alle acties zijn afgeleid uit een abstracte basisklasse. Deze structuur volgt volledig de
Hoofdstuk 3. Software
24
afhankelijksrelaties tussen de acties, maar laat ook toe om eenvoudig acties toe te voegen zonder ingrijpende veranderingen. Fysisch communicatiekanaal : de communicatielogica is gelaagd. Een Communicator
verwerkt abstracte hoogniveauopdrachten en gebruikt voor de eigenlijke communicatie een klasse die specifiek gericht is op een bepaald communicatiekanaal. In ons geval is dit FT232RDevice. Stel dat men wil communiceren over een ander medium (ethernet, draadloos, bluetooth, . . . ), dan hoeft enkel die specifieke klasse vervangen te worden. Opnieuw zijn er dan geen grote veranderingen. Het is zelfs mogelijk dit dynamisch te doen zodat de gebruiker zelf een keuze kan maken. Semi-automatische vermogenmeting: in het volgende hoofdstuk staat beschreven hoe
het programma aangepast werd om vermogenmeting mogelijk te maken. Deze componenten worden in dit hoofdstuk dus niet meer vermeld. Interne Structuur Zoals bij elk niet-triviaal softwareproject geldt het verdeel-en-heers principe. De functionaliteit wordt verdeeld over een aantal delen, op het laagste niveau klassen, die interageren. De opsplitsing van de PC-software in de wrapper FT232RDevice en het programma BDControl is daar een eerste toepassing van. Een verdere iteratieve opsplitsing van BDControl levert de volgende blokken op (verzamelingen van klassen): klassen voor project- en beeldbeheer; de GUI-klassen die grafische interface voorstellen, input van de gebruiker krijgen en
uitvoer weergeven; de klassen die de communicatie met de chip verzorgen; de klassen die de acties implementeren; een aantal hulpklassen.
Deze logische structuur wordt in grote lijnen gereflecteerd in de gebruikte namespaces: BD.GUI, BD.Actions, BD.Util en BD.Components. Die laatste namespace bevat de communicatie-, project- en beeldklassen. In wat volgt worden deze blokken en hun inderlinge interactie besproken. Enkel de belangrijkste velden en methodes worden vermeld. Standaardmethodes zoals een ToString() overload zijn triviaal en worden achterwege gelaten, net zoals de meeste constructors. Het volledige klassendiagram, maar zonder de velden en methoden is te vinden in appendix A en toont een
Hoofdstuk 3. Software
25
overzicht van de applicatie. Dit biedt een aanknopingpunt voor een beter begrip van de tekst. Opmerking
Hoewel we beelden definieerden met een vaste resolutie van 16 × 16 pixels, zijn
de sleutelroutines — op enkele uitzonderingen na — grootte-onafhankelijk gemaakt. Dit laat toe om met een minimum aan aanpassingen een ander display te gebruiken. Ook zijn alle velden privaat gedefinieerd. Via een gelijknamige property kunnen ze gecontroleerd benaderd worden.
3.4.1
Klassen voor project- en beeldbeheer
Dit blok bevat de klassen die een beeld en project defini¨eren en een hulpklasse. Het klassendiagram is te zien in figuur 3.10. BD.Components.BDImageProperties: deze klasse bevat de configuratiedata, naam en
dimensies van het beeld. BD.Components.BDImage: deze klasse stelt een beeld zoals gedefinieerd in 1.4 voor. De
property RawData is een byte matrix die de eigenlijke beelddata voorstelt. ImageProperties bevat de eigenschapppen van het beeld. BitmapImage is een getter die het beeld converteert naar een GDI+ Bitmap. Deze bitmap kan gemakkelijk afgebeeld worden in een venster. De methode GetByteStream genereert de bytestream die in het flashgeheugen wordt opgeslagen uit de beelddata en de configuratiegegevens. Verder is een overload van de aftrekoperator gedefinieerd die twee instanties van BDImage vergelijkt en het verschilbeeld teruggeeft. Dit is handig om te controleren of een beeld correct weggeschreven werd in het flashgeheugen. De standaardconstructor zal een beeld aanmaken van 16 × 16 pixels, maar er is ook een overload aanwezig die arbitraire dimensies accepteert. De statisch methode ImportFromTextFile() maakt een BDImage-object aan door een ASCII-bestand te interpreteren als beeldbestand (zie verder). Hiermee kunnen dus gemakkelijk testbeelden aanmaakt worden. BD.Components.BDProject: een project bestaat uit 16 beelden, die hier in een array
zijn opgeslagen. De properties Path en Name spreken voor zich. BD.Components.BDProjectManager: deze klasse neemt het beheer van projecten en
beelden op zich. Er zijn methoden aanwezig om beelden en project op te slaan of op te halen uit het flash (StoreImage(), StoreProject(), LoadImage() en LoadProject)
Hoofdstuk 3. Software
26
BDImageProperties Class
Fields CRDuringLineScan : bool CRDuringReset : bool Dimensions : Size LinetimeHigh : byte LinetimeLow : byte Name : string Methods BDImageProperties() ToString() : string
BDImage Class
Fields props : BDImageProperties rawData : byte[][] Properties BitmapImage { get; } : Bitmap ImageProperties { get; set; } : BDImageProperties RawData { get; set; } : byte[][] Methods BDImage() BDImage(byte[][] rawGrayscaleData, BDImageProperties props) BDImage(int width, int height) GetByteStream() : byte[] ImportFromTextFile(string filename) : BDImage operator -(BDImage i1, BDImage i2) : BDImage ToString() : string
(a)
Figuur 3.10: De klassen voor project- en beeldbeheer.
Hoofdstuk 3. Software
27
BDProject Sealed Class
Fields bdImages : BDImage[] name : string path : string Properties BdImages { get; set; } : BDImage[] FileName { get; set; } : string Name { get; set; } : string Methods BDProject(string name) BDProjectManager Class
Fields comm : Communicator displayAction : DisplayAction index : int ivc : BDImageViewController loadAction : LoadAction loadProjectAction : LoadProjectAction storeAction : StoreAction storeProjectAction : StoreProjectAction Methods BDProjectManager(Communicator c, BDImageViewController ivc) comm_ActionCompleted(object sender, ActionCompletedEventArgs e) : void DisplayImage(int index) : void LoadImage(int index) : void LoadProject(out BDProject project) : void OpenProject(string path) : BDProject SaveProject(BDProject project) : void StoreImage(BDImage image, int index) : void StoreProject(BDProject project) : void
(b)
Figuur 3.10: De klassen voor project- en beeldbeheer (vervolg).
Hoofdstuk 3. Software
28
en om een beeld af te beelden op het display (DisplayImage()). Al deze methoden roepen hun corresponderende acties op die het eigenlijke werk uitvoeren. Een project kan geopend of opgeslagen worden met OpenProject() en SaveProject(). Dat wordt (de)serializeren genoemd. Formeel betekent dit het converteren van een object in een bytestroom die persistent kan opgeslagen worden (en omgekeerd). Die bytestroom bevat naast de eigenlijke data ook metadata over het type van het object. De klassen BDImage, BDImageProperties en BDProject zijn geannoteerd met het attribuut5 Serializable. Op die manier kan het standaard serializatiemechanisme van C# gebruikt worden: een formatter zal een serializeerbaar object in een stroom met een bepaald formaat converteren en omgekeerd. Een formatter moet de interface IFormatter implementeren. Het formaat kan bijvoorbeeld een XML-bestand of een binair bestand zijn. Een voorbeeld maakt dit duidelijk: public BDProject OpenProject(string path) { BDProject bdProject = null; Stream sw = File.OpenRead(path); IFormatter formatter = new BinaryFormatter(); bdProject = (BDProject)formatter.Deserialize(sw); sw.Close(); return bdProject; }
Het opgegeven binair bestand wordt eerst als een stroom geopend. Dan zal de BinaryFormatter het bestand deserializeren en een object van het type BDProject aanmaken en teruggeven, waarna de stroom wordt gesloten. De code voor SaveProject() is even eenvoudig. Formaat ASCII-bestand Om een tekstbestand op een correcte manier te interpreteren als beeldbestand moeten dit een specifieke vorm hebben. Grijswaardepixels worden voorgesteld door de hexadecimale getallen 00 tot FF. Een 16×16 beeld heeft dus 16 rijen en 32 kolommen. Het niet mogelijk om configuratiedata te defini¨eren.
3.4.2
GUI-klassen
Deze klassen bepalen het uiterlijk van de applicatie. Ze bestaan meestal uit twee bestanden: Klasse.cs en Klasse.Designer.cs. Hierbij wordt gebruik gemaakt van de nieuwe parti¨ele 5
Een attribuut associeert bepaalde informatie met een element (klasse, enum, delegate,. . . )
Hoofdstuk 3. Software
29
klassen in C# 2: de definitie en implementatie van een partial class kan verspreid worden over meerdere bestanden. De Visual Studio 2005 IDE gebruikt dit idee om de automatisch gegenereerde code van de Designer – waarbij vensters via drag and drop visueel opgebouwd worden – te verbergen voor de programmeur. Een verdere toepassing van het verdeel-en-heers principe leidt ook tot een opdeling van de UI. Het hoofdvenster bevat een tweetal componenten die de interface vormen naar een beeld of project. Daarvoor werden klassen ontworpen die afgeleid zijn van System.Windows.Forms.UserControl, in essentie een container voor andere controls. Een van UserControl afgeleide control kan visueel opgebouwd worden in Visual Studio en erft een aantal standaardproperties en -events over zoals BackGroundColor of OnDoubleClick(). BD.GUI.MainForm: het hoofdvenster van BDControl wordt voorgesteld door MainForm.
Deze klasse bevat geen business logic. Haar enige taak is het ontvangen van gebruikersinvoer en het weergeven van resultaten. Alle functionaliteit wordt door andere klassen ge¨ımplementeerd. De meeste methodes zijn rechtoe-rechtaan event handlers die weinig uitleg vergen. Figuur 3.11 toont een sterk vereenvoudigd klassediagram. Daarin zijn enkel de belangrijke velden te zien die door de glue logic met elkaar in interactie staan. MainForm Class Form
Fields bdImageViewController : BDImageViewController bdNavigator : BDNavigator bdProject : BDProject bdProjectManager : BDProjectManager comm : Communicator serialPort : SerialPort WordwrapButton : ToolStripButton
Figuur 3.11: De sterk vereenvoudigde MainForm-klasse.
BD.GUI.BDNavigator (figuur 3.12): deze component biedt een visuele interface voor een
BDProject. Het bevat daarvoor een TreeView, een aloud Windows control dat data in een boom weergeeft. Een ingang in de boom wordt een knoop genoemd. De properties SelectedImage en SelectedImageIndex geven respectievelijk het huidig geselecteerde beeld en beeldnummer terug. De private methode CreateTree() bouwt de boom.
Hoofdstuk 3. Software
30
UpdateDisplay() geeft de knopen een bepaalde kleur naargelang het beeld leeg is of niet. Om het hoofdvenster te laten weten dat de gebruiker een knoop geselecteerd heeft, stelt de navigator een event (nu w´el in de .NET betekenis, cfr. 3.3) beschikbaar: public event EventHandler ImageSelected;
In het .NET event model verbindt een ‘event delegate’ een event met de methode die erop reageert. Om een event op te roepen, zijn twee elementen noodzakelijk: 1. Een klasse die event-specifieke data (toestand) bevat. Die is afgeleid van EventArgs. 2. Een delegate die naar een methode (de event handler) wijst die reageert op het event. Dit is hier EventHandler. BDNavigator Class UserControl
Fields bdProject : BDProject parent : TreeNode tree : TreeView Properties BdProject { get; set; } : BDProject SelectedImage { get; } : BDImage SelectedImageIndex { get; } : int Methods BDNavigator() CreateTree() : void RefreshTree() : void UpdateDisplay() : void Events ImageSelected : EventHandler
Figuur 3.12: De BDNavigator-component en een screenshot.
In dit geval is de toestand het nieuwe geselecteerde beeld: public class ImageSelectedEventArgs : EventArgs { public BDImage SelectedBdImage; public ImageSelectedEventArgs(BDImage image) { SelectedBdImage = image; } }
De event handler moet dezelfde parameters hebben als de delegate. De klasse EventHandler
Hoofdstuk 3. Software
31
is als volgt gedefinieerd, waarbij het generieke6 type argument het type is van de data gegenereerd door het event: public delegate void EventHandler ( Object sender, TEventArgs e ) where TEventArgs : EventArgs
De where-clausule zegt dat TEventArgs minstens van het type EventArgs moet zijn. Afgeleide klassen voldoen uiteraard aan deze eis. Uit dit alles volgt dat de event handler die reageert op dit event, als volgt gedefinieerd is in MainForm: void bdNavigator_ImageSelected(object sender, ImageSelectedEventArgs e) { /* knip */ }
Deze handler wordt aan het event gekoppeld door: bdNavigator.ImageSelected += new EventHandler(bdNavigator_ImageSelected);
BD.GUI.BDImageViewController: voor het visualiseren van een beeld is ook een apar-
te component ontwikkeld. Daarbij is een vereenvoudigde versie van het Model-ViewController (MVC) patroon gebruikt. Dat is een architecturaal softwarepatroon waarbij data (het model) en gebruikersinterface (view) gescheiden worden. De interactie tussen de twee wordt dan door een controller geregeld. Omdat deze applicatie niet extreem complex is, zijn de view en controller samengenomen in de component BDImageViewController. Het datamodel is dan BDImage. Figuur 3.13 toont de klasse, figuur 3.14 een afbeelding. Opnieuw zijn de triviale velden en methodes achterwege gelaten. Er zijn twee properties van het type BDImage: LocalBDImage is het beeld dat op de PC wordt getoond, los van het bistabiel display. RemoteBDImage kan ingevuld worden met een beeld dat terug opgehaald is uit het flash. Door de DisplayMode op een gepaste waarde te zetten, wordt of het lokale beeld, of het opgehaalde beeld, of het 6
‘Generics’ is C# 2.0 zijn te vergelijken met templates in C++ of generics in Java.
Hoofdstuk 3. Software
32
verschilbeeld getoond. Dit is handig om te controleren als een beeld juist is weggeschreven. Een volledig zwart verschilbeeld is correct. De geneste enum DisplayModeType definieert deze drie toestanden. De huidige toestand wordt weergeven in het venster. BDImageViewController Class UserControl
Fields displayMode : DisplayModeType localBDImage : BDImage remoteBDImage : BDImage Properties DisplayMode { get; set; } : DisplayModeType LocalBDImage { get; set; } : BDImage RemoteBDImage { get; set; } : BDImage Methods BDImageViewController(Communicator c) UpdateDisplay() : void Events NameChanged : EventHandler<EventArgs> Nested Types DisplayModeType Enum
Local Remote Difference
Figuur 3.13: De vereenvoudigde klasse BDImageViewController.
De interface bevat links het beeld en rechts de configuratiedata. Stel dat de gebruiker de hoge lijntijd verandert, dan zal de bijhorende UpDown control zijn event ValueChanged oproepen. De component past dan de eigenschappen van het lokale BDImage aan: private void LinetimeHighUpDown_ValueChanged(object sender, EventArgs e) { localBDImage.ImageProperties.LinetimeHigh = (byte)LinetimeHighUpDown. Value; }
Op die manier wordt het model vanuit de viewcontroller bijgewerkt. De andere event
Hoofdstuk 3. Software
33
handlers zijn analoog. Het event NameChanged wordt opgeroepen wanneer de gebruiker de naam van het beeld verandert. Het hoofdvenster reageert hierop en roept de RefreshTree() methode op van de navigator zodat die op zijn beurt de naam kan aanpassen. In figuur 3.14 zijn ook twee knoppen te zien: ‘Save as default’ slaat de huidige configuratiedata als standaardwaarde op en ‘Reset’ laadt de opgeslagen standaardwaarden.
Figuur 3.14: Een screenshot van de BDImageViewController.
BD.GUI.HeaderLabel: voor de blauwe titelbalkjes (voorbeeld in figuur 3.15) in de deel-
vensters is gebruik gemaakt van een afgeleide klasse van het tekstlabel System.Windows.Forms.Label:
public class HeaderLabel : Label { public HeaderLabel() { this.Height = 18; this.AutoSize = false; this.BackColor = Color.LightSteelBlue; }
Hoofdstuk 3. Software
34
protected override void OnPaint(PaintEventArgs pe) { // basisklasse OnPaint base.OnPaint(pe);
ControlPaint.DrawBorder(pe.Graphics, this.Bounds, Color.SteelBlue, ButtonBorderStyle.Solid); } }
Figuur 3.15: Voorbeeld van een titelbalkje.
De code in de constructor zorgt voor een vaste hoogte en lichtblauwe achtergrond. Voor het donkerblauwe kader werd de OnPaint-handler herdefinieerd. De .NET klasse ControlPaint werd speciaal voor dit soort zaken ontwikkeld. Een eenvoudige oproep van DrawBorder() neemt het vuile werk voor zijn rekening. BD.GUI.AboutBox: bij een druk op het menu Help – About komt een informatievenster
te voorschijn dat door AboutBox wordt voorgesteld.
3.4.3
Actieklassen
Om de verwerking van de acties algemeen aan te pakken en doordat ze onderling afhankelijk zijn, zijn de klassen die de basisacties implementeren afgeleid van een abstracte basisklasse Action. De afhankelijke acties zijn op hun beurt afgeleid van de basisacties. Die klassenhi¨erarchie is getoond in figuur 3.16. De abstracte basisklasse BD.Actions.Action bevat alle velden, properties en methoden die de acties gemeen hebben. De betekenis van de abstracte ‘get’ properties Description en Name spreekt voor zich; zij moeten door de afgeleide klassen ingevuld worden. De virtuele methode DoAction() wordt ook in de kindklassen ge¨ımplementeerd. Zij bevat de eigenlijke implementatie van de acties en gebruikt de Communicator (zie 3.4.4) voor het zenden en ontvangen van data. Een boolese retourwaarde geeft het al dan niet slagen van de uitvoering aan.
Hoofdstuk 3. Software
35
A ct ion Abstract Class
Fields comm : Communicator Properties Description { get; } : string Name { get; } : string Methods Action(Communicator c) DoAction() : bool
DisplayAction
LoadAction
StoreAction
Class Action
Class Action
Class Action
Fields
Fields
Fields
_imageIndex : int
image : BDImage index : int
Properties Description { get; } : string ImageIndex { get; set; } : int Name { get; } : string Methods DisplayAction(Communicator c) DoAction() : bool
Properties Description { get; } : string Image { get; } : BDImage Index { get; set; } : int Name { get; } : string Methods DoAction() : bool LoadAction(Communicator c)
Nested Types
Nested Types
image : BDImage index : int Properties Description { get; } : string Image { get; set; } : BDImage Index { get; set; } : int Name { get; } : string Methods DoAction() : bool StoreAction(Communicator c) StoreAction(Communicator c, BDImage image) Nested Types
LoadProjectAction
StoreProjectAction
Class LoadAction
Class StoreAction
Fields bdProject : BDProject Properties BdProject { get; set; } : BDProject Description { get; } : string Name { get; } : string Methods DoAction() : bool LoadProjectAction(Communicator c) LoadProjectAction(Communicator c, BDProject bdProject)
Fields bdProject : BDProject Properties BdProject { get; set; } : BDProject Description { get; } : string Name { get; } : string Methods DoAction() : bool StoreProjectAction(Communicator c) StoreProjectAction(Communicator c, BDProjec…
Figuur 3.16: De hi¨erarchische structuur van de actieklassen.
Hoofdstuk 3. Software
36
Als voorbeeld wordt de tak StoreAction – StoreProjectAction gegeven. De andere acties DisplayAction, LoadAction en LoadProjectAction zijn analoog ge¨ımplementeerd. StoreAction zal een beeld sturen naar het aanstuurbord. Via de microcontroller wordt het dan in het flashgeheugen geschreven. Het beeld kan meegegeven worden bij de constructie van de klasse of nadien via de property Image. Index is het beeldnummer (0→15). Het actieprotocol leent zich uitstekend om als communicerende toestandsmachines (FSMs) ge¨ımplementeerd te worden: zowel in de PC als in de microcontroller. Voor een grafische voorstelling wordt de lezer opnieuw verwezen naar 3.2.1. Voor de FSM aan de PC kant is in StoreAction een genest type gedefinieerd dat de toestanden bepaalt: private enum StoreStates { INIT_STORE, SEND_CONFIG, SEND_BLOCK, SEND_LAST_BLOCK, FINISH };
De FSM zelf komt dan in de override van DoAction(). De volgende code is ontdaan van alle overbodige loginstructies. Nuttige uitleg is genoteerd in commentaar: public override bool DoAction() { byte[] data = image.GetByteStream(); byte[] buffer = new byte[128]; bool done = false; bool result = false; uint curWholeBlock = 0;
// bepaal het aantal blokken van 128 bytes en alloceer een buffer // voor het laatste (eventueel) onvolledige blok uint numWholeBlocks = (uint)data.Length >> 7; // delen door 128 uint lengthOfLastBlock = (uint)data.Length - (numWholeBlocks << 7); byte[] bufferLastBlock = new byte[lengthOfLastBlock];
StoreStates state = StoreStates.INIT_STORE;
while (!done) { switch (state) { case StoreStates.INIT_STORE:
Hoofdstuk 3. Software
// ledig de zend- en ontvangbuffers in de driver comm.Purge();
// commando ’S’ comm.WriteChar(’S’);
// wacht op een ACK en ga naar de volgende toestand if (comm.WaitForAck()) state = StoreStates.SEND_CONFIG; else done = true; break; case StoreStates.SEND_CONFIG: // verstuur het beeldnummer en de datalengte (2 bytes)
comm.WriteByte((byte)index);
byte lo = (byte)data.Length; byte hi = (byte)(data.Length >> 8);
comm.WriteBytes(new byte[] { lo, hi });
if (comm.WaitForAck()) state = StoreStates.SEND_BLOCK; else done = true; break; case StoreStates.SEND_BLOCK: // verstuur het huidig blok van 128 bytes
if (curWholeBlock == numWholeBlocks) { state = StoreStates.SEND_LAST_BLOCK; break; }
Array.Copy(data, 128 * curWholeBlock, buffer, 0, 128); comm.WriteBytes(buffer);
37
Hoofdstuk 3. Software
38
if (!comm.WaitForAck()) done = true;
curWholeBlock++;
break; case StoreStates.SEND_LAST_BLOCK: // verstuur het laatste (eventueel onvolledige) blok
Array.Copy(data, 128 * curWholeBlock, bufferLastBlock, 0, lengthOfLastBlock); comm.WriteBytes(bufferLastBlock);
if (comm.WaitForAck()) state = StoreStates.FINISH; else done = true; break; case StoreStates.FINISH: // als we hier geraken is de uitvoering geslaagd done = true; result = true; break; } } return result; }
De klasse StoreProjectAction zorgt voor het opslaan van een volledig project in het flashgeheugen. Ze is afgeleid van StoreAction. De property BdProject bepaalt het op te slaan project. De nieuwe override van DoAction() ziet er als volgt uit: public override bool DoAction() { for (int i = 0; i < 16; i++) { this.Image = bdProject.BdImages[i];
Hoofdstuk 3. Software
39
if (this.Image != null) { if (!base.DoAction()) return false; } } return true; }
Ieder niet-ledig beeld wordt verwerkt door DoAction() van de basisklasse StoreAction op te roepen. De andere acties DisplayAction, LoadAction en LoadProjectAction zijn op analoge manier ge¨ımplementeerd: een geneste enum bepaalt de toestanden en een override van DoAction() voert de actie in de vorm van een FSM uit.
3.4.4
Communicatieklassen
De communicatie met de FT232R chip verloopt gelaagd (figuur 3.17). Een Communicator verwerkt abstracte communicatieopdrachten. De concrete communicatie gebeurt dan over een specifiek kanaal dat zijn eigen aansturing heeft.
Het USB-kanaal dat hier gebruikt
wordt, is ook gelaagd. Op het laagste niveau worden golfvormen, die bits voorstellen, op een draad geplaatst (fysisch kanaal). Het USB-protocol bepaalt de volgorde en betekenis van die bits. Onder Windows wordt de USB-stack aangestuurd door de chipdriver van FTDI FTD2XX.DLL. Zoals beschreven in 3.3 kapselt FT232RDevice die driver in in een C# klasse. De Communicator gebruikt deze klasse voor het concretiseren van de communicatie. Dit gelaagd model maakt het mogelijk om andere kanalen te gebruiken zonder veel te veranderen. FT232RWrapper.FT232RDevice: deze klasse in de vorm van een DLL werd al uitvoering
besproken in 3.3, maar wordt hier voor de volledigheid vermeld.
Hoofdstuk 3. Software
40
Communicator
abstracte communicatie
Wrapper (FT232RDevice.dll) Win32 driver (FTD2XX.dll) concrete communicatie USB-protocol (USB-stack) Fysisch kanaal
Figuur 3.17: Het gelaagde communicatiemodel.
Communicator Class
Fields _action : Action device : FT232RDevice deviceDescriptor : FT232RDeviceDescriptor devices : List ev : EventWaitHandle worker : BackgroundWorker Properties Action { get; set; } : Action Device { set; } : FT232RDeviceDescriptor Methods CloseDevice() : void Communicator() GetDevices() : List InvokeAction() : void OpenDevice() : void Purge() : void ReadBytes(int length) : byte[] Rescan() : void WaitForAck() : bool worker_DoWork(object sender, DoWorkEventArgs e) : void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) : void WriteByte(byte b) : void WriteBytes(byte[] b) : void WriteChar(char c) : void Events ActionCompleted : EventHandler
Figuur 3.18: De klasse Communicator.
BD.Components.Communicator: figuur 3.18 toont deze klasse. Via GetDevices() krijgt
Hoofdstuk 3. Software
41
men een lijst van beschikbare devices terug, in de vorm van FT232RDeviceDescriptors. Rescan() ververst die lijst door rechtstreeks FT232RDevice.GetDeviceDescriptors(), zie 3.3, op te roepen. Een exemplaar van die lijst kan in de property Device geplaatst worden, de communicator werkt dan met dat apparaat. De methoden OpenDevice(), CloseDevice(), Purge() en ReadBytes() roepen rechtstreeks de gelijknamige methodes van FT232RDevice aan. Voor het schrijven van data zijn drie methodes voorzien: WriteByte(), WriteBytes() en WriteChar(). Die roepen alle het onderliggende WriteData() op. WaitForAck() geeft true terug als binnen 5 seconden een Ack ontvangen wordt. Deze methode krijgt uiteraard veel toepassing bij de uitvoering van de acties. De implementatie is gebaseerd op het event notificatie mechanisme beschreven in 3.3.
Asynchrone uitvoering van de acties Hierboven is reeds vermeld dat acties worden uitgevoerd op een aparte thread. Daar wordt nu wat dieper op in gegaan. De rationale die schuilt achter deze aanpak is de volgende: tijdrovende operaties zoals het downloaden van een bestand, database transacties of I/O kunnen ervoor zorgen dat een programma schijnbaar hangt. De gebruikersinterface reageert niet meer op invoer omdat de thread bezig met uitvoering van de operatie. Daarom worden dergelijke operaties op een aparte thread uitgevoerd. Het .NET raamwerk biedt een eenvoudig middel om in deze functionaliteit te voorzien: de BackgroundWorker. Een tijdrovende operatie wordt opgeroepen in de event handler van het event DoWork. De uitvoering wordt gestart door een oproep naar RunWorkerAsync(), die op zijn het event oproept. Er moet uiteraard voor gezorgd worden dat geen interface-elementen worden aangesproken in deze operaties. Onze tijdrovende operatie is uiteraard de uitvoering van een actie. Hier wordt het voordeel van klassenhi¨erarchie van de acties duidelijk: onze communicator werkt met de abstracte basisklasse Action. Haar virtuele DoAction() moet op een aparte thread uitgevoerd worden. Wanneer de uitvoering be¨eindigd is, roept de BackGroundWorker zijn event RunWorkerCompleted op. Daarin roept de communicator op zijn beurt zijn eigen event ActionCompleted op. In code: private BackgroundWorker worker;
Hoofdstuk 3. Software
42
// dit wordt opgeroepen als de actie uitgevoerd is public event EventHandler ActionCompleted;
public Communicator() { worker = new BackgroundWorker(); worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler( worker_RunWorkerCompleted); worker.DoWork += new DoWorkEventHandler(worker_DoWork); }
De event handlers worden opnieuw via delegates gekoppeld aan de events zelf. worker DoWork() voert de actie uit: private void worker_DoWork(object sender, DoWorkEventArgs e) { // knip OpenDevice(); // knip
// actie uitvoeren en resultaat inkapselen e.Result = _action.DoAction() ? ActionResult.SUCCEEDED : ActionResult. FAILED;
// indien de actie werd geannuleerd, vul het resultaat zo in if (worker.CancellationPending) { e.Cancel = true; e.Result = ActionResult.CANCELLED; }
CloseDevice(); }
In e.Result wordt de status opgeslagen, een waarde uit ActionResult: public enum ActionResult {
Hoofdstuk 3. Software
43
SUCCEEDED, FAILED, CANCELLED }
De status e wordt dan door de BackGroundWorker verder gebruikt om op het einde van de uitvoering de RunWorkerCompleted event handler op te roepen: oid worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { ActionCompletedEventArgs args = new ActionCompletedEventArgs();
// referentie naar de opgeroepen actie args.action = _action;
if (e.Cancelled) { args.result = ActionResult.CANCELLED; } else if (e.Error != null) { args.result = ActionResult.FAILED; } else { args.result = ActionResult.SUCCEEDED; }
// eigen event (van de Communicator zelf) oproepen if (ActionCompleted != null) ActionCompleted(this, args); }
Hier wordt het argument van het eigen event ActionCompleted aangemaakt en met de juiste waarden ingevuld. Dan wordt het event opgeroepen.
Hoofdstuk 3. Software
3.4.5
44
Hulpklassen
De klasse Log Om het debuggen eenvoudiger te maken, of om de interne werking te observeren, is de programmacode doorspekt met loginstructies. Daarvoor is gebruik gemaakt van het uitstekende framework log4net[3]. Dit pakket maakt het mogelijk om informatie in de vorm van LoggingEvents op een gecontroleerde en gestructuurde wijze naar een bepaalde output, de Appender, te sturen. Voorbeelden zijn de ConsoleAppender, die boodschappen naar een consolevenster stuurt, of de FileAppender, die naar een bestand schrijft. De mogelijkheid bestaat om zelf eigen appenders te ontwikkelen. De boodschap wordt geformatteerd volgens een bepaalde layout. Het gedrag van de software kan tijdens uitvoering gewijzigd worden door middel van XML-configuratiebestanden. Ook bestaan er verschillende logmethodes naargelang de ernst van de boodschap, zoals Debug(), Info(), Error(), . . . De appenders kunnen z´ o geconfigureerd worden, dat ze enkel die boodschappen die ernstig genoeg zijn, gaan verwerken. De Threshold regelt dit. In BDControl is een eigen klasse Log aanwezig (zie figuur 3.19). Omdat deze statisch is, kunnen haar methodes eenvoudig worden opgeroepen vanuit elke andere klasse, zonder daar een instantie aan te maken. Deze klasse zorgt er voor dat iedere boodschap wordt uitgeschreven naar het logvenster van de applicatie. Daartoe moet de property Box naar een instantie van een TextBox wijzen. Het private veld log is de eigenlijk logger. Die wordt niet direct ge¨ınstantieerd, maar teruggegeven door de klasse LogManager van log4net: private static readonly ILog log = LogManager.GetLogger(typeof(Log));
Het argument typeof(Log) is de naam van de logger.
De methoden Debug(), Warn()
Error(), Fatal() en Info() roepen de gelijknamige methoden op van log. Bijvoorbeeld: public static void Fatal(object o) { log.Fatal(o); updateBox(); }
De aanroep van updateBox zal de boodschap uitschrijven naar het logvenster. Daar wordt straks op terug gekomen. Boodschappen worden in een geheugenbuffer opgeslagen door een MemoryAppender. Een PatternLayout neemt het formatteren voor zijn rekening. Deze klasse zal op basis van een patroon een LoggingEvent converteren in een string. Dat is in dit geval:
Hoofdstuk 3. Software
45
Log Static Class
Fields box : TextBox layout : PatternLayout log : ILog MemAppender : MemoryAppender Properties Box { get; set; } : TextBox Methods Clear() : void CreateLoggers() : void Debug(object o) : void Error(object o) : void Fatal(object o) : void Info(object o) : void RenderLoggingEvent(LoggingEvent loggingEvent) : string updateBox() : void Warn(object o) : void Nested Types
Figuur 3.19: De statische hulpklasse Log.
layout = new PatternLayout(); layout.ConversionPattern = " %date{HH:mm:ss} %-5level - %message";
Het statement Log.Info("Found 1 FT232 USB device(s)"); resulteert dan in de volgende boodschap: 14:50:50 INFO - Found 1 FT232 USB device(s). De functie updateBox() schrijft de geheugenbuffer naar een TextBox control. Omdat controls in Windows Forms, de UI laag van .NET, gebonden zijn aan een bepaalde thread, moeten voorzorgen genomen worden in ons multithreaded programma. Er geldt dat een thread een control mag benaderen, enkel en alleen als die thread de control heeft aangemaakt. Men zegt dat die thread de control bezit. Wanneer updateBox() opgeroepen wordt, zijn er dus twee gevallen. Stel dat van de threads T1 en T2 , T1 de TextBox heeft aangemaakt. 1. T1 logt een boodschap: er is geen probleem; de TextBox wordt door T1 aangepast; 2. T2 logt een boodschap: het updaten van de TextBox wordt uitbesteed aan T1 . In het tweede geval wordt de methode Invoke(textCallBack) van de TextBox opgeroepen. Het argument textCallBack is een zg. delegate, objecten die veel gelijkenissen vertonen met
Hoofdstuk 3. Software
46
functiepointers in C of C++. Invoke() zorgt er voor dat die delegate dan op de T1 wordt uitgevoerd. Als we als delegate dezelfde functie updateBox() opgeven, krijgen we de volgende compacte code: delegate void SetTextCallback(); private static SetTextCallback textCallBack = new SetTextCallback(updateBox);
private static void updateBox() { if (box == null) return;
if (box.InvokeRequired) { box.Invoke(textCallBack); } else { LoggingEvent[] events = MemAppender.GetEvents(); box.AppendText(RenderLoggingEvent(events[events.Length - 1]) + Environment. NewLine); box.SelectionStart = box.Text.Length; box.ScrollToCaret(); } }
InvokeRequired geeft false terug als de aanroepende thread de control bezit. Het laatste event wordt dan geformatteerd door middel van de functie RenderLoggingEvent() en wordt als laatste lijn toegevoegd aan de TextBox. De klasse Program Deze klasse bevat het entry point van de applicatie: de Main() functie. Daarin wordt de logger aangemaakt en het hoofdvenster getoond.
3.5
Microcontrollercode
De microcontroller heeft als taak de basisacties af te handelen: 1. beelden ontvangen van de PC en in het flashgeheugen schrijven;
Hoofdstuk 3. Software
47
2. beelden uit het flashgeheugen halen en terugsturen naar de PC; 3. beelden uit het flashgeheugen halen en naar de FPGA sturen. Deze drie bewerkingen zijn zoals beschreven in 3.2.1 als FSM’s ge¨ımplementeerd. De communicatie verloopt via de eerste seri¨ele poort (UART0). Door die ge¨ıntegreerde logica moet niet op bitniveau gewerkt worden, maar moeten enkel de operatiemode en baudrate ingesteld worden. Eenzelfde special-function register (SFR), SBUF0 wordt beschreven of gelezen om resp. te verzenden of te ontvangen. We werken in mode 1, dit is standaard full-duplex asynchrone communicatie. Per byte worden er 10 bits verstuurd: 1 startbits, 8 databits en 1 stopbit. In deze mode is de baudrate een functie van een timer overflow. Daardoor is de baudrate programmeerbaar. Hier wordt timer 1 gebruikt als baudrategenerator. Door hem in autoreload mode te plaatsen, zal hij bij een overloop (FFh) een klokpuls zenden naar het baudrate circuit en opnieuw vanaf zijn startwaarde beginnen tellen. Een gepaste startwaarde laat toe de baudrate bijna willekeurig te bepalen. Ze wordt in het register TH1 geplaatst en wordt berekend via de volgende formule[13]: TH1 = 256 −
2SM ODx × fclk 384 × baudrate
SM ODx geeft aan of de baudrate verdubbeld moet worden of niet. De initialisatiecode van UART0 void InitUARTTimer1(const word BAUD RATE) heeft als parameter de baudrate. Er kunnen dus eenvoudig andere snelheden getest worden. De rest van de code is standaard en vergt weinig uitleg. Ze kan opgezocht worden op de meegeleverde cdrom. Wanneer er een reset optreedt, springt de microcontroller naar de main() functie: SuperSystemState superState;
void main(void) { /* watchdog timer uitschakelen */ TA = 0xAA; TA = 0x55; EWT = 0;
/* init UART0, baudrate 9600, met Timer 1 */ InitUARTTimer1(9600);
Hoofdstuk 3. Software
48
/* globale interrupts toelaten */ EA = 1;
/* superState wordt gezet in de UART0 ISR */ superState = IDLE;
while (1) { switch (superState){ case IDLE: /* idle mode: energie sparen */ PCON |= 0x01; break;
case START_STORE_UART: ES0 = 0; /* seri¨ ele interrupts uitschakelen */ TI = 1; /* verzenden mogelijk maken */ StoreToFlash(); ES0 = 1; /* seri¨ ele interrupts inschakelen */ superState = IDLE; break;
case START_GET_UART: ES0 = 0; /* seri¨ ele interrupts uitschakelen */ TI = 1; /* verzenden mogelijk maken */ SendToUART(); ES0 = 1; /* seri¨ ele interrupts inschakelen */ superState = IDLE; break;
case START_DISPLAY: ES0 = 0; /* seri¨ ele interrupts uitschakelen */ TI = 1; /* verzenden mogelijk maken */ Display(); ES0 = 1; /* seri¨ ele interrupts inschakelen */ superState = IDLE; break;
Hoofdstuk 3. Software
49
} } }
De watchdog wordt uitgeschakeld omdat dit problemen gaf met de communicatie. Na initialisatie van de seri¨ele poort komt de uitvoering in een toestandsmachine terecht. De standaardtoestand is IDLE, waarin de microcontroller zich in een energiezuinige toestand bevindt. De andere toestanden roepen de FSMs die de acties afwerken op. De toestand (bepaald door de variabele superState) wordt veranderd als een ASCII-commando ontvangen wordt. Dit gebeurt in de interrupt service routine (ISR) van de UART0: bij ontvangen of verzenden van data wordt een interrupt gegenereerd. Om racecondities te vermijden worden de seri¨ele interrupts dan ook uitgeschakeld bij uitvoering van een actie. De ISR ziet er als volgt uit: extern SuperSystemState superState;
void UART_ISR(void) interrupt INTERRUPT_UART /* int 4, adres 0023h */ { char ch; if (RI == 1) { ch = SBUF0; /* seri¨ ele buffer */
switch (ch) { case ’S’: superState = START_STORE_UART; break; case ’L’: superState = START_GET_UART; break; case ’D’: superState = START_DISPLAY; break; default: break; } RI = 0; }
Hoofdstuk 3. Software
50
if (TI == 1) { TI = 0; } }
Het speciale sleutelwoord interrupt geeft aan dat de functie een ISR is. De registers RI en TI zeggen of data ontvangen of verzonden werd; er wordt dan op gepaste wijze gereageerd. Zoals het hoort bij een ISR is deze functie zeer kort. Haar doel is enkel een variabele te veranderen waardoor de acties in ‘normale’ uitvoering worden afgewerkt. Omdat seri¨ele interrupts dan uitgeschakeld zijn, moeten de functies die data verzenden of ontvangen zelf controleren als de poort vrij is door RI en TI te lezen of te zetten: byte putch_no_ints (const byte c)
{
while (!TI) ; /* wacht */ TI = 0; return (SBUF0 = c); }
byte getch() { byte ch;
while (!RI) ; /* wacht */ RI = 0; return (ch = SBUF0); }
3.5.1
Implementatie van de acties
De toestandsdiagramma van de acties zijn terug te vinden in figuur 3.3, 3.5 en 3.7. Omdat de code meestal triviaal is, wordt ze niet gegeven. Enkel de meest interessante zaken worden hier vermeld.
Hoofdstuk 3. Software
51
Schrijven naar het flashgeheugen Bij een ‘opslaan’-actie wordt in de toestanden RECEIVE BLOCK en RECEIVE LAST BLOCK geschreven naar het flashgeheugen. Het geheugenmodel van de 8051 is nogal verschillend van dat van een ‘gewone’ mini- of microcomputer. Daar wordt programmacode en data in dezelfde fysische geheugenruimte geladen. Onze microcontroller gebruikt een adresseerschema dat code en data van elkaar scheidt. Beide zijn 64 KiB groot (0000h tot FFFFh). Ze kunnen echter wel fysisch overlappen. Programmageheugen Dit is alleen-lezen, intern (in de chip zelf) of extern geheugen dat automatisch wordt gelezen bij uitvoering van een programma of door de MOVC-instructie. Datageheugen Dit is lezen/schrijven geheugen, intern of extern. Het kan aangesproken worden door de instructie MOVX. Ons flashgeheugen is duidelijk extern. We moeten niet rechtstreeks met de MOVX werken omdat de C51 compiler een aantal sleutelwoorden heeft die toelaten het soort geheugen te specifi¨eren: xdata zal een variabele in het extern datageheugen plaatsen, idata in het intern datageheugen. We defini¨eren dus een pointer naar de beelddata in het flash en een buffer van die data in het intern geheugen: byte volatile xdata * xblock = 0x0000; // extern geheugen byte idata block[128]; // buffer
Het sleutelwoord volatile zegt aan de compiler om gelezen data niet in een register te plaatsen, maar opnieuw echt te lezen. Dit is vereist bij het lezen van het flash in een lus. Het adres waar xblock naar wijst wordt aangepast om het correct beeld en blok te schrijven of te lezen. Er wordt sector-gebaseerd naar het flashgeheugen geschreven: als er 128 bytes ingeladen zijn, worden die gelijktijdig geprogrammeerd. De chip zal de adressen en data latchen zodat de adres- en databus vrijkomen voor andere operaties. Het einde van een programmeercylcus kan bepaald worden door DATA-polling: wanneer de laatste ingeladen byte tijdens het programmeren gelezen wordt, zal het complement van de ingeladen data verschijnen op de pin I/O7. Als de programmeercyclus afgelopen is, verschijnt de volledig correct data op de databus. Een en ander wordt verduidelijkt in de volgende code: /*
Hoofdstuk 3. Software
52
* CEb is de chip enable poort van het flash (actief laag). * block wijst naar het startadres van een blok */
/* block ontvangen en bufferen */ for (byteNum = 0; byteNum < 128; byteNum++) block[byteNum] = getch();
/* blok wegschrijven */ CEb = 0; for (byteNum = 0; byteNum < 128; byteNum++) *(xblock + byteNum) = block[byteNum];
CEb = 1;
/* data polling: laatste byte lezen en vergelijken met versie in het intern geheugen */ while (1) { CEb = 0; if (*(xblock + 127) == block[127]) break; CEb = 1; }; CEb = 1;
/* ga nu verder met het volgende blok */
De notaties *(rij + offset) en rij[offset] zijn semantisch gelijk: ze benaderen de data op het adres rij + offset. De array-notatie voor block geeft hier aan dat het om een buffer met vaste grootte aan, terwijl het externe geheugen xblock beschreven wordt op adressen die tijdens de uitvoering berekend worden aan de hand van het beeld- en bloknummer. Lezen uit het flashgeheugen De microcontroller zal een volledige beeld in een keer doorsturen naar de PC, dit gebeurt in de toestand SEND van de FSM: /* length en xblock zijn berekend in een vorige toestand */ case SEND: CEb = 0;
Hoofdstuk 3. Software
53
for (byteNum = 0; byteNum < length; byteNum++) putch_no_ints(*(xblock + byteNum)); CEb = 1;
Afbeelden van een beeld In deze actie stuurt de microcontroller een beeld naar de FPGA, voor de onderlinge communicatie moeten beide dus een protocol volgen. Er worden 3 signalen gebruikt: sbit RSTb = P3 ^ 2; /* reset, actief laag, bit, pin P3.2 van uC */ sbit DAVb = P3 ^ 3; /* data available, actief laag, bit, pin P3.3 van uC */ sfr DATA = 0x90; /* data, byte, poort P2 = adres 0x90 van uC */
Het tijdsdiagram is te zien in figuur 3.20.
RST
ÀÀÀÀ ÄÄ¡ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ
DAV
ÀÀÀÀÀÀÀÀÀÀÀÀÀ ÄÄ¡ÀÀÀÀ ÄÄ¡ÀÀÀÀ ÄÄ¡ÀÀÀÀ Ä
DATA
ÍÍÍÍÍÍÍÍÍÍͧÎÎÎÎÎΦ§ÎÎÎÎÎΦ§ÎÎÎÎÎΦ§ÎÎÎ
Figuur 3.20: Tijdsdiagram van de signalen voor communicatie met de FPGA.
Na een reset van de FPGA wordt een byte op de databus gezet en DAV geasserteerd. De FPGA zal nu deze data lezen en cachen. Er moet voor gewaakt worden dat de data lang genoeg op de bus staat. De FPGA werkt namelijk maar met een 4 MHz klok. Daarom wordt een hardwarevertraging Delay() ingevoerd om zeker te zijn dat er minstens ´e´en FPGAkloktransitie is wanneer de data geldig is. Tussen de bytes moet ook, maar heel wat korter, gewacht worden. Dat gebeurt dan door aantal NOPs uit te voeren. case DISPLAY: /* puls op RST (actief laag) */ RSTb = 1; Delay(); RSTb = 0; Delay(); RSTb = 1;
Hoofdstuk 3. Software
54
/* zend 4 config bytes + 16 rijen van 16 pixels naar de de FPGA */ for (byteNum = 0; byteNum < 16 * 16 + 4; byteNum++){ /* data op bus plaatsen */ DATA = *(xblock + byteNum);
/* deasseerteer DAV */ DAVb = 1;
/* beetje wachten */ _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
/* asserteer DAV */ DAVb = 0;
/* wacht (FPGA haalt data op) */ Delay(); }
De hardwarevertraging gebeurt door een timer in te stellen op een goed gekozen waarde. Na overflow wordt dan verder gegaan met de uitvoering.
3.5.2
Mini DS89C420 programmer
De DS89C420 is ontworpen om in-system geprogrammeerd te worden via zijn interne UART. Het bestaande systeem voorziet echter niet in deze functie. Daarom werd een eenvoudige programmer ontwikkeld die gebaseerd is op deze techniek. Om de chip te kunnen programmeren, dient hij naar zijn bootloader te springen. Dit gebeurt door een aantal pinnen een specifieke waarde op te dringen. Na ontvangst van een CR-karakter
Hoofdstuk 3. Software
55
zal de microcontroller zijn seri¨ele poort automatisch synchroniseren op de baudrate van de PC. Het programmeren zelf kan dan makkelijkst gebeuren met de gratis Microcontroller Toolkit van Dallas. Een gewone seri¨ele terminal is ook geschikt. In de volgende tabel zijn de vereiste stimuli te zien. EA 0
RST 1
PSEN 0
Tabel 3.1: Inputstimuli voor bootloadermodus.
Figuur 3.21 toont het schema van de programmer. Het voedingsgedeelte is standaard: een 7805 spanningsregelaar en een aantal afvlakcondensatoren. De diode beschermt de regelaar tegen spanningspieken die kunnen optreden bij het uitschakelen. Vanaf een ingangsspanning Vin van 7 V is de uitgang van dit blok een stabiele 5 V. Het circuit is zo opgezet dat de microcontroller automatisch naar zijn bootloader springt bij het asserteren van de seri¨ele DTR-pin (actief laag). Daartoe is een 74125 ingezet. Dit IC bevat 4 niet-inverterende tristate buffers, waarvan er 3 worden gebruikt. De stuuringangen zijn inverterend. Het is duidelijk dat bij assertie van DTR op de pinnen de correcte waarden worden geplaatst. De spanningsomzetting tussen het RS232-domein en CMOS-domein wordt verzorgd door een MAX232. Om de chip zo betrouwbaar mogelijk te programmeren, mogen de baudrates van PC en microcontroller niet meer dan 3 % van elkaar verschillen. Het kristal dat gebruikt wordt voor klokgeneratie moet dan een gepaste trilfrequentie hebben. Er werd gekozen om met de standaardsnelheid van 9600 bps te programmeren. De trilfrequentie van het kristal wordt dan als volgt bepaald: de bootloader gebruikt de seri¨ele poort 0 in mode 1[13]: asynchroon, 1 startbit, 1 stopbit, geen pariteit, full duplex. Daarbij gebeurt de baudrategeneratie door timer 1 in 8 bit autoreload. Dat wil zeggen dat de timer vanaf een bepaalde waarde t naar boven telt en bij FFh automatisch opnieuw begint. In deze mode wordt de baudrate b gegeven door:
b=
2SM OD 32
x
×
fclk 1 × 12 256 − t
Daarbij geldt dat SM OD x = 1. Dat geeft aan dat de baudrate verdubbeld wordt, wat het geval is als de chip zich in de bootloadermode bevindt. De formule wordt:
Hoofdstuk 3. Software
56
9600 =
fclk 192 (256 − t)
Oplossen naar gehele waarden van t geeft als oplossing fclk = 11.0952 MHz en t = FAh. De automatische baudratesynchronisatie werkt als volgt: voor t worden achtereenvolgens de waarden uit de set {FFh, FEh, FDh, FCh, FBh, FAh, F8h, F6h, F5h, F4h, F3h, F0h, ECh, EAh, E8h, E6h, E0h, DDh, D8h, D4h, D0h, CCh, C0h, BAh, B0h, A8h, A0h, 98h, 80h, 60h, 40h} geprobeerd totdat een minimale afwijking tussen de interne en externe baudrate gevonden wordt. In ons geval zal dus een optimale waarde voor t van FAh gevonden worden, bij een afwijking 0. Een foto van de programmer opgebouwd op een breadboard is te zien in figuur 3.22.
Hoofdstuk 3. Software
Figuur 3.21: Het schema van de mini-programmer.
57
Hoofdstuk 3. Software
Figuur 3.22: De mini-programmer opgebouwd op een breadboard.
58
Hoofdstuk 4
Semi-automatische vermogenmeting De aanstuurchips die de complexe golfvormen genereren waarmee een beeld wordt opgebouwd, maken gebruik van 6 voedinglijnen: 85 V, 60 V, 55 V, 45 V, 35 V en 5 V. Om het totale vermogen te kennen dat gedissipeerd wordt bij het afbeelden van een beeld, moet de som van het vermogenverbruik in elk van die lijnen gemeten worden. De methode die hier voorgesteld wordt, stelt ons in staat om de dissipatie in 1 lijn te meten.
4.1
Opzet
Opmerking vooraf
De lengte van een afbeeldcyclus is onder andere afhankelijke van de
configuratiedata. In wat volgt wordt uitgegaan van de volgende – vaste – parameters:
4.1.1
lijntijd laag
lijntijd hoog
CR tijdens reset
CR tijdens lijnscan
10
90
nee
nee
Methode
In [9] wordt een oplossing voorgesteld om het verbruik te meten: voor het (ogenblikkelijk) vermogenverbruik Pi in lijn i geldt: Pi = Vi Ii met Vi de voedingsspanning van en Ii de stroom door de voedingslijn. Het meten van de stroom gebeurt door de spanningsval Vdi te meten over een kleine shuntweerstand Ri . Die weerstand moet klein gehouden worden om de rest van het systeem zo weinig mogelijk te be¨ınvloeden. We bekomen: Pi = V i 59
Vdi Ri
Hoofdstuk 4. Semi-automatische vermogenmeting
60
In tabel 4.1 zijn eigenschappen van de voedingslijnen samengevat. De index i wordt vanaf nu achterwege gelaten. Voor het meten van de veranderlijke spanningsval Vt,d (t) wordt de digitale oscilloscoop THS720A van Tektronix gebruikt[15]. Dit is een 2-kanaalsscoop die programmeerbaar is via een RS232 poort. De minimale sampleperiode is 2 ns (500 megasamples per seconde), waarbij per meting 2500 samples genomen worden. Kanaal 1 meet de spanningsval over de shuntweerstand, terwijl op kanaal 2 getriggerd wordt. Het triggersignaal is afkomstig van een rij- of kolomstuursignaal gegenereerd door een driverchip. Het is mogelijk om hierop te triggeren omdat zo’n signaal altijd eenzelfde structuur heeft: een aantal resetpulsen gevolgd door een selectiepuls die de rij of kolom selecteert. Door het triggerniveau adequaat in te stellen, en door een vertraagde tijdsbasis te gebruiken kan een arbitraire portie van de spanningsval over de shuntweerstand opgenomen worden. Een vertraagde tijdsbasis van t s wil zeggen dat er, nadat de scoop getriggerd wordt, t s gewacht wordt met opnemen van een golfvorm (figuur 4.1).
Figuur 4.1: Illustratie bij een vertraagde tijdsbasis.
We kiezen het eerste rijsignaal als triggersignaal voor de metingen (schets in figuur 4.2), met een triggerniveau van 50 V. Het signaal Vt,d (t) wordt dus met een sampleperiode Ts lijn 1 2 3 4 5 6
Vi (V) 85 60 55 45 35 5
Ri (Ω) 56 56 56 56 56 220
Tabel 4.1: Voedingslijnen en shuntweerstanden
Hoofdstuk 4. Semi-automatische vermogenmeting
61
90,0 80,0
Spanning (V)
70,0 60,0 50,0 40,0 30,0 20,0 10,0 0,0
tijd
Figuur 4.2: Het signaal waarop getriggerd wordt.
bemonsterd: Vd (n) = Vt,d (n Ts ) , n = 1..N Ts wordt op een ‘goede’ waarde ingesteld afhankelijk van de meting. De PC haalt de golfvorm Vd (n) op voor verdere verwerking. Het vermogenverbruik tijdens een interval [nTs , (n + 1)Ts ] is: P (n) =
V Vd (n) R
De gemiddelde dissipatie en het piekvermogen over het volledig meetinterval worden:
Pavg
N 1 V X = Vd (n) N R
(4.1)
n=1
Ppeak =
4.1.2
V Vd,max R
(4.2)
Globale en lokale metingen
Er zijn twee soorten metingen: een globale meting van het vermogen tijdens een volledige afbeeldcyclus; een lokale meting, met een in te stellen meetvenster.
Globale meting Deze meting is bedoeld om een globaal overzicht van de dissipatie in een bepaalde voedingslijn te hebben. Het woord ‘globaal’ slaat op het feit dat de meting gebeurt
Hoofdstuk 4. Semi-automatische vermogenmeting rij 1 2 3 4 5 6 7 8
tijd (ms) 160 116.8 113.6 90.4 252 228 206 182.4
kolom 1 2 3 4 5 6 7 8
62 tijd (ms) 246 246 246 246 246 246 246 246
Tabel 4.2: Eindtijden van de selectiepuls van de stuursignalen.
over de volledige afbeeldcyclus. Om de duur van zo’n cyclus te bepalen is van elk stuursignaal het tijdstip bepaald van het einde van de selectiepuls ten opzichte van de eerste resetpuls. De langste tijd is de duur van een afbeeldcyclus. Uit tabel 4.2 blijkt dat de cylcustijd 252 ms is. De scoop heeft horizontaal 10 divisies. De tijdsbasisschaal zou dus op ≈ 25 ms per divisie moeten ingesteld worden. De THS720A laat echter enkel schaalfactoren toe in een 1-2-5 reeks. De schaalfactor wordt dus 20 ms per divisie. Nu moet het signaal in twee keer gemeten worden: de tweede keer met een vertraagde tijdsbasis van 200 ms. Vd (n) bestaat dan uit 5000 samples genomen over een tijdspanne van 400 ms. De sampleperiode Ts is dan 80 µs. Het is duidelijk dat Ts veel te groot is om accurate resultaten te verkrijgen (Nyquistvoorwaarde niet voldaan). Belangrijk is dus op te merken dat een globale meting geen goede indicatie geeft van het vermogengebruik. Het is hoogstens een hulpmiddel om meetvensters te bepalen voor een lokale meting. Lokale meting Een lokale meting is een meting van 2500 samples die gebeurt in een zelf in te stellen interval: het meetvenster. Zo kan men de interessante componenten van de dissipatie afzonderen. De gebruiker geeft een starttijd t en lengte l (uit de sequentie 1-2-5) op. De starttijd t is de vertraging van tijdsbasis. Een voldoende klein venster, en daardoor een voldoende kleine sampleperiode, zal een goede meting opleveren.
4.2
Uitbreiding van BDControl
BDControl werd aangepast om te voorzien in de nieuwe functionaliteit. Er werd een nieuwe namespace BD.Power ingevoerd. De visuele component PowerMeasureControl is een nieuw deelvenster voor de vermogenmeting, PowerGraphForm is een simpel venster dat het signaal Vd (n) toont en PowerMeasureManager neemt het eigenlijke meetwerk op zich. Een meting
Hoofdstuk 4. Semi-automatische vermogenmeting
63
wordt opgeslagen in een PowerMeasurement-object. PowerMeasurement Class
Properties XValues YValues Methods GetAveragePower GetPeakPower
Figuur 4.3: De klasse PowerMeasurement
BD.Power.PowerMeasurement: deze klasse (figuur 4.3) stelt de eigenlijke meting Vd (n)
voor. De twee methodes berekenen het piek- en gemiddelde vermogen. PowerMeasureManager Class
Properties SupplyLines Methods ConformData ExecuteCommands GetWaveformParams InitScope InitSerialPort InvokeCompletionEvent MeasureGlobal MeasureGlobalTask MeasureLocal MeasureLocalTask WaitForBusy Events MeasurementCompleted Nested Types
Figuur 4.4: PowerMeasureManager
BD.Power.PowerMeasureManager: (figuur 4.4) de metingen worden hier ook asynchroon
uitgevoerd. Ditmaal is geen gebruik gemaakt van een BackgroundWorker, maar van de .NET threadpool. Dit zijn een aantal slapende threads (standaard 25 per logische pro-
Hoofdstuk 4. Semi-automatische vermogenmeting
64
cessor). Ze kunnen een taak toegewezen worden met QueueUserWorkItem(). De methoden MeasureGlobal() en MeasureLocal() hebben een corresponderende *Task() die de meting uitvoert. Eerst wordt de oscilloscoop ingesteld, dan wordt het beeld afgebeeld. Daarbij zal de scoop getriggerd worden. Als laatste haalt de PC de golfvorm binnen. Bij een meting zijn er dus drie threads betrokken: ´e´en voor de GUI, ´e´en voor de scoopsturing en een laatste die de display-actie uitvoert. Uiteraard wordt opnieuw een synchronisatieprimitief (semafoor) gebruikt. Na afloop wordt het event MeasurementCompleted opgeroepen dat de gemeten data kan doorgeven. Het aansturen van de scoop gebeurt met ASCII-commando’s[16]. Om bijvoorbeeld in te stellen dat een vertraagde tijdsbasis van 100 ms met een schaal van 2 µs/divisie gebruikt worden, moet de volgende opdrachten gegeven worden: HORIZONTAL:DELAY:TIME:RUNSAFTER 100e-3 HORIZONTAL:MAIN:SCALE 2e-6 HORIZONTAL:MODE DELAYED
ExecuteCommand() zal een lijst van commando’s naar de oscilloscoop sturen. WaitForBusy bevraagt het apparaat in een lus met BUSY? totdat een 0 geantwoord wordt. Dit is handig om het programma en de scoop de synchroniseren. De get-property SupplyLines geeft een lijst van SupplyLineSettings objecten terug die verschillende voedingslijnen voorstellen (voedingsspanning en shuntweerstand). De data wordt ontvangen in ASCII formaat, waarbij elk datapunt een tekstuele voorstelling is van een 16-bit waarde, dus tussen -32768 en 32767. De methode ConformData() zal uit deze punten de werkelijke waarden berekenen, samen met de tijdschaal, aan de hand van de instellingen van de oscilloscoop. BD.GUI.PowerMeasureControl en BD.GUI.PowerGraphForm: een screenshot van het
nieuwe interface-element is te zien in figuur 4.5. Dit venster verwacht invoer van de gebruiker: de aansluiting van de oscilloscoop, welke voedingslijn gemeten wordt en het meetvenster. Er zijn knoppen aanwezig om een globale of lokale meting te starten. Een druk op ‘graph’ toont de golfvorm in een instantie van PowerGraphForm. Daarvoor werd de open-source grafiekbibliotheek NChart gebruikt[10]. De werking van de klassen is uiterst triviaal – ze bevatten bijna uitsluitend glue logic – en wordt dan ook niet gegeven.
Hoofdstuk 4. Semi-automatische vermogenmeting
65
Figuur 4.5: Het nieuwe interface-element voor de vermogenmeting.
4.3
Illustratie van de methode
Om correct te meten dient de volgende procedure gevolgd te worden. Hierbij wordt ervan uitgegaan dat het display aangesloten is en dat BDControl draait. Het is belangrijk dat een Tektronix THDS720A oscilloscoop gebruikt wordt. De RS232 snelheid moet 19200 baud zijn. 1. Sluit de probe van kanaal 1 aan over de gewenste shuntweerstand. 2. Sluit de probe van kanaal 2 aan op het eerste rijsignaal. 3. Selecteer in BDControl de juiste poorten (zowel van het aanstuurbord als de oscilloscoop). 4. Open indien nodig een beeld met de configuratieparamaters hierboven beschreven en verstuur het naar het display. 5. Selecteer de correcte instelling van de voedingslijn in BDControl. 6. Doe een globale meting. 7. Gebruik de globale meting om een interessant venster te bepalen voor een lokale meting.
4.4
Voorbeeld
Er werd een meting gedaan op de 60 V voedingslijn. Figuur 4.6 toont de globale meting en twee lokale metingen met steeds kleiner meetvenster. Enkel bij de laatste meting is de samplefrequentie groot genoeg en zijn de betrouwbare resultaten af te lezen in het resultaatveld (figuur 4.7).
Hoofdstuk 4. Semi-automatische vermogenmeting
(a) De globale meting.
(b) De eerste lokale meting.
(c) De tweede lokale meting.
Figuur 4.6: Een meetvoorbeeld.
66
Hoofdstuk 4. Semi-automatische vermogenmeting
Figuur 4.7: Het vermogenverbruik van de laatste lokale meting.
67
Hoofdstuk 5
Besluit Het doel van deze scriptie was drieledig. Ten eerste werd de seri¨ele interface vervangen door een USB-kanaal. De chip FT232R van FTDI neemt hier de meeste taken op zich. De gerealiseerde module kan ingeplugd worden in het voetje van de MAX232. Op die manier moet geen nieuw, en duur, PCB ontwikkeld worden. Ten tweede is een nieuwe, grafische front-end voor de PC, BDControl, ontwikkeld. Het C#programma laat toe om het display te besturen en om gemakkelijk met beelden om te gaan. Daartoe werden acties gedefinieerd. Door de object-ge¨ori¨enteerde aanpak is de applicatie makkelijk uitbreidbaar. Parallel hieraan werd ook de code voor de microcontroller in C herschreven. Als laatste werd een methode opgesteld voor semi-automatische meting van de dissipatie in een voedingslijn. Daarvoor wordt een THS720A oscilloscoop bestuurd vanuit BDControl. Een globale meting meet de spanningsval tijdens een volledige afbeeldcyclus, maar door de te grote sampleperiode zijn de resultaten niet correct. Een lokale meting zal tijdens een klein tijdsinterval meten en geeft accurate resultaten.
68
Bijlage A
Klassendiagram Figuur A.1 toont een iets vereenvoudigd klassendiagram van BDControl. Enkel de belangrijkste associaties zijn weergegeven.
69
BDNavigator
PowerMeasureControl
Class UserControl
Figuur A.1: Een overzicht van de applicatie BDControl.
Class
PowerMeasurement
Class
PowerMeasureManager
Class Form
PowerGraphForm
powerGraph…
pow…
Class UserControl
BDImageViewController
bdImageViewCon…
Class Form
MainForm
bdNavigator
Class UserControl
comm
powerMeasurement
powerMeasureManager
Class
Communicator
comm
BDProjectManag…
Bd
IDisposable
A ct ion
Log
Class Action
StoreProjectAction Class StoreAction
LoadProjectAction Class LoadAction
StoreAction
Static Class
LoadAction
Abstract Class
Program Static Class
Class Action
Action
Class
BDImage
Im
BDImageProper … Class
DisplayAction
Sealed Class
FT232RDevice
Class
FT232RDeviceDescriptor
IDeviceDescriptor
Sealed Class
BDProject
Class
Class Action
de…
D…
bdProject
bdProjectManager
Bijlage A. Klassendiagram 70
Bijlage B
Inhoud van de cdrom De meegeleverde cdrom bevat BDControl, de microcontrollercode, een aantal testprogramma’s en deze scriptie als LATEX broncode. Instructies voor installatie zijn te vinden in bijlage C. De mapstructuur is als volgt: BDControl/: De Visual Studio 2005 solution met alle subprojecten.
– BDControl: Het hoofdprogramma. – Publish: De installatiebestanden van BDControl. Dit kan gebruikt worden als de broncode ontbreekt. – FT232RWrapper: De DLL die de FTDI driver inkapselt. – THS720Test: Een klein programma dat de aansturing van de oscilloscoop test. – WrapperTest: Een testprogramma voor de initi¨ele testen van FT232RWrapper. uC/: De microcontrollercode samen met een µVision3 project. FT232R Driver/: De FTDI driver van de chip. Verslag/: De broncode van dit verslag.
71
Bijlage C
Installatie-instructies Allereerst moet de FT232R driver ge¨ınstalleerd worden. Voer daarom v´o´or het inpluggen van de USB-kabel /FT232R Driver/DPInst.exe uit. BDControl kan uitgevoerd worden door het project te starten vanuit de Visual Studio. Als de broncode niet voorhanden is, kan het met /BDControl/Publish/Setup.exe ge¨ınstalleerd worden. Als alles goed is moet de chip door BDControl herkend worden. In het keuzevak rechtsboven in het hoofdvenster is de chip dan geselecteerd.
72
Bibliografie [1] The Altera Cyclone FPGA family.
http://www.altera.com/products/devices/
cyclone/cyc-index.jsp. [2] Keil µVision IDE en C51 compiler. http://www.keil.com/. [3] The log4net framework. http://www.apache.com/log4net. [4] Atmel. 1-megabit (128K x 8) 5-volt Only Flash Memory, 2 2005. [5] Microsoft Corporation. The .NET framework. http://www.microsoft.com/dotnet. [6] Dallas. Microcontroller Tool Kit 2. http://files.dalsemi.com/microcontroller/ dev_tool_software/mtk/, 2004. [7] Future Technology Devices International Ltd. FT232R USB UART IC, 2005. [8] USB 2.0 Promoter Group. USB 2.0 specificatie. http://www.usb.org/developers/ docs/. [9] Jeroen Hoet. Automatiseren van een meetopstelling voor het opmeten van het vermogenverbruik in beeldschermen. Afstudeerwerk, UGent, 2006. [10] Samuel Pickard. nChart, open source advanced charting solution for .NET. http: //sourceforge.net/projects/nchart/. [11] Michael J. Pont. Embedded C. Adisson-Wesley, Londen, 2002. [12] Maxim Integrated Products. Application Note 3262: In-System Programming with 8051Based Microcontrollers. Dallas Semiconductor, June 2004. [13] Maxim Integrated Products. Ultra-High-Speed Flash Microcontroller User’s Guide. Dallas Semiconductor, 10 2005. 73
Bibliografie
74
[14] Thomas W. Schultz. C and the 8051. Pagefree Publishing, 3rd edition, june 2004. [15] Tektronix.
THS720A
oscilloscoop.
http://www.tek.com/site/ps/0,
,3M-10566-INTRO_EN,00.html. [16] Tektronix, Inc., P.O. Box 1000, Wilsonville. THS720P TekScope Programmer Manual.
THS710A, THS720A, THS730A &
Lijst van figuren 1.1
Overzicht van het systeem. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
1.2
Structuur van het aanstuurbord. . . . . . . . . . . . . . . . . . . . . . . . . .
2
1.3
Het datamodel: schema van project en beeld en alignatie op 4 KiB grenzen. .
5
2.1
Circuitschema van de oorspronkelijke seri¨ele communicatie. . . . . . . . . . .
6
2.2
Blokschema van de FT232R. . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
2.3
Het schema van de USB-module. . . . . . . . . . . . . . . . . . . . . . . . . .
8
2.4
Een foto van het opgebouwde prototype. . . . . . . . . . . . . . . . . . . . . .
9
3.1
Sequentiediagram van de communicatie bij uitvoering van een basisactie. . . .
13
3.2
Sequentiediagram van de communicatie bij opslaan.
. . . . . . . . . . . . . .
14
3.3
Toestandsmachines voor de uitvoering van een ‘opslaan’ actie. . . . . . . . . .
15
3.4
Sequentiediagram van de communicatie bij ophalen van een beeld. . . . . . .
15
3.5
Toestandsmachines voor de uitvoering van een ‘laad’ actie. . . . . . . . . . . .
16
3.6
Sequentiediagram van de communicatie bij afbeelden van een beeld. . . . . .
16
3.7
Toestandsmachines voor de uitvoering van een ‘afbeeld’ actie. . . . . . . . . .
17
3.8
Vereenvoudigd klassendiagram van de wrapper en de geneste klasse NativeMethods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
Screenshot van BDControl. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
3.10 De klassen voor project- en beeldbeheer. . . . . . . . . . . . . . . . . . . . . .
26
3.11 De sterk vereenvoudigde MainForm-klasse. . . . . . . . . . . . . . . . . . . . .
29
3.12 De BDNavigator-component en een screenshot. . . . . . . . . . . . . . . . . .
30
3.13 De vereenvoudigde klasse BDImageViewController. . . . . . . . . . . . . . .
32
3.14 Een screenshot van de BDImageViewController. . . . . . . . . . . . . . . . .
33
3.15 Voorbeeld van een titelbalkje. . . . . . . . . . . . . . . . . . . . . . . . . . . .
34
3.16 De hi¨erarchische structuur van de actieklassen. . . . . . . . . . . . . . . . . .
35
3.9
75
Lijst van figuren
76
3.17 Het gelaagde communicatiemodel. . . . . . . . . . . . . . . . . . . . . . . . .
40
3.18 De klasse Communicator.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
40
3.19 De statische hulpklasse Log. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
45
3.20 Tijdsdiagram van de signalen voor communicatie met de FPGA. . . . . . . .
53
3.21 Het schema van de mini-programmer. . . . . . . . . . . . . . . . . . . . . . .
57
3.22 De mini-programmer opgebouwd op een breadboard. . . . . . . . . . . . . . .
58
4.1
Illustratie bij een vertraagde tijdsbasis. . . . . . . . . . . . . . . . . . . . . . .
60
4.2
Het signaal waarop getriggerd wordt. . . . . . . . . . . . . . . . . . . . . . . .
61
4.3
De klasse PowerMeasurement . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
4.4
PowerMeasureManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
4.5
Het nieuwe interface-element voor de vermogenmeting. . . . . . . . . . . . . .
65
4.6
Een meetvoorbeeld. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
66
4.7
Het vermogenverbruik van de laatste lokale meting. . . . . . . . . . . . . . . .
67
A.1 Een overzicht van de applicatie BDControl. . . . . . . . . . . . . . . . . . . .
70
Lijst van tabellen 3.1
Inputstimuli voor bootloadermodus. . . . . . . . . . . . . . . . . . . . . . . .
55
4.1
Voedingslijnen en shuntweerstanden . . . . . . . . . . . . . . . . . . . . . . .
60
4.2
Eindtijden van de selectiepuls van de stuursignalen. . . . . . . . . . . . . . . .
62
77