initial version
This commit is contained in:
commit
f7952dcacb
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build
|
47
Makefile
Normal file
47
Makefile
Normal 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
61
event.c
Normal 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
29
event.h
Normal 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
200
funk_proto.h
Normal 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
290
funkstuff.c
Normal 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
181
input.c
Normal 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
24
input.h
Normal 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
202
pwm.c
Normal 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
28
pwm.h
Normal 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
438
rfm12.c
Normal 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
39
rfm12.h
Normal 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
575
rfm12_hw.h
Normal 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) It’s 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
81
target.h
Normal 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
57
timer.c
Normal 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
15
timer.h
Normal 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
167
uart.c
Normal 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
21
uart.h
Normal 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__ */
|
Loading…
Reference in New Issue
Block a user