/** * @brief Switches the PLL on */ static void switch_pll_on(void) { uint32_t start_time; uint32_t current_time; /* Check if trx is in TRX_OFF; only from PLL_ON the following procedure is applicable */ if (pal_trx_bit_read(SR_TRX_STATUS) != TRX_OFF) { ASSERT("Switch PLL_ON failed, because trx is not in TRX_OFF" == 0); return; } /* Clear all pending trx interrupts */ pal_trx_reg_read(RG_IRQ_STATUS); /* Get current IRQ mask */ uint8_t trx_irq_mask = pal_trx_reg_read(RG_IRQ_MASK); /* Enable transceiver's PLL lock interrupt */ pal_trx_reg_write(RG_IRQ_MASK, TRX_IRQ_0_PLL_LOCK); ENTER_TRX_REGION(); // Disable trx interrupt handling /* Switch PLL on */ pal_trx_reg_write(RG_TRX_STATE, CMD_PLL_ON); pal_get_current_time(&start_time); /* Wait for transceiver interrupt: check for IRQ line */ while (PAL_TRX_IRQ_HIGH() == false) { /* Handle errata "potential long PLL settling duration". */ pal_get_current_time(¤t_time); if (pal_sub_time_us(current_time, start_time) > PLL_LOCK_DURATION_MAX_US) { uint8_t reg_value; reg_value = pal_trx_reg_read(RG_PLL_CF); if (reg_value & 0x01) { reg_value &= 0xFE; } else { reg_value |= 0x01; } pal_trx_reg_write(RG_PLL_CF, reg_value); pal_get_current_time(&start_time); } /* Wait until trx line has been raised. */ } /* Clear PLL lock interrupt at trx */ pal_trx_reg_read(RG_IRQ_STATUS); /* Clear MCU's interrupt flag */ pal_trx_irq_flag_clr(); LEAVE_TRX_REGION(); // Enable trx interrupt handling again /* Restore transceiver's interrupt mask. */ pal_trx_reg_write(RG_IRQ_MASK, trx_irq_mask); }
/** * @brief Switches the PLL on */ static void switch_pll_on(void) { trx_irq_reason_t irq_status; uint32_t start_time, now; /* Check if trx is in TRX_OFF; only from PLL_ON the following procedure is applicable */ if (pal_trx_bit_read(SR_TRX_STATUS) != TRX_OFF) { ASSERT("Switch PLL_ON failed, because trx is not in TRX_OFF" == 0); return; } /* use the IRQ status register checking for the actual PLL status */ pal_trx_irq_dis(); pal_trx_reg_write(RG_IRQ_MASK, TRX_IRQ_PLL_LOCK); /* allow PLL lock IRQ only*/ pal_trx_reg_read(RG_IRQ_STATUS); /* clear PLL lock bit */ /* Switch PLL on */ pal_trx_reg_write(RG_TRX_STATE, CMD_PLL_ON); /* Check if PLL has been locked. */ pal_get_current_time(&start_time); while (1) { irq_status = (trx_irq_reason_t)pal_trx_reg_read(RG_IRQ_STATUS); if (irq_status & TRX_IRQ_PLL_LOCK) { break; // PLL is locked now } /* Check if polling needs too much time. */ pal_get_current_time(&now); if (pal_sub_time_us(now, start_time) > (10 * PLL_LOCK_TIME_US)) { /* leave poll loop and throw assertion */ #if (DEBUG > 0) ASSERT("PLL switch failed" == 0); #endif break; } } pal_trx_irq_flag_clr(); pal_trx_reg_write(RG_IRQ_MASK, TRX_IRQ_TRX_END); /* enable TRX_END interrupt */ pal_trx_irq_en(); }
/** * \brief Calculates backoff duration and handles the start of the CCA */ static void csma_backoff_calculation(void) { uint32_t current_CAP_duration_sym; uint32_t current_CAP_end_sym; uint32_t next_backoff_boundary_us; uint32_t now_time_sym; uint32_t guard_time_before_next_beacon; /* \TODO consider CFP and BLE mode */ current_CAP_duration_sym = TAL_GET_SUPERFRAME_DURATION_TIME( tal_pib.SuperFrameOrder); current_CAP_end_sym = tal_add_time_symbols(tal_pib.BeaconTxTime, current_CAP_duration_sym); /* * Add some guard time to ensure that the transaction is completed * before * the timer fires that is going to track the next beacon. */ guard_time_before_next_beacon = TAL_RADIO_WAKEUP_TIME_SYM << (tal_pib.BeaconOrder + 2); guard_time_before_next_beacon += TAL_CONVERT_US_TO_SYMBOLS( PRE_BEACON_GUARD_TIME_US); current_CAP_end_sym = tal_sub_time_symbols(current_CAP_end_sym, guard_time_before_next_beacon); /* Calculate next backoff period boundary. */ { uint32_t time_since_last_beacon_sym; uint32_t next_backoff_boundary_period; pal_get_current_time(&now_time_sym); now_time_sym = TAL_CONVERT_US_TO_SYMBOLS(now_time_sym); time_since_last_beacon_sym = tal_sub_time_symbols(now_time_sym, tal_pib.BeaconTxTime); next_backoff_boundary_period = time_since_last_beacon_sym / aUnitBackoffPeriod; if ((time_since_last_beacon_sym % aUnitBackoffPeriod) > 0) { next_backoff_boundary_period++; } next_backoff_boundary_us = TAL_CONVERT_SYMBOLS_TO_US( pal_add_time_us(tal_pib.BeaconTxTime, (next_backoff_boundary_period * aUnitBackoffPeriod))); } /* Check if we are still within the CAP. */ if (next_backoff_boundary_us >= TAL_CONVERT_SYMBOLS_TO_US(current_CAP_end_sym)) { /* current CAP is over, wait for next CAP */ tal_csma_state = BACKOFF_WAITING_FOR_BEACON; start_beacon_loss_timer(); } else { /* next backoff boundary is within current CAP */ uint32_t remaining_periods_in_CAP; /* \TODO check if variable * size can be reduced */ /* Check if the remaining backoff time will expire in current * CAP. */ remaining_periods_in_CAP = tal_sub_time_symbols( current_CAP_end_sym, now_time_sym) / aUnitBackoffPeriod; if (remaining_backoff_periods > remaining_periods_in_CAP) { /* * Reduce the backoff peridos by the remaining duration * in * the current CAP and continue in next CAP. */ remaining_backoff_periods -= remaining_periods_in_CAP; tal_csma_state = BACKOFF_WAITING_FOR_BEACON; start_beacon_loss_timer(); } else { /* there are enough backoff periods in current CAP */ uint32_t time_after_transaction_sym; /* \TODO check if * variable size * can be reduced **/ uint32_t transaction_duration_sym; /* \TODO check if * variable size can * be reduced */ /* Add some guard time to wakeup the transceiver. */ transaction_duration_sym = (transaction_duration_periods * aUnitBackoffPeriod) + TAL_CONVERT_US_TO_SYMBOLS( SLEEP_TO_TRX_OFF_TYP_US + CCA_GUARD_DURATION_US); time_after_transaction_sym = tal_add_time_symbols(TAL_CONVERT_US_TO_SYMBOLS( next_backoff_boundary_us), transaction_duration_sym); /* Check if the entire transaction fits into the current * CAP. */ if (time_after_transaction_sym < current_CAP_end_sym) { retval_t timer_status; uint32_t callback_start_time; /* Calculate the time needed to backoff. */ cca_starttime_us = pal_add_time_us( next_backoff_boundary_us, TAL_CONVERT_SYMBOLS_TO_US( remaining_backoff_periods * aUnitBackoffPeriod)); /* * Ensure that wakeup time is available before * CCA. * The required duration depends on the current * trx status. * Assume here the worst case: trx is in SLEEP. */ /* * \TODO depending on the duration that we need * to backoff, * set trx to SLEEP, TRX_OFF or PLL_ON * meanwhile. */ while (pal_sub_time_us(cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US( now_time_sym)) < (SLEEP_TO_TRX_OFF_TYP_US + CCA_GUARD_DURATION_US)) { cca_starttime_us = pal_add_time_us( cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US( aUnitBackoffPeriod)); } /* * Start the CCA timer. * Add some time to locate the next backoff * boundary * once CCA timer fires. */ callback_start_time = pal_sub_time_us(cca_starttime_us, (SLEEP_TO_TRX_OFF_TYP_US + CCA_PREPARATION_DURATION_US)); timer_status = pal_timer_start(TAL_CSMA_CCA, callback_start_time, TIMEOUT_ABSOLUTE, (FUNC_PTR)cca_timer_handler_cb, NULL); if (timer_status == MAC_SUCCESS) { tal_csma_state = BACKOFF_WAITING_FOR_CCA_TIMER; } else if (timer_status == PAL_TMR_INVALID_TIMEOUT) { /* Start the CCA immediately. */ cca_timer_handler_cb(NULL); } else { tal_csma_state = CSMA_ACCESS_FAILURE; Assert("CCA timer start problem" == 0); } /* debug pin to switch on: define * ENABLE_DEBUG_PINS, pal_config.h */ PIN_BACKOFF_START(); } else { /* Restart again after next beacon. */ NB = 0; remaining_backoff_periods = (uint8_t)(rand() & ((1 << BE) - 1)); tal_csma_state = BACKOFF_WAITING_FOR_BEACON; start_beacon_loss_timer(); } } } }
/** * @brief Resets transceiver(s) * * @param trx_id Transceiver identifier * * @return MAC_SUCCESS if the transceiver returns TRX_OFF * FAILURE otherwise */ retval_t trx_reset(trx_id_t trx_id) { ENTER_TRX_REGION(); uint32_t start_time; uint32_t current_time; pal_get_current_time(&start_time); if (trx_id == RFBOTH) { TAL_RF_IRQ_CLR_ALL(RF09); TAL_RF_IRQ_CLR_ALL(RF24); tal_state[RF09] = TAL_RESET; tal_state[RF24] = TAL_RESET; /* Apply reset pulse; low active */ #ifdef IQ_RADIO RST_LOW(); PAL_WAIT_1_US(); PAL_WAIT_1_US(); RST_HIGH(); #if (BOARD_TYPE == EVAL215_FPGA) pal_timer_delay(10000); #endif RST_LOW(); PAL_WAIT_1_US(); RST_HIGH(); #else RST_LOW(); PAL_WAIT_1_US(); RST_HIGH(); #endif /* Wait for IRQ line */ while (1) { /* * @ToDo: Use a different macro for IRQ line; the *polarity might be * different after reset */ #ifdef IQ_RADIO if ((PAL_DEV_IRQ_GET(RF215_BB) == HIGH) && (PAL_DEV_IRQ_GET(RF215_RF) == HIGH)) { break; } #else if (TRX_IRQ_GET() == HIGH) { break; } #endif /* Handle timeout */ pal_get_current_time(¤t_time); /* @ToDo: Remove magic number */ if (pal_sub_time_us(current_time, start_time) > 1000) { return FAILURE; } } #ifdef IQ_RADIO trx_state[RF09] = (rf_cmd_state_t)pal_dev_reg_read(RF215_RF, RG_RF09_STATE); trx_state[RF24] = (rf_cmd_state_t)pal_dev_reg_read(RF215_RF, RG_RF24_STATE); rf_cmd_state_t bb_trx_state[NUM_TRX]; bb_trx_state[RF09] = (rf_cmd_state_t)pal_dev_reg_read(RF215_BB, RG_RF09_STATE); bb_trx_state[RF24] = (rf_cmd_state_t)pal_dev_reg_read(RF215_BB, RG_RF24_STATE); if ((bb_trx_state[RF09] != RF_TRXOFF) || (bb_trx_state[RF24] != RF_TRXOFF)) { return FAILURE; } #else trx_state[RF09] = trx_reg_read(RG_RF09_STATE); trx_state[RF24] = trx_reg_read(RG_RF24_STATE); #endif if ((trx_state[RF09] != RF_TRXOFF) || (trx_state[RF24] != RF_TRXOFF)) { return FAILURE; } /* Get all IRQ status information */ #ifdef IQ_RADIO bb_irq_handler_cb(); rf_irq_handler_cb(); #else trx_irq_handler_cb(); #endif TAL_RF_IRQ_CLR(RF09, RF_IRQ_WAKEUP); TAL_RF_IRQ_CLR(RF24, RF_IRQ_WAKEUP); } else { TAL_RF_IRQ_CLR_ALL(trx_id); tal_state[trx_id] = TAL_RESET; /* Trigger reset of device */ uint16_t reg_offset = RF_BASE_ADDR_OFFSET * trx_id; #ifdef IQ_RADIO pal_trx_reg_write(RF215_RF, reg_offset + RG_RF09_CMD, RF_RESET); pal_trx_reg_write(RF215_BB, reg_offset + RG_RF09_CMD, RF_RESET); #else trx_reg_write(reg_offset + RG_RF09_CMD, RF_RESET); #endif /* Wait for IRQ line */ while (1) { #ifdef IQ_RADIO if ((PAL_DEV_IRQ_GET(RF215_BB) == HIGH) && (PAL_DEV_IRQ_GET(RF215_RF) == HIGH)) { break; } #else if (TRX_IRQ_GET() == HIGH) { break; } #endif /* Handle timeout */ pal_get_current_time(¤t_time); /* @ToDo: Remove magic number */ if (pal_sub_time_us(current_time, start_time) > 1000) { return FAILURE; } } trx_state[trx_id] = RF_TRXOFF; /* Get all IRQ status information */ #ifdef IQ_RADIO bb_irq_handler_cb(); rf_irq_handler_cb(); #else trx_irq_handler_cb(); #endif TAL_RF_IRQ_CLR(trx_id, RF_IRQ_WAKEUP); } #ifdef IQ_RADIO pal_trx_irq_flag_clr(RF215_BB); pal_trx_irq_flag_clr(RF215_RF); #else pal_trx_irq_flag_clr(); #endif LEAVE_TRX_REGION(); return MAC_SUCCESS; }