//-----------------------------------------------------------------------------
// F320_FM_RadioMain.c
//-----------------------------------------------------------------------------
// Copyright 2006 Silicon Laboratories, Inc.
// http://www.silabs.com
//
// Program Description:
//
// This is the main project module.  It initializes all system hardware, and 
// then enables interrupts.  All further processing is done in the various
// interrupt handlers.
//
// Linker options are used to absolutely locate code to specific locations.
//
// The linker option can be viewed from the Tool Chain Integration window
// Project -> Tool Chain Integration -> Linker (tab)
//
// FID:            32X000042
// Target:         C8051F320
// Tool chain:     KEIL C51 7.0.0.1
//                 Silicon Laboratories IDE version 2.3
// Command Line:   See Readme.txt
// Project Name:   F320_FM_Radio
//
// Release 1.1
//    -Changed Oscillator_Init to start clock multiplier correctly (DM)
//    -16 JAN 2006
//
// Release 1.0
//    -Initial Revision (DM)
//    -05 AUG 2005
//

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------

#include "c8051f320.h"                 // SFR declarations
#include "F320_FM_RadioMain.h"         // Main project header
#include "F320_Si470x_Interface.h"     // Si470x interface header
#include "F320_USB_Register.h"         // USB core register header
#include "F320_LED_Control.h"          // LED control header

//-----------------------------------------------------------------------------
// main
//
// Return Value : None
// Parameters   : None
//
// Main program routine.
//
//-----------------------------------------------------------------------------
void main (void)
{
   PCA0MD &= ~0x40;                    // Disable Watchdog timer
   VDM0CN |= 0x80;                     // Enable the VDD Monitor

   // Configure comparator0 and crystal drive
   Crystal_Init ();

   // Initialize SPI0, should be before crossbar enable
   SPI0_Init ();                               

   Timer0_2_Init ();                   // Initialize timers
   PCA0_Init ();                       // Initialize PCA module
   Port_Init ();                       // Initialize crossbar and GPIO
   LED_StateMachine ();                // Set initial LED state

   Oscillator_Init ();                 // Initialize oscillator

   ADC0_Init ();                       // Initialize ADC

   // Select VDD Monitor as a Reset Source
   RSTSRC = 0x02;                            

   EnableRadio ();                     // Bring SI 470x out of reset

   USB0_Init ();                       // Initialize USB0

   Next_LED_Mode = BLINK_ORANGE_LED;   // Set next LED state

   EA = 1;                             // Global Interrupt enable
   
   while (1);                          // Spin forever
}

//-----------------------------------------------------------------------------
// Initialization Subroutines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Oscillator_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function initializes the clock multiplier and selects it as the system
// clock.
//
//-----------------------------------------------------------------------------
void Oscillator_Init (void)
{
   // Configure internal oscillator for its maximum frequency and enable
   // missing clock detector
   OSCICN |= 0x03;

   // Select internal oscillator as input to clock multiplier
   CLKMUL = 0x00;                        

   CLKMUL |= 0x80;                     // Enable clock multiplier
   Delay_Main ();                      // Delay for clock multiplier
   CLKMUL |= 0xC0;                     // Initialize the clock multiplier

   while (!(CLKMUL & 0x20));           // Wait for multiplier to lock
   CLKSEL = SYS_4X_DIV_2;              // Select 24 MHz clock as sysclock
}

//-----------------------------------------------------------------------------
// Crystal_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function starts the crystal drive circuit and configures the comparator
// to operate as a clock buffer for Si470x.
//
//-----------------------------------------------------------------------------
void Crystal_Init (void)
{
   // Turn on crystal drive circuit for 32.768 kHz crystal
   OSCXCN = 0x61;

   CPT0CN = 0x85;                      // Turn on comparator 0, 5 mV hysteresis
   CPT0MX = 0x00;                      // Negative = P1.1, Positive = P1.0
   CPT0MD = 0x00;                      // Response time = 100 ns
}

//-----------------------------------------------------------------------------
// SPI0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function configures the SPI for Si470x.
//
//-----------------------------------------------------------------------------
void SPI0_Init (void)
{
   // Clock idle high, Data centered on 2nd edge, SPI master
   SPICFG = 0x70;
   SPI0CN = 0x83;                      // Enable SPI, 3-wire mode selected

   // Configure SPI clock rate for 2.4 MHz (Maximux 2.5 MHz for Si4701)
   SPICKR = ((SYSCLK / (2400000*2)) - 1);
}

//-----------------------------------------------------------------------------
// Port_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function configures the Crossbar and GPIO ports.
//
// P0.2  analog               XTAL1
// P0.3  analog               XTAL2
//
// P1.0  analog               COMPARATOR POSITIVE INPUT
// P1.1  analog               COMPARATOR NEGATIVE INPUT
// P1.2  digital  push-pull   Si 4701 RESET
// P1.3  digital  push-pull   SEN_BAR
// P1.4  digital  push-pull   SCLK
// P1.5  digital  open-drain  MISO
// P1.6  digital  push-pull   MOSI
// P1.7  digital  push-pull   RCLK/COMPARATOR OUTPUT
//
// P2.0  analog               RIGHT AUDIO CHANNEL
// P2.1  analog               LEFT AUDIO CHANNEL
// P2.3  digital  push-pull   GREEN LED/PCA OUTPUT
//
// P3.0  digital  push-pull   RED LED
//
//-----------------------------------------------------------------------------
void Port_Init (void)
{
   P0MDIN = 0x03;                      // Port 0 pins 2-7 analog
   P1MDIN = 0xFC;                      // Port 1 pins 0, 1 analog
   P2MDIN = 0x0C;                      // Port 2 pins 0, 1, 4-7 analog
   P3MDIN = 0x01;                      // Port 3 all digital

   P0MDOUT = 0x00;                     // Port 0 pins set open-drain
   P1MDOUT = 0xDC;                     // Port 1 pins 2-4, 6, 7 set push-pull
   P2MDOUT = 0x0C;                     // Port 2 pins 2, 3 set push-pull
   P3MDOUT = 0x01;                     // Port 3 pin 1 set push-pull

   P0SKIP = 0xFF;                      // Port 0 skip pins 0-7
   P1SKIP = 0x0F;                      // Port 1 skip pins 0-3
   P2SKIP = 0xF3;                      // Port 2 skip pins 0, 1, 4-7

   P0 = 0x03;                          // Port 0 pins 0, 1 start high
   P1 = 0xE3;                          // Port 1 pins 0, 1, 5-7 start high
   P2 = 0x03;                          // Port 2 pins 0, 1 start high
   P3 = 0x00;                          // Port 3 starts low

   XBR0 = 0x22;                        // Comparator0, SPI enabled

   // Enable Crossbar, Weak Pull-ups on, 2 PCA modules are on
   XBR1 = 0x42;
}

//-----------------------------------------------------------------------------
// USB0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Initializes USB0, enable transceiver and USB0 interrupts.
//
//-----------------------------------------------------------------------------
void USB0_Init (void)
{
   POLL_WRITE_BYTE (POWER,  0x08);     // Force Asynchronous USB Reset
   POLL_WRITE_BYTE (IN1IE,  0x01);     // Enable Endpoint 0 in interrupts
   POLL_WRITE_BYTE (OUT1IE, 0x04);     // Enable Endpoint 2 out interrupts

   // Enable SOF, Reset, and Suspend interrupt
   POLL_WRITE_BYTE (CMIE, rbSOFE | rbRSTINTE | rbSUSINTE);

   USB0XCN = 0xE0;                     // Enable transceiver; select full speed

   // Enable clock recovery, single-step mode disabled
   POLL_WRITE_BYTE (CLKREC, 0x80);

   EIE1 |= 0x02;                       // Enable USB0 Interrupts

   // Enable USB0 by clearing the USB Inhibit bit, and enable suspend detection
   POLL_WRITE_BYTE (POWER,  0x01);
}

//-----------------------------------------------------------------------------
// Timer0_2_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Initializes timer 2 used for ADC conversion start, and timer 0 used for PCA.
//
//-----------------------------------------------------------------------------
void Timer0_2_Init (void)
{
   CKCON = 0x02;                       // Prescale bits set to system clock/48
   CKCON &= ~0x04;                     // Timer0 uses prescaler defined clock
   TMOD = 0x02;                        // Timer0 set to mode 2
   TH0 = (-(SYSCLK/48/30000));         // PCA frequency = 30 kHz
   TR0 = ON;                           // Start timer 0

   TMR2CN = 0x00;                      // Stop Timer2; Clear TF2;
   CKCON |= 0x10;                      // Timer2 clocked based on system clock
   TMR2RL = (-(SYSCLK/192000));        // Initialize reload value for 192000 Hz
   TMR2 = 0xFFFF;                      // Set to reload immediately

   ET2 = 1;                            // Enable Timer2 interrupts
   PT2 = ON;                           // Set interrupt to high priority
}

//-----------------------------------------------------------------------------
// ADC0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configure ADC for single ended conversions on timer 2 overflows.
//
//-----------------------------------------------------------------------------
void ADC0_Init (void)
{
   // Disable internal voltage reference VREF, use VDD instead for ADC
   REF0CN = 0x0A;

   AMX0N = 0x1F;                       // Single ended mode (negative = gnd)

   ADC0CF = 0x3C;                      // SAR clock 3 MHz, left adjusted output

   // Converion on timer 2 overflow with low power tracking mode off
   ADC0CN = 0x82;
}

//-----------------------------------------------------------------------------
// PCA_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Initializes USB0, enable transceiver and USB0 interrupts.
//
//-----------------------------------------------------------------------------
void PCA0_Init (void)
{
   PCA0CPM0 = 0x42;                    // PCA module 0 in 8-bit PWM Mode
   PCA0CPM1 = 0x42;                    // PCA module 1 in 8-bit PWM Mode
   PCA0CPM2 = 0x00;                    // PCA module 2 currently unused
   PCA0CPM3 = 0x00;                    // PCA module 3 currently unused
   PCA0CPM4 = 0x00;                    // PCA module 4 currently unused

   // PCA module 0 starts with minimum duty cycle for all pulses
   PCA0CPL0 = 0xFF;
   PCA0CPH0 = 0xFF;

   // PCA module 1 starts with minimum duty cycle for all pulses
   PCA0CPL1 = 0xFF;
   PCA0CPH1 = 0xFF;

   // PCA timer uses timer 0 overflows and runs when CPU is idle
   PCA0MD = 0x04;
   PCA0CN = 0x40;                      // PCA timer running, interrupts cleared
}

//-----------------------------------------------------------------------------
// Delay_Main
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Used for a 500 microsecond pause during hardware configuration.
// There are two identical versions of this routine, one for the main program
// and another for the USB interrupt.  This version is for the main program.
// (assuming 24 MHz system clock)
//
//-----------------------------------------------------------------------------
void Delay_Main (void)
{
   data volatile int x;
   for (x = 0;x < 500;x)
      x++;
}

//-----------------------------------------------------------------------------
// EnableRadio
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Brings Si470x out of reset and sets initial values.
//
//-----------------------------------------------------------------------------
void EnableRadio (void)
{
   data WORD Temp;

   Delay_Main ();

   // Wait for crystal to stabilize, make sure crystal has been running 1 ms
   while ((OSCXCN & 0x80) == 0);              

   // Make sure crystal frequency is stable
   for (Temp.i = 0; Temp.i < 200; Temp.i++) Delay_Main ();

   RADIO_RESET = ON;                   // Bring radio out of reset
   SEN_bar = ON;

   CONTROL_READ (POWERCONFIG, Temp);
   Temp.i &= ~DISABLE_PC;
   Temp.i |= ENABLE_PC;
   CONTROL_WRITE (POWERCONFIG, Temp);  // Enable radio

   for (Temp.i = 0; Temp.i < 1000; Temp.i++) Delay_Main ();

   // Full volume, mute off, set initial channel
   ORL_REGISTER (SYSCONFIG2, 0x000F, Temp);
   ORL_REGISTER (POWERCONFIG, MUTE_OFF_PC, Temp);
   // 102.3 MHz
   SET_REGISTER (CHANNEL, (TUNE_CH | (74 & CHANMASK_CH)), Temp);

   CONTROL_READ (STATUSRSSI, Temp);    // Wait for initial tune to complete
   while (!(Temp.i & STC_SC)) 
      CONTROL_READ (STATUSRSSI, Temp);

   ANL_REGISTER (CHANNEL, ~TUNE_CH, Temp);
}

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------