2.3 Relaxatie-oscillator
2.3 Relaxatie-oscillator
2.3 Relaxatie-oscillator
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