EENG 383

In Lab 4 - Music Box

Requirements

Working in teams of two, read through the following lab activity and perform all the actions prescribed. You do not need to document bullet items. Make a record of your response to numbered items and turn them in a single copy as your teams solution on Canvas using the instructions posted there.

Include the names of both team members at the top of your solutions. Use complete English sentences when answering questions. If the answer to a question is a table or other piece of art (like an oscilloscope trace or a figure), then include a sentence explaining the piece of art. Only include your answers, do not include the question-text unless it is absolutely needed.

Objective

The objective of this lab is to introduce you to the MPLAB Code Configurator (MCC) and how to use timers to generate delays.

External Hardware

Today you will write code that you can interact with through the terminal (input) and generate musical tones through the speaker (output) using timers. Let's start by looking at the speaker and the hardware that will drive it. Start by searching the Internet for the technical documents for the 1" Mallory PSR-28N08A-JQ speaker. Go to http://www.mallory-sonalert.com and search for the product number of the speaker. We will need to use the frequency response graph for the following questions.
  1. Using the Frequency Response graph in the technical documents, at what frequency does the speaker produce the loudest sound and what is the magnitude of this sound (include units)?
  2. A person with good hearing should be able to detect sounds with a volume level of 70db. What is the lowest frequency, for our speaker, that has this volume level?
The I/O pins on the PIC microcontroller are not capable of supplying enough current to adequately drive the speaker. Fortunately you soldered a National Semiconductors LM4862 Boomer Audio Power Amplifier to the development board. You will need to run a jumper wire from the pin generating the audio signal to the low pass filter (LPF) input and then from the LPF output to the LM4862 input. The LPF is needed to "soften" out the hard audio edges created by the PIC, consequently reducing the voltage drop induced by the power drain of the audio amplifier. This will help prevent the development board from resetting due to a brown-out.
  1. Pull the technical documents for the LM4862 (audio amp) by looking up its part number from the bill of materials linked in inlab01. On page 1 of the technical documents, Figure 1 shows a typical audio amplifier application. Which pin number and pin name is connected to the audio input through a series RC circuit?
  2. Look over the schematic and layout of the development board and identify the silk screened identifier next to the header pin that is connected to the audio input pin of the LM4862. Hint: Follow the connection from the LM4862 pin past the series RC circuit. See the image below if you still need help.

Today's program will utilize a terminal interface to the program. A terminal interface provides a much more flexible method to interact with the PIC. We will investigate the hardware that makes this possible in next week's lab. However there are some basics that you must understand to use the terminal.

When you plug your development board into a PC you may have noticed that your PC makes a chime sound. This chime tells you that the PC has recognized your development board and assigned it a COM port. A PC may have any number of COM ports, so each is given a unique numerical value. There are a variety of different ways to determine the COM port assigned to your FT230 chip, but I find the easiest to run Brandon's COM port finder. There is also a link to this application on the class main page. When your board is connected to a PC through a USB cable, running COMSniffer should produce output similar to the following; in this case my development board was assigned COM3. Note that more than likely your PC will assign your development board a different port number. You will need the port number anytime you need to connect your PIC to a terminal application like PuTTY.


Internal Subsystem

We will hold off exploring the The Enhanced Universal Synchronous Asynchronous Receiver Transmitter (EUSART) subsystem of the PIC until next week's lab. Instead we will focus on the big idea in today's lab, interrupts.

The internal configuration bits for the PIC are contained in the special function registers (SFR). You can manipulate the values of the bits in these registers in your C programs by concatenating the register NAME with the FIELD name in the format NAMEbits.FIELD = value;, the expression "bits" needs to be appended to the register NAME and the "." separates the NAME and FIELD. Let's look at a practical example; open the Microchips PIC18(L)F2X/4XK22 Data Sheet to page 166. Let's say that you wanted to enable timer 1 by setting the TMR1ON field of the T1CON register. In your C code you would add the line T1CONbits.TMR1ON = 1;
  1. To enable Timer 1 to run off Fosc/4 mode using a 1:1 prescaler, how do we need to configure the following T1CON bits?
    • T1CONbits.TMR1ON =
    • T1CONbits.TMR1CS =
    • T1CONbits.T1CKPS =

Firmware Organization

A microcontroller by itself is not terribly interesting, being not much more than a slow-running processor. Microcontrollers derive their power from the subsystems they contain and the external devices these subsystems can control. In order to be able to interface to as many different external devices as possible, the computer engineers who design microcontollers make the subsystems as flexible as possible. This means that there are potentially a great number of alternative configurations for each subsystem. When developing an embedded application that interfaces microcontroller subsystem to an external device, one of the first tasks you must complete is configuring the subsystem.

In my experience, subsystem configuration is a necessary task that can turn into a time consuming and potentially-frustrating challenge Thankfully, the folks at Microchips developed the MPLAB Code Configurator (MCC) to address this challenge. Paraphrasing the text in the MPLAB® Code Configurator User’s Guide, MCC is a plug-in tool in MPLAB X which generates drivers (software) for controlling and driving subsystems based on the settings and selections made in the Graphical User Interface. The generated drivers can be used in your program.

Create new project

The first step to using MCC is to create a new project and launch MCC as described in the steps below.

Configure the PIC with MCC

You should use MCC for the remainder of the term for all future projects. Every project in this class will require you to configure the oscillator and I/O pins. In addition to these two subsystems, you will also configure the timer subsystem in the steps below. Before downloading and running the inlab04.c program, take a moment to look at the structure of the files created by MCC and the utility functions and definitions contained in them. In the project manager window expand Header and Source Files → MCC Generated Files. Double click and open all the files in these folder. Use the contents of the files to answer the following questions.
  1. What two EUSART1.c functions are being used just inside the infinte loop in main?

  2. When the TMR1 interrupt occurs, the INTERRUPT_InterruptManager function (you can find it in the MCC Generated File interrupt_manager.c) is invoked by the PIC. Find the INTERRUPT_InterruptManager function and name the function that it calls. Call this the daughter function of INTERRUPT_InterruptManager. To find the granddaughter function of the INTERRUPT_InterruptManager function look at the code associated with the daughter function of the INTERRUPT_InterruptManager function. When you are looking for a MCC function, the first word of the function is the name of its MCC Generated File. This code in the daughter function looks like it calls TMR1_InterruptHandler(), but this "function" is really a function pointer - note that the TMR1_InterruptHandler variable is true. A function pointer is variable that can be assigned to a function. In our case the TMR1_SetInterruptHandler function performs this assignment by taking in as an argument, the function you would like to serve as the interrupt service routine for TMR1. The TMR1_SetInterruptHandler function is called in two separate placed.
  3. Write out the two calls to TMR1_SetInterruptHandler and in what functions each call occured.
  4. The second call to TMR1_SetInterruptHandler will be the one used by your program. What ISR will your program use to service the TMR1 interrupt?
  5. When answering the following questions, focus on the interrupt handler not calls to the WriteTimer function.
    • What is the daughter function of INTERRUPT_InterruptManager?
    • What is the granddaughter function of INTERRUPT_InterruptManager?
  6. What does the daughter function do to TMR1IF and to the value of TMR1?
  7. Where is the variable timer1ReloadVal given a value? What is this value? Note the initial value is set by MCC in the Timer Period field in the TMR1 Easy Setup window.
Look at the myTMR1ISRr function (defined in main.c) to answer the following questions.
  1. What is the first and last thing that happen in this function? Ignore variable declarations.
  2. A flag is a variable (usually typed uint8_t) that equals either true or false. What three flags are checked in this function? Briefly explain what each flag does.
  3. What behavior would you expect if you removed the incNote=false; statement from the function. Hint, consider what would happen if playNote was true (a note is being played) and the user typed "i" in at the terminal. Heck, give it a try (by commenting out the statement) if you want to check your answer.

Firmware Operation

Your next step will be to download, run and observe the behavior of the inlab04.c program. MCC does not require you to change the process you used in lab02; so the following steps should be familiar.

Observe output

One of the primary reasons electrical engineers use oscilloscopes is to observe phenomena that are too fast and too small to be observed directly with something like a digital multimeter. In the following steps, you will use the oscilloscope to observe the signal generated on TEST_PIN while your program is inside the ISR. Make sure that a note is not playing when you capture this waveform. To do this setup your oscilloscope as follows:

Ch1 probe RA6
Ch1 ground clip Dev board ground loop
Horizontal (scale) 500ns
Ch1 (scale) 1V
Trigger mode Auto
Trigger source 1
Trigger slope
Trigger level 1.5V
Make sure to:
  1. Include the screenshot from the waveform save on your oscilloscope. Use a thumb drive to capture the image and paste it into your lab report - credit will not be given to cell phone pictures. Please do not submit them.
  2. Using the time per division information on the oscilloscope screen and the time high duration the waveform (described in divisions), show your calculation for the time high duration of the waveform on RA6 using an approach like:
    Oscilloscope set to 10us/division
    The duration of the RA6 pulse is 0.6 divisions
    
    	 10us       
    	-------- *  0.6 divisions = 6us
    	division 
    
  3. Using the horizontal scale adjust and horizontal position adjust, zoom in on the positive edge (from 0V to 3.3V) of the signal being generated by the TEST_PIN to determine the rise time of the signal. The rise time is defined as the time to transition from 10% of the final value (0.33V) to 90% of the final value (2.97V). You may find increasing the vertical scale also helps. Be careful if you decide to use the oscilloscope’s built in rise time measurement instead of the cursors. You’ll want to make sure that you’re set up to do a 10% to 90% rise time. You need to include an oscilloscope screen shot that shows information necessary to backup your answer.
  4. Using the rise time from the previous problem, determine the slew rate of the PIC output. The slew rate of a signal transition is the change in voltage divided by the time that change takes. Slew rate has units of Volts per microsecond. In our case, this is the change in voltage divided by the rise time. Make sure you state your answer in volts per microsecond.

Firmware Experiments

Keep the oscilloscope connected to TEST_PIN while answering the following questions. You may need to make adjustments to the horizontal scale and position in order to see and measure the values asked for.
  1. Compare the time spent inside the ISR when the PIC is and is not playing a note.
  2. The TMR1_WriteTimer function is largely responsible for this increased execution time. How long does it takes the PIC to execute this line of code?
  3. When the playNote flag is true, the if/then associated with this flag is executed and TMR1 gets initialized to the half period of the note being played. However, then the playNote flag is false, TMR1 is not initialized by the ISR. Use the MCC generated code and the oscilloscope to determine the initial count value of TMR1 when playNote == false? Hint: Zoom out and look at the time between positive pulses on the oscilloscope. Does that number look familiar?
One principle of embedded programming is to minimize the time spent inside an ISR. The purpose of this principle is to ensure that the ISR completes its execution before it is invoked again. Printing characters to the terminal is always a time consuming operation; at 9600 Baud (Baud has units of bits per second) it takes 833µs to print a single character.
  1. Using the Baud rate information above, how long should it take to print the string inside the ISR associated with the doSomethingBad flag? Make sure to include the carriage return and line feed (each as a single character) in your calculation. Show your work.
  2. Use the "t" function to measure the actual time it takes to print this string.
  3. Press the "b" key to set the doSomethingBad flag true. What happened? Note: You will have to short out the reset pads of your development board to break out of this bad behavior. The reset pads are labeled "RST" and are near the programming pins of your board (AKA: near the white triangle).
  4. Explain what happened. Your answer should comment on the relative time required to print the message and the interrupt frequency.
When writing embedded program, one of my most common errors is to forget to include:
    INTERRUPT_GlobalInterruptEnable();      // ISR not working? - you probably 
    INTERRUPT_PeripheralInterruptEnable();  // forgot to add these 2 lines
You can find these two lines at the top of main.
  1. Commenting out these two lines, compile and download the resulting code. What functionality works and what functionality of your program no longer works?