*** stock_iot-lab_M3/openwsn/radio.c Thu Apr 24 11:19:40 2014 --- riot-openwsn-wip/openwsn/radio.c Thu Apr 24 16:55:54 2014 *************** *** 0 **** --- 1,451 ---- + /** + \brief AT86RF231-specific definition of the "radio" bsp module. + + \author Thomas Watteyne , February 2012. + */ + + + #include "board_ow.h" + #include "radio.h" + #include "at86rf231.h" + #include "spi.h" + #include "radiotimer.h" + #include "debugpins.h" + #include "leds_ow.h" + + #define ENABLE_DEBUG (0) + #include "debug.h" + + + //=========================== defines ========================================= + + //=========================== variables ======================================= + + typedef struct { + radiotimer_capture_cbt startFrame_cb; + radiotimer_capture_cbt endFrame_cb; + radio_state_t state; + } radio_vars_t; + + radio_vars_t radio_vars; + + //=========================== prototypes ====================================== + + void radio_spiWriteReg(uint8_t reg_addr, uint8_t reg_setting); + uint8_t radio_spiReadReg(uint8_t reg_addr); + void radio_spiWriteTxFifo(uint8_t* bufToWrite, uint8_t lenToWrite); + void radio_spiReadRxFifo(uint8_t* pBufRead, + uint8_t* pLenRead, + uint8_t maxBufLen, + uint8_t* pLqi); + uint8_t radio_spiReadRadioInfo(void); + + //=========================== public ========================================== + + //===== admin + + void radio_init() { + + // clear variables + memset(&radio_vars,0,sizeof(radio_vars_t)); + + // change state + radio_vars.state = RADIOSTATE_STOPPED; + + // configure the radio + radio_spiWriteReg(RG_TRX_STATE, CMD_FORCE_TRX_OFF); // turn radio off + + radio_spiWriteReg(RG_IRQ_MASK, + (AT_IRQ_RX_START| AT_IRQ_TRX_END)); // tell radio to fire interrupt on TRX_END and RX_START + radio_spiReadReg(RG_IRQ_STATUS); // deassert the interrupt pin in case is high + radio_spiWriteReg(RG_ANT_DIV, RADIO_CHIP_ANTENNA); // use chip antenna + #define RG_TRX_CTRL_1 0x04 + radio_spiWriteReg(RG_TRX_CTRL_1, 0x20); // have the radio calculate CRC + //busy wait until radio status is TRX_OFF + while((radio_spiReadReg(RG_TRX_STATUS) & 0x1F) != TRX_OFF); + + // change state + radio_vars.state = RADIOSTATE_RFOFF; + } + + void radio_setOverflowCb(radiotimer_compare_cbt cb) { + radiotimer_setOverflowCb(cb); + } + + void radio_setCompareCb(radiotimer_compare_cbt cb) { + radiotimer_setCompareCb(cb); + } + + void radio_setStartFrameCb(radiotimer_capture_cbt cb) { + radio_vars.startFrame_cb = cb; + } + + void radio_setEndFrameCb(radiotimer_capture_cbt cb) { + radio_vars.endFrame_cb = cb; + } + + //===== reset + + void radio_reset() { + PORT_PIN_RADIO_RESET_LOW(); + } + + //===== timer + + void radio_startTimer(PORT_TIMER_WIDTH period) { + radiotimer_start(period); + } + + PORT_TIMER_WIDTH radio_getTimerValue() { + return radiotimer_getValue(); + } + + void radio_setTimerPeriod(PORT_TIMER_WIDTH period) { + radiotimer_setPeriod(period); + } + + PORT_TIMER_WIDTH radio_getTimerPeriod() { + return radiotimer_getPeriod(); + } + + //===== RF admin + + void radio_setFrequency(uint8_t frequency) { + // change state + radio_vars.state = RADIOSTATE_SETTING_FREQUENCY; + + // configure the radio to the right frequecy + radio_spiWriteReg(RG_PHY_CC_CCA,0x20+frequency); + + // change state + radio_vars.state = RADIOSTATE_FREQUENCY_SET; + } + + void radio_rfOn() { + PORT_PIN_RADIO_RESET_LOW(); + } + + void radio_rfOff() { + DEBUG("%s\n",__PRETTY_FUNCTION__); + // change state + radio_vars.state = RADIOSTATE_TURNING_OFF; + radio_spiReadReg(RG_TRX_STATUS); + DEBUG("step 1\n"); + // turn radio off + radio_spiWriteReg(RG_TRX_STATE, CMD_FORCE_TRX_OFF); + DEBUG("step 2\n"); + radio_spiWriteReg(RG_TRX_STATE, CMD_TRX_OFF); + + // busy wait until done + while((radio_spiReadReg(RG_TRX_STATUS) & 0x1F) != TRX_OFF); + + DEBUG("step 3\n"); + // wiggle debug pin + debugpins_radio_clr(); + leds_radio_off(); + DEBUG("step 4\n"); + // change state + radio_vars.state = RADIOSTATE_RFOFF; + DEBUG("step 5\n"); + } + + //===== TX + + void radio_loadPacket(uint8_t* packet, uint8_t len) { + // change state + radio_vars.state = RADIOSTATE_LOADING_PACKET; + + // load packet in TXFIFO + radio_spiWriteTxFifo(packet,len); + + // change state + radio_vars.state = RADIOSTATE_PACKET_LOADED; + } + + void radio_txEnable() { + // change state + radio_vars.state = RADIOSTATE_ENABLING_TX; + + // wiggle debug pin + debugpins_radio_set(); + leds_radio_on(); + + // turn on radio's PLL + radio_spiWriteReg(RG_TRX_STATE, CMD_PLL_ON); + + while((radio_spiReadReg(RG_TRX_STATUS) & 0x1F) != PLL_ON); // busy wait until done + + // change state + radio_vars.state = RADIOSTATE_TX_ENABLED; + } + + void radio_txNow() { + PORT_TIMER_WIDTH val; + // change state + radio_vars.state = RADIOSTATE_TRANSMITTING; + leds_radio_toggle(); + // send packet by pulsing the SLP_TR_CNTL pin + PORT_PIN_RADIO_SLP_TR_CNTL_HIGH(); + PORT_PIN_RADIO_SLP_TR_CNTL_LOW(); + + // The AT86RF231 does not generate an interrupt when the radio transmits the + // SFD, which messes up the MAC state machine. The danger is that, if we leave + // this funtion like this, any radio watchdog timer will expire. + // Instead, we cheat an mimick a start of frame event by calling + // ieee154e_startOfFrame from here. This also means that software can never catch + // a radio glitch by which #radio_txEnable would not be followed by a packet being + // transmitted (I've never seen that). + if (radio_vars.startFrame_cb!=NULL) { + // call the callback + val=radiotimer_getCapturedTime(); + radio_vars.startFrame_cb(val); + } + DEBUG("SENT"); + } + + //===== RX + + void radio_rxEnable() { + // change state + radio_vars.state = RADIOSTATE_ENABLING_RX; + + // put radio in reception mode + radio_spiWriteReg(RG_TRX_STATE, CMD_RX_ON); + + // wiggle debug pin + debugpins_radio_set(); + leds_radio_on(); + + // busy wait until radio really listening + while((radio_spiReadReg(RG_TRX_STATUS) & 0x1F) != RX_ON); + + // change state + radio_vars.state = RADIOSTATE_LISTENING; + } + + void radio_rxNow() { + // nothing to do + } + + void radio_getReceivedFrame(uint8_t* pBufRead, + uint8_t* pLenRead, + uint8_t maxBufLen, + int8_t* pRssi, + uint8_t* pLqi, + uint8_t* pCrc) { + uint8_t temp_reg_value; + + //===== crc + temp_reg_value = radio_spiReadReg(RG_PHY_RSSI); + *pCrc = (temp_reg_value & 0x80)>>7; // msb is whether packet passed CRC + + //===== rssi + // as per section 8.4.3 of the AT86RF231, the RSSI is calculate as: + // -91 + ED [dBm] + temp_reg_value = radio_spiReadReg(RG_PHY_ED_LEVEL); + *pRssi = -91 + temp_reg_value; + + //===== packet + radio_spiReadRxFifo(pBufRead, + pLenRead, + maxBufLen, + pLqi); + } + + //=========================== private ========================================= + + + uint8_t radio_spiReadRadioInfo(void){ + uint8_t spi_tx_buffer[3]; + uint8_t spi_rx_buffer[3]; + + // prepare buffer to send over SPI + spi_tx_buffer[0] = (0x80 | 0x1E); // [b7] Read/Write: 1 (read) + // [b6] RAM/Register : 1 (register) + // [b5-0] address: 0x1E (Manufacturer ID, Lower 16 Bit) + spi_tx_buffer[1] = 0x00; // send a SNOP strobe just to get the reg value + spi_tx_buffer[2] = 0x00; // send a SNOP strobe just to get the reg value + + // retrieve radio manufacturer ID over SPI + spi_txrx(spi_tx_buffer, + sizeof(spi_tx_buffer), + SPI_BUFFER, + spi_rx_buffer, + sizeof(spi_rx_buffer), + SPI_FIRST, + SPI_LAST); + + return spi_rx_buffer[2]; + } + + void radio_spiWriteReg(uint8_t reg_addr, uint8_t reg_setting) { + uint8_t spi_tx_buffer[2]; + uint8_t spi_rx_buffer[2]; + + spi_tx_buffer[0] = (0xC0 | reg_addr); // turn addess in a 'reg write' address + spi_tx_buffer[1] = reg_setting; + + spi_txrx(spi_tx_buffer, + sizeof(spi_tx_buffer), + SPI_BUFFER, + (uint8_t*)spi_rx_buffer, + sizeof(spi_rx_buffer), + SPI_FIRST, + SPI_LAST); + } + + uint8_t radio_spiReadReg(uint8_t reg_addr) { + uint8_t spi_tx_buffer[2]; + uint8_t spi_rx_buffer[2]; + + spi_tx_buffer[0] = (0x80 | reg_addr); // turn addess in a 'reg read' address + spi_tx_buffer[1] = 0x00; // send a no_operation command just to get the reg value + + spi_txrx(spi_tx_buffer, + sizeof(spi_tx_buffer), + SPI_BUFFER, + (uint8_t*)spi_rx_buffer, + sizeof(spi_rx_buffer), + SPI_FIRST, + SPI_LAST); + + + return spi_rx_buffer[1]; + } + + /** for testing purposes, remove if not needed anymore**/ + + void radio_spiWriteTxFifo(uint8_t* bufToWrite, uint8_t lenToWrite) { + uint8_t spi_tx_buffer[2]; + uint8_t spi_rx_buffer[1+1+127]; // 1B SPI address, 1B length, max. 127B data + + spi_tx_buffer[0] = 0x60; // SPI destination address for TXFIFO + spi_tx_buffer[1] = lenToWrite; // length byte + + spi_txrx(spi_tx_buffer, + sizeof(spi_tx_buffer), + SPI_BUFFER, + spi_rx_buffer, + sizeof(spi_rx_buffer), + SPI_FIRST, + SPI_NOTLAST); + + spi_txrx(bufToWrite, + lenToWrite, + SPI_BUFFER, + spi_rx_buffer, + sizeof(spi_rx_buffer), + SPI_NOTFIRST, + SPI_LAST); + } + + + + void radio_spiReadRxFifo(uint8_t* pBufRead, + uint8_t* pLenRead, + uint8_t maxBufLen, + uint8_t* pLqi) { + // when reading the packet over SPI from the RX buffer, you get the following: + // - *[1B] dummy byte because of SPI + // - *[1B] length byte + // - [0-125B] packet (excluding CRC) + // - *[2B] CRC + // - *[1B] LQI + uint8_t spi_tx_buffer[125]; + uint8_t spi_rx_buffer[3]; + + spi_tx_buffer[0] = 0x20; + + // 2 first bytes + spi_txrx(spi_tx_buffer, + 2, + SPI_BUFFER, + spi_rx_buffer, + sizeof(spi_rx_buffer), + SPI_FIRST, + SPI_NOTLAST); + + *pLenRead = spi_rx_buffer[1]; + + if (*pLenRead>2 && *pLenRead<=127) { + // valid length + + //read packet + spi_txrx(spi_tx_buffer, + *pLenRead, + SPI_BUFFER, + pBufRead, + 125, + SPI_NOTFIRST, + SPI_NOTLAST); + + // CRC (2B) and LQI (1B) + spi_txrx(spi_tx_buffer, + 2+1, + SPI_BUFFER, + spi_rx_buffer, + 3, + SPI_NOTFIRST, + SPI_LAST); + + *pLqi = spi_rx_buffer[2]; + + } else { + // invalid length + + // read a just byte to close spi + spi_txrx(spi_tx_buffer, + 1, + SPI_BUFFER, + spi_rx_buffer, + sizeof(spi_rx_buffer), + SPI_NOTFIRST, + SPI_LAST); + } + } + + //=========================== callbacks ======================================= + + //=========================== interrupt handlers ============================== + + kick_scheduler_t radio_isr() { + PORT_TIMER_WIDTH capturedTime; + uint8_t irq_status; + + // capture the time + capturedTime = radiotimer_getCapturedTime(); + + // reading IRQ_STATUS causes radio's IRQ pin to go low + irq_status = radio_spiReadReg(RG_IRQ_STATUS); + + // start of frame event + if (irq_status & AT_IRQ_RX_START) { + DEBUG("Start of frame.\n"); + // change state + radio_vars.state = RADIOSTATE_RECEIVING; + if (radio_vars.startFrame_cb!=NULL) { + // call the callback + radio_vars.startFrame_cb(capturedTime); + // kick the OS + return KICK_SCHEDULER; + } else { + while(1); + } + } + // end of frame event + if (irq_status & AT_IRQ_TRX_END) { + DEBUG("End of Frame.\n"); + // change state + radio_vars.state = RADIOSTATE_TXRX_DONE; + if (radio_vars.endFrame_cb!=NULL) { + // call the callback + radio_vars.endFrame_cb(capturedTime); + // kick the OS + return KICK_SCHEDULER; + } else { + while(1); + } + } + + return DO_NOT_KICK_SCHEDULER; + }