/* * 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. * */ /** * \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.h" #include "contiki-esb.h" #include "dev/tr1001.h" #include "dev/radio-sensor.h" #include "lib/gcr.h" #include "lib/crc16.h" #include "net/netstack.h" #include "net/rime/rimestats.h" #include "isr_compat.h" #include #ifdef TR1001_CONF_BEEP_ON_BAD_CRC #define BEEP_ON_BAD_CRC TR1001_CONF_BEEP_ON_BAD_CRC #else #define BEEP_ON_BAD_CRC 1 #endif /* TR1001_CONF_BEEP_ON_BAD_CRC */ #if BEEP_ON_BAD_CRC #include "dev/beep.h" #define BEEP_BEEP(t) beep_beep(t) #else #define BEEP_BEEP(t) #endif /* BEEP_ON_BAD_CRC */ #define RXSTATE_READY 0 #define RXSTATE_RECEIVING 1 #define RXSTATE_FULL 2 #define SYNCH1 0x3c #define SYNCH2 0x03 #ifdef TR1001_CONF_BUFFER_SIZE #define RXBUFSIZE TR1001_CONF_BUFFER_SIZE #else #define RXBUFSIZE PACKETBUF_SIZE #endif /* TR1001_CONF_BUFFER_SIZE */ /* * Pending data to send when using prepare/transmit functions. */ static const void *pending_data; /* * The buffer which holds incoming data. */ 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 uint16_t rxcrc, rxcrctmp; /* * The structure of the packet header. */ struct tr1001_hdr { uint8_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 uint8_t onoroff = OFF; #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; static unsigned short tmp_count; #define DEBUG 0 #if DEBUG #include #define LOG(...) printf(__VA_ARGS__) #else #define LOG(...) #endif #define GCRLOG(...) /* printf(__VA_ARGS__)*/ /*---------------------------------------------------------------------------*/ PROCESS(tr1001_process, "TR1001 driver"); /*---------------------------------------------------------------------------*/ static int prepare_packet(const void *data, unsigned short len); static int transmit_packet(unsigned short len); static int receiving_packet(void); static int pending_packet(void); static int channel_clear(void); static int tr1001_on(void); static int tr1001_off(void); const struct radio_driver tr1001_driver = { tr1001_init, prepare_packet, transmit_packet, tr1001_send, tr1001_read, channel_clear, receiving_packet, pending_packet, tr1001_on, tr1001_off }; /*---------------------------------------------------------------------------*/ /* * 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. */ /*---------------------------------------------------------------------------*/ static int tr1001_off(void) { if(onoroff == OFF) { return 1; } onoroff = OFF; rxoff(); rxclear(); ENERGEST_OFF(ENERGEST_TYPE_LISTEN); return 1; } /*---------------------------------------------------------------------------*/ /* * Turn TR1001 radio transceiver on. */ /*---------------------------------------------------------------------------*/ static int tr1001_on(void) { if(onoroff == ON) { return 1; } ENERGEST_ON(ENERGEST_TYPE_LISTEN); onoroff = ON; rxon(); rxclear(); return 1; } /*---------------------------------------------------------------------------*/ /* * 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 sendx(unsigned char b) { gcr_encode(b); GCRLOG("(%02x)", b); while(gcr_get_encoded(&b)) { send(b); GCRLOG("%02x ", b); } } static uint16_t sendx_crc16(unsigned char b, uint16_t crcacc) { gcr_encode(b); GCRLOG("(%02x)", b); crcacc = crc16_add(b, crcacc); while(gcr_get_encoded(&b)) { send(b); GCRLOG("C%02x ", b); } return 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) */ } /*---------------------------------------------------------------------------*/ int tr1001_init(void) { PT_INIT(&rxhandler_pt); onoroff = OFF; 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); tr1001_on(); tr1001_set_txpower(100); /* Reset reception state. */ rxclear(); process_start(&tr1001_process, NULL); return 1; } /*---------------------------------------------------------------------------*/ ISR(UART0RX, tr1001_rxhandler) { ENERGEST_ON(ENERGEST_TYPE_IRQ); tr1001_default_rxhandler_pt(RXBUF0); if(tr1001_rxstate == RXSTATE_FULL) { LPM4_EXIT; } ENERGEST_OFF(ENERGEST_TYPE_IRQ); } /*---------------------------------------------------------------------------*/ #if DEBUG static void dump_packet(int len) { int i; for(i = 0; i < len; ++i) { LOG("%d: 0x%02x\n", i, tr1001_rxbuf[i]); } } #endif /* DEBUG */ /*---------------------------------------------------------------------------*/ 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_RECEIVING) { unsigned short signal = radio_sensor.value(0); tmp_sstrength += (signal >> 2); tmp_count++; } 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_RECEIVING; /* 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_count = 0; /* Reset the CRC. */ rxcrc = 0xffff; gcr_init(); GCRLOG("RECV: "); /* Read packet header. */ for(tmppos = 0; tmppos < TR1001_HDRLEN; ++tmppos) { /* Wait for the first byte of the packet to arrive. */ do { PT_YIELD(&rxhandler_pt); GCRLOG("(%02x) ", incoming_byte); gcr_decode(incoming_byte); /* If the incoming byte isn't a valid GCR encoded byte, we start again from the beginning. */ if(!gcr_valid()) { BEEP_BEEP(1000); LOG("Incorrect GCR in header at byte %d/1 %x\n", tmppos, incoming_byte); RIMESTATS_ADD(badsynch); PT_RESTART(&rxhandler_pt); } } while(!gcr_get_decoded(&rxtmp)); GCRLOG("%02x ", rxtmp); tr1001_rxbuf[tmppos] = rxtmp; /* Calculate the CRC. */ rxcrc = crc16_add(rxtmp, 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)) { RIMESTATS_ADD(toolong); PT_RESTART(&rxhandler_pt); } /* Read packet data. */ for(; tmppos < tr1001_rxlen + TR1001_HDRLEN; ++tmppos) { /* Wait for the first byte of the packet to arrive. */ do { PT_YIELD(&rxhandler_pt); GCRLOG("(%02x)", incoming_byte); gcr_decode(incoming_byte); /* If the incoming byte isn't a valid Manchester encoded byte, we start again from the beinning. */ if(!gcr_valid()) { BEEP_BEEP(1000); LOG("Incorrect GCR 0x%02x at byte %d/1\n", incoming_byte, tmppos - TR1001_HDRLEN); RIMESTATS_ADD(badsynch); PT_RESTART(&rxhandler_pt); } } while(!gcr_get_decoded(&rxtmp)); GCRLOG("%02x ", rxtmp); tr1001_rxbuf[tmppos] = rxtmp; /* Calculate the CRC. */ rxcrc = crc16_add(rxtmp, rxcrc); } /* Read the frame CRC. */ for(tmppos = 0; tmppos < 2; ++tmppos) { do { PT_YIELD(&rxhandler_pt); GCRLOG("(%02x)", incoming_byte); gcr_decode(incoming_byte); if(!gcr_valid()) { BEEP_BEEP(1000); RIMESTATS_ADD(badsynch); PT_RESTART(&rxhandler_pt); } } while(!gcr_get_decoded(&rxtmp)); GCRLOG("%02x ", rxtmp); rxcrctmp = (rxcrctmp << 8) | rxtmp; } GCRLOG("\n"); 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. */ RIMESTATS_ADD(llrx); process_poll(&tr1001_process); /* 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\n"); BEEP_BEEP(1000); RIMESTATS_ADD(badcrc); } } PT_END(&rxhandler_pt); } /*---------------------------------------------------------------------------*/ static int prepare_packet(const void *data, unsigned short len) { pending_data = data; return 0; } /*---------------------------------------------------------------------------*/ static int transmit_packet(unsigned short len) { int ret = RADIO_TX_ERR; if(pending_data != NULL) { ret = tr1001_send(pending_data, len); pending_data = NULL; } return ret; } /*---------------------------------------------------------------------------*/ int tr1001_send(const void *packet, unsigned short len) { int i; uint16_t crc16; LOG("tr1001_send: sending %d bytes\n", len); if(onoroff == ON) { ENERGEST_OFF(ENERGEST_TYPE_LISTEN); } ENERGEST_ON(ENERGEST_TYPE_TRANSMIT); /* Prepare the transmission. */ /* Delay the transmission for a short random duration. */ clock_delay(random_rand() & 0x3ff); /* Check that we don't currently are receiving 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_RECEIVING && !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 < NUM_SYNCHBYTES; ++i) { send(SYNCH1); } send(SYNCH2); crc16 = 0xffff; gcr_init(); GCRLOG("SEND: "); /* Send packet header. */ crc16 = sendx_crc16(len >> 8, crc16); crc16 = sendx_crc16(len & 0xff, crc16); /* Send packet data. */ for(i = 0; i < len; ++i) { crc16 = sendx_crc16(((uint8_t *)packet)[i], crc16); } /* Send CRC */ sendx(crc16 >> 8); sendx(crc16 & 0xff); /* if not encoding has sent all bytes - let it send another GCR specific */ if (!gcr_finished()) { sendx(0); } GCRLOG("\n"); /* Send trailing bytes. */ send(0x33); send(0xcc); send(0x33); send(0xcc); /* Turn on (or off) reception again. */ if(onoroff == ON) { ENERGEST_ON(ENERGEST_TYPE_LISTEN); rxon(); rxclear(); } else { rxoff(); rxclear(); } ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT); RIMESTATS_ADD(lltx); return RADIO_TX_OK; } /*---------------------------------------------------------------------------*/ int tr1001_read(void *buf, unsigned short bufsize) { unsigned short tmplen; if(tr1001_rxstate == RXSTATE_FULL) { #if DEBUG dump_packet(tr1001_rxlen + 2); #endif /* DEBUG */ tmplen = tr1001_rxlen; if(tmplen > bufsize) { LOG("tr1001_read: too large packet: %d/%d bytes\n", tmplen, bufsize); rxclear(); RIMESTATS_ADD(toolong); return -1; } memcpy(buf, &tr1001_rxbuf[TR1001_HDRLEN], tmplen); /* header + content + CRC */ sstrength = (tmp_count ? ((tmp_sstrength / tmp_count) << 2) : 0); rxclear(); LOG("tr1001_read: got %d bytes\n", tmplen); return tmplen; } return 0; } /*---------------------------------------------------------------------------*/ static int receiving_packet(void) { return tr1001_rxstate == RXSTATE_RECEIVING && !timer_expired(&rxtimer); } /*---------------------------------------------------------------------------*/ static int pending_packet(void) { return tr1001_rxstate == RXSTATE_FULL; } /*---------------------------------------------------------------------------*/ static int channel_clear(void) { /* TODO add CCA functionality */ return 0; } /*---------------------------------------------------------------------------*/ PROCESS_THREAD(tr1001_process, ev, data) { int len; PROCESS_BEGIN(); /* Reset reception state now that the process is ready to receive data. */ rxclear(); while(1) { PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); packetbuf_clear(); len = tr1001_read(packetbuf_dataptr(), PACKETBUF_SIZE); if(len > 0) { packetbuf_set_datalen(len); NETSTACK_RDC.input(); } } PROCESS_END(); } /*---------------------------------------------------------------------------*/ 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; } /*---------------------------------------------------------------------------*/ /** @} */ /** @} */