Browse Source

initial version

master
Olaf Rempel 6 years ago
commit
f7952dcacb
18 changed files with 2456 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +47
    -0
      Makefile
  3. +61
    -0
      event.c
  4. +29
    -0
      event.h
  5. +200
    -0
      funk_proto.h
  6. +290
    -0
      funkstuff.c
  7. +181
    -0
      input.c
  8. +24
    -0
      input.h
  9. +202
    -0
      pwm.c
  10. +28
    -0
      pwm.h
  11. +438
    -0
      rfm12.c
  12. +39
    -0
      rfm12.h
  13. +575
    -0
      rfm12_hw.h
  14. +81
    -0
      target.h
  15. +57
    -0
      timer.c
  16. +15
    -0
      timer.h
  17. +167
    -0
      uart.c
  18. +21
    -0
      uart.h

+ 1
- 0
.gitignore View File

@ -0,0 +1 @@
build

+ 47
- 0
Makefile 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
- 0
event.c 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
- 0
event.h 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
- 0
funk_proto.h 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
- 0
funkstuff.c 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
- 0
input.c 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
- 0
input.h 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
- 0
pwm.c 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
- 0
pwm.h 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
- 0
rfm12.c 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
- 0
rfm12.h 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
- 0
rfm12_hw.h 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