/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See the file COPYING * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (C) 2010 Frank van Maarseveen */ #include #include #include "common.h" #include "bits.h" #include "types.h" #include "timer0.h" #include "hardware.h" #include "receive.h" /* * Define the clock period T expressed in 16us ticks. Constraints: * - T must be >= 4 * RX_INPUT_TICKS, i.e. the sampling function should * delay at most T/4 * - 2T must be <= 255 * * T==16 causes asymmetrical tolerance: transmitter may go 12% faster but * not slower. By picking 20 (i.e. 5 * RX_INPUT_TICKS) here the transmitter * works sufficiently in the range T=16..24 inclusive, see T in transmit.c */ #define T 20 /* 320 us, 3125Hz */ /* * Packet layout * ============= * * Since it's used for reprogramming flash a packet must contain at least * - 1 start byte for synchronization and early error detection. * - packet data: * - 1 byte page number * - 1 byte total number of (consecutive) pages to reprogram. * - 64 byte data (SPM page size is 32 words) * - 16 bit CCITT CRC (includes start byte). */ uint8_t rx_buffer[69]; /* * Discharge the LED capacity and give it RX_INPUT_TICKS time to charge up * using the tiny current it produces when enough light falls on it. The * voltage reached maxes at a bandgap related value which happens to be enough * for a pin change as long as Vcc is between 2 and 3V and when using a yellow * LED (e.g. OLS-330HY). */ #define RX_INPUT_TICKS 4 /* 64 us */ __attribute__((noinline)) bool_t rx_input(void) { uint8_t sreg, start, t; bool_t b; CLR(LED); saveicli(sreg); OUTPUT(LED); /* discharge */ OUTPUT(LED); /* nop delay */ INPUT(LED); start = t0_ticks(0); restorei(sreg); do { saveicli(sreg); b = TST(LED); t = t0_ticks(start); restorei(sreg); } while (!b && t < RX_INPUT_TICKS); return b; } /* * Wait at most 1.5T for the specified input level. The input level * is returned upon success. */ __attribute__((noinline)) static bool_t rx_wait(bool_t level) { uint8_t start, t; bool_t b; start = t0_ticks(0); do { b = rx_input(); t = t0_ticks(start); if (t >= (uint8_t)(1.5 * T + 0.5)) break; } while (b != level); return b; } /* * Bit encoding/decoding (Manchester/BPSK) * ======================================= * * Initial level is '1' (LED on). The first transition is for the first bit * which by definition must be zero: At T/2 of every clock cycle there is a * transition: up means bit value '1', down means bit value '0'. The bit stream * ends when there is no transition during 1.5T. The clock cycle transition * itself is missing when there is a phase change. A phase change * indicates that the next bit will be the last one inverted. Bit and byte * order (for crc) is BE. * _ _ _ _ _ _ _ _ _ _ _ _ _ _ * clock | |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ * cycle | | | | | | | | | | | | | | * __________ _ _ _ _ _ _ ___ ___ * LED |_| |_| |_| |___| |_| |_| |_| |___| |________ * * data 0 0 0 0 1 1 1 1 0 1 0 * * Here the start byte has value 0x0F and the payload consists of '010' * (for simplicity, must be a multiple of 8 bits). Notice that the final * clock cycle transition is optional. * * The rx_packet() function implements a one-shot packet reception. It will * perform some basic checks and upon success return the packet length * (excluding stop-bit byte). Return value is -1 on failure. */ int8_t rx_packet(void) { uint8_t start, t, byte; uint8_t bitcount; bool_t clockedge, level, b, b16; int8_t len; uint16_t crc; level = rx_wait(false); start = t0_ticks(0); if (level) goto err; clockedge = false; len = 0; crc = 0; byte = 0; bitcount = 1; while (1) { b = rx_wait(!level); t = t0_ticks(start); if (b == level) break; start = t0_ticks(0); level = b; if (t < (uint8_t)(0.75 * T + 0.5)) { clockedge = !clockedge; if (clockedge) continue; } if (clockedge) goto err; if (!len && b != (bitcount >= 4)) /* syn must be 0x0F */ goto err; byte = (byte << 1) | b; if (++bitcount == 8) { if (len == sizeof (rx_buffer)) goto err; rx_buffer[len++] = byte; bitcount = 0; } b16 = crc & 0x8000 ? true : false; crc = (crc << 1) + b; if (b16) crc ^= 0x11021; /* x16 + x12 + x5 + x0 (CRC-CCITT) */ } if (bitcount || crc) goto err; if (len < 3) /* syn and 2 crc bytes at minimum */ goto err; return len; err: return -1; }