/***
 *
 * Main program to evaluate the various functions of the usb pic
 * Copyright (c) 2005, Inventronik GmbH
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this 
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Inventronik nor the names of its contributors may be 
 * used to endorse or promote products derived from this software without 
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: rc-5.c,v 1.2 2005/01/29 13:15:44 jensi Exp $
 */

/*
 * RC-5-Encoder
 *
 * Many of todays available IR remote controls use the RC-5 coding to control
 * all kinds of el. devices (e.g. TV sets, DVD players ...)
 * This source module encodes the signal received from a Photo Module like the 
 * TSOP173x from VISHAY (see datasheets in the download area for details). 
 *
 * This is a short description of the RC-5 code.
 *
 * The code exists of 14 bits. Originally it has 2 start bits (high), 1 toggle 
 * bit, 5 address bits and 6 command bits.
 * The 5 address bits can be used to address up to 32 different devices.
 * Each device is able to decode 64 commands (6 cmd bits). However, Philips
 * the inventor of the RC-5 code, figured out that 64 commands might be a 
 * little too short for more complex devices. As a *hardware hack* they 
 * sacrificed the 2nd start bit and used this as an inverted most significant 
 * command bit (-C6).
 * The modified code now accepts up to 128 different commands.
 * The complete code looks like this:
 *
 * 1 -C6 T A4 A3 A2 A1 A0 C5 C4 C3 C2 C1 C0 (total 14 bits)
 *
 * 1 - start bit (high)
 * T - toggle bit
 * A0-A4 - device address bits
 * C0-C6 - command bits
 *
 * Every bit will be transmitted over a period of 1.778 ms. One complete IR 
 * sequence lasts 14 x 1.778 ms = 24.889 ms.
 * The toggle bit is used to differentiate between an automatic re-transmission 
 * of the same IR sequence when a IR button is pressed over a longer period 
 * of time (toggle bit will not be changed) and the repeated pressing of the 
 * same IR button (toggle bit changes on every keystroke).
 * However the IR signal received through the Photo Module looks a bit different.
 * One single bit exists of 32 pulses over a period of 889us and a pause of 
 * 889us, the order determines the value of the bit.
 *
 * A "high" value is defined when the transmission starts with the pause (889 us) 
 * followed by 32 pulses @ 36kHz (within the next 889 us). A "low" has the 
 * opposite order (pulses first and then the pause).
 * Luckily, the TSOP173x does the most of the work detecting the pulses. The 
 * TSOP173x outputs a "high" during the pause and a "low" when it receives 
 * pulses.
 * This means 2 successively received bits define the bit value 
 * (biphase encoding). 
 *
 * Example:
 * Say, the 14 bits should look like this:
 * 11 1 00000 010000 (2 start bits, the toggle bit, 5 address bits and 6 
 *                    command bits)
 * The biphase encoded bit stream then looks like that:
 * 0101 01 1010101010 100110101010
 *
 * However the TSOP173x inverts this signal!
 * TSOP173x output:
 * 1010 10 0101010101 011001010101
 *   ^
 *
 * Program idea:
 *
 * The TSOP173x has high level when no IR signal is detected. This means 
 * the first detected falling edge marks the "high" of the start bit.
 * Let's measure the low and high timmes of the transmitted pulses. We
 * will receive a maximum of 3 bits every measurement cycle. 
 */

#if defined(__PCM__)
#include <16f876.h>
#fuses HS, NOWDT, NOPROTECT, PUT, NOBROWNOUT, NOLVP
#elif defined(__PCH__)
#include <18f252.h>
#fuses EC, NOWDT, NOPROTECT, PUT, NOBROWNOUT, NOLVP
#endif

#zero_ram // erase all used ram cells at initialisation.
#use delay(clock=6000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

#include <ctype.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>

#define MAX_RC5_ENTRIES 10

#define DELAY_1333US	2000 // (.001333/(4/6000000))
#define DELAY_2227US	3341 // (.002227/(4/6000000))

#define RCV_RC5 PIN_B0
#define DBG_LED PIN_A0

/* RC-5 data container */
struct rc5struct
{
	int1 toggle;			/* toggle bit */
	unsigned char dev;		/* device */
	unsigned char cmd;		/* command */
};

/* global rc-5 fifo */
struct rc5struct rc5data[MAX_RC5_ENTRIES];
unsigned int rc5inPtr = 0, rc5outPtr = 0;
#define rc5avail (rc5inPtr!=rc5outPtr)


/**
 * external interrupt at falling edge
 */
#INT_EXT
void ext_isr(void)
{
	unsigned long ttcnt=0, bitseq=0;
	unsigned int bit2cnt=1, bits2=1, bitcnt=0, bitsh=0;
	int1 ibit, failure=false;

	do {
		ttcnt = 0;
		set_timer1(0);
		ibit = input(RCV_RC5);
		while(input(RCV_RC5)==ibit && ttcnt<DELAY_2227US)
			ttcnt = get_timer1();

		if (ttcnt>=DELAY_2227US) { /* timeout - leave while */
			/* last bit was 1, process last bit too */
			if (bitcnt==26)
				ttcnt = 0;
			else
				break;
		}

		/* at least 889us pulse */
		bits2 = bits2<<1 | ibit;
		bit2cnt++;
		bitcnt++;
		if (ttcnt>=DELAY_1333US && ttcnt<DELAY_2227US) {
			/* double value its a 1778us pules */
			bits2 = bits2<<1 | ibit;
			bit2cnt++;
			bitcnt++;
		}

		/* check bit order and save sequence */
		if (bit2cnt>=2) {
			if (bit2cnt==3) {
				ibit  = bits2&1;
				bitsh = bits2>>1;
				bits2 = ibit;
			} else {
				bitsh = bits2;
				bits2 = 0;
			}

			bitseq <<= 1;
			if (bitsh==2) {
				bitseq |= 1;
			} else if (bitsh!=1) {
				failure = true;
			}
			bit2cnt -= 2;
		}
	} while(1);

//printf("%lx (t:%d, b2c:%d)\r\n", bitseq, (bitseq&0x1000)>0, bit2cnt);

	if (bitcnt>=26 && !failure) { // no errors occurred
		// save data in fifo
		rc5data[rc5inPtr].toggle = bitseq & 0x800;
		rc5data[rc5inPtr].cmd = (bitseq & 0x3f); // | (bitseq & 0x2000) ? 0 : (1<<6);
		rc5data[rc5inPtr].dev = (bitseq>>6) & 0x1f;
		rc5inPtr = (rc5inPtr + 1) % MAX_RC5_ENTRIES;
	}
}

struct rc5struct getr5data(void)
{
	struct rc5struct rc5entry;

	while (!rc5avail); // waiting for data, if none available
	rc5entry.toggle = rc5data[rc5outPtr].toggle;
	rc5entry.cmd    = rc5data[rc5outPtr].cmd;
	rc5entry.dev    = rc5data[rc5outPtr].dev;
	rc5outPtr       = (rc5outPtr+1) % MAX_RC5_ENTRIES;
	return rc5entry;
}

void main(void)
{
	struct rc5struct rc5entry;

	output_high(DBG_LED);
	delay_ms(500);
	printf("\r\n*** Start RC-5 encoder ***\r\n");
	output_low(DBG_LED);

	input(RCV_RC5);
	ext_int_edge(H_TO_L); // interrupt on falling edge.

	setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); // fosc/4
	enable_interrupts(INT_EXT);
	enable_interrupts(GLOBAL);

	for (;;) {
		rc5entry = getr5data();
		printf("device: 0x%x, command: 0x%x (toggle:%d)\r\n", 
			rc5entry.dev, rc5entry.cmd, rc5entry.toggle);
	}
}
