CSCI 255 — Better digital to analog

Getting ready

You need to start with the setup you had at the end of the Pulsing with the PIC lab. If you have to start all over, here at the parts you need.

References

Our objective

We are going to look at these topics today:

Getting to the starting point

You need to demonstrate you were playing tunes at the end of the last lab before going any further.

Pointers and structure in C

By the end of the last lab, you had written a routine playTone to play a tone at a frequency, given in Hz, for a duration, given in milliseconds. Here’s the C/C++ prototype for that function.

void playTone(uint16_t frequency, uint16_t duration) ;

Our representation of a note has an abstraction issue. The note is a real-world entity. we shouldn’t be splitting the note into two arrays, one for frequency and the other for duration. We should have an array of C structures that represent notes.

So add the following structure definition at the front of your program, close to the prototype you have for playTone.

struct noteInfo {
   uint16_t frequency ;    /* frequency in Hz */
   uint16_t duration ;     /* duration in mSec */
} ;
typedef struct noteInfo Note ;

Note the slight resemblance to a Java class definition.

Right now your program should have two array definitions borrowed from Tom Igoe’s Arduino tutorial. This is how Tom Igoe wrote them:

// notes in the melody:
int melody[] = {
  NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  4, 8, 8, 4,4,4,4,4 };

Replace these two array of int definitions with a single array of Note definitions. It starts with the following:

Note song[] = {
  { NOTE_C4, 1000/4 },

Add the definition for Note and the initialization for song into your program.

Of course you could continue to call playTone with something like playTone(song[i].frequency,song[i].duration) but you really ought to pass a Note; or, since this is C, a pointer to a Note.

Start by updating the prototype for playTone.

void playTone(const Note *tone) ;

Modify the prototype for playTone. Also modify the calls to playTone in main to match the prototype. Expect NetBeans to display quite a few red exclamation marks.

Now you need to turn your attention to the implementation of playTone.

Now modify playTone to accept a pointer to a Note as its single arugment. It won’t be much more than changing the function header and updating references like frequency to note->frequency.

At this point you should be able to compile and test your modified program.

Louder sound

If you didn’t get a chance to play loud music in the previous lab, you should return to Getting louder section of the Pulsing with the PIC lab and try out both the 3904 and LM386.

A new pulsing routine

If you reach this point, we are going to start some experiments. These experiments are largely independent. You do what you can. If you do two, that is great. None of these will involve playing a song, so you can just comment out your for for the song

Even with its modification playTone still generate high and low pulses with the same duration. duration. This means that the average voltage output on the pin does not vary. This does not allow us to change the volume of our speaker or the speed to a motor. It also doesn’t allow us to control a servo.

Let’t try again. Start by creating a new C function that conforms to the following prototype where pulseHi is the length of the high pulse in microseconds; pulseLow is the length of low high pulse in microseconds; and duration is the time in which the alternating pulses are generated in milliseconds. Here’s a prototype for you.

void genPulseWave(uint32_t pulseHi, uint32_t pulseLo, uint32_t duration) ;

Add a prototype and implementation for getPulseWave. Of course, you should start genPulseWave with a cut-and-paste from playTone.

Comment out all the song stuff. Test genPulseWave with direct calls similar to the following:

genPulseWave(500000UL/261, 500000UL/261, 5000) ;
genPulseWave(200000UL/261, 800000UL/261, 5000) ;
genPulseWave(500000UL/20000, 500000UL/20000, 1000) ;
genPulseWave(500000UL/2000, 500000UL/2000, 1000) ;
genPulseWave(500000UL/200, 500000UL/200, 1000) ;
genPulseWave(500000UL/20, 500000UL/20, 1000) ;

Experiment 1 — Pulse Width Modulation

By keeping the period fixed, but changing the width of the high pulse you can vary the voltage, and often the power, delivered to a device. You have already seen how to do this with an LED.

In the last lab, you used a 3904 transistor to drive your speaker. You can actually replace the speaker with a small DC motor. If you increase the high pulse width as you decrease the low pulse width, the motor should speed up.

Try to make a motor cycle between fast and low speed with PWM.

You can read more about motor control in Rebecca Bruce’s presentation for EGM 360/CSCI 373 this term.

Experiment 2 — Servos

All those radio controlled cars use servos to turn the wheels. In servo control the width of a high pulse is varied from 1 to 2 milliseconds within a 20 millisecond period to change the rotation of a servo motor. DC motors can spin around, but servo motors just rotate to a position and stay there for a little while.

You should look at the servo section of Rebecca Bruce’s presentation for EGM 360/CSCI 373 this term to see how servos must be connected to your PIC before you start using them.

Program a servo to move between various position. Start by making the servo move a few degrees at a time. You’ll need to stay in a position for a half second or so to give it time to settle down.

You can read more about motor control in Rebecca Bruce’s presentation for EGM 360/CSCI 373 this term.

Experiment 3 — Music and sine controlled PWM with a touch of PCM

This one is a hard one. Right now our speaker receives a high pulse for half the period and a low pulse for the rest of the period. What if we divided the duration into 16 equal sections and the high pulse of each of those sections varied according to the output of a sine function? Would the music sound any better?

You could call sin from your PIC program but it would be better generate a table of sine values from either a spreadsheet or C program. Here is a link to a table of sixteen values generated using C.

You would need to add this C array declaration to your program. I suggest declare it of type uint8_t. If you were generating a tone of 131 Hz, you would first figure out the usual period, 7633 µsec. Then divide that by 16 to get 477 µsec.

Your program will then go through a loop 16 times, for each element of the array. On the i’th iteration, you will look up the i’th element of the array. Let’s say it is pi. Now will call genPulseWave for a high pulse for pi, a low pulse of 16-pi µsec, and a duration of 477 µsec.