The project is an investigation of sorts, insofar as it shows that alternative I/O systems can be made. The structure of the system is easily adapted to more sophisticated (i.e. more expensive with a longer time-to-market) I/O peripherals, chosen based on the constraints of the application. While the serial keypad input isn’t relatively complicated, TV output is, so we expect the Mega32 to be adequate for all but the most demanding of applications.
High Level Design
The user uses a keypad to input commands, which the MCU decodes and sends to a computer running Winamp via an RS-232 serial communication line. A custom module for Winamp listens on COM1 for commands and executes the appropriate function. The module also sends information such as track title and various initial sound preferences to the MCU, which prints the information to a TV. We used code from a 2001 home audio control system project as a starting point for the Winamp module.
We need two interrupts: One for TIMER1 compare/match to time the TV screen refreshes and one for receive information from Winamp through the USART RS-232 connection. The main part of the program will decode keypad commands, send commands to the Winamp module, and update the TV screen as necessary. We adapted keypad polling code from our security system lab as well as the TIMER1 interrupt function and video functions/character bitmaps from our television oscilloscope lab. Our MCU code is depicted in figure 2.
The code has three main tasks:
Task 1. Translate keypad presses into characters sent to a WinAmp DLL module.
- Poll the keypad and determine which button has been pressed
- Retrieve the corresponding command character in the current mode
- Send the command and set appropriate flags to indicate necessary changes.
Task 2. Accept information from the WinAmp module through the serial port.
- Store each character received from the UART register in a string.
- End transmission on detection of a return carriage.
- Record received information in variables, print necessary information to screen.
- Update relevant variables.
Task 3. Display a GUI on a television
- Determine what needs to be added or changed on the screen. This includes scrolling the track information and drawing the track time.
- Partition the screen information as necessary into small enough parts that can be displayed without interfering with the timer1 function.
- Wait for the timer1 function to blast screen bits in the screen array to screen.
- Write new graphics to the screen array.
On power-up, our program requests the playlist length from WinAmp and the first five tracks. Information about these tracks are displayed on a formatted screen with a separate area to display the volume, the track time, PLAY/PAUSE/STOP, and SHUFFLE/REPEAT/MUTE. The first track is selected and is seen scrolling from right to left in the top position.
A keypad function polls the keypad to detect user input. Each button is mapped to a specific command in each mode. A command consists of a character which is sent back through the UART register to WinAmp. Certain commands (i.e. forward/backward and page up/down) request a movement up or down in the playlist. Changes in the playlist ellicit transmissions from WinAmp with new track information. Our UART interrupt function handles this serial communication and stores the incoming data into string arrays.
We start in regular mode. In equilizer mode, preamp and frequency bands can be incremented (no shift) or decremented (in shift mode). The shift mode we implemented to allow for both up and down movement of all equilizer levels since we only have 16 keypad buttons.
Helper subfunctions were created to modularize the code. They are:
- initialize() -> initiailize clocks, PORTS, variables, and enable interrupts
- checkbutton() -> command execution on the microcontroller side. different things happen depending on the command entered
- EQ_setup() -> initialize the equilizer mode screen
- move_band() -> updates volume/preamp/frequency bands if their corresponding increment/decrement buttons are held down
- pageswitch() -> start a batch request for five new tracks beginning with a particular track specified in the “request” variable
- refresh() -> initialize the regular mode screen
- runtimers() -> update software timers i.e. scrolling timer, keypad timer, track timer (seconds, minutes)
- scroll() -> scroll the track information and display to screen
- video_clear() -> zero out the screen array to clear the screen
All commands are communicated by sending characters to the WinAmp module. Each command has a unique character associated with it. All strings and information passed back from WinAmp are terminated with a return carriage. We use this fact to determine when a transmission has ended.
One complex feature of our MCU program involve the ability to scroll up and down through the playlist. This is accomplished by sending a batch request for a particular track number. The WinAmp module then sends back info for five consecutive tracks starting with the one four tracks ahead of that which was requested and ending with the requested track. This reverse ordering means that we do not need to store the track info for each track as the last string sent is the one for the selected track. By changing which track we request, we can either scroll the playlist back/ahead by one or page up, page down. Since we display only five tracks at a time, a “page” consists of five tracks.
We used Land’s video code as a template for our MCU software. To maximize video write speed and to fit more text on screen, we strictly used the small bitmap characters. We attempted writing our own lower case characters, but they looked like crap, so we mapped all lower case characters back to upper case. Furthermore, a complete screen cannot be written in one pass; both the equalizer and playlist screens require three full cycles.
We found we could clear the screen easily by zeroing out the screen[ ] array.
We optimized the line drawing function by creating two separate functions, one for horizontal lines, one for vertical lines. Each function only has to alter one Cartesian coordinate.
With more time, we would have our program detect when a song finishes playing so that it would update the playlist on the tv. Also, we would remove the video display functions from the UART interrupt so that they would occur only when the timer1 interrupt was done drawing the screen. Hopefully, this change would eliminate the blip associated with changing tracks on the playlist.
Our DLL code performs a large array of functions, and the DLL actually allows for greater functionality than the MCU takes advantage of. This makes our DLL flexible and will expedite any improvements we want to make to the MCU code.
Our code contains logic to check the readiness of COM1. For example, if there is no COM1 available, winamp will alert the user with a message box and take no further action.
The main routine is checkPorts() which is invoked every 20ms through a windows API mechanism. checkPorts() watches COM1 to see if the MCU needs anything. It also checks to see if the song has changed. Whenever the song changes, it grabs the title and track length then relays this information to the MCU. checkPorts() recognizes an extraordinary number of commands that can be sent from the MCU. These include:
- Retrieve information for a batch of songs on the playlist
- Turn on/off the equalizer
- Apply a boost or reduction to the preamp, or any of 10 frequency bands
- Play, pause, stop, back, skip track, rewind, fast forward
- Adjust volume up/down
- Adjust panning left/right
- Toggle shuffle, repeat
- Retrieve playlist length
These commands are each represented by a unique character, which the DLL receives over the UART.
- Atmel MEGA32 $8.00
- Whiteboard $5.00
- 3 Resistors $0.30
For more detail: TV/Keypad Interface for Winamp