rc5switch/main.c

162 lines
5.2 KiB
C

/***************************************************************************
* Copyright (C) 04/2011 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License, *
* *
* 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. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <avr/sleep.h>
/*
* attiny24: (no self programming, 2.7V BOD, 8MHz internal RC Osz)
* LFUSE = 0xC2
* HFUSE = 0xDD
* EFUSE = 0xFF
*
* RC5 Input on PB2 (INT0)
* PROGMODE Jumper (active low) on PB1
* OUTPUT (active low) on PB0
*/
/* RC5 bitlength is 1778us +/-10% convert to timer0 ticks (8us) */
#define BITWIDTH (1778 / 8)
#define BITWIDTH_MIN (BITWIDTH - (BITWIDTH / 10))
#define BITWIDTH_MAX (BITWIDTH + (BITWIDTH / 10))
#define RC5_BITCNT 14
#define RC5_CMD_MASK 0x37FF
#define RC5_COMPLETE 0x8000
#define PROGMODE_CHECK() (!(PINB & (1<<PINB1)))
#define OUTPUT_ON() { PORTB &= ~(1<<PORTB0); }
#define OUTPUT_OFF() { PORTB |= (1<<PORTB0); }
struct ee_param {
uint16_t rc5cmd;
uint8_t state;
};
struct ee_param params;
struct ee_param params_in_eeprom EEMEM = {
.rc5cmd = 0x350C,
.state = 0x00,
};
static uint8_t bitcnt;
static volatile uint16_t value;
ISR(EXT_INT0_vect)
{
if (bitcnt == 0)
value = 0;
bitcnt++;
value = (value << 1);
if (!(PINB & (1<<PINB2)))
value |= 0x0001;
/* setup sample window for next edge */
OCR0A = TCNT0 + BITWIDTH_MIN;
OCR0B = TCNT0 + BITWIDTH_MAX;
/* clear and enable COMPA / COMPB */
TIFR0 = (1<<OCF0A) | (1<<OCF0B);
TIMSK0 |= (1<<OCIE0A) | (1<<OCIE0B);
/* disable INT0 until COMPA hits */
GIMSK &= ~(1<<INT0);
}
ISR(TIM0_COMPA_vect)
{
/* clear and enable INT0 */
GIFR = (1<<INTF0);
GIMSK |= (1<<INT0);
}
ISR(TIM0_COMPB_vect)
{
/* disable sample window */
TIMSK0 &= ~((1<<OCIE0A) | (1<<OCIE0B));
/* final bit received? */
if (bitcnt >= RC5_BITCNT)
value |= RC5_COMPLETE;
bitcnt = 0;
}
int main(int argc, char *argv[])
{
DDRB = (1<<PORTB0);
/* pullup on RC5IN and PROGMEM, OUTPUT is low active */
PORTB = (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);
/* INT0: both edges generate interrupt */
MCUCR = (1<<ISC00);
GIMSK = (1<<INT0);
/* Timer0: 8Mhz/64 */
TCCR0B = (1<<CS01) | (1<<CS00);
/* load configuration, restore state */
eeprom_read_block(&params, &params_in_eeprom, sizeof(struct ee_param));
if (params.state) {
OUTPUT_ON();
} else {
OUTPUT_OFF();
}
/* all interrupts can wake up */
set_sleep_mode(SLEEP_MODE_IDLE);
sei();
uint16_t old_value = 0x0000;
while (1) {
/* wait for next interrupt */
sleep_mode();
if (value & RC5_COMPLETE) {
/* PROGMODE jumper set? */
if (PROGMODE_CHECK()) {
params.rc5cmd = (value & RC5_CMD_MASK);
eeprom_write_block(&params, &params_in_eeprom, sizeof(struct ee_param));
/* current command matches stored one */
} else if (params.rc5cmd == (value & RC5_CMD_MASK) && value != old_value) {
if (params.state) {
params.state = 0;
OUTPUT_OFF();
} else {
params.state = 1;
OUTPUT_ON();
}
eeprom_write_block(&params, &params_in_eeprom, sizeof(struct ee_param));
old_value = value;
}
value = 0x0000;
}
}
return 0;
}