initial version

This commit is contained in:
Olaf Rempel 2015-01-10 11:42:36 +01:00
commit f7952dcacb
18 changed files with 2456 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

47
Makefile Normal file
View File

@ -0,0 +1,47 @@
CC := avr-gcc
LD := avr-ld
OBJCOPY := avr-objcopy
OBJDUMP := avr-objdump
SIZE := avr-size
TARGET = funkstuff
SOURCE = $(wildcard *.c)
BUILD_DIR = build
#AVRDUDE_PROG := -c butterfly -b 19200 -P /dev/ttyUSB0
AVRDUDE_PROG := -c avr910 -b 115200 -P /dev/ispprog
#AVRDUDE_PROG := -c dragon_isp -P usb
MCU = atmega168
AVRDUDE_MCU=m168 -F
# ---------------------------------------------------------------------------
CFLAGS = -pipe -g -Os -mmcu=$(MCU) -Wall -fdata-sections -ffunction-sections
CFLAGS += -Wa,-adhlns=$(BUILD_DIR)/$(*D)/$(*F).lst -MMD -MP -MF $(BUILD_DIR)/$(*D)/$(*F).d
LDFLAGS = -Wl,-Map,$(@:.elf=.map),--cref,--relax,--gc-sections
# ---------------------------------------------------------------------------
$(TARGET): $(BUILD_DIR)/$(TARGET).elf
@$(SIZE) -B -x --mcu=$(MCU) $<
$(BUILD_DIR)/$(TARGET).elf: $(patsubst %,$(BUILD_DIR)/%,$(SOURCE:.c=.o))
@echo " Linking file: $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
@$(OBJDUMP) -h -S $@ > $(@:.elf=.lss)
@$(OBJCOPY) -j .text -j .data -O ihex $@ $(@:.elf=.hex)
@$(OBJCOPY) -j .text -j .data -O binary $@ $(@:.elf=.bin)
$(BUILD_DIR)/%.o: %.c $(MAKEFILE_LIST)
@echo " Building file: $<"
@$(shell test -d $(BUILD_DIR)/$(*D) || mkdir -p $(BUILD_DIR)/$(*D))
@$(CC) $(CFLAGS) -o $@ -c $<
include $(shell find $(BUILD_DIR) -name \*.d 2> /dev/null)
clean:
rm -rf $(BUILD_DIR)
install: $(BUILD_DIR)/$(TARGET).elf
avrdude $(AVRDUDE_PROG) -p $(AVRDUDE_MCU) -U flash:w:$(<:.elf=.hex)

61
event.c Normal file
View File

@ -0,0 +1,61 @@
/***************************************************************************
* Copyright (C) 11/2014 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 "event.h"
/* *********************************************************************** */
#define EVENT_COUNT 16
#define TIMER_COUNT 8
static struct event_entry events[EVENT_COUNT];
static volatile uint8_t event_in_idx;
static volatile uint8_t event_out_idx;
/* *********************************************************************** */
void event_queue(uint8_t type, uint8_t num, uint16_t value)
{
uint8_t idx = event_in_idx;
struct event_entry *event = &events[idx++];
if (event->type == EVENT_TYPE_EMPTY)
{
event->type = type;
event->num = num;
event->value = value;
event_in_idx = idx % EVENT_COUNT;
}
} /* event_queue */
struct event_entry * event_get(void)
{
return &events[event_out_idx];
} /* event_get */
void event_clear(void)
{
uint8_t idx = event_out_idx;
events[idx++].type = EVENT_TYPE_EMPTY;
event_out_idx = idx % EVENT_COUNT;
} /* event_clear */

29
event.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef __EVENT_H__
#define __EVENT_H__
/* *********************************************************************** */
struct event_entry
{
uint8_t type;
uint8_t num;
uint16_t value;
};
#define EVENT_TYPE_EMPTY 0x00
#define EVENT_TYPE_GENERIC 0x01
#define EVENT_TYPE_INPUT 0x02
#define EVENT_TYPE_PWM_COMMAND 0x03
#define EVENT_TYPE_PWM_VALUE 0x04
#define EVENT_TYPE_TIMER_SET 0x05
#define EVENT_TYPE_TIMER_ELAPSED 0x06
/* *********************************************************************** */
void event_queue (uint8_t type, uint8_t num, uint16_t value);
struct event_entry * event_get (void);
void event_clear (void);
/* *********************************************************************** */
#endif /* __EVENT_H__ */

200
funk_proto.h Normal file
View File

@ -0,0 +1,200 @@
#ifndef __FUNK_PROTO_H__
#define __FUNK_PROTO_H__
/* *********************************************************************** */
/*
* Protocol:
* - sender will restransmit up to 3 times if no response/confirmation is received after 10ms
* - receiver will ignore messages with corrupt headers/data
* - messages have a seqnum, retransmissions are are detected by same seqnum
* - response with cause "invalid seqnum"
* - initiator increases seqnum and sends request again
* - commands have 2bit req/resp/ind/conf encoding
* - single window
* - multi master should be supported (for lights talking to each other)
*
*
* - firmware has modules, that communicate through "events"
* - INPUT modules (button/quadencoder) send events to all other (!INPUT) modules
* - WORK modules (dimming, store/restore) send event to all other (!INPUT && !WORK) modules
* - OUTPUT modules (pwm channels, digital channels) send event to nobody
* - communication receives all events and can send to anyone
* - configuration in eeprom(?) to enable which events shall be send (and to who)
*
* - or is there a register service?
* - need to detect if device is restarted and register again
*
* - support to switch to bootloader
* - support forward to mpm/twi
*
* - version request?
* - multiple events in one message?
* - analog values
*
*
*/
#define MSG_TYPE_REQUEST 0x00 /* master -> slave req */
#define MSG_TYPE_CONFIRMATION 0x40 /* master -> slave rsp */
#define MSG_TYPE_INDICATION 0x80 /* slave -> master req */
#define MSG_TYPE_RESPONSE 0xC0 /* slave -> master rsp */
#define MSG_TYPE_MASK 0xC0
#define MSG_CMD_MASK 0x3F
#define MSG_CMD_STATUS_REQUEST (MSG_TYPE_REQUEST | 0x01) /* 0x01 */
#define MSG_CMD_STATUS_RESPONSE (MSG_TYPE_RESPONSE | 0x01) /* 0xC1 */
#define MSG_CMD_DESCRIPTION_REQUEST (MSG_TYPE_REQUEST | 0x02) /* 0x02 */
#define MSG_CMD_DESCRIPTION_RESPONSE (MSG_TYPE_RESPONSE | 0x02) /* 0xC2 */
#define MSG_CMD_CONTROL_REQUEST (MSG_TYPE_REQUEST | 0x03) /* 0x03 */
#define MSG_CMD_CONTROL_RESPONSE (MSG_TYPE_RESPONSE | 0x03) /* 0xC3 */
#define MSG_CMD_EVENT_INDICATION (MSG_TYPE_INDICATION | 0x03) /* 0x83 */
#define MSG_CMD_EVENT_CONFIRMATION (MSG_TYPE_CONFIRMATION | 0x03) /* 0x43 */
#define MSG_CMD_SWITCHAPP_REQUEST (MSG_TYPE_REQUEST | 0x20)
#define MSG_CMD_SWITCHAPP_RESPONSE (MSG_TYPE_RESPONSE | 0x20)
#define MSG_CMD_VERSION_REQUEST (MSG_TYPE_REQUEST | 0x21)
#define MSG_CMD_VERSION_RESPONSE (MSG_TYPE_RESPONSE | 0x21)
#define MSG_CMD_CHIPINFO_REQUEST (MSG_TYPE_REQUEST | 0x22)
#define MSG_CMD_CHIPINFO_RESPONSE (MSG_TYPE_RESPONSE | 0x22)
#define MSG_CMD_READ_REQUEST (MSG_TYPE_REQUEST | 0x23)
#define MSG_CMD_READ_RESPONSE (MSG_TYPE_RESPONSE | 0x23)
#define MSG_CMD_WRITE_REQUEST (MSG_TYPE_REQUEST | 0x24)
#define MSG_CMD_WRITE_RESPONSE (MSG_TYPE_RESPONSE | 0x24)
#define CAUSE_SUCCESS 0x00
#define CAUSE_NOT_SUPPORTED 0xF0
#define CAUSE_INVALID_PARAMETER 0xF1
#define CAUSE_UNSPECIFIED_ERROR 0xFF
#define BOOTMODE_BOOTLOADER 0x00
#define BOOTMODE_APPLICATION 0x80
#define MEMTYPE_FLASH 0x01
#define MEMTYPE_EEPROM 0x02
/* *********************************************************************** */
struct rfm12_msg
{
uint8_t command; /* MSG_CMD_* */
uint8_t seqnum;
uint8_t cause; /* CAUSE_* */
union {
/*
* Send a Description Request to a Light
* - type is "Control" or "Event"
* - num is 1-based number of controls or events
*/
struct { /* MSG_CMD_DESCRIPTION_REQUEST */
uint8_t type;
uint8_t num;
} desc_req;
/*
* Send a Description Response to the master
* - type is Control or Event
* - num is 1-based number of controls or event
* - max_num is the max number of controls or events
* - data_type is used in Control_req / Event_ind
* - data_num is used in Control_req / Event_ind
* - data_desc is short ASCII name of that control/event
*/
struct { /* MSG_CMD_DESCRIPTION_RESPONSE */
uint8_t type;
uint8_t num;
uint8_t max_num;
uint8_t data_type;
uint8_t data_num;
uint8_t data_desc[16];
} desc_rsp;
/*
* Send a Control Request to a Light
* - data_type is "button_press", "abs_dim_value", "step_up/down"
* - data_num is button_numer or channel ID of light
* - data is for abs values, width is encoded in ctrl_type
*/
struct { /* MSG_CMD_CONTROL_REQUEST */
uint8_t data_type;
uint8_t data_num;
union {
uint8_t u8[4];
uint16_t u16[2];
uint32_t u32;
} data;
} ctrl_req;
/*
* Response for Control Response
* just repeat the type & num
* cause is in header
*/
struct { /* MSG_CMD_CONTROL_RESPONSE */
uint8_t data_type;
uint8_t data_num;
} ctrl_rsp;
/*
* Send a Event to Master
* - data_type is "button_press", "movement detected", "step up/down"
* - data_num is button number or channel ID
* - data is for abs values, width is encoded in ctrl_type
*/
struct { /* MSG_CMD_EVENT_INDICATION */
uint8_t data_type;
uint8_t data_num;
union {
uint8_t u8[4];
uint16_t u16[2];
uint32_t u32;
} data;
} evt_ind;
/*
* Confirmation of event indication
* just repeat the type & num
* cause is in header
*/
struct { /* MSG_CMD_EVENT_CONFIRMATION */
uint8_t data_type;
uint8_t data_num;
} evt_cnf;
/* bootloader messages */
struct { /* MSG_CMD_SWITCHAPP_REQUEST */
uint8_t app;
} switchapp;
struct { /* MSG_CMD_VERSION_RESPONSE */
uint8_t data[16];
} version;
struct { /* MSG_CMD_CHIPINFO_RESPONSE */
uint8_t data[8];
} chipinfo;
struct { /* MSG_CMD_READ_REQUEST */
uint16_t address;
uint8_t mem_type;
uint8_t size;
} read_req;
struct { /* MSG_CMD_READ_RESPONSE */
uint8_t data[32];
} read_rsp;
struct { /* MSG_CMD_WRITE_REQUEST */
uint16_t address;
uint8_t mem_type;
uint8_t size;
uint8_t data[32];
} write_req;
} p;
};
/* *********************************************************************** */
#endif /* __FUNK_PROTO_H__ */

290
funkstuff.c Normal file
View File

@ -0,0 +1,290 @@
/***************************************************************************
* Copyright (C) 11/2014 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/wdt.h>
#include <avr/pgmspace.h>
#include <string.h> /* memcpy() */
#include "event.h"
#include "input.h"
#include "pwm.h"
#include "rfm12.h"
#include "timer.h"
#include "uart.h"
#include "funk_proto.h"
#include "target.h"
/* *********************************************************************** */
#define LED_RX 0
#define LED_TX 1
static uint8_t led[2];
volatile static uint8_t clock_tick;
ISR(TIMER0_OVF_vect)
{
/* 1ms interrupt */
TCNT0 = TIMER_RELOAD;
clock_tick = 1;
} /* TIMER0_OVF_vect */
void uart_event_handler(struct event_entry *event)
{
/* output all events on UART */
uart_putstr_p(PSTR("evt: "));
uart_put_hex(event->type);
uart_putc(' ');
uart_put_hex(event->num);
uart_putc(' ');
uart_put_hex(event->value >> 8);
uart_put_hex(event->value & 0xFF);
uart_putstr("\r\n");
} /* uart_event_handler */
/*
* For newer devices the watchdog timer remains active even after a
* system reset. So disable it as soon as possible.
* automagically called on startup
*/
void disable_wdt_timer(void) __attribute__((naked, section(".init3")));
void disable_wdt_timer(void)
{
MCUSR = 0;
WDTCSR = (1<<WDCE) | (1<<WDE);
WDTCSR = (0<<WDE);
} /* disable_wdt_timer */
int main(void)
{
LED_INIT();
/* timer0, FCPU/64, overflow interrupt */
TCCR0B = (1<<CS01) | (1<<CS00);
TIMSK0 = (1<<TOIE0);
uart_init();
input_init();
pwm_init();
rfm12_init(RFM12_ADDRESS);
sei();
uart_putstr_p(PSTR("init\r\n"));
while (1)
{
if (clock_tick)
{
clock_tick = 0;
if (led[LED_RX] > 0)
{
led[LED_RX]--;
LED_GN_ON();
}
else
{
LED_GN_OFF();
}
if (led[LED_TX] > 0)
{
led[LED_TX]--;
LED_RT_ON();
}
else
{
LED_RT_OFF();
}
/* do periodic work (wait for 5 ticks silence before start TX) */
rfm12_tick(5);
input_tick();
timer_tick();
}
struct event_entry *event = event_get();
if (event->type != EVENT_TYPE_EMPTY)
{
uart_event_handler(event);
switch (event->type)
{
case EVENT_TYPE_PWM_COMMAND:
case EVENT_TYPE_PWM_VALUE:
pwm_event_handler(event);
break;
case EVENT_TYPE_INPUT:
if (event->num == EVENT_NUM_INPUT_QUAD)
{
if (event->value == EVENT_VALUE_INPUT_QUAD_DEC)
{
event_queue(EVENT_TYPE_PWM_COMMAND,
EVENT_NUM_PWM_CH1,
EVENT_VALUE_PWM_DEC
);
}
else if (event->value == EVENT_VALUE_INPUT_QUAD_INC)
{
event_queue(EVENT_TYPE_PWM_COMMAND,
EVENT_NUM_PWM_CH1,
EVENT_VALUE_PWM_INC
);
}
}
else if (event->num == EVENT_NUM_INPUT_BUTTON)
{
if (event->value == EVENT_VALUE_INPUT_BUTTON_PRESSED)
{
event_queue(EVENT_TYPE_PWM_COMMAND,
EVENT_NUM_PWM_CH1,
PWM_MODE_TOGGLE
);
}
}
else if (event->num == EVENT_NUM_INPUT_DOOR)
{
if (event->value == EVENT_VALUE_INPUT_DOOR_CLOSED)
{
event_queue(EVENT_TYPE_PWM_COMMAND,
EVENT_NUM_PWM_CH0,
EVENT_VALUE_PWM_KEEP
);
event_queue(EVENT_TYPE_TIMER_SET,
TIMER_DOOR_CLOSE_DELAY,
2000
);
}
else if (event->value == EVENT_VALUE_INPUT_DOOR_OPEN)
{
event_queue(EVENT_TYPE_PWM_COMMAND,
EVENT_NUM_PWM_CH0,
EVENT_VALUE_PWM_FADE_MAX
);
event_queue(EVENT_TYPE_TIMER_SET,
TIMER_DOOR_CLOSE_DELAY,
0
);
}
}
break;
case EVENT_TYPE_TIMER_SET:
timer_event_handler(event);
break;
case EVENT_TYPE_TIMER_ELAPSED:
if (event->num == TIMER_DOOR_CLOSE_DELAY)
{
event_queue(EVENT_TYPE_PWM_COMMAND,
EVENT_NUM_PWM_CH0,
EVENT_VALUE_PWM_FADE_MIN
);
}
break;
default:
break;
}
event_clear();
}
/* get RX data */
struct rfm12_packet *req_pkt = rfm12_get_rxpkt();
struct rfm12_packet *rsp_pkt = rfm12_get_txpkt();
if (req_pkt != NULL)
{
led[LED_RX] = 5;
if (rsp_pkt == NULL)
{
rfm12_clear_rx();
continue;
}
struct rfm12_msg *req_msg = (struct rfm12_msg *)req_pkt->data;
struct rfm12_msg *rsp_msg = (struct rfm12_msg *)rsp_pkt->data;
/* retransmitted request -> retransmit response */
if ((req_pkt->source_address == rsp_pkt->dest_address) &&
((req_msg->command & MSG_CMD_MASK) == (rsp_msg->command & MSG_CMD_MASK)) &&
(req_msg->seqnum == rsp_msg->seqnum)
)
{
/* RX packet no longer needed */
rfm12_clear_rx();
/* transmit response */
if (rfm12_start_tx())
{
led[LED_TX] = 5;
}
continue;
}
rsp_pkt->dest_address = req_pkt->source_address;
rsp_pkt->data_length = 3;
rsp_msg->command = req_msg->command | MSG_TYPE_RESPONSE;
rsp_msg->seqnum = req_msg->seqnum;
rsp_msg->cause = CAUSE_SUCCESS;
switch (req_msg->command)
{
case MSG_CMD_SWITCHAPP_REQUEST:
if (req_msg->p.switchapp.app == BOOTMODE_BOOTLOADER)
{
wdt_enable(WDTO_15MS);
}
else if (req_msg->p.switchapp.app != BOOTMODE_APPLICATION)
{
rsp_msg->cause = CAUSE_INVALID_PARAMETER;
}
break;
default:
rsp_msg->cause = CAUSE_NOT_SUPPORTED;
break;
}
/* RX packet no longer needed */
rfm12_clear_rx();
/* transmit response */
if (rfm12_start_tx())
{
led[LED_TX] = 5;
}
}
}
return 0;
} /* main */

181
input.c Normal file
View File

@ -0,0 +1,181 @@
/***************************************************************************
* Copyright (C) 11/2014 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 "input.h"
#include "event.h"
/* *********************************************************************** */
static uint8_t pcint_old;
static uint8_t quad_state;
static uint8_t quad_state_old;
static uint8_t debounce_timer[2];
/* *********************************************************************** */
ISR(PCINT1_vect)
{
uint8_t pcint_new = PINC & PCMSK1;
uint8_t pcint = pcint_new ^ pcint_old;
uint8_t quad_state_new = quad_state;
/* quadruple decoder A changed */
if (pcint & (1<<PINC0))
{
/* positive edge */
if (pcint_new & (1<<PINC0))
{
if ((quad_state == 0) || (quad_state == 2))
{
quad_state_new++;
}
}
/* negative edge */
else
{
if ((quad_state == 1) || (quad_state == 3))
{
quad_state_new--;
}
}
}
/* quadruple decoder B changed */
else if (pcint & (1<<PINC1))
{
/* positive edge */
if (pcint_new & (1<<PINC1))
{
if ((quad_state == 0) || (quad_state == 1))
{
quad_state_new += 2;
}
}
/* negative edge */
else
{
if ((quad_state == 2) || (quad_state == 3))
{
quad_state_new -= 2;
}
}
}
/* quadruple decoder switch changed */
else if (pcint & (1<<PINC2))
{
if (debounce_timer[0] == 0)
{
debounce_timer[0] = 60; /* ~60ms */
event_queue(EVENT_TYPE_INPUT,
EVENT_NUM_INPUT_BUTTON,
!!(pcint_new & (1<<PINC2))
);
}
}
/* door switch changed */
else if (pcint & (1<<PINC3))
{
if (debounce_timer[1] == 0)
{
debounce_timer[1] = 60; /* ~60ms */
event_queue(EVENT_TYPE_INPUT,
EVENT_NUM_INPUT_DOOR,
!!(pcint_new & (1<<PINC3))
);
}
}
/* one revolution completed? */
if ((quad_state_new == 3) && (quad_state_old == 0))
{
/* counter clock wise */
if (quad_state == 1)
{
event_queue(EVENT_TYPE_INPUT,
EVENT_NUM_INPUT_QUAD,
EVENT_VALUE_INPUT_QUAD_DEC
);
}
/* clock wise */
else if (quad_state == 2)
{
event_queue(EVENT_TYPE_INPUT,
EVENT_NUM_INPUT_QUAD,
EVENT_VALUE_INPUT_QUAD_INC
);
}
}
/* remember every two states */
if ((quad_state_new == 0) || (quad_state_new == 3))
{
quad_state_old = quad_state_new;
}
quad_state = quad_state_new;
pcint_old = pcint_new;
} /* PCINT1_vect */
void input_tick(void)
{
if (debounce_timer[0] > 0)
{
debounce_timer[0]--;
}
if (debounce_timer[1] > 0)
{
debounce_timer[1]--;
}
} /* input_tick */
void input_init(void)
{
/* pullup on all inputs */
DDRC &= ~((1<<PORTC3) | (1<<PORTC2) | (1<<PORTC1) | (1<<PORTC0));
PORTC |= (1<<PORTC3) | (1<<PORTC2) | (1<<PORTC1) | (1<<PORTC0);
/* Pinchange Interrupts enabled */
PCMSK1 = (1<<PCINT11) | (1<<PCINT10) | (1<<PCINT9) | (1<<PCINT8);
PCICR = (1<<PCIE1);
/* initial pin interrupt state */
pcint_old = PINC & PCMSK1;
/* initial quadruple decoder state */
quad_state = PINC & ((1<<PINC0) | (1<<PINC1));
/* send button state */
event_queue(EVENT_TYPE_INPUT,
EVENT_NUM_INPUT_BUTTON,
!!(pcint_old & (1<<PINC2))
);
/* send door state */
event_queue(EVENT_TYPE_INPUT,
EVENT_NUM_INPUT_DOOR,
!!(pcint_old & (1<<PINC3))
);
} /* input_init */

24
input.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __INPUT_H__
#define __INPUT_H__
/* *********************************************************************** */
#define EVENT_NUM_INPUT_QUAD 0x01
#define EVENT_NUM_INPUT_BUTTON 0x02
#define EVENT_NUM_INPUT_DOOR 0x03
#define EVENT_VALUE_INPUT_QUAD_DEC 0x01
#define EVENT_VALUE_INPUT_QUAD_INC 0x02
#define EVENT_VALUE_INPUT_BUTTON_RELEASED 0x00
#define EVENT_VALUE_INPUT_BUTTON_PRESSED 0x01
#define EVENT_VALUE_INPUT_DOOR_OPEN 0x00
#define EVENT_VALUE_INPUT_DOOR_CLOSED 0x01
/* *********************************************************************** */
void input_init(void);
void input_tick(void);
/* *********************************************************************** */
#endif /* __INPUT_H__ */

202
pwm.c Normal file
View File

@ -0,0 +1,202 @@
/***************************************************************************
* Copyright (C) 11/2014 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/pgmspace.h>
#include "event.h"
#include "pwm.h"
#include "target.h"
/* *********************************************************************** */
const uint16_t pwmtable[128] PROGMEM =
{
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5,
6, 6, 7, 8, 9, 9, 10, 11, 12, 14, 15, 16, 18, 20, 22, 24, 26,
28, 31, 34, 37, 40, 44, 48, 53, 58, 63, 69, 75, 82, 90, 98,
107, 116, 127, 139, 151, 165, 180, 196, 214, 234, 255, 278,
303, 331, 361, 394, 430, 469, 511, 557, 608, 663, 723, 789,
860, 938, 1023, 1116, 1217, 1327, 1447, 1578, 1721, 1877,
2047, 2232, 2434, 2655, 2895, 3157, 3443, 3755, 4095, 4466,
4870, 5311, 5792, 6316, 6888, 7511, 8191, 8932, 9741, 10623,
11584, 12633, 13776, 15023, 16383, 17866, 19483, 21246, 23169,
25267, 27553, 30047, 32767, 35733, 38967, 42494, 46340, 50534,
55108, 60096, 65535
};
#define PWMTABLE_SIZE (sizeof(pwmtable) / sizeof(uint16_t))
static uint8_t pwm_index[2];
static uint8_t pwm_setpoint[2];
static uint8_t pwm_setpoint_save[2];
/* *********************************************************************** */
void pwm_event_handler(struct event_entry *event)
{
uint8_t channel = event->num;
if (event->type == EVENT_TYPE_PWM_COMMAND)
{
switch (event->value)
{
case EVENT_VALUE_PWM_KEEP:
pwm_setpoint[channel] = pwm_index[channel];
break;
/* increment PWM */
case EVENT_VALUE_PWM_INC:
if (pwm_setpoint[channel] < (PWMTABLE_SIZE -1))
{
pwm_setpoint[channel]++;
}
break;
/* decrement PWM */
case EVENT_VALUE_PWM_DEC:
if (pwm_setpoint[channel] > 0)
{
pwm_setpoint[channel]--;
}
break;
/* fade to min */
case EVENT_VALUE_PWM_FADE_MIN:
pwm_setpoint[channel] = 0;
break;
/* fade to max */
case EVENT_VALUE_PWM_FADE_MAX:
pwm_setpoint[channel] = (PWMTABLE_SIZE -1);
break;
// FIXME: move to some other module?
case PWM_MODE_TOGGLE:
if (pwm_setpoint[channel] > 5)
{
pwm_setpoint_save[channel] = pwm_setpoint[channel];
pwm_setpoint[channel] = 0;
}
else
{
pwm_setpoint[channel] = pwm_setpoint_save[channel];
}
break;
default:
break;
}
PWM_TIM_ENABLE();
}
else if (event->type == EVENT_TYPE_PWM_VALUE)
{
pwm_setpoint[event->num] = event->value;
PWM_TIM_ENABLE();
}
} /* pwm_event_handler */
static void pwm_update(uint8_t channel)
{
if ((pwm_setpoint[channel] > pwm_index[channel]) &&
(pwm_index[channel] < (PWMTABLE_SIZE -1))
)
{
pwm_index[channel]++;
}
else if ((pwm_setpoint[channel] < pwm_index[channel]) &&
(pwm_index[channel] > 0)
)
{
pwm_index[channel]--;
}
/* if PWM is zero, disable output */
if (pwm_index[channel] == 0)
{
if (channel == 0)
{
PWM_CH0_OFF();
}
else
{
PWM_CH1_OFF();
}
}
/* if PWM is max, enable output */
else if (pwm_index[channel] == (PWMTABLE_SIZE -1))
{
if (channel == 0)
{
PWM_CH0_ON();
}
else
{
PWM_CH1_ON();
}
}
/* else load new PWM into timer */
else
{
if (channel == 0)
{
PWM_CH0_PWM(pgm_read_word(&pwmtable[pwm_index[0]]));
}
else
{
PWM_CH1_PWM(pgm_read_word(&pwmtable[pwm_index[1]]));
}
}
} /* pwm_set */
ISR(PWM_TIM_VECT)
{
static uint8_t delay;
delay++;
if (delay == 4)
{
delay = 0;
/* update PWM values */
pwm_update(0);
pwm_update(1);
/* disable timer if both channels are ON or OFF */
if (PWM_TIM_CHECK())
{
PWM_TIM_ENABLE();
}
else
{
PWM_TIM_DISABLE();
}
}
} /* TIM1_OVF_vect */
void pwm_init(void)
{
PWM_TIM_INIT();
} /* pwm_init */

28
pwm.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef __PWM_H__
#define __PWM_H__
#include "event.h"
/* *********************************************************************** */
#define EVENT_NUM_PWM_CH0 0x00
#define EVENT_NUM_PWM_CH1 0x01
#define EVENT_VALUE_PWM_KEEP 0x00
#define EVENT_VALUE_PWM_INC 0x01
#define EVENT_VALUE_PWM_DEC 0x02
#define EVENT_VALUE_PWM_FADE_MIN 0x03
#define EVENT_VALUE_PWM_FADE_MAX 0x04
#define PWM_MODE_TOGGLE 6
#define PWM_VALUE_MAX 128
/* *********************************************************************** */
void pwm_init (void);
void pwm_event_handler (struct event_entry *event);
/* *********************************************************************** */
#endif /* __PWM_H__ */

438
rfm12.c Normal file
View File

@ -0,0 +1,438 @@
/***************************************************************************
* Copyright (C) 11/2014 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/pgmspace.h>
#include <util/crc16.h>
#include "target.h"
#include "rfm12_hw.h"
#include "rfm12.h"
/* *********************************************************************** */
#define RFM12_PREAMBLE 0xAA
#define RFM12_SYNC_LSB 0xD4
#define RFM12_SYNC_MSB 0x2D
#define RFM12_BASEBAND RFM12_BAND_868
#define RFM12_XTAL_LOAD RFM12_XTAL_11_5PF
#define RFM12_FREQUENCY_CALC RFM12_FREQUENCY_CALC_868
#define RFM12_FREQUENCY 869800000UL
#define RFM12_DATARATE RFM12_DATARATE_CALC_HIGH(19200.0)
#define RFM12_FILTER_BW RFM12_RXCTRL_BW_400
#define RFM12_LNA_GAIN RFM12_RXCTRL_LNA_6
#define RFM12_RSSI_THRESHOLD RFM12_RXCTRL_RSSI_79
#define RFM12_POWER RFM12_TXCONF_POWER_0
#define RFM12_FSK_SHIFT 125000
/* *********************************************************************** */
#define RFM12_DATA_STATE_FREE 0x00
#define RFM12_DATA_STATE_USED 0x01
struct rfm12_data
{
uint8_t state; /* RFM12_DATA_STATE_* */
struct rfm12_packet packet;
};
#define RFM12_CTX_STATE_INAVTIVE 0x00
#define RFM12_CTX_STATE_RX_IDLE 0x01
#define RFM12_CTX_STATE_RX_ACTIVE 0x02
#define RFM12_CTX_STATE_TX_ACTIVE 0x03
struct rfm12_context
{
uint8_t state; /* RFM12_CTX_STATE_* */
uint8_t ch_free_ticks; /* number of ticks channel is unused */
uint8_t own_address;
uint8_t rx_idx_in; /* pkt receiving */
uint8_t rx_idx_out; /* pkt given to app */
uint8_t rx_checksum; /* receive checksum */
uint8_t rx_data_idx; /* byte position inside rx[].packet */
uint8_t tx_data_idx; /* byte position inside tx.packet */
struct rfm12_data rx[2];
struct rfm12_data tx;
};
static struct rfm12_context rfm12_ctx;
/* *********************************************************************** */
static uint16_t rfm12_data(uint16_t txdata)
{
uint16_t retval;
RFM12_CS_ACTIVE();
SPDR = (txdata >> 8);
while (!(SPSR & (1<<SPIF)));
retval = (SPDR << 8);
SPDR = (txdata & 0xFF);
while (!(SPSR & (1<<SPIF)));
retval |= SPDR;
RFM12_CS_INACTIVE();
return retval;
} /* rfm12_data */
ISR(RFM12_INT_VECT)
{
uint16_t rfm12_status;
uint8_t rfm12_reset_fifo = 0;
uint8_t data;
/* disable interrupt */
RFM12_INT_OFF();
/* clear interrupt flag */
RFM12_INT_CLEAR();
/* read upper status byte (interrupt flags) */
rfm12_status = rfm12_data(RFM12_CMD_STATUS);
/* FIFO interrupt */
if (rfm12_status & RFM12_STATUS_FFIT)
{
switch (rfm12_ctx.state)
{
case RFM12_CTX_STATE_RX_IDLE:
data = rfm12_data(RFM12_CMD_READ);
/* check if buffer is free */
if (rfm12_ctx.rx[rfm12_ctx.rx_idx_in].state != RFM12_DATA_STATE_FREE)
{
rfm12_reset_fifo = 1;
break;
}
/* store first header byte */
rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.sync[RFM12_PKT_SYNC_SIZE] = data;
/* store state */
rfm12_ctx.state = RFM12_CTX_STATE_RX_ACTIVE;
rfm12_ctx.rx_data_idx = 1;
rfm12_ctx.rx_checksum = data;
break;
case RFM12_CTX_STATE_RX_ACTIVE:
data = rfm12_data(RFM12_CMD_READ);
/* check buffer size */
if (rfm12_ctx.rx_data_idx >= (RFM12_PKT_HEADER_SIZE + RFM12_PKT_MAX_DATA_SIZE))
{
rfm12_reset_fifo = 1;
break;
}
/* store data */
(& rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.sync[RFM12_PKT_SYNC_SIZE])[rfm12_ctx.rx_data_idx++] = data;
/* calculate checksum */
rfm12_ctx.rx_checksum ^= data;
/* check if header address, data_length and checksum are ok */
if ((rfm12_ctx.rx_data_idx == RFM12_PKT_HEADER_SIZE) &&
((rfm12_ctx.rx_checksum != 0xFF) ||
(rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.dest_address != rfm12_ctx.own_address) ||
(rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.source_address == rfm12_ctx.own_address) ||
(rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.data_length > RFM12_PKT_MAX_DATA_SIZE)
))
{
rfm12_reset_fifo = 1;
break;
}
/* packet complete? */
if (rfm12_ctx.rx_data_idx == (RFM12_PKT_HEADER_SIZE + rfm12_ctx.rx[rfm12_ctx.rx_idx_in].packet.data_length))
{
/* mark buffer as full */
rfm12_ctx.rx[rfm12_ctx.rx_idx_in].state = RFM12_DATA_STATE_USED;
/* switch to other buffer */
rfm12_ctx.rx_idx_in ^= 1;
/* receiving is complete, reset fifo anyway */
rfm12_reset_fifo = 1;
}
break;
case RFM12_CTX_STATE_TX_ACTIVE:
/* send one additional byte! (<= not <) */
if (rfm12_ctx.tx_data_idx <= (RFM12_PKT_SYNC_SIZE + RFM12_PKT_HEADER_SIZE + rfm12_ctx.tx.packet.data_length))
{
rfm12_data(RFM12_CMD_TX | rfm12_ctx.tx.packet.sync[rfm12_ctx.tx_data_idx++]);
}
else
{
/* enable RX */
rfm12_data(RFM12_CMD_PWRMGT | RFM12_PWRMGT_ER | RFM12_PWRMGT_DC);
/* TX dummy byte to clear interrupt */
rfm12_data(RFM12_CMD_TX | RFM12_PREAMBLE);
/* mark buffer as empty */
rfm12_ctx.tx.state = RFM12_DATA_STATE_FREE;
/* transmit is complete, reset fifo */
rfm12_reset_fifo = 1;
}
break;
default:
rfm12_reset_fifo = 1;
break;
}
if (rfm12_reset_fifo)
{
/* flush fifo and wait for sync pattern */
rfm12_data(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4));
rfm12_data(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4) | RFM12_FIFORESET_FF);
/* wait for RX data */
rfm12_ctx.state = RFM12_CTX_STATE_RX_IDLE;
}
}
/* enable interrupt again */
RFM12_INT_ON();
} /* INT1_vect */
void rfm12_tick(uint8_t channel_free_time)
{
uint16_t status;
/* receiver not idle, come back later */
if (rfm12_ctx.state != RFM12_CTX_STATE_RX_IDLE)
{
return;
}
RFM12_INT_OFF();
status = rfm12_data(RFM12_CMD_STATUS);
RFM12_INT_ON();
/* check if module receives a carrier */
if (status & RFM12_STATUS_RSSI)
{
rfm12_ctx.ch_free_ticks = 0;
return;
}
else if (rfm12_ctx.ch_free_ticks <= channel_free_time)
{
rfm12_ctx.ch_free_ticks++;
return;
}
if (rfm12_ctx.tx.state == RFM12_DATA_STATE_USED)
{
RFM12_INT_OFF();
/* disable receiver */
rfm12_data(RFM12_CMD_PWRMGT | RFM12_PWRMGT_DC);
/* put preamble in fifo */
rfm12_data(RFM12_CMD_TX | RFM12_PREAMBLE);
rfm12_data(RFM12_CMD_TX | RFM12_PREAMBLE);
/* start TX */
rfm12_data(RFM12_CMD_PWRMGT | RFM12_PWRMGT_ET | RFM12_PWRMGT_DC);
/* change state */
rfm12_ctx.state = RFM12_CTX_STATE_TX_ACTIVE;
rfm12_ctx.tx_data_idx = 0;
RFM12_INT_ON();
}
} /* rfm12_tick */
static uint16_t rfm12_calc_crc(const struct rfm12_packet *pkt)
{
uint16_t crc = 0x0000;
uint8_t i;
const uint8_t *tmp = pkt->data;
for (i = 0; i < pkt->data_length; i++)
crc = _crc_ccitt_update(crc, *tmp++);
return crc;
} /* pkt_check_crc */
struct rfm12_packet * rfm12_get_txpkt(void)
{
if (rfm12_ctx.tx.state != RFM12_DATA_STATE_FREE)
{
return (void *)0;
}
return &rfm12_ctx.tx.packet;
} /* rfm12_get_txpkt */
uint8_t rfm12_start_tx(void)
{
struct rfm12_packet *pkt = &rfm12_ctx.tx.packet;
if ((rfm12_ctx.tx.state != RFM12_DATA_STATE_FREE) &&
(pkt->data_length > RFM12_PKT_MAX_DATA_SIZE)
)
{
return 0;
}
/* calculate data crc */
uint16_t *data_crc = (uint16_t *)(pkt->data + pkt->data_length);
*data_crc = rfm12_calc_crc(pkt);
pkt->data_length += 2;
/* setup packet */
pkt->sync[0] = RFM12_SYNC_MSB;
pkt->sync[1] = RFM12_SYNC_LSB;
pkt->source_address = rfm12_ctx.own_address;
pkt->header_checksum = pkt->dest_address ^ pkt->source_address ^ pkt->data_length ^ 0xFF;
/* change state */
rfm12_ctx.tx.state = RFM12_DATA_STATE_USED;
return 1;
} /* rfm12_start_tx */
struct rfm12_packet * rfm12_get_rxpkt(void)
{
if (rfm12_ctx.rx[rfm12_ctx.rx_idx_out].state != RFM12_DATA_STATE_USED)
{
return (void *)0;
}
/* calculate data crc */
struct rfm12_packet *pkt = &rfm12_ctx.rx[rfm12_ctx.rx_idx_out].packet;
pkt->data_length -= 2;
uint16_t *data_crc = (uint16_t *)(pkt->data + pkt->data_length);
if (*data_crc != rfm12_calc_crc(pkt))
{
rfm12_clear_rx();
return (void *)0;
}
return pkt;
} /* rfm12_get_rxpkt */
void rfm12_clear_rx(void)
{
/* mark buffer as empty */
rfm12_ctx.rx[rfm12_ctx.rx_idx_out].state = RFM12_DATA_STATE_FREE;
/* switch to other buffer */
rfm12_ctx.rx_idx_out ^= 1;
} /* rfm12_clear_rx */
static const uint16_t init_cmds[] PROGMEM =
{
/* set power default state (disable clock output) */
(RFM12_CMD_PWRMGT | RFM12_PWRMGT_DC),
/* dummy write after power management change, prevent lockup of module */
(RFM12_CMD_TX),
/* enable internal data register and fifo, setup selected band */
(RFM12_CMD_CFG | RFM12_CFG_EL | RFM12_CFG_EF | RFM12_BASEBAND | RFM12_XTAL_LOAD),
/* set frequency */
(RFM12_CMD_FREQUENCY | RFM12_FREQUENCY_CALC(RFM12_FREQUENCY)),
/* set data rate */
(RFM12_CMD_DATARATE | RFM12_DATARATE),
/* set rx parameters: vdi-out, bandwidth, LNA, RSSI */
(RFM12_CMD_RXCTRL | RFM12_RXCTRL_P16_VDI | RFM12_RXCTRL_VDI_FAST | RFM12_FILTER_BW | RFM12_LNA_GAIN | RFM12_RSSI_THRESHOLD),
/* automatic clock lock control, digital Filter,
* Data quality detector value 3, slow clock recovery lock
*/
(RFM12_CMD_DATAFILTER | RFM12_DATAFILTER_AL | 3),
/* 2 Byte Sync Pattern, Start fifo fill when sychron pattern received,
* disable sensitive reset, Fifo filled interrupt at 8 bits
*/
(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4)),
/* set AFC to automatic, (+4 or -3)*2.5kHz Limit, fine mode, active and enabled */
(RFM12_CMD_AFC | RFM12_AFC_AUTO_KEEP | RFM12_AFC_LIMIT_4 | RFM12_AFC_FI | RFM12_AFC_OE | RFM12_AFC_EN),
/* set TX Power, frequency shift */
(RFM12_CMD_TXCONF | RFM12_POWER | RFM12_TXCONF_FS_CALC(RFM12_FSK_SHIFT)),
/* disable low dutycycle mode */
(RFM12_CMD_DUTYCYCLE),
/* disable wakeup timer */
(RFM12_CMD_WAKEUP),
/* enable rf receiver chain */
(RFM12_CMD_PWRMGT | RFM12_PWRMGT_ER | RFM12_PWRMGT_DC),
/* flush fifo, start receiving */
(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4)),
(RFM12_CMD_FIFORESET | RFM12_FIFORESET_DR | (8<<4) | RFM12_FIFORESET_FF),
};
void rfm12_init(uint8_t own_address)
{
uint8_t i;
/* init chipselect GPIO */
RFM12_CS_INIT();
RFM12_CS_INACTIVE();
/* init internal SPI */
RFM12_SPI_INIT();
/* send init commands */
for (i = 0; i < ( sizeof(init_cmds) / 2) ; i++)
{
rfm12_data(pgm_read_word(&init_cmds[i]));
}
/* store own address */
rfm12_ctx.own_address = own_address;
rfm12_ctx.state = RFM12_CTX_STATE_RX_IDLE;
/* initalize & activate interrupt */
RFM12_INT_INIT();
RFM12_INT_CLEAR();
RFM12_INT_ON();
} /* rfm12_init */

39
rfm12.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __RFM12_H__
#define __RFM12_H__
/* ************************************************************************ */
#define RFM12_PKT_SYNC_SIZE 2
#define RFM12_PKT_HEADER_SIZE 4
#define RFM12_PKT_MAX_DATA_SIZE 42
struct rfm12_packet
{
/* tx-only sync bytes */
uint8_t sync[RFM12_PKT_SYNC_SIZE];
/* Header */
uint8_t dest_address;
uint8_t source_address;
uint8_t data_length;
uint8_t header_checksum;
/* Data */
uint8_t data[RFM12_PKT_MAX_DATA_SIZE];
uint16_t data_crc;
};
/* ************************************************************************ */
void rfm12_init (uint8_t own_address);
void rfm12_tick (uint8_t channel_free_time);