/* * Bytes start with START bit (0) followed by 8 data bits and then the * STOP bit (1). STOP bit should be configurable. Data bits are sent LSB first. */ static void uart_bitbang_tx_timer(void *arg) { struct uart_bitbang *ub = (struct uart_bitbang *)arg; uint32_t next = 0; int data; if (!ub->ub_txing || ub->ub_tx.bits > 9) { if (ub->ub_tx.bits > 9) { if (ub->ub_tx_done) { ub->ub_tx_done(ub->ub_func_arg); } } data = ub->ub_tx_func(ub->ub_func_arg); if (data < 0) { ub->ub_txing = 0; return; } else { ub->ub_tx.byte = data; } /* * Start bit */ hal_gpio_write(ub->ub_tx.pin, 0); ub->ub_tx.start = cputime_get32(); next = ub->ub_tx.start + ub->ub_bittime; ub->ub_txing = 1; ub->ub_tx.bits = 0; } else { if (ub->ub_tx.bits++ < 8) { hal_gpio_write(ub->ub_tx.pin, ub->ub_tx.byte & 0x01); ub->ub_tx.byte = ub->ub_tx.byte >> 1; next = ub->ub_tx.start + (ub->ub_bittime * (ub->ub_tx.bits + 1)); } else {
uint64_t cputime_get64(void) { uint32_t ctx; uint32_t high; uint32_t low; uint64_t cpu_time; __HAL_DISABLE_INTERRUPTS(ctx); high = g_cputime.cputime_high; low = cputime_get32(); if (CPUTIMER->EVENTS_COMPARE[CPUTIMER_CC_OVERFLOW]) { ++high; low = cputime_get32(); } __HAL_ENABLE_INTERRUPTS(ctx); cpu_time = ((uint64_t)high << 32) | low; return cpu_time; }
static void ble_ll_adv_set_sched(struct ble_ll_adv_sm *advsm, int sched_new) { uint32_t max_usecs; struct ble_ll_sched_item *sch; sch = &advsm->adv_sch; sch->cb_arg = advsm; sch->sched_cb = ble_ll_adv_tx_start_cb; sch->sched_type = BLE_LL_SCHED_TYPE_ADV; /* Set end time to maximum time this schedule item may take */ max_usecs = BLE_TX_DUR_USECS_M(advsm->adv_pdu_len); switch (advsm->adv_type) { case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: max_usecs += BLE_LL_ADV_DIRECT_SCHED_MAX_USECS; break; case BLE_HCI_ADV_TYPE_ADV_IND: case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: max_usecs += BLE_LL_ADV_SCHED_MAX_USECS; break; default: break; } /* * XXX: For now, just schedule some additional time so we insure we have * enough time to do everything we want. */ max_usecs += XCVR_PROC_DELAY_USECS; if (sched_new) { /* * We have to add the scheduling delay and tx start delay to the max * time of the event since the pdu does not start at the scheduled start. */ max_usecs += XCVR_TX_SCHED_DELAY_USECS; sch->start_time = cputime_get32(); sch->end_time = sch->start_time + cputime_usecs_to_ticks(max_usecs); } else { sch->start_time = advsm->adv_pdu_start_time - cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); sch->end_time = advsm->adv_pdu_start_time + cputime_usecs_to_ticks(max_usecs); } }
/** * Called to set the start time of a transmission. * * This function is called to set the start time when we are not going from * rx to tx automatically. * * NOTE: care must be taken when calling this function. The channel should * already be set. * * @param cputime * * @return int */ int ble_phy_tx_set_start_time(uint32_t cputime) { int rc; NRF_TIMER0->CC[0] = cputime; NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk; NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; if ((int32_t)(cputime_get32() - cputime) >= 0) { STATS_INC(ble_phy_stats, tx_late); ble_phy_disable(); rc = BLE_PHY_ERR_TX_LATE; } else { rc = 0; } return rc; }
/** * cputime set ocmp * * Set the OCMP used by the cputime module to the desired cputime. * * NOTE: Must be called with interrupts disabled. * * @param timer Pointer to timer. */ void cputime_set_ocmp(struct cpu_timer *timer) { /* Disable ocmp interrupt and set new value */ cputime_disable_ocmp(); /* Set output compare register to timer expiration */ CPUTIMER->CC[CPUTIMER_CC_INT] = timer->cputime; /* Clear interrupt flag*/ CPUTIMER->EVENTS_COMPARE[CPUTIMER_CC_INT] = 0; /* Enable the output compare interrupt */ CPUTIMER->INTENSET = CPUTIMER_INT_MASK(CPUTIMER_CC_INT); /* Force interrupt to occur as we may have missed it */ if ((int32_t)(cputime_get32() - timer->cputime) >= 0) { NVIC_SetPendingIRQ(CPUTIMER_IRQ); } }
/** * Called when an advertising event is over. * * Context: Link Layer task. * * @param arg Pointer to advertising state machine. */ void ble_ll_adv_event_done(void *arg) { uint8_t mask; uint8_t final_adv_chan; int32_t delta_t; uint32_t itvl; uint32_t start_time; struct ble_ll_adv_sm *advsm; /* Stop advertising event */ advsm = (struct ble_ll_adv_sm *)arg; assert(advsm->enabled); /* Remove the element from the schedule if it is still there. */ ble_ll_sched_rmv_elem(&advsm->adv_sch); os_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); /* * Check if we have ended our advertising event. If our last advertising * packet was sent on the last channel, it means we are done with this * event. */ if (advsm->adv_chanmask & 0x04) { final_adv_chan = BLE_PHY_ADV_CHAN_START + 2; } else if (advsm->adv_chanmask & 0x02) { final_adv_chan = BLE_PHY_ADV_CHAN_START + 1; } else { final_adv_chan = BLE_PHY_ADV_CHAN_START; } if (advsm->adv_chan == final_adv_chan) { /* Check if we need to resume scanning */ ble_ll_scan_chk_resume(); /* This event is over. Set adv channel to first one */ advsm->adv_chan = ble_ll_adv_first_chan(advsm); /* Calculate start time of next advertising event */ itvl = advsm->adv_itvl_usecs; if (advsm->adv_type != BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { itvl += rand() % (BLE_LL_ADV_DELAY_MS_MAX * 1000); } advsm->adv_event_start_time += cputime_usecs_to_ticks(itvl); advsm->adv_pdu_start_time = advsm->adv_event_start_time; /* * The scheduled time better be in the future! If it is not, we will * just keep advancing until we the time is in the future */ start_time = advsm->adv_pdu_start_time - cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); delta_t = (int32_t)(start_time - cputime_get32()); if (delta_t < 0) { /* Calculate start time of next advertising event */ while (delta_t < 0) { itvl = advsm->adv_itvl_usecs; if (advsm->adv_type != BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { itvl += rand() % (BLE_LL_ADV_DELAY_MS_MAX * 1000); } itvl = cputime_usecs_to_ticks(itvl); advsm->adv_event_start_time += itvl; advsm->adv_pdu_start_time = advsm->adv_event_start_time; delta_t += (int32_t)itvl; } } } else { /* * Move to next advertising channel. If not in the mask, just * increment by 1. We can do this because we already checked if we * just transmitted on the last advertising channel */ ++advsm->adv_chan; mask = 1 << (advsm->adv_chan - BLE_PHY_ADV_CHAN_START); if ((mask & advsm->adv_chanmask) == 0) { ++advsm->adv_chan; } /* * We will transmit right away. Set next pdu start time to now * plus a xcvr start delay just so we dont count late adv starts */ advsm->adv_pdu_start_time = cputime_get32() + cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); } /* * Stop high duty cycle directed advertising if we have been doing * it for more than 1.28 seconds */ if (advsm->adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { if (advsm->adv_pdu_start_time >= advsm->adv_dir_hd_end_time) { /* Disable advertising */ advsm->enabled = 0; ble_ll_conn_comp_event_send(NULL, BLE_ERR_DIR_ADV_TMO); ble_ll_scan_chk_resume(); return; } } /* Schedule advertising transmit */ ble_ll_adv_set_sched(advsm, 0); /* * In the unlikely event we cant reschedule this, just post a done * event and we will reschedule the next advertising event */ if (ble_ll_sched_adv_reschedule(&advsm->adv_sch)) { os_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); } }