Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C Programming for microcontrollers (Joe Pardue, 2005).pdf
Скачиваний:
260
Добавлен:
12.08.2013
Размер:
4.55 Mб
Скачать

Chapter 8: C Pointers and Arrays

Initializing the Timer1 for PWM to the piezo-element.

We initialize Timer1 to generate a PWM waveform as follows:

We set Timer/Counter Control Register A, TCCR1A, so that the OC1A pin (PortB pin 5) will be set when up counting and cleared when down counting.

TCCR1A = (1<<COM1A1);

We set the TCCR1B for phase and frequency correct PWM with a top value in ICR1.

TCCR1B = (1<<WGM13);

We set the TCCR1B to start Timer 1 with no prescaling

sbi(TCCR1B, CS10);

We set the Output Compare Register 1 High to 0 and Low to the value in the Volume variable. A lower value of Volume will produce a higher volume.

OCRA1H = 0;

OCRA1L = Volume;

Generating the tone using PWM from Timer1

When Play_Tune is called periodically by Timer0, we set the Timer1 TCCR1B, the Timer/Counter1 High and Low registers, TCNT1H TCNT1L , and the Input Capture Register 1, ICR1H, which in this case is used to set the counter top value.

In Play_Tune:

If the song calls for a pause we stop Timer1, otherwise we start it

if((pgm_read_word(pSong + Tone) == p) | (pgm_read_word(pSong + Tone) == P)) cbi(TCCR1B, CS10); // stop Timer1, prescaler(1)

else

sbi(TCCR1B, CS10); // start Timer1, prescaler(1)

We then load the tone value from the song array.

193

Chapter 8: C Pointers and Arrays

temp_hi = pgm_read_word(pSong + Tone); // read out the PWM-value

The Tone is an integer, so we get it into a temporary variable and shift it right by 8 bits and load that value into the high byte of the counter top. Except that we actually only shift it right by 7 bits to adjust it (cut it in half) to compensate for the use of a 2MHz system clock in this applications (for the USART) when a 1 MHz clock was used in the original Butterfly code.

temp_hi >>= 7;

We clear the Timer1 count.

TCNT1H = 0;

TCNT1L = 0;

We load the counter top value high byte.

ICR1H = temp_hi;

Finally we load the counter top value low byte and adjust it for the 2 MHz clock.

ICR1L = pgm_read_word(pSong + Tone);

ICR1L <<= 1;

Using the Timer0 interrupt to play a tune

As mentioned above the Timer0 interrupt calls the Play_Tune function every 10 ms. We set up the Timer0 much as we’ve seen before:

// Enable timer0 compare interrupt TIMSK0 = (1<<OCIE0A);

//Sets the compare value OCR0A = 38;

//Set Clear on Timer Compare (CTC) mode, CLK/256 prescaler TCCR0A = (1<<WGM01)|(0<<WGM00)|(4<<CS00);

194

Chapter 8: C Pointers and Arrays

The Play it again Sam Software.

For some reason, the Butterfly code commented out the songs Minuet and AuldLangSyne, but the WinAVR version uses them, so we get to hear two songs absent on the store-bought Butterfly.

Create a new directory: Play it again Sam, and copy the .c and .h files and the makefile from the Messenger directory. From the WinAVR Butterfly port, bf_gcc directory copy sound.c and sound.h. In Programmers Notepad, create a new C/C++ file, tunes.h and add:

// tunes.h

#include "sound.h"

Then copy the following from sound.c to tunes.h

/****************************************************************

*

*A song is defined by a table of notes. The first byte sets

*the tempo. A high byte will give a low tempo, and opposite.

*Each tone consists of two bytes. The first gives the length of

*the tone, the other gives the frequency. The frequencies for

*each tone are defined in the "sound.h". Timer0 controls the

*tempo and the length of each tone, while Timer1 with PWM gives

*the frequency. The second last byte is a "0" which indicates

*the end, and the very last byte makes the song loop if it's

*"1", and not loop if it's "0".

*

****************************************************************?

// mt

__flash char TEXT_SONG1[]

= "Fur Elise";

const

char TEXT_SONG1[] PROGMEM

= "Fur Elise";

// __flash int FurElise[] = const int FurElise[] PROGMEM=

{

3,

8,e2, 8,xd2, 8,e2, 8,xd2, 8,e2, 8,b1, 8,d2, 8,c2, 4,a1, 8,p, 8,c1, 8,e1, 8,a1, 4,b1, 8,p, 8,e1, 8,xg1, 8,b1, 4,c2, 8,p, 8,e1,

8,e2, 8,xd2, 8,e2, 8,xd2, 8,e2, 8,b1, 8,d2, 8,c2, 4,a1, 8,p, 8,c1, 8,e1, 8,a1, 4,b1, 8,p, 8,e1, 8,c2, 8,b1, 4,a1,

195

Chapter 8: C Pointers and Arrays

0, 1

};

 

//__flash char TEXT_SONG2[]

= "Turkey march";

const char TEXT_SONG2[] PROGMEM

= "Turkey march";

//__flash int Mozart[] =

const int Mozart[] PROGMEM =

{

3,

16,xf1, 16,e1, 16,xd1, 16,e1, 4,g1, 16,a1, 16,g1, 16,xf1, 16,g1,4,b1, 16,c2, 16,b1, 16,xa1, 16,b1, 16,xf2, 16,e2, 16,xd2, 16,e2, 16,xf2, 16,e2, 16,xd2, 16,e2, 4,g2, 8,e2, 8,g2, 32,d2, 32,e2, 16,xf2, 8,e2, 8,d2, 8,e2, 32,d2, 32,e2, 16,xf2, 8,e2, 8,d2, 8,e2, 32,d2, 32,e2, 16,xf2, 8,e2, 8,d2, 8,xc2, 4,b1, 0, 1

};

//mt song 3 & 4 where commented out by ATMEL - see their readme

//well, the gcc-geek wants all the songs ;-)

const char TEXT_SONG3[] PROGMEM

= "Minuet";

const int Minuet[] PROGMEM =

{

2,

4,d2, 8,g1, 8,a1, 8,b1, 8,c2, 4,d2, 4,g1, 4,g1, 4,e2, 8,c2, 8,d2, 8,e2, 8,xf2, 4,g2, 4,g1, 4,g1, 4,c2, 8,d2, 8,c2, 8,b1, 8,a1, 4,b1, 8,c2, 8,b1, 8,a1, 8,g1, 4,xf1, 8,g1, 8,a1, 8,b1, 8,g1, 4,b1, 2,a1,

0, 1

};

char TEXT_SONG4[] PROGMEM

= "Auld Lang Syne";

const int AuldLangSyne[] PROGMEM =

{

3,

4,g2, 2,c3, 8,c3, 4,c3, 4,e3, 2,d3, 8,c3, 4,d3, 8,e3, 8,d3, 2,c3, 8,c3, 4,e3, 4,g3, 2,a3, 8,p, 4,a3, 2,g3, 8,e3, 4,e3, 4,c3, 2,d3, 8,c3, 4,d3, 8,e3, 8,d3, 2,c3, 8,a2, 4,a2, 4,g2, 2,c3, 4,p,

0, 1

};

196

Chapter 8: C Pointers and Arrays

//__flash char TEXT_SONG5[]

= "Sirene1";

const char TEXT_SONG5[] PROGMEM =

"Sirene1";

//__flash int Sirene1[] = const int Sirene1[] PROGMEM =

{

0, 32,400, 32,397, 32,394, 32,391, 32,388, 32,385, 32,382,

32,379, 32,376, 32,373, 32,370, 32,367, 32,364, 32,361, 32,358, 32,355, 32,352, 32,349, 32,346, 32,343, 32,340, 32,337, 32,334, 32,331, 32,328, 32,325, 32,322, 32,319, 32,316, 32,313, 32,310, 32,307, 32,304, 32,301, 32,298, 32,298, 32,301, 32,304, 32,307, 32,310, 32,313, 32,316, 32,319, 32,322, 32,325, 32,328, 32,331, 32,334, 32,337, 32,340, 32,343, 32,346, 32,349, 32,352, 32,355, 32,358, 32,361, 32,364, 32,367, 32,370, 32,373, 32,376, 32,379, 32,382, 32,385, 32,388, 32,391, 32,394, 32,397, 32,400, 0, 1

};

//__flash char TEXT_SONG6[]

=

"Sirene2";

const char TEXT_SONG6[] PROGMEM =

 

"Sirene2";

//__flash int Sirene2[] =

 

 

const int Sirene2[] PROGMEM =

 

 

{

 

 

3,

 

 

4,c2, 4,g2,

 

 

0, 1

 

 

};

 

 

//__flash char TEXT_SONG7[]

=

"Whistle";

const char TEXT_SONG7[] PROGMEM

 

= "Whistle";

//__flash int Whistle[] =

 

 

const int Whistle[] PROGMEM =

 

 

{

 

 

0, 32,200, 32,195, 32,190, 32,185, 32,180, 32,175, 32,170,

32,165, 32,160, 32,155, 32,150, 32,145, 32,140, 32,135, 32,130, 32,125, 32,120, 32,115, 32,110, 32,105, 32,100, 8,p, 32,200, 32,195, 32,190, 32,185, 32,180, 32,175, 32,170, 32,165, 32,160, 32,155, 32,150, 32,145, 32,140, 32,135, 32,130, 32,125, 32,125, 32,130, 32,135, 32,140,

197

Chapter 8: C Pointers and Arrays

32,145, 32,150, 32,155, 32,160, 32,165, 32,170, 32,175, 32,180, 32,185, 32,190, 32,195, 32,200, 0, 0

};

// pointer-array with pointers to the song arrays

// mt: __flash int __flash *Songs[] = { FurElise, Mozart,

// /*Minuet, AuldLangSyne,*/

Sirene1, Sirene2, Whistle, 0};

const int *Songs[] PROGMEM

= { FurElise, Mozart, Minuet,

AuldLangSyne, Sirene1, Sirene2, Whistle, 0};

 

//mt: __flash char __flash *TEXT_SONG_TBL[]

= { TEXT_SONG1,

//TEXT_SONG2, /*TEXT_SONG3, TEXT_SONG4,*/TEXT_SONG5, TEXT_SONG6,

//TEXT_SONG7, 0};

////

mt: 16 ram-bytes (8 words) "wasted"

- TODO

////

PGM_P TEXT_SONG_TBL[] PROGMEM = {

TEXT_SONG1, TEXT_SONG2,

///*TEXT_SONG3, TEXT_SONG4,*/TEXT_SONG5, TEXT_SONG6, TEXT_SONG7,

//0};

const char *TEXT_SONG_TBL[]

= { TEXT_SONG1, TEXT_SONG2,

TEXT_SONG3, TEXT_SONG4, TEXT_SONG5, TEXT_SONG6, TEXT_SONG7, 0};

//__flash char PLAYING[]

= "PLAYING";

const char PLAYING[] PROGMEM

= "PLAYING";

//mt: int __flash *pSong; //pointer to the different songs in flash const int *pSong; // mt point to a ram location (pointer array Songs)

static char Volume = 80; static char Duration = 0; static char Tone = 0; static char Tempo;

Save tunes.h to the Play it again Sam directory.

Copy the sounds.h file from the WinAVR port of the Butterfly code, bf_gcc directory to the Play it again Sam directory. No changes, we’ll just steal the whole thing.

Change the contents of the messages.h file to:

// identify yourself specifically

const char TALKING_TO[] PROGMEM = "\rYou are talking to the ";

198

Chapter 8: C Pointers and Arrays

const char WHO_DEMO[] PROGMEM = "'Play it again Sam' demo.\r";

// bad command

const char BAD_COMMAND1[] PROGMEM = "\rYou sent: '";

const char BAD_COMMAND2[] PROGMEM = "' - I don't understand.\r";

const char ENTER[] PROGMEM = "Enter ";

const char TEXT_FUR_ELISE[] PROGMEM = "1 for Fur Elise.\r"; const char TEXT_TURKEY_MARCH[] PROGMEM = "2 for Turkey march.\r"; const char TEXT_MINUET[] PROGMEM = "3 for Minuet.\r";

const char TEXT_AULD_LANG_SYNE[] PROGMEM = "4 for Auld Lang Syne.\r";

const char TEXT_SIRENE1[] PROGMEM = "5 for Sirene1.\r"; const char TEXT_SIRENE2[] PROGMEM = "6 for Sirene2.\r"; const char TEXT_WHISTLE[] PROGMEM = "7 for Whistle.\r"; const char VOLUME_UP[] PROGMEM = "+ to increase"; const char VOLUME_DOWN[] PROGMEM = "- to decrease"; const char THE_VOLUME[] PROGMEM = " the volume.\r";

const char STOP[] PROGMEM ="stop to stop the music.\r" ;

Change the Demonstrator.c to:

// Demonstrator.c PWM version

#include "PC_Comm.h" #include "Messages.h"

#include "tunes.h"

void initializer()

{

//Calibrate the oscillator: OSCCAL_calibration();

//Initialize the USART USARTinit();

//Initialize timer0 to play a tune Timer0_Init();

//initialize piezo-element

sbi(DDRB, 5);

//

set

OC1A

as output

sbi(PORTB, 5);

//

set

OC1A

high

// Display instructions on PC

199

Chapter 8: C Pointers and Arrays

sendFString(TALKING_TO); sendFString(WHO_DEMO); sendFString(ENTER); sendFString(TEXT_FUR_ELISE); sendFString(ENTER); sendFString(TEXT_TURKEY_MARCH); sendFString(ENTER); sendFString(TEXT_AULD_LANG_SYNE); sendFString(ENTER); sendFString(TEXT_SIRENE1); sendFString(ENTER); sendFString(TEXT_SIRENE2); sendFString(ENTER); sendFString(TEXT_WHISTLE); sendFString(ENTER); sendFString(VOLUME_UP); sendFString(THE_VOLUME); sendFString(ENTER); sendFString(VOLUME_DOWN); sendFString(THE_VOLUME); sendFString(ENTER); sendFString(STOP);

}

void parseInput(char s[])

{

if( (s[0] <= '7') && ( s[0] >= '1') ) // 7 tunes

{

startTune(s[0]);

}

else

{

// parse first character switch (s[0])

{

case '+': volumeUp(); break;

case '-': volumeDown(); break;

case 's':

if( (s[1] == 't') && (s[2] == 'o') && (s[3] == 'p')) stopTune();

200

Chapter 8: C Pointers and Arrays

break; case 'd':

if((s[1]=='e')&&(s[2]=='m')&&(s[3]=='o')&& (s[4 =='?')) sendFString(TALKING_TO);

sendFString(WHO_DEMO); break;

default: sendFString(BAD_COMMAND1); sendChar(s[0]); sendFString(BAD_COMMAND2); break;

}

s[0] = '\0';

}

}

 

void volumeUp()

 

{

 

if(Volume >= 250)

 

Volume = 250;

 

else

 

Volume += 50;

 

OCRA1H = 0;

 

OCRA1L = Volume;

 

}

 

void volumeDown()

 

{

 

if(Volume < 11)

 

Volume = 6;

 

else

 

Volume -= 50;

 

OCRA1H = 0;

 

OCRA1L = Volume;

 

}

 

void stopTune()

 

{

// stop Playing

cbi(TCCR1B, 0);

TCCR1A = 0;

 

TCCR1B = 0;

// set OC1A high

sbi(PORTB, 5);

201

Chapter 8: C Pointers and Arrays

}

void startTune(char tune)

{

int song = atoi(&tune) - 1;

stopTune(); Tone = 0; Tempo = 0; Duration = 0;

//Send the song title to the PC sendFString(TEXT_SONG_TBL[song]); sendChar('\r');

//looks too complicated.. pSong=(int*)pgm_read_word(&Songs[song]);

Sound_Init();

}

void Sound_Init(void)

{

//Set OC1A when upcounting, clear when downcounting TCCR1A = (1<<COM1A1);

//Phase/Freq-correct PWM, top value = ICR1

TCCR1B = (1<<WGM13);

sbi(TCCR1B, CS10); // start Timer1, prescaler(1)

//Set a initial value in the OCR1A-register OCRA1H = 0;

//This will adjust the volume on the buzzer, lower value

//=>higher volume

OCRA1L = Volume;

}

void Play_Tune(void)

{

int temp_hi;

char loop;

202

Chapter 8: C Pointers and Arrays

if(!Tone)

{

Duration = 0;

Tempo = (uint8_t)pgm_read_word(pSong + 0); Tempo <<= 1; // compensate for using 2 MHz clock Tone = 1; //Start the song from the beginning

}

if(!Tempo)

{

if(Duration) // Check if the length of the tone has "expired"

{

Duration--;

}

else if(pgm_read_word(pSong + Tone))// If not the song end

{

// store the duration

Duration = (DURATION_SEED/pgm_read_word(pSong+Tone)); Duration <<= 1;// compensate for using 2 MHz clock Tone++; // point to the next tone in the Song-table

// if pause if((pgm_read_word(pSong+Tone) == p)|

(pgm_read_word(pSong+Tone) == P))

cbi(TCCR1B, CS10); // stop Timer1, prescaler(1) else

sbi(TCCR1B, CS10); // start Timer1, prescaler(1)

cli();

// read out the PWM-value

temp_hi = pgm_read_word(pSong + Tone);

temp_hi >>= 7; // move integer 8 bits to the right

TCNT1H = 0; // reset TCNT1H/L

TCNT1L = 0;

ICR1H = temp_hi; // load ICR1H/L

ICR1L = pgm_read_word(pSong + Tone);

ICR1L <<= 1; // compensate for using 2 MHz clock

sei();

Tone++; // point to the next tone in the Song-table

203

Chapter 8: C Pointers and Arrays

}

else // the end of song

{

Tone++; // point to the next tone in the Song-table

// get the byte that tells if the song should loop or not loop = (uint8_t)pgm_read_word(pSong + Tone);

if( loop )

{

Tone = 1;

}

else // if not looping the song

{

Tone = 0;

cbi(TCCR1B, 0); // stop Playing TCCR1A = 0;

TCCR1B = 0;

sbi(PORTB, 5); // set OC1A high

}

}

Tempo = (uint8_t)pgm_read_word(pSong + 0);

}

else Tempo--;

}

void Timer0_Init(void)

{

//Initialize Timer0.

//Used to give the correct time-delays in the song

//Enable timer0 compare interrupt

TIMSK0 = (1<<OCIE0A);

//Sets the compare value OCR0A = 38;

//Set Clear on Timer Compare (CTC) mode, CLK/256 prescaler TCCR0A = (1<<WGM01)|(0<<WGM00)|(4<<CS00);

}

204

Chapter 8: C Pointers and Arrays

SIGNAL(SIG_OUTPUT_COMPARE0)

{

Play_Tune();

}

Finally change Demonstrator.h to:

// Demonstrator.h PWM version

void initializer(void); void parseInput(char *);

int parseTune(char *); void startTune(char);

void volumeUp(void); void volumeDown(void); void stopTune(void);

void Sound_Init(void); void Timer0_Init(void);

Using Play it again Sam:

This is what you should see in HyperTerminal, and an example of use:

You are talking to the 'Play it again Sam' demo. Enter 1 for Fur Elise.

Enter 2 for Turkey march. Enter 3 for Minuet.

Enter 4 for Auld Lang Syne. Enter 5 for Sirene1.

Enter 6 for Sirene2. Enter 7 for Whistle.

Enter + to increase the volume. Enter - to decrease the volume. Enter stop to stop the music.

4

Auld Lang Syne 1

Fur Elise

205