// Microsystems on Silicon(Pty) Ltd.
// M2012A DOCI to RS232 SERIAL INTERFACE
// Version 1 made for m2012a1_smd1.pcb
// 24 Feb 2003

#define alog(x) (1<<(x))

//#define DEBUG 1

#include <pic.h>

#define TX_LED_OUT    0b00000001 // Transmit LED output
#define RX_LED_OUT    0b00000010 // Receive LED output
#define ERROR_LED_OUT 0b00000100 // Error LED output
#define RUN_LED_OUT   0b00100000 // RUN  LED output


#define CS_OUT        0b00000010 // Chip select/Load to DAC
#define SCK_OUT       0b00001000 // Clock output for DAC
#define MOSI_OUT      0b00100000 // Data output to DAC

#define RX_OUT        0b01000000 // RS232 PIC Transmit PC RX
#define TX_IN         0b10000000 // RS232 PIC Receive PC TX

#define DOCI_BID      0b00010000 // Bidirectional data/clock for 1st DPIR

#define INTR_OUT      0b00000001 // Toggle output on interrupt

#define NOP() asm("nop")

#define PORTBIT(p,b) ((unsigned)&(p)*8+(b))

// Port A assignments
static bit TX_LED      @ PORTBIT(PORTA,0);
static bit RX_LED      @ PORTBIT(PORTA,1);
static bit RUN_LED     @ PORTBIT(PORTA,2);
static bit ERROR_LED   @ PORTBIT(PORTA,5);

static bit PC0  @ PORTBIT(PORTC,0);  // Input
static bit CS   @ PORTBIT(PORTC,1);
static bit PC2  @ PORTBIT(PORTC,2);
static bit SCK  @ PORTBIT(PORTC,3);
static bit PC4  @ PORTBIT(PORTC,4);
static bit MOSI @ PORTBIT(PORTC,5);
static bit RX   @ PORTBIT(PORTC,6);
static bit TX   @ PORTBIT(PORTC,7);

static bit DOCI  @ PORTBIT(PORTB,4);
static bit INTR  @ PORTBIT(PORTB,0);
 

//----------------------------------------------------------------------
#define SENSORS       1      // RS232 sensor strings to send  //could be up to 8
#define HEADER_BYTES  3      // Header + Device Info + amount of bytes
#define DEV_INFO      1      // Device type only 7 bits

#define IGNORE_INTRRUPTS_FROM_DPIR 0   // Interrupts to ignore from sensors
//-------------------------------------------------------------------------

#define HEADER       0xA5  // Header byte for serial packet
#define MAX_SLOTS    (SENSORS*2)+HEADER_BYTES

#define SERIAL_SPEED 9   //18.432MHz 115.2kbps High speed

#define LED_ON  0
#define LED_OFF 1

unsigned char que[MAX_SLOTS+1]; // array that makes up the que in memory  pir_temp[j]
unsigned int  t, pir_data;      // variable with PIR movement data

unsigned char new_portb; // sample port B
unsigned char old_portb; // sample port B
unsigned char elements;   // amount of elements in buffer maximum is MAX_SLOTS+1
unsigned char read_skip;   // If serial data is too much some interrupts will be ignored

unsigned char i; // Temp variable
unsigned char  n; // Temp Variable
unsigned int  mask; // Mask for shift out
unsigned char rank;  // rank in the array that is set via serial port
unsigned int  cnt; // Counter for Run LED

//-------------------------------------------------------------------------
// Do initialization of the micro controller
//-------------------------------------------------------------------------
void do_startup(void)
{
  //processor setup
  //  (1 is input, 0 is output)

  TRISA=0x00;  //  (ONLY 6 PINS)
  TRISB=0x00;  //  upper bits of portB is outputs for change on interrupt
  TRISC=0x00;

  //Port B
  TRISB|=DOCI_BID;   // Only one input
  TRISB&=~INTR_OUT;  // Shows interrupts by the M2012A

  //Port A
  TRISA&=~TX_LED_OUT;    //
  TRISA&=~RX_LED_OUT;    //
  TRISA&=~ERROR_LED_OUT; //
  TRISA&=~RUN_LED_OUT;   //

  //Port C
  TRISC&=~SCK_OUT;    // used with DAC
  TRISC&=~MOSI_OUT;
  TRISC&=~CS_OUT;
  TRISC&=~RX_OUT;  // Names as PC sees it
  TRISC|=TX_IN;   //  Names as PC sees it

  ADCON1=0x07;  // RE and RA pins to digital IOs

  RBPU=1;   // Disable RB pull ups
  INTEDG=0; // 1==Rising edge of RB0
  T0CS=0;   // Timer0 clock source = internal
  T0SE=0;   // Timer0 source edge = falling // not used
  PSA=0;    // Prescalers assigned to WDT
  PS2=0;    // Timer is set to 1:128
  PS1=0;
  PS0=0;

  CLRWDT();
  GIE=0;     // Disable all interrupts
  PEIE=0;    // Peripheral int enable
  T0IE=0;    // Timer interrupt enable
  INTE=0;    // RB0 interrupt
  RBIE=1;    // Port B change interrupt
  T0IF=0;    // Clear timer0 interrupt flag
  INTF=0;    // Clear Port B interrupt flag
  RBIF=0;    // Clear Port B change interrupt flag

  ADIE=0;    // A/D int
  RCIE=0;    // USART receive int
  TXIE=0;    // USART transmit int
  SSPIE=0;   // Serial port int
  CCP1IE=0;  // Comp disable bit
  TMR2IE=0;  // Timer 2 match int

  TMR1IE=0;  // Timer 1 overflow  match int

  ADIF=0;
  RCIF=0;
  TXIF=1;
  SSPIF=0;
  CCP1IF=0;
  TMR2IF=0;
  TMR1IF=0;

  BRGH=1;
  SPBRG=SERIAL_SPEED;

  SYNC = 0;  // Set to async port
  SPEN = 1;  // Enable serial port

  TXEN=1;  // Enable transmit
  CREN=0;  // 1 = Continuous RX enable

  TXIE=0;  // Disable Transmit Interrupt
  RCIE=0;  //1 = Enable Receive  Interrupt
  PEIE=1;  // Enable peripheral interrupts


  EEIE=0; // EEPROM int
  BCLIE=0; // int
  CCP2IE=0; //int

  EEIF=0;
  BCLIF=0;
  CCP2IF=0;

  GIE=1; // Enable all interrupts

  // Program variables
  TX_LED=LED_OFF;
  RX_LED=LED_OFF;
  ERROR_LED=LED_OFF;
  RUN_LED=LED_OFF;
  read_skip=IGNORE_INTRRUPTS_FROM_DPIR;
  CS=1;
}


//=========================================================================
static void interrupt __INT(void)
{
  new_portb=PORTB;
  RBIF=0;
}
//=========================================================================
// This leaves the MOS bus in a state where the sensor can generate an interrupt
//=========================================================================
void clear_DOCI_interrupt(void)
{
  //Release the DOCI Line leave in zero state
  DOCI=0;           // Clear DOCI Pin
  TRISB&=~DOCI_BID; // DOCI Output pin
  NOP();
  TRISB|=DOCI_BID;  // DOCI Input pins high impedance
}

//=========================================================================
// Put micro controller in a sleep state
//=========================================================================
void goto_sleep(void)
{
  old_portb=PORTB; // Save state of port B
  SLEEP();
}

//=========================================================================
// Send a 16 bit word to a 16 bit DAC  LT1655
// Procedure was used for debugging
//=========================================================================
void load_dac(int dat)
{
  CS=0;
  NOP();
  mask=0x8000;
  for(n=0;n<16;n++)
  {
    if(dat&mask)
      MOSI=1;
    else
      MOSI=0;

    dat<<=1;
    // Clock bit in DAC
    SCK=1;
    NOP(); NOP();
    SCK=0;
  }
  NOP();
  CS=1;
}

//=========================================================================
// Read filter from M2012A
// DOCI is a IO pin
// DOCI_BID is a bit pattern that set or clear the direction
// of the IO pad on the processor
//=========================================================================
unsigned int dpir_filt_rd(void)
{
  unsigned char n;
  // Wait for at least 1 device clock on the sensor before loading
  NOP(); NOP(); NOP(); NOP(); NOP();
 // NOP(); NOP(); NOP(); NOP(); NOP();
 // The micro controller takes much longer than a clock cycle on the M2012A 
 // to wake up from sleep mode. So the delay here is not a problem

  pir_data=0;         // this is the word that will contain the data from the M2012A
  for(n=0;n<15;n++)   // There are 15 bits of data from the sensors
  {
    DOCI=0;           // Clear DOCI pin
    TRISB&=~DOCI_BID; // DOCI pin output
    NOP();            // Wait a bit
    DOCI=1 ;          // Set DOCI pin
    TRISB|=DOCI_BID;  // DOCI Pin input
    pir_data<<=1;     // make room for first bit
    NOP(); NOP();     // Wait before sampling the DOCI pin
    if(DOCI)          // Test DOCI pin
      pir_data++;     // Set LSB
  }
  return pir_data;    // pir_data holds sensor info
}

//=========================================================================
void pack_data(void)
{
  unsigned int x;
  unsigned char sensors;

  // PCB uses only 1 sensor, the header and PC SW will accept more inputs from sensors
  // This was kept in to stay compatible with the windows scope program
  sensors=SENSORS;

  elements=0;
  rank=(sensors*2)+HEADER_BYTES;  // positions in the array 
  //rank=MAX_SLOTS;
  que[rank--]=HEADER;             // Identification byte
  elements++;
  que[rank--]=(DEV_INFO<<1);      // Device info for windows SW
  elements++;
  que[rank--]=rank+2;             // Total packet length 
  elements++;

  // This part makes provision for multiple sensors
  for(n=0;n<sensors;n++)
  {
    t=pir_data;
    x=t&0xff00;
    x>>=8;
    que[rank--]=(unsigned char)x;
    elements++;
    que[rank--]=(unsigned char)t&0x00ff;
    elements++;
  }
}

//=========================================================================
void main(void)
{
  do_startup();
  clear_DOCI_interrupt();
  while(1)
  {
    CLRWDT();
    INTR^=1;
    if(cnt++>50)
    {
      RUN_LED^=1;  // Show some activity
      cnt=0;
    }
    if(!read_skip--)
    {
      read_skip=IGNORE_INTRRUPTS_FROM_DPIR;  //Could skip some interrupts if needed
      dpir_filt_rd();         // Read the Digital PIR
      clear_DOCI_interrupt(); // Leave port B in high Z state ready for interrupts
      pack_data();            // Put PIR data in a array
      //      load_dac(pir_data);     // Send data to the DAC  (Only for debugging)
      while(elements>0)     // Amount of elements comes from pack_data()
      {
        if(TRMT)    // Transmit buffer Empty
        {
          TX_LED=LED_ON;
          TXREG=que[elements--]; // send next element
        }
        if(elements==0)   //Finished with the transmission
          TX_LED=LED_OFF;
      }
    }
    while(!TRMT)  //wait here until serial data is sent
      NOP();

    clear_DOCI_interrupt(); // Leave port B in high Z state ready for interrupts
    goto_sleep();
  }
}