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);
struct rfm12_packet * rfm12_get_txpkt (void);
uint8_t rfm12_start_tx (void);
struct rfm12_packet * rfm12_get_rxpkt (void);
void rfm12_clear_rx (void);
/* ************************************************************************ */
#endif /* __RFM12_H__ */

575
rfm12_hw.h Normal file
View File

@ -0,0 +1,575 @@
/**** RFM 12 library for Atmel AVR Microcontrollers *******
*
* This software 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; either version 2 of the License,
* or (at your option) any later version.
*
* This software 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 software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* @author Peter Fuhrmann, Hans-Gert Dahmen, Soeren Heisrath
*/
/* Configuration setting command
Bit el enables the internal data register.
Bit ef enables the FIFO mode. If ef=0 then DATA (pin 6) and DCLK (pin 7) are used for data and data clock output.
x3 x2 x1 x0 Crystal Load Capacitance [pF]
0 0 0 0 8.5
0 0 0 1 9.0
0 0 1 0 9.5
0 0 1 1 10.0
....
1 1 1 0 15.5
1 1 1 1 16.0
*/
# define RFM12_CMD_CFG 0x8000
# define RFM12_CFG_EL 0x80
# define RFM12_CFG_EF 0x40
# define RFM12_CFG_BAND_MASK 0x30
# define RFM12_BAND_315 0x00
# define RFM12_BAND_433 0x10
# define RFM12_BAND_868 0x20
# define RFM12_BAND_915 0x30
# define RFM12_CFG_XTAL_MASK 0x0F
# define RFM12_XTAL_8_5PF 0x00
# define RFM12_XTAL_9_0PF 0x01
# define RFM12_XTAL_9_5PF 0x02
# define RFM12_XTAL_10_0PF 0x03
# define RFM12_XTAL_10_5PF 0x04
# define RFM12_XTAL_11_0PF 0x05
# define RFM12_XTAL_11_5PF 0x06
# define RFM12_XTAL_12_0PF 0x07
# define RFM12_XTAL_12_5PF 0x08
# define RFM12_XTAL_13_0PF 0x09
# define RFM12_XTAL_13_5PF 0x0A
# define RFM12_XTAL_14_0PF 0x0B
# define RFM12_XTAL_14_5PF 0x0C
# define RFM12_XTAL_15_0PF 0x0D
# define RFM12_XTAL_15_5PF 0x0E
# define RFM12_XTAL_16_0PF 0x0F
/*
2. Power Management Command
Bit Function of the control bit Related blocks
er Enables the whole receiver chain RF front end, baseband, synthesizer, oscillator
ebb The receiver baseband circuit can be separately switched on Baseband
et Switches on the PLL, the power amplifier, and starts the
transmission (If TX register is enabled) Power amplifier, synthesizer, oscillator
es Turns on the synthesizer Synthesizer
ex Turns on the crystal oscillator Crystal oscillator
eb Enables the low battery detector Low battery detector
ew Enables the wake-up timer Wake-up timer
dc Disables the clock output (pin 8) Clock output buffer
*/
#define RFM12_CMD_PWRMGT 0x8200
#define RFM12_PWRMGT_ER 0x80
#define RFM12_PWRMGT_EBB 0x40
#define RFM12_PWRMGT_ET 0x20
#define RFM12_PWRMGT_ES 0x10
#define RFM12_PWRMGT_EX 0x08
#define RFM12_PWRMGT_EB 0x04
#define RFM12_PWRMGT_EW 0x02
#define RFM12_PWRMGT_DC 0x01
/*
3. Frequency Setting Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 0 1 0 f11 f10 f9 f8 f7 f6 f5 f4 f3 f2 f1 f0 A680h
The 12-bit parameter F (bits f11 to f0) should be in the range
of 96 and 3903. When F value sent is out of range, the
previous value is kept. The synthesizer center frequency f0 can
be calculated as:
f0 = 10 * C1 * (C2 + F/4000) [MHz]
The constants C1 and C2 are determined by
the selected band as:
Band [MHz] C1 C2
433 1 43
868 2 43
915 3 30
Frequency in 433 Band can be from 430.24MHz to 439.7575MHz in 2.5kHz increments.
*/
#define RFM12_CMD_FREQUENCY 0xA000
#define RFM12_FREQUENCY_MASK 0x0FFF
#define RFM12_FREQUENCY_CALC_433(f) (((f)-430000000UL)/2500UL)
#define RFM12_FREQUENCY_CALC_868(f) (((f)-860000000UL)/5000UL)
#define RFM12_FREQUENCY_CALC_915(f) (((f)-900000000UL)/7500UL)
/*
4. Data Rate Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 0 0 0 1 1 0 cs r6 r5 r4 r3 r2 r1 r0 C623h
The actual bit rate in transmit mode and the expected bit rate of the received data stream in receive mode is determined by the 7-bit
parameter R (bits r6 to r0) and bit cs.
BR = 10000 / 29 / (R+1) / (1+cs*7) [kbps]
In the receiver set R according to the next function:
R = (10000 / 29 / (1+cs*7) / BR) 1, where BR is the expected bit rate in kbps.
Apart from setting custom values, the standard bit rates from 600 bps to 115.2 kbps can be approximated with small error.
Data rate accuracy requirements:
Clock recovery in slow mode: ΔBR/BR < 1/(29*Nbit) Clock recovery in fast mode: ΔBR/BR < 3/(29*Nbit)
BR is the bit rate set in the receiver and ΔBR is the bit rate difference between the transmitter and the receiver. Nbit is the maximum
number of consecutive ones or zeros in the data stream. It is recommended for long data packets to include enough 1/0 and 0/1
transitions, and to be careful to use the same division ratio in the receiver and in the transmitter.
*/
#define RFM12_CMD_DATARATE 0xC600
#define RFM12_DATARATE_CS 0x80
//calculate setting for datarates >= 2700 Baud
#define RFM12_DATARATE_CALC_HIGH(d) ((uint8_t)((10000000.0/29.0/d)-0.5))
//calculate setting for datarates < 2700 Baud
#define RFM12_DATARATE_CALC_LOW(d) (RFM12_DATARATE_CS|(uint8_t)((10000000.0/29.0/8.0/d)-0.5))
#define RFM12_DATARATE_MASK 0x00ff
/*
5. Receiver Control Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 0 0 1 0 p16 d1 d0 i2 i1 i0 g1 g0 r2 r1 r0 9080h
Bit 10 (p16): pin16 function select
p16 Function of pin 16
0 Interrupt input
1 VDI output
Bits 9-8 (d1 to d0): VDI (valid data indicator) signal response time setting:
d1 d0 Response
0 0 Fast
0 1 Medium
1 0 Slow
1 1 Always on
Bits 7-5 (i2 to i0): Receiver baseband bandwidth (BW) select:
i2 i1 i0 BW [kHz]
0 0 0 reserved
0 0 1 400
0 1 0 340
0 1 1 270
1 0 0 200
1 0 1 134
1 1 0 67
1 1 1 reserved
Bits 4-3 (g1 to g0): LNA gain select:
g1 g0 relative to maximum [dB]
0 0 0
0 1 -6
1 0 -14
1 1 -20
Bits 2-0 (r2 to r0): RSSI detector threshold:
r2 r1 r0 RSSIsetth [dBm]
0 0 0 -103
0 0 1 -97
0 1 0 -91
0 1 1 -85
1 0 0 -79
1 0 1 -73
1 1 0 Reserved
1 1 1 Reserved
The RSSI threshold depends on the LNA gain, the real RSSI threshold can be calculated:
RSSIth=RSSIsetth+GLNA
*/
#define RFM12_CMD_RXCTRL 0x9000
#define RFM12_RXCTRL_P16_VDI 0x400
#define RFM12_RXCTRL_VDI_FAST 0x000
#define RFM12_RXCTRL_VDI_MEDIUM 0x100
#define RFM12_RXCTRL_VDI_SLOW 0x200
#define RFM12_RXCTRL_VDI_ALWAYS_ON 0x300
#define RFM12_RXCTRL_BW_MASK 0xE0
#define RFM12_RXCTRL_BW_400 0x20
#define RFM12_RXCTRL_BW_340 0x40
#define RFM12_RXCTRL_BW_270 0x60
#define RFM12_RXCTRL_BW_200 0x80
#define RFM12_RXCTRL_BW_134 0xA0
#define RFM12_RXCTRL_BW_67 0xC0
#define RFM12_RXCTRL_LNA_MASK 0x18
#define RFM12_RXCTRL_LNA_0 0x00
#define RFM12_RXCTRL_LNA_6 0x08
#define RFM12_RXCTRL_LNA_14 0x10
#define RFM12_RXCTRL_LNA_20 0x18
#define RFM12_RXCTRL_RSSI_103 0x00
#define RFM12_RXCTRL_RSSI_97 0x01
#define RFM12_RXCTRL_RSSI_91 0x02
#define RFM12_RXCTRL_RSSI_85 0x03
#define RFM12_RXCTRL_RSSI_79 0x04
#define RFM12_RXCTRL_RSSI_73 0x05
#define RFM12_RXCTRL_RSSI_67 0x06
#define RFM12_RXCTRL_RSSI_61 0x07
#define RFM12_RXCTRL_RSSI_MASK 0x07
/*
6. Data Filter Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 0 0 0 0 1 0 al ml 1 s 1 f2 f1 f0 C22Ch
Bit 7 (al): Clock recovery (CR) auto lock control, if set.
CR will start in fast mode, then after locking it will automatically switch to slow mode.
Bit 6 (ml): Clock recovery lock control
1: fast mode, fast attack and fast release (4 to 8 bit preamble (1010...) is recommended)
0: slow mode, slow attack and slow release (12 to 16 bit preamble is recommended)
Using the slow mode requires more accurate bit timing (see Data Rate Command).
Bits 4 (s): Select the type of the data filter:
s Filter Type
0 Digital filter
1 Analog RC filter
Digital: This is a digital realization of an analog RC filter followed by a comparator with hysteresis. The time constant is
automatically adjusted to the bit rate defined by the Data Rate Command.
Note: Bit rate can not exceed 115 kpbs in this mode.
Analog RC filter: The demodulator output is fed to pin 7 over a 10 kOhm resistor. The filter cut-off frequency is set by the
external capacitor connected to this pin and VSS.
Bits 2-0 (f2 to f0): DQD threshold parameter.
Note: To let the DQD report "good signal quality" the threshold parameter should be 4 in cases where the bitrate is close to the
deviation. At higher deviation/bitrate settings, a higher threshold parameter can report "good signal quality" as well.
*/
#define RFM12_CMD_DATAFILTER 0xC228
#define RFM12_DATAFILTER_AL 0x80
#define RFM12_DATAFILTER_ML 0x40
#define RFM12_DATAFILTER_S 0x10
/*
7. FIFO and Reset Mode Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 0 0 1 0 1 0 f3 f2 f1 f0 sp al ff dr CA80h
Bits 7-4 (f3 to f0): FIFO IT level. The FIFO generates IT when the number of received data bits reaches this level.
Bit 3 (sp): Select the length of the synchron pattern:
sp Byte1 Byte0 (POR) Synchron Pattern (Byte1+Byte0)
0 2Dh D4h 2DD4h
1 Not used D4h D4h
Note: Byte0 can be programmed by the Synchron Pattern Command.
Bit 2 (al): Set the input of the FIFO fill start condition:
al
0 Synchron pattern
1 Always fill
Bit 1 (ff): FIFO fill will be enabled after synchron pattern reception. The FIFO fill stops when this bit is cleared.
Bit 0 (dr): Disables the highly sensitive RESET mode.
*/
#define RFM12_CMD_FIFORESET 0xCA00
#define RFM12_FIFORESET_SP 0x08
#define RFM12_FIFORESET_AL 0x04
#define RFM12_FIFORESET_FF 0x02
#define RFM12_FIFORESET_DR 0x01
/*
8. Synchron Pattern Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 0 0 1 1 1 0 b7 b6 b5 b4 b3 b2 b1 b0 CED4h
The Byte0 used for synchron pattern detection can be reprogrammed by B <b7:b0>.
*/
#define RFM12_CMD_SYNCPATTERN 0xCE00
/*
9. Receiver FIFO Read Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 B000h
With this command, the controller can read 8 bits from the receiver FIFO. Bit 6 (ef) must be set in Configuration Setting Command.
Note:: During FIFO access fSCK cannot be higher than fref /4, where fref is the crystal oscillator frequency. When the duty-cycle of the
clock signal is not 50 % the shorter period of the clock pulse width should be at least 2/fref .
*/
#define RFM12_CMD_READ 0xB000
/*
10. AFC Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 0 0 0 1 0 0 a1 a0 rl1 rl0 st fi oe en C4F7h
Bit 7-6 (a1 to a0): Automatic operation mode selector:
a1 a0
0 0 Auto mode off (Strobe is controlled by microcontroller)
0 1 Runs only once after each power-up
1 0 Keep the foffset only during receiving (VDI=high)
1 1 Keep the foffset value independently from the state of the VDI signal
Bit 5-4 (rl1 to rl0): Range limit. Limits the value of the frequency offset register to the next values:
rl1 rl0 Max deviation
0 0 No restriction (+63 fres to -64 fres)
0 1 +15 fres to -16 fres
1 0 +7 fres to -8 fres
1 1 +3 fres to -4 fres
fres:
433 MHz bands: 2.5 kHz
868 MHz band: 5 kHz
915 MHz band: 7.5 kHz
Bit 3 (st): Strobe edge, when st goes to high, the actual latest calculated frequency error is stored into the offset register of the AFC
block.
Bit 2 (fi): Switches the circuit to high accuracy (fine) mode. In this case, the processing time is about twice as long, but the measurement
uncertainty is about half.
Bit 1 (oe): Enables the frequency offset register. It allows the addition of the offset register to the frequency control word of the PLL.
Bit 0 (en): Enables the calculation of the offset frequency by the AFC circuit.
In automatic operation mode (no strobe signal is needed from the microcontroller to update the output offset register) the AFC circuit
is automatically enabled when the VDI indicates potential incoming signal during the whole measurement cycle and the circuit
measures the same result in two subsequent cycles.
There are three operation modes, examples from the possible application:
1, (a1=0, a0=1) The circuit measures the frequency offset only once after power up. This way, extended TX-RX maximum distance
can be achieved.
Possible application:
In the final application, when the user inserts the battery, the circuit measures and compensates for the frequency offset caused by
the crystal tolerances. This method allows for the use of a cheaper quartz in the application and provides protection against tracking
an interferer.
2a, (a1=1, a0=0) The circuit automatically measures the frequency offset during an initial effective low data rate pattern easier to
receive- (i.e.: 00110011) of the package and changes the receiving frequency accordingly. The further part of the package can be
received by the corrected frequency settings.
2b, (a1=1, a0=0) The transmitter must transmit the first part of the packet with a step higher deviation and later there is a possibility
of reducing it.
In both cases (2a and 2b), when the VDI indicates poor receiving conditions (VDI goes low), the output register is automatically
cleared. Use these settings when receiving signals from different transmitters transmitting in the same nominal frequencies.
3, (a1=1, a0=1) Its the same as 2a and 2b modes, but suggested to use when a receiver operates with only one transmitter. After a
complete measuring cycle, the measured value is kept independently of the state of the VDI signal.
*/
#define RFM12_CMD_AFC 0xC400
#define RFM12_AFC_AUTO_OFF 0x00
#define RFM12_AFC_AUTO_ONCE 0x40
#define RFM12_AFC_AUTO_VDI 0x80
#define RFM12_AFC_AUTO_KEEP 0xC0
#define RFM12_AFC_LIMIT_OFF 0x00 /* 64 */
#define RFM12_AFC_LIMIT_16 0x10
#define RFM12_AFC_LIMIT_8 0x20
#define RFM12_AFC_LIMIT_4 0x30
#define RFM12_AFC_ST 0x08
#define RFM12_AFC_FI 0x04
#define RFM12_AFC_OE 0x02
#define RFM12_AFC_EN 0x01
/*
11. TX Configuration Control Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 0 0 1 1 0 0 mp m3 m2 m1 m0 0 p2 p1 p0 9800h
Bits 8-4 (mp, m3 to m0): FSK modulation parameters:
The resulting output frequency can be calculated as:
fout = f0 + (-1)SIGN * (M + 1) * (15 kHz)
where:
f0 is the channel center frequency (see the
Frequency Setting Command)
M is the four bit binary number <m3 : m0>
SIGN = (mp) XOR FSK
Bits 2-0 (p2 to p0): Output power:
p2 p1 p0 Relative Output Power [dB]
0 0 0 0
0 0 1 -2.5
0 1 0 -5
0 1 1 -7.5
1 0 0 -10
1 0 1 -12.5
1 1 0 -15
1 1 1 -17.5
*/
#define RFM12_CMD_TXCONF 0x9800
#define RFM12_TXCONF_MP 0x100
#define RFM12_TXCONF_POWER_0 0x00
#define RFM12_TXCONF_POWER_3 0x01
#define RFM12_TXCONF_POWER_6 0x02
#define RFM12_TXCONF_POWER_9 0x03
#define RFM12_TXCONF_POWER_12 0x04
#define RFM12_TXCONF_POWER_15 0x05
#define RFM12_TXCONF_POWER_18 0x06
#define RFM12_TXCONF_POWER_21 0x07
#define RFM12_TXCONF_FSK_MASK 0xf0
#define RFM12_TXCONF_FS_CALC(f) (((f/15000UL)-1)<<4)
#define RFM12_TXCONF_MASK 0x01F7
#define RFM12_TXCONF_POWER_MASK 0x07
/*
12. PLL Setting Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 0 0 1 1 0 0 0 ob1 ob0 0 ddy ddit 1 bw0 CC67h
Note: POR default setting of the register carefully selected to cover almost all typical applications.
Bit 6-5 (ob1-ob0): Microcontroller output clock buffer rise and fall time control.
ob1 ob0 Selected uC CLK frequency
0 0 5 or 10 MHz (recommended)
0 1 3.3 MHz
1 X 2.5 MHz or less
(Typ conditions: Top = 27 oC; Vdd = Voc = 2.7 V, Crystal ESR = 30 Ohm)
Bit 3 (ddy): Switches on the delay in the phase detector when this bit is set.
Bit 2 (ddit): When set, disables the dithering in the PLL loop.
Bit 0 (bw0): PLL bandwidth can be set for optimal TX RF performance.
bw0 Max bit rate [kbps] Phase noise at 1MHz offset [dBc/Hz]
0 86.2 -107
1 256 -102
Note: Needed for optimization of the RF
performance. Optimal settings can vary
according to the external load capacitance.
*/
#define RFM12_CMD_PLL 0xCC02
#define RFM12_PLL_DDY 0x08
#define RFM12_PLL_DDIT 0x04
#define RFM12_PLL_BW0 0x01
/*
13. Transmitter Register Write Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 0 1 1 1 0 0 0 t7 t6 t5 t4 t3 t2 t1 t0 B8AAh
With this command, the controller can write 8 bits (t7 to t0) to the transmitter data register. Bit 7 (el) must be set in Configuration
Setting Command.
*/
#define RFM12_CMD_TX 0xB800
/*
14. Wake-Up Timer Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 1 r4 r3 r2 r1 r0 m7 m6 m5 m4 m3 m2 m1 m0 E196h
The wake-up time period can be calculated by (m7 to m0) and (r4 to r0):
Twake-up = 1.03 * M * 2R + 0.5 [ms]
Note:
For continual operation the ew bit should be cleared and set at the end of every cycle.
For future compatibility, use R in a range of 0 and 29.
*/
#define RFM12_CMD_WAKEUP 0xE000
/*
15. Low Duty-Cycle Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 0 0 1 0 0 0 d6 d5 d4 d3 d2 d1 d0 en C80Eh
With this command, Low Duty-Cycle operation can be set in order to decrease the average power consumption in receiver mode.
The time cycle is determined by the Wake-Up Timer Command.
The Duty-Cycle can be calculated by using (d6 to d0) and M. (M is parameter in a Wake-Up Timer Command.)
Duty-Cycle= (D * 2 +1) / M *100%
The on-cycle is automatically extended while DQD indicates good received signal condition (FSK transmission is detected in the
frequency range determined by Frequency Setting Command plus and minus the baseband filter bandwidth determined by the
Receiver Control Command).
*/
#define RFM12_CMD_DUTYCYCLE 0xC800
#define RFM12_DUTYCYCLE_ENABLE 0x01
/*
16. Low Battery Detector and Microcontroller Clock Divider Command
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 POR
1 1 0 0 0 0 0 0 d2 d1 d0 0 v3 v2 v1 v0 C000h
The 4 bit parameter (v3 to v0) represents the value V, which defines the threshold voltage Vlb of the detector:
Vlb= 2.25 + V * 0.1 [V]
Clock divider configuration:
Clock Output
Frequency [MHz]
0 0 0 1
0 0 1 1.25
0 1 0 1.66
0 1 1 2
1 0 0 2.5
1 0 1 3.33
1 1 0 5
1 1 1 10
d2 d1 d0
The low battery detector and the clock output can be enabled or disabled by bits eb and dc, respectively, using the Power
Management Command.
*/
#define RFM12_CMD_LBDMCD 0xC000
#define RFM12_LBD_VOLTAGE_2V2 0
#define RFM12_LBD_VOLTAGE_2V3 1
#define RFM12_LBD_VOLTAGE_2V4 2
#define RFM12_LBD_VOLTAGE_2V5 3
#define RFM12_LBD_VOLTAGE_2V6 4
#define RFM12_LBD_VOLTAGE_2V7 5
#define RFM12_LBD_VOLTAGE_2V8 6
#define RFM12_LBD_VOLTAGE_2V9 7
#define RFM12_LBD_VOLTAGE_3V0 8
#define RFM12_LBD_VOLTAGE_3V1 9
#define RFM12_LBD_VOLTAGE_3V2 10
#define RFM12_LBD_VOLTAGE_3V3 11
#define RFM12_LBD_VOLTAGE_3V4 12
#define RFM12_LBD_VOLTAGE_3V5 13
#define RFM12_LBD_VOLTAGE_3V6 14
#define RFM12_LBD_VOLTAGE_3V7 15
#define RFM12_CLOCK_OUT_FREQUENCY_1_00_MHz (0<<5)
#define RFM12_CLOCK_OUT_FREQUENCY_1_25_MHz (1<<5)
#define RFM12_CLOCK_OUT_FREQUENCY_1_66_MHz (2<<5)
#define RFM12_CLOCK_OUT_FREQUENCY_2_00_MHz (3<<5)
#define RFM12_CLOCK_OUT_FREQUENCY_2_50_MHz (4<<5)
#define RFM12_CLOCK_OUT_FREQUENCY_3_33_MHz (5<<5)
#define RFM12_CLOCK_OUT_FREQUENCY_5_00_MHz (6<<5)
#define RFM12_CLOCK_OUT_FREQUENCY_10_00_MHz (7<<5)
/*
17. Status Read Command
The read command starts with a zero, whereas all other control commands start with a one. If a read command is identified, the
status bits will be clocked out on the SDO pin as follows:
bitnumber
15 RGIT TX register is ready to receive the next byte (Can be cleared by Transmitter Register Write Command)
FFIT The number of data bits in the RX FIFO has reached the pre-programmed limit (Can be cleared by any of the
FIFO read methods)
14 POR Power-on reset (Cleared after Status Read Command)
13 RGUR TX register under run, register over write (Cleared after Status Read Command)
FFOV RX FIFO overflow (Cleared after Status Read Command)
12 WKUP Wake-up timer overflow (Cleared after Status Read Command)
11 EXT Logic level on interrupt pin (pin 16) changed to low (Cleared after Status Read Command)
10 LBD Low battery detect, the power supply voltage is below the pre-programmed limit
9 FFEM FIFO is empty
8 ATS Antenna tuning circuit detected strong enough RF signal
RSSI The strength of the incoming signal is above the pre-programmed limit
7 DQD Data quality detector output
6 CRL Clock recovery locked
5 ATGL Toggling in each AFC cycle
4 OFFS(6) MSB of the measured frequency offset (sign of the offset value)
3 OFFS(3) -OFFS(0) Offset value to be added to the value of the frequency control parameter (Four LSB bits)
2
1
0
*/
#define RFM12_CMD_STATUS 0x0000
#define RFM12_STATUS_RGIT 0x8000
#define RFM12_STATUS_FFIT 0x8000
#define RFM12_STATUS_POR 0x4000
#define RFM12_STATUS_RGUR 0x2000
#define RFM12_STATUS_FFOV 0x2000
#define RFM12_STATUS_WKUP 0x1000
#define RFM12_STATUS_EXT 0x0800
#define RFM12_STATUS_LBD 0x0400
#define RFM12_STATUS_FFEM 0x0200
#define RFM12_STATUS_ATS 0x0100
#define RFM12_STATUS_RSSI 0x0100
#define RFM12_STATUS_DQD 0x0080
#define RFM12_STATUS_CRL 0x0040
#define RFM12_STATUS_ATGL 0x0020
/* undocumented software reset command for the rf12
*/
#define RFM12_RESET 0xffff

81
target.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef __TARGET_H__
#define __TARGET_H__
/* *********************************************************************** */
/*
* using ATmega168 @16MHz:
* Fuse E: 0xFA (512 words bootloader)
* Fuse H: 0xDD (2.7V BOD)
* Fuse L: 0xFF (external crystal)
*/
#define F_CPU 16000000
#define BAUDRATE 19200
#define RFM12_ADDRESS TWAR
/* 1ms @16MHz */
#define TIMEOUT 1000
#define TIMER_RELOAD (0xFF - 250)
/* *********************************************************************** */
#define LED_INIT() { DDRD |= ((1<<PORTD5) | (1<<PORTD6)); LED_OFF(); }
#define LED_GN_ON() PORTD &= ~(1<<PORTD5)
#define LED_GN_OFF() PORTD |= (1<<PORTD5)
#define LED_GN_TOGGLE() PORTD ^= (1<<PORTD5)
#define LED_RT_ON() PORTD &= ~(1<<PORTD6)
#define LED_RT_OFF() PORTD |= (1<<PORTD6)
#define LED_OFF() PORTD |= ((1<<PORTD5) | (1<<PORTD6))
/* *********************************************************************** */
#define UART_RXBUF_SIZE 128
#define UART_TXBUF_SIZE 128
/* *********************************************************************** */
#define PWM_TIM_INIT() { \
DDRB |= (1<<PORTB1) | (1<<PORTB2); \
/* Timer1: 8MHz, FastModePWM, overflow interrupt */ \
TCCR1A = (1<<WGM11) | (0<<WGM10); \
TCCR1B = (1<<WGM12) | (1<<WGM13); \
TIMSK1 = (1<<TOIE1); \
ICR1 = 0xFFFF; \
}
#define PWM_TIM_ENABLE() { TCCR1B |= (1<<CS10); }
#define PWM_TIM_DISABLE() { TCCR1B &= ~(1<<CS10); }
#define PWM_TIM_CHECK() (TCCR1A & ((1<<COM1A1) | (1<<COM1B1)))
#define PWM_TIM_VECT TIMER1_OVF_vect
#define PWM_CH0_PORT PORTB
#define PWM_CH0_NUM 1
#define PWM_CH0_OFF() { PWM_CH0_PORT &= ~(1<<PWM_CH0_NUM); TCCR1A &= ~(1<<COM1A1); }
#define PWM_CH0_ON() { PWM_CH0_PORT |= (1<<PWM_CH0_NUM); TCCR1A &= ~(1<<COM1A1); }
#define PWM_CH0_PWM(x) { PWM_CH0_PORT &= ~(1<<PWM_CH0_NUM); TCCR1A |= (1<<COM1A1); OCR1A = x; }
#define PWM_CH1_PORT PORTB
#define PWM_CH1_NUM 2
#define PWM_CH1_OFF() { PWM_CH1_PORT &= ~(1<<PWM_CH1_NUM); TCCR1A &= ~(1<<COM1B1); }
#define PWM_CH1_ON() { PWM_CH1_PORT |= (1<<PWM_CH1_NUM); TCCR1A &= ~(1<<COM1B1); }
#define PWM_CH1_PWM(x) { PWM_CH1_PORT &= ~(1<<PWM_CH1_NUM); TCCR1A |= (1<<COM1B1); OCR1B = x; }
/* *********************************************************************** */
#define RFM12_INT_INIT() EICRA |= (1<<ISC11)
#define RFM12_INT_ON() EIMSK |= (1<<INT1)
#define RFM12_INT_OFF() EIMSK &= ~(1<<INT1)
#define RFM12_INT_CLEAR() EIFR = INTF1
#define RFM12_INT_VECT INT1_vect
#define RFM12_CS_INIT() DDRD |= (1<<PORTD7)
#define RFM12_CS_ACTIVE() PORTD &= ~(1<<PORTD7)
#define RFM12_CS_INACTIVE() PORTD |= (1<<PORTD7)
#define RFM12_SPI_INIT() { /* SS, SCK and MOSI are outputs */ \
DDRB |= (1<<PORTB2) | (1<<PORTB3) | (1<<PORTB5); \
/* SPI Master, F_CPU /16 */ \
SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0); \
}
/* *********************************************************************** */
#endif /* __TARGET_H__ */

57
timer.c Normal file
View File

@ -0,0 +1,57 @@
/***************************************************************************
* 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 TIMER_COUNT 2
static uint16_t timers[TIMER_COUNT];
/* *********************************************************************** */
void timer_tick(void)
{
uint8_t i;
for (i = 0; i < TIMER_COUNT; i++)
{
if (timers[i] > 0)
{
timers[i]--;
if (timers[i] == 0)
{
event_queue(EVENT_TYPE_TIMER_ELAPSED, i, 0);
}
}
}
} /* timer_tick */
void timer_event_handler(struct event_entry *event)
{
if ((event->type == EVENT_TYPE_TIMER_SET) &&
(event->num < TIMER_COUNT)
)
{
timers[event->num] = event->value;
}
} /* timer_event_handler */

15
timer.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef __TIMER_H__
#define __TIMER_H__
/* *********************************************************************** */
#define TIMER_DOOR_CLOSE_DELAY 0
/* *********************************************************************** */
void timer_tick (void);
void timer_event_handler (struct event_entry *event);
/* *********************************************************************** */
#endif /* __TIMER_H__ */

167
uart.c Normal file
View File

@ -0,0 +1,167 @@
/***************************************************************************
* 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 "target.h"
#include "uart.h"
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)(F_CPU) / ((uint32_t)(baudRate)*16) -1)
/* *********************************************************************** */
static uint8_t uart_rxbuf[UART_RXBUF_SIZE];
static uint8_t uart_txbuf[UART_TXBUF_SIZE];
static volatile uint8_t uart_rx_idx[2];
static volatile uint8_t uart_tx_idx[2];
#define UART_IDX_IN 0
#define UART_IDX_OUT 1
/* *********************************************************************** */
ISR(USART_RX_vect)
{
uint8_t idx = uart_rx_idx[UART_IDX_IN];
uart_rxbuf[idx++] = UDR0;
uart_rx_idx[UART_IDX_IN] = idx % sizeof(uart_rxbuf);
} /* USART_RX_vect */
ISR(USART_UDRE_vect)
{
/* tx buffer empty? */
if (uart_tx_idx[UART_IDX_IN] != uart_tx_idx[UART_IDX_OUT])
{
/* send next byte */
uint8_t idx = uart_tx_idx[UART_IDX_OUT];
UDR0 = uart_txbuf[idx++];
uart_tx_idx[UART_IDX_OUT] = idx % sizeof(uart_txbuf);
}
else
{
/* disable tx-interrupt */
UCSR0B &= ~(1<<UDRIE0);
}
} /* USART_UDRE_vect */
void uart_init(void)
{
/* set baudrate */
UBRR0H = (UART_CALC_BAUDRATE(BAUDRATE)>>8) & 0xFF;
UBRR0L = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF);
/* USART: rx/tx enable, 8n1 */
UCSR0B = (1<<TXEN0) | (1<<RXEN0) | (1<<RXCIE0);
UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
} /* uart_init */
void uart_putc(uint8_t data)
{
uint8_t idx = uart_tx_idx[UART_IDX_IN];
uart_txbuf[idx++] = data;
uart_tx_idx[UART_IDX_IN] = idx % sizeof(uart_txbuf);
/* enable tx-interrupt */
UCSR0B |= (1<<UDRIE0);
} /* uart_putc */
uint8_t uart_getc(void)
{
while (uart_rx_idx[UART_IDX_IN] == uart_rx_idx[UART_IDX_OUT]);
/* send next byte */
uint8_t idx = uart_rx_idx[UART_IDX_OUT];
uint8_t result = uart_rxbuf[idx++];
uart_rx_idx[UART_IDX_OUT] = idx % sizeof(uart_rxbuf);
return result;
} /* uart_getc */
uint8_t uart_rx_count(void)
{
return (uart_rx_idx[UART_IDX_IN] - uart_rx_idx[UART_IDX_OUT]) % sizeof(uart_rxbuf);
} /* uart_check */
void uart_putstr(const char *str)
{
while (*str)
{
uart_putc((uint8_t)(*str++));
}
} /* uart_putstr */
void uart_putstr_p (const prog_char *str)
{
uint8_t c;
while ((c = pgm_read_byte(str++)) != 0)
{
uart_putc(c);
}
} /* uart_putstr_p */
static uint8_t bin2hex(uint8_t data)
{
return data + ((data < 0x0A) ? '0' : ('A' - 0x0A));
} /* bin2hex */
static uint8_t hex2bin(uint8_t data)
{
if (data >= '0' && data <= '9')
{
return (data - '0');
}
else
{
data &= ~(0x20);
if (data >= 'A' && data <= 'F')
{
return (data - 'A' + 0x0A);
}
}
return 0x00;
} /* hex2bin */
void uart_put_hex(uint8_t data)
{
uart_putc(bin2hex(data >> 4));
uart_putc(bin2hex(data & 0x0F));
} /* uart_hex */
uint8_t uart_get_hex(void)
{
uint8_t result;
result = hex2bin(uart_getc()) << 4;
result |= hex2bin(uart_getc());
return result;
} /* uart_hex */

21
uart.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef __UART_H__
#define __UART_H__
#include <avr/pgmspace.h>
/* *********************************************************************** */
void uart_init (void);
void uart_putc (uint8_t data);
uint8_t uart_getc (void);
uint8_t uart_rx_count (void);
void uart_putstr (const char *str);
void uart_putstr_p (const prog_char *str);
void uart_put_hex (uint8_t data);
uint8_t uart_get_hex (void);
/* *********************************************************************** */
#endif /* __UART_H__ */