// ------------------------------------------------------------------------------------------------ // Reception test with interrupt handling int radio_receive_test_int(spi_parms_t *spi_parms, arguments_t *arguments) // ------------------------------------------------------------------------------------------------ { uint8_t nb_rx, rx_bytes[RADIO_BUFSIZE]; init_radio_int(spi_parms, arguments); PI_CC_SPIStrobe(spi_parms, PI_CCxxx0_SFRX); // Flush Rx FIFO verbprintf(0, "Starting...\n"); while((arguments->repetition == 0) || (packets_received < arguments->repetition)) { radio_init_rx(spi_parms, arguments); // Init for new packet to receive radio_turn_rx(spi_parms); // Put back into Rx do { radio_wait_free(); // make sure no radio operation is in progress nb_rx = radio_receive_packet(spi_parms, arguments, rx_bytes); } while(nb_rx == 0); rx_bytes[nb_rx] = '\0'; verbprintf(0,"\"%s\"\n", rx_bytes); } }
// ------------------------------------------------------------------------------------------------ // Simple echo test void radio_test_echo(spi_parms_t *spi_parms, radio_parms_t *radio_parms, arguments_t *arguments, uint8_t active) // ------------------------------------------------------------------------------------------------ { uint8_t nb_bytes, rtx_bytes[RADIO_BUFSIZE]; uint8_t rtx_toggle, rtx_count; uint32_t timeout_value, timeout; struct timeval tdelay, tstart, tstop; init_radio_int(spi_parms, arguments); radio_flush_fifos(spi_parms); timeout_value = (uint32_t) (arguments->packet_length * 10 * radio_get_byte_time(radio_parms)); timeout = 0; if (active) { nb_bytes = strlen(arguments->test_phrase); strcpy(rtx_bytes, arguments->test_phrase); rtx_toggle = 1; } else { rtx_toggle = 0; } while (packets_sent < arguments->repetition) { rtx_count = 0; do // Rx-Tx transaction in whichever order { if (arguments->tnc_keyup_delay) { usleep(arguments->tnc_keyup_delay); } if (rtx_toggle) // Tx { verbprintf(0, "Sending #%d\n", packets_sent); radio_wait_free(); // make sure no radio operation is in progress radio_send_packet(spi_parms, arguments, rtx_bytes, nb_bytes); radio_wait_a_bit(4); timeout = timeout_value; // arm Rx timeout rtx_count++; rtx_toggle = 0; // next is Rx } if (rtx_count >= 2) { break; } if (arguments->tnc_keydown_delay) { usleep(arguments->tnc_keydown_delay); } if (!rtx_toggle) // Rx { verbprintf(0, "Receiving #%d\n", packets_received); radio_init_rx(spi_parms, arguments); // Init for new packet to receive radio_turn_rx(spi_parms); // Put back into Rx if (timeout > 0) { gettimeofday(&tstart, NULL); } do { radio_wait_free(); // make sure no radio operation is in progress nb_bytes = radio_receive_packet(spi_parms, arguments, rtx_bytes); radio_wait_a_bit(4); if (timeout > 0) { gettimeofday(&tstop, NULL); timeval_subtract(&tdelay, &tstop, &tstart); if (ts_us(&tdelay) > timeout) { verbprintf(0, "Time out reached. Faking receiving data\n"); nb_bytes = strlen(arguments->test_phrase); strcpy(rtx_bytes, arguments->test_phrase); break; } } } while (nb_bytes == 0); rtx_count++; rtx_toggle = 1; // next is Tx } } while(rtx_count < 2); } }
/// main loop for time division multiplexing transparent serial /// void tdm_serial_loop(void) { __pdata uint16_t last_t = timer2_tick(); __pdata uint16_t last_link_update = last_t; _canary = 42; for (;;) { __pdata uint8_t len; __pdata uint16_t tnow, tdelta; __pdata uint8_t max_xmit; if (_canary != 42) { panic("stack blown\n"); } if (pdata_canary != 0x41) { panic("pdata canary changed\n"); } // give the AT command processor a chance to handle a command at_command(); // display test data if needed if (test_display) { display_test_output(); test_display = 0; } if (seen_mavlink && feature_mavlink_framing && !at_mode_active) { seen_mavlink = false; MAVLink_report(); } // set right receive channel radio_set_channel(fhop_receive_channel()); // get the time before we check for a packet coming in tnow = timer2_tick(); // see if we have received a packet if (radio_receive_packet(&len, pbuf)) { // update the activity indication received_packet = true; fhop_set_locked(true); // update filtered RSSI value and packet stats statistics.average_rssi = (radio_last_rssi() + 7*(uint16_t)statistics.average_rssi)/8; statistics.receive_count++; // we're not waiting for a preamble // any more transmit_wait = 0; if (len < 2) { // not a valid packet. We always send // two control bytes at the end of every packet continue; } // extract control bytes from end of packet memcpy(&trailer, &pbuf[len-sizeof(trailer)], sizeof(trailer)); len -= sizeof(trailer); if (trailer.window == 0 && len != 0) { // its a control packet if (len == sizeof(struct statistics)) { memcpy(&remote_statistics, pbuf, len); } // don't count control packets in the stats statistics.receive_count--; } else if (trailer.window != 0) { // sync our transmit windows based on // received header sync_tx_windows(len); last_t = tnow; if (trailer.command == 1) { handle_at_command(len); } else if (len != 0 && !packet_is_duplicate(len, pbuf, trailer.resend) && !at_mode_active) { // its user data - send it out // the serial port //printf("rcv(%d,[", len); LED_ACTIVITY = LED_ON; serial_write_buf(pbuf, len); LED_ACTIVITY = LED_OFF; //printf("]\n"); } } continue; } // see how many 16usec ticks have passed and update // the tdm state machine. We re-fetch tnow as a bad // packet could have cost us a lot of time. tnow = timer2_tick(); tdelta = tnow - last_t; tdm_state_update(tdelta); last_t = tnow; // update link status every 0.5s if (tnow - last_link_update > 32768) { link_update(); last_link_update = tnow; } if (lbt_rssi != 0) { // implement listen before talk if (radio_current_rssi() < lbt_rssi) { lbt_listen_time += tdelta; } else { lbt_listen_time = 0; if (lbt_rand == 0) { lbt_rand = ((uint16_t)rand()) % lbt_min_time; } } if (lbt_listen_time < lbt_min_time + lbt_rand) { // we need to listen some more continue; } } // we are allowed to transmit in our transmit window // or in the other radios transmit window if we have // bonus ticks #if USE_TICK_YIELD if (tdm_state != TDM_TRANSMIT && !(bonus_transmit && tdm_state == TDM_RECEIVE)) { // we cannot transmit now continue; } #else if (tdm_state != TDM_TRANSMIT) { continue; } #endif if (transmit_yield != 0) { // we've give up our window continue; } if (transmit_wait != 0) { // we're waiting for a preamble to turn into a packet continue; } if (!received_packet && radio_preamble_detected() || radio_receive_in_progress()) { // a preamble has been detected. Don't // transmit for a while transmit_wait = packet_latency; continue; } // sample the background noise when it is out turn to // transmit, but we are not transmitting, // averaged over around 4 samples statistics.average_noise = (radio_current_rssi() + 3*(uint16_t)statistics.average_noise)/4; if (duty_cycle_wait) { // we're waiting for our duty cycle to drop continue; } // how many bytes could we transmit in the time we // have left? if (tdm_state_remaining < packet_latency) { // none .... continue; } max_xmit = (tdm_state_remaining - packet_latency) / ticks_per_byte; if (max_xmit < PACKET_OVERHEAD) { // can't fit the trailer in with a byte to spare continue; } max_xmit -= PACKET_OVERHEAD; if (max_xmit > max_data_packet_length) { max_xmit = max_data_packet_length; } // ask the packet system for the next packet to send if (send_at_command && max_xmit >= strlen(remote_at_cmd)) { // send a remote AT command len = strlen(remote_at_cmd); memcpy(pbuf, remote_at_cmd, len); trailer.command = 1; send_at_command = false; } else { // get a packet from the serial port memset(pbuf, 0x40, 16); len = packet_get_next(max_xmit-16, pbuf+16) + 16; trailer.command = packet_is_injected(); if (len == 16) len = 0; } if (len > max_data_packet_length) { panic("oversized tdm packet"); } trailer.bonus = (tdm_state == TDM_RECEIVE); trailer.resend = packet_is_resend(); if (tdm_state == TDM_TRANSMIT && len == 0 && send_statistics && max_xmit >= sizeof(statistics)) { // send a statistics packet send_statistics = 0; memcpy(pbuf, &statistics, sizeof(statistics)); len = sizeof(statistics); // mark a stats packet with a zero window trailer.window = 0; trailer.resend = 0; } else { // calculate the control word as the number of // 16usec ticks that will be left in this // tdm state after this packet is transmitted trailer.window = (uint16_t)(tdm_state_remaining - flight_time_estimate(len+sizeof(trailer))); } // set right transmit channel radio_set_channel(fhop_transmit_channel()); memcpy(&pbuf[len], &trailer, sizeof(trailer)); if (len != 0 && trailer.window != 0) { // show the user that we're sending real data LED_ACTIVITY = LED_ON; } if (len == 0) { // sending a zero byte packet gives up // our window, but doesn't change the // start of the next window transmit_yield = 1; } // after sending a packet leave a bit of time before // sending the next one. The receivers don't cope well // with back to back packets transmit_wait = packet_latency; // if we're implementing a duty cycle, add the // transmit time to the number of ticks we've been transmitting if ((duty_cycle - duty_cycle_offset) != 100) { transmitted_ticks += flight_time_estimate(len+sizeof(trailer)); } // start transmitting the packet if (!radio_transmit(len + sizeof(trailer), pbuf, tdm_state_remaining + (silence_period/2)) && len != 0 && trailer.window != 0 && trailer.command == 0) { packet_force_resend(); } if (lbt_rssi != 0) { // reset the LBT listen time lbt_listen_time = 0; lbt_rand = 0; } // set right receive channel radio_set_channel(fhop_receive_channel()); // re-enable the receiver radio_receiver_on(); if (len != 0 && trailer.window != 0) { LED_ACTIVITY = LED_OFF; } } }
// ------------------------------------------------------------------------------------------------ // Run the KISS virtual TNC void kiss_run(serial_t *serial_parms, spi_parms_t *spi_parms, arguments_t *arguments) // ------------------------------------------------------------------------------------------------ { static const size_t bufsize = RADIO_BUFSIZE; uint32_t timeout_value; uint8_t rx_buffer[bufsize], tx_buffer[bufsize]; uint8_t rtx_toggle; // 1:Tx, 0:Rx uint8_t rx_trigger; uint8_t tx_trigger; uint8_t force_mode; int rx_count, tx_count, byte_count, ret; uint64_t timestamp; struct timeval tp; set_serial_parameters(serial_parms, arguments); init_radio_int(spi_parms, arguments); memset(rx_buffer, 0, bufsize); memset(tx_buffer, 0, bufsize); radio_flush_fifos(spi_parms); verbprintf(1, "Starting...\n"); force_mode = 1; rtx_toggle = 0; rx_trigger = 0; tx_trigger = 0; rx_count = 0; tx_count = 0; radio_init_rx(spi_parms, arguments); // init for new packet to receive Rx radio_turn_rx(spi_parms); // Turn Rx on while(1) { byte_count = radio_receive_packet(spi_parms, arguments, &rx_buffer[rx_count]); // check if anything was received on radio link if (byte_count > 0) { rx_count += byte_count; // Accumulate Rx gettimeofday(&tp, NULL); timestamp = tp.tv_sec * 1000000ULL + tp.tv_usec; timeout_value = arguments->tnc_radio_window; force_mode = (timeout_value == 0); if (rtx_toggle) // Tx to Rx transition { tx_trigger = 1; // Push Tx } else { tx_trigger = 0; } radio_init_rx(spi_parms, arguments); // Init for new packet to receive rtx_toggle = 0; } byte_count = read_serial(serial_parms, &tx_buffer[tx_count], bufsize - tx_count); if (byte_count > 0) { tx_count += byte_count; // Accumulate Tx gettimeofday(&tp, NULL); timestamp = tp.tv_sec * 1000000ULL + tp.tv_usec; timeout_value = arguments->tnc_serial_window; force_mode = (timeout_value == 0); if (!rtx_toggle) // Rx to Tx transition { rx_trigger = 1; } else { rx_trigger = 0; } rtx_toggle = 1; } if ((rx_count > 0) && ((rx_trigger) || (force_mode))) // Send bytes received on air to serial { radio_wait_free(); // Make sure no radio operation is in progress radio_turn_idle(spi_parms); // Inhibit radio operations verbprintf(2, "Received %d bytes\n", rx_count); ret = write_serial(serial_parms, rx_buffer, rx_count); verbprintf(2, "Sent %d bytes on serial\n", ret); radio_init_rx(spi_parms, arguments); // Init for new packet to receive Rx radio_turn_rx(spi_parms); // Put back into Rx rx_count = 0; rx_trigger = 0; } if ((tx_count > 0) && ((tx_trigger) || (force_mode))) // Send bytes received on serial to air { if (!kiss_command(tx_buffer)) { radio_wait_free(); // Make sure no radio operation is in progress radio_turn_idle(spi_parms); // Inhibit radio operations (should be superfluous since both Tx and Rx turn to IDLE after a packet has been processed) radio_flush_fifos(spi_parms); // Flush result of any Rx activity verbprintf(2, "%d bytes to send\n", tx_count); if (tnc_tx_keyup_delay) { usleep(tnc_tx_keyup_delay); } radio_send_packet(spi_parms, arguments, tx_buffer, tx_count); radio_init_rx(spi_parms, arguments); // init for new packet to receive Rx radio_turn_rx(spi_parms); // put back into Rx } tx_count = 0; tx_trigger = 0; } if (!force_mode) { gettimeofday(&tp, NULL); if ((tp.tv_sec * 1000000ULL + tp.tv_usec) > timestamp + timeout_value) { force_mode = 1; } } radio_wait_a_bit(4); } }