Getting ready
You’ll need the follow hardware for this lab.
- 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 four topics today:
- How digital systems can generate periodic output.
- How digital systems can generate “analog” output.
- Using transistors and amplifiers to make loud noises.
- Creating and using C header files.
Getting started
Putting the Microstick on the board
We continue our use of the Microstick in this lab.
Place you Microstick on the breadboard with ground connections
to pins 8, 19, and 27 as you did for the
Pin I/O on the PIC lab;
however, this time use only a single LED and connect the LED and
330 Ω resistor, in series, to pin 15
(RB6
) of the Microstick.
Getting MPLABX ready
Start up MPLABX and download the following program as the main C routine.
// pulser lab for CSCI 255 // CSCI 255 clock configuration #define CSCI255CLOCK FRCPLL80MZ #include "csci255setup.h" #pragma config FWDTEN=OFF // Watch dog timer disabled // Standard includes #include <stdlib.h> #include <stdint.h> // PIC includes #include <xc.h> #include <libpic30.h> // Output pin // Pin 15 on PIC24HJ64GP502 -- PGEC3 / ASCL1 / RP6 / CN24 / PMD6 / RB6 #define PULSERTRIS _TRISB6 #define PULSERVALUE _LATB6 const int16_t pulseMS = 800 ; const int16_t periodMS = 1000 ; int main() { setupClock() ; setupAllDigitalOutputWritingZero() ; PULSERTRIS = 0 ; PULSERVALUE = 1 ; while (1) { PULSERVALUE = 1 ; __delay_ms(pulseMS) ; PULSERVALUE = 0 ; __delay_ms(periodMS-pulseMS) ; } return (EXIT_SUCCESS) ; }
Can this be right?
MPLABX is not happy. Let’s try to fix the problems. First of all, your program is missing the include file csci255setup.h . Right click on Header Files in the Projects tab and add a new C header file called csci255setup.h .
Delete the initial contents MPLABX has generated for you and replace them with the following definitions.
#ifndef CSCI255SETUP_H #define CSCI255SETUP_H #if CSCI255CLOCK == FRCPLL80MHZ #define FCY (79227500UL/2) #elif CSCI255CLOCK == LPRC #define FCY=(32768UL/2) #else // assume FRC #define FCY (7370000UL/2) #endif #include <xc.h> #if CSCI255CLOCK == FRCPLL80MHZ #pragma config FNOSC=FRC // Fast RC Oscillator #pragma config FCKSM=CSECMD // Clock switching enabled, FSCM disabled inline void setupClock(void) { PLLFBD = 41 ; // PLL Feedback Divisor _PLLPOST = 0 ; // PLL postscaler _PLLPRE = 0 ; // PLL prescaler // Multiplying by (41+2)/((0+2)*2+(0+2)*2) or 10.75 // 10.75 * 7370000 = 79227500 // Writes 0x78 then 0x9A to unlock and then updates _NOSC __builtin_write_OSCCONH(0b001) ; // Writes 0x46 then 0x57 to unlock and then updates _OSWEN __builtin_write_OSCCONL(0x1) ; while(_COSC != 0b001) ; // waiting for clock switch while(_LOCK != 0b1) ; // waiting for PLL to lock } #elif CSCI255CLOCK == LPRC #pragma config FNOSC=LPRC #pragma config IESO=OFF inline void setupClock(void) { } #else // assume FRC #pragma config FNOSC=FRC inline void setupClock(void) { } #endif inline void setupAllDigitalOutput(void) { AD1PCFGL = 0xFFFF ; // Set all pins for digital CNEN1 = 0 ; // Disable change notification CNEN2 = 0 ; CNPU1 = 0 ; // Disable weak pullup CNPU2 = 0 ; ODCA = 0 ; // Disable open drain ODCB = 0 ; TRISA = 0 ; // Set all pins for output TRISB = 0 ; } inline void setupAllDigitalOutputWritingZero(void) { setupAllDigitalOutput() ; LATA = 0 ; LATB = 0 ; } #endif /* CSCI255SETUP_H */
We are going to take a little time now for everyone in the lab to get their LED flashing. If you get yours working quicker than your neighbors, help them out.
The include file
Now set back for a minute for a little lecture on C (and C++) include files. Here are the topics to be covered.
- The
#include
guard - Conditional directives
- Inline functions
- Setting prescaler and postscaler for generating a clock signal
Pulse Width Modulation
Now back to the program.
Change your program so that pulseMS
is 1 and
periodMS
is 10.
You’ve did something like this
in the Pin I/O on the PIC
lab.
You can’t see the LED flashing on and off; but, because it
is only on 10% of the time, it appears to be dimmer.
This is called pulse width modulation. This is pretty much the way the light dimmers in your living room work. They turn the light on and off about 120 times per second, and your eyes just don’t notice the difference. (However, standard fluorescent light (CFL) bulbs do notice the difference, so you need to purchase special dimmable CFLs for those dimmer circuits in your home.)
Modify your program so that it gradually brightens the LED
by varying the value of pulseMS
with time.
Turn the LED completely off for one second and then increase the pulse width every second until the LED is completely on. Have your program repeat the cycle of dark to light every ten seconds.
Now for sound
First of all, undo some of what you just did.
Modify your program so that pulseMS
is always 2
and periodMS
is always 4.
If you can’t bring yourself to delete your old code, follow the
practice of the pros as shown below. It’s easier than adding
comments.
#ifdef DELETEDCODE for (i=0; i<1000; ++i) { printf("Annoying message #%d\n", i) ; } #endif
The LED should now be flashing on and off 250 times per second. More formally, we’d say that it has a frequency of 250 Hz and a period of 4 msec. More musically, we’d say that it is close to the B below middle C. (Due to variations in clock frequency of the PIC, neither of these is precisely true; but hopefully, they are almost true.)
Get a speaker and plug it into your breadboard. Just replace the LED with the speaker. You should be able to hear a faint tone.
The tone will be a bit unmusical because it is being generated by a square wave. A sine wave would be more pleasent, but that requires more mathematics and a faster processor. For the most part we are going to concentrate on generating louder, rather than better, sound today. But first we need to work on creating a little music.
A useful function
Right now your are,
presumably, writing a doubly nested loop for every tone we generate.
Let’s write a C function playTone
for generating a note.
playTone
take two arguments, the frequency
of the note given in Hertz and the
duration of the note given in milliseconds.
Start by replacing your loop for playing the alternating tones with this much simpler loop.
while (1) { playTone(500, 1000) ; playTone(250, 1000) ; }
Now add the outline for a function playTone
at
the end of your program, after main
.
void playTone(uint16_t frequency, uint32_t duration) { // frequency: frequency of the tone in cycles per second (Hz) // if frequency is 0, place no tone // duration: length of the tone in milliseconds (msec) }
In C and C++ it is considered good style to provide prototypes
for functions that are used before they are defined.
C/C++ prototypes are a bit like method signatures in Java.
Add a one-line prototype for playTone
before your main
function, so that the compiler will know
the types of the arguments for playTone
when
it is compiling main
void playTone(uint16_t frequency, uint32_t duration) ;
Usually functions are written in their own .c files and distributed with corresponding .h files containing prototypes and other useful constant and data structure definitions, but here we are placing the prototype and implementation in the same file.
Complete the playTone
procedure.
We suggest you work in units of microseconds inside the
procedure. Start by computing both the period and duration
in microseconds.
16-bit integers are not big enough to deal with units stored in microseconds. You need to move to 32-bit integers.
What about a song?
Let’s take advantage of the work of someone else, even if it was done for an Arduino. In another window open up the Arduino tutorial Play a Melody using the Tone() function written by Tom Igoe. Read the tutorial.
Now download Igoe’s
pitches.h and place it into
your MPLABX project.
You also ought to open another window with the
Arduino sketch and take a look at Igoe’s
setup
function. This looks like something you
could use in your main
routine.
Make your main
function look a lot like Tom Igoe’s
setup
function.
This isn’t that hard. Start with a cut-and-paste and them make
the needed adjustments.
By the way, this is the sort of thing embedded systems programmers do
frequently.
Now play your song over and over and over, but add a delay
of about one second between performances.
If you wish, you may encode a melody
of your own choosing.
Getting louder
The PIC is a control device. It can’t generate the current needed to make a lot of noise.
A few times during these labs we have mentioned the need to read schematics. The time has come. If you think you need a tutorial on schematics, I suggest you look at From Schematic to Reality: Understanding Schematics from Beavis Audio.
Revisiting the 3904 transistor
You used the 3904 transistor for making NOR gates in the transistors lab. This is a odd role for the 3904 these days. It, along with its more powerful colleague 2222, are usually used to switching small current hungry devices off and on.
Do you remember turning a LED on with a 3904 in the
Transistors lab?
Do the following diagrams revive your memory?
To get this LED controller to run a speaker,
you need to replace the LED and collector resistor
Rcol
with the speaker.
However use a 6 volt
battery pack to drive the speaker. Otherwise, it will be even more
piano. We are going for forte.
So collect one end of the speaker to the
battery pack and the other end to the collector of the transistor.
Be sure to use a base resistor Rbas
to connect the
PIC output pin to the base of the transistor.
Your 330 Ω resistor will work fine.
If you need more information, take a look at
Using a Speaker for Audio Output, a tutorial written for
the mbed microcontroller development board.
This should make your speaker much louder. By the way, you can also use a 3904 or 2222 in similar configurations to control the speed of motors.
The LM386
The LM386 is a low voltage audio power amplifier that is used in many audio devices, such as the Smokey® Amp. In fact, the Smokey Amp is little more than an LM386 with a couple of capacitors.
Take a look at the schematic in the upper left corner of page 5 of the LM386 datasheet which only appears on the Audio Power Amplifiers with LM386 page of Hobby-Hour.com.
Try to build this on your breadboard. It’s only one potentiometer, one resistor, and a couple of capacitors. Be sure to power the LM386 from your battery holder.
This setup won’t be any louder than the 3904 amplifier,
but it can amplify very weak signals through a high resistance channel,
such as the 4M Ω human body.
Here’s an example of a four-digit device
opening and closing the circuit.
I suspect this would work even if the entire class held hands to complete
the circuit, but this is a Computer Science class.
The PIC only produces 3.3 v. Do not try this at home
with the speaker wires coming out of your
200 watt stereo amplifier.
What to do
Make it loud.