/* * Copyright (c) 2005, Swedish Institute of Computer Science * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file is part of the Contiki operating system. * * @(#)$Id: tr1001.c,v 1.6 2007/03/28 09:52:35 joxe Exp $ */ /** * \addtogroup esb * @{ */ /** * \defgroup tr1001 TR1001 radio tranciever device driver * @{ */ /** * \file * Device driver and packet framing for the RFM-TR1001 radio module. * \author Adam Dunkels * * This file implements a device driver for the RFM-TR1001 radio * tranciever. * */ #include "contiki-esb.h" #include "lib/me.h" #include "lib/crc16.h" #include "net/tr1001-drv.h" #include #include #include #define RXSTATE_READY 0 #define RXSTATE_RECEVING 1 #define RXSTATE_FULL 2 #define SYNCH1 0x3c #define SYNCH2 0x03 #if TR1001_STATISTICS static unsigned short sstrength_dropped, sstrength_max, sstrength_min, tmp_sstrength_max, tmp_sstrength_min; /* The number of dropped packets */ static unsigned short packets_err; /* The number of accepted packets */ static unsigned short packets_ok; #endif /* TR1001_STATISTICS */ /* * The buffer which holds incoming data. */ #define RXBUFSIZE UIP_BUFSIZE unsigned char tr1001_rxbuf[RXBUFSIZE]; /* * The length of the packet that currently is being received. */ static unsigned short tr1001_rxlen = 0; /* * The reception state. */ volatile unsigned char tr1001_rxstate = RXSTATE_READY; static u8_t radio_active; static u16_t rxcrc, rxcrctmp; /* * The structure of the packet header. */ struct tr1001_hdr { u8_t len[2]; /**< The 16-bit length of the packet in network byte order. */ }; /* * The length of the packet header. */ #define TR1001_HDRLEN sizeof(struct tr1001_hdr) #define OFF 0 #define ON 1 static u8_t onoroff = ON; #define NUM_SYNCHBYTES 4 void tr1001_default_rxhandler(unsigned char c); PT_THREAD(tr1001_default_rxhandler_pt(unsigned char c)); static struct pt rxhandler_pt; /* * This timer is used to keep track of when the last byte was received * over the radio. If the inter-byte time is too large, the packet * currently being received is discarded and a new packet reception is * initiated. */ static struct timer rxtimer; static unsigned short tmp_sstrength, sstrength; #define DEBUG 0 #if DEBUG #include #define LOG(...) printf(__VA_ARGS__) #else #define LOG(...) #endif /*---------------------------------------------------------------------------*/ #if TR1001_STATISTICS #define PACKET_DROPPED(bytes) do { \ if(packets_err < ~0) {\ packets_err++;\ }\ sstrength_dropped = ((bytes) == 0 ? 0 : ((tmp_sstrength / (bytes)) << 1));\ } while(0) #define PACKET_ACCEPTED() do {\ if(packets_ok < ~0) {\ packets_ok++;\ }\ } while(0); #else #define PACKET_DROPPED(bytes) #define PACKET_ACCEPTED() #endif /* TR1001_STATISTICS */ /*---------------------------------------------------------------------------*/ /* * Turn on data transmission in On-Off-Keyed mode. */ static void txook(void) { P3SEL = 0xf0; P5OUT |= 0x40; P5OUT &= 0x7f; } /*---------------------------------------------------------------------------*/ /* * Turn on data reception for the radio tranceiver. */ static void rxon(void) { P3SEL = 0xe0; P5OUT |= 0xc0; /* Enable the receiver. */ ME1 |= URXE0; /* Turn on receive interrupt. */ IE1 |= URXIE0; } /*---------------------------------------------------------------------------*/ /* * Turn off data reception for the radio tranceiver. */ static void rxoff(void) { P5OUT &= 0x3f; /* Disable the receiver. */ ME1 &= ~URXE0; /* Turn off receive interrupt. */ IE1 &= ~URXIE0; } /*---------------------------------------------------------------------------*/ /* * Clear the recevie buffer and reset the receiver state. */ static void rxclear(void) { tr1001_rxstate = RXSTATE_READY; } /*---------------------------------------------------------------------------*/ /* * Turn TR1001 radio transceiver off. */ /*---------------------------------------------------------------------------*/ void radio_off(void) { onoroff = OFF; rxoff(); rxclear(); } /*---------------------------------------------------------------------------*/ /* * Turn TR1001 radio transceiver on. */ /*---------------------------------------------------------------------------*/ void radio_on(void) { onoroff = ON; rxon(); rxclear(); } /*---------------------------------------------------------------------------*/ /* * Send a byte of data over the radio. * * \param b The byte to be sent. */ static void send(unsigned char b) { clock_time_t start; start = clock_time(); /* Wait until the USART0 TX buffer is ready. */ while((IFG1 & UTXIFG0) == 0) { /* Wait no more than one second. */ if((clock_time_t)(clock_time() - start) > (clock_time_t)CLOCK_SECOND) { break; } } /* Send the byte. */ TXBUF0 = b; } /*---------------------------------------------------------------------------*/ /* * Send a byte of data and its logical negation (all bits inverted) * over the radio. * * \param b The byte to be sent. */ static void send2(unsigned char b) { u16_t m; m = me_encode(b); send(m >> 8); send(m & 0xff); } static u16_t send2_crc16(unsigned char b, u16_t crcacc) { u16_t m; m = me_encode(b); send(m >> 8); send(m & 0xff); return crc16_add(b, crcacc); } /*---------------------------------------------------------------------------*/ void tr1001_set_txpower(unsigned char p) { int i; /* Clamp maximum power. */ if(p > 100) { p = 100; } /* First, run the potentiometer down to zero so that we know the start value of the potentiometer. */ P2OUT &= 0xDF; /* P25 = 0 (down selected) */ P2OUT &= 0xBF; /* P26 = 0 (chipselect on) */ for(i = 0; i < 102; ++i) { P2OUT &= 0xEF; /* P24 = 0 (inc) */ P2OUT |= 0x10; } /* Now, start to increase the value of the potentiometer until it reaches the desired value.*/ P2OUT |= 0x20; /* P25 = 1 (up selected) */ for(i = 0; i < p; ++i) { P2OUT &= 0xEF; /* P24 = 0 (inc) */ P2OUT |= 0x10; } P2OUT |= 0x40; /* P26 = 1 (chipselect off) */ } /*---------------------------------------------------------------------------*/ void tr1001_init(void) { PT_INIT(&rxhandler_pt); #if TR1001_STATISTICS packets_ok = packets_err = 0; sstrength_dropped = 0; sstrength_min = 0xFFFF; sstrength_max = 0; #endif /* TR1001_STATISTICS */ UCTL0 = CHAR; /* 8-bit character */ UTCTL0 = SSEL1; /* UCLK = SMCLK */ tr1001_set_speed(TR1001_19200); ME1 |= UTXE0 + URXE0; /* Enable USART0 TXD/RXD */ /* Turn on receive interrupt. */ IE1 |= URXIE0; timer_set(&rxtimer, CLOCK_SECOND / 4); radio_on(); tr1001_set_txpower(100); /* Reset reception state. */ rxclear(); } /*---------------------------------------------------------------------------*/ interrupt (UART0RX_VECTOR) tr1001_rxhandler(void) { tr1001_default_rxhandler_pt(RXBUF0); if(tr1001_rxstate == RXSTATE_FULL) { LPM_AWAKE(); } } /*---------------------------------------------------------------------------*/ static void dump_packet(int len) { int i; for(i = 0; i < len; ++i) { LOG("%d: 0x%02x\n", i, tr1001_rxbuf[i]); } } /*---------------------------------------------------------------------------*/ PT_THREAD(tr1001_default_rxhandler_pt(unsigned char incoming_byte)) { static unsigned char rxtmp, tmppos; if(timer_expired(&rxtimer) && tr1001_rxstate != RXSTATE_FULL) { PT_INIT(&rxhandler_pt); } timer_restart(&rxtimer); if(tr1001_rxstate == RXSTATE_RECEVING) { unsigned short signal = radio_sensor_signal; tmp_sstrength += (signal >> 2); if(signal < tmp_sstrength_min) { tmp_sstrength_min = signal; } if(signal > tmp_sstrength_max) { tmp_sstrength_max = signal; } } PT_BEGIN(&rxhandler_pt); while(1) { /* Reset reception state. */ rxclear(); /* Wait until we receive the first syncronization byte. */ PT_WAIT_UNTIL(&rxhandler_pt, incoming_byte == SYNCH1); tr1001_rxstate = RXSTATE_RECEVING; /* Read all incoming syncronization bytes. */ PT_WAIT_WHILE(&rxhandler_pt, incoming_byte == SYNCH1); /* We should receive the second synch byte by now, otherwise we'll restart the protothread. */ if(incoming_byte != SYNCH2) { PT_RESTART(&rxhandler_pt); } /* Start signal strength measurement */ tmp_sstrength = 0; tmp_sstrength_max = 0; tmp_sstrength_min = 0xFFFF; /* Reset the CRC. */ rxcrc = 0xffff; /* Read packet header. */ for(tmppos = 0; tmppos < TR1001_HDRLEN; ++tmppos) { /* Wait for the first byte of the packet to arrive. */ PT_YIELD(&rxhandler_pt); /* If the incoming byte isn't a valid Manchester encoded byte, we start again from the beinning. */ if(!me_valid(incoming_byte)) { beep_beep(1000); LOG("Incorrect manchester in header at byte %d/1\n", tmppos); PACKET_DROPPED(tmppos); PT_RESTART(&rxhandler_pt); } rxtmp = me_decode8(incoming_byte); /* Wait for the next byte to arrive. */ PT_YIELD(&rxhandler_pt); if(!me_valid(incoming_byte)) { beep_beep(1000); LOG("Incorrect manchester in header at byte %d/2\n", tmppos); PACKET_DROPPED(tmppos + 1); PT_RESTART(&rxhandler_pt); } /* Put together the two bytes into a single Manchester decoded byte. */ tr1001_rxbuf[tmppos] = (rxtmp << 4) | me_decode8(incoming_byte); /* Calculate the CRC. */ rxcrc = crc16_add(tr1001_rxbuf[tmppos], rxcrc); } /* Since we've got the header, we can grab the length from it. */ tr1001_rxlen = ((((struct tr1001_hdr *)tr1001_rxbuf)->len[0] << 8) + ((struct tr1001_hdr *)tr1001_rxbuf)->len[1]); /* If the length is longer than we can handle, we'll start from the beginning. */ if(tmppos + tr1001_rxlen > sizeof(tr1001_rxbuf)) { PACKET_DROPPED(tmppos); PT_RESTART(&rxhandler_pt); } /* Read packet data. */ for(; tmppos < tr1001_rxlen + TR1001_HDRLEN; ++tmppos) { PT_YIELD(&rxhandler_pt); if(!me_valid(incoming_byte)) { LOG("Incorrect manchester 0x%02x at byte %d/1\n", incoming_byte, tmppos - TR1001_HDRLEN); beep_beep(1000); PACKET_DROPPED(tmppos); PT_RESTART(&rxhandler_pt); } rxtmp = me_decode8(incoming_byte); PT_YIELD(&rxhandler_pt); if(!me_valid(incoming_byte)) { LOG("Incorrect manchester at byte %d/2\n", tmppos - TR1001_HDRLEN); beep_beep(1000); PACKET_DROPPED(tmppos + 1); PT_RESTART(&rxhandler_pt); } tr1001_rxbuf[tmppos] = (rxtmp << 4) | me_decode8(incoming_byte); rxcrc = crc16_add(tr1001_rxbuf[tmppos], rxcrc); } /* Read the frame CRC. */ for(tmppos = 0; tmppos < 4; ++tmppos) { PT_YIELD(&rxhandler_pt); if(!me_valid(incoming_byte)) { beep_beep(1000); PACKET_DROPPED(tr1001_rxlen + TR1001_HDRLEN); PT_RESTART(&rxhandler_pt); } rxcrctmp = (rxcrctmp << 4) | me_decode8(incoming_byte); } if(rxcrctmp == rxcrc) { /* A full packet has been received and the CRC checks out. We'll request the driver to take care of the incoming data. */ PACKET_ACCEPTED(); tr1001_drv_request_poll(); /* We'll set the receive state flag to signal that a full frame is present in the buffer, and we'll wait until the buffer has been taken care of. */ tr1001_rxstate = RXSTATE_FULL; PT_WAIT_UNTIL(&rxhandler_pt, tr1001_rxstate != RXSTATE_FULL); } else { LOG("Incorrect CRC"); beep_beep(1000); PACKET_DROPPED(tr1001_rxlen + TR1001_HDRLEN); } } PT_END(&rxhandler_pt); } /*---------------------------------------------------------------------------*/ /* * Prepare a transmission. * * This function does the necessary setup before a packet can be sent * out. */ static void prepare_transmission(int synchbytes) { int i; /* Delay the transmission for a short random duration. */ clock_delay(random_rand() & 0x3ff); /* Check that we don't currently are receiveing a packet, and if so we wait until the reception has been completed. Reception is done with interrupts so it is OK for us to wait in a while() loop. */ while(tr1001_rxstate == RXSTATE_RECEVING && !timer_expired(&rxtimer)) { /* Delay the transmission for a short random duration. */ clock_delay(random_rand() & 0x7ff); } /* Turn on OOK mode with transmission. */ txook(); /* According to the datasheet, the transmitter must wait for 12 us in order to settle. Empirical tests show that is it better to wait for something like 283 us... */ clock_delay(200); /* Transmit preamble and synch bytes. */ for(i = 0; i < 20; ++i) { send(0xaa); } /* send(0xaa); send(0xaa);*/ send(0xff); for(i = 0; i < synchbytes; ++i) { send(SYNCH1); } send(SYNCH2); } /*---------------------------------------------------------------------------*/ u8_t tr1001_send(u8_t *packet, u16_t len) { int i; u16_t crc16; LOG("tr1001_send: sending %d bytes\n", len); /* Prepare the transmission. */ prepare_transmission(NUM_SYNCHBYTES); radio_active = 1; crc16 = 0xffff; /* Send packet header. */ crc16 = send2_crc16(len >> 8, crc16); crc16 = send2_crc16(len & 0xff, crc16); /* Send packet data. */ for(i = 0; i < len; ++i) { crc16 = send2_crc16(packet[i], crc16); } /* Send CRC */ send2(crc16 >> 8); send2(crc16 & 0xff); /* Send trailing bytes. */ send(0x33); send(0xcc); send(0x33); send(0xcc); /* Turn on (or off) reception again. */ if(onoroff == ON) { rxon(); rxclear(); } else { rxoff(); rxclear(); } return UIP_FW_OK; } /*---------------------------------------------------------------------------*/ unsigned short tr1001_poll(u8_t *buf, u16_t bufsize) { unsigned short tmplen; if(tr1001_rxstate == RXSTATE_FULL) { dump_packet(tr1001_rxlen + 2); tmplen = tr1001_rxlen; /* if(tmplen > UIP_BUFSIZE - (UIP_LLH_LEN - TR1001_HDRLEN)) { tmplen = UIP_BUFSIZE - (UIP_LLH_LEN - TR1001_HDRLEN); }*/ if(tmplen > bufsize - TR1001_HDRLEN) { tmplen = bufsize - TR1001_HDRLEN; } memcpy(buf, &tr1001_rxbuf[TR1001_HDRLEN], tmplen); /* header + content + CRC */ sstrength = (tmp_sstrength / (TR1001_HDRLEN + tr1001_rxlen + 2)) << 1; sstrength_max = tmp_sstrength_max; sstrength_min = tmp_sstrength_min; rxclear(); LOG("tr1001_poll: got %d bytes\n", tmplen); return tmplen; } return 0; } /*---------------------------------------------------------------------------*/ void tr1001_set_speed(unsigned char speed) { if(speed == TR1001_19200) { /* Set TR1001 to 19200 */ UBR00 = 0x80; /* 2,457MHz/19200 = 128 -> 0x80 */ UBR10 = 0x00; /* */ UMCTL0 = 0x00; /* no modulation */ } else if(speed == TR1001_38400) { /* Set TR1001 to 38400 */ UBR00 = 0x40; /* 2,457MHz/38400 = 64 -> 0x40 */ UBR10 = 0x00; /* */ UMCTL0 = 0x00; /* no modulation */ } else if(speed == TR1001_57600) { UBR00 = 0x2a; /* 2,457MHz/57600 = 42.7 -> 0x2A */ UBR10 = 0x00; /* */ UMCTL0 = 0x5b; /* */ } else if(speed == TR1001_115200) { UBR00 = 0x15; /* 2,457MHz/115200 = 21.4 -> 0x15 */ UBR10 = 0x00; /* */ UMCTL0 = 0x4a; /* */ } else { tr1001_set_speed(TR1001_19200); } } /*---------------------------------------------------------------------------*/ unsigned short tr1001_sstrength(void) { return sstrength; } /*---------------------------------------------------------------------------*/ #if TR1001_STATISTICS unsigned short tr1001_packets_ok(void) { return packets_ok; } #endif /* TR1001_STATISTICS */ /*---------------------------------------------------------------------------*/ #if TR1001_STATISTICS unsigned short tr1001_packets_dropped(void) { return packets_err; } #endif /* TR1001_STATISTICS */ /*---------------------------------------------------------------------------*/ #if TR1001_STATISTICS void tr1001_clear_packets(void) { packets_ok = packets_err = 0; } #endif /* TR1001_STATISTICS */ /*---------------------------------------------------------------------------*/ #if TR1001_STATISTICS unsigned short tr1001_sstrength_value(unsigned int type) { switch(type) { case TR1001_SSTRENGTH_DROPPED: return sstrength_dropped; case TR1001_SSTRENGTH_MAX: return sstrength_max; case TR1001_SSTRENGTH_MIN: return sstrength_min < sstrength_max ? sstrength_min : 0; default: return 0; } } #endif /* TR1001_STATISTICS */ /*---------------------------------------------------------------------------*/ unsigned char tr1001_active(void) { return radio_active; } /*--------------------------------------------------------------------------*/ void tr1001_clear_active(void) { radio_active = 0; } /*---------------------------------------------------------------------------*/ /** @} */ /** @} */