commit f7952dcacbb7b4ef3438f6a815f710e45ad3bcf1 Author: Olaf Rempel Date: Sat Jan 10 11:42:36 2015 +0100 initial version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dedeb3c --- /dev/null +++ b/Makefile @@ -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) diff --git a/event.c b/event.c new file mode 100644 index 0000000..3ae1286 --- /dev/null +++ b/event.c @@ -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 + +#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 */ diff --git a/event.h b/event.h new file mode 100644 index 0000000..c5429f1 --- /dev/null +++ b/event.h @@ -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__ */ diff --git a/funk_proto.h b/funk_proto.h new file mode 100644 index 0000000..0916b45 --- /dev/null +++ b/funk_proto.h @@ -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__ */ diff --git a/funkstuff.c b/funkstuff.c new file mode 100644 index 0000000..3c22f38 --- /dev/null +++ b/funkstuff.c @@ -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 +#include +#include +#include + +#include /* 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< 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 */ diff --git a/input.c b/input.c new file mode 100644 index 0000000..df302d3 --- /dev/null +++ b/input.c @@ -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 +#include + +#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< 0) + { + debounce_timer[0]--; + } + + if (debounce_timer[1] > 0) + { + debounce_timer[1]--; + } +} /* input_tick */ + + +void input_init(void) +{ + /* pullup on all inputs */ + DDRC &= ~((1< +#include +#include + +#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 */ diff --git a/pwm.h b/pwm.h new file mode 100644 index 0000000..17001b6 --- /dev/null +++ b/pwm.h @@ -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__ */ diff --git a/rfm12.c b/rfm12.c new file mode 100644 index 0000000..cd47498 --- /dev/null +++ b/rfm12.c @@ -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 +#include +#include +#include + +#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<= (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 */ diff --git a/rfm12.h b/rfm12.h new file mode 100644 index 0000000..4096082 --- /dev/null +++ b/rfm12.h @@ -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__ */ diff --git a/rfm12_hw.h b/rfm12_hw.h new file mode 100644 index 0000000..e90e7b4 --- /dev/null +++ b/rfm12_hw.h @@ -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 . +*/ +#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 + 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 diff --git a/target.h b/target.h new file mode 100644 index 0000000..fb151c7 --- /dev/null +++ b/target.h @@ -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< + +#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 */ diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..624dfad --- /dev/null +++ b/timer.h @@ -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__ */ diff --git a/uart.c b/uart.c new file mode 100644 index 0000000..2e5d353 --- /dev/null +++ b/uart.c @@ -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 +#include + +#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<>8) & 0xFF; + UBRR0L = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF); + + /* USART: rx/tx enable, 8n1 */ + UCSR0B = (1<= '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 */ diff --git a/uart.h b/uart.h new file mode 100644 index 0000000..0a6b040 --- /dev/null +++ b/uart.h @@ -0,0 +1,21 @@ +#ifndef __UART_H__ +#define __UART_H__ + +#include + +/* *********************************************************************** */ + +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__ */