| 
  • If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • Get control of your email attachments. Connect all your Gmail accounts and in less than 2 minutes, Dokkio will automatically organize your file attachments. You can also connect Dokkio to Drive, Dropbox, and Slack. Sign up for free.

View
 

Final Project - Spencer Shulem

Page history last edited by spshulem@... 8 years, 11 months ago

VERPLANK DIAGRAM:

STATE DIAGRAM:

My design was to make a simple but small and efficient FM transmitter that could work on any station and with any MP3 player. Later on did I find that all of the LCD's that i tried failed to even run hello world so I had to go to a more bare bones... This still took a while because the example code and such was a pain. Plus the datasheet was also a pain. But here it is.

This is the curcuit outside of it's body

This is it inside of the body with a radio next to it so it looks cooler

Here is the code I used for it to work with just one station set in the program.

 

int CK = 20;  //clock pin
int DA = 19;  //data pin
int LA = 21;  //latch pin



void setup(){
  Serial.begin(9600);  //begin Serial connection for debugging

  pinMode(CK, OUTPUT);
  pinMode(DA, OUTPUT);
  pinMode(LA, OUTPUT);
  digitalWrite(LA, LOW); //unlatch transmitter
  delay(100);        //Wait for VDD to settle


  spi_send(0x0E, B00000101); //Software reset

  spi_send(0x01, B10110100); //Register 1: forced subcarrier, pilot tone on

  spi_send(0x02, B00000011); //Register 2: Unlock detect off, 2mW Tx Power

  spi_send(0x03, B10001010); //Register 3: Set broadcast freq to 97.3, lower byte
  spi_send(0x04, B00101110); //Register 4: Set broadcast freq to 97.3, upper byte

  spi_send(0x08, B00011010); //Register 8: set Osc on band 2

  spi_send(0x00, B10100001); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power on

  spi_send(0x0E, B00000101); //Software reset

  spi_send(0x06, B00011110); //Register 6: charge pumps at 320uA and 80 uA

  Serial.print("Transmitting");  //for debugging


}


void loop(){

}




void spi_send(byte reg, byte data)  //routine to send Register Address and Data as LSB-first SPI
{
    int x;
    int n;
    digitalWrite(LA, LOW);

    for(x = 0 ; x < 4 ; x++)         //send four-bit register address
    {
      digitalWrite(CK, LOW);       //Toggle the SPI clock
      n = (reg >> x) & 1;        //n is the xth bit of the register byte
      if (n == 1){
        digitalWrite(DA, HIGH);    //Put high bit on SPI data bus
      }
      else {
        digitalWrite(DA, LOW);     //Put low bit on SPI data bus
      }
      Serial.print(n);           //send bit to serial connection for debugging
      digitalWrite(CK, HIGH);      //Toggle the SPI clock
    }

    for(x = 0 ; x < 8 ; x++)         //send eight-bit register data
    {
      digitalWrite(CK, LOW);       //Toggle the SPI clock
      n = (data >> x) & 1;
      if (n == 1){
        digitalWrite(DA, HIGH);    //Put high bit on SPI data bus
      }
      else {
        digitalWrite(DA, LOW);    //Put low bit on SPI data bus
      }
      Serial.print(n);          //send bit to serial connection for debugging
      digitalWrite(CK, HIGH);     //Toggle the SPI clock
    }
    delayMicroseconds(1);         //might not be needed, supposedly unstable command anyway

    digitalWrite(LA, HIGH);         //Latch this transfer
    delayMicroseconds(4);
    digitalWrite(LA, LOW);

    digitalWrite(CK, LOW);        //This is to keep CK pin at 0V at end of data transfer
    Serial.print("\n");           // send new-line to serial for debugging

}
 

and here is the code of it if the channel and down were plugged in and the LCD worked:

 

#include <Wire.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>

#define encoderPinA 18                  // up button on pin 2
#define encoderPinB 17                // down button on pin 3

volatile unsigned int encoderPos = 0;
unsigned int encoderLast = 0;


#define setButton 16                 // set button on pin 4

#define topFM  107900000            // Top of the FM Dial Range in USA
#define botFM   87500000            // Bottom of the FM Dial Range in USA
#define incrFM    200000            // FM Channel Increment in USA
// define incrFM   100000           // FM Channel Increment - certain countries.
// define incrFM    50000           // FM Channel Increment - certain countries...


long frequency = 97300000;          // the default initial frequency in Hz
long newFrequency = 0;
boolean gOnAir = false;             // Initially, NOT On The Air...

// Define the LCD
#define rs 12
#define rw 11                        // Indicate RW tied to 0.0 volts / WRITE ONLY
#define enable  5
#define d0  4
#define d1  3
#define d2  2
//#define d3  10

LiquidCrystal lcd( rs, rw, enable, d0, d1, d2);


 
void setup() {
 
  // Setup the Up/Down and SET buttons
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  pinMode(setButton, INPUT);
 
  //Serial.begin(9600);                 //for debugging
 
  // Attempt to read the last saved frequency from EEPROM
  newFrequency = loadFrequency();

  // Test if outside our FM Range...
  if ( newFrequency < botFM || newFrequency > topFM ) {
    // Sorry - haven't saved before - use the default.
    frequency = 97300000;
  }
  else {
    // We have a valid frequency!
    frequency = newFrequency;
  }  
 
  // Startup the LCD...

  lcd.clear();
 
  lcd.setCursor( 0, 0 );
  lcd.print( "FM Stere");
  lcd.setCursor( 0, 1 );
  lcd.write( 'o' );
 
  delay(2000);

  lcd.setCursor(0, 0);
  lcd.print( "Broadcas");
  lcd.setCursor(0, 1);
  lcd.print( "ter...");
  delay(2000);
 
  displayFrequency( frequency);

  Wire.begin();                       // join i2c bus as master

  transmitter_setup( frequency );
 
  //  Enable the Interrupts now...
  attachInterrupt( 0, doEncoderA, CHANGE );
  attachInterrupt( 1, doEncoderB, CHANGE );  
}



void loop() {

  /* **** OLDER, Button Code - works - removed in lieu of rotary encoder ****
  if (digitalRead(upButton) == LOW) {
    frequency += incrFM;                              // 200kHz steps for North American channel spacing
    frequency = constrain( frequency, botFM, topFM);  // Keeps us in range...

    //Serial.println( frequency, DEC );
    transmitter_standby( frequency );
    delay( 250 );
  }

  if (digitalRead(downButton) == LOW) {
    frequency -= incrFM;                              // 200kHz steps for North American channel spacing
    frequency = constrain( frequency, botFM, topFM);  // Keeps us in range...
    
    //Serial.println( frequency, DEC );
    transmitter_standby( frequency );
    delay( 250 );
  }
  */
 
  /* *** START of NEW Rotary Encoder Code *** */
  if ( encoderPos != encoderLast ) {
    
    if ( encoderPos > encoderLast )
        frequency += incrFM;
    else
        frequency -= incrFM;    
    frequency = constrain( frequency, botFM, topFM);  // Keeps us in range...
    transmitter_standby( frequency );
 
    //Serial.println (encoderPos, DEC);          
    // use for debugging - remember to comment out
    encoderLast = encoderPos;
  }
  /* *** END of NEW Rotary Encoder Code *** */
 
 
  if (digitalRead(setButton) == LOW) {
    // Create a 'toggle' - pressing set while OnAir - set's StandBy and
    // if we're already StandBy, set the Frequency and go OnAir...
    if ( gOnAir ) {
      transmitter_standby( frequency );
    }
    else {
      set_freq( frequency );
      saveFrequency( frequency );     // Save the Frequency to EEPROM Memory
    }
    delay( 500 );
  }
  // Lather, Rinse, Repeat....
}



void transmitter_setup( long initFrequency )
{
  i2c_send(0x0E, B00000101); //Software reset

  i2c_send(0x01, B10110100); //Register 1: forced subcarrier, pilot tone on
    
  i2c_send(0x02, B00000011); //Register 2: Unlock detect off, 2mW Tx Power

  set_freq( initFrequency);

  i2c_send(0x00, B10100001); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power on
 
  i2c_send(0x0E, B00000101); //Software reset
 
  i2c_send(0x06, B00011110); //Register 6: charge pumps at 320uA and 80 uA
}

void transmitter_standby( long aFrequency )
{
  i2c_send(0x00, B10100000); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power OFF
 
  displayFrequency( aFrequency );
 
  delay(100);
  gOnAir = false;
}

void set_freq( long aFrequency )
{
  int new_frequency;

  // New Range Checking... Implement the (experimentally determined) VFO bands:
  if (aFrequency < 88500000) {                       // Band 3
    i2c_send(0x08, B00011011);
    //Serial.println("Band 3");
  }  
  else if (aFrequency < 97900000) {                 // Band 2
    i2c_send(0x08, B00011010);
    //Serial.println("Band 2");
  }
  else if (aFrequency < 103000000) {                  // Band 1
    i2c_send(0x08, B00011001);
    //Serial.println("Band 1");
  }
  else {
    // Must be OVER 103.000.000,                    // Band 0
    i2c_send(0x08, B00011000);
    //Serial.println("Band 0");
  }


  new_frequency = (aFrequency + 304000) / 8192;
  byte reg3 = new_frequency & 255;                  //extract low byte of frequency register
  byte reg4 = new_frequency >> 8;                   //extract high byte of frequency register
  i2c_send(0x03, reg3);                             //send low byte
  i2c_send(0x04, reg4);                             //send high byte
 
  // Retain old 'band set' code for reference....  
  // if (new_frequency <= 93100000) { i2c_send(0x08, B00011011); }
  // if (new_frequency <= 96900000) { i2c_send(0x08, ); }
  // if (new_frequency <= 99100000) { i2c_send(0x08, B00011001); }
  // if (new_frequency >  99100000) { i2c_send(0x08, B00011000); }
 
  i2c_send(0x0E, B00000101);                        //software reset  

  //Serial.print("Frequency changed to ");
  //Serial.println(aFrequency, DEC);

  i2c_send(0x00, B10100001); //Register 0: 200mV audio input, 75us pre-emphasis on, crystal off, power ON
 
  lcd.setCursor(0, 1);
  lcd.print( " On Air ");
  gOnAir = true;
}

void i2c_send(byte reg, byte data)
{
    Wire.beginTransmission(B1100111);               // transmit to device 1100111
    Wire.send(reg);                                 // sends register address
    Wire.send(data);                                // sends register data
    Wire.endTransmission();                         // stop transmitting
    delay(5);                                       // allow register to set
}

void saveFrequency ( long aFrequency )
{
  long memFrequency = 0;                   // For use in Read / Write to EEProm

  //Serial.print( "Saving: " );
  //Serial.println(aFrequency, DEC);

  memFrequency = aFrequency / 10000;
  EEPROM.write( 0, memFrequency / 256);   // right-most byte
  EEPROM.write( 1, memFrequency - (memFrequency / 256) * 256 );   // next to right-most byte
}

long loadFrequency ()
{
  long memFrequency = 0;                   // For use in Read / Write to EEProm

  memFrequency = EEPROM.read(0) * 256 + EEPROM.read(1);
  memFrequency *= 10000;

  //Serial.print("Retrieving: " );
  //Serial.println(memFrequency, DEC);
  return  memFrequency;
}


void displayFrequency( long aFrequency)
{
  long memFrequency = 0;
  int  aDigit = 0;
 
  // LCD Display template: "108.1Mhz" --> 8 characters  
  memFrequency = aFrequency / 100000;       // Gets us down to 9999 digits (where 1011 ---> 101.1
 
  // Write to the LCD
  lcd.clear();
 
  if (memFrequency < 1000) {
    lcd.write( ' ' );          // pad left for 99.9
  }
  lcd.print( memFrequency / 10 );
  lcd.write( '.' );
  lcd.print( memFrequency % 10 );
  lcd.print( "mhz" );
 
  lcd.setCursor(0, 1);
  lcd.print( " Standby");
}

/* *** Interrupt Routines *** */
void doEncoderA() {
  if (digitalRead(encoderPinA) == HIGH) {     // look for a low-to-high on channel A
    // check channel B to see which way encoder is turning
    if ( digitalRead(encoderPinB) == LOW )
      encoderPos = encoderPos + 1;         // CW
    else
      encoderPos = encoderPos - 1;         // CCW
  }
  else {                                       // look for a high-to-low on channel A
    // check channel B to see which way encoder is turning  
    if ( digitalRead(encoderPinB) == HIGH )   
      encoderPos = encoderPos + 1;          // CW
    else
      encoderPos = encoderPos - 1;          // CCW
  }
}

void doEncoderB(){
  if (digitalRead(encoderPinB) == HIGH) {      // look for a low-to-high on channel B
    // check channel A to see which way encoder is turning
    if ( digitalRead(encoderPinA) == HIGH )
      encoderPos = encoderPos + 1;         // CW
    else
      encoderPos = encoderPos - 1;         // CCW
  }
  else {                                       // Look for a high-to-low on channel B
    // check channel B to see which way encoder is turning  
    if ( digitalRead(encoderPinA) == LOW )
      encoderPos = encoderPos + 1;          // CW
    else
      encoderPos = encoderPos - 1;          // CCW
  }
}

... My wires are very fragile and I broke my antena so a video was not possible, but ask anyone that saw it at the presentations IT WORKS! If you would like to see it working again I will try and fix it but... I don't have time to fix some of the wires that snapped...

 

 

FM Radio Sparkfun part: http://www.sparkfun.com/products/8482

If I could rebuild it I would spend more time on the case, and making it smaller, probably using cell batteries, that way it would less than 2 inches wide and less than a half of an inch thick. The user can buy any of these parts today, I mean they have them in some cars but they plug into the car jack rather than be independent so mine is a lot more portable and usable to other places.

Building an MP3 player into the device would be somewhat counter productive, because yes you could have preloaded songs on it and just hit play, but it adds more bulk to the device as well as it's not what it would be mainly used for. It would probably be mainly used for in the car listening to music, at a friends house listening to music. Having just another MP3 player would be pointless because the market towards this product are ones with MP3 players already. As well as adding the MP3 player to this device would make the price of the device in total a lot more, and do a lot less things and not have as wide as a market in general.

If I had a working LCD I wouldn't add anything to my final project. The frequency up and down and select the frequencies works fine just the LCD doesn't like to show anything.. not even hello world. So I went like I said to a more stripped version.

My fabrication process wasn't really much, I didn't take pictures of it because all I did was solder 2 circuits and a battery to a board... and then make a box in AI. The circuit wasn't the hard part of this project. It was programming that was the really problem getting this to work properly.

I put close to the same exact time and effort into this as Scott did, and started at the same time, and finished pretty much at the same time. I got something working, well, and consistently every time. It is small, does what I set out for it to do, and is a pretty practical device that would somewhat be an actually selling product. The circuit wasn't hard, as it wasn't for most projects really, it's the programming that seemed to be the biggest problems. Especially in my project. Maybe I looked over something and there were answers sitting right in front of me but I did the work anyways and I did it the hard way then but I got it done.

For the amount of time I put into this I would really hope for the best. But it seems that because I didn't add lab 6 to it I won't get the grade I looked out to get. I misunderstood the assignment I suppose.

Broadcasting  is it is on the air, transmitting could be during the changing channel part and receiving data from the MP3 player.

Comments (2)

Akil Srinivasan said

at 10:04 pm on Aug 15, 2011

We like that you have a solid point-of-view and made a sketch that conveys it. We also like that you included a state diagram.

We wish that your state diagram had sharper distinctions among states (for example, is broadcasting different than transmitting?) and was reflected in your code. We also wish that you had included more background into your design process (sketches, paper prototypes, Verplank diagrams) as well as your fabrication process. For example, you didn't include the manufacturer and part number of the FM transmitter you selected, or a Sparkfun link to it, or pinout or wiring diagrams.

If you could re-build the project again, what would you add or remove in terms of design or functionality? Try not to think in terms of smaller size (which, granted, would be nice), but rather in terms of additional functionality. What does your user need that you or he or she can't buy today? For instance, incorporating a barebones mp3 player directly into the device, or some form of tactile interface so that when you're driving, you don't have to look down to change the channel.

spshulem@... said

at 10:27 pm on Aug 16, 2011

I hope my added part to this lab help clear any questions up.

You don't have permission to comment on this page.