Файл: C Programming for microcontrollers (Joe Pardue, 2005).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 13.06.2025

Просмотров: 2459

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

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


Chapter 9 – Digital Meets Analog – ADC and DAC

Chapter 9 – Digital Meets Analog – ADC and

DAC

But First - A Debugging Tale

In the ADC project that follows, I liberally ‘borrowed’ code from the Butterfly, adding my own inimitable style to allow a user from the PC to ask for a measure of light, temperature, and voltage. All was well except for a tiny problem with the voltage measurement. Tiny as in the first time I tried to measure voltage on the Butterfly I destroyed it. Well destroyed is a bit harsh. It looks just like it always did, but it doesn’t work. Fortunately I know myself so I had ordered six Butterflys because, as I said elsewhere, my nickname is Smoky Joe since my favorite learning method is producing copious quantities of smoke in my hardware projects. The Butterfly didn’t smoke though. It just died. Belly up, legs in the air, ready for a pin thru the thorax to be box mounted in the Dead Butterfly Museum. But Lepidopteron death is not what this is about. I eventually found that I had done something unbelievably stupid and since you wouldn’t believe it, I won’t relate that tale. Lets just say this event led me to becoming a bit paranoid about the voltage measurement part of the Butterfly hardware and I went forward on tiptoes and slightly hyperventilating as I proceeded with the ADC code.

My next version was able to read the light just fine, and the temperature just fine, and the voltage just one time.

When I requested: volt the hardware responded to HyperTerminal with: The reading is 1.1 volts.

And promptly died. No further responses to the PC. My response involved lots of obscenities and complaints about flushing another $19.99, but the Butterfly wasn’t destroyed this time, it rebooted just fine and only crashed when I asked for ‘volt’. My first assumption, reasonable I thought, was that it’s the light level in the room. Sound crazy? Well, it seems that the light sensor affects the Butterfly voltage reference and we have to measure the ambient room light to calibrate the voltage reference before we measure volts. So I covered the light sensor and the Butterfly still crashed. Then I went the other direction and put a bright light on it to no avail. So I thought that if its all that sensitive to light derived voltages maybe the USART traffic voltage is propagating about unpredictably and screwing things up. The USART uses a higher voltage than the Butterfly and they

207

Chapter 9 – Digital Meets Analog – ADC and DAC

have included a voltage inverter circuit that looked like a prime candidate to radiate messy voltages that might combine with a voltage on the Voltage In pin and might, theoretically, cause a problem. So I changed PC_Comm so that it sent the PC a ‘!’ every time it received a character. In HyperTerminal I got:

So after requesting ‘volt’ the Butterfly was no longer exclaiming with a ‘!’ but decided it wanted to play cards with a black club, or perhaps more reasonably, I thought, the ‘!’ was being scrambled on reception by the PC because the Baud rate had changed (I’ve seen that happen before). So I reread the data sheet on the USART and diddled with the Butterfly schematics and tried a few coding changes. Hours passed and still no fix. I messed with the USART initialization function, the ADC initialization function, the ADC read function, the oscillator calibration function and generally had myself a merry old goose chase for about half a day. Nothing fixed the problem, but at least the Butterfly didn’t explode nor make the least bit of smoke, the code was consistently responding with a black club rather than the ‘!’ but at least it was running.

Finally, in total desperation, I tried what I should have tried in the first place. I bracketed code by commenting out sections (putting // in front of a line) to see where exactly the problem occurred. Eventually I got to the getVolt function and started commenting out sections. This is a time consuming process, each time you comment something out, you have to recompile, load, and test the code. It takes a while. So here is the getVolt code:

void getVolt()

{

char voltintpart[]= {''0','\0'}; char voltfractpart[]= {'0','\0'}; int intpart = 0;

int fractpart = 0; int ADCresult = 0;

ADCresult = ADC_read(); intpart = ADCresult/50;

208


Chapter 9 – Digital Meets Analog – ADC and DAC

fractpart = ADCresult%50;

itoa(intpart, voltintpart, 10); itoa(fractpart, voltfractpart, 10);

// Send the voltage to the PC sendString("The reading is "); sendChar(voltintpart [0]); sendChar('.'); sendChar(voltfractpart [0]); sendString(" volts.\r");

}

I commented out each logical part and still nothing worked. Finally, because there was nothing logical to try, I commented out the itoa functions. And the Butterfly no longer messed up. Also, it started returning ‘!’ rather than the black club for each character I sent it. Of course, it didn’t return the correct voltage, because I wasn’t converting it to ASCII, but it was running fine otherwise and correctly returned the light and temperature. The itoa function is in the standard library, so I assumed that it must have a problem. I changed it to the itoa function (and the other support functions) that we wrote at the end of Chapter 6. Guess what? They also fail! I went for a long walk.

Later, after more staring at the function I noticed:

char voltintpart[]= {''0','\0'}; char voltfractpart[]= {'0','\0'};

These can’t be the problem, can they? They work just fine in the original Butterfly code. But many years ago I learned the hard way that if you assign memory to an array and then foolishly write beyond that memory, say to voltintpart[2], the third element of the array which only has two elements you will in fact write to the byte in memory that follows the array bytes which may not cause a problem if nothing else is using that byte, or it might just change it from the ASCII code for an explanation point to the Microsoft extended ASCII code for a black club. So I enlarged them to:

char voltintpart[]= {'0','0',0','\0'}; char voltfractpart[]= {''0',0','0','\0'};

And the code works just fine.

209


Chapter 9 – Digital Meets Analog – ADC and DAC

Why, you may ask, didn’t the designers of the standard library require the itoa function to check the size of the array before using it? Good question. My guess is that the standard library functions were written to be as fast and small as possible. They also likely assumed nobody would be stupid enough to send it an array that was too small, and if they are that dumb, they deserve what they get. C is fast, small, and mean. C++ was designed to take out some of the meanness by forcing features that protect the programmer from himself, but this was done at the expense of size, speed, and simplicity. Other higher-level languages provide even more protection and are even larger, slower, and more complex. You almost certainly won’t be using C to write programs for windows based programs on a PC where you’ve got plenty of hardware speed and memory and high level development tools, but for microcontrollers with their limited speed and memory, C probably has the best set of tradeoffs. I acknowledge that the arguments for the choice of a programming language borders on the religious, so I say that if you choose not to use C, you will be eternally damned.

Debugging can be a maddeningly frustrating process, especially if you are on a deadline. I spent half a day finding this problem. I didn’t make my array large enough. HALF A DAY! Am I stupid? No, I’m not. This kind of debugging is part and parcel of working with microcontrollers. If you have the wrong attitude, you will drive yourself nuts trying to find these bugs. What is the right attitude? It is to understand that you have to be really smart, work very hard, and know a lot about C programming and microcontrollers to make a mistake this dumb. You have to keep telling yourself over and over: “This is better than putting shingles on roofs in the summer.” I understand that alcohol also helps as does having an obsessivecompulsive disorder. Speaking of which, Let’s move on to the next project.

Analog to Digital Conversion

What is Analog to Digital Conversion?

During a discussion with one of my EE professors about Analog to Digital Conversion, I made the mistake of bringing up Heraclitus and Democritus and the ancient debate about the fundamental nature of reality: is reality made of a continuum of a single thing (analog) or of bits of multiple things (digital). He shook his head, as my profs often did, and said, “Son, I don’t give a damn what some sheep header said 3,000 years ago, this IS an analog world!” Humpf! I say it

210

Chapter 9 – Digital Meets Analog – ADC and DAC

depends on your perspective. To electronics, Heraclitus was right and the world is analog and you can’t step in the same river twice since the only constant is change. To computers, Democritus was right, the world is digital (well, he said atomic, but it’s the same thing philosophically speaking) and you can theoretically step in the same river twice if you arrange the atoms (bits) of the river the same way.

Another professor of mine, also shaking his head, said, “Son, a difference that makes no difference is no difference!” So Let’s drop this right here and say that the world we want to control is analog and the world we will use to control it is digital, and somewhere up there in philosopher heaven Heraclitus and Democritus can give each other a big hug.

Analog to Digital Conversion by Successive Approximation

We want to measure voltage, and in the real world, voltages can be any value whatever, they represent a continuum of electromotive force. There are many ways to convert an analog signal to a digital value each having strengths and weaknesses, we will examine successive approximation since that is what the AVR uses.

Analog Input

Analog

Comparator

10-bit DAC

Successive Approximation

Register

Start Conversion

Control Logic

End Conversion

Data Registers

Oscillator

Figure 27: 10-bit successive approximation ADC Figure

211