/****************************************************************************** * @fn cc115LRxTxISR * * @brief ISR that's called when sync signal goes low. * In RX State: Filters incoming data. The global rxData pointer * always points to this functions static rxData_tmp(struct of * same kind). The validnes of rxData fields is indicated by the * the global flag packetSemaphore. * In TX State: Nothing is done except it facilitates power * consumption reduction when TX since the program doesn't need * to wait until TX is done before re-enabling sync pin interrupt. * cc11xLRadioTxRx is also set to CC115L_STATE_IDLE to be consistent with * program. * * input parameters * * @param none * * output parameters * * @return void */ void perCC115LRxTxISR(void) { /* This variable stores the data locally. Access is given to per_test by * assigning this instance to the global rxData pointer */ static rxData_t rxData_tmp; rxData = &rxData_tmp; packetSemaphore |= SYNC_FOUND; /* Only relevant for 1-way PER test. In case of receiver not finding sync, * the MSP will sample the RSSI value right after the instant where the packet * was supposed to be received. By setting the 32k timer at this point, the * sample instant will be very close to the end of the wanted packet. The RSSI * value will hence hold lot of the signal power from the packet. */ if((perSettings.masterSlaveLinked == PER_DEVICE_LINKED) && (perSettings.deviceMode == MASTER_DEVICE) && (perSettings.testRunning == PER_TRUE)) { if(perSettings.linkTopology == LINK_1_WAY) { /* Read timer value and set the perSettings.packetRate valu(adjustment for temperature drift */ halTimer32kSetIntFrequency(perSettings.packetRate); halTimer32kIntEnable(); } } cc115LRadioTxIdle = CC115L_STATE_IDLE; return; }
/****************************************************************************** * @fn halTimer32kMcuSleepTicks * * @brief This function uses Timer B to sleep for a specfied number of * ACLK ticks, that is less than 2^15 tics(1s). * Assumes that the only interrupt source is * generated by Timer B. * * NOTE: When called, this function will assign NO ISR to the * TIMERB0_VECTOR interrupt(NULL pointer). When interrupt triggers * it will just wake up the MCU. Conflicts are possible if not * used carefully since other parts of a program might use the * TIMER B. * * input parameters * * @param ticks - Number of ACLK(32768Hz) ticks that the MCU will sleep * * output parameters * * @return void */ void halTimer32kMcuSleepTicks(uint16 ticks) { halTimer32kIntConnect(NULL); halTimer32kInit(ticks); halTimer32kIntEnable(); __low_power_mode_3(); halTimer32kAbort(); }
/*********************************************************************************** * @fn main * * @brief This is the main entry of the RF Modem application. It sets * distinct short addresses for the nodes, initalises and runs * receiver and sender tasks sequentially in an endless loop. * * @return none */ void main(void) { char *szTitle= "MRFI RF modem"; appUartRxIdle = FALSE; // Initialise board peripherals halBoardInit(); halUartInit(HAL_UART_BAUDRATE_38400, 0); // 100 ms RX idle timeout appConfigTimer(1000/UART_RX_IDLE_TIME); // Indicate that the application has been initialised halLcdClear(); halLcdWriteLine(HAL_LCD_LINE_1, szTitle); halLedSet(1); // Select application role (Device 1, Device 2 or Loopback) appSelectRole(); if (appRole != DEVICE_LOOPBACK) { // Initialize the MRFI RF link layer mrfiLinkInit(appLocalAddr,appRemoteAddr,MRFI_CHANNEL); } // Indicate that the modem is operating halLcdWriteLine(HAL_LCD_LINE_1, szTitle); // Initialise error counters nTxErr= nRxErr= 0; // Enable RX idle timeout interrupt halTimer32kIntEnable(); // Main processing loop while(TRUE) { // On-board device processing (UART etc.) HAL_PROCESS(); if (appRole == DEVICE_LOOPBACK) { // Loopback processing appLoopbackTask(); } else { // RF transmitter processing appRfSenderTask(); // RF receiver processing appRfReceiverTask(); } } }
/*********************************************************************************** * @fn appRfSenderTask * * @brief Checks if new bytes have arrived from the UART. If there * are enough bytes to fill a maximal sized packet, or if the UART * is idle, the bytes are transmitted on the air. * * @param none * * @return none */ static void appRfSenderTask(void) { uint8 nBytes; uint8 payloadLength; uint8 bytesToRead; nBytes = halUartGetNumRxBytes(); payloadLength= 0; bytesToRead= 0; if(nBytes >= APP_PAYLOAD_LENGTH || (appUartRxIdle && nBytes>0) ) { // Signal PC not to send on UART, while sending on air. halUartEnableRxFlow(FALSE); // Wait for PC to respond halMcuWaitUs(1000); bytesToRead = MIN(nBytes, APP_PAYLOAD_LENGTH); halUartRead(pTxData,bytesToRead); payloadLength+= bytesToRead; halLedToggle(3); if( (mrfiLinkSend(pTxData, payloadLength,N_RETRIES)) != MRFI_TX_RESULT_SUCCESS) { nTxErr++; appUpdateDisplay(); } // Signal RX flow on halUartEnableRxFlow(TRUE); // Restart idle timer halTimer32kRestart(); halTimer32kIntEnable(); // Reset idle fimer flag appUartRxIdle = FALSE; } }
/****************************************************************************** * @fn perCC1120CC1190RxTxISR * * @brief ISR that's called when sync signal goes low. * In RX State: Filters incoming data. The global rxData pointer * always points to this functions static rxData_tmp(struct of * same kind). The validnes of rxData fields is indicated by the * the global flag packetSemaphore. * In TX State: Nothing is done except it facilitates power * consumption reduction when TX since the program doesn't need * to wait until TX is done before re-enabling sync pin interrupt. * cc1120cc1190RadioTxRx is also set to CC112X_STATE_IDLE to be consistent * with program. * * input parameters * * @param none * * output parameters * * @return void */ void perCC1120CC1190RxTxISR(void) { uint8 rxBytes,rxLength,rssiIndex,lqiIndex; /* This variable stores the data locally. Access is given to per_test by * assigning this instance to the global rxData pointer */ static rxData_t rxData_tmp; rxData = &rxData_tmp; /* Checking if the chip is in RX state: */ if(cc1120cc1190RadioTxRx != CC112X_STATE_RX) { /* Transmission finished */ if((perSettings.deviceMode == MASTER_DEVICE) && (perSettings.linkTopology == LINK_2_WAY) && (perSettings.masterSlaveLinked ==PER_DEVICE_LINKED)) { /* Only applicable when master in 2-way test */ cc1120cc1190RadioTxRx=CC112X_STATE_RX; } else { cc1120cc1190RadioTxRx = CC112X_STATE_IDLE; } return; } packetSemaphore |= SYNC_FOUND; if(((perSettings.masterSlaveLinked == PER_DEVICE_LINKED)||(perSettings.masterSlaveLinked == PER_DEVICE_LINK_BYPASS)) && (perSettings.deviceMode == MASTER_DEVICE) && (perSettings.testRunning == PER_TRUE)) { if(perSettings.linkTopology == LINK_1_WAY) { /* Read timer value and set the perSettings.packetRate valu(adjustment for temperature drift */ halTimer32kSetIntFrequency(perSettings.packetRate); halTimer32kIntEnable(); } else { /* LINK_2_WAY */ /* Timeout interrupt configuring is handled by the 2-way Per test */ timer32kValue = halTimer32kReadTimerValue(); halTimer32kAbort(); } } cc112xSpiReadReg(CC112X_NUM_RXBYTES,&rxBytes,1); /* Checking if the FIFO is empty */ if(rxBytes == PER_FALSE) { /* The packet was removed by HW due to addr or length filtering -> Do nothing */ /* Report that a sync was detected */ rxData_tmp.rssi = perCC1120CC1190Read8BitRssi(); return; } else { /* The RX FIFO is not empty, process contents */ cc112xSpiReadRxFifo(&rxLength, 1); /* Check that the packet length just read + FCS(2B) + length byte match the RXBYTES */ /* If these are not equal: * - RXFIFO overflow: Received packets not processed while beeing in RX. */ if(rxBytes != (rxLength+3)) { /* This is a fault FIFO condition -> clean FIFO and register a sync detection */ /* IDLE -> FLUSH RX FIFO -> RX */ cc1120cc1190RxIdle(); perCC1120CC1190EnterRx(); /* Report that a sync was detected */ rxData_tmp.rssi = perCC1120CC1190Read8BitRssi(); return; } else { /* We don't have a FIFO error condition -> get packet */ /* Length Field */ rxData_tmp.data[0] = rxLength; rssiIndex = rxLength+1; lqiIndex = rssiIndex +1; /* Payload(ADDR + DATA + FCS) */ cc112xSpiReadRxFifo(&rxData_tmp.data[1], lqiIndex); /* The whole packet has been read from the FIFO. * Check if the CRC is correct and that the packet length is as expected. * If not correct: report sync found and do not update RSSI or LQI. */ if((!(rxData_tmp.data[lqiIndex] & CC112X_LQI_CRC_OK_BM)) || (perSettings.payloadLength != rxLength )) { rxData_tmp.rssi = perCC1120CC1190Read8BitRssi(); return; } /* A complete error-free packet has arrived */ /* Measured data */ rxData_tmp.length = rxLength; rxData_tmp.lqi = rxData_tmp.data[lqiIndex] & CC112X_LQI_EST_BM; rxData_tmp.addr = rxData_tmp.data[1]; /* Convert RSSI value from 2's complement to decimal value accounting for offset value */ rxBytes = rxData_tmp.data[rssiIndex]; rxData_tmp.rssi = (int8)((int8)rxBytes) - cc1120cc1190RssiOffset; /* Signal a good packet is received */ packetSemaphore |= PACKET_RECEIVED; return; } } }
int main(void) { #error "Hi, Currently not working, still to be tested! - I didn't find time to debug it! " //halIntOn(); unsigned long pktsSent = 0; perConfig.mode = PER_MODE_TX; perConfig.state = PER_IDLE; perConfig.channel = 26; perConfig.txPower = 0; // Index 0. Max output perConfig.burstSize = 1000000; // Max value perConfig.pktRate = 20; // 20 pkts per second perConfig.gainMode = PER_GAIN_MODE_NONE; // No PA/LNA // // Config basicRF // basicRfConfig.panId = PAN_ID; basicRfConfig.ackRequest = false; if(basicRfInit(&basicRfConfig) == FAILED) { while(1); } basicRfReceiveOff(); halRfSetTxPower(0); // appTimerConfig(20); halTimer32kInit(32768/20); halTimer32kIntConnect(&appTimerIsr); // Connect ISR halTimer32kIntEnable(); // Enable interrupts while(1) { if(perConfig.state == PER_TRANSMIT) { if(pktsSent < perConfig.burstSize) { // // Make sure sequence number has network byte order // UINT32_HTON(tTxPacket.seqNumber); basicRfSendPacket(RX_ADDR, (unsigned char*)&tTxPacket, PACKET_SIZE); // // Change byte order back to host order before increment // UINT32_NTOH(tTxPacket.seqNumber); // // Update variables // tTxPacket.seqNumber++; pktsSent++; perConfig.state = PER_PACKET_RECEIVED; // // Update LED // // bspLedToggle(BSP_LED_1); } else { // // Done sending packets, exit TX loop // break; } } } }
/****************************************************************************** * @fn trxDetectCC112xCrystal() * * @brief This function estimates the crystal frequency if a CC112x * is detected. * SPI init must be applied before this function * can be called. * * @param none * * @return none */ static uint8 trxDetectCC112xCrystal() { // Write EXT CLOCK to IOCFG3 and IOCFG2 uint8 writeBytes1[2] = {0x31, 0x31}; trx8BitRegAccess(CC112X_WRITE_BURST, CC112X_IOCFG3, writeBytes1, 2); // set external clock divider to 32 writeBytes1[0] = 0x00; trx16BitRegAccess(CC112X_WRITE_BURST,CC112X_EXT_MEM_ACCESS,CC112X_ECG_CFG,writeBytes1,1); //wait for crystal to be stable (CHIP_RDYn) while((trxSpiCmdStrobe(CC112X_SNOP)& 0xF0) != 0x00); //get system clock frequency uint32_t systemClockBak = bspSysClockSpeedGet(); //set system clock frequency up to 25 MHz for accurate sampling bspSysClockSpeedSet(BSP_SYS_CLK_25MHZ); // initialize timerA to capture rising and falling edges on EXT CLOCK cc112xInitTimerA(); // Setting up time out in case we hang wating for capture interrupt halTimer32kIntConnect(&timeOutISR); halTimer32kSetIntFrequency(1); // 1 sec timeout halTimer32kIntEnable(); // wait for interrupt on timer capture or timeout while((!timerSemaphore) && (!timeoutSemaphore)); // stop timeuot timer halTimer32kIntDisable(); // stop timer disableTimerA(); if(timerSemaphore) { // assuming 50% duty cycle. Period time = time between rising and // falling edge x 2 capturePeriod = (captureTable[1] - captureTable[0])*2; //check for negative number and set absolute value capturePeriod= (capturePeriod<0)?(0-capturePeriod):capturePeriod; // Claculate XOSC frequency in MHz: // system clock frequency / capturePeriod // times external clock divider (32) // times digital clock divider (2) floatingEstimate = (((25.0*32.0*2.0)/capturePeriod)); //Round up/down estimated frequency and truncate to int xoscFreqEstimate = (uint8) (floatingEstimate + 0.5); } else { xoscFreqEstimate = XOSC_FREQ_NONE; } //set system clock frequency back to standard bspSysClockSpeedSet(systemClockBak); //reset radio trxSpiCmdStrobe(CC112X_SRES); return xoscFreqEstimate; }
/****************************************************************************** * @fn trxDetectCC1101Crystal() * * @brief This function estimates the crystal frequency if a NextGen * radio is detected. * SPI init must be applied before this function * can be called. * * @param none * * @return none */ static uint8 trxDetectCC1101Crystal() { // Write CLOCK_XOSC/192 to GDO2 uint8 writeByte = 0x3F; trx8BitRegAccess(CC1101_WRITE_BURST, CC1101_IOCFG2, &writeByte, 1); //Wait for crystal to be stable (CHIP_RDYn) while((trxSpiCmdStrobe(CC1101_SNOP)& 0xF0) != 0x00); //Get current system clock frequency uint32_t systemClockBak = bspSysClockSpeedGet(); //set system clock frequency up to 25 MHz for accurate sampling bspSysClockSpeedSet(BSP_SYS_CLK_25MHZ); // initialize timerA to capture rising edges on CLOCK XOSC cc1101InitTimerA(); // Setting up time out in case we hang wating for capture interrupt halTimer32kIntConnect(&timeOutISR); halTimer32kSetIntFrequency(1); // 1 sec timeout halTimer32kIntEnable(); // wait for interrupt on timer capture or timeout while((!timerSemaphore) && (!timeoutSemaphore)); // stop timeuot timer halTimer32kIntDisable(); // stop timer disableTimerA(); if(timerSemaphore) { // assuming 50% duty cycle. Period time = time between rising and // falling edge x 2 capturePeriod = (captureTable[1] - captureTable[0])*2; //check for negative number and set absolute value capturePeriod= (capturePeriod<0)?(0-capturePeriod):capturePeriod; // Claculate XOSC frequency in MHz: // system clock frequency / capturePeriod // times clock xosc divider (192) floatingEstimate = (((25.0*192.0)/capturePeriod)); //Round up/down estimated frequency and truncate to int xoscFreqEstimate = (uint8) (floatingEstimate + 0.5); } else { xoscFreqEstimate = XOSC_FREQ_NONE; } //set system clock frequency back to standard bspSysClockSpeedSet(systemClockBak); //reset radio trxSpiCmdStrobe(CC1101_SRES); return xoscFreqEstimate; }