/* File: robotics8d.c
   Version 5 of basic controller routines for Version 8 of the UE 
   controller board.  Corrected ReadInputPort to return 1 rather than
   non-zero on close.

   Keil C headers are not guarded.  If robotics library is used, its header
   file should be the only included file unless KEIL_C_H is defined first.
*/

#include "robotics8d.h"
#include <stdlib.h>

/* State variables */
unsigned char motors = 0x00;      /* Current state of motor port */
unsigned char outputs = 0xFF;     /* Current state of output ports  */
unsigned int tenths = 0;          /* Counter for clock in tenths of second */

/* ------------------------------------------------------------------------
INITIALIZE ROUTINE.  Initializes all of the parts of the controller board.
Must be called before any other routines.
---------------------------------------------------------------------------*/
void Initialize(void)
{
   /* Initialize PWM */
   CMOD   = 0X01;         // put PCA in osc divide by 6 mode (clock period = 1/2 us with a 12MHz xtal) 
                          // and enable pca interrupt
   CCAPM1 = 0x42;         // Enable Motor 1 PWM for 8-bit PWM
   CCAPM0 = 0x42;         // Enable Motor 2 PWM for 8-bit PWM
   CCAPM3 = 0X11;         // put module 3 in falling edge capture mode and enable interrrupt
   CCON   = 0X40;         // enable PCA clock source (PCA starts counting) 

   /* Initialize real-time clock */
   TMOD = TMOD & 0xf0;    // save timer 1 settings              
   TMOD = TMOD | 0x01;    // or in timer 0 16 bit clock mode    
   TH0 = -(50000) >> 8;   // load high byte for 25 ms           
   TL0 = -(50000);        // load low  byte for 25 ms           
   TR0 = 1;               // start the clock (timer 0)          

   IE = IE | 0xC2;        // start interrupts

   /* Initialize all output ports to high */
   XBYTE[IO_IDX] = outputs;

   /* Stop all motors */
   AllStop ();
}  /* Initialize */

/* -------------------------------------------------------------------
INTERRUPT ROUTINES
----------------------------------------------------------------------*/
void ClockTicInterrupt(void) interrupt 1 using 3
{
   static unsigned char twentyfivemilliseconds;

   TH0 = -(50000) >> 8;               // load high byte for 25 ms
   TL0 = -(50000);                    // load low  byte for 25 ms
  
   if (++twentyfivemilliseconds > 3)  // increment and test 
   {
      twentyfivemilliseconds = 0;     // 4 interrupts have expired since last clearing
      tenths++;                       // increment tenths of seconds
   }  /* end if */
}  /* end ClockTickInterrupt */

unsigned int difference, diffvalue;

void PWMInterrupt(void) interrupt 6 using 2

{  
   static unsigned int lastread;
   static unsigned char PCAoverflow;
      
   if (CF == 1)
   {
      CF = 0;
      if (PCAoverflow++ > 1) // increment and test overflow
      {
          difference = 0;  // clear measured values
          diffvalue = 0;
          lastread = 256*CCAP3H + CCAP3L;  // read for next calculation
      }
   }
   else
   {
      if (CCF3 ==1) // must be  a capture
      {  
         difference = (256*CCAP3H+ CCAP3L) - lastread; // read beacon wave period
         PCAoverflow=0;
         CCF3 = 0;
         lastread = 256*CCAP3H + CCAP3L;
      }
   } 
}  /* end PWMInterrupt */

/* ------------------------------------------------------------------------
BEACON ROUTINES
Returns 0 (slowest) to 3 (fastest) for beacons oscillating at different 
frequencies.
---------------------------------------------------------------------------*/
unsigned char BeaconType()

{
   diffvalue = difference;
   if (diffvalue  > 960)
      return (3);
   else if (diffvalue > 720)
      return (2);
   else if (diffvalue > 440)                 
      return (1);
   else 
      return (0);
}  /* end BeaconType */

/* ------------------------------------------------------------------------
DELAY ROUTINE.  With count = 1 this routine provides a delay of 100 msec.
This timer routine blocks.
---------------------------------------------------------------------------*/
void Delay(unsigned char count)
{
   unsigned int start = tenths;
   while (start + count > tenths);
}  /* end Delay */

/* ------------------------------------------------------------------------
MOTOR ROUTINES
Speed is -255 to 255, however |speed| needs to be > 190 to be effective
for most vehicle designs.
Direction is based on sign of speed:
                   < 0 for left/reverse
                   = 0 for stop
                   > 0 for right/forward
Port bits control direction:
                   00 - stop (but motor still on, used for dynamic braking)
                   01 - "left"
                   10 - "right"
                   11 - not used
Port bits assigned: M11 M12 M21 M22 X X X X
---------------------------------------------------------------------------*/
void AllStop(void)
{
   motors = ALLMOTORSOFF;
   XBYTE[MOTOR_IDX] = motors;
}  /* end AllStop */

void SetMotor(unsigned char motorID, int speed)
{
   unsigned char shift = 2 * (4 - motorID);
   unsigned int realSpeed = abs(speed);

   if (motorID > MAXMOTORID || motorID == 0)  /* error in usage, turn everything off */
   {
      AllStop();
      return;
   }  /* motor number out of range */

   if (realSpeed > 255)   /* cap speed number at 255 */
       realSpeed = 255;

   /* set direction */
   motors = motors & ~(MOTOROFF << shift);       /* Turn motor num off */
   if (speed < 0)                                /* Turn motor num on left/reverse */
      motors = motors | (MOTORLEFT << shift);
   else if (speed > 0)                           /* Turn motor num on right/forward */
      motors = motors | (MOTORRIGHT << shift);

   /* Set speed */
   switch (motorID)  
   {
      case 1:                            // Motor 1 uses module 1
         CCAP1H = realSpeed;             // Set speed  
         break;
      case 2:                            // Motor 2 uses module 0
         CCAP0H = realSpeed;             // Set speed
         break;
   }  /* end setspeed */

   XBYTE[MOTOR_IDX] = motors;  /* set direction control */
}  /* SetMotor */

/* ------------------------------------------------------------------------
I/O ROUTINES
Input ports
Indexed 0-7 (left to right when viewed from front)
Returns 1 when closed, 0 when open
---------------------------------------------------------------------------*/

unsigned char ReadInputPort (unsigned char num)
{
   if (num > 7)
   {
      return 0;
   }  /* input port number out of range */
   else
   {
      if (XBYTE[IO_IDX] & (BITMASK << num))
         return 1;
      else
         return 0;
   }  /* read input port */   
} /* ReadInputPort */ 
    
/*-------------------------------------------------------------------
Ouput ports
Indexed 0-7 (left to right when viewed from front)
Low is 0, high is 1
---------------------------------------------------------------------*/
void SetOutputPort (unsigned char num, unsigned char direction)
{
   if (num <= 7)
   {
      if (direction == LOW)                         
         outputs = outputs & ~(BITMASK << num);    /* Pull port num low  */
      else if (direction == HIGH)
         outputs = outputs | (BITMASK << num);     /* Pull port num high */

      /* Write output port */
      XBYTE[IO_IDX] = outputs;
   }
   // else ignore port number out of range
} /* SetOutputPort */

/* ------------------------------------------------------------------------
A TO D CONVERTER ROUTINES
Channels are numbered 0-7
---------------------------------------------------------------------------*/
sbit notDone = 0xB5;  /* Port 3 Bit 5 */

unsigned char GetAtoD (unsigned char channel)
{
   /* OR 8 with channel for single ended entry */
   XBYTE[ATOD_IDX] = (channel | 8); 

   /* Delay to allow conversion to finish       */
   /* Interrupt sets P3.5 to 0 when finished */
   notDone = 1;
   while (notDone);  /* just waiting for change in state */
   
   /* Get the data and return it */
   return XBYTE[ATOD_IDX];
}  /* end GetAtoD */

unsigned char code message0[]=" ch0=";
unsigned char code message1[]=" ch1=";
unsigned char code message2[]=" ch2=";
unsigned char code message3[]=" ch3=";
unsigned char code message4[]=" ch4=";
unsigned char code message5[]=" ch5=";
unsigned char code message6[]=" ch6=";
unsigned char code message7[]=" ch7=";

void DisplayNum(unsigned char channel)
{  
   unsigned char voltage;
   unsigned int index; 
   unsigned char number[3];

   /* Read voltage and convert number to a string */   
   voltage = GetAtoD(channel);
   number[0] = ((voltage%1000) / 100) | 0x30;
   number[1] = ((voltage%100) / 10)   | 0x30;
   number[2] = (voltage%10)           | 0x30;

   /* Write characters to serial port */
   for (index = 0; index < 3; index++)
   {
      TI = 0;
      SBUF = number[index];
      while (TI == 0);   // Wait for write to be done

   }  /* end for */
}  /* end DisplayNum */

void DisplayMsg(unsigned char msg[])
{
   unsigned int index;

   for (index = 0; index < 5; index++)
   {
      TI = 0;
      SBUF=msg[index];
      while (TI==0);          
   }  /* end for */
}

/*******************************************************************/
/*                                                                 */
/*  ScanAnalog scans all eight channels of the A to D converter    */
/*  and sends them to the serial port.  Use it with HyperTerm with */
/*  the following setup:                                           */
/*  19.2 kilobits per second, 1 stop bit, no parity, and no flow   */
/*  control                                                        */
/*                                                                 */
/*******************************************************************/

void ScanAnalog()
{
   unsigned int index;

   SCON = 0X50;
   TR2=0;       // using timer 2 as a baud clock
   RCLK=1;      // enable receiver clock
   TCLK=1;      // enable transmitter clock
   RCAP2H=0XFF; // 19.2k baud
   RCAP2L=0xD9; // 
   TR2=1;       // start timer 2
     
   /* Initialization of serial port ? */
   for (index = 0; index < 5000; ++index);
   TI=0;
   SBUF=0x0D;
   while (TI==0);

   /* Display each channel */
   DisplayMsg(message0);
   DisplayNum(0);

   DisplayMsg(message1);
   DisplayNum(1);

   DisplayMsg(message2);
   DisplayNum(2);

   DisplayMsg(message3);
   DisplayNum(3);

   DisplayMsg(message4);
   DisplayNum(4);

   DisplayMsg(message5);
   DisplayNum(5);

   DisplayMsg(message6);
   DisplayNum(6);

   DisplayMsg(message7);
   DisplayNum(7);
}  /* end ScanAnalog */

/* ------------------------------------------------------------------------
SOUND ROUTINES
The buzzer is on port 1 at pin 0.  Set to 0 for on, 1 for off
state = 1 turns buzzer on; state = 0 turns buzzer off
---------------------------------------------------------------------------*/
sbit P1_0 = 0x90;  /* Port 1 Pin 0 */

void Sound(unsigned char state)
{
   if (state == OFF)  /* Turn buzzer off */
   { 
      /* Set P1.0 to 1 */
      P1_0 = 1;            
   }  /* end if */
   else   /* Turn buzzer on */
   {
      /* Set P1.0 to 0 */
      P1_0 = 0;
   }  /* end else */
}  /* end Sound */

/* Beep drives buzzer as a beeper at approximately .25 seconds on, */
/* .25 seconds off for duration number of cycles.  E.g., Beep(5)   */
/* beeps 5 times.                                                  */
void Beep(unsigned char duration)
{
   unsigned char j;
   for(j = 0; j <duration; j++)
   {
      Sound (ON);
      Delay (2);     /* .2 seconds */
      Sound (OFF);
      Delay (2);
   }  /* end for */
}  /* end Beep */
