20.01.2014 Views

Den musikaliska kretsen

Den musikaliska kretsen

Den musikaliska kretsen

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Den</strong> <strong>musikaliska</strong> <strong>kretsen</strong><br />

Demonstrationslaboration om programmering av<br />

PIC-kretsar<br />

<strong>Den</strong>na demonstrationslaboration visar, steg för steg, hur man skriver ett kort program<br />

i programspråket C, och hur man "laddar ned det" det till en PIC-krets ( Perpherial<br />

Interface Computer ) med en kretsprogrammerare. Om PIC-<strong>kretsen</strong> förses med några<br />

elektroniska kringkomponenter kan den sedan utföra den uppgift den programmerats<br />

för.<br />

Vi har valt att låta PIC-<strong>kretsen</strong> spela upp en melodi. Detta är inte bara en "lek", utan<br />

det kan faktiskt vara en viktig funktion hos en datorstyrd konsumentprodukt av idag.<br />

Möjligheten att få en personlig ringsignal till sin mobiltelefon har varit ett av de<br />

viktigaste konkurrensmedlen mellan mobiltelefontillverkarna!<br />

Uppgiften är tillräckligt komplex för att visa PIC-<strong>kretsen</strong>s möjligheter, och för den<br />

delen, egenheter.<br />

Som programmeringsuppgift kommer Du sedan att få välja en egen uppgift som Du<br />

realiserar med en likadan PIC-krets.<br />

Musik, ton och not<br />

ton<br />

<strong>Den</strong> intresserade kan finna mycket information om musikens beståndsdelar och<br />

uppbyggnad på internet. Där finns även många exempel på hur ringsignalmelodier för<br />

olika mobiltelefoner är uppbyggda (RTTTL-formatet för ringsignaler). De kan<br />

eventuellt skrivas om och utnyttjas i denna "melodispelare".<br />

Vi börjar med att göra en "stämgaffel", det vill säga låta PIC-<strong>kretsen</strong> avge en<br />

kontinuerlig 440 Hz ton.<br />

1


För att få en stämton ska man "byta" värde "1"/"0" på <strong>kretsen</strong>s utgång varje<br />

halvperiod, vilket blir var 1140 µs. <strong>Den</strong>na tid kan man få genom att genomlöpa en 10<br />

µs programslinga 114 ggr. Man börjar därför med att designa en programslinga som<br />

tar exakt 10 µs att exekvera.<br />

Antalet varv i slingan för skalans övriga toner finns i en tabell.<br />

Ett bra hjälpmedel för att ställa upp sin C-kod är en JSP-editor. Alla datorprogram<br />

kan byggas upp av beståndsdelarna sekvens, selektion, och iteration som ingår i JSPdiagrammet.<br />

När man byggt upp sitt blockdiagram kan man välja menyvalet Generate, Generate<br />

C, för att få ett programskelett. Programskelettet visar programstrukturen, blockens<br />

rubriker blir till "C-kommentarer" som visar var man ska lägga in sin kod.<br />

int main()<br />

{<br />

while (I1){<br />

while (I2)<br />

/* note times - 10 us instructions */<br />

/* Toggle Output */<br />

}<br />

}<br />

Så här kan programmet se ut (för kompilering med B Knudsen's CC5X).<br />

Programskelettets andra while-slinga har bytts ut mot en (bekvämare) for-slinga. Man<br />

kompilerar programmet "på försök" för att se hur assemblerkoden blir och kan då<br />

beräkna hur många nop()-instruktioner som behövs för att slingan ska ta exakt 10<br />

µs. Om PIC-processorn har klockfrekvensen 4 MHz tar instruktionerna 1 µs (med<br />

några få undantag som tar 2 µs ) - detta innebär att det räcker med huvudräkning!<br />

2


* playtone.c Generates a 440 Hz "A4" tone<br />

*/<br />

/* Use processor 16F628<br />

*/<br />

#include "16F628.h"<br />

#pragma config |= 0x3df0<br />

void main(void )<br />

{<br />

char note = 114; /* gives standard pitch A4 440 Hz */<br />

TRISB = 0b11111110; /* B.0 is output */<br />

while (1) /* forever */<br />

{<br />

char i; /* local variable */<br />

for(i = note; i > 0; i--)<br />

{ /* Delay. Loop + nop()'s totals 10 us */<br />

nop(); nop(); nop(); nop();<br />

}<br />

/* Toggle Output */<br />

PORTB.0 = !PORTB.0; /* Toggle bit B.0 */<br />

}<br />

}<br />

Genomgång av assemblerkodens aktuella avsnitt tillsammans med huvudräkning visar<br />

att det är fyra nop() som behövs!<br />

; The 10 us delay loop.<br />

m002 MOVF i,1 ; 1 us<br />

BTFSC 0x03,Zero_ ; 2 us (When skipped)<br />

GOTO m003 ;<br />

NOP<br />

; 1 us<br />

NOP<br />

; 1 us<br />

NOP<br />

; 1 us<br />

NOP<br />

; 1 us<br />

DECF i,1<br />

; 1 us<br />

GOTO m002 ; 2 us<br />

; Total of 10 us!<br />

Efter kompileringen är det bara att "ladda ned" koden till sin krets med en kretsprogrammerare<br />

och jämföra ljudet från den med en stämgaffel för att höra om man<br />

träffat rätt ton!<br />

not<br />

För att spela noter (helnot, halvnot, fjärdedelsnot, åttondelsnot) behöver man hålla<br />

reda på tonernas varaktighet. Enklast är att bestämma ett fixt tidsintervall för den<br />

kortaste tonen (åttondelsnoterna), och att låta längre toner bestå av samma ton<br />

upprepad i flera intervall.<br />

3


TIMER0 inställningar i PIC-processorns OPTION-register.<br />

PIC-<strong>kretsen</strong> innehåller en 8-bitars räknare, TIMER0. <strong>Den</strong> kan "nollställas" och "läsas<br />

av" från programmet, och det är lämpligt att använda denna för att hålla reda på<br />

tonernas varaktighet. Med ett så kallat OPTION-register kan man koppla in en<br />

prescaler (frekvensdelare) så att timern räknar "lagom fort" för den aktuella<br />

tillämpningen. ( OPTION = 0b11000111 ).<br />

I programmet med den eviga stämtonen står vilkoret while(1). För att få en<br />

åttondelsnot byter man i princip ut det vilkoret till while(TMR0 < 250).<br />

Musik<br />

För att musik ska uppstå behöver man spela ett antal noter i följd. Vi antar att noterna<br />

finns lagrade i en funktion LookUpNote(i), som när den anropas med notens<br />

nummer i returnerar motsvarande tabellvärde för den noten. Två tabellvärden har<br />

speciell betydelse. Tabellvärdet "0" markerar slutet på melodin, och tabellvärdet "1"<br />

markerar en pausnot ( = tystnad ).<br />

4


JSP-diagram<br />

Programskelett<br />

int main()<br />

{<br />

while (I1){<br />

/* Look up note - note = LookUpNote( i ) */<br />

if (S1)<br />

/* Output off */<br />

else if (S2)<br />

/* Break */<br />

else<br />

/* Output on */<br />

/* Start "1/8"-time - Timer0 = 0 */<br />

while (I2){<br />

while (I3)<br />

/* note times - 10 us instructions */<br />

/* Toggle Output */<br />

}<br />

/* next note - i++ */<br />

}<br />

}<br />

5


Program (för kompilering med B Knudsen's CC5X).<br />

/* mel.c Play a melody */<br />

#include "16F628.h"<br />

#pragma config |= 0x3df0<br />

#include "lookup.c"<br />

#define EIGHT_NOTE 250<br />

char LookUpNote(char); /* function prototype */<br />

void main(void)<br />

{<br />

char note;<br />

PORTB = 0;<br />

OPTION = 0b11000111; /* Timer0 Prescaler divide by 256<br />

*/<br />

char i;<br />

for(i=0;;i++)<br />

{<br />

note = LookUpNote(i);<br />

if( note == 0 ) break;<br />

if( note == 1 ) TRISB = 0b11111111; /* pause note is silent<br />

*/<br />

else TRISB = 0b11111110;<br />

/* B.0 is output<br />

*/<br />

TMR0 = 0; /* Reset timer0<br />

*/<br />

while (TMR0 < EIGHT_NOTE) /* "1/8"-note duration<br />

*/<br />

*/<br />

*/<br />

}<br />

}<br />

{<br />

}<br />

char j;<br />

for(j = note; j > 0; j--)<br />

{ /* Delay. Loop + nop()'s totals 10 us<br />

nop(); nop(); nop(); nop();<br />

}<br />

/* Toggle Output */<br />

PORTB.0 = !PORTB.0; /* Toggle bit PORTB.0 On/Off<br />

PIC16F628 är en mycket liten processor med små resurser - egentligen omöjlig (?) att<br />

programmera i C! Många gånger stöter man på att kompilatorn "ber" om förenklad<br />

kod. I detta exempel innehöll slingan for(i=0;;i++) från början ett<br />

avbrottsvilkor, men kompilatorn "klagade" på för komplicerad kod. Avbrottsvilkoret<br />

placerades i stället inuti slingan som satsen if( note == 0 ) break; och med<br />

denna omskrivning blir det inte så sammansatta vilkor för kompilatorn att reda ut.<br />

Ett annat knep för att förenkla programkoden för kompilatorn är att använda goto<br />

(även om mången van C-programmerare nog skulle rynka på näsan åt detta).<br />

6


Noternas tabellvärden<br />

Melodin, noternas tabellvärden är lagras i funktionen LookUpNote(i) som<br />

placerats fristående från resten av programmet i filen lookup.c. Filen ska placeras i<br />

samma mapp som melody.c så att kompilatorn hittar den.<br />

PIC-processorns instruktioner är 12 bitar långa. Konstanter, sk "literals", lagras i<br />

programminnet inbakade i instruktioner. Det finns flera instruktioner av denna typ.<br />

Tabellvärden lagras i allmänhet med assemblerinstruktionen RETLW k ( RETurn<br />

with Litteral in W ), där k är den 8 bitars konstant som ska bakas in i instruktionen.<br />

Om processorn "hoppar" till en sådan instruktion, skickas den tillbaks med talet k i<br />

arbetsregistret, w-registret. Vid assemblerprogrammering används detta tillsammans<br />

med indexerad adressering så att man med hjälp av ett index kan styra vilken av<br />

tabellens konstanter man ska komma tillbaks med. Detta kallas för Computed<br />

GOTO.<br />

Exempel assemblerprogram. k1 k2 k3 är tabellens tre konstanter.<br />

CALL TABLE ; W contains table offset<br />

...<br />

TABLE ADDWFPC ; W = offset, go to offset table entry<br />

RETLW k1 ; Begin table, return with k1 in w<br />

RETLW k2 ; return with k2 in w<br />

RETLW k3 ; End of table, return with k3 in w<br />

Det finns inget "självklart" sätt att utrycka detta med C-språket. B Knutsen har infört<br />

en (inbyggd) funktion skip(i) som ska lösa detta. <strong>Den</strong>na betyder att man hoppar<br />

över det antal C-instruktioner som anges med "i".<br />

Exempel C-funktion (enligt CC5X). k1 k2 k3 är tabellens tre<br />

konstanter.<br />

char table(char i)<br />

{<br />

skip(i); /* internal function in CC5X */<br />

return k1;<br />

return k2;<br />

return k3;<br />

}<br />

Med detta skrivsätt får man samma kompakta kod som vid assemblerprogrammering<br />

med Computed GOTO, med en C-funktion som är "nästan" korrekt.<br />

Så här ser vår melodifunktion ut med detta skrivsätt<br />

( den återfinns i filen lookup.c ):<br />

char LookUpNote(char W)<br />

{<br />

skip(W); /* internal function to CC5X compiler */<br />

return 1; /* Pause */<br />

return 76; /* E5 */<br />

return 85; /* D5 */<br />

return 76; /* E5 */<br />

7


eturn 76;<br />

return 76;<br />

return 76;<br />

return 114; /* A4 */<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 72; /* F5 */<br />

return 76; /* E5 */<br />

return 72; /* F5 */<br />

return 72;<br />

return 76; /* E5 */<br />

return 76;<br />

return 85; /* D5 */<br />

return 85;<br />

return 85;<br />

return 85;<br />

return 85;<br />

return 85;<br />

return 85;<br />

return 85;<br />

return 1; /* Pause */<br />

return 1;<br />

return 72; /* F5 */<br />

return 76; /* E5 */<br />

return 72; /* F5 */<br />

return 72;<br />

return 72;<br />

return 72;<br />

return 114; /* A4 */<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 114;<br />

return 85; /* D5 */<br />

return 96; /* C5 */<br />

return 85; /* D5 */<br />

return 85;<br />

return 96; /* C5 */<br />

return 96;<br />

return 101; /* B4 */<br />

return 101;<br />

return 85; /* D5 */<br />

return 85;<br />

return 96; /* C5 */<br />

return 96;<br />

return 96;<br />

return 96;<br />

return 1; /* Pause */<br />

return 0; /* End */<br />

}<br />

8


PIC 4MHz Not-tabell<br />

Tabellen anger hur många gånger ( = "Tal" i tabellen ) man ska genomlöpa en 10 µs<br />

lång fördröjningsrutin så att den totala fördröjningen ska ta en halvperiod vid de olika<br />

toner ( = "Not" i tabellen ) som används i västerländsk musik.<br />

En ton skapas genom att man byter mellan "1"/"0" på en portpinne vid varje<br />

halvperiod.<br />

Stämton A 4 = 440 Hz<br />

Delay 10 µs<br />

Frekvenslista för tempererad stämning.<br />

Not Tal Not Tal Not Tal<br />

B 3 202 B 4 101 B 5 51<br />

A 3 # 214 A 4 # 107 A 5 # 54<br />

A 3 228 A 4 114 A 5 57<br />

G 3 # 240 G 4 # 120 G 5 # 60<br />

G 3 255 G 4 128 G 5 64<br />

F 4 # 135 F 5 # 68<br />

F 4 143 F 5 72<br />

E 4 152 E 5 76<br />

D 4 # 161 D 5 # 80<br />

D 4 170 D 5 85<br />

C 4 # 180 C 5 # 90<br />

C 4 191 C 5 96<br />

9

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!