05.09.2013 Views

2.3 Relaxatie-oscillator

2.3 Relaxatie-oscillator

2.3 Relaxatie-oscillator

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Afstudeeropdracht<br />

Saxion Hogeschool Enschede<br />

Meting van de kindruk tijdens vioolspel<br />

Marc Nijdam<br />

Begeleider: ir. ing. E. Bouwmeester<br />

Studierichting elektrotechniek<br />

December 2004


Lucas v. Leyden, 1524, gravure, (detail van) “Musicerend paar”


Inhoudsopgave<br />

Pagina<br />

Hoofdstuk 1 Inleiding<br />

1.1 Meting van de kindruk tijdens vioolspel............................... 6<br />

1.2 Voorwaarden bij het meten.................................................... 6<br />

1.2.1 Synchronisatie met metronoom........................................ 6<br />

1.2.2 Muziek/midi omgeving..................................................... 7<br />

1.<strong>2.3</strong> Sequencer.......................................................................... 7<br />

1.3 Ontwerp................................................................................. 8<br />

1.4 Systeem.................................................................................. 8<br />

Hoofdstuk 2<br />

2.1 Drukmeting - krachtmeting................................................... 9<br />

2.1.1 Soorten meetsensoren....................................................... 9<br />

2.1.2 PZT-sensor als krachtsensor............................................. 11<br />

2.2 Oscillatoren........................................................................... 11<br />

2.2.1 Harmonische <strong>oscillator</strong>..................................................... 12<br />

2.2.1.1 Complexe Impedantie van de Sensor.......................... 12<br />

2.2.1.2 Pierce Oscillator.......................................................... 15<br />

2.2.1.3 Overdracht................................................................... 17<br />

2.2.1.4 Berekening van R1, C1 en C2..................................... 19<br />

2.2.1.5 Wijzigingen Pierce en Simulatie................................. 22<br />

2.2.1.6 Metingen bij de resonantiefrequentie.......................... 24<br />

2.2.1.7 Serieresonantie............................................................ 24<br />

<strong>2.3</strong> <strong>Relaxatie</strong>-<strong>oscillator</strong>................................................................ 25<br />

<strong>2.3</strong>.1 Capaciteitsverandering ten gevolge van druk.................. 26<br />

<strong>2.3</strong>.2 Meetcircuit....................................................................... 28<br />

Hoofdstuk 3 Data-acquisitie en –verwerking.......................................................... 29<br />

3.1 Microcontroller..................................................................... 29<br />

3.1.1 Gestuurde oscillatie......................................................... 30<br />

3.1.2.1 Omzetting naar midi................................................... 32<br />

3.1.2.2 Linearisatie................................................................. 35<br />

3.1.3 Calibratie......................................................................... 37<br />

3.1.4 Complete schakeling....................................................... 39<br />

3.2 PC-Software......................................................................... 40<br />

Hoofdstuk 4 Mechanische constructie en realisatie................................................ 43<br />

4.1 Specificaties van het systeem............................................... 45<br />

4.1.1 Meetbereik....................................................................... 45<br />

4.1.2 Gevoeligheid.................................................................... 45<br />

4.1.3 Meetfrequentie................................................................. 45<br />

4.1.4 Omgevingsinvloeden....................................................... 45<br />

4.1.5 Massa van het meetapparaat............................................. 45


Hoofdstuk 5 Meetresultaten..................................................................................... 46<br />

5.1 Inzichten uit de meetresultaten............................................. 48<br />

Hoofdstuk 6 Conclusies en aanbevelingen.............................................................. 49<br />

6.1 Conclusies............................................................................. 49<br />

6.2 Aanbevelingen...................................................................... 49<br />

Appendix<br />

Sourcecode van het stuurprogramma voor de AT89C2051................ 51<br />

Sourcecode van het midifile-conversieprogramma............................ 64<br />

MIDI File standaard........................................................................... 75<br />

Lijst van literatuur en andere bronnen................................................ 86


1 Inleiding<br />

Door de eeuwen heen heeft zowel de klassieke muziek als het vioolspel een bijzonder grote ontwikkeling<br />

doorgemaakt. Vanwege de ontwikkeling in de muziek werden de technische vaardigheden die van<br />

de violisten werden gevraagd ook steeds groter. Een belangrijke basis die deze ontwikkeling mogelijk<br />

maakte, is de manier waarop het instrument werd vastgehouden. Immers deze bepaalt de stabiliteit van<br />

het instrument tijdens het spel, waardoor de musicus niet geremd hoeft te worden in het weergeven<br />

van zijn interpretatie.<br />

Tegenwoordig tekent zich echter ook een schaduwzijde af aan deze ontwikkeling. Bij sommige violisten<br />

ontstaan lichamelijke klachten, vanwege overbelasting van gewrichten en spieren. Om meer<br />

inzicht in de oorzaken hiervan te krijgen, hebben we getracht een meetapparaat te ontwikkelen, dat tot<br />

doel had bepaalde bewegingen te kunnen meten tijdens het spel. De bewegingen die we onderzoeken,<br />

hebben te maken met het moment waarop de viool werd vastgehouden met de kin of de kaak en met<br />

welke kracht dat gebeurde.<br />

Drie belangrijke aspecten worden daarbij behandeld:<br />

• Het type sensor en het bijbehorende algorithme om de kracht te meten.<br />

• De software die de data verwerkt.<br />

• De mechanische constructie waarin deze sensor voorkomt.<br />

Hoewel de meetresultaten die uit dit onderzoek voortkomen niet direct van betekenis zijn voor de technische<br />

kant van dit verslag, geven ze wel inzicht in de werking van het apparaat en de eigenschappen<br />

waaraan het moet voldoen. Daarom is hieraan hoofdstuk 5 gewijd.<br />

Tenslotte worden er conclusies getrokken en aanbevelingen gedaan die uit de ontwikkeling van het apparaat<br />

zijn voortgekomen.<br />

5


1.1 Meting van de kindruk tijdens het vioolspel<br />

Op de moderne viool zit een kinhouder (afb. 1.1) gemonteerd. Deze kinhouder is een opstaand deel<br />

waar een rand op zit. De kaak of kin van de violist valt over deze rand, waardoor de violist het instrument<br />

daarmee kan vastklemmen en kan voorkomen dat het weggetrokken wordt door de linkerhand<br />

tijdens het spel. Aan de onderkant van het instrument zorgt op zijn beurt het sleutelbeen voor steun.<br />

Afb. 1.1. Kinhouder<br />

(van www.viva-sas.com)<br />

Afb. 1.2. Kracht op de viool<br />

(uit “6 Lessons w. Menuhin”)<br />

Tijdens het spelen op een viool hoeft deze kindruk niet altijd aanwezig te zijn, omdat de linkerhand het<br />

instrument ook kan dragen. Bij sommige bewegingen echter kan de linkerhand niet tegelijkertijd zowel<br />

het instrument dragen als een nieuwe stand opzoeken. Op deze momenten is het van groot belang<br />

dat het instrument wordt vastgeklemd tussen de kaak en het sleutelbeen (afb. 1.2). Deze spanning kan<br />

worden losgelaten zodra de linkerhand de dragende functie van het instrument weer kan overnemen.<br />

We zijn erin geïnteresseerd op welke momenten deze overgang plaatsvindt. En hoe groot deze kracht<br />

is.<br />

Voor het meten van deze kracht volstaat het de kinhouder zo te veranderen, dat er tussen de kinhouder<br />

en de viool een krachtsensor gemonteerd kan worden, waarmee de neerwaartse kracht op de kinhouder<br />

op een krachtsensor kan worden overgebracht.<br />

1.2 Voorwaarden bij het meten<br />

Er bestaan talloze soorten kinhouders. Ze onderscheiden zich in vorm en in hoogte. Elke speler heeft<br />

een voorkeur voor een bepaalde vorm. Deze hangt samen met de lichaamsbouw. Deze bouw heeft<br />

tevens invloed op de hoogte von de kinhouder. Als deze immers te laag is, moet het hoofd een ongezonde<br />

houding aannemen om het instrument te kunnen vasthouden.<br />

Aangezien we verschillende proefpersonen wilden laten spelen, was het nodig de speciale kinhouder<br />

zo te construeren dat hij in hoogte verstelbaar was, om niet meetwaarden te krijgen die door een<br />

krampachtige houding veroorzaakt werden. Daarbij is wat de vorm aangaat een compromis genomen.<br />

We vonden het kunnen aanpassen van de hoogte belangrijker dan de vorm.<br />

1.2.1 Synchronisatie met metronoom<br />

Als iemand zonder overbodige spanningen speelt, zijn de momenten waarop verandering van kracht<br />

wordt uitgeoefend op de kinhouder, meestal gerelateerd aan bepaalde technische moeilijke passage’s in<br />

de muziek. Om te kunnen analyseren op welke plaatsen deze geweest zijn, moeten we de geregistreer-<br />

6


de data vergelijken met de overeenkomstige plaats in de muziek. Als we de gegevens van meerdere<br />

personen ook onderling willen vergelijken is het handig wanneer iedereen in hetzelfde tempo speelt.<br />

Dit leidt ertoe dat een computer het tempo dicteert waarin gespeeld wordt.<br />

1.2.2 Muziek/midi omgeving<br />

De meetdata worden uiteindelijk weergegeven onder de gespeelde notenbalk. (figuur 1.3). Achteraf<br />

kunnen we dan een analyse maken op welke momenten in de muziek er spieren werden aangespannen.<br />

Figuur 1.3. representatie van meetwaarden<br />

De bovenstaande grafiek hebben we in een aantal stappen verkregen:<br />

• In het meetapparaat met de krachtsensor zit een een microcontroller, die de meting uitvoert en<br />

de data produceert.<br />

• Voor de transmissie van de data van het meetapparaat naar de computer gebruik we een bestaand<br />

formaat dat geschikt is voor dit doel: de MIDI-standaard in combinatie met de MIDI File<br />

standaard, die tijdcode-informatie toevoegt. Deze standaard voorziet in een seriële datatransmissie,<br />

waarvan de baudrate en de datastructuur vastligt.<br />

• Op de pc kunnen we met de juiste software deze data opnemen. De software voorziet de data,<br />

op het moment dat deze de PC binnenkomen, van een tijdstempel, tijdcode genaamd.<br />

• De opgenomen data worden geconverteerd, zodat een spreadsheet deze kan importeren.<br />

• Een spreadsheet geeft de data als grafieken weer en toont het notenschrift daarboven.<br />

1.<strong>2.3</strong> Sequencer<br />

De software die de MIDI-data opneemt, heet ook wel een sequencer. Zulke programma’s zijn erg handig<br />

om meetdata te verzamelen. Voor verdere verwerking is deze software minder geschikt, omdat een<br />

sequencer niet de mogelijkheid heeft om data van bijvoorbeeld verschillende spelers eenvoudig onderling<br />

te kunnen vergelijken en andersom een spreadsheet niet met data van een sequencer kan omgaan.<br />

We kunnen bij de meeste programma’s wel alle data van een sequencer exporteren naar een MIDI-File<br />

formaat, welke ook gedocumenteerd (Zie hiervoor de appendix) en gestandaardiseerd 1 is. Dit geeft ons<br />

de mogelijkheid met een ander programma de data te kunnen analyseren. Speciaal hiervoor hebben<br />

we een programma geschreven dat MIDI-files omzet naar een tekstbestand, dat we vervolgens in een<br />

spreadsheet kunnen importeren.<br />

7


1.3 Ontwerp<br />

Uit de hiervoor genoemde voorwaarden hebben we onze aandacht op een zestal punten gericht:<br />

• Een geschikte sensor vinden.<br />

• Een speciale kinhouder fabriceren waarin die sensor geplaatst kan worden, zodanig dat we de<br />

drukkracht op de houder kunnen meten.<br />

• De hardware rondom de microcontroller en de sensor realiseren.<br />

• Software voor het debuggen tijdens de testfase.<br />

• Software voor de microcontroller waarop de sensor zit aangesloten.<br />

• Software die een MIDI-file omzet naar een tekstbestand.<br />

Voordat we de speciale kinhouder gemaakt hebben, zijn we eerst gaan kijken welke sensor we konden<br />

gaan gebruiken. De afmeting en de vorm daarvan heeft namelijk invloed op het ontwerp van de kinhouder.<br />

In hoofdstuk twee wordt er op die sensor dieper ingegaan.<br />

Tijdens de testfase hadden we de mogelijkheid de software van de microcontroller te debuggen. Hiervoor<br />

hadden we een programma, geschreven in C, dat bytes van de MIDI-interface inlas, en uitvoerde<br />

naar het scherm. In de microcontroller hadden we vervolgens een routine die de inhoud van een bepaald<br />

register via de seriële poort naar de computer, waarop dit debugprogramma draaide, kon sturen.<br />

De software voor het verwerken van de sensordata in de microcontroller hebben we in assembler<br />

geschreven en niet in C. De reden hiervoor is dat de gebruikte microcontroller een Atmel AT89C2051,<br />

deel van de familie van de MCS ® 51, niet uitblinkt in snelheid. Om toch nauwkeurig te kunnen meten,<br />

was het gebruik van alle timers en een aantal interruptleidingen noodzakelijk. In assembly konden we<br />

zo efficiëntere code schrijven. Bijvoorbeeld doordat we direct konden zien hoeveel clockcycli bepaalde<br />

instructies zouden duren.<br />

Het converteringsprogramma is in C++ geschreven. Met behulp van de fileselector wordt met dit<br />

windowsprogramma een MIDI File geselecteerd, waarin zich MIDI controller data bevinden. De<br />

MIDI controller data zet het programma om naar een standaard, met komma gescheiden, tekstbestand.<br />

Ook de naam van de uitvoerfile wordt met behulp van de standaard windows-fileselector aangegeven.<br />

De converteringsroutine heeft een systeem gebaseerd op het zetten van vlaggen voor alle toestanden<br />

waarin de gelezen character kan voorkomen. Deze volgt de MIDI File standaard (Ondersteund zowel<br />

type 0 als 1)<br />

1.4 Systeem<br />

Het uiteindelijke systeem bestaat uit:<br />

• De speciale kinhouder met de microcontroller en de sensor.<br />

• Een computer, voorzien van een sequencerprogramma waarmee we MIDI-data kunnen opnemen<br />

en het converteringsprogramma om deze data importeerbaar te maken in een spreadsheetprogramma.<br />

8


2.1 Drukmeting - krachtmeting<br />

Hoofdstuk 2 gaat nader in op de sensor waarvan gebruik wordt gemaakt om de drukkracht te meten.<br />

Ook beschrijft het op welke wijze dat is gerealiseerd. Tijdens de zoektocht naar een geschikte sensor,<br />

zijn we uiteindelijk uitgekomen op een PZT-sensor die aan een aantal gunstige voorwaarden voldoet<br />

zoals: een eenvoudige interface naar een schakeling voor verdere dataverwerking, geschikt voor het<br />

meten van een absolute kracht (een soort DC-meting), geringe massa en afmeting. Hiertegenover staan<br />

ook een aantal minder gunstige eigenschappen, zoals onder andere de temperatuurafhankelijkheid<br />

en het ontbreken van specificaties van de producent. Daarnaast is de programmatuur die de verdere<br />

dataverwerking voor zijn rekening neemt ingewikkelder, omdat er vanwege mechanische koppeling<br />

stoorsignalen van het instrument worden opgepikt, die de meetwaarden kunnen beïnvloeden. In de<br />

literatuur is er veel informatie over PZT te vinden. Dit gaat echter maar heel zelden over het gebruik<br />

van PZT als sensor voor het meten van een absolute kracht.<br />

2.1.1 Soorten meetsensoren<br />

Op het internet vinden we onder andere op de homepage van Farnell (www.farnell.com) een overzicht<br />

van leverbare krachtsensoren, waarvan we er hier een paar opnoemen. Daarnaast volgt er een beschrijving<br />

van krachtsensoren die van flexibele folie gemaakt worden. Juist als er in het ontwerp niet veel<br />

ruimte voor een sensor beschikbaar is, zou overwogen kunnen worden deze te gebruiken.<br />

Honeywell FSL05N2C: (figuur 2.1 maten zijn in mm)<br />

Figuur 2.1 Honeywell Forcesensor with specifications<br />

De bijbehorende beschrijving:<br />

“The FS Series Sensors provide precise, reliable force sensing performance in a compact commercial<br />

grade package. The sensor ... utilizes a specialized piezoresistive micro-machined silicon sensing element.<br />

The low power, unamplified, noncompensated Wheatstone bridge circuit design provides inherently<br />

stable mV outputs over the force range.<br />

Force sensors operate on the principle that the resistance of silicon implanted piezoresistors will increase<br />

when the resistors flex under any applied force. The sensor concentrates force from the application,<br />

through the stainless steel plunger, directly to the silicon sensing element. The amount of resistance<br />

changes in proportion to the amount of force being applied. This change in circuit resistance results<br />

in a corresponding mV output level...”<br />

9


Honeywell FSG15N1A: (figuur 2.2 maten zijn in mm)<br />

Figuur 2.2 Honeywell Forcesensor<br />

Deze krachtsensor heeft een iets andere behuizing, maar is wat betreft het grotere meetbereik (0-<br />

1500g) geheel vergelijkbaar met de eerder genoemde Honeywell FSL05N2C.<br />

Omdat de sensor op een plaats gemonteerd gaat worden waar maar heel weinig ruimte is, hebben we<br />

naar nog dunnere sensoren gezocht. Zo kwamen we bij de firma Tekscan. Deze maakt sensoren die uit<br />

twee flinterdunne folies bestaan, waartussen een speciale geleidende inkt zit. Bijvoorbeeld de Flexi-<br />

Force A210 (figuur <strong>2.3</strong>)<br />

Figuur <strong>2.3</strong> FlexiForce krachtsensor<br />

Deze sensor heeft een dikte van 0.127 mm. Bij toenemende kracht neemt de weerstand af van 20 MΩ<br />

naar ongeveer 20 kΩ. De beschrijving van het bedrijf:<br />

“With its paper-thin construction, flexibility and force measurement ability, the FlexiForce force sensors<br />

can measure force between almost any two surfaces and is durable enough to stand up to most environments.<br />

FlexiForce has better force sensing properties, linearity, hysteresis, drift and temperature<br />

sensitivity than any other thin-film force sensors.<br />

The new and improved FlexiForce A201 force sensor is an ultra-thin, flexible printed circuit. It is 14<br />

mm wide and 203 mm in full length. The active force sensing area is a 9.5 mm diameter circle at the<br />

end of the force sensor. The force sensors are constructed of two layers of substrate, such as a polyester<br />

film. On each layer, a conductive material (silver) is applied, followed by a layer of pressuresensitive<br />

ink. ...The active sensing area is defined by the silver circle on top of the pressure-sensitive<br />

ink. Silver extends from the sensing area to the connectors at the other end of the sensor, forming the<br />

conductive leads. ....”<br />

Het bedrijf heeft drie soorten krachtsensoren, varierend in het maximale meetbereik: (4.4N,111N en<br />

444N)<br />

Alle hierboven genoemde sensoren hebben als eigenschap dat de weerstand verandert als er een kracht<br />

op wordt uitgeoefend.<br />

Naast de kant-en-klare oplossingen van hierboven, bestaan er ook andere methoden om kracht te meten.<br />

Meestal gaat dat via een mechanische omzetting van kracht naar verplaatsing. Dit gebeurt bijvoorbeeld<br />

bij sommige elektronische weegschalen waarin twee of meer platen zitten die als condensator<br />

fungeren. Via de capaciteit van deze condensator, een maat voor de afstand tussen de platen, kan de<br />

kracht gemeten worden, terwijl een veer de platen van elkaar houdt.<br />

10


Een andere manier om kracht te meten is door de spanning te meten die een PZT-sensor (figuur 2.4)<br />

opwekt als de aangelegde kracht erop verandert 2 . Deze spanning ontstaat door ionen die een andere<br />

plaats innemen in het kristal ten gevolge van een kracht.<br />

<br />

Figuur 2.4 PZT-sensor<br />

<br />

Willen we de spanning meten die een PZT-sensor opwekt, dan moet het meetcircuit een erg hoge ingangsimpedantie<br />

hebben. We kunnen op deze manier echter niet een onveranderende kracht (statisch)<br />

meten, omdat er altijd heel kleine lekstromen zijn. Ook als op deze statische kracht een wisselende<br />

kracht gesuperponeerd wordt (dynamisch), is deze meetmethode ongeschikt, omdat over een langere<br />

tijd de statische kracht vanwege lekstromen uit het meetsignaal verdwijnt. We moeten dus een andere<br />

methode vinden als we met een PZT-sensor statische kracht willen meten.<br />

2.1.2 PZT-sensor als krachtsensor<br />

Voor het bepalen van krachtverloop op een PZT-sensor meten we daarom niet de opgewekte spanning,<br />

maar de resonantiefrequentie. Deze neemt af bij belasting 3 . Het ligt dus voor de hand een <strong>oscillator</strong>schakeling<br />

te nemen waarin de PZT-sensor als frequentiebepalend element wordt opgenomen.<br />

2.2 Oscillatoren<br />

Er zijn twee belangrijke klassen van <strong>oscillator</strong>en: harmonische <strong>oscillator</strong>en (figuur 2.5) en relaxatie<strong>oscillator</strong>en<br />

(figuur 2.6). Harmonische <strong>oscillator</strong>en met een kristal hebben als eigenschap dat er een<br />

interne en periodieke uitwisseling van energie plaatsvindt, zoals bijvoorbeeld bij een spoel in serie met<br />

een condensator mogelijk is. Bij relaxatie-<strong>oscillator</strong>en wordt er steeds omgeschakeld tussen twee a-stabiele<br />

toestanden, waarin het tijdbepalende element gevormd wordt door bijvoorbeeld een RC-tijd.<br />

-<br />

+<br />

Figuur 2.5 Principeschema<br />

harmonische osicllator (Pierce)<br />

Figuur 2.6 Principeschema<br />

relaxatie-<strong>oscillator</strong><br />

-<br />

+<br />

11


2.2.1 Harmonische <strong>oscillator</strong><br />

Omdat we de PZT-sensor bij de resonantiefrequentie willen laten oscilleren, moeten we dus een configuratie<br />

met een harmonische <strong>oscillator</strong> bekijken. Belangrijke voorwaarden voor harmonische oscillatie<br />

zijn de zogenaamde Barkhausen criteria: In een teruggekoppelde lus waarin een frequentieselecterend<br />

en een versterkend element zit, moet de totale fasedraaiing gelijk zijn aan 0 + 2k (k=0,1....). Verder<br />

moet de modulus van de totale versterking gelijk aan 1 zijn voor stabiele oscillatie.<br />

In de praktijk zal een frequentieselecterend element meestal als eigenschap hebben dat er wat energie<br />

verloren gaat. Om dit tegen te gaan, en de oscillatie in stand te houden, moet de versterker dus wat<br />

energie toevoeren. Het verlies van het element wordt uitgedrukt in de kwaliteitsfactor Q en is omgekeerd<br />

evenredig met de kwaliteit: een element met een hoge Q zal weinig energie verliezen.<br />

2.2.1.1 Complexe Impedantie van de Sensor<br />

Het gedrag van de sensor kunnen we afleiden met behulp van de (complexe) impedantie mits in het<br />

gekozen frequentiedomein het netwerk zich lineair gedraagt. Dat betekent dat rond de oscillatiefrequentie<br />

de versterking niet of weinig varieert. Aan de hand daarvan kunnen we de <strong>oscillator</strong> instellen<br />

om aan de criteria voor oscillatie te voldoen. Om de impedantie te berekenen moeten we de elektrische<br />

grootheden vinden van het vervangingsschema van de sensor. De resonantie wordt bepaald door de<br />

mechanische en elctrische eigenschappen van de sensor. We kunnen de mechanische grootheden omzetten<br />

naar hun equivalente elektrische grootheden 4 : Trillende massa naar zelfinductie (L 1 ), elasticiteit<br />

van het kristal naar capaciteit (C 1 ), en wrijvingsverliezen naar weerstand (R 1 ). Parallel aan deze drie<br />

grootheden bevindt zich nog een condensator, die de parasitaire capaciteit vormt van de aansluitingen<br />

en het oppervlak van de elektroden.<br />

R S<br />

2.16kΩ<br />

L S<br />

1.586H<br />

PZT Sensor<br />

C 0<br />

5.4nF<br />

Figuur 2.7 Vervangingsschema PZT-sensor<br />

C S<br />

411.7pF<br />

In het bovenstaande vervangingsschema, figuur 2.7, zien we voor de grondtoon een LCR-keten met<br />

parallel daaraan capaciteit C 0 . Het gedrag veroorzaakt door boventonen op de grondtoon, zouden we in<br />

het vervangingsschema kunnen opnemen door parallel aan de LCR-keten, andere LCR-ketens aan te<br />

sluiten. Omdat we ons toch alleen maar willen beperken tot frequenties in de buurt van de grondtoon,<br />

is het niet nodig het vervangingsschema uit te breiden.<br />

De sensor is met een HP network analyzer (HP 8510C) doorgemeten. Na opgave van de configuratie<br />

waarin deze onderdelen in het vervangingsschema staan, heeft deze de waarden van L s , C s , R s en C 0<br />

berekend:<br />

R S =2.16kΩ L S =1.586H C S =411.7pF C 0 =5.4nF<br />

12


Het apparaat meet de serie- en parallelresonantiefrequentie, waaruit het de waarden in het vervangingsschema<br />

kan berekenen. Aanvankelijk lieten we de HP network analyzer over een heel groot<br />

frequentiegebied, tot 1 MHz doormeten, om vervolgens het frequentiegebied te kunnen afbakenen,<br />

waarbinnen in ieder geval de serie- en parallelresonantiefrequentie van de grondtoon lagen. Na een<br />

aantal pogingen lag het meetbereik uiteindelijk tussen 4.8 en 6.8 kHz. Buiten deze frequenties kon er<br />

geen vervangingsschema bepaald worden.<br />

De totale impedantie Z X volgt uit de impedanties van de samenstellende componenten. Naast R S zijn<br />

dat:<br />

Z Ls( ~ ) = j $ ~ $ LS<br />

, met ω de frequentie in rad/s (2.1)<br />

Z ( )<br />

1<br />

Cs ~ = Z ( )<br />

1<br />

C0 ~<br />

j $ ~ $ C<br />

= (2.2)(<strong>2.3</strong>)<br />

j $ ~ $ C0<br />

S<br />

Uit de voorgaande waarden berekenen we de vervangingsimpedantie Z X van de PZT-sensor, bij een ω<br />

in de buurt van een resonantiefrequentie ω r (binnen het afgebakende meetbereik):<br />

1<br />

4<br />

~ r=<br />

~ 3.9 10<br />

rad<br />

r.<br />

$<br />

LS$ C<br />

s<br />

(2.4)<br />

S<br />

Z X(<br />

~ ) =<br />

1<br />

1<br />

+<br />

1<br />

RS+ Z LS( ~ ) + Z CS( ~ ) Z C0(<br />

~ )<br />

Als we Z X in een grafiek uitzetten tegen ω moeten we, omdat het om een complexe grootheid gaat,<br />

twee functies tekenen. Een voor |Z X | en een voor het argument van Z X .<br />

We tekenen de grafiek in de buurt van ω r zodat we een duidelijk beeld krijgen hoe de impedantie verandert.<br />

13<br />

(2.5)<br />

absZ X (ω)=|Z X (ω)| argZ X (ω)=arg(Z X (ω)) (2.6)(2.7)


absZ x<br />

24<br />

22<br />

20<br />

18<br />

16<br />

14<br />

12<br />

10<br />

50<br />

(in kΩ)<br />

8<br />

60<br />

(in ˚)<br />

6<br />

4<br />

2<br />

0 2 10 4<br />

0<br />

4 10 4<br />

Impedantiecurve Zx<br />

6 10 4<br />

<br />

x<br />

8 10 4<br />

(in rad/s)<br />

Figuur 2.8. Verandering impedantie<br />

1 10 5<br />

20<br />

10<br />

0<br />

10<br />

20<br />

30<br />

40<br />

70<br />

80<br />

90<br />

100<br />

1.2 10 5<br />

Uit grafiek 2.8 blijkt dat |Z X | naar oneindig gaat als het 0 rad/s nadert. Hij wordt pas zichtbaar na 1·10 4<br />

rad/s. Hier bedraagt zijn waarde 20 kΩ. Het argument loopt van -90° tot 4.1°.<br />

Ook kunnen we aflezen dat de impedantie van de sensor twee extremen heeft rond de 4·10 4 rad/s. Een<br />

lokaal minimum en een lokaal maximum. We bekijken deze punten wat nauwkeuriger vanaf 3.8·10 4<br />

rad/s tot 4.2·10 4 rad/s, (Figuur 2.9)<br />

absZ x<br />

(in kΩ)<br />

12<br />

11<br />

10<br />

9<br />

8<br />

7<br />

6<br />

5<br />

4<br />

3<br />

2<br />

1<br />

3.7 10 4<br />

0<br />

3.8 10 4<br />

Impedantiecurve Zx<br />

3.9 10<br />

Figuur 2.9. Detail verandering impedantie<br />

4<br />

4 10 4<br />

4.1 10 4<br />

<br />

x (in rad/s)<br />

4.2 10 4<br />

20<br />

10<br />

-10<br />

-20<br />

-30<br />

-40<br />

-50<br />

-60<br />

-70<br />

-80<br />

-90<br />

argZ x<br />

0<br />

4.3 10 4<br />

-100<br />

argZ x<br />

(in ˚)<br />

14


De (absolute) impedantie is minimaal bij ω=3.9·10 4 rad/s. Deze is:<br />

4<br />

absZ (3.9 10<br />

rad<br />

X $<br />

s<br />

) . 1.8kX<br />

Dit punt, waar de impedantie minimaal is, noemen we de serie-resonantiefrequentie f s , omdat daar de<br />

frequentiebepalende elementen L s , C s en R s in serie staan.<br />

De impedantie is maximaal bij:<br />

4<br />

absZ (4.09 10<br />

rad<br />

X $<br />

s<br />

) . 11.3kX<br />

Deze frequentie van 4.09·10 4 rad/s (6.50 kHz) noemen we de parallel-resonantiefrequentie f p . We<br />

mogen echter dit alleen zo noemen als de verliezen minimaal zijn. Bij de PZT-sensor zijn die verliezen<br />

groot. R s speelt een grote rol. Dit punt noemen we f n : het punt waarbij de admittantie minimaal is (of<br />

de impedantie maximaal).<br />

Uit de impedantie van dit systeem vinden we ook twee frequenties waar de impedantie zuiver reëel is.<br />

Deze liggen bij 3.96·10 4 rad/s (6.30 kHz) en bij 4.02·10 4 rad/s (6.39 kHz). Deze frequenties noemen<br />

we respectievelijk resonantiefrequentie f r en anti-resonantiefrequentie f a . Tussen f r en f a gedraagt de<br />

sensor zich inductief. Daarbuiten capacitief.<br />

De kwaliteitsfactor Q van de serietak L s , C s en R s bij:<br />

f s =6.2kHz<br />

is gedefinieerd als 4 :<br />

Q<br />

2 fS LS<br />

=<br />

r $ $<br />

R<br />

(2.8)<br />

S<br />

Als we de bekende waarden invullen krijgen we:<br />

Q=28.6<br />

Deze Q waarde is bijzonder laag voor de PZT-sensor. Quartzkristallen, met een vergelijkbaar vervangingsschema,<br />

hebben meestal een waarde die een orde 1000 tot 10000 groter is.<br />

Een lage Q waarde is voor de <strong>oscillator</strong>schakeling ongunstig, omdat er veel energie moet worden<br />

toegevoerd om oscillatie op gang te houden. Bovendien gedraagt de sensor zich alleen tussen de frequenties<br />

f r en f a inductief. Dit betekent dat eigenlijk alleen daartussen de sensor een duidelijk werkpunt<br />

kan vinden. Omdat echter f r en f a tamelijk ver uit elkaar liggen, zal de <strong>oscillator</strong> niet erg stabiel op een<br />

frequentie kunnen werken.<br />

2.2.1.2 Pierce Oscillator<br />

Een veelgebruikte <strong>oscillator</strong>schakeling voor quartzkristallen is de Pierce <strong>oscillator</strong>. Een typische<br />

voorbeeld wordt hieronder gegeven. De <strong>oscillator</strong> bestaat uit een inverter en het terugkoppelnetwerk<br />

R 1 C 1 X 1 C 2 .<br />

Door de looptijdvertraging in de inverter is het faseverschil tussen het uitgangs- en het ingangssignaal<br />

iets meer dan 180°. Het terugkoppelnetwerk hoeft voor oscillatie niet de volledige 180° faseverschil te<br />

15


geven. Als de inverter ook voldoende versterkt, is aan de Barkhausencriteria voldaan en zal de <strong>oscillator</strong><br />

een harmonisch signaal opwekken 5 .<br />

Vaak wordt in de literatuur niet over de looptijdvertraging gesproken en neemt men aan dat de condensater<br />

C 1 een fasedraaiing van -90° geeft, en het kristal (de sensor) samen met C 2 voor de resterende<br />

-90° zorgt. Deze voorstelling is niet correct.<br />

Een typische Pierceschakeling (figuur 2.10) met inverter of nand-poort zien we in de volgende figuur:<br />

Rg<br />

10MΩ<br />

Ri<br />

C2<br />

10nF<br />

U1A<br />

CD4049<br />

X1<br />

PZT-Sensor<br />

C1<br />

10nF<br />

Figuur 2.10. Pierce <strong>oscillator</strong><br />

Deze <strong>oscillator</strong> bevat niet veel onderdelen. Hij wordt veelvuldig gebruikt als klokgenerator voor onder<br />

andere microprocessoren. Een voordeel van deze schakeling is dat de uitgangsamplitude groot is, zodat<br />

er direct een andere CMOS-poort op kan worden aangesloten. Bovendien is de ingangsimpedantie<br />

hoog. Daar staan echter ook een aantal nadelen tegenover zoals de lage versterking en de looptijdvertraging.<br />

Overigens wordt in sommige schakelingen R 1 weggelaten, omdat de uitgangsimpedantie van het IC de<br />

weerstand kan vormen van de R i C 1 integrator.<br />

Om de sensor te kunnen laten oscilleren moeten we de juiste waarden vinden voor de drie overige<br />

componenten. Voor de dimensionering van C 1 , C 2 en R 1 zijn er enkele regels, waaruit blijkt hoe deze<br />

waarden samenhangen 6 . Bij paragraaf 2.2.1.4 gaan we iets dieper in over het verband tussen R 1 , C 1<br />

en C 2 . Overigens wordt de dimensionering meestal overgelaten aan de fabrikant, omdat de sensor (bij<br />

kwartskristallen) maar beperkt belast kan worden. De waarden voor C 1 , C 2 en R 1 hebben immers ook<br />

invloed op de stroom door de sensor.<br />

Omdat de inverter een niet-lineaire versterking heeft, nemen we een R g op, zodat daarmee een geschikt<br />

werkpunt ingesteld kan worden. De grootte van R g hangt af van het type inverter. Voor CMOS wordt<br />

daarvoor geadviseerd een waarde te nemen tussen 22 kΩ en 100 MΩ. We nemen 10 MΩ. Omdat we de<br />

sensor bij een lage frequentie laten oscilleren, speelt de looptijdvertraging maar een heel geringe rol.<br />

Voor R 1 geldt dat als hij te groot is, het signaal teveel verzwakt zal worden. Als hij te klein is, kan het<br />

zijn dat de sensor teveel belast wordt. Uit de berekeningen verderop zal bovendien ook blijken, dat R 1<br />

grote invloed heeft op de totale fasedraaiing van het -netwerk. De voedingsspanning bedraagt hier 5<br />

V.<br />

Voor C 1 en C 2 geldt de vuistregel dat de twee capaciteiten parallel geschakeld, groter moeten zijn dan<br />

twee keer de parasitaire capaciteit in de sensor. Ze mogen echter ook niet te groot zijn, omdat dan het<br />

signaal teveel verzwakt. We proberen voor C 1 en C 2 een capaciteit van 10nF uit.<br />

In de praktijk oscilleert de bovenstaande schakeling uitstekend, echter bij een frequentie van 140kHz.<br />

Kennelijk een boventoon of andere trilmodus. In ieder geval veel hoger dan de bij het vervangingsschema<br />

behorende frequentie van ongeveer 6.3 kHz. De twee capaciteiten experimenteel vergroten of<br />

verkleinen brengt de frequentie niet omlaag.<br />

R1<br />

12kΩ<br />

16


Een mogelijke oorzaak dat de schakeling niet oscilleert op de verwachte frequentie, zou kunnen zijn<br />

dat de belasting van de sensor in het netwerk te groot is door de lage uitgangsimpedantie van de<br />

inverter. Dit kan een probleem vormen voor de sensor, die namelijk bij een te grote belasting invloed<br />

ondervindt van stoorresonanties (activity dips), waardoor de impedantie kan toenemen 7 .<br />

Met weerstand R 1 kunnen we de uitgang van de inverter die een lage en niet-lineaire uitgangsimpedantie<br />

heeft, verhogen. Hierdoor wordt de sensor minder gedempt.<br />

We proberen bovenstaande schakeling opnieuw uit. We nemen voor R 1 een instelbare weerstand, zodat<br />

we kunnen kijken wat voor gevolg dit heeft. Bij vergroten van de weerstand tot zelfs 100 kΩ krijgen<br />

we wel allemaal vreemde oscillaties, maar zeker geen harmonisch signaal in de buurt van de verwachte<br />

frequentie.<br />

2.2.1.3 Overdracht<br />

Om meer inzicht te krijgen in waarom dit circuit niet wil oscilleren op de verwachte frequentie, is het<br />

zinvol de overdracht te berekenen van het netwerk van figuur 2.10. Hierdoor kunnen we controleren<br />

op welke wijze aan de Barkhausen criteria kan worden voldaan. We noemen de instelbare weerstand<br />

van hierboven R 1 .<br />

Om de overdracht te berekenen maken we gebruik van het volgende netwerk (figuur 2.11):<br />

R 1<br />

Z C1<br />

Ingang<br />

C 1<br />

0<br />

C0 5.4n<br />

PZT Sensor<br />

Z C2<br />

Z x<br />

Rs 2.16k<br />

Ls 1.586<br />

Cs 411.7p<br />

0<br />

Figuur 2.11. Netwerk met sensor<br />

C 2<br />

Uitgang<br />

We bekijken hoe groot de overdracht is bij de genomen waarden van C 1 en C 2 van 10nF en een R 1 van<br />

12 kΩ uit de vorige Pierce-schakeling.<br />

er geldt:<br />

C 1 =10nF C 2 =10nF R 1 =12kΩ<br />

Z ( )<br />

1<br />

C1 ~ = en Z ( )<br />

1<br />

C2 ~<br />

j $ ~ $ C<br />

= (2.9)(2.10)<br />

1<br />

j $ ~ $ C2<br />

17


De totale vervangingsimpedantie van het netwerk onder weerstand R1 is dan:<br />

Z ( )<br />

1<br />

V ~ =<br />

(2.11)<br />

1<br />

1<br />

Z C1( ~ )<br />

+<br />

Z X( ~ ) + Z C2(<br />

~ )<br />

Vervolgens berekenen we de overdracht H(ω), die per definitie gelijk is aan de ingangsspanning gedeeld<br />

door de uitgangsspanning.<br />

H( ~ ) =<br />

1<br />

$<br />

1<br />

1<br />

R1<br />

Z X<br />

+<br />

( ~ )<br />

Z ( ) 1<br />

V ~ +<br />

Z C2(<br />

~ )<br />

(2.12)<br />

In de buurt van de resonantiefrequentie ziet de overdrachtsfunctie (figuur 2.12) er als volgt uit:<br />

absH<br />

0.09<br />

0.085<br />

0.08<br />

0.075<br />

0.07<br />

0.065<br />

0.06<br />

0.055<br />

0.05<br />

0.045<br />

0.04<br />

0.035<br />

0.03<br />

3.8 10 4<br />

3.9 10 4<br />

4 10 4<br />

(in rad/s)<br />

C 1 =10nF<br />

C 2 =10nF<br />

R1 =12kΩ<br />

4.1 10 4<br />

Figuur 2.12. overdracht sensor in de Pierceschakeling<br />

80<br />

85<br />

90<br />

95<br />

100<br />

105<br />

110<br />

115<br />

120<br />

125<br />

130<br />

135<br />

4.2 10 4<br />

140<br />

We zien duidelijk dat bij 10 nF er niet voldoende fasedraaiing is om aan een Barkhausencriterium<br />

te voldoen. We vergroten C 1 en C 2 om meer in de buurt van de 180° te komen. We nemen -enigszins<br />

experimenteel- de volgende waarden: C 1 =0.33μF, C 2 =68nF, R 1 =4.7kΩ.<br />

argH<br />

(in ˚)<br />

18


Hieruit volgt de volgende overdrachtscurve (figuur 2.13):<br />

absH<br />

0.003<br />

0.0025<br />

0.002<br />

0.0015<br />

0.001<br />

0.0005<br />

0<br />

3.8 10 4<br />

3.9 10 4<br />

4 10 4<br />

(in rad/s)<br />

C 1 =330nF<br />

C 2 =68nF<br />

R 1 =4.7kΩ<br />

4.1 10 4<br />

Figuur 2.13. overdracht sensor in de Pierceschakeling met andere componenten<br />

4.2 10 4<br />

Uit de grafiek blijkt dat met de grotere waarden voor C 1 en C 2 , we nu veel meer fasedraaiing kunnen<br />

krijgen. Deze is op zijn grootst 177°. De versterking is door de grotere condensatoren wel flink afgenomen.<br />

Bij het punt waar de fasedraaiing het grootst is, blijft er ongeveer nog maar 1/1000 deel van<br />

het oorspronkelijke signaal over. In de vorige situatie was dat 1/20 deel.<br />

Ook met de nieuwe waarden krijgen we de CMOS-inverter niet aan het oscilleren in de buurt van de 6<br />

kHz. De versterking kunnen we echter niet genoeg beïnvloeden.<br />

Omdat bovendien het meten aan de schakeling een steeds groter probleem wordt vanwege de belasting<br />

van de meetpennen, vervangen we de CMOS-inverter door twee op-amps. We kunnen een op-amp als<br />

spanningsvolger/versterker schakelen, waardoor die een veel hogere ingangsimpedantie krijgt. Immers<br />

weerstand R g is niet meer nodig. De andere op-amp moet dan het signaal in ieder geval inverteren en<br />

eventueel ook versterken. We laten onze keuze vallen op een TL084. Dit is een veel voorkomende opamp,<br />

zonder spannende eigenschappen.<br />

2.2.1.4 Berekening van R 1 , C 1 en C 2<br />

We hebben gezien dat C 1 en C 2 veel invloed hebben op de overdracht van het netwerk. Dit verband is<br />

iets eenvoudiger te begrijpen als we de impedantie van de sensor bij de resonantiefrequentie als zuiver<br />

reëel beschouwen. We nemen daarvoor het volgende schema (figuur 2.14)<br />

80<br />

90<br />

100<br />

110<br />

120<br />

130<br />

140<br />

150<br />

160<br />

170<br />

180<br />

argH<br />

(in ˚)<br />

19


De overdracht H(ω) is dan:<br />

<br />

R1<br />

R2<br />

C1 C2<br />

Figuur 2.14. Overdracht met reële R2<br />

H( ~ ) =<br />

1<br />

$<br />

1<br />

j $ ~ $ C 1 + j $ ~ $ R $ C<br />

1 + R 1$ (j $ ~ $ C1+ )<br />

1 + j $ ~ $ R2$ C2<br />

<br />

2 2 2<br />

[ 2 2 1 1 2 2 1 2]<br />

= 1/ 1 + j $ ~ $ R $ C + j $ ~ $ R $ C $ _1 + j $ ~ $ R $ C i + j $ ~ $ R $ C<br />

=<br />

1<br />

2<br />

1 - ~ $ R1$ R2$ C1$ C2+ j $ ~ $ (R2$ C2+ R1$ C1+ R1$ C 2)<br />

De tangens van het argument van H(ω) is dan:<br />

- ~ $ (R2$ C2+ R1$ C1+ R1$ C 2)<br />

tan(arg(H( ~ )) =<br />

2<br />

1 - ~ $ R1$ R2$ C1$ C2<br />

Bij de juiste instelling is ω 2 R 1 R 2 C 1 C 2 >>1, waardoor we dit mogen vereenvoudigen tot:<br />

20<br />

(2.13)<br />

R2 C2 R1 C1 R1 C2<br />

.<br />

$ + $ + $<br />

(2.14)<br />

~ $ R1$ R2$ C1$ C2<br />

Volgens het Barkhausencriterium moet de fasedraaiing 180° zijn. Omdat de teller ongelijk aan nul is,<br />

is deze fasedraaiing mogelijk als de noemer van het argument van H(ω) naar oneindig gaat. We zien<br />

dat hoe groter C 1 en C 2 worden des te dichter komt de fasedraaiing in de buurt van de 180°.<br />

In de praktijk is deze afleiding voor R 1 , C 1 en C 2 meestal niet houdbaar, omdat de versterker in de Pierce<strong>oscillator</strong><br />

bij zijn werkpunt niet zuiver reëel is. Bovendien kunnen we uit het bovenstaande niet een<br />

eenduidige bepaling voor R 1 , C 1 en C 2 vinden. Een betere manier om uit het Barkhausencriterium de<br />

waarden voor R 1 , C 1 en C 2 te bepalen is als we het criterium volgens de definitie gaan toepassen. Daaruit<br />

volgt dat de (complexe) versterking A van het versterkend element gelijk moet zijn aan 1/H(ω):<br />

(Zie voor H(ω) vergelijking 12 en figuur 2.11.)<br />

A =<br />

1 (2.15)<br />

H( ~ )<br />

We gaan uit van een bepaalde, door de fabrikant gespecificeerde waarde voor de fasedraaiing van A.<br />

Vaak kunnen we uit een grafiek of tabel aflezen hoeveel de fasedraaiing φ bij een bepaalde frequentie<br />

ω is. Voor de absolute versterking |A| kunnen we binnen bepaalde grenzen zelf een waarde te kiezen.<br />

Er geldt:<br />

1<br />

(1<br />

H( ) Z Z )(1<br />

Z R X<br />

1 )<br />

~ C2<br />

V<br />

= + + , met 1<br />

Z Z 1<br />

= +<br />

1<br />

Z +<br />

Z<br />

V C1 X C2


(1<br />

Z Z X<br />

= + )(1 + R (<br />

1 1<br />

1<br />

))<br />

C2 Z<br />

+<br />

C1 ZX+ Z<br />

(2.16)<br />

C2<br />

Zodat we kunnen schrijven:<br />

jc2<br />

A = (1 + jZXc 2)(1 + R 1(jc1+ )) , met c =ωC , c =ωC<br />

1 + jZXc 1 1 2 2<br />

2<br />

(1 jZ c )(1<br />

R1<br />

= + X 2 + (jc1 jc2 ZXc1c 2))<br />

1 jZXc + -<br />

+ 2<br />

= (1 + jZXc2+ jR 1(c1+ c 2) - ZXR1c 1c 2))<br />

(2.17)<br />

Voor het voldoen aan de Barkhausencriteria moet deze uitdrukking gelijk zijn aan de complexe versterking<br />

A=A r +jA i .(met behulp van Euler |A|e jφ ). We vervangen Z X door de complexe vorm Z Xr + jZ Xi .<br />

Voor het reële deel van de versterking krijgen we:<br />

Ar= 1 - ZXic2- ZXrR1c 1c2 (2.18)<br />

en voor het imaginaire deel:<br />

Ai= ZXrc2+ R1c1+ R1c2- ZXiR1c1c 2<br />

(2.19)<br />

A r is bekend dus vinden we voor c 2 :<br />

c<br />

1 Ar<br />

2=<br />

-<br />

ZXi+ ZXrR1c 1<br />

c 2 ingevuld in vergelijking (19) levert ons op:<br />

Ai= c 2(ZXr+ R1- ZXiR1c 1) + R1c1 1 Ar<br />

=<br />

- (ZXr R1 ZXiR1c 1) R1c1 ZXi+ ZXrR1c + - +<br />

1<br />

&<br />

(Ai- R1c 1)(Z Xi+ ZXrR1c 1) = (1 - A r)(Z Xr+ R1- ZXiR1c 1)<br />

21<br />

(2.20)<br />

2 2<br />

& c 1 (ZXrR 1 ) + c 1(ArZXi- AiZ Xr)R 1+ (1 - A r)(Z Xr+ R 1) - AiZXi= 0<br />

(2.21)<br />

In deze kwadratische vergelijking zijn we geïnteresseerd in één oplossing. Voor de discriminant D<br />

geldt dan:<br />

2<br />

2<br />

D = R 1 7(ArZXi- AiZ Xr)<br />

- 4Z Xr((1 - A r)(Z Xr+ R 1) - AiZ Xi)<br />

A = 0<br />

(2.22)<br />

Voor R 1 vinden we:<br />

2<br />

(ArZXi- AiZ Xr)<br />

+ 4A Z Z<br />

R1=<br />

4(1 - A r)Z Xr<br />

i Xr Xi<br />

- ZXr<br />

(2.23)<br />

R 1 ingevuld in de kwadratische vergelijking geeft ons de oplossing van c 1 :


c<br />

A Z A Z<br />

1=<br />

-<br />

2<br />

2ZXrR1 i Xr r Xi<br />

De waarden voor C 1 en C 2 verkrijgen we tenslotte door c 1 en c 2 te delen door ω.<br />

22<br />

(2.24)<br />

2.2.1.5 Wijzigingen Pierce en Simulatie<br />

Naar aanleiding van de constateringen uit het vorige deel, hebben we onze schakeling veranderd. We<br />

proberen onderstaand schema. Weerstand R 8 dient ervoor om de ingang van de op-amp op een gedefinieerd<br />

niveau te brengen. De versterking stellen we in met weerstand R 6 /R 7 en R 2 /R 3 . Met de in de<br />

schakeling gegeven waarden bedraagt deze ongeveer 1000 keer.<br />

C4<br />

47n<br />

R8<br />

100MEG<br />

3<br />

2<br />

TL084<br />

R6<br />

470<br />

0<br />

U1A<br />

+<br />

-<br />

R7<br />

0<br />

4<br />

11<br />

220k<br />

V+<br />

V2<br />

12V<br />

V- OUT<br />

V1<br />

12V<br />

1<br />

R1<br />

2.16k<br />

R2<br />

4.7k<br />

C1<br />

.4117n<br />

C2<br />

5.4n<br />

0<br />

6<br />

L1<br />

TL084<br />

Fig. 2.15 Schema Pierce <strong>oscillator</strong><br />

We voeren de schakeling (figuur 2.15) in Orcad in en laten er met PSpice AD een ‘tijddomein’-simulatie<br />

op los (figuur 2.16). Hieruit blijkt dat de schakeling in de buurt van de berekende frequentie oscilleert:<br />

6.0 kHz. De schakeling start omdat er ruis op het circuit staat. Het valt ook op dat de <strong>oscillator</strong><br />

tijd nodig heeft om op gang te komen. Dit komt doordat er steeds maar weinig energie toegevoerd kan<br />

worden aan het netwerk, dat namelijk een hoge impedantie heeft. Vanwege de niet-lineaire versterking<br />

van de op-amps, bij grotere signalen neemt deze af, vindt het circuit een werkpunt waarbij de totale<br />

versterking gelijk aan 1 wordt. De amplitude verandert na dit startverschijnsel niet meer.<br />

5<br />

1.586<br />

0<br />

U1B<br />

+<br />

-<br />

0<br />

R3<br />

10k<br />

4<br />

11<br />

V+<br />

V4<br />

12V<br />

V- OUT<br />

V3<br />

12V<br />

7<br />

R5<br />

6.8k<br />

R4<br />

4.7k<br />

C3<br />

330n


Fig. 2.16 PSpice simulatie startverschijnsel<br />

Van de curve lichten we een deel uit, om inzicht te krijgen in de faseverschuiving die de componenten<br />

in het netwerk veroorzaken. We bekijken drie punten in de schakeling: het signaal dat aangeboden<br />

wordt op de ingang van de eerste op-amp, pin 3 (In de grafiek is dit signaal vergroot weergegeven), het<br />

versterkte signaal dat uit deze op-amp komt, pin 1 en het signaal dat uit de inverterende op-amp komt,<br />

pin 7. In de curve valt met name op dat het signaal van pin 1 achter loopt op dat van pin 3. Dit komt<br />

ons van pas. Uit de overdracht bleek namelijk dat het -netwerk niet 180° kon halen. Uit de simulatie<br />

blijkt dat bij 6.0 kHz faseverschuiving van de op-amps 9.7 µs is. (omgerekend naar de periode is dit<br />

bijna 180°-22°=158°). De faseverschuiving wordt kennelijk veroorzaakt door de grote versterking van<br />

468 keer. Dit blijkt des te meer als we de (absolute) versterking van de twee op-amps verwisselen,<br />

zodat de tweede op-amp een heel grote (inverterende) versterking gaat krijgen. In dat geval treedt de<br />

faseverschuiving op in de tweede op-amp.<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

Fig. 2.17 Faseverschuiving bij de TL084 PSpice Simulatie<br />

In de praktijk funktioneert de schakeling zoals we uit de berekeningen zouden verwachten. De faseverschuiving<br />

(figuur 2.17) in de op-amps is echter iets meer dan uit de simulatie: 180°-30°=150°. Om<br />

toch, wat betreft de faseverschuiving, aan het Barkhausencriterium te voldoen, zal daardoor de oscillatiefrequentie<br />

omlaag gaan naar 5.3 kHz (figuur 2.20. Bij die frequentie komt de faseverschuiving van<br />

de overdrachtscurve van het -netwerk verder van de 180° af te liggen. Een grafiek uit de datasheet<br />

(figuur 2.18) van de TL084 waarin de open-lus versterking staat afgebeeld en een PSpice AC-analyse<br />

(figuur 2.19) is hiermee ook in overeenstemming:<br />

23


Fig. 2.18 Open-lus versterking TL084<br />

Versterking<br />

|A| (in Vuit/Vin)<br />

1000<br />

900<br />

800<br />

700<br />

Gemeten faseverschuiving<br />

16µs<br />

0 5*10 3<br />

Fig. 2.20 Gemeten faseverschuiving: 30° bij 5.3 kHz<br />

2.2.1.6 Metingen bij de resonantiefrequentie<br />

Versterking vs. Frequentie TL084 (PSpice simulatie )<br />

Frequentie (in Hz)<br />

|A|<br />

φ<br />

10*10 3<br />

180<br />

170<br />

160<br />

150<br />

140<br />

Fig. 2.19. Inverterende versterker, ingesteld op 1000 keer<br />

Nu we met de sensor in de buurt van de resonantiefrequentie kracht kunnen meten, moeten we controleren<br />

of inderdaad de frequentie afneemt bij de belasting, zoals eerder beweerd is. We leggen de<br />

sensor op een vlakke ondergrond, meten de frequentieverandering als er kracht op de sensor wordt<br />

uitgeoefend. We meten onbelast een frequentie van 5.3 kHz. Belasten we de sensor met een kracht<br />

van ongeveer 50mN, loopt de frequentie op naar 5.4 kHz. Als we vervolgens de kracht nog meer laten<br />

toenemen, tot 4 N, verandert de frequentie niet. Een nog grotere kracht zorgt ervoor dat de sensor ophoudt<br />

met resoneren, omdat hij dan teveel gedempt wordt. Het valt ook op dat tijdens het aanbrengen<br />

van een kracht de frequentie verandert met de mate waarmee de kracht verandert. Dit effect is echter<br />

van korte duur, omdat de frequentie nadat de kracht niet meer verandert, weer terugloopt naar 5.4 kHz.<br />

Blijkbaar verandert de frequentie kortdurig, maar valt direct daarna terug naar de beginwaarde. Omdat<br />

bovendien de Q-faktor erg laag is van de sensor, betekent dit dat hij niet erg stabiel op een bepaalde<br />

frequentie zal oscilleren. Samen met het voorgaande concluderen we dat deze schakeling ongeschikt is<br />

om nauwkeurig de resonantiefrequentie te meten.<br />

2.2.1.7 Serieresonantie<br />

Naast de Pierce <strong>oscillator</strong>, zijn er verschillende andere typen schakelingen bekeken, waaronder een<br />

schakeling om de sensor bij serieresonantie te laten oscilleren 8 . Zie figuur 2.21. Deze schakeling be-<br />

Fase<br />

φ (in °)<br />

24


stond uit twee in serie geschakelde inverters, met daarop de sensor aangesloten.<br />

U1A U2A<br />

C1<br />

R1 R2<br />

Figuur 2.21. Basisschakeling serieresonantie<br />

De sensor zal, afhankelijk van de totale versterking en de faseverschuiving in de lus, ergens oscilleren<br />

tussen twee karakteristieke punten: de frequentie waarbij de impedantie het laagst is en de frequentie<br />

waarbij de faseverschuiving 0 is, respectievelijk f s en f r .<br />

Zoals eerder genoemd in paragraaf 2.2.1.2 nemen we voor R1 en R2 een waarde tussen 100 kΩ en 22<br />

MΩ. Tegen de verwachting in komt de frequentie van de sensor bij die waarden niet in de buurt van<br />

de resonantiefrequentie. Uit de volgende tabel (tabel 2.1) blijkt welk verband er tussen R1 (=R2) en de<br />

frequentie bestaat:<br />

X1<br />

R1 (in Ω) f (in Hz)<br />

16.3·10 3 4.0·10 3<br />

21.0·10 3 3.3·10 3<br />

68.0·10 3 1.0·10 3<br />

380·10 3 760<br />

1.0·10 6 73<br />

Tabel 2.1. Frequenties bij verschillende<br />

terugkoppelweerstanden. (V DD =6V)<br />

Uit de tabel blijkt dat de basisschakeling bij de gegeven weerstandswaarden en de daarna gemeten<br />

resonantiefrequentie niet functioneert als serieresonantie<strong>oscillator</strong>. De reden dat de <strong>oscillator</strong> niet aan<br />

de verwachtingen voldoet, is dat vanwege de lage Q-factor, de CMOS-poort teveel moet versterken.<br />

Wel valt ons op dat de frequentie van de <strong>oscillator</strong> in deze schakeling bij alle waarden van R1 (en<br />

R2) beïnvloed kan worden door het aanbrengen van een kracht. Bij lage weerstandswaarden voor R1<br />

gedraagt de schakeling zich meer als een relaxatie-<strong>oscillator</strong>. Ongeacht de richting van de krachtuitoefening<br />

neemt de frequentie toe. Hiermee kunnen dus statische krachten gemeten worden.<br />

<strong>2.3</strong> <strong>Relaxatie</strong>-<strong>oscillator</strong><br />

<strong>Relaxatie</strong>-<strong>oscillator</strong>en worden steeds vaker gebruikt bij sensorsystemen, omdat veranderingen van het<br />

sensorsignaal doorwerken in een verandering van de periode, die meestal eenvoudig te meten valt 9 .<br />

Bij relaxatie-<strong>oscillator</strong>en wordt er steeds omgeschakeld tussen twee (a-stabiele) toestanden, waarvan<br />

de periode vooral bepaald wordt door de weerstand R en de sensor, hier opgenomen als condensator<br />

C. Daarnaast spelen ook nog eigenschappen van de inverter-IC’s een rol, zoals de spanning waarbij de<br />

inverters van logisch niveau wisselen en bij hoge frequenties de vertragingstijd.<br />

25


R<br />

“C”<br />

INV1 INV2<br />

Figuur 2.22. <strong>Relaxatie</strong>-<strong>oscillator</strong><br />

Het schema van een relaxatie-<strong>oscillator</strong> vinden we in figuur 2.22. De voedingsspanning U DD van de<br />

NAND-poort, een CD4011, bedraagt 6.0 V. We hebben gemeten dat het omschakelpunt U s bij 59% van<br />

de voedingsspanning U DD ligt.<br />

Via weerstand R wordt de condensator steeds opgeladen en ontladen. Van toestand wisselt hij als de<br />

spanning op de ingang van de NAND-poort gelijk wordt aan U s . De spanning die direct na het wisselen<br />

op de condensator staat, is dan:<br />

• Tijdens het laden: U s -U DD<br />

• Tijdens het ontladen: U s +U DD<br />

Hieruit kunnen we de vergelijkingen voor de duur van het laden en ontladen afleiden:<br />

T ln<br />

2 UDD US<br />

1 $<br />

$<br />

=<br />

UDD- US<br />

-<br />

x c m T ln<br />

US UDD<br />

2=<br />

x $ c + m T=T +T (2.25)<br />

U<br />

1 2<br />

S<br />

Als we de volgende waarden nemen:<br />

R=68kΩ C=5.4nF τ=RC<br />

U DD =6.0V U S =0.59U DD<br />

Is de periode gelijk aan:<br />

T=8.2·10 4 s (of f=1.2·10 3 Hz)<br />

In het schema waar deze <strong>oscillator</strong> deel van uitmaakt (figuur 3.7), vervangen we overigens de weerstand<br />

R door een vaste en een regelbare weerstand in serie, zodat de frequentie niet te hoog kan worden<br />

voor de microcontroller.<br />

<strong>2.3</strong>.1 Capaciteitsverandering ten gevolge van druk<br />

We meten het signaal, weergegeven in figuur 2.23, dat op de ingang van de NAND-poort staat, als we<br />

de sensor in de schakeling van figuur 2.22 gebruiken. Behalve dat de sensor grote invloed heeft op de<br />

oscillatiefrequentie, ondervindt de sensor ook invloed van het oscillatiesignaal zelf. Het signaal tussen<br />

de aansluitpunten van de sensor zorgt ervoor dat de sensor steeds afwisselend op en neer beweegt.<br />

Als we de sensor niet afdempen, maar vrij laten bewegen, zien we bovenop het signaal nog een soort<br />

tweede-orde effect gesuperponeerd. De frequentie hiervan komt ongeveer overeen met de resonantiefrequentie<br />

van de sensor.<br />

26


Figuur 2.23. Gemeten golfvorm <strong>oscillator</strong>, zonder demping<br />

Als we vervolgens de sensor gaan belasten, zie figuur 2.24, verdwijnt door de demping dit effect.<br />

<br />

<br />

<br />

Figuur 2.24. Gemeten golfvorm <strong>oscillator</strong>, sensor wordt gedemp<br />

Bovendien wordt de frequentie hoger. Deze frequentieverandering is anders dan in de configuratie<br />

bij de Pierce<strong>oscillator</strong>. Bij de relaxatie-<strong>oscillator</strong> bestaat er namelijk een verband tussen de frequentie<br />

en de kracht die op de sensor wordt uitgeoefend. Blijkbaar is de sensor wel degelijk te gebruiken om<br />

ook een statische kracht te meten. We vermoeden dat de kracht samenhangt met de capaciteit C 0 in het<br />

elektrisch vervangingsschema.<br />

Als we een kracht uitoefen op de sensor, neemt de capaciteit alsmede de periode (zie formule 25),<br />

maximaal af met ongeveer 10%. Zie grafiek 3.1 voor een grafiek waaruit de samenhang zichtbaar is<br />

tussen de kracht en de frequentie. Boven een kracht van ongeveer 25N, verandert de capaciteit niet<br />

meer. We merken overigens niet dat het kristal door deze kracht samendrukbaar zou zijn, immers ondanks<br />

dat de platen ten gevolge van de (druk)kracht dichter bij elkaar zouden moeten komen, neemt de<br />

capaciteit toch af.<br />

27


<strong>2.3</strong>.2 Meetcircuit<br />

Het volledige meetcircuit zien we in figuur 2.25. De relaxatie-<strong>oscillator</strong> wordt gevormd door R1 en R2<br />

in serie en de capaciteit van X2. Via weerstand R3 aan de ingang van U1A kunnen we de sensor in een<br />

begintoestand zetten. Transistor Q2 die aan de uitgang van U1B zit vormt de interface met de logica<br />

van de microcontroller. De microcontroller heeft een interne pull-up. We kunnen de uitgang van U1B<br />

niet direct met de ingang van de microcontroller verbinden, omdat de voedingsspanning van het meetcircuit<br />

lager dan 5 V is en de microcontroller 5 V nodig heeft.<br />

Met weerstand R2 kunnen we de frequentie bijregelen waarop de sensor oscilleert.<br />

R5<br />

12k<br />

Vadj.<br />

Q1<br />

BC558C<br />

D1<br />

D1N4148<br />

R3<br />

3k3<br />

1<br />

2<br />

Vadj.<br />

GND<br />

R1<br />

50k<br />

X2<br />

Piezo Cry stal<br />

U1A<br />

CD4011A<br />

R2<br />

Figuur 2.25. Meetcircuit<br />

3<br />

82k<br />

5<br />

6<br />

U1B<br />

CD4011A<br />

De voedingsspanning van het meetcircuit is instelbaar gemaakt, om de amplitude van het signaal in<br />

de relaxatie-<strong>oscillator</strong> te kunnen regelen. Bij 5 V voedingsspanning is het signaal dat van de sensor<br />

afkomstig is erg goed hoorbaar. Dit willen liever niet en kunnen we zachter krijgen door de spanning<br />

te verlagen. Bij 3.5 V werkt de sensor nog erg goed, en is het geluid dat de sensor produceert al aanzienlijk<br />

zachter.<br />

De voedingsspanningen (figuur 2.26) van 5 V en 3.5 V worden door een 5-volts stabilisator (7805) en<br />

transistor Q3 (BC547) geregeld.<br />

Vin<br />

C4<br />

10u<br />

1<br />

U9<br />

LM78L05A/TO39<br />

VIN<br />

GND<br />

3<br />

VOUT<br />

GND GND<br />

GND<br />

2<br />

Figuur 2.26. Spanningsstabilisatie<br />

4<br />

R4<br />

10k<br />

+5v<br />

Q3<br />

BC547A<br />

Vadj.<br />

C5<br />

10u<br />

R7<br />

1k<br />

R8<br />

4k7<br />

GND GND<br />

C6<br />

1uF<br />

Q2<br />

BC548B<br />

GND<br />

28


3. Data-acquisitie en –verwerking<br />

Om te begrijpen hoe de data-acquisitie plaatsvindt, maken we een iets eenvoudiger voorstelling van de<br />

processen die zich hierbij afspelen. Voor de beschrijving maken we gebruik van het schema van figuur<br />

3.7. De data-acquisitie bestaat in principe uit een bepaling van de lengte van het periodieke signaal<br />

afkomstig van de relaxatie-<strong>oscillator</strong>. De microcontroller, een AT89C2051, krijgt dit signaal aangeboden<br />

op zijn interrupt-ingang /INT1. Als er op die ingang een transitie plaatsvindt van een hoog naar<br />

laag niveau 10 , wordt een interrupt routine uitgevoerd die de timerwaarden verwerkt. Zo kan de microcontroller<br />

bepalen hoe groot de lengte van het periodieke signaal van de sensor is. De microcontroller<br />

rekent deze tijd om naar een waarde die de kracht op de sensor voorstelt. Tenslotte zet hij 1 op de 10<br />

keer deze waarde conform de MIDI-standaard als een ‘aftertouch control’ op de seriële uitgang. Hierna<br />

kan de cyclus opnieuw beginnen.<br />

De reden dat zowel /INT0 als /INT1 zijn aangesloten op dezelfde lijn, is dat we handig gebruik maken<br />

van de mogelijkheid via maskering van een van de interrupts /INT0 of /INT1 te bepalen waarheen<br />

de interrupt vector zal springen. Afhankelijk van de instelling zal er dan gesprongen worden naar de<br />

“calibration” of “measuring”). We kunnen op deze wijze een instructie uitsparen die anders nodig was<br />

geweest om te bepalen waarheen gesprongen zou moeten worden.<br />

De dataverwerking vindt in de microcontroller steeds plaats gedurende een halve periode van de<br />

cyclus. Op dat moment is de timer ingesteld op ongeveer 1.3 ms. In die tijd kan de microcontroller<br />

ongeveer 650 instructies van 2 machinecycles afwerken. Tijdens de verwerking worden de volgende<br />

opdrachten uitgevoerd:<br />

• Tel de de gevonden meetwaarde bij de vorige op.<br />

• Ieder tiende keer wordt van de cumulatieve meetwaarde een gemiddelde berekend, gelijk aan de<br />

som gedeeld door het aantal meetwaarden. Deze waarde wordt omgezet naar een waarde tussen<br />

0 en 255. Vervolgens wordt deze waarde uit een linearisatietabel omgerekend naar een waarde<br />

tussen 0 en 127. Om de transmissie van mididata efficiënter te laten verlopen sturen we deze<br />

waarde alleen als er een verandering is opgetreden ten opzichte van de vorige. Zo worden er niet<br />

onnodig gegevens overgestuurd.<br />

3.1 Microcontroller<br />

Oorspronkelijk hadden we de schakeling opgebouwd en getest met een 8031. De keuze voor deze controller<br />

hadden we genomen omdat we beschikten over een uitgebreide ontwikkelomgeving daarvoor.<br />

Daarnaast hadden we veel ervaring met de MCS ® 51 familie. De ontwikkelomgeving bestond uit een<br />

eprom-emulator, een PC speciaal ingericht met software (asem51) om de sourcecode te assembleren,<br />

een experimenteerprint gebaseerd op de 8031, een monitorprogramma om de software te debuggen en<br />

een ‘Mini Flash Programmer’, die al in oktober 1996 in het tijdschrift Elektuur gestaan heeft, om een<br />

Atmel AT89C2051 te kunnen programmeren.<br />

We hebben verschillende instellingen in de software getest, gebruikmakend van de ontwikkelomgeving.<br />

Nadat het systeem naar behoren werkte, konden we de software in de veel kleinere microcontroller<br />

van Atmel, de AT89C2051, programmeren. Omdat deze op een aantal externe porten na geheel<br />

compatibel was met de 8031, hoefden we niets aan de software te veranderen. Daarnaast had de<br />

AT89C2051 2KB aan programmeerbare Flash geheugen aan boord, waardoor het externe geheugen en<br />

de latch, zoals die bij de 8031 noodzakelijk waren, konden worden weggelaten.<br />

29


3.1.1 Gestuurde oscillatie<br />

Zonder kracht op de sensor en zonder dat de <strong>oscillator</strong> aan de ingang belast wordt, genereert de relaxatie-<strong>oscillator</strong><br />

tijdens het opstarten een frequentie van ongeveer 370 Hz (of periode is 2.7ms). Deze<br />

wordt door de microcontroller gemeten door gebruik te maken van een hardware-interrupt en een<br />

timer. De hardware-interrupt is zo ingesteld dat hij op een neergaande flank van het signaal reageert.<br />

Weliswaar is de flank aan de uitgang van de sensor op het moment dat we willen meten van laag naar<br />

hoog, maar omdat deze via een transistor loopt die de interruptingang van de microcontroller naar<br />

beneden trekt, krijgen we in de microcontroller een flank van hoog naar laag. Dankzij een hardwareinterrupt<br />

kan de microcontroller, afhankelijk van de instructie waar hij op dat moment nog mee bezig<br />

zou kunnen zijn, binnen 24 klokcycli reageren. Bij een klokfrequentie van 12 MHz is dit 2 μs. Als gevolg<br />

hiervan kan de gemeten frequentie van de sensor met ongeveer +0.28 Hz (2.7ms + 2μs) afwijken.<br />

Na de frequentie bepaald te hebben zonder kracht op de sensor, is de microcontroller gecalibreerd. De<br />

volgende stap in de software is een speciale (interrupt-)procedure starten die met (een frequentie die<br />

iets hoger ligt dan) de gemeten frequentie, de sensor aanstuurt. We kunnen de frequentie beïnvloeden,<br />

door voordat de periode afgelopen zou zijn, de sensor steeds terug te zetten in een uitgangspositie. Dit<br />

geschiedt via het kort sturen van transistor Q1 die ervoor zorgt dat beide aansluitingen van de sensor<br />

op hetzelfde spanningsniveau komen. Door het gering verhogen van de frequentie kunnen we ongewenste<br />

flanken voorkomen, die aan het eind van de periode door storingen zich kunnen manifesteren,<br />

immers dan ligt de ingangsspanning op de NAND-poort van de sensor in de buurt van het omschakelpunt<br />

U s . Storende flanken zijn ongewenst, omdat deze kunnen leiden tot een interrupt die op een<br />

verkeerd moment een meetwaarde gaat vastleggen. Een ander voordeel van deze aansturing is dat<br />

ervoor gezorgd wordt dat de sensor steeds vanuit een gedefinieerde uitgangspositie zijn ‘periode’ begint,<br />

doordat de sensor steeds heel even op een vaste spanning wordt gebracht. De interruptprocedure<br />

kent drie fasen, in figuur 3.1 Part I,II en III genaamd. De hierbovengenoemde aansturing van de sensor<br />

gebeurt gedurende Part I, waarna de verwerking van de meetgegevens, en het registreren van nieuwe<br />

gedurende Part II en III gebeurt. (Zie figuur 3.3 voor details)<br />

Hi-state<br />

(Microcontroller<br />

output port)<br />

Lo-state<br />

Part I Part II Part III<br />

Sensor <strong>oscillator</strong> circuitry is<br />

connected to an open collector.<br />

Which means when wvmem (or<br />

sensctrl) is hi, it is free to make<br />

a transition, depending on<br />

applied force.<br />

Duration: lo_Tx hi_Tx hivdTx0<br />

Flag:<br />

wvmem<br />

vldsens<br />

0<br />

0<br />

1<br />

0<br />

1<br />

1<br />

Sensor interrupt driven control-timing diagram Fig. 3.1 onderverdeling periode<br />

De PZT-sensor buigt afhankelijk van de aangelegde spanning of naar beneden of naar boven door.<br />

(figuur 3.2) De doorbuiging bij Part I en II is in dezelfde richting. Bij Part III is deze in de andere richting.<br />

Voor een goede werking maakt het overigens niet welke richting het eerst komt.<br />

<br />

<br />

<br />

<br />

Figuur 3.2 Doorbuiging PZT-sensor<br />

<br />

<br />

30


De aansturing van de sensor bij Part I duurt ongeveer 35μs. Deze tijd is voldoende om storingen te<br />

voorkomen en de sensor niet teveel te hinderen bij zijn mechanische beweging. Bij Part II en III neemt<br />

de relaxatie-<strong>oscillator</strong> de ‘sturing’ van de sensor over. De interrupt die door de uitgang van de <strong>oscillator</strong><br />

op de microcontroller wordt aangeboden, wordt alleen bij Part III toegelaten.<br />

Een stroomdiagram van de gehele procedure die de aansturing van de sensor beschrijft, staat in figuur<br />

3.3<br />

631<br />

633<br />

636<br />

637<br />

Part I<br />

clr TR0<br />

test vldsns<br />

wvmem<br />

wvmem<br />

Clear flag<br />

608 wvmem<br />

Set flag<br />

598<br />

Clear output port<br />

sensctrl ti_hins:<br />

Set output port<br />

sensctrl<br />

vldsens<br />

lo_Tx->Timer0<br />

setb TR0<br />

clr EX1<br />

wvmem=1<br />

vldsens=1<br />

117<br />

593<br />

wavectrl:<br />

Load Timer0 with<br />

counter values for part I.<br />

Start Timer0<br />

Do not allow<br />

int1 during<br />

part I.<br />

Return from<br />

293 lo_Tx->Timer0<br />

296 setb TR0<br />

Timer0 IRQ vector points here:<br />

Part II<br />

610<br />

613<br />

np_flag=1<br />

616<br />

624<br />

R3!=0<br />

625<br />

626<br />

hi_Tx->Timer0<br />

setb TR0<br />

wvmem=0<br />

vldsens=0<br />

test np_flag<br />

np_flag=0<br />

OP_x+= tmp_TH0<br />

tmp_TL0<br />

R3--, test R3<br />

setb np_flag<br />

reti interrupt<br />

reti interrupt<br />

reti<br />

Figuur 3.3 Stroomdiagram sensor stuurprocedure<br />

Timer0 runs untill TR0 is cleared.<br />

Timer0 counts machine cycles (1/12 Clk)<br />

Overflow causes TF0 to become high, which<br />

is vectored to 000Bh. 'Zig-zag' means further<br />

processing is done through an interrupt vector.<br />

Stop Timer0<br />

Load Timer0 with<br />

counter values for part II.<br />

Start Timer0<br />

599<br />

If main is still processing 601<br />

data which was captured<br />

here, it is better not to interfere.<br />

So just exit interrupt<br />

Add captured sensor data to<br />

the 24 bits counter<br />

Track number of captured<br />

data. Test if we have enough<br />

Send message to main to<br />

indicate processing is<br />

needed<br />

Return from<br />

604<br />

606<br />

Part III<br />

wvmem=1<br />

vldsens=0<br />

hivdTx0->Timer0<br />

setb TR0<br />

tmp_TH0=0FFh<br />

tmp_TL0=0FFh<br />

clr IE0<br />

setb EX1<br />

Allow sensor to<br />

produce valid<br />

signal<br />

Load Timer0 with<br />

counter values for<br />

part III.<br />

Start Timer0<br />

Preload tmp_Tx0<br />

with the maximum<br />

value. If during<br />

this cycle the sensor<br />

does not produce<br />

an interrupt, we<br />

will have a value<br />

anyway<br />

Software reset for<br />

interrupt flag. (Because<br />

EX1 is still<br />

disabled)<br />

Allow external<br />

interrupt EX1.<br />

So wait for an interrupt<br />

from sensor,<br />

which is vectored<br />

to 0013h.<br />

Return from<br />

interrupt<br />

31


Twee interrupts hebben invloed op het verloop van de procedure. Timer 0 gebruiken we om te bepalen<br />

hoe lang Part I, II en III duren. De timer is geconfigureerd als een 16-bits teller die 1/12 van de<br />

systeemklok, machinecycles genaamd, telt. Tijdens het calibreren worden er voor het telregister drie<br />

verschillende beginwaarden berekend in calc_t0. Deze waarden worden in het stroomdiagram gebruikt<br />

in de drie verticale takken. De tijd dat iedere tak in beslag neemt is hiermee vastgelegd. Omdat deze<br />

waarden snel oproepbaar moeten zijn, worden ze in het geheugen opgeslagen. Een interrupt door de<br />

timer treedt op wanneer alle bits van het telregister van 1 naar 0 verspringen en veroorzaakt een sprong<br />

naar de routine wavectrl. Daar wordt het telregister van timer 0 geladen met de beginwaarden voor het<br />

volgende deel. Na Part III volgt weer Part I, zodat we daarmee een cyclus hebben.<br />

De sensor is verbonden met interrupt /INT1. Deze leidt naar de routine sens_stamp, van waaruit de<br />

waarde die timer 0 op dat moment heeft, wordt opgeslagen.<br />

3.1.2.1 Omzetting naar midi<br />

Voor de omzetting van kracht naar een midi-conform signaal, moeten we de gemeten waarden zo<br />

converteren dat daar een waarde uitkomt dat geschikt is om via midi te versturen. Voor midi-controllerdata<br />

is vastgelegd dat deze een 7-bits getal moeten zijn. (0 tot en met 127). Om een goed beeld van<br />

het krachtverloop te krijgen, ligt het voor de hand om de gemeten waarden zodanig om te zetten dat er<br />

een lineair verband bestaat tussen de uitgeoefende kracht en de waarde die het in midi voorstelt.<br />

Voor deze omzetting worden een aantal stappen doorlopen. We beginnen bij het moment dat er een<br />

meting van de sensor gedaan wordt. Als er een interrupt van de sensor optreedt, moeten we zo vlug<br />

mogelijk de waarde van het telregister opslaan, omdat de timer doorloopt. Om dit mogelijk te maken<br />

wordt na een interrupt als eerste de timer stopgezet om te voorkomen dat het telregister verandert. Vervolgens<br />

wordt de routine sens_stamp uitgevoerd die het telregister tijdelijk kopieert naar het 16-bits<br />

register tmp_T0, waarna timer 0 weer kan doorlopen en de interruptroutine verlaten kan worden.<br />

De verdere dataverwerking van tmp_T0 geschiedt in de interruptroutine wavectrl, tijdens de fase<br />

Part II. De keuze om de verwerking op een andere plaats uit te voeren dan bij sens_stamp, is dat we<br />

tijdens Part II voldoende tijd hebben om de verwerking van tmp_T0 te doen. Timer 0 kan daar ondertussen<br />

tijdens het verwerken namelijk al lopen; immers een interrupt afkomstig van timer 0 hoefden<br />

we de eerste 650 instructies niet te verwachten. De verwerking tijdens Part II van tmp_T0 bestaat uit<br />

het volgende: We tellen de waarden van tmp_T0 cumulatief op in een 24 bits register (OP_1..OP_3).<br />

Nadat we uiteindelijk Part II num_per keer doorlopen hebben, of anders gezegd na num_per perioden<br />

hebben we het gewenste aantal metingen gedaan en maken we vanuit wavectrl de flag np_flag<br />

actief. Deze vlag deelt aan de hoofdlus van het programma mee dat de meetwaarden klaar zijn voor<br />

verdere verwerking. Na het verlaten van de interruptroutine zal in de hoofdlus deze melding worden<br />

opgemerkt.<br />

In de hoofdlus geschiedt de verdere verwerking in convert. We berekenen een gemiddelde van de<br />

meetwaarden. We moeten hiervoor 0xffff (=65535) aftrekken van de meetwaarde. Namelijk als er geen<br />

kracht op de sensor wordt uitgeoefend zou deze flank optreden net iets later dan de periode van de<br />

timer. Zie figuur 3.4. Bij het ontbreken van een kracht wordt er geen interrupt gegenereerd, omdat de<br />

sturing dan alweer aan een volgende periode is begonnen. De sensor krijgt net niet de gelegenheid om<br />

‘uit zichzelf’ van richting te veranderen. Aangezien tmp_T0 door het ontbreken van de interrupt niet<br />

overschreven is en de software tmp_T0 geinitialiseerd had met 0xffff, krijgen we door de aftrekking<br />

bij het ontbreken van druk een resultaat 0. Naar gelang de interrupt door het uitoefenen van kracht eerder<br />

was opgetreden, zou tmp_T0 een waarde lager dan 0xffff hebben gekregen, waardoor het resultaat<br />

een (niet-lineair) verband heeft met de uitgeoefende kracht.<br />

32


4<br />

Vadj -<br />

2<br />

Vt -<br />

GND -<br />

_INT<br />

0<br />

2<br />

0 0.001 0.002<br />

1 -<br />

0 -<br />

1 -<br />

0 -<br />

Timer 0 register<br />

during Part III:<br />

hivdtmr0<br />

(start)<br />

Vadj : VDD CD4011<br />

Vt : Toggle voltage<br />

(typ. 0.59*Vadj)<br />

0xFFFF<br />

(end)<br />

Figuur 3.4 Timingsdiagram van de sensor, interrupt<br />

Sensor waveform output<br />

Interrupt signal<br />

without force applied<br />

Interrupt signal<br />

with force applied<br />

We hebben gemeten dat boven een bepaalde kracht op de sensor er geen verandering meer optreedt in<br />

het oscillatiegedrag. De maximale verschuiving van de flank tijdens Part III hebben we laten afhangen<br />

van de beginfrequentie bij het calibreren. Hij bedraagt 46% (sens_tm) van de duur van Part III.<br />

Het voordeel van deze relatieve waarde is dat we niet gebonden zijn een bepaald werkpunt, mits de<br />

controller vantevoren tijdens het calibreren de frequentie gemeten heeft zonder dat de sensor sturing<br />

kreeg. Aangezien we voor de maximale kracht een van de waarden 0 tot en met 127 willen krijgen,<br />

moeten we in principe de gemeten waarde delen door het maximum en vermenigvuldigen met 127.<br />

Rekentechnisch lossen we dit in de microcontroller anders op, omdat we niet met een drijvende komma<br />

willen gaan rekenen. Er is bovendien nog een andere reden, namelijk dat we het a-lineaire gedrag<br />

eerst met behulp van een tabel willen lineariseren. Hierdoor kunnen we de kracht uitdrukken in een<br />

midi-waarde. De grootte van de tabel bepaalt nu wat de maximale waarde van de meetwaarde moet<br />

zijn. We gebruiken een tabel van 255 elementen. In paragraaf 3.1.2.1 gaan we hier nader op in.<br />

De versturing van de MIDI-data gaat via de seriële poort van de microcontroller. Volgens het midi-protocol<br />

moet deze uit twee bytes bestaan: een controlbyte en de bijbehorende waarde.<br />

Eerst sturen we in de routine sendmd1 een controlbyte met de instructie:<br />

mov SBUF,#midictrl<br />

In de routine waittx wachten we totdat deze verstuurd is.<br />

De volgende byte die we in de routine sendmd2 versturen, is de waarde die we na de linearisatie van<br />

de kracht gekregen hebben. Dit gaat met de volgende instructie:<br />

mov SBUF, midival<br />

En wederom wachten we totdat deze verstuurd is.<br />

33


Na het versturen van de data initialiseren we een aantal registers, waardoor de microcontroller klaar is<br />

om de volgende meting te doen. Zie de lus measure in figuur 3.5<br />

main:<br />

loop:<br />

Initialize<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

Measure<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

Description of startup procedure, prior to<br />

normal operation. Stackpointer needs to be set<br />

before the stack is being used (e.g. acall), to<br />

prevent program/stack corruption.<br />

With the aid of timer 1 Calib measures the<br />

period from the relaxation <strong>oscillator</strong>.<br />

The result is processed in calc_t0, which<br />

calculates values for a fully interrupt driven<br />

process (wavectrl) which controls proper<br />

oscillation.<br />

Figuur 3.5 Stroomdiagram van initialisatie en normaal bedrijf<br />

During normal operation an endless<br />

loop is being performed. Here presented<br />

as a ‘C-style switch case’ instruction.<br />

The loop does basically three things:<br />

-wait for data<br />

-process them<br />

-send to midi.<br />

cptfull has enough data when the flag<br />

np_flag is set. It is set from within wavectrl.<br />

subsequently cptfull calculates a mean value<br />

from a number of captured periods.<br />

convert calculates from the value from the<br />

previous step a level of force which lies<br />

between 0 (no force) and 127 (maximum force).<br />

sendmd1, waittx and sendmd2 take care of<br />

transmitting the level of force to the midi port.<br />

caseovr puts registers back into an initial<br />

state, which permits waiting for new data.<br />

34


3.1.2.2 Linearisatie<br />

We hebben de sensor opgenomen in het circuit met de relaxatie-<strong>oscillator</strong>, figuur 2.25. De sensor lag<br />

op een vlakke horizontale plaat. Op de sensor hebben we verschillende massa’s, met bekende waarden,<br />

gelegd en vervolgens de daarbij behorende periode gemeten. Het verband tussen de uitgeoefende<br />

kracht en de periode is a-lineair. Grafiek 3.1 toont dit verband.<br />

<br />

<br />

2.80<br />

2.75<br />

2.70<br />

2.65<br />

2.60<br />

2.55<br />

0 5 10 15 20 25 30<br />

<br />

Grafiek 3.1 Periode van de relaxatie-<strong>oscillator</strong> uitgezet tegen de kracht. (bij 18<br />

Door in de software gebruik te maken van een tabel kunnen we op een eenvoudige wijze de gemeten<br />

periode omzetten naar een kracht. Om de tabel te maken hebben we een functie gezocht waarmee we<br />

de kracht, uitgezet tegen de periode, kunnen benaderen. (Grafiek 3.2)<br />

<br />

<br />

30<br />

25<br />

20<br />

15<br />

10<br />

5<br />

0<br />

2.54 2.56 2.58 2.60 2.62 2.64 2.66 2.68 2.7<br />

<br />

Grafiek 3.2 Gemeten en benaderde kracht uitgezet tegen de periode.<br />

Meting<br />

Benadering<br />

35


Gezien de vorm kunnen we een benadering proberen met een logarithmische functie. In het bereik<br />

waarmee we te maken hebben, vermenigvuldigen we de periode met 4000, zodat tussen de grootste en<br />

de kleinste waarde een verschil van 1 komt. Na aftrekking van 10.2 komt de periode ongeveer tussen 0<br />

en 1 te liggen. Als we hiervan een logarithmische functie nemen, krijgen we een nagenoeg rechte lijn,<br />

die we gebruiken om het volgende functievoorschrift te maken:<br />

F(T)<br />

=<br />

*<br />

9.82 $ ( - ln(4000 $ T - 10.2) - 0.5894)<br />

0.9813<br />

– 710.18 · T + 1.9537<br />

als T G 0.00269<br />

als T > 0.00269<br />

36<br />

(3.1)<br />

(T in s, F in N)<br />

Voor perioden groter dan 2.69 ms is het verloop van de curve zo steil, dat de logarithmische functie<br />

geen goede benadering meer geeft. Daarom hebben we het tweede deel in het functievoorschrift opgenomen,<br />

dat dit (korte) lijnstukje beschrijft. Dit deel is in grafiek 3.2 met een stippellijntje weergegeven.<br />

Voor de tabel hebben we nog de volgende gegevens nodig:<br />

• Kortste periode: 2.561 ms<br />

• Langste periode: 2.751 ms<br />

• Kleinste waarde in de tabel: 0<br />

• Grootste waarde in de tabel: 127<br />

• Aantal elementen in de tabel: 256<br />

Vervolgens hebben met behulp van een spreadsheet de volgende waarden berekend: (Tabel 3.1)<br />

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0<br />

16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0<br />

32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0<br />

48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0<br />

64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0<br />

80 0 0 0 0 0 0 1 1 1 1 2 2 2 3 3 3<br />

96 3 4 4 4 5 5 5 6 6 6 6 7 7 7 8 8<br />

112 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 14<br />

128 14 14 15 15 15 16 16 17 17 17 18 18 18 19 19 20<br />

144 20 20 21 21 22 22 23 23 23 24 24 25 25 26 26 27<br />

160 27 28 28 29 29 29 30 30 31 31 32 32 33 34 34 35<br />

176 35 36 36 37 37 38 39 39 40 40 41 42 42 43 43 44<br />

192 45 45 46 47 47 48 49 50 50 51 52 53 53 54 55 56<br />

208 56 57 58 59 60 61 62 63 64 64 65 66 67 68 69 71<br />

224 72 73 74 75 76 77 79 80 81 83 84 85 87 88 90 91<br />

240 93 95 97 98 100 102 104 106 108 111 113 116 118 121 124 127<br />

Tabel 3.1 Linearisatie periode. naar kracht<br />

Ook de temperatuur van de sensor heeft invloed op de capaciteit, en daarmee op de periode. In de volgende<br />

grafiek (3.3) hebben we het temperatuurverloop gegeven bij drie verschillende krachten:


400<br />

390<br />

380<br />

370<br />

360<br />

350<br />

340<br />

330<br />

0 10 20 30 40<br />

<br />

Grafiek 3.3 Temperatuurverloop sensor bij verschillende krachten<br />

2.17 N<br />

4.75 N<br />

11.4 N<br />

Boven 26 ˚C vallen de frequenties van de verschillende kracht samen. Voor een goede werking van de<br />

sensor is het belangrijk dat temperatuur niet te veel afwijkt van 18 ˚C.<br />

3.1.3 Calibratie<br />

Nadat de microcontroller een aantal registers heeft geïnitialiseerd, wordt er een subroutine calib<br />

uitgevoerd die de periode van het uitgangssignaal meet, afkomstig van de relaxatie-<strong>oscillator</strong>. Het<br />

resultaat is een 16-bits waarde. Op dit moment wordt de relaxatie-<strong>oscillator</strong> nog niet actief gestuurd.<br />

Door de periode iets te verkorten, kunnen we tijdens de actieve sturing op een later moment ervoor<br />

zorgen dat tijdens de stijgende flank de bewegingsrichting van de sensor gelijk is aan wat hij door de<br />

sturing krijgt opgelegd. Zonder die correctie zou de sensor door de relaxatie-<strong>oscillator</strong> aan het eind van<br />

de periode onder bepaalde omstandigheden uit zichzelf van bewegingsrichting kunnen veranderen, en<br />

vervolgens door de sturing weer opnieuw gestart worden. We zijn van mening dat dit kort achter elkaar<br />

opnieuw starten onwenselijk is, doordat het een gelijkmatige beweging verstoort. De verkorting van<br />

de periode is vastgelegd in de constante shift_wv. We hebben deze proefondervindelijk vastgesteld op<br />

25μs bij een periode van 2.7 ms. Bij deze tijd ontstonden er net geen vroegtijdige flanken meer. De periode<br />

waarmee de sensor gestuurd wordt is daardoor 0.9% korter. Na aftrekking van shift_wv (in μs!)<br />

van de gemeten periode (in machinecycles!) staat het resultaat in de registers prd_h en prd_l.<br />

De calibratie wordt vervolgd in de routine calc_t0, vanwaar een verdeling gemaakt wordt van prd(_<br />

h/_l) over de tijd die de drie fasen (zie figuur 3.6) in beslagen nemen tijdens een meetcyclus.<br />

lo_TH0<br />

lo_TL0<br />

sigpw<br />

hi_TH0<br />

hi_TL0<br />

prd_h/prd_l<br />

Figuur 3.6 Verdeling prd_h/prd_l<br />

hivdTH0<br />

hivdTL0<br />

sens_tm<br />

37


De verdeling berekent de microcontroller met de volgende formules:<br />

- 6<br />

sigpw $ 10 $ Xtal<br />

lo_tmr0 = 0x(1)0000h -<br />

12<br />

sens_tm<br />

hi_tmr0 = 0x(1)0000h - ( prd - lo_tmr0) $ (1 - )<br />

100<br />

hivdtmr0 = 0x(1)0000h - ( prd - lo_tmr0) $<br />

(sigpw in μs, Xtal in MHz)<br />

sens_tm<br />

100<br />

De reden waarom we 0x(1)0000h geschreven hebben is rekentechnisch. Softwarematig trekken we<br />

de getallen van 0 af, maar om de begripsvorming hier duidelijker te maken dat we niet met negatieve<br />

getallen werken, zoals ‘signed-integers’, hebben we hier 0x10000h opgeschreven.<br />

Van de drie berekende waarden wordt tenslotte nog een constante afgetrokken, zodat we een correctie<br />

kunnen maken voor de tijd die er ligt tussen het stoppen van de timer, het schrijven van de nieuwe<br />

waarden in de timerregister en het starten van de timer.<br />

38


3.1.4 Complete schakeling<br />

Om de microcontroller te laten werken, hoeven we behalve een 5 V voeding alleen nog een kristal van<br />

12 MHz en een reset-circuit aan te sluiten. De MIDI-connector is via transistor Q4 verbonden met de<br />

seriële uitgangspoort RXD. Het uitgangssignaal van de relaxatie-<strong>oscillator</strong> verbinden we via de twee<br />

in serie geschakelde NAND-poorten, weerstand R4 en transistor Q2 zowel aan /INT0 als aan /INT1<br />

(deze hebben een interne pull-up), zodat we in de software via maskering van de interrupts kunnen<br />

bepalen waar er naartoe moet worden gesprongen als er een interrupt optreedt. De sturing van de <strong>oscillator</strong><br />

is via weerstand R5 en transistor Q1 aangesloten op een van de vrije poorten van de controller.<br />

Vin<br />

C4<br />

10u<br />

Vadj.<br />

1<br />

Q1<br />

BC558C<br />

R5<br />

12k<br />

U9<br />

LM78L05A/TO39<br />

VIN<br />

GND<br />

3<br />

D1<br />

D1N4148<br />

VOUT<br />

2<br />

R3<br />

3k3<br />

+5v<br />

19<br />

18<br />

17<br />

16<br />

15<br />

14<br />

13<br />

12<br />

C5<br />

10u<br />

U5<br />

GND GND<br />

GND GND GND<br />

GND<br />

1<br />

2<br />

R1<br />

50k<br />

P1.7<br />

P1.6<br />

P1.5<br />

P1.4<br />

P1.3<br />

P1.2<br />

P1.1/AIN1<br />

P1.0/AIN0<br />

AT89C2051<br />

R7<br />

1k<br />

R8<br />

4k7<br />

10<br />

U1A<br />

CD4011A<br />

GND<br />

X2<br />

Piezo Cry stal<br />

R2<br />

3<br />

82k<br />

GND<br />

VCC<br />

RST/VPP<br />

XTAL2<br />

XTAL1<br />

P3.7<br />

P3.5/T1<br />

P3.4/T0<br />

P3.3/INT1<br />

P3.2/INTO<br />

P3.1/TXD<br />

P3.0/RXD<br />

Q3<br />

BC547A<br />

C6<br />

1uF<br />

20<br />

1<br />

4<br />

5<br />

5<br />

6<br />

11<br />

9<br />

8<br />

7<br />

6<br />

3<br />

2<br />

Vadj.<br />

+5v<br />

U1B<br />

CD4011A<br />

+5v<br />

4<br />

GND<br />

X1<br />

C2<br />

20pF<br />

R11<br />

220<br />

R10<br />

220<br />

GND<br />

R9<br />

1k Q4<br />

BC557C<br />

8<br />

9<br />

4<br />

5<br />

Vadj.<br />

14<br />

7<br />

12.0 MHz<br />

C3<br />

20pF<br />

GND<br />

U1C<br />

CD4011A<br />

Reset<br />

Fig. 3.7 Totale schema van de krachtsensor aangesloten op de microcontroller<br />

3<br />

+5v<br />

GND<br />

10<br />

C1<br />

1.0uF<br />

R6<br />

68k<br />

2<br />

1<br />

13<br />

12<br />

JP1<br />

DIN 5P<br />

U1D<br />

CD4011A<br />

Midi Out<br />

11<br />

R4<br />

10k<br />

Q2<br />

BC548B<br />

GND<br />

39


3.2 PC-Software<br />

Het programma dat de data op de PC opneemt, de sequencer, moet deze data kunnen exporteren als<br />

een standaard MIDI File. Ons converteringsprogramma kan zowel type 0 (single multi-channel track)<br />

als type 1 (one or more simultaneous tracks) MIDI Files importeren. Het formaat waarnaar we converteren<br />

is een textbestand met getallen, gescheiden door een komma en een ‘carriage return’: (hier<br />

vanwege de overzichtelijkheid weergegeven in zes kolommen.)<br />

40 mit<br />

384<br />

0,0<br />

3,32<br />

9,56<br />

14,28<br />

19,36<br />

26,44<br />

31,52<br />

41,60<br />

48,52<br />

53,44<br />

58,60<br />

80,56<br />

85,52<br />

92,64<br />

114,68<br />

235,76<br />

240,68<br />

516,64<br />

528,60<br />

539,64<br />

545,68<br />

699,72<br />

710,76<br />

726,88<br />

733,84<br />

738,72<br />

743,68<br />

804,64<br />

809,68<br />

876,64<br />

881,60<br />

888,64<br />

893,60<br />

898,64<br />

903,68<br />

920,72<br />

925,68<br />

1218,72<br />

1223,68<br />

1384,60<br />

1395,64<br />

etc.<br />

In MIDI Files wordt de informatie in pakketten, ‘chunks’ genaamd, ingebed. De pakketten hebben<br />

een header met een vaste structuur en een gedeelte dat volgt met een variabele structuur. Omdat er bij<br />

de vaste structuur opgegeven wordt hoeveel data er in de daarnavolgende variabele structuur komen,<br />

kunnen MIDI Files altijd gelezen worden. Immers, zonder de opbouw van stuurcodes te kennen, kunnen<br />

namelijk data overgeslagen worden. In hoeverre de gegevens ook verwerkt kunnen worden hangt<br />

uiteraard van het implementatieniveau af van de software.<br />

Met verwijzing naar de appendix, waar een uitvoerige beschrijving over de opbouw van MIDI Files<br />

wordt gegeven, laten we de basisstructuur van MIDI Files zien. Het conversieprogramma volgt die<br />

structuur. Om inzicht in de opbouw van dit programma te krijgen, laten we een aantal details weg,<br />

zodat een soort C++ raamwerk van dit programma overblijft:<br />

while ( ( c=getc(fp)) != EOF)<br />

switch (head)<br />

{<br />

case MHDR:<br />

{<br />

if (what2do!=ERR) // process header data if head == TRUE<br />

switch (what2do)<br />

{<br />

case HDR:<br />

..<br />

case HBL:<br />

..<br />

case FMT:<br />

..<br />

case NTB:<br />

..<br />

case RES:<br />

..<br />

} // escape from switch (what2do)<br />

// endif (what2do!=ERR)<br />

break; // escape from case MHDR<br />

}<br />

case MSIG:<br />

{<br />

if (mfmt==1 && what2do!=ERR) // process time/signature tempo track data in format 1<br />

{<br />

switch (what2do)<br />

{<br />

case SHDR:<br />

40


..<br />

case SHL:<br />

..<br />

case STAIL:// need only tempo, if more needed, change case META_DATA<br />

..<br />

} // end switch (what2do)<br />

} // end if<br />

break; // escape from case MSIG<br />

}<br />

case MTRK:<br />

{<br />

if (mfmt==1 && what2do!=ERR) // process track data in format 1<br />

{<br />

switch (what2do)<br />

{<br />

case THDR:<br />

..<br />

case TRKL:<br />

..<br />

case TTAIL:// proces control code AFTER_TOUCH somewhere<br />

..<br />

} // end switch (what2do)<br />

break;<br />

} // escape from case MTRK<br />

}<br />

} // end switch (head)<br />

// end while<br />

In de eerste regel van het bovenstaande C-raamwerk wordt er net zolang een character uit de MIDI<br />

File gelezen, totdat er geen character meer volgt. Zonder dat er eerst gedecomprimeerd moet worden,<br />

kunnen we de deze data sequentieel lezen. Deze data hebben altijd de volgende structuur en kunnen als<br />

‘chunk’ of pakket meerdere keren achter elkaar voorkomen:<br />

•Een identificatiecode van 4 ASCII characters aan het begin van een pakket, gevolgd door<br />

•een ‘word’ van 4 bytes die het aantal volgende bytes behorende bij dit pakket aangeeft.<br />

•De eigenlijke data.<br />

Er bestaan maar twee identieficatiecodes. Dit zijn de characters ‘MThd’ (Master Track header) of<br />

‘MTrk’ (Master Track). Een MIDI File begint altijd met ‘MThd’, waarna de tracks daarna altijd met<br />

‘MTrk’ beginnen:<br />

MThd <br />

MTrk <br />

MTrk <br />

etc.<br />

...<br />

In het programma wordt de ‘MThd’ header aan het begin getest bij case HDR doordat de variabelen<br />

head en what2do juist zijn geïnitialiseerd. Als er inderdaad ‘MThd’ staat, kunnen we de daarnavolgende<br />

vier bytes lezen, die de omvang van het pakket aangeven. Dit gebeurt door de variabele<br />

what2do gelijk te maken aan de constante HBL (what2do wordt met een opgehoogd), zodat het<br />

programma case HBL bereikt. Opnieuw wordt what2do met een opgehoogd. Het krijgt de waarde van<br />

FMT, zodat in case FMT kan worden vastgesteld of we met een type 0 of type 1 MIDI File te maken<br />

hebben. Daarna wordt met behulp van what2do, case NTB en RES doorlopen. In case NTB lezen we<br />

uit de file hoeveel ‘Master Tracks’ er nog volgen. Tenslotte stellen we in case RES de resolutie van<br />

een kwartnoot vast. Deze wordt uitgedrukt in een deltatijd (delta-time). De deltatijd bepaalt in hoeveel<br />

stappen een kwartnoot wordt onderverdeeld. Samen met het tempo, bepaalt zij de kortst mogelijke<br />

opeenvolging van twee meetwaarden.<br />

De volgende ‘chunk’ of pakket ‘MTrk’ komt nu een beurt. De variabele head wordt gelijk aan MSIG,<br />

waardoor case MSIG doorlopen wordt. Hier wordt de time signature data verwerkt. Omdat we met het<br />

41


tempo verder niets doen, wordt dit alleen pro-forma doorlopen, totdat we bij de data komen waar we in<br />

geïnteresseerd zijn. Deze data vinden we in case MTRK. Na de vier characters ‘MTrk’ en de (4 bytes-<br />

)word dat de pakketgrootte aangeeft volgt hier de data van de krachtsensor. In de case TTAIL worden<br />

twee belangrijke zaken bijgehouden. Ten eerste wordt er konkreet gezocht naar ‘After Touch’ MIDI<br />

Events. Ten tweede houden we de plaats bij waarop dit event staat. Nadat er een ‘After Touch’ MIDI<br />

Event gevonden wordt, kunnen we de volgende byte verwachten die de grootte van de kracht aangeeft.<br />

Deze waarde slaan we op in de variabele data1. De plaats waar het event staat en data1 schrijven we<br />

aan het eind van TTAIL naar het textbestand. De laatste case wordt net zolang doorlopen totdat alle<br />

bytes gelezen zijn van deze track. Aan het eind van een track wordt het tekstbestand afgesloten. Als<br />

deze track de laatste was, is het programma klaar. Anders herhaalt het programma case MTRK, waar<br />

weer een nieuw tekstbestand gemaakt wordt met de data uit de volgende track.<br />

42


4. Mechanische constructie en realisatie<br />

Standaard, zoals eerder uitgelegd, zit op de meeste violen een kinhouder. We willlen de kracht meten<br />

die door de violist hierop wordt uitgeoefend. Daartoe wordt het meetapparaat uitgevoerd als kinhouder.<br />

Om het spel niet te beïnvloeden, dient het meetapparaat dezelfde eigenschappen hebben die voor<br />

een kinhouder noodzakelijk zijn. Dit zijn:<br />

• Juiste pasvorm van het gedeelte op de bovenkant dat tegen de kaak aankomt (de houder).<br />

• Juiste hoogte, zodat het hoofd niet ingetrokken of zijwaarts gekanteld hoeft te worden.<br />

• Stevige montage op het instrument, het liefst in het midden over het staartstuk, omdat daar het<br />

instrument het stevigst is, en omdat dat voor de klank beter is.<br />

Daarenboven dient het meetapparaat ook te voldoen aan de volgende eisen:<br />

•De plaatsing van de sensor zodanig dat de denkbeeldige krachtlijn van de kaak door de kinhouder<br />

naar het instrument, midden door de sensor gaat, die zich vlak onder de houder bevindt.<br />

• De houder moet beweegbaar zijn in verticale richting, in de richting van de krachtlijn, zodat de<br />

kracht van de kaak op de houder overgedragen kan worden op de sensor.<br />

• De houder moet in hoogte verstelbaar zijn, zodat die aangepast kan worden aan de lengte van de<br />

nek van de instrumentalist.<br />

• De afmetingen dienen ondanks de toegevoegde onderdelen bescheiden te blijven.<br />

Om de kracht goed op de sensor te kunnen overdragen en de houder voldoende stevigheid te geven,<br />

monteren we de houder op een scharnier. (Figuur 4.1) Aan het andere been van het scharnier zit een<br />

v-vormig plaatje dat deel uitmaakt van een zwaluwstaartgeleiding. Omdat de zwaluwstaartgeleiding<br />

onder een hoek is bevestigd, kunnen we de hoogte van de houder aanpassen aan de instrumentalist: als<br />

we de houder helemaal naar links verschuiven staat die op zijn hoogst, en in de rechtse stand helemaal<br />

op zijn laagst. (Figuur 4.2, 4.3 en 4.5)<br />

Geleidingssysteem<br />

Scharnier<br />

Klemschroef om deel vast te zetten<br />

Drukrichting kaak<br />

Fig. 4.1 Constructieschets meetapparaat<br />

Beweegbaar deel kinhouder<br />

Onderaanzicht beweegbaar deel<br />

Klemdeel kinhouder dat op instrument<br />

vastgezet wordt.<br />

43


Fig. 4.2 Hoge stand kinhouder Fig. 4.3 Middenstand kinhouder Fig. 4.5 Lage stand kinhouder<br />

De sensor monteren we tussen de benen van het scharnier, midden onder de houder. (Figuur 4.4)<br />

Fig. 4.4 Plaatsing sensor Fig. 4.6 Besturing<br />

De elektronica die de sensor bestuurt en alle data verwerkt zit achter het vaste deel van de kinhouder.<br />

(Figuur 4.6)<br />

44


4.1 Specificaties van het systeem<br />

4.1.1 Meetbereik<br />

De op de kinhouder uitgeoefende krachten liggen in de ordegrootte van 0 tot 20 N. Het meetapparaat<br />

kan krachten meten tot 25 N. Daarmee wordt het gewenste meetbereik ruimschoots gehaald.<br />

4.1.2 Gevoeligheid<br />

In de grafiek waarin de frequentie is uitgezet tegen de kracht (grafiek 4.7), zien we dat bij een lage<br />

kracht de periode veel meer verandert dan bij een grote kracht. De gevoeligheid is daardoor bij kleine<br />

krachten veel groter dan bij grote krachten.<br />

<br />

<br />

2.80<br />

2.75<br />

2.70<br />

2.65<br />

2.60<br />

2.55<br />

0 5 10 15 20 25 30<br />

<br />

Grafiek 4.7 Periode van de relaxatie-<strong>oscillator</strong> uitgezet tegen de kracht. (bij 18 ˚C)<br />

4.1.3 Meetfrequentie<br />

Om een duidelijk beeld te krijgen van het krachtverloop tijdens het spelen, dienen er voldoende metingen<br />

per tijdseenheid uitgevoerd te worden. De meetfrequentie is (bij 18˚C) ingesteld op 370 Hz. Deze<br />

meetwaarden worden per 10 gemiddeld, en het gemiddelde verschijnt op de MIDI-uitgang.<br />

4.1.4 Omgevingsinvloeden<br />

De invloeden waarmee we te maken hebben zijn zowel invloeden van het meetapparaat op de proefpersoon<br />

als invloeden van de proefpersoon op het meetapparaat.<br />

• Stoorgeluiden: Omdat de relaxatie-<strong>oscillator</strong> de sensor in beweging zet tijdens het meten, wekt<br />

de sensor stoorgeluiden op. Deze zijn tot een afstand van ongeveer 30 centimeter nog net hoorbaar.<br />

Tijdens het spelen zijn deze geluiden onhoorbaar omdat het instrument de sensor overstemt.<br />

• Mechanische trillingen: Als de mechanische trillingen van het instrument lager dan de meetfrequentie<br />

zijn, is de kans veel groter dat de schakeling verkeerd getriggerd wordt. Omdat de<br />

altviool lager gestemd is, en de mechanische trillingen groter zijn dan bij de viool, is het bij lage<br />

tonen nodig de trillingen van de altviool te dempen met een zwaar blok op de kam.<br />

• Omgevingstemperatuur: Omdat de temperatuur invloed heeft op de meetsensor, dient de omgevingstemperatuur<br />

te liggen tussen 18 en 22˚C<br />

4.1.5 Massa van het meetapparaat<br />

Ondanks de toegevoegde functionaliteit is het meetapparaat, vanwege de gebruikte materialen, niet<br />

zwaarder dan een gewone kinhouder. Zonder aansluitdraden voor de voedingsspanning is de massa 94<br />

gram<br />

45


5. Meetresultaten<br />

s<br />

e<br />

i<br />

l<br />

r<br />

a<br />

M<br />

t<br />

i<br />

m<br />

0<br />

4<br />

s<br />

e<br />

i<br />

l<br />

r<br />

a<br />

M<br />

e<br />

n<br />

h<br />

o<br />

0<br />

4<br />

s<br />

e<br />

i<br />

l<br />

r<br />

a<br />

M<br />

t<br />

i<br />

m<br />

0<br />

6<br />

s<br />

e<br />

i<br />

l<br />

r<br />

a<br />

M<br />

e<br />

n<br />

h<br />

o<br />

0<br />

6<br />

s<br />

e<br />

i<br />

l<br />

r<br />

a<br />

M<br />

t<br />

i<br />

m<br />

0<br />

8<br />

s<br />

e<br />

i<br />

l<br />

r<br />

a<br />

M<br />

t<br />

i<br />

m<br />

0<br />

0<br />

1<br />

figuur 5.1 Kindrukmeting van proefpersoon 1<br />

c<br />

r<br />

a<br />

M<br />

Bij acht musici hebben we met behulp<br />

van het apparaat metingen verricht.<br />

Speciaal hiervoor hebben we een<br />

oefening uitgezocht waarbij specifieke<br />

houdings- en bewegingskenmerken<br />

van belang zijn, zodat bepaalde<br />

spanningen en afwijkingen daarvan<br />

gemeten kunnen worden. In de muzieknotatie<br />

leggen we vast met welke<br />

vinger en op welke snaar gespeeld<br />

moet worden. Bij de resultaten hebben<br />

we in de muzieknotatie bovendien<br />

een speciaal teken met vertikale lijnen<br />

aangebracht, die aangeeft waar in ieder<br />

geval kindruk noodzakelijk is. Op de<br />

horizontale as van de grafieken staat<br />

de tijd afgezet, die afhankelijk is van<br />

het tempo waarin gespeeld wordt. We<br />

drukken deze uit in het metronoomcijfer,<br />

vooraan de grafieken. Op de verticale<br />

as staat de kindruk. De schaling<br />

is bij alle personen dezelfde, zodat we<br />

de meetwaarden kunnen vergelijken.<br />

Vooraan de verticale as staat tenslotte<br />

ook nog geschreven of de test met of<br />

zonder een schoudersteun is uitgevoerd.<br />

We kiezen twee personen uit<br />

waarvan we de serie metingen hier<br />

afbeelden (figuur 5.1 en 5.2). Omdat<br />

een uitgebreid inzicht in deze materie<br />

buiten het bestek valt van dit afstudeerverslag,<br />

verwijzen we voor de<br />

overige metingen naar het internet 11 .<br />

Als eerste tonen we de meetresultaten<br />

van persoon 1. Deze persoon heeft een<br />

korte nek, speelt gewoonlijk met een<br />

schoudersteun, maar probeert soms<br />

ook zonder te spelen. Omdat haar linkerhand<br />

relatief klein is, lukt het haar<br />

niet in hoge posities te spelen, zonder<br />

dat de duim onder de hals van het<br />

instrument blijft. Dit verklaart waarom<br />

of waarvoor zij de schoudersteun<br />

nodig heeft. Deze neemt de dragende<br />

functie van de duim over. Het instrument<br />

steunt op het sleutelbeen. Duidelijk<br />

is te zien dat ze het instrument heel<br />

gelijkmatig vastklemt, hoewel dat op<br />

veel momenten niet noodzakelijk is.<br />

46<br />

t<br />

i<br />

m<br />

0<br />

4<br />

Marc<br />

40<br />

ohne<br />

Marc<br />

60<br />

mit<br />

Marc<br />

60<br />

ohne<br />

Marc<br />

80<br />

ohne<br />

c<br />

r<br />

a<br />

M<br />

e<br />

n<br />

h<br />

o<br />

0<br />

0<br />

1


co<br />

r<br />

a<br />

M<br />

t<br />

i<br />

m<br />

0<br />

4<br />

co<br />

r<br />

a<br />

M<br />

e<br />

n<br />

h<br />

o<br />

0<br />

4<br />

co<br />

r<br />

a<br />

M<br />

t<br />

i<br />

m<br />

0<br />

6<br />

co<br />

r<br />

a<br />

M<br />

e<br />

n<br />

h<br />

o<br />

0<br />

6<br />

co<br />

r<br />

a<br />

M<br />

e<br />

n<br />

h<br />

o<br />

0<br />

8<br />

co<br />

r<br />

a<br />

M<br />

e<br />

n<br />

h<br />

o<br />

0<br />

0<br />

1<br />

figuur 5.2 Kindrukmeting van proefpersoon 2<br />

Proefpersoon 2 heeft een tamelijk<br />

lange nek, waardoor we de kinhouderhoogte<br />

hieraan moeten aanpassen.<br />

In vergelijking met proefpersoon<br />

1 is de linkerhand groot<br />

genoeg om ook in hoge posities de<br />

duim onder de hals van het instrument<br />

te houden. Omdat de linkerhand<br />

het instrument in de meeste<br />

gevallen kan dragen kan proefpersoon<br />

2 zonder problemen zonder<br />

schoudersteun spelen. De afwezigheid<br />

van een permanente kracht in<br />

de meetresultaten bevestigd dat er<br />

op de juiste manier gespeeld wordt.<br />

Voor een iets beter houvast gebruikt<br />

deze persoon een zeemleren doekje<br />

tussen de kin en de kinhouder. De<br />

reden dat de meetresultaten ook bij<br />

verschillende tempi goed reproduceerbaar<br />

zijn, is dat deze persoon<br />

de oefening uitgebreid geoefend<br />

heeft. De meetresultaten zijn zoals<br />

verwacht: Op de plaatsen waar we<br />

druk verwachten, zien we dit ook<br />

terug.<br />

47


5.1 Inzichten uit de meetresultaten<br />

Door meetresultaten tussen spelers onderling te vergelijken, kan de violist (of altviolist) een objectiever<br />

inzicht krijgen in zijn eigen speelwijze dan voorheen. Immers, als de vraag gesteld wordt of hij<br />

of zij met veel spanning speelt, wordt dat gewoonlijk door iedereen verschillend beantwoord. Of de<br />

speler moet overwegen zijn speelwijze te veranderen om mogelijke problemen te voorkomen, kan niet<br />

eenduidig hierdoor aangegeven worden, omdat iedere speler een andere mate van tolerantie heeft.<br />

Een belangrijk inzicht aan de hand van de meetresultaten is of de violist spieren wellicht efficiënter<br />

kan benutten. De instrumentalist kan nagaan of het vastklemmen van het instrument op alle momenten<br />

werkelijk noodzakelijk is geweest. Als het instrument op het sleutelbeen rust en aan de andere kant<br />

met de linkerhand wordt gedragen, kan het zijn dat de hand teveel gehinderd wordt om tegelijkertijd<br />

te bewegen. Met bepaalde oefeningen kan het mogelijk zijn de linkerhand meer vrijheden te geven,<br />

zonder zijn dragende functie te verliezen.<br />

Bij de uitgevoerde metingen waren we er ook in geïnteresseerd op wat voor manier bij het spelen<br />

van een passage, het tempo invloed heeft op de spanning. Komen er bijvoorbeeld bij passages die in<br />

een hoog tempo gespeeld worden paniekreacties voor, waardoor het instrument steviger wordt vastgeklemd?<br />

Uit de resultaten zien we hiertussen geen duidelijk verband. Bij sommige violisten is de<br />

kindruk bij een hoog tempo voortdurend aanwezig. Bij andere violisten lijkt het erop dat juist bij een<br />

langzaam tempo vaker met de kin gedrukt wordt.<br />

Problemen die zich kunnen voordoen, vinden we met name bij altviolisten. Voor hen geldt dat relatief<br />

gezien de meeste lichamelijke klachten bij de nek en de schouders zich manifesteren 12 . (Tabel 5.1).<br />

Bovendien komen de klachten meer aan de linkerkant voor dan aan de rechterkant.<br />

Schoudergewricht<br />

Bovenarm<br />

Elleboog<br />

Onderarm<br />

Pols<br />

Vinger<br />

10<br />

10<br />

12<br />

11<br />

13<br />

12<br />

14<br />

15<br />

11<br />

0 5 10<br />

13<br />

15 20 25 30 %<br />

Tabel 5.1 Overzicht lokalisatie van lichaamsklachten bij<br />

altviolisten met een verhouding links tegen rechts van 2:1.<br />

Uit een groep van 311 altviolisten<br />

18<br />

25<br />

Altviool ≤ 40 cm<br />

Altviool > 40 cm<br />

Als een altviolist klachten heeft en uit de meetresultaten ziet dat er met meer spanning gespeeld wordt<br />

dan nodig is, zou hij of zij dit kunnen verminderen door andere hulpmiddelen te nemen, zoals een<br />

kinhouder met een andere hoogte en/of vorm of een andere schoudersteun die de schouders meer ontlast<br />

of zelfs zonder schoudersteun. Meestal valt er ook wel iets aan de speelwijze te veranderen. Door<br />

bijvoorbeeld de linkerhand een aktievere rol te geven het instrument te dragen tijdens het spelen.<br />

De kinhouder zorgt met name voor een bewustwording van de speelwijze, waardoor uiteindelijk de<br />

expressie in de muziek beter beheersbaar is.<br />

48


6. Conclusies en aanbevelingen<br />

6.1 Conclusies<br />

We kunnen met succes kindruk meten met het meetapparaat tijdens het spelen op de viool of altviool.<br />

De ontwikkeling van het apparaat en de verwerking van de meetgegevens heeft inzichten gegeven in<br />

zowel elektrotechnische als muziektechnische aspekten. Op de muziektechnische aspekten zijn we in<br />

hoofdstuk 5 nader ingegaan.<br />

Wat het meetapparaat en de meetresultaten betreft kunnen we het volgende concluderen:<br />

• Het apparaat geeft een goed beeld van de momenten waarop op de kinhouder gedrukt wordt en<br />

hoe die kracht verloopt tijdens het spelen.<br />

• Het apparaat is door gebruik te maken van een PZT-sensor in combinatie met een microcontroller<br />

zeer klein gebleven. Het apparaat kan daardoor op het eigen instrument van proefpersonen<br />

geplaatst worden. De afmetingen van het meetapparaat zijn te vergelijken met die van een standaard<br />

kinhouder. Hierdoor wordt het normale vioolspel niet beinvloed. Ook de bijgeluiden van<br />

de meting zijn onhoorbaar voor de proefpersoon.<br />

• Het apparaat is volledig inzetbaar in een muziekomgeving, omdat de meetdata in een standaard<br />

MIDI-formaat zijn. Hierdoor kan met de meeste sequencers probleemloos het meetsignaal, gecombineerd<br />

met het muzieksignaal (het vioolspel) geregistreerd worden, om later geanalyseerd<br />

te worden.<br />

• Door de meetresultaten te analyseren kan de instrumentalist inzien op welke wijze er aan de manier<br />

van spelen verbeteringen kunnen worden aangebracht. Een uitgebreid rapport valt te zien<br />

op het internet 11 .<br />

De gekozen sensor in het meetapparaat en de aansturing daarvan leidt tot de volgende conclusies:<br />

•Door het gebruik van een microcontroller is het meetsysteem automatisch calibreerbaar.<br />

•De kindruk is zeer goed met een PZT-sensor te meten. In tegenstelling tot wat werd verwacht, is<br />

niet de resonantiefrequentie van de sensor afhankelijk van de kracht, maar de impedantie van de<br />

sensor (hierna te noemen capaciteit). Deze capaciteit kan eenvoudig met een relaxatie-<strong>oscillator</strong><br />

worden bepaald. In ons geval was de capaciteit in onbelaste toestand 7.4 nF. Bij een kracht van<br />

25 N nam deze af tot 6.7 nF.<br />

•De capaciteitsverandering is niet te verklaren vanuit een model van een condensator waarbij<br />

twee platen met een gegeven oppervlak met daartussen een diëlektrikum een bepaalde afstand<br />

van elkaar afstaan. Hiervoor varieert de afstand te weinig, daar het kristal nauwelijks samendrukbaar<br />

is.<br />

•De relatie tussen opgelegde kracht en capaciteit blijkt uit de metingen a-lineair te zijn. Bij geringe<br />

massa’s is de capaciteitsverandering veel groter dan bij grote massa’s. Dit is gunstig voor<br />

metingen bij kleine krachten. Deze a-lineariteit wordt in de software gecompenseerd.<br />

6.2 Aanbevelingen<br />

Ondanks het succesvolle ontwerp van de kindrukmeter, zijn er toch nog enkele vragen en kan men op<br />

een aantal onderdelen verbeteringen doorvoeren.<br />

•De resonantiefrequentie van de sensor verandert, zoals bepaald binnen dit onderzoek, niet met<br />

de opgelegde druk; tegen onze verwachting in. Men zou een betere schakeling kunnen gebruiken<br />

die de resonantiefrequentie van de sensor meet. Daarvoor zou de sensor opgenomen kunnen<br />

worden in een brugschakeling. Een andere manier om nauwkeuriger de resonantiefrequentie te<br />

kunnen meten, is met behulp van een circuit waarin over een groot frequentiebereik de fase met<br />

49


90° wordt verschoven 13 . De lage Q-factor van de PZT-sensor versterkt het vermoeden dat er een<br />

betere schakelingen moet worden gebruikt. Mogelijk is de invloed van de temperatuur op de<br />

resonantiefrequentie veel kleiner dan op de capaciteit.<br />

•Temperatuurafhankelijkheid. Als men nauwkeuriger wil meten, en dan voornamelijk grote<br />

krachten (omdat daar de relatieve verandering van de frequentie gering is), moet er geprobeerd<br />

worden te voorkomen dat het temperatuurverloop de meetwaarden teveel beïnvloedt. Hiervoor<br />

kan gedurende het meten van de frequentie dit verloop ook in de gaten gehouden worden, en de<br />

gemeten capaciteit met de gemeten temperatuur compenseren. Een tweede mogelijkheid is de<br />

temperatuur van de sensor constant te houden.<br />

Zonder correctie/compensatie is het noodzakelijk om binnen niet te grote tijdsafstanden een<br />

calibratie uit te voeren, echter de meettijd wordt hierdoor wel beperkt.<br />

•Bij krachtuitoefening op een PZT-sensor, verandert niet alleen de capaciteit, maar wordt ook een<br />

spanning opgewekt. Deze spanning kan een storende werking hebben op de relaxatie-<strong>oscillator</strong>,<br />

omdat deze spanning de NAND-poorten kan triggeren, zodat deze van toestand wisselen. Een<br />

remedie hiertegen zou kunnen zijn het filteren van deze frequenties.<br />

•Mechanische trillingen van het instrument storen meetwaarden, dit kan opgelost worden door<br />

een mechanische ontkoppeling (Bijvoorbeeld demping met behulp van rubber). Daarnaast kan<br />

het (mechanische) systeem mogelijk geoptimaliseerd worden door de plaatsing en de constructie<br />

rond de sensor te verbeteren.<br />

• Met een ander type sensor, waarbij direct, zonder omwegen, de (statische) kracht kan worden<br />

omgezet in een uitgangsspanning, zou men de software aanmerkelijk kunnen vereenvoudigen.<br />

Met de juiste microcontroller (bijvoorbeeld de ATMEL ATtiny26) met ingebouwde AD-converter<br />

kan direct de uitgangsspanning van de sensor omgezet en verwerkt worden.<br />

• Op basis van de meetresultaten van hoofdstuk 5 zou men naast het inzicht op welke momenten<br />

er gedrukt wordt en hoe sterk, ook moeten onderzoeken op wat voor manier er gedrukt wordt.<br />

Te denken valt daarbij aan het inzicht in de snelheid van de krachtverandering tijdens het spelen.<br />

50


Sourcecode van het stuurprogramma voor de AT89C2051<br />

Voor een stroomdiagram waarin de hoofdlijn van het programma wordt uitgelegd, verwijzen we naar<br />

hoofdstuk 5. De sourcecode, die uitgebreid van commentaar is voorzien, begint met een kort overzicht<br />

te geven van alle geheugenadressen, variabelen, constanten, gebruikte registers en functies en interrupt<br />

vectoren. Deze opzet heeft ook een nadeel, omdat we altijd op twee plaatsen een verandering moeten<br />

aanbrengen, namelijk in het overzicht aan het begin en bij de code zelf. Functies laten we meestal<br />

voorafgaan door een blok commentaar, waarin onder andere de naam en de werking staan beschreven.<br />

Voor zover van toepassing hebben we ook door middel van de tekens -> en (wvmem): 000bh (interrupt vector for overflow in timer0)<br />

; (jmps to wavectrl: process: modificate sensctrl)<br />

51


; EXTI1->(sens_stamp): 0013h (interrupt vector for saving pos. of timer0)<br />

; (jmps to sens_stamp)<br />

; TIMER1 001bh (interrupt vector for overflow in timer1)<br />

; (process: carry timer overflow to OP_1)<br />

;************************* functions ***********************************<br />

; main: (main program starts here)<br />

; initsys: (initializes system: ports, interrupts, variables)<br />

; sett0_cnt: (initializes timer0 for counting)<br />

; sett1_cnt: (initializes timer1 for counting)<br />

; sett1_midi: (initializes timer1 for midi)<br />

; calc_t0: (calculates setup timer values for signal)<br />

; calib: (loads prd with proper values)<br />

; freqcap: (measures frequency on _int0<br />

; wavectrl(wvmem): (processes generated waveform)<br />

; sens_stamp(tmp_TH0,tmp_TL0) (stamps position of TH0 and TL0, when sensor)<br />

; Mul_16( (OP_0..3) * (R2,R3) ) (Performs multiply between long and int)<br />

; Div_16( (OP_0..3) / (R2,R3) ) (Performs division between long and int)<br />

;************************* constants ***********************************<br />

clk equ 12000 ;system clock in kHz<br />

baud equ 31250 ;bits/s on midi output<br />

sigpw equ 35 ;pulsewidth of lo state in µsec<br />

ccpins equ 12 ;#clockcycles per instruction<br />

proc_tm equ 20 ;est. processing time, interrupt routine<br />

num_per equ 10 ;#periods to calculate average<br />

shift_wv equ 25 ;force sensor to run in phase with wave (in µsec)<br />

sens_tm equ 46 ;sensor reaction is valid during 42% of hi-wave<br />

sensctrl bit P1.0 ;output port, controls sensor<br />

sensor bit P3.2 ;input port, sensor feed back signal<br />

midictrl equ 0d0h ;coding for general aftertouch with midi<br />

;************************** variables ***********************************<br />

wvmem equ 20h.0 ;bit adres for hi or lo state of output signal<br />

vldsns equ 20h.1 ;bit adres for valid sensor measurement (1=valid)<br />

np_flag equ 20h.2 ;flag for avialability of enough data from int. routine<br />

tmp_C equ 20h.3 ;flag for temporary storing Carry<br />

tmp_A equ 43h ;register is faster than stack<br />

prd_h equ 44h ;target period MSB in timer counting rate<br />

prd_l equ 45h ;target period LSB in timer counting rate<br />

lo_TH0 equ 46h ;duration lo state MSB timer0<br />

lo_TL0 equ 47h ;duration lo state LSB timer0<br />

hi_TH0 equ 48h ;duration hi state MSB timer0<br />

hi_TL0 equ 49h ;duration hi state LSB timer0<br />

hivdTH0 equ 4ah ;duration hi state, during which sensor may respond<br />

hivdTL0 equ 4bh ;duration hi state, during which sensor may respond<br />

OP_0 equ 4ch ;Operand1 (Highest byte unsigned long)<br />

OP_1 equ 4dh ;<br />

OP_2 equ 4eh ;<br />

OP_3 equ 4fh ;<br />

;R2 ;Operand2 (Highest byte unsigned ‘long’<br />

;R3 ;functions implement uptill now only int size<br />

TMP_0 equ 50h ;Highest byte unsigned long<br />

TMP_1 equ 51h ;<br />

TMP_2 equ 52h ;<br />

TMP_3 equ 53h ;<br />

tmp_TH0 equ 54h ;memory for position of TH0<br />

tmp_TL0 equ 55h ;memory for position of TL0<br />

wndwTH0 equ 56h ;size of valid window where sensor may respond<br />

wndwTL0 equ 57h ;size of valid window where sensor may respond<br />

case equ 58h ;task dispatcher in main<br />

midival equ 59h ;pressure (0..127) into control midivalue<br />

52


;******************* from here the assembly starts ***************************<br />

org RESET<br />

ajmp main ;continue at main<br />

;*****************************************************************************<br />

;* EXTI0 -> R3 *<br />

;* interrupt from EXTI0 occurred, so there had been a hi->lo transition *<br />

;* wvmem *<br />

;* interrupt from timer0 occurred, so there’s some work to do. Precalculated *<br />

;* values for TH0 & TL0 determine how long the hi-state (in hi_TH0 & hi_TL0) *<br />

;* and the lo-state (in lo_TH0 & lo_TL0) on output port sensctrl will last. *<br />

;* TH0, TL0 *<br />

;* interrupt from EXTI1 occurred, so sensor measures pressure *<br />

;* tmp_TH0,tmp_TL0) *<br />

;*****************************************************************************<br />

org EXTI1<br />

clr TR0 ;1 stop timer 0 during processing<br />

ajmp sens_stamp ;process routine there further<br />

;*****************************************************************************<br />

;* TIMER1 -> *<br />

;* interrupt from timer1 occurred during measuring frequency. Overflow from *<br />

;* TH1 causes a carry to variable OP_1 *<br />

;* op_1++<br />

reti<br />

;******************************** M A I N ************************************<br />

main: mov SP,#020h ;init stack;starts now at 020h+1=021h<br />

acall initsys ;setup system ports and variables<br />

acall sett1_cnt ;set timer1 for counting system clock (with EXT1)<br />

acall calib ;calibrate piezo sensor frequency<br />

acall sett1_midi ;set timer1 for midi transmission (and for debug)<br />

acall calc_t0 ;converts prd_h,prd_l,sigpw into lo/hi_t0/hivd<br />

mov A,#0 ;clear OP_0..3, to start sum from 0<br />

mov OP_0,A ;with accumulator I save 2 bytes, and speeds up!<br />

mov OP_1,A<br />

mov OP_2,A<br />

mov OP_3,A<br />

mov R2,A ;R2 with next R3 forms the divider<br />

mov R3,#num_per ;for fillbuf, R3 must be initialized<br />

mov case,A ;set task dispatcher with initial value 0<br />

acall sett0_cnt ;init timer0, initiate signal generating<br />

inccase: inc case ;case 1 is the first case<br />

loop: mov DPTR,#jmp_tbl<br />

mov ACC,case<br />

rl A<br />

jmp @A+DPTR<br />

jmp_tbl: ajmp loop<br />

ajmp cptfull<br />

ajmp convert<br />

ajmp sendmd1<br />

ajmp waittx<br />

53


ajmp sendmd2<br />

ajmp waittx<br />

ajmp caseovr<br />

;*****************************************************************************<br />

;* cptfull -> np_flag, OP_0..3, R2, R3 *<br />

;* from interrupt proces there might be enough captured date, to proces *<br />

;* further here: calculate average. When finished next task *<br />

;* OP_0..3 *<br />

;* The summing must be converted to a proper midi value (0..127) *<br />

;* midival is only stored if it is different from calculated value in acc. *<br />

;* If they’re equal, the dispatcher starts from beginning, so byte will not *<br />

;* be transmitted. *<br />

;* (OP_2..3) *<br />

;* 256 multiply: (OP_2..3) -> (OP_1..2) *<br />

;* division: (OP_0..3)/(wndwTH/L0) (creates value 0..255) *<br />

;* linearisation: array[0..255] -> 0..127 *<br />

;*****************************************************************************<br />

convert: clr C ;substr. ffffh - <br />

mov A,#0ffh<br />

subb A,OP_3 ;LSB<br />

mov OP_3,A<br />

mov A,#0ffh ;MSB<br />

subb A,OP_2<br />

mov OP_1,A ;store result in OP_1..2<br />

mov OP_2,OP_3 ;perform 256*multiply<br />

mov OP_3,#0<br />

mov R2,wndwTH0<br />

mov R3,wndwTL0<br />

acall Div_16 ;result in OP_0..3 (0..255)<br />

mov DPTR,#lintbl ;in lintbl is a table to linearizate output<br />

mov A,OP_3 ;index to accumulator<br />

movc A,@A+DPTR ;move array value to accumulator<br />

cjne A,midival,newmdi ;continue only if value is different<br />

ajmp caseovr ;in the beginning there was...<br />

newmdi: mov midival,A ;new value, so save value in midival<br />

ajmp inccase<br />

;*****************************************************************************<br />

;* sendmd1 -> *<br />

;* transmits first control byte midictrl *<br />

;* *<br />

;* transmits second midi value byte *<br />

;* *<br />

;* waits till byte is transmitted *<br />

;* *<br />

54


;* case has reached maximum, so now it is reset with value 1 *<br />

;* nothing *<br />

;* initializes port sensctrl and its flag wvmem *<br />

;* initializes syst.variable sigpw with a startup value *<br />

;* enable overflow interrupt timer0 and timer1 *<br />

;* set serial port to 8-bit UART *<br />

;* hi_TH0 & hi_TL0 *<br />

;* initializes timer 0 for the pulse generator. overflow *<br />

;* generates an interrupt to vector 000bh, routed to the *<br />

;* service routine for generating the pulses *<br />

;* nothing *<br />

;* initializes the timer1 for counting the system clock *<br />

;* nothing *<br />

55


;* initializes timer1 for generating baud rate for the *<br />

;* serial port, needed for midi *<br />

;* prd_h,prd_l,sigpw,sens_tm *<br />

;* calculates counter rate, needed for *<br />

;* keeping sensctrl alternating high or low, *<br />

;* using timer0 with an interrupt service *<br />

;* routine. Stores the calc. values in *<br />

;*


mov OP_2,#((sens_tm*256)/100) / 256 ;MSB sens_tm<br />

mov OP_3,#((sens_tm*256)/100) and 255 ;LSB sens_tm<br />

mov R2,hi_TH0 ;MSB hi_tmr0<br />

mov R3,hi_TL0 ;LSB hi_tmr0<br />

acall Mul_16<br />

mov hivdTH0,OP_1 ;MSB of result, which is factor 256 too hi<br />

mov hivdTL0,OP_2 ;LSB of result, so just shift byte<br />

mov wndwTH0,OP_1 ;MSB function convert uses the max. size<br />

mov wndwTL0,OP_2 ;LSB<br />

pop hi_TH0 ;calculate hi_tmr0 = hi_tmr0 - hivdtmr0<br />

pop hi_TL0<br />

clr C<br />

mov A,hi_TL0 ;hi_TL0 - hivdTL0<br />

subb A,hivdTL0<br />

mov hi_TL0,A<br />

mov A,hi_TH0 ;borrow in MSB?<br />

subb A,hivdTH0<br />

mov hi_TH0,A<br />

jnc ca_corl ;no borrow, so result still pos.<br />

mov hi_TL0,#001 ;timing is too short, so take minimum<br />

mov hi_TH0,#000 ;value, and store temp. in hi_t0<br />

ca_corl: clr C ;timing correction for lo_tmr0<br />

mov A,lo_TL0 ;lo_TL0 - proc_tm<br />

subb A,#proc_tm<br />

mov lo_TL0,A<br />

mov A,lo_TH0 ;borrow in MSB?<br />

subb A,#000<br />

mov lo_TH0,A<br />

jnc ca_crh ;no borrow, so result still pos.<br />

mov lo_TL0,#001 ;timing is too short, so take minimum<br />

mov lo_TH0,#000 ;value, and store temp. in lo_t0<br />

ca_crh: clr C ;timing correction for hi_tmr0<br />

mov A,hi_TL0 ;hi_TL0 - proc_tm<br />

subb A,#proc_tm<br />

mov hi_TL0,A<br />

mov A,hi_TH0 ;borrow in MSB?<br />

subb A,#000<br />

mov hi_TH0,A<br />

jnc ca_crhv ;no borrow, so result still pos.<br />

mov hi_TL0,#001 ;timing is too short, so take minimum<br />

mov hi_TH0,#000 ;value, and store temp. in hi_t0<br />

ca_crhv: clr C ;timing correction for hivdtmr0<br />

mov A,hivdTL0 ;hivdTL0 - proc_tm<br />

subb A,#proc_tm<br />

mov hivdTL0,A<br />

mov A,hivdTH0 ;borrow in MSB?<br />

subb A,#000<br />

mov hivdTH0,A<br />

jnc ca_conv ;no borrow, so result still pos.<br />

mov hivdTL0,#001 ;timing is too short, so take minimum<br />

mov hivdTH0,#000 ;value, and store temp. in hivdt0<br />

ca_conv: clr A ;convert timer0 clk cycles to load values.<br />

mov B,A ;So do substraction 0-lo_t0<br />

clr C ;for lo_t0<br />

subb A,lo_TL0<br />

mov lo_TL0,A<br />

mov A,B<br />

subb A,lo_TH0<br />

mov lo_TH0,A<br />

clr C ;and same for hi_t0<br />

mov A,B<br />

subb A,hi_TL0<br />

mov hi_TL0,A<br />

mov A,B<br />

subb A,hi_TH0<br />

mov hi_TH0,A<br />

clr C ;and same for hivdtmr0<br />

mov A,B<br />

subb A,hivdTL0<br />

57


mov hivdTL0,A<br />

mov A,B<br />

subb A,hivdTH0<br />

mov hivdTH0,A<br />

ret<br />

;*************************************************<br />

;* calib -> shift_wv *<br />

;* Calculates the proper counter rate for sensor *<br />

;* nothing *<br />

;* measures period on external interrupt input (sensor), during *<br />

;* sensctrl in hi-state *<br />

;* lo transition on _INT0 or sensor *<br />

;* At EXTI0 #samples (in R3) are counted downwards *<br />

;* 2: Int. call to TIMER1 happens when TH1 has an overflow *<br />

;* At TIMER1 #overflows are counted in OP_1 *<br />

;* Timer0 keeps counting from 0 upwards *<br />

;* measurement is ready when R3 reaches 0 *<br />

;* *<br />

;* prd is in timer counting rate *<br />

;* timer1 counts in clk/ccpins *<br />

;* prd is till where counter1/num_per reached when R3:=0 *<br />

;* *<br />

;* uses EXT0 and TIMER1 interrupt (and of course timer1 itself) *<br />

;******************************************************************<br />

freqcap:<br />

clr EA ;disable all interrupts<br />

setb sensctrl ;put piezo into free running frequency<br />

fr_1: jnb sensor,fr_1 ;stabilise free running oscillation<br />

fr_2: jb sensor,fr_2 ;loop during sensor is hi<br />

fr_3: jnb sensor,fr_3 ;loop during sensor is lo<br />

fr_4: jb sensor,fr_4 ;loop during sensor is hi<br />

fr_5: jnb sensor,fr_5 ;loop during sensor is lo<br />

58


fr_6: jb sensor,fr_6 ;loop during sensor is hi<br />

mov R3,#num_per ;temporarily counter for number of captures<br />

mov OP_0,#000 ;not used<br />

mov OP_1,#000 ;possible carry from TH1 (updated in TIMER1)<br />

setb EX0 ;enable EX0 (but not yet EA)<br />

setb ET1 ;enable interrupt from timer1 overflow<br />

fr_strt: jnb sensor,fr_strt ;loop during sensor is lo<br />

fr_h2l: jb sensor,fr_h2l ;loop during sensor is hi<br />

setb TR1 ;timer1 runs: start with measuring<br />

clr IE0 ;no hardware to reset, so software does this task now<br />

setb EA ;enable interrupts (vectored to EXT0)<br />

fr_loop: cjne R3,#000,fr_loop ;wait for num_rep captured timingdata<br />

;R3 is decreased through int. routine EXTI0<br />

clr EA ;finished measuring, disable all interrupts<br />

clr TR1 ;stop timer1<br />

clr EX0 ;disable _INT0<br />

clr ET1 ;disable interrupt from timer1<br />

mov OP_2,TH1 ;timer1 MSB result in OP_2<br />

mov OP_3,TL1 ;timer1 LSB result in OP_3<br />

mov R2,#000 ;MSB of divider<br />

mov R3,#num_per ;LSB of divider<br />

acall Div_16 ;calc.: OP_(0..3) \ num_per = <br />

mov prd_h,OP_2 ;store result counting in prd<br />

mov prd_l,OP_3 ;store result counting in prd<br />

mov C,wvmem ;put port sensctrl through carry in original state<br />

mov sensctrl,C<br />

setb EA ;enable interrupts<br />

ret ;ready with measuring & calculating<br />

;*******************************************************************************<br />

;* wavectrl -> wvmem,vldsns *<br />

;* interrupt from timer0 occurred, so there’s some work to do. Precalculated *<br />

;* values for TH0 & TL0 determine how long the hi-state (in hi_TH0 & hi_TL0) *<br />

;* for vldsns=0 and (hivdTH0 & hivdTL0) for vldsns=1 lasts, followed by how *<br />

;* long lo-state (in lo_TH0 & lo_TL0) on output port sensctrl will last. *<br />

;*


setb TR0 ;1 timer 0 runs, but enough time till int. comes<br />

jb np_flag,ti_nnow ;2 if main is processing data, skip summing<br />

mov tmp_A,A<br />

mov tmp_C,C<br />

mov A,OP_3 ;now enough time to perform 24 bits addition<br />

add A,tmp_TL0 ;LSB<br />

mov OP_3,A<br />

mov A,OP_2<br />

addc A,tmp_TH0 ;MSB<br />

mov OP_2,A<br />

jnc ti_fmty ;if there is no carry, routine is ready<br />

inc OP_1 ;I say: 24 bits is really enough!<br />

ti_fmty: djnz R3,ti_reta ;decrease counter for #num_per, jmp if not zero<br />

setb np_flag ;flag to tell main interrupt routine is ready<br />

ti_reta: mov C,tmp_C<br />

mov A,tmp_A<br />

ti_nnow: reti<br />

;**** part I **** wvmem:=lo, vldsns no change ****<br />

;put sensctrl and flag wvmem in low state, disable EX1 (no _int1), set timer0<br />

ti_dolo: clr wvmem ;1 flag of sensctrl<br />

clr sensctrl ;1 sensctrl goes lo<br />

mov TH0,lo_TH0 ;2 timer 0 hi-byte<br />

mov TL0,lo_TL0 ;2 timer 0 lo-byte<br />

setb TR0 ;1 timer 0 runs<br />

clr EX1 ;1 no _int1 allowed during lo state<br />

reti<br />

;*******************************************************************************<br />

;* sens_stamp -> (from EXTI1) TH0, TL0 *<br />

;* interrupt from _int1 occurred, so there’s some work to do. Sensor produced *<br />

;* an transistion from hi to lo, so it’s position where that happened, rela- *<br />

;* ted to the pos. of timer0, generating a control signal, must be stamped. *<br />

;* (OP_0..3) * (R2,R3) *<br />

;* *<br />

;* Performs a multiply between a 32 and a 16 bit integer *<br />

;* *<br />

;*


;Now generate the next higher order byte<br />

MOV B,OP_2<br />

MOV A,R3<br />

MUL AB<br />

ADD A,TMP_2 ;low-order result<br />

MOV TMP_2,A ;save<br />

MOV A,B ;get high-order result<br />

ADDC A,TMP_1 ;add now ACC with TMP_1 (was 0) with carry<br />

MOV TMP_1,A ;save<br />

JNC Mul_noCl<br />

INC TMP_0 ;carry is set and ACC is FF, so TMP_0++<br />

Mul_noCl:<br />

;Now start working on the 3rd byte<br />

MOV B,OP_1<br />

MOV A,R3<br />

MUL AB<br />

ADD A,TMP_1 ;low-order result<br />

MOV TMP_1,A ;save<br />

MOV A,B ;get high-order result<br />

ADDC A,TMP_0 ;include carry from previous operation<br />

MOV TMP_0,A ;save<br />

;Now finish off the highest order byte<br />

MOV B,OP_0<br />

MOV A,R3<br />

MUL AB<br />

ADD A,TMP_0 ;low-order result<br />

MOV TMP_0,A ;save<br />

;Forget about the high-order result, this is only 32 bit math!<br />

MOV B,OP_3<br />

MOV A,R2<br />

MUL AB<br />

ADD A,TMP_2 ;low-order result<br />

MOV TMP_2,A ;save<br />

MOV A,B ;get high-order result<br />

ADDC A,TMP_1 ;include carry from previous operation<br />

MOV TMP_1,A ;save<br />

JNC Mul_noCh<br />

INC TMP_0 ;carry is set and ACC is FF, so TMP_0++<br />

Mul_noCh:<br />

;Now the other half<br />

MOV B,OP_2<br />

MOV A,R2<br />

MUL AB<br />

ADD A,TMP_1 ;low-order result<br />

MOV TMP_1,A ;save<br />

MOV A,B ;get high-order result<br />

ADDC A,TMP_0 ;include carry from previous operation<br />

MOV TMP_0,A ;save<br />

;The next one<br />

MOV B,OP_1<br />

MOV A,R2<br />

MUL AB<br />

ADD A,TMP_0 ;low-order result<br />

;Now we are all done, move the TMP values back into OP<br />

MOV OP_0,A ;save finally OP_0<br />

MOV OP_1,TMP_1<br />

MOV OP_2,TMP_2<br />

MOV OP_3,TMP_3<br />

RET<br />

;********************************************************************<br />

;* Div_16 -> (OP_0..3) / (R2,R3) *<br />

;* *<br />

;* Performs a division between a 32 and a 16 bit integer *<br />

;* *<br />

;*


;* *<br />

;* R6 ;MSB, Remainder *<br />

;* R7 ;LSB *<br />

;* *<br />

;* TMP_0 ;MSB *<br />

;* TMP_1 ; *<br />

;* TMP_2 ; *<br />

;* TMP_3 ;LSB *<br />

;* *<br />

;* A and B registers are used in routine *<br />

;********************************************************************<br />

Div_16:<br />

;This divides the 32 bit OP register by the value supplied<br />

MOV R6,#0<br />

MOV R7,#0 ;zero out partial remainder<br />

MOV TMP_3,R6<br />

MOV TMP_2,R6<br />

MOV TMP_1,R6<br />

MOV TMP_0,R6<br />

MOV B,#32 ;loop count<br />

;This begins the loop<br />

Div_loop:<br />

CALL Shift_D ;shift the dividend and return MSB in C<br />

MOV A,R7 ;shift carry into LSB of partial remainder<br />

RLC A<br />

MOV R7,A<br />

MOV A,R6<br />

RLC A<br />

MOV R6,A<br />

;now test to see if R6:R7 >= R2:R3<br />

CLR C<br />

MOV A,R6 ;subtract R2 from R6 to see if R2 < R6<br />

SUBB A,R2 ;A = R6 - R2, carry set if R6 < R2<br />

JC Cant_sub<br />

;at this point R6>R2 or R6=R2<br />

JNZ Can_sub ;jump if R6>R2<br />

;if R6 = R2, test for R7>=R3<br />

CLR C<br />

MOV A,R7<br />

SUBB A,R3 ;A = R7 - R3, carry set if R7 < R3<br />

JC Cant_sub<br />

Can_sub:<br />

;subtract the divisor from the partial remainder<br />

CLR C<br />

MOV A,R7<br />

SUBB A,R3 ;A = R7 - R3<br />

MOV R7,A<br />

MOV A,R6<br />

SUBB A,R2 ;A = R6 - R2 - Borrow<br />

MOV R6,A<br />

SETB C ;shift a 1 into the quotient<br />

JMP Quot<br />

Cant_sub:<br />

;shift a 0 into the quotient<br />

CLR C<br />

Quot:<br />

;shift the carry bit into the quotient<br />

CALL Shift_Q<br />

;Test for completion<br />

DJNZ B,Div_loop<br />

;Now we are all done, move the TMP values back into OP<br />

MOV OP_3,TMP_3<br />

MOV OP_2,TMP_2<br />

MOV OP_1,TMP_1<br />

MOV OP_0,TMP_0<br />

RET<br />

Shift_D:<br />

;shift the dividend one bit to the left and return the MSB in C<br />

CLR C<br />

62


Shift_Q:<br />

MOV A,OP_3<br />

RLC A<br />

MOV OP_3,A<br />

MOV A,OP_2<br />

RLC A<br />

MOV OP_2,A<br />

MOV A,OP_1<br />

RLC A<br />

MOV OP_1,A<br />

MOV A,OP_0<br />

RLC A<br />

MOV OP_0,A<br />

RET<br />

;shift the quotient one bit to the left and shift the C into LSB<br />

MOV A,TMP_3<br />

RLC A<br />

MOV TMP_3,A<br />

MOV A,TMP_2<br />

RLC A<br />

MOV TMP_2,A<br />

MOV A,TMP_1<br />

RLC A<br />

MOV TMP_1,A<br />

MOV A,TMP_0<br />

RLC A<br />

MOV TMP_0,A<br />

RET<br />

;*****************************************************************************<br />

;* linearization table *<br />

;* array of 256 elements *<br />

;* converting to linear output *<br />

;*****************************************************************************<br />

lintbl: DB 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000<br />

DB 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000<br />

DB 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000<br />

DB 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000<br />

DB 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000<br />

DB 000,000,000,000,000,000,001,001,001,001,002,002,002,003,003,003<br />

DB 003,004,004,004,005,005,005,006,006,006,006,007,007,007,008,008<br />

DB 008,009,009,009,010,010,010,011,011,011,012,012,012,013,013,014<br />

DB 014,014,015,015,015,016,016,017,017,017,018,018,018,019,019,020<br />

DB 020,020,021,021,022,022,023,023,023,024,024,025,025,026,026,027<br />

DB 027,028,028,029,029,029,030,030,031,031,032,032,033,034,034,035<br />

DB 035,036,036,037,037,038,039,039,040,040,041,042,042,043,043,044<br />

DB 045,045,046,047,047,048,049,050,050,051,052,053,053,054,055,056<br />

DB 056,057,058,059,060,061,062,063,064,064,065,066,067,068,069,071<br />

DB 072,073,074,075,076,077,079,080,081,083,084,085,087,088,090,091<br />

DB 093,095,097,098,100,102,104,106,108,111,113,116,118,121,124,127<br />

END<br />

63


Sourcecode van het midifile-conversieprogramma<br />

Voor het conversieprogramma hebben we gebruik gemaakt van een programma dat we eerder geschreven<br />

hadden op een Atari ST in Pure-C (Borland C). Voor de PC moesten we de functies die specifiek<br />

te maken hadden met vensters en de afhandeling van de muis omzetten, zodat deze aan MS-Windows<br />

voldeed. Daarnaast hebben we bepaalde functies omgezet in methoden behorend bij de class CMyDlg.<br />

In vergelijking met Ansi-C hebben we hier een groot voordeel, doordat we functionaliteit van bijelkaar<br />

horende methodes kunnen samenvoegen. Bovendien blijft het programmeren van de grafische schil<br />

daaromheen overzichtelijk. We programmeren in Microsoft Visual C++ (2003).<br />

Het stroomdiagram waarin we de methode Convert beschrijven, behandelen we in hoofdstuk 5.<br />

Een overzicht van alle methoden die in de class CMyDlg voorkomen:<br />

Methoden voor de uitvoer naar het scherm GetMsg, SaveMsg<br />

Methoden voor het vragen van een filenaam voor de invoer- en uitvoerfile FileODlg, FileSDlg<br />

Methode voor het converteren van een MIDI File naar een textbestand Convert<br />

Alle constanten en ‘#define’-waarden staan in de bijbehorende headerfile ‘MyDlg.h’:<br />

#pragma once // just a compiler directive<br />

#define MHDR 0 // different kind of headers<br />

#define MSIG 1<br />

#define MTRK 2<br />

#define HDR 0 // kind of metaevents (META_TP)<br />

#define HBL 1<br />

#define FMT 2<br />

#define NTB 3<br />

#define RES 4<br />

#define SHDR 5<br />

#define SHL 6<br />

#define STAIL 7<br />

#define THDR 8<br />

#define TRKL 9<br />

#define TTAIL 10<br />

#define FINISH 11<br />

#define ERR 12<br />

#define MIDIEVNT 0 // kind of events, nb. second is running status<br />

#define SYSEXEVNT 2<br />

#define MTAEVNT 4<br />

#define META_FF 0 // order to parse metaevent<br />

#define META_TP 1<br />

#define META_LEN 2<br />

#define META_DATA 3<br />

#define SQN 0 // kind of metaevents (META_TP)<br />

#define TXT 1<br />

#define COPY 2<br />

#define SQTRNM 3<br />

#define INSTRNM 4<br />

#define LYRIC 5<br />

#define TXTMRK 6<br />

#define TXTCPT 7<br />

#define EOTR 0x2f<br />

#define TEMPO 0x51<br />

#define SMPTE 0x54<br />

#define TMSIG 0x58<br />

#define KEYSG 0x59<br />

#define SEQSP 0x7f<br />

#define AFTER_TOUCH 0x0d // kind of midi channel commands with 1 data byte<br />

#define PRG_CHNG 0x0c<br />

#define NOTE_ON 0x09 // kind of midi channel commands with 2 data byte<br />

64


#define NOTE_OFF 0x08<br />

//***************************************************************************<br />

// class CMyDlg<br />

// this class uses five or so methods:<br />

// GetMsg(int) Delivers message nr...<br />

// SaveMsg(CString) Stores message in list. Last list item is nr. maxL-1<br />

// FileODlg Asks user for filename to read from<br />

// FileSDlg Asks user for filename to write to<br />

// Convert Converts a MIDI File to TXT File<br />

class CMyDlg<br />

{<br />

public:<br />

CMyDlg(void); // constructor<br />

CString GetMsg(int i); // ALWAYS DO A FULL REBUILD AFTER MODIFYING A CONST!!!<br />

const static int maxL=16; // maximum number of rows in the text output dialog<br />

const static int maxP=260; // maximum number of characters in a path+filename<br />

const static int xOD=10; // x- left top coordinate output dialog<br />

const static int yOD=144; // y- left top coordinate output dialog<br />

const static int hSZ=461; // x- right bot coordinate output dialog<br />

const static int vSZ=421; // y- right bot coordinate output dialog<br />

void SaveMsg(CString msg);<br />

bool FileODlg(void); // filename->FONm, return=succes<br />

bool FileSDlg(void); // filename->FSNm, return=succes<br />

CString FONm; // Member with path+filename+extension input file<br />

CString FOLp; // Member with filename+extension<br />

CString FOFn; // Member with filename<br />

CString FOEx; // Member with extension<br />

CString FSNm; // Member with path+filename output file<br />

CString FSPt; // Member with path only<br />

CString FSLp; // Member with filename+extension<br />

CString FSFn; // Member with filename<br />

CString FSEx; // Member with extension<br />

bool Convert(void); // Do Conversion midifile 2 Txt<br />

private:<br />

int MsgCount;<br />

CString Msgs[maxL+1];<br />

};<br />

De class CMyDlg met ondermeer de methode Convert die de conversie van een MIDI File afhandelt:<br />

//********************************************************<br />

// Class MyDlg.cpp<br />

//<br />

#<br />

#include “StdAfx.h”<br />

#include “MyDlg.h”<br />

CMyDlg::CMyDlg(void)<br />

{<br />

MsgCount=0;<br />

for (int i=0;i


ool CMyDlg::FileODlg(void)<br />

{<br />

bool os; // os records succes of GetOpenFileName<br />

char tmp[maxP]; // temporary store for Path+Filename+Extension<br />

OPENFILENAME ofn; // structure to FILE ofn<br />

}<br />

ZeroMemory(&ofn, sizeof(ofn));<br />

ofn.lStructSize = sizeof(ofn);<br />

ofn.lpstrFilter = “Standard MIDI files\0*.MID\0ALL\0*.*\0”;<br />

ofn.lpstrFile = tmp;<br />

ofn.lpstrFile[0] = ‘\0’; // Set lpstrFile[0] to ‘\0’ so that GetOpenFileName<br />

// does not use contents of szFile to initialize<br />

ofn.nMaxFile = maxP; // Max. length of filename. Must be declared!!<br />

ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;<br />

os=(GetOpenFileName(&ofn)==TRUE);<br />

if (os)<br />

{<br />

FONm=tmp; // see declaration for explanation of these members<br />

FOLp=tmp+ofn.nFileOffset;<br />

FOFn=FOLp.Left(ofn.nFileExtension-ofn.nFileOffset-1);<br />

FOEx=tmp+ofn.nFileExtension;<br />

}<br />

return (os); // check if file was selected. If selected, nResult=True<br />

bool CMyDlg::FileSDlg(void) // char array for path + file name<br />

{<br />

bool os; // os records succes of GetOpenFileName<br />

char tmp[maxP]; // temporary store for Filename<br />

OPENFILENAME ofn; // structure to FILE ofn<br />

ZeroMemory(&ofn, sizeof(ofn));<br />

ofn.lStructSize = sizeof(ofn);<br />

ofn.lpstrFilter = “Text Files (*.txt)\0*.txt\0ALL\0*.*\0”;<br />

ofn.lpstrFile = tmp;<br />

ofn.lpstrFile[0] = ‘\0’; // Set lpstrFile[0] to ‘\0’ so that GetOpenFileName<br />

// does not use the contents of szFile to initialize<br />

ofn.nMaxFile = maxP;<br />

ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;<br />

os=(GetSaveFileName(&ofn)==TRUE);<br />

if (os)<br />

{<br />

FSNm=tmp; // see declaration for explanation of these members<br />

FSLp=tmp+ofn.nFileOffset;<br />

FSFn=FSLp.Left(ofn.nFileExtension-ofn.nFileOffset-1);<br />

FSEx=tmp+ofn.nFileExtension;<br />

FSPt=FSNm.Left(ofn.nFileOffset);<br />

}<br />

return (os); // check if file was selected. If selected, nResult=True<br />

}<br />

bool CMyDlg::Convert(void)<br />

{<br />

SaveMsg(“Analyzing File...”+FOLp);<br />

FILE *fp,*o;<br />

CString bsenm; // basename for output files<br />

CString outnm; // name for output files<br />

CString ascnum; // name for ascii extension<br />

int c; // readed byte always stored in c<br />

int i; // counter to order the readed bytes<br />

int head; // kind tells function whether header or track<br />

int track; // every time a new track comes, track ++<br />

char what2do; // what2do tells function what to do with the byte<br />

char mfmt; // file tells type of midi file (0,1 or 2)<br />

int mres; // resolution of the delta-time, in ticks/quarter note<br />

unsigned long chunkl; // length of chunk (4 bytes max.)<br />

char dtm; // flag for status of reading. TRUE: read delta time<br />

unsigned long vrl; // result of variable length calculation<br />

unsigned long sdtm; // savereg. for temporarily storing delta time<br />

unsigned long cumdtm; // cumulative deltatime<br />

66


char evtdsp; // flag for valid event or invalid. FALSE means invalid<br />

char evtknd; // tells function what kind of event. see #define<br />

unsigned long mvrl; // length of specific meta event<br />

char meta; // meta tells function what2do with byte from meta event<br />

char metaevnt; // type of meta event<br />

CString stri; // temporary holder for string converted integer<br />

char mcmd; // tells function what kind of midi command<br />

char mch; // with channel commands, this gives the channel<br />

char cmdsize; // tells function of many bytes midi command consists<br />

char data1; // first byte after midi command<br />

char data2; // second byte after midi command<br />

char tmp[]=”MThd0006MTrk”; // Reserved keywords in a standard midi file<br />

i = 0; // increased when next byte belongs, when not: reset<br />

chunkl = 0;<br />

vrl = 0;<br />

mvrl = 0;<br />

metaevnt= 0; // see #define<br />

cmdsize = 0;<br />

cumdtm = 0;<br />

what2do = HDR; // begin with analysing MThd<br />

head = MHDR; // difference between format 0, 1 and 3; see #define<br />

track = 0; // first track will be track 0<br />

if ((fp = fopen(FONm, “rb”)) == NULL) return(FALSE);<br />

//<br />

bsenm=FSFn+”_0”;<br />

while ( ( c=getc(fp)) != EOF)<br />

switch (head)<br />

{<br />

case MHDR:<br />

{<br />

if (what2do!=ERR) // process header data if head == TRUE<br />

switch (what2do)<br />

{<br />

case HDR:<br />

{<br />

if ( tmp[i] == (char)c ) i++;<br />

else<br />

{<br />

SaveMsg(“Sorry, header is not midi file standard”);<br />

what2do=ERR;<br />

}<br />

if (i==4) // MThd had been found succesfully<br />

{<br />

SaveMsg(“Header found”);<br />

i=0;<br />

what2do++;<br />

}<br />

break;<br />

}<br />

case HBL:<br />

{<br />

if ( tmp[i + 4]-’0’==(char)c ) i++; // search for numbers 0006 in header<br />

else<br />

{<br />

SaveMsg(“Sorry, header length is not midi file standard”);<br />

what2do=ERR;<br />

}<br />

if (i==4) // header length had been found succesfully<br />

{<br />

SaveMsg(“Header length found”);<br />

i=0;<br />

what2do++;<br />

}<br />

break;<br />

}<br />

case FMT:<br />

{<br />

if (i==0) {i++; break;}<br />

67


if (i==1)<br />

{<br />

(char)mfmt=c;<br />

i=0;<br />

what2do++;<br />

SaveMsg(“midi file format: “+mfmt);<br />

}<br />

break;<br />

}<br />

case NTB:<br />

{<br />

if (i==0) {i++; break;}<br />

if (i==1)<br />

{<br />

i=0;<br />

what2do++;<br />

stri.Format(“number of tracks: %d”,c);<br />

SaveMsg(stri);<br />

}<br />

break;<br />

}<br />

case RES:<br />

{<br />

if (i==0) {mres=c; i++; break;}<br />

if (i==1)<br />

{<br />

mres=256*mres+c;<br />

i=0;<br />

what2do++;<br />

if (mfmt==0) head=MTRK;<br />

if (mfmt==1) head=MSIG; // in format 1 exist time/signature track<br />

stri.Format(“number of ticks per quarter-note: %d”,mres);<br />

SaveMsg(stri);<br />

}<br />

break;<br />

}<br />

} // escape from switch (what2do)<br />

// endif (what2do!=ERR)<br />

break; // escape from case MHDR<br />

}<br />

case MSIG:<br />

{<br />

if (mfmt==1 && what2do!=ERR) // process time/signature tempo track data in format 1<br />

{<br />

switch (what2do)<br />

{<br />

case SHDR:<br />

{<br />

if ( tmp[i + 8] == (char)c ) i++;<br />

else<br />

{<br />

SaveMsg(“Sorry, time/signature trackheader is not according to midi file standard”);<br />

what2do=ERR;<br />

}<br />

if (i==4) // MTrk had been found succesfully<br />

{<br />

SaveMsg(“time/signature trackheader found”);<br />

i=0;<br />

what2do++;<br />

}<br />

break;<br />

}<br />

case SHL:<br />

{<br />

chunkl=(chunkl


dtm=TRUE;<br />

vrl=0;<br />

what2do++;<br />

}<br />

break;<br />

}<br />

case STAIL: // need only tempo,if more needed,change case META_DATA<br />

{<br />

if (chunkl) chunkl--;<br />

if (dtm) // read delta time<br />

{<br />

if (c & 0x80) vrl=(vrl


else<br />

{<br />

SaveMsg(“error, meta event type “+stri+” is bigger than 127”);<br />

what2do=ERR;<br />

}<br />

break;<br />

}<br />

case META_LEN:<br />

{<br />

if (c & 0x80) mvrl=(mvrl


what2do++;<br />

}<br />

break;<br />

}<br />

case TRKL:<br />

{<br />

chunkl=(chunkl


switch(evtknd)<br />

{<br />

case MIDIEVNT: //(in running status vrl is initialized with cmdsize)<br />

{<br />

if (!vrl) // if 1st byte is midi cmd, init. variables cmdsize,mcmd<br />

{<br />

cmdsize=2; // most midi channel commands have 2 data bytes<br />

// in midi-file only sys excl and common are supported<br />

// ; no sys. realtime!<br />

mch=c & 0x0f; // get midi channel<br />

mcmd=c >> 4; // filter out midi channel, shift 4 places to the right<br />

switch( mcmd ) // test for commands with only 1 byte data<br />

{<br />

case AFTER_TOUCH: cmdsize=1; break;<br />

case PRG_CHNG: cmdsize=1; break;<br />

} // endswitch (c)<br />

vrl=cmdsize; // initialize vrl<br />

} // end if (!vrl)<br />

else // proces following byte(s) after midi command byte<br />

{<br />

if(cmdsize==1)data1=c; // need only this byte with command mcmd<br />

if(cmdsize==2)<br />

{<br />

if (vrl==2) data1=c;<br />

else data2=c;<br />

}<br />

vrl--;<br />

if (!vrl) dtm=TRUE; // ready with event, find delta time<br />

} // end else if(!vrl)<br />

break; // end case MIDIEVNT<br />

}<br />

case SYSEXEVNT:<br />

break;<br />

case MTAEVNT:<br />

{<br />

switch (meta)<br />

{<br />

case META_FF:<br />

{<br />

meta++; // ignore this byte ff, next byte will be type<br />

break;<br />

}<br />

case META_TP:<br />

{<br />

(char)metaevnt=c;<br />

if ( metaevnt


}<br />

{<br />

if (mvrl) mvrl--;<br />

switch (metaevnt)<br />

{<br />

case SQTRNM:<br />

{<br />

putc((char)c,o); // write text meta evnt<br />

break;<br />

}<br />

}<br />

if (!mvrl) // start again with finding delta-time before event<br />

{<br />

dtm=TRUE;<br />

vrl=0;<br />

evtdsp=FALSE;<br />

switch (metaevnt) // finish of with CR/LF etc.<br />

{<br />

case SQTRNM:<br />

putc(10,o); // finish off with CR/LF<br />

ascnum.Format(“%d”,mres);<br />

fputs(ascnum,o);<br />

putc(10,o); // finish off with CR/LF<br />

break;<br />

}<br />

} // end switch (metaevnt)<br />

break; // end case META_DATA<br />

}<br />

} // end switch(meta)<br />

break; // end case MTAEVNT<br />

}<br />

} // end switch evntknd<br />

if(dtm && (evtknd==MIDIEVNT)) // show package of data; only be checked if !dtm<br />

{<br />

switch (cmdsize) // cmdsize 0 is not specified in midi files<br />

{<br />

case 1: // command with 1 byte data: mcmd, data1<br />

{<br />

sdtm+=cumdtm;<br />

cumdtm=sdtm;<br />

sdtm=250*sdtm/mres; // value range: [0,10000><br />

ascnum.Format(“%d”,sdtm);<br />

fputs(ascnum,o);<br />

putc(‘,’,o); // put division marker<br />

ascnum.Format(“%d”,data1);<br />

fputs(ascnum,o);<br />

putc(10,o); // finish off with CR/LF<br />

break;<br />

}<br />

case 2: // command with 2 bytes data<br />

{<br />

mch=mch;<br />

data2=data2;<br />

break;<br />

}<br />

} // end switch (cmdsize)<br />

} // end if (dtm && ....)<br />

} // end else (dtm)<br />

if (!chunkl)<br />

{<br />

stri.Format(“finish with track %d”,track);<br />

SaveMsg(stri);<br />

i=0;<br />

vrl=0;<br />

cumdtm=0;<br />

track++;<br />

dtm=TRUE;<br />

fclose(o); // ready with file<br />

what2do=THDR;<br />

}<br />

break; // end case TTAIL<br />

73


} // end switch (what2do)<br />

break;<br />

} // escape from case MTRK<br />

}<br />

} // end switch (head)<br />

// end while<br />

fclose(fp);<br />

return(what2do!=ERR);<br />

}<br />

74


MIDI File standaard<br />

Het beheer van ondermeer de MIDI- en de MIDI File-standaard wordt door de MIDI Manufacturers<br />

Association 1 gedaan. Een gedrukt document waarin de MIDI File-standaard beschreven wordt kan bij<br />

die organisatie tegen betaling worden verkregen. Op het internet is er op verschillende lokaties echter<br />

al voldoende informatie te vinden, over hoe MIDI Files zijn opgebouwd en hoe die kunnen worden<br />

geanalyseerd. Een van die bronnen 15 voeg ik hier bij.<br />

Standard MIDI Files 1.0 July, 1988<br />

•Introduction<br />

•Sequences, Tracks, Chunks: File Block Structure<br />

•Chunk Descriptions<br />

•Meta-Events<br />

•Program Fragments and Example MlDI Files<br />

Introduction<br />

The document outlines the specification for MIDI Files. The purpose of MIDI Files is to provide a way<br />

of interchanging time-stamped MIDI data between different programs on the same or different computers.<br />

One of the primary design goals is compact representation, which makes it very appropriate for a<br />

disk-based file format, but which might make it inappropriate for storing in memory for quick access<br />

by a sequencer program. (It can be easily converted to a quickly-accessible format on the fly as files<br />

are read in or written out.) It is not intended to replace the normal file format of any program, though it<br />

could be used for this purpose if desired.<br />

MIDI Files contain one or more MIDI streams, with time information for each event. Song, sequence,<br />

and track structures, tempo and time signature information, are all supported. Track names and other<br />

descriptive information may be stored with the MIDI data. This format supports multiple tracks and<br />

multiple sequences so that if the user of a program which supports multiple tracks intends to move a<br />

file to another one, this format can allow that to happen.<br />

This spec defines the 8-bit binary data stream used in the file. The data can be stored in a binary file,<br />

nibbleized, 7-bit-ized for efficient MIDI transmission, converted to Hex ASCII, or translated symbolically<br />

to a printable text file. This spec addresses what’s in the 8-bit stream. It does not address how<br />

a MIDI File will be transmitted over MIDI. It is the general feeling that a MIDI transmission protocol<br />

will be developed for files in general and MIDI Files will used this scheme.<br />

75


Sequences, Tracks, Chunks: File Block Structure<br />

Conventions<br />

In this document, bit 0 means the least significant bit of a byte, and bit 7 is the most significant. Some<br />

numbers in MIDI Files are represented in a form called a variable-length quantity. These numbers are<br />

represented 7 bits per byte, most significant bits first. All bytes except the last have bit 7 set, and the<br />

last byte has bit 7 clear. If the number is between 0 and 127, it is thus represented exactly as one byte.<br />

Here are some examples of numbers represented as variable-length quantities:<br />

Number (hex) Representation (hex)<br />

00000000 00<br />

00000040 40<br />

0000007F 7F<br />

00000080 81 00<br />

00002000 C0 00<br />

00003FFF FF 7F<br />

00004000 81 80 00<br />

00100000 C0 80 00<br />

00lFFFFF FF FF 7F<br />

00200000 81 80 80 00<br />

08000000 C0 80 80 00<br />

0FFFFFFF FF FF FF 7F<br />

The largest number which is allowed is 0FFFFFFF so that the variable-length representation must fit in<br />

32 bits in a routine to write variable-length numbers. Theoretically, larger numbers are possible, but 2<br />

x 108 96ths of a beat at a fast tempo of 500 beats per minute is four days, long enough for any deltatime!<br />

Files<br />

To any file system, a MIDI File is simply a series of 8-bit bytes. On the Macintosh, this byte stream<br />

is stored in the data fork of a file (with file type ‘Midi’), or on the Clipboard (with data type ‘Midi’).<br />

Most other computers store X-bit byte streams in files -- naming or storage conventions for those computers<br />

will be defined as required.<br />

Chunks<br />

MIDI Files are made up of chunks. Each chunk has a 4-character type and a 32-bit length, which is<br />

the number of bytes in the chunk. This structure allows future chunk types to be designed which may<br />

easily be ignored if encountered by a program written before the chunk type is introduced. Your programs<br />

should Expect alien chunks and treat them as if they weren’t there.<br />

Each chunk begins with a 4-character ASCII type. It is followed by a 32-bit length, most significant<br />

byte first (a length of 6 is stored as 00 00 00 06). This length refers to the number of bytes of data<br />

which follow: the eight bytes of type and length are not included. Therefore, a chunk with a length of 6<br />

would actually occupy 14 bytes in the disk file.<br />

This chunk architecture is similar to that used by Electronic Arts’ IFF format, and the chunks described<br />

herein could easily be placed in an IFF file. The MIDI File itself is not an IFF file: it contains no nested<br />

chunks, and chunks are not constrained to be an even number of bytes long. Converting it to an IFF<br />

file is as easy as padding odd-length chunks, and sticking the whole thing inside a FORM chunk.<br />

MIDI Files contain two types of chunks: header chunks and track chunks. A header chunk provides a<br />

minimal amount of information pertaining to the entire MIDI file. A track chunk contains a sequential<br />

stream of MIDI data which may contain information for up to 16 MIDI channels. The concepts of<br />

76


multiple tracks, multiple MIDI outputs, patterns, sequences, and songs may all be implemented using<br />

several track chunks.<br />

A MIDI file always starts with a header chunk, and is followed by one or more track chunks.<br />

MThd <br />

<br />

MTrk <br />

<br />

MTrk <br />

<br />

...<br />

Chunk Descriptions<br />

Header Chunks<br />

The header chunk at the beginning of the file specifies some basic information about the data in the<br />

file. Here’s the syntax of the complete chunk:<br />

= <br />

As described above, is the four ASCII characters ‘MThd’; is a 32-bit representation<br />

of the number 6 (high byte first).<br />

The data section contains three 16-bit words, stored most-significant byte first. The first word, format,<br />

specifies the overall organisation of the file. Only three values of format are specified:<br />

0 the file contains a single multi-channel track<br />

1 the file contains one or more simultaneous tracks (or MIDI outputs) of a sequence<br />

2 the file contains one or more sequentially independent single-track patterns<br />

More information about these formats is provided below.<br />

The next word, ntrks, is the number of track chunks in the file. It will always be 1 for a format 0 file.<br />

The third word, division, specifies the meaning of the delta-times. It has two formats, one for metrical<br />

time, and one for time-code-based time:<br />

========================================<br />

| 0 | ticks per quarter-note |<br />

========================================<br />

========================================<br />

| 1 | neg SMPTE format| ticks per frame|<br />

========================================<br />

15 14 8 7 0<br />

If bit 15 of division is a zero, the bits 14 thru 0 represent the number of delta-time ticks” which make<br />

up a quarter-note. For instance, if division is 96, then a time interval of an eighth-note between two<br />

events in the file would be 48.<br />

If bit 15 of division is a one, delta-times in a file correspond to subdivisions of a second, in a way<br />

consistent with SMPTE and MIDI time code. Bits 14 thru 8 contain one of the four values -24, -25,<br />

-29, or -30, corresponding to the four standard SMPTE and MIDI time code formats (-29 corresponds<br />

to 30 drop frame), and represents the number of frames per second. These negative numbers are stored<br />

in two’s complement form. The second byte (stored positive) is the resolution within a frame: typical<br />

77


values may be 4 (MIDI time code resolution), 8, 10, 80 (bit resolution), or 100. This system allows<br />

exact specification of time- code-based tracks, but also allows millisecond-based tracks by specifying<br />

25 frames/sec and a resolution of 40 units per frame. If the events in a file are stored with bit resolution<br />

of thirty-frame time code, the division word would be E250 hex.<br />

Formats 0, 1, and 2<br />

A Format 0 file has a header chunk followed by one track chunk. It is the most interchangeable representation<br />

of data. It is very useful for a simple single-track player in a program which needs to make<br />

synthesisers make sounds, but which is primarily concerned with something else such as mixers or<br />

sound effect boxes. It is very desirable to be able to produce such a format, even if your program is<br />

track-based, in order to work with these simple programs. On the other hand, perhaps someone will<br />

write a format conversion from format 1 to format 0 which might be so easy to use in some setting that<br />

it would save you the trouble of putting it into your program.<br />

A Format 1 or 2 file has a header chunk followed by one or more track chunks. Programs which support<br />

several simultaneous tracks should be able to save and read data in format 1, a vertically one-dimensional<br />

form, that is, as a collection of tracks. Programs which support several independent patterns<br />

should be able to save and read data in format 2, a horizontally one-dimensional form. Providing these<br />

minimum capabilities will ensure maximum interchangeability.<br />

In a MIDI system with a computer and a SMPTE synchroniser which uses Song Pointer and Timing<br />

Clock, tempo maps (which describe the tempo throughout the track, and may also include time signature<br />

information, so that the bar number may be derived) are generally created on the computer. To<br />

use them with the synchroniser, it is necessary to transfer them from the computer. To make it easy<br />

for the synchroniser to extract this data from a MIDI File, tempo information should always be<br />

stored in the first MTrk chunk. For a format 0 file, the tempo will be scattered through the track and<br />

the tempo map reader should ignore the intervening events; for a format 1 file, the tempo map must<br />

be stored as the first track. It is polite to a tempo map reader to offer your user the ability to make a<br />

format O file with just the tempo, unless you can use format 1.<br />

All MIDI Files should specify tempo and time signature. If they don’t, the time signature is assumed to<br />

be 4/4, and the tempo 120 beats per minute. In format 0, these meta-events should occur at least at the<br />

beginning of the single multi-channel track. In format 1, these meta-events should be contained in the<br />

first track. In format 2, each of the temporally independent patterns should contain at least initial time<br />

signature and tempo information.<br />

We may decide to define other format IDs to support other structures. A program encountering an<br />

unknown format ID may still read other MTrk chunks it finds from the file, as format 1 or 2, if its user<br />

can make sense of them and arrange them into some other structure if appropriate. Also, more parameters<br />

may be added to the MThd chunk in the future: it is important to read and honour the length, even<br />

if it is longer than 6.<br />

Track Chunks<br />

The track chunks (type MTrk) are where actual song data is stored. Each track chunk is simply a<br />

stream of MIDI events (and non-MIDI events), preceded by delta-time values. The format for Track<br />

Chunks (described below) is exactly the same for all three formats (O, 1, and 2: see “Header Chunk”<br />

above) of MIDI Files.<br />

Here is the syntax of an MTrk chunk (the + means “one or more”: at least one MTrk event must be<br />

present):<br />

= <br />

78


The syntax of an MTrk event is very simple:<br />

= <br />

is Stored as a variable-length quantity. It represents the amount of time before the following<br />

event. If the first event in a track occurs at the very beginning of a track, or if two events occur<br />

simultaneously, a delta-time of zero is used. Delta-times are always present. (Not storing delta-times<br />

of 0 requires at least two bytes for any other value, and most delta-times aren’t zero.) Delta-time is<br />

in some fraction of a beat (or a second, for recording a track with SMPTE times), as specified in the<br />

header chunk.<br />

= | | <br />

is any MIDI channel message. Running status is used: status bytes of MIDI channel<br />

messages may be omitted if the preceding event is a MIDI channel message with the same status. The<br />

first event in each MTrk chunk must specify status. Delta-time is not considered an event itself: it is an<br />

integral part of the syntax for an MTrk event. Notice that running status occurs across delta-times.<br />

is used to specify a MIDI system exclusive message, either as one unit or in packets, or<br />

as an “escape” to specify any arbitrary bytes to be transmitted. A normal complete system exclusive<br />

message is stored in a MIDI File in this way:<br />

FO <br />

The length is stored as a variable-length quantity. It specifies the number of bytes which follow it, not<br />

including the F0 or the length itself. For instance, the transmitted message F0 43 12 00 07 F7 would<br />

be stored in a MIDI file as F0 05 43 12 00 07 F7. It is required to include the F7 at the end so that the<br />

reader of the MIDI file knows that it has read the entire message.<br />

Another form of sysex event is provided which does not imply that an F0 should be transmitted. This<br />

may be used as an “escape” to provide for the transmission of things which would not otherwise be<br />

legal, including system realtime messages, song pointer or select, MIDI Time Code, etc. This uses the<br />

F7 code:<br />

F7 <br />

Unfortunately, some synthesiser manufacturers specify that their system exclusive messages are to be<br />

transmitted as little packets. Each packet is only part of an entire syntactical system exclusive message,<br />

but the times they are transmitted at are important. Examples of this are the bytes sent in a CZ<br />

patch dump, or the FB-01’s “system exclusive mode” in which microtonal data can be transmitted.<br />

The F0 and F7 sysex events may be used together to break up syntactically complete system exclusive<br />

messages into timed packets.<br />

An F0 sysex event is used for the first packet an a series -- it is a message in which the F0 should be<br />

transmitted. An F7 sysex event is used for the remainder of the packets, which do not begin with F0.<br />

(Of course, the F7 is not considered part of the system exclusive message).<br />

A syntactic system exclusive message must always end with an F7, even if the real-life device didn’t<br />

send one, so that you know when you’ve reached the end of an entire sysex message without looking<br />

ahead to the next event in the MIDI file. If it’s stored in one complete F0 sysex event, the last byte<br />

must be an F7. If it is broken up into packets, the last byte of the last packet must be an F7. There also<br />

must not be any transmittable MIDI events in between the packets of a multi-packet system exclusive<br />

message. This principle is illustrated in the paragraph below.<br />

Here is an example of a multi-packet system exclusive message: suppose the bytes F0 43 12 00 were<br />

79


to be sent, followed by a 200-tick delay, followed by the bytes 43 12 00 43 12 00, followed by a 100tick<br />

delay, followed by the bytes 43 12 00 F7, this would be in the MIDI File:<br />

F0 03 43 12 00<br />

81 48 200-tick delta-time<br />

F7 06 43 12 00 43 12 00<br />

64 100-tick delta-time<br />

F7 04 43 12 00 F7<br />

When reading a MlDI File, and an F7 sysex event is encountered without a preceding F0 sysex event<br />

to start a multi-packet system exclusive message sequence, it should be presumed that the F7 event is<br />

being used as an “escape” In this case, it is not necessary that it end with an F7, unless it is desired that<br />

the F7 be transmitted.<br />

specifies non-MIDI information useful to this format or to sequencers, with this syntax:<br />

FF <br />

All meta-events begin with FF, then have an event type byte (which is always less than 128), and then<br />

have the length of the data stored as a variable-length quantity, and then the data itself. If there is no<br />

data, the length is 0. As with chunks, future meta-events may be designed which may not be known to<br />

existing programs, so programs must properly ignore meta-events which they do not recognise, and<br />

indeed, should expect to see them. Programs must never ignore the length of a meta-event which they<br />

do recognise, and they shouldn’t be surprised if it’s bigger than they expected. If so, they must ignore<br />

everything past what they know about. However, they must not add anything of their own to the end of<br />

a meta-event.<br />

Sysex events and meta-events cancel any running status which was in effect. Running status does not<br />

apply to and may not be used for these messages.<br />

Meta-Events<br />

A few meta-events are defined herein. It is not required for every program to support every meta-event.<br />

In the syntax descriptions for each of the meta-events a set of conventions is used to describe parameters<br />

of the events. The FF which begins each event, the type of each event, and the lengths of events<br />

which do not have a variable amount of data are given directly in hexadecimal. A notation such as dd<br />

or se, which consists of two lower-case letters, mnemonically represents an 8-bit value. Four identical<br />

lower-case letters such as wwww refer to a 16-bit value, stored most-significant-byte first. Six identical<br />

lower-case letters such as tttttt refer to a 24-bit value, stored most-significant-byte first. The notation<br />

len refers to the length portion of the meta-event syntax, that is, a number, stored as a variable- length<br />

quantity, which specifies how many data bytes follow it in the meta-event. The notations text and data<br />

refer to however many bytes of (possibly text) data were just specified by the length.<br />

In general, meta-events in a track which occur at the same time may occur in any order. If a copyright<br />

event is used, it should be placed as early as possible in the file, so it will be noticed easily. Sequence<br />

Number and Sequence/Track Name events, if present, must appear at time 0. An end-of-track event<br />

must occur as the last event in the track.<br />

Meta-events initially defined include:<br />

80


FF 00 02 ssss Sequence Number<br />

This optional event, which must occur at the beginning of a track, before any nonzero deltatimes,<br />

and before any transmittable MIDI events, specifies the number of a sequence. In a<br />

format 2 MIDI file, it is used to identify each “pattern” so that a “song” sequence using the Cue<br />

message to refer to the patterns. If the ID numbers are omitted, the sequences’ locations in order<br />

in the file are used as defaults. In a format 0 or 1 MIDI file, which only contain one sequence,<br />

this number should be contained in the first (or only) track. If transfer of several multitrack<br />

sequences is required, this must be done as a group of format 1 files, each with a different sequence<br />

number.<br />

FF 01 len text Text Event<br />

Any amount of text describing anything. It is a good idea to put a text event right at the beginning<br />

of a track, with the name of the track, a description of its intended orchestration, and any<br />

other information which the user wants to put there. Text events may also occur at other times<br />

in a track, to be used as lyrics, or descriptions of cue points. The text in this event should be<br />

printable ASCII characters for maximum interchange. However, other character codes using<br />

the high-order bit may be used for interchange of files between different programs on the same<br />

computer which supports an extended character set. Programs on a computer which does not<br />

support non- ASCII characters should ignore those characters. Meta event types 01 through<br />

0F are reserved for various types of text events, each of which meets the specification of text<br />

events(above) but is used for a different purpose:<br />

FF 02 len text Copyright Notice<br />

Contains a copyright notice as printable ASCII text. The notice should contain the characters<br />

(C), the year of the copyright, and the owner of the copyright. If several pieces of music are in<br />

the same MIDI file, all of the copyright notices should be placed together in this event so that it<br />

will be at the beginning of the file. This event should be the first event in the first track chunk,<br />

at time 0.<br />

FF 03 len text Sequence/Track Name<br />

If in a format 0 track, or the first track in a format I file, the name of the sequence. Otherwise,<br />

the name of the track.<br />

FF 04 len text Instrument Name<br />

A description of the type of instrumentation to be used in that track. May be used with the MIDI<br />

Prefix meta-event to specify which MIDI channel the description applies to, or the channel may<br />

be specified as text in the event itself.<br />

FF 05 len text Lyric<br />

A lyric to be sung. Generally, each syllable will be a separate lyric event which begins Bt the<br />

event’s time.<br />

FF 06 len text Marker<br />

Normally in a format 0 track, or the first track in a format 1 file. The name of that point in the<br />

sequence, such as a rehearsal letter or section name (“First Verse”, etc.).<br />

FF 07 len text Cue Point<br />

A description of something happening on a film or video screen or stage at that point in the musical<br />

score (“Car crashes into house”, “curtain opens”, “she slaps his face”, etc.)<br />

FF 20 01 cc MIDI Channel Prefix<br />

The MIDI channel (0-15) contained in this event may be used to associate a MIDI channel with<br />

all events which follow, including System Exclusive and meta-events. This channel is “effective”<br />

until the next normal MIDI event (which contains a channel) or the next MIDI Channel<br />

Prefix meta-event. If MIDI channels refer to “tracks”, this message may help jam several tracks<br />

into a format 0 file, keeping their non-MIDI data associated with a track. This capability is also<br />

present in Yamaha’s ESEQ file format.<br />

81


FF 2F 00 End of Track<br />

This event is nor optional. It is included so that an exact ending point may be specified for the<br />

track, so that it has an exact length, which is necessary for tracks which are looped or concatenated.<br />

FF 51 03 tttttt Set Tempo, in microseconds per MIDI quarter-note<br />

This event indicates a tempo change. Another way of putting “microseconds per quarter-note”<br />

is “24ths of a microsecond per MIDI clock Representing tempos as time per beat instead of beat<br />

per time allows absolutely exact long-term synchronisation with a time-based sync protocol<br />

such as SMPTE time code or MIDI time code. This amount of accuracy provided by this tempo<br />

resolution allows a four- minute piece at 120 bears per minute to be accurate within 500 usec at<br />

the end of the piece. Ideally, these events should only occur where MIDI clocks would be located<br />

- this convention is intended to guarantee, or at least increase the likelihood, of compatibility<br />

with other synchronisation devices so that a time signature/tempo map stored in this format<br />

may easily be transferred to another device.<br />

FF 54 05 hr mn se fr ff SMPTE Offset<br />

This event, if present, designates the SMPTE time at which the track chunk is supposed to start.<br />

It should be present at the beginning of the track, that is, before any nonzero delta-times, and<br />

before any transmittable MIDI events. The hour must be encoded with the SMPTE format, just<br />

as it is in MIDI Time Code. In a format 1 file, the SMPTE Offset must be stored with the tempo<br />

map, and has no meaning in any of the other tracks. The ff field contains fractional frames, in<br />

l00ths of a frame, even in SMPTE-based tracks which specify a different frame subdivision for<br />

delta-limes.<br />

FF 58 04 nn dd cc bb Time Signature<br />

The time signature is expressed as four numbers. nn and dd represent the numerator and denominator<br />

of the time signature as it would be notated. The denominator is a negative power of<br />

two: 2 represents a quarter-note, 3 represents an eighth-note, etc. The cc parameter expresses<br />

the number of MIDI clocks in a metronome click. The bb parameter expresses the number of<br />

notated 32nd-notes in a MIDI quarter-note (24 MIDI Clocks). This was added because there are<br />

already multiple programs which allow the user to specify that what MIDI thinks of as a quarter-note<br />

(24 clocks) is to be notated as, or related to in terms of, something else. Therefore, the<br />

complete event for 6/8 time, where the metronome clicks every three eighth-notes, but there are<br />

24 clocks per quarter-note, 72 to the bar, would be (in hex): FF 58 04 06 03 24 08<br />

That is, 6/8 time (8 is 2 to the 3rd power, so this is 06 03), 36 MIDI clocks per dotted- quarter<br />

(24 hex!), and eight notated 32nd-notes per MIDI quarter note.<br />

FF 59 02 sf mi Key Signature<br />

sf = -7: 7 flats<br />

sf = -1: 1 flat<br />

sf = 0: key of C<br />

sf = 1: 1 sharp<br />

sf = 7: 7 sharps<br />

mi = 0: major key<br />

mi = 1: minor key<br />

82


FF 7F len data Sequencer-Specific Meta-Event<br />

Special requirements for particular sequencers may use this event type: the first byte or bytes<br />

of data is a manufacturer ID (these are one byte, or, if the first byte is 00, three bytes). As with<br />

MIDI System Exclusive, manufacturers who define something using this meta-event should publish<br />

it so that others may know how to use it. After all, this is an interchange format. This type<br />

of event may be used by a sequencer which elects to use this as its only file format; sequencers<br />

with their established feature-specific formats should probably stick to the standard features<br />

when using this format.<br />

Program Fragments and Example MlDI Files<br />

Here are some of the routines to read and write variable-length numbers in MIDI Files. These routines<br />

are in C, and use getc and putc, which read and write single 8-bit characters from/to the files infile and<br />

outfile. An long is assumed to be 32 bits.<br />

#define TRUE 1<br />

WriteVarLen( long value ) {<br />

long buffer;<br />

buffer = value & 0x7f;<br />

while ((value = 7) 0) {<br />

buffer


Delta Time<br />

(decimal)<br />

Event Code<br />

(hex)<br />

Other Bytes<br />

(decimal)<br />

Comment<br />

0 FF 58 04 04 02 24 08 4 bytes: 4/4 time, 24 MIDI clocks/click,<br />

8 32nd notes/24 MIDI clocks<br />

0 FF 51 03 500000 3 bytes: 500,000 μsec per quarter-note<br />

0<br />

0<br />

0<br />

0<br />

0<br />

96<br />

96<br />

192<br />

0<br />

0<br />

0<br />

0<br />

C0<br />

C1<br />

C2<br />

92<br />

92<br />

91<br />

90<br />

82<br />

82<br />

81<br />

80<br />

FF<br />

05<br />

46<br />

70<br />

48 96<br />

60 96<br />

67 64<br />

76 32<br />

48 64<br />

60 64<br />

67 64<br />

76 64<br />

2F 00<br />

Ch. 1, Program Change 5<br />

Ch. 2, Program Change 46<br />

Ch. 3, Program Change 70<br />

Ch. 3 Note On C2, forte<br />

Ch. 3 Note On C3, forte<br />

Ch. 2 Note On G3, mezzo-forte<br />

Ch. 1 Note On E4, piano<br />

Ch. 3 Note Off C2, standard<br />

Ch. 3 Note Off C3, standard<br />

Ch. 2 Note Off G3, standard<br />

Ch. 1 Note Off E4, standard<br />

Track End<br />

The entire format 0 MIDI file contents in hex follow. First, the header chunk:<br />

4D 54 68 64<br />

00 00 00 06<br />

00 00<br />

00 01<br />

00 60<br />

MThd<br />

chunk length<br />

format 0<br />

one track<br />

96 per quarter-note<br />

Then, the track chunk. Its header, followed by the events (notice that running status is used in places):<br />

00<br />

00<br />

00<br />

00<br />

00<br />

00<br />

00<br />

60<br />

60<br />

81 40<br />

00<br />

00<br />

00<br />

00<br />

FF<br />

FF<br />

C0<br />

C1<br />

C2<br />

92<br />

91<br />

90<br />

82<br />

81<br />

80<br />

FF<br />

4D 54 72 6B<br />

00 00 00 3B<br />

58 04 04 02 18 08<br />

51 03 07 Al 20<br />

05<br />

2E<br />

46<br />

30 60<br />

3C 60<br />

43 40<br />

4C 20<br />

30 40<br />

3C 40<br />

43 40<br />

4C 40<br />

2F 00<br />

MTrk<br />

chunk length (59)<br />

time signature<br />

tempo<br />

running status<br />

two-byte delta-time<br />

running status<br />

end of track<br />

A format 1 representation of the file is slightly different. Its header chunk:<br />

4D 54 68 64<br />

00 00 00 06<br />

00 01<br />

00 04<br />

00 60<br />

MThd<br />

chunk length<br />

format 1<br />

four tracks<br />

96 per quarter-note<br />

84


First, the track chunk for the time signature/tempo track. Its header, followed by the events:<br />

00<br />

00<br />

83 00<br />

FF<br />

FF<br />

FF<br />

4D 54 72 6B<br />

00 00 00 14<br />

58 04 04 02 18 08<br />

51 03 07 Al 20<br />

2F 00<br />

MTrk<br />

chunk length (20)<br />

time signature<br />

tempo<br />

end of track<br />

Then, the track chunk for the first music track. The MIDI convention for note on/off running status is<br />

used in this example:<br />

00<br />

81 40<br />

81 40<br />

00<br />

C0<br />

90<br />

FF<br />

4D 54 72 6B<br />

00 00 00 10<br />

05<br />

4C 20<br />

4C 00<br />

2F 00<br />

Then, the track chunk for the second music track:<br />

00<br />

60<br />

82 20<br />

00<br />

C1<br />

91<br />

FF<br />

4D 54 72 6B<br />

00 00 00 0F<br />

2E<br />

43 40<br />

43 00<br />

2F 00<br />

Then, the track chunk for the third music track:<br />

00<br />

00<br />

00<br />

83 00<br />

00<br />

00<br />

C2<br />

92<br />

4D 54 72 6B<br />

00 00 00 15<br />

46<br />

30 60<br />

3C 60<br />

30 00<br />

3C 00<br />

FF 2F 00<br />

MTrk<br />

chunk length (16)<br />

Running status: note on, vel = 0<br />

end of track<br />

MTrk<br />

chunk length (15)<br />

running status<br />

end of track<br />

MTrk<br />

chunk length (21)<br />

running status<br />

two-byte delta-time, running status<br />

running status<br />

end of track<br />

85


Lijst van literatuur en andere bronnen<br />

1. MIDI Manufacturers Association, http://www.midi/org<br />

2. Elektuur, E-Inside , 6/2001, applications of ceramics in electronics, p.30 (Engelse versie)<br />

3. http://www.pi.ws “Tutorial PZT Theory” P.4, Dynamic behavior.<br />

4. “Kristall Kochbuch”, manuscript, Harald Albrecht, NDK Europe Ltd.<br />

5. National Semiconductor, Application Note AN-400, a Study of the Crystal Oscillator for CMOS-<br />

COPS<br />

6. “Kristall Kochbuch”, manuscript, 06 - Oszillatorschaltungen mit Schwingquarzen, p. 18, Harald<br />

Albrecht, NDK Europe Ltd.<br />

7. “Kristall Kochbuch”, manuscript, Harald Albrecht, NDK Europe Ltd, activity dips p. 233<br />

8. Oscillators for Microcontrollers, Intel Application Note AP-155, p.11<br />

9. “Elektronische implementatiekunde”, p.227, dr. ir. G.C.M. Meyer, ISBN 90-407-1245-X<br />

10. MCS51 Microcontroller Family User’s Manual, External Interrupts, p. 3-25, Order No.:272383-<br />

002, Feb. 1994.<br />

11. “Stützenloses Spiel auf der Bratsche”, p. 35-38, http://www.violavirtuoso.de/chinrest/stzeart.pdf,<br />

Marc Nijdam<br />

12. Medizinische Probleme bei Instrumentalisten, Ursache & Prävention, Christoph Wagner, 1995,<br />

Verlag Laaber<br />

13. H.Geschwinde, “Einführung in die PLL-Technik”, Vieweg, eerste druk 1978 en Th. Mollinga,<br />

“De Phase Locked Loop, theorie en zijn toepassingen”, ‘n Educaboek, 1980<br />

14. ASEM-51, W.W. Heinz, http://plit.de/asem-51/<br />

15. MIDI File specification, http://www.omega-art.com/midi/mfiles.html<br />

86

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!