/*******************************************************************************
*
* File:     em4095.c
* Author:   Roman Mego
* Date:     22. July 2011
*
* Brief:    Functions for RFID reader EM4095
*           On migrating to another platform or circiuit, change function
*           rfid_init() and first #include <*.h>
*
*******************************************************************************/

#include <htc.h>        // library with registers (platform dependent)
#include "em4095.h"

#define MANCHESTER_BIT          64	// number of clock pulses on bit
#define MANCHESTER_HALF_BIT     MANCHESTER_BIT / 2
#define MANCHESTER_MIDDLE       ((MANCHESTER_BIT + MANCHESTER_HALF_BIT) / 2)
#define MANCHESTER_MAX          (MANCHESTER_MIDDLE + MANCHESTER_HALF_BIT)
#define MANCHESTER_MIN          (MANCHESTER_MIDDLE - MANCHESTER_HALF_BIT)

#define IS_MANCHESTER           (CAP_REG < MANCHESTER_MAX && CAP_REG > \
                                 MANCHESTER_MIN)
#define IS_MANCHESTER_T         (CAP_REG < MANCHESTER_MIDDLE)
#define IS_MANCHESTER_2T        (CAP_REG > MANCHESTER_MIDDLE)

char dataReady = 0;

char currentBit = 1;
char needNextManchesterT = 0;

unsigned char bitReceived = 7;
unsigned char byteReceived = 6;
unsigned char buffer[8];
unsigned char data[5];


/*******************************************************************************
* Initialozation of RFID module (platform dependent)
********************************************************************************
 1. set port direction
 2. set timer to 125kHz or set external from CLK/RDY on EM4095
 3. set interrupt on caprute (falling edge)
*******************************************************************************/

void init_rfid(void) {
    OPTION_REGbits.nRBPU = 0;   // PORTB pull-ups are enabled
                                // by individual port latch values

    // set MOD and SHDN pins as outputs
    MOD_dir &= ~(1 << MOD_pin);
    SHDN_dir &= ~(1 << SHDN_pin);

    // sets CLK and DEMOD pins as inputs
    CLK_dir |= 1 << CLK_pin;
    DEMOD_dir |= 1 << DEMOD_pin;

    // Timer1 initialization
    T1CON = 3 << 4;             // 1:8 Prescale value (125kHz)
    T1CONbits.TMR1CS = 0;       // Internal clock (FOSC/4)
    T1CONbits.TMR1ON = 1;       // Enables Timer1

    // initialization of capture module
    CCP1CON = 0x05;             // Capture mode, every falling edge
    PIE1bits.CCP1IE = 1;        // Enables the CCP1 interrupt
    INTCONbits.PEIE = 1;        // Enables all un-masked peripheral interrupts

    rfid_off();                 // turns RFID module off
}


/*******************************************************************************
* Data reception (call on capture interrupt)
*******************************************************************************/

void rfid_interrupt(void) {
    TIM_REG = 0;                     // change timer to zero

    if (IS_MANCHESTER) {            // test if it can be manchester symbol
        if (needNextManchesterT) {  // if previous reception was half-bit
            if (IS_MANCHESTER_T) {  // if it's second half-bit
                needNextManchesterT = 0;    // we don't expecting next half-bit
                buffer_add_bit();   // add received bit to buffer
            }
            else {              // if we expected half bit and it wasn't
                rfid_reset();   // reset couters
                return;
            }
        }
        else {                      // if half-bit wasn't expected
            if (IS_MANCHESTER_T) {  // expect second half-bit on next capture
                needNextManchesterT = 1;
            }
            else {                  // if it was full byte
                currentBit ^= 0x01; // toggle bit that will be added
                buffer_add_bit();   // add received bit to buffer
            }
        }
    }
    else {                          // if not manchester symbol
        rfid_reset();               // reset counters and buffer
        return;
    }

    CAP_EDGE_TOGGLE;                // change capture edge
    return;
}


/*******************************************************************************
* Add bit to buffer on correct reception
*******************************************************************************/

void buffer_add_bit(void) {
    if (buffer[7] == 0xff) {    // if we received 8 bits of header
                                // (it is synchronized)

        // save received bit on current position
        buffer[byteReceived] |= currentBit << bitReceived;

        if (bitReceived-- == 0) {   // change bit position
            bitReceived = 7;    // if we are at the and of byte jump on start

            if (byteReceived-- == 0) {  // and change byte position
                check_rfid_data();      // check received buffer and save data
                rfid_reset();           // prepare buffer and counters for next transfer
                return;
            }
        }
    }
    else {                          // if we are not synchronized
        buffer[7] <<= 1;            // shift first byte
        buffer[7] |= currentBit;    // and add received bit
    }
}


/*******************************************************************************
* Reset buffer, positions and capture edge
*******************************************************************************/

void rfid_reset(void) {
    buffer[0] = 0;
    buffer[1] = 0;
    buffer[2] = 0;
    buffer[3] = 0;
    buffer[4] = 0;
    buffer[5] = 0;
    buffer[6] = 0;
    buffer[7] = 0;              // reset buffer

    currentBit = 1;             // next bit that will be added is 1
    needNextManchesterT = 0;    // don't expect half-bit

    bitReceived = 7;            // reset bit position in buffer
    byteReceived = 6;           // reset byte position in buffer

    CAP_EDGE_RISING;            // set rising edge for capture
}


/*******************************************************************************
* Set RFID module to sleep mode
*******************************************************************************/

void rfid_off(void) {
    dataReady = 0;  // if it's in sleep mode, data cant't be ready
    RFID_MOD_OFF;   // it's recomended to turn modulation off in sleep mode
    SHDN_port |= 1 << SHDN_pin; // high level forces the circuit into sleep mode
}


/*******************************************************************************
* Wake-up from sleep mode
*******************************************************************************/

void rfid_on(void) {
    rfid_reset();                   // reset buffer etc.
    RFID_MOD_OFF;                   // turn modulation off
    SHDN_port &= ~(1 << SHDN_pin);  // wake-up
    WAIT_35_MS;                     // wait for antenna oscillation
}

/*******************************************************************************
* Return even parity of data
*******************************************************************************/

char parity(unsigned char dataIn) {
    char n;
    char par = 0;

    for (n = 8; n; n--) {
        par ^= dataIn & 0x01;   // makes exclussive-or with every bit
        dataIn >>= 1;
    }
    return par;
}


/*******************************************************************************
* Check received data in buffer
********************************************************************************
 Returns 1 if it's correct format
 Returns 0 if there is something wrong

 Data format:
 1 1 1 1  1   1   1   1   1	- header (9 bit)
              x   x   x   x  P0 \
              x   x   x   x  P1  |
              x   x   x   x  P2  |
              x   x   x   x  P3  |
              x   x   x   x  P4   \ parity
              x   x   x   x  P5   /
              x   x   x   x  P6  |
              x   x   x   x  P7  |
              x   x   x   x  P8  |
              x   x   x   x  P9 /
    parity  PC0 PC1 PC2 PC3  0 - stop bit
*******************************************************************************/

char check_rfid_data(void) {

    // check header bits
    if ((buffer[7] != 0xff) || ((buffer[6] & 0x80) != 0x80)) {
        return 0;
    }

    // check stop bit
    if ((buffer[0] & 0x01) != 0) {
        return 0;
    }

    // check P0
    if (parity(buffer[6] & 0b01111000) != ((buffer[6] & 0x04) >> 2))
        return 0;

    // check P1
    if (parity(buffer[6] & 0b00000011) ^ parity(buffer[5] & 0b11000000) != ((buffer[5] & 0x20) >> 5))
        return 0;

    // check P2
    if (parity(buffer[5] & 0b00011110) != (buffer[5] & 0x01))
        return 0;

    // check P3
    if (parity(buffer[4] & 0b11110000) != ((buffer[4] & 0x08) >> 3))
        return 0;

    // check P4
    if (parity(buffer[4] & 0b00000111) ^ parity(buffer[3] & 0b10000000) != ((buffer[3] & 0x40) >> 6))
        return 0;

    // check P5
    if (parity(buffer[3] & 0b00111100) != ((buffer[3] & 0x02) >> 1))
        return 0;

    // check P6
    if (parity(buffer[3] & 0b00000001) ^ parity(buffer[2] & 0b11100000) != ((buffer[2] & 0x10) >> 4))
        return 0;

    // check P7
    if (parity(buffer[2] & 0b00001111) != ((buffer[1] & 0x80) >> 7))
        return 0;

    // check P8
    if (parity(buffer[1] & 0b01111000) != ((buffer[1] & 0x04) >> 2))
        return 0;

    // check P9
    if (parity(buffer[1] & 0b00000011) ^ parity(buffer[0] & 0b11000000) != ((buffer[0] & 0x20) >> 5))
        return 0;

	if (parity(buffer[6] & 0b01000010) ^
		parity(buffer[5] & 0b00010000) ^
		parity(buffer[4] & 0b10000100) ^
		parity(buffer[3] & 0b00100001) ^
		parity(buffer[2] & 0b00001000) ^
		parity(buffer[1] & 0b01000010) != ((buffer[0] & 0x10) >> 4))	// check PC0
		return 0;

	if (parity(buffer[6] & 0b00100001) ^
		parity(buffer[5] & 0b00001000) ^
		parity(buffer[4] & 0b01000010) ^
		parity(buffer[3] & 0b00010000) ^
		parity(buffer[2] & 0b10000100) ^
		parity(buffer[1] & 0b00100001) != ((buffer[0] & 0x08) >> 3))	// check PC1
		return 0;

	if (parity(buffer[6] & 0b00010000) ^
		parity(buffer[5] & 0b10000100) ^
		parity(buffer[4] & 0b00100001) ^
		parity(buffer[3] & 0b00001000) ^
		parity(buffer[2] & 0b01000010) ^
		parity(buffer[1] & 0b00010000) ^
		parity(buffer[0] & 0b10000000) != ((buffer[0] & 0x04) >> 2))	// check PC2
		return 0;

	if (parity(buffer[6] & 0b00001000) ^
		parity(buffer[5] & 0b01000010) ^
		parity(buffer[4] & 0b00010000) ^
		parity(buffer[3] & 0b10000100) ^
		parity(buffer[2] & 0b00100001) ^
		parity(buffer[1] & 0b00001000) ^
		parity(buffer[0] & 0b01000000) != ((buffer[0] & 0x02) >> 1))	// check PC3
		return 0;

	data[4] = ((buffer[6] & 0b01111000) << 1) |
			  ((buffer[6] & 0b00000011) << 2) |
			  ((buffer[5] & 0b11000000) >> 6);

	data[3] = ((buffer[5] & 0b00011110) << 3) |
			  ((buffer[4] & 0b11110000) >> 4);

	data[2] = ((buffer[4] & 0b00000111) << 5) |
			  ((buffer[3] & 0b10000000) >> 3) |
			  ((buffer[3] & 0b00111100) >> 2);

	data[1] = ((buffer[3] & 0b00000001) << 7) |
			  ((buffer[2] & 0b11100000) >> 1) |
			  (buffer[2] & 0b00001111);

	data[0] = ((buffer[1] & 0b01111000) << 1) |
			  ((buffer[1] & 0b00000011) << 2) |
			  ((buffer[0] & 0b11000000) >> 6);		// save data from buffer

	dataReady = 1;		// data is ready for reading

	return 1;
}


/*****************************************************************************************
* Convert low nibble to ASCII character
*****************************************************************************************/

char toAscii(char hex) {
	hex &= 0x0f;
	if (hex > 9) hex += 7;
	hex += 0x30;
	return hex;
}

/*****************************************************************************************
* Save code to array
******************************************************************************************
 Final string is compatible with Innovations ID-12 module

 Format:
 [0x12] [Code - HEX format] [Checksum] [0x0d] [0x0a] [0x03] [0x00]
   1b           10b             2b       1b     1b     1b     1b
*****************************************************************************************/

char rfid_read(char *code) {
	signed char n;
	char checksum;
	if (dataReady) {
		*(code++) = 0x02;						// start of text

		for (n = 4; n >= 0; n--) {
			*(code++) = toAscii(data[n] >> 4);
			*(code++) = toAscii(data[n]);		// convert code to text
		}

		checksum = data[4] ^ data[3] ^ data[2] ^ data[1] ^ data[0];	// compute checksum

		*(code++) = toAscii(checksum >> 4);
		*(code++) = toAscii(checksum);			// checksum

		*(code++) = 0x0d;						// carriage reurn
		*(code++) = 0x0a;						// new line
		*(code++) = 0x03;						// end of text
		*(code++) = 0x00;						// end of array

		dataReady = 0;							// data was read

		return 1;
	}
	return 0;
}
