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.
- Breadboard — large or medium
- Microstick
- One LM386 DIP
- One 3904 transistor
- Two 330 Ω resistors (orange–orange–brown)
- One 10 Ω resistor (brown–black–black)
- One 220 µF capacitor
- Two 0.1 µF capacitors (labeled 104 for 10⨯104 pF)
- One potentiometer
- One LED
References
- LM386 low voltage audio power amplifier
- 3904 NPN transistor
- PIC24HJ32GP302/304, PIC24HJ64GPX02/X04 and PIC24HJ128GPX02/X04 datasheet
- PIC24H Family Reference Manual
Our objective
We are going to look at these topics today:
- Using structures and pointers in C
- How digital systems can control analog devices such as speakers and motors through pulse wave modulation
- How digital systems can control servos by varying pulse width
- Perhaps, how pulse wave modulation with a sine wave might generate decent sound
- Perhaps, how pulse density modulation might generate decent sound
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.