/** * \brief Sends the frame at the next backoff boundary */ static void send_frame_at_next_backoff_boundary(void) { uint8_t ack_is_requested; uint32_t now_time_us; /* * Locate the next backoff boundary for the frame transmissiom; * this backoff boundary is the starttime for the frame fransmission. * Use a blocking approach, since next backoff boundary should be close. */ do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, PRE_TX_DURATION_US) < cca_starttime_us); /* re-programm the interrupt handler */ pal_trx_irq_init((FUNC_PTR)ack_reception_handler_cb); pal_trx_irq_en(); /* debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h */ PIN_TX_START(); /* Check if an acknowledgement is requested for this frame. */ ack_is_requested = *(tal_frame_to_tx + 1) & FCF_ACK_REQUEST; if (ack_is_requested > 0) { tal_csma_state = FRAME_SENDING_WITH_ACK; } else { tal_csma_state = FRAME_SENDING_NO_ACK; } /* download and send frame, no CSMA and no frame_retry */ send_frame(NO_CSMA_NO_IFS, false); }
/** * \brief Sends the frame at the next backoff boundary */ static void send_frame_at_next_backoff_boundary(void) { uint32_t now_time_us; /* * Locate the next backoff boundary for the frame transmissiom; * this backoff boundary is the starttime for the frame fransmission. * Use a blocking approach, since next backoff boundary should be close. */ do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, PRE_TX_DURATION_US) < cca_starttime_us); /* debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h */ PIN_TX_START(); tal_csma_state = FRAME_SENDING; /* download and send frame, no CSMA and no frame_retry */ send_frame(NO_CSMA_NO_IFS, false); }
/** * \brief Performs CCA twice */ static uint8_t perform_cca_twice(void) { uint8_t cca_status; uint8_t cca_done; uint8_t CW = 2; uint32_t now_time_us; do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (SLEEP_TO_TRX_OFF_TYP_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); #if ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT)) if (tal_beacon_transmission) { #if (_DEBUG_ > 0) Assert("Ongoing beacon transmission, slotted CSMA busy" == 0); #endif return PHY_BUSY; } #endif /* ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT)) */ /* Ensure that trx is at least in TRX_OFF mode at this time. */ if (tal_trx_status == TRX_SLEEP) { set_trx_state(CMD_TRX_OFF); } do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (TRX_OFF_TO_PLL_ON_TIME_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); /* * Set trx to PLL_ON. * If trx is busy and trx cannot be set to PLL_ON, assess channel as * busy. */ if (set_trx_state(CMD_PLL_ON) != PLL_ON) { return PHY_BUSY; } /* no interest in receiving frames while doing CCA */ trx_bit_write(SR_RX_PDT_DIS, RX_DISABLE); /* disable frame reception * indication */ /* do CCA twice */ do { /* wait here until 16us before backoff boundary */ /* assume TRX is in PLL_ON */ do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, CCA_PRE_START_DURATION_US) < cca_starttime_us); set_trx_state(CMD_RX_ON); /* debug pin to switch on: define ENABLE_DEBUG_PINS, * pal_config.h */ PIN_CCA_START(); /* Start CCA */ trx_bit_write(SR_CCA_REQUEST, CCA_START); /* wait until CCA is done and get status */ pal_timer_delay(TAL_CONVERT_SYMBOLS_TO_US(CCA_DURATION_SYM)); do { /* poll until CCA is really done; */ cca_done = trx_bit_read(SR_CCA_DONE); } while (cca_done != CCA_COMPLETED); /* between both CCA switch trx to PLL_ON to reduce power * consumption */ set_trx_state(CMD_PLL_ON); /* debug pin to switch on: define ENABLE_DEBUG_PINS, * pal_config.h */ PIN_CCA_END(); /* check if channel was idle or busy */ if (trx_bit_read(SR_CCA_STATUS) == CCA_CH_IDLE) { /* do next CCA at next backoff boundary */ cca_starttime_us = pal_add_time_us(cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US( aUnitBackoffPeriod)); CW--; cca_status = PHY_IDLE; } else { /* PHY busy */ cca_status = PHY_BUSY; set_trx_state(CMD_RX_AACK_ON); break; /* if channel is busy do no do CCA for the second * time */ } } while (CW > 0); /* * Keep trx ready for transmission if channel is idle. * The transceiver is still in PLL_ON. * If the channel is not idle, the trx handling is done in * csma_backoff(). */ /* * Clear CCA interrupt flag. * This is only necessary for debugging, because only in debug mode * interrupt that are not handled cause an assert in the ISR. */ #if (_DEBUG_ > 0) trx_reg_read(RG_IRQ_STATUS); #endif /* * Since we are not interested in any frames that might be received * during CCA, reject any information that indicates a previous frame * reception. */ trx_bit_write(SR_RX_PDT_DIS, RX_ENABLE); /* enable frame reception * indication */ return cca_status; }
/** * \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 Performs CCA twice */ static uint8_t perform_cca_twice(void) { uint8_t cca_status; uint8_t cca_done; uint8_t CW = 2; uint32_t now_time_us; do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (SLEEP_TO_TRX_OFF_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); #ifndef RFD if (tal_beacon_transmission) { #if (DEBUG > 0) ASSERT("Ongoing beacon transmission, slotted CSMA busy" == 0); #endif return PHY_BUSY; } #endif // Ensure that trx is at least in TRX_OFF mode at this time. if (tal_trx_status == TRX_SLEEP) { set_trx_state(CMD_TRX_OFF); } do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (PLL_LOCK_TIME_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); /* * Set trx to PLL_ON. * If trx is busy and trx cannot be set to PLL_ON, assess channel as busy. */ if (set_trx_state(CMD_PLL_ON) != PLL_ON) { return PHY_BUSY; } // no interest in receiving frames while doing CCA pal_trx_bit_write(SR_RX_PDT_DIS, RX_DISABLE); // disable frame reception indication // do CCA twice do { // wait here until 16us before backoff boundary // assume TRX is in PLL_ON do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, CCA_PRE_START_DURATION_US) < cca_starttime_us); pal_trx_reg_write(RG_TRX_STATE, CMD_RX_ON); // debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h PIN_CCA_START(); /* Start CCA */ pal_trx_bit_write(SR_CCA_REQUEST, CCA_START); // wait until CCA is done and get status pal_timer_delay(TAL_CONVERT_SYMBOLS_TO_US(CCA_DURATION_SYM)); do { // poll until CCA is really done; cca_done = pal_trx_bit_read(SR_CCA_DONE); } while (cca_done != CCA_DETECTION_DONE); // between both CCA switch trx to PLL_ON to reduce power consumption pal_trx_reg_write(RG_TRX_STATE, CMD_PLL_ON); // debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h PIN_CCA_END(); // check if channel was idle or busy if (pal_trx_bit_read(SR_CCA_STATUS) == CCA_STATUS_CHANNEL_IS_IDLE) { // do next CCA at next backoff boundary cca_starttime_us = pal_add_time_us(cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US(aUnitBackoffPeriod)); CW--; cca_status = PHY_IDLE; } else // PHY busy { cca_status = PHY_BUSY; break; // if channel is busy do no do CCA for the second time } } while (CW > 0); /* * Keep trx ready for transmission if channel is idle. * The transceiver is still in PLL_ON. * If the channel is not idle, the trx handling is done in csma_backoff(). */ /* * Clear CCA interrupt flag. * This is only necessary for debugging, because only in debug mode * interrupt that are not handled cause an assert in the ISR. */ #if (DEBUG > 0) pal_trx_reg_read(RG_IRQ_STATUS); #endif /* * Since we are not interested in any frames that might be received * during CCA, reject any information that indicates a previous frame * reception. */ pal_trx_bit_write(SR_RX_PDT_DIS, RX_ENABLE); // enable frame reception indication return cca_status; }
/** * \brief Performs CCA twice */ static uint8_t perform_cca_twice(void) { tal_trx_status_t trx_status; uint8_t cca_status; uint8_t CW = 2; uint32_t now_time_us; do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (SLEEP_TO_TRX_OFF_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); /* Ensure that trx is at least in TRX_OFF mode at this time. */ if (tal_trx_status == TRX_SLEEP) { set_trx_state(CMD_TRX_OFF); } do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (PLL_LOCK_TIME_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); /* * Set trx to PLL_ON. * If trx is busy and trx cannot be set to PLL_ON, assess channel as *busy. */ if (set_trx_state(CMD_PLL_ON) != PLL_ON) { return PHY_BUSY; } /* no interest in trx interrupts while doing CCA */ pal_trx_irq_dis(); /* do CCA twice */ do { /* wait here until 16us before backoff boundary */ /* assume TRX is in PLL_ON */ do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, CCA_PRE_START_DURATION_US) < cca_starttime_us); set_trx_state(CMD_RX_ON); /* debug pin to switch on: define ENABLE_DEBUG_PINS, *pal_config.h */ PIN_CCA_START(); /* Start CCA by writing any dummy value to this register */ pal_trx_bit_write(SR_CCA_REQUEST, 1); /* wait until CCA is done and get status */ pal_timer_delay(TAL_CONVERT_SYMBOLS_TO_US(CCA_DURATION_SYM)); do { /* poll until CCA is really done; */ trx_status = (tal_trx_status_t)pal_trx_reg_read( RG_TRX_STATUS); } while ((trx_status & CCA_DONE_BIT) != CCA_DONE_BIT); /* between both CCA switch trx to PLL_ON to reduce power *consumption */ set_trx_state(CMD_PLL_ON); /* debug pin to switch on: define ENABLE_DEBUG_PINS, *pal_config.h */ PIN_CCA_END(); /* check if channel was idle or busy */ if (trx_status & CCA_STATUS_BIT) { /* do next CCA at next backoff boundary */ cca_starttime_us = pal_add_time_us(cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US( aUnitBackoffPeriod)); CW--; cca_status = PHY_IDLE; } else { /* PHY busy */ cca_status = PHY_BUSY; set_trx_state(CMD_RX_AACK_ON); break; /* if channel is busy do no do CCA for the second * time */ } } while (CW > 0); /* * Keep trx ready for transmission if channel is idle. * The transceiver is still in PLL_ON. * If the channel is not idle, the trx handling is done in *csma_backoff(). */ /* * Since we are not interested in any frames that might be received * during CCA, reject any information that indicates a previous frame * reception. */ pal_trx_reg_read(RG_IRQ_STATUS); pal_trx_irq_flag_clr(); pal_trx_irq_en(); return cca_status; }