Add en bloc send capability to radio TX.

pull/4/head
CInsights 2018-04-05 22:45:21 +10:00
rodzic 125bbaf7df
commit 8acae72d21
6 zmienionych plików z 470 dodań i 22 usunięć

Wyświetl plik

@ -264,8 +264,7 @@ CPPWARN = -Wall -Wextra -Wundef
# List all user C define here, like -D_DEBUG=1
UDEFS = -D_GNU_SOURCE -DARM_MATH_CM4 -DSHELL_CMD_TEST_ENABLED=1 \
-DSHELL_CMD_EXIT_ENABLED=1 -DUSB_TRACE_LEVEL=4 \
-DSHELL_USE_COMPLETION=1 -DSHELL_USE_HISTORY=1
-DSHELL_CMD_EXIT_ENABLED=1 -DUSB_TRACE_LEVEL=4
# Define ASM defines here
UADEFS =

Wyświetl plik

@ -987,6 +987,222 @@ THD_FUNCTION(min_si_fifo_feeder_afsk, arg) {
chThdExit(exit_msg);
}
/*
* Simple AFSK send thread using minimized buffering.
* Uses an iterator to size NRZI output and allocate suitably sized buffer.
* Plan is to replace with a version using even less memory.
*
*/
THD_FUNCTION(bloc_si_fifo_feeder_afsk, arg) {
radio_task_object_t *rto = arg;
packet_t pp = rto->packet_out;
radio_unit_t radio = rto->handler->radio;
pktAcquireRadio(radio, TIME_INFINITE);
/* Initialize radio. */
Si446x_conditional_init(radio);
Si446x_setBandParameters(radio, rto->base_frequency,
rto->step_hz);
/* Set 446x back to READY. */
Si446x_pauseReceive(radio);
Si446x_setModemAFSK_TX(radio);
/* Initialize variables for AFSK encoder. */
virtual_timer_t send_timer;
chVTObjectInit(&send_timer);
msg_t exit_msg = MSG_OK;
tx_iterator_t iterator;
packet_t np = NULL;
do {
// ======================== loop here for linked packets ============
/*
* Set NRZI encoding format.
* Iterator object.
* Packet reference.
* Preamble length (HDLC flags)
* Postamble length (HDLC flags)
* Tail length (HDLC zeros)
* Scramble off/on
*/
pktStreamIteratorInit(&iterator, pp, 30, 10, 10, false);
uint16_t all = pktStreamEncodingIterator(&iterator, NULL, 0);
if(all == 0) {
/* Nothing encoded. Release packet send object. */
TRACE_ERROR("SI > AFSK TX no NRZI data encoded");
// Free packet object memory
pktReleaseSendQueue(pp);
/* Schedule thread and task object memory release. */
pktSignalSendComplete(rto, chThdGetSelfX());
/* Exit thread. */
chThdExit(MSG_ERROR);
/* We never arrive here. */
}
/* Allocate buffer and perform NRZI encoding. */
uint8_t layer0[all];
//memset(layer0, 0, sizeof(layer0));
pktStreamEncodingIterator(&iterator, layer0, all);
all *= SAMPLES_PER_BAUD;
/* Reset TX FIFO in case some remnant unsent data is left there. */
const uint8_t reset_fifo[] = {0x15, 0x01};
Si446x_write(reset_fifo, 2);
up_iterator_t upsampler = {0};
upsampler.phase_delta = PHASE_DELTA_1200;
/* Maximum amount of FIFO data when using combined TX+RX (safe size). */
uint8_t localBuffer[Si446x_FIFO_COMBINED_SIZE];
/* Get the FIFO buffer amount currently available. */
uint8_t free = Si446x_getTXfreeFIFO();
/* Calculate initial FIFO fill. */
uint16_t c = (all > free) ? free : all;
/*
* Start transmission timeout timer.
* If the 446x gets locked up we'll exit TX and release packet object.
*/
chVTSet(&send_timer, TIME_S2I(10),
(vtfunc_t)Si446x_transmitTimeoutI, chThdGetSelfX());
/* The exit message if all goes well. */
exit_msg = MSG_OK;
/* Initial FIFO load. */
for(uint16_t i = 0; i < c; i++)
localBuffer[i] = Si446x_getUpsampledNRZIbits(&upsampler, layer0);
Si446x_writeFIFO(localBuffer, c);
uint8_t lower = 0;
/* Request start of transmission. */
if(Si446x_transmit(radio,
rto->base_frequency,
rto->step_hz,
rto->channel,
rto->tx_power,
all,
rto->squelch,
TIME_S2I(10))) {
/* Feed the FIFO while data remains to be sent. */
while((all - c) > 0) {
/* Get TX FIFO free count. */
uint8_t more = Si446x_getTXfreeFIFO();
/* Update the FIFO free low water mark. */
lower = (more > lower) ? more : lower;
/* If there is more free than we need for send use remainder only. */
more = (more > (all - c)) ? (all - c) : more;
/* Load the FIFO. */
for(uint16_t i = 0; i < more; i++)
//localBuffer[i] = Si446x_getUpsampledAFSKbits(layer0);
localBuffer[i] = Si446x_getUpsampledNRZIbits(&upsampler, layer0);
Si446x_writeFIFO(localBuffer, more); // Write into FIFO
c += more;
/*
* Wait for a timeout event during up-sampled NRZI send.
* Time delay allows ~SAMPLES_PER_BAUD bytes to be consumed from FIFO.
* If no timeout event go back and load more data to FIFO.
*/
eventmask_t evt = chEvtWaitAnyTimeout(SI446X_EVT_TX_TIMEOUT,
chTimeUS2I(833 * 8));
if(evt) {
/* Force 446x out of TX state. */
Si446x_setReadyState(radio);
exit_msg = MSG_TIMEOUT;
break;
}
}
} else {
/* Transmit start failed. */
TRACE_ERROR("SI > Transmit start failed");
exit_msg = MSG_ERROR;
}
chVTReset(&send_timer);
/*
* If nothing went wrong wait for TX to finish.
* Else don't wait.
*/
while(Si446x_getState(radio) == Si446x_STATE_TX && exit_msg == MSG_OK) {
/* TODO: Add an absolute timeout on this. */
/* Sleep for an AFSK byte time. */
chThdSleep(chTimeUS2I(833 * 8));
continue;
}
if(lower > (free / 2)) {
/* Warn when level drops below 50% of FIFO size. */
TRACE_WARN("SI > AFSK TX FIFO dropped below safe threshold %i", lower);
}
/* Next packet in queue. */
np = pp->nextp;
if(exit_msg == MSG_OK) {
/* Send was OK. Release the just completed packet. */
pktReleaseSendObject(pp);
} else {
/* Send failed so release queue and terminate. */
pktReleaseSendQueue(pp);
np = NULL;
}
// Free packet object memory
pktReleaseSendObject(pp);
} while(np != NULL);
// ======================== loop here for linked packets ============
/* Schedule thread memory release. */
pktSignalSendComplete(rto, chThdGetSelfX());
//pktScheduleThreadRelease(radio, chThdGetSelfX());
/* Exit thread. */
chThdExit(exit_msg);
}
/*
*
*/
bool Si446x_blocSendAFSK(radio_task_object_t *rt) {
thread_t *afsk_feeder_thd = NULL;
/* Create a send thread name which includes the sequence number. */
chsnprintf(rt->packet_out->tx_thd_name, sizeof(rt->packet_out->tx_thd_name),
"446x_afsk_tx_%03i", rt->packet_out->tx_seq);
afsk_feeder_thd = chThdCreateFromHeap(NULL,
THD_WORKING_AREA_SIZE(SI_AFSK_FIFO_MIN_FEEDER_WA_SIZE),
rt->packet_out->tx_thd_name,
NORMALPRIO - 10,
bloc_si_fifo_feeder_afsk,
rt);
if(afsk_feeder_thd == NULL) {
TRACE_ERROR("SI > Unable to create AFSK transmit thread");
return false;
}
return true;
}
/*
*
*/
@ -1180,10 +1396,187 @@ THD_FUNCTION(min_si_fifo_feeder_fsk, arg) {
chThdExit(exit_msg);
}
void Si446x_taskSend2FSK(radio_task_object_t *rt) {
/*
* New 2FSK send thread using minimised buffer space and linked queue send.
*/
THD_FUNCTION(bloc_si_fifo_feeder_fsk, arg) {
radio_task_object_t *rto = arg;
packet_t pp = rto->packet_out;
radio_unit_t radio = rto->handler->radio;
/* TODO: Check result for MSG_RESET. */
pktAcquireRadio(radio, TIME_INFINITE);
// Initialize radio
Si446x_conditional_init(radio);
/* Set 446x back to READY. */
Si446x_pauseReceive(radio);
Si446x_setBandParameters(radio, rto->base_frequency, rto->step_hz);
/* Set parameters for 2FSK transmission.
* TODO: Should we pass in 9600 or just set it here?
* In any case we should have a define I guess. */
Si446x_setModem2FSK_TX(9600);
/* Initialize variables for 2FSK encoder. */
virtual_timer_t send_timer;
chVTObjectInit(&send_timer);
tx_iterator_t iterator;
packet_t np = NULL;
/* The exit message. */
msg_t exit_msg;
do {
pktStreamIteratorInit(&iterator, pp, 30, 10, 10, true);
/* Compute size of NRZI stream. */
uint16_t all = pktStreamEncodingIterator(&iterator, NULL, 0);
if(all == 0) {
/* Nothing encoded. Release packet send object. */
TRACE_ERROR("SI > 2FSK TX no NRZI data encoded");
// Free packet object memory
pktReleaseSendQueue(pp);
/* Schedule thread and task object memory release. */
pktSignalSendComplete(rto, chThdGetSelfX());
/* Exit thread. */
chThdExit(MSG_ERROR);
/* We never arrive here. */
}
/* Allocate buffer and perform NRZI encoding. */
uint8_t layer0[all];
pktStreamEncodingIterator(&iterator, layer0, all);
/* Reset TX FIFO in case some remnant unsent data is left there. */
const uint8_t reset_fifo[] = {0x15, 0x01};
Si446x_write(reset_fifo, 2);
/* Get the FIFO buffer amount currently available. */
uint8_t free = Si446x_getTXfreeFIFO();
/* Calculate initial FIFO fill. */
uint16_t c = (all > free) ? free : all;
/*
* Start/re-start transmission timeout timer for this packet.
* If the 446x gets locked up we'll exit TX and release packet object.
*/
chVTSet(&send_timer, TIME_S2I(10),
(vtfunc_t)Si446x_transmitTimeoutI, chThdGetSelfX());
/* The exit message if all goes well. */
exit_msg = MSG_OK;
uint8_t *bufp = layer0;
/* Initial FIFO load. */
Si446x_writeFIFO(bufp, c);
bufp += c;
uint8_t lower = 0;
/* Request start of transmission. */
if(Si446x_transmit(radio,
rto->base_frequency,
rto->step_hz,
rto->channel,
rto->tx_power,
all,
rto->squelch,
TIME_S2I(10))) {
/* Feed the FIFO while data remains to be sent. */
while((all - c) > 0) {
/* Get TX FIFO free count. */
uint8_t more = Si446x_getTXfreeFIFO();
/* Update the FIFO free low water mark. */
lower = (more > lower) ? more : lower;
/* If there is more free than we need for send use remainder only. */
more = (more > (all - c)) ? (all - c) : more;
/* Load the FIFO. */
Si446x_writeFIFO(bufp, more); // Write into FIFO
bufp += more;
c += more;
/*
* Wait for a timeout event during up-sampled NRZI send.
* Time delay allows ~10 bytes to be consumed from FIFO.
* If no timeout event go back and load more data to FIFO.
*/
eventmask_t evt = chEvtWaitAnyTimeout(SI446X_EVT_TX_TIMEOUT,
chTimeUS2I(104 * 8 * 10));
if(evt) {
/* Force 446x out of TX state. */
Si446x_setReadyState(radio);
exit_msg = MSG_TIMEOUT;
break;
}
}
} else {
/* Transmit start failed. */
TRACE_ERROR("SI > 2FSK transmit start failed");
exit_msg = MSG_ERROR;
}
chVTReset(&send_timer);
/*
* If nothing went wrong wait for TX to finish.
* Else don't wait.
*/
while(Si446x_getState(radio) == Si446x_STATE_TX && exit_msg == MSG_OK) {
/* TODO: Add an absolute timeout on this. */
/* Sleep for a 2FSK byte time. */
chThdSleep(chTimeUS2I(104 * 8 * 10));
continue;
}
if(lower > (free / 2)) {
/* Warn when level drops below 50% of FIFO size. */
TRACE_WARN("SI > AFSK TX FIFO dropped below safe threshold %i", lower);
}
/* Get the next packet in the send queue. */
np = pp->nextp;
if(exit_msg == MSG_OK) {
/* Send was OK. Release the just completed packet. */
pktReleaseSendObject(pp);
} else {
/* Send failed so release any queue and terminate. */
pktReleaseSendQueue(pp);
np = NULL;
}
} while(np != NULL);
/* Finished send so schedule thread memory and task object release. */
pktSignalSendComplete(rto, chThdGetSelfX());
/* Exit thread. */
chThdExit(exit_msg);
}
/*
* Return true on send successfully enqueued.
* Task object will be returned
* Return false on failure
*/
bool Si446x_blocSend2FSK(radio_task_object_t *rt) {
thread_t *fsk_feeder_thd = NULL;
/* TODO: Don't need to put the thread name in the packet. Just use local var. */
/* Create a send thread name which includes the sequence number. */
chsnprintf(rt->packet_out->tx_thd_name, sizeof(rt->packet_out->tx_thd_name),
"446x_2fsk_tx_%03i", rt->packet_out->tx_seq);
@ -1192,16 +1585,14 @@ void Si446x_taskSend2FSK(radio_task_object_t *rt) {
THD_WORKING_AREA_SIZE(SI_FSK_FIFO_FEEDER_WA_SIZE),
rt->packet_out->tx_thd_name,
NORMALPRIO - 10,
min_si_fifo_feeder_fsk,
bloc_si_fifo_feeder_fsk,
rt);
if(fsk_feeder_thd == NULL) {
/* Release packet object (to be done by TX thread). */
//pktReleaseSendObject(pp);
TRACE_ERROR("SI > Unable to create FSK transmit thread");
return false;
}
return;
return true;
}
/*
*

Wyświetl plik

@ -215,13 +215,16 @@ typedef struct {
uint8_t current_byte;
} up_iterator_t;
typedef struct radioTask radio_task_object_t;
// Public methods
int16_t Si446x_getLastTemperature(radio_unit_t radio);
void Si446x_shutdown(radio_unit_t radio);
void Si446x_sendAFSK(packet_t pp);
bool Si446x_blocSendAFSK(radio_task_object_t *rto);
void Si446x_send2FSK(packet_t pp);
bool Si446x_blocSend2FSK(radio_task_object_t *rto);
void Si446x_disableReceive(radio_unit_t radio);
void Si446x_stopDecoder(void);
bool Si4464_resumeReceive(radio_unit_t radio,

Wyświetl plik

@ -176,24 +176,28 @@ THD_FUNCTION(pktRadioManager, arg) {
/* TODO: Move all setting of pp params to radio.c */
packet_t pp = task_object->packet_out;
pp->base_frequency = task_object->base_frequency;
/* pp->base_frequency = task_object->base_frequency;
pp->radio_step = task_object->step_hz;
pp->radio_chan = task_object->channel;
pp->radio_pwr = task_object->tx_power;
pp->cca_rssi = task_object->squelch;
pp->cca_rssi = task_object->squelch;*/
/* Give each send a sequence number. */
/* TODO: Put in task object instead? */
pp->tx_seq = ++handler->radio_tx_config.seq_num;
if(pktLLDsendPacket(pp, task_object->type)) {
if(pktLLDsendPacket(task_object)) {
/* TODO: Deprecate this gear shift stuff. */
handler->tx_count++;
poll_hysteresis = PKT_RADIO_TASK_MANAGER_TX_HYSTERESIS;
poll_rate = PKT_RADIO_TASK_MANAGER_TX_RATE_MS;
/* Success. */
break;
/* Send Successfully enqueued.
* Unlike receive the task object is held by the TX until complete.
* It is then released in the TX thread release task. */
continue;
}
/* Send failed so release send packet object. */
pktReleaseSendObject(pp);
/* Send failed so release send packet object(s). */
pktReleaseSendQueue(pp);
break;
} /* End case PKT_RADIO_TX. */
@ -429,6 +433,26 @@ void pktSubmitRadioTask(radio_unit_t radio,
chFifoSendObject(task_queue, object);
}
/**
* @brief Called by transmit threads to schedule release after completing.
* @post A thread release task is posted to the radio manager queue.
*
* @param[in] radio radio unit ID.
* @param[in] thread thread reference.
*
* @api
*/
void pktSignalSendComplete(radio_task_object_t *rto,
thread_t *thread) {
packet_svc_t *handler = rto->handler;
radio_unit_t radio = handler->radio;
/* The handler and radio ID are set in returned object. */
rto->command = PKT_RADIO_TX_THREAD;
rto->thread = thread;
pktSubmitRadioTask(radio, rto, rto->callback);
}
/**
* @brief Called by transmit threads to schedule release after completing.
@ -505,7 +529,7 @@ radio_freq_t pktComputeOperatingFrequency(radio_freq_t base_freq,
}
/**
* @brief Enable receive on a radio.
* @brief Send on radio.
* @notes This is the API interface to the radio LLD.
* @notes Currently just map directly to 446x driver.
* @notes In future would implement a lookup and VMT to access radio methods.
@ -514,14 +538,14 @@ radio_freq_t pktComputeOperatingFrequency(radio_freq_t base_freq,
*
* @notapi
*/
bool pktLLDsendPacket(packet_t pp, mod_t type) {
switch(type) {
bool pktLLDsendPacket(radio_task_object_t *rto) {
switch(rto->type) {
case MOD_2FSK:
Si446x_send2FSK(pp);
Si446x_blocSend2FSK(rto);
break;
case MOD_AFSK:
Si446x_sendAFSK(pp);
Si446x_blocSendAFSK(rto);
break;
case MOD_NONE:

Wyświetl plik

@ -109,7 +109,9 @@ extern "C" {
channel_hz_t step,
radio_ch_t chan);
bool pktLLDresumeReceive(radio_unit_t radio);
bool pktLLDsendPacket(packet_t pp, mod_t type);
bool pktLLDsendPacket(radio_task_object_t *rto);
void pktSignalSendComplete(radio_task_object_t *rto,
thread_t *thread);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -45,6 +45,8 @@
#include "bit_array.h"
/* Extend standard OS result codes. */
#define MSG_ERROR (msg_t)-3 /**< @brief Error condition. */
/* General event definitions. */
#define EVT_NONE 0
@ -378,6 +380,7 @@ static inline msg_t pktSendRadioCommand(radio_unit_t radio,
static inline void pktReleaseSendObject(packet_t pp) {
#if USE_SPI_ATTACHED_RADIO == TRUE
#if USE_NEW_PKT_TX_ALLOC == TRUE
pktReleaseOutgoingBuffer(pp);
#else
ax25_delete (pp);
@ -387,6 +390,32 @@ static inline void pktReleaseSendObject(packet_t pp) {
#endif
}
/**
* @brief Release memory from one or more send object(s).
* @notes a linked list will have all members released.
* @post The object memory is released.
*
* @param[in] pp pointer to a @p packet send object
*
* @api
*/
static inline void pktReleaseSendQueue(packet_t pp) {
#if USE_SPI_ATTACHED_RADIO == TRUE
#if USE_NEW_PKT_TX_ALLOC == TRUE
/* Release all packets in linked list. */
do {
packet_t np = pp->nextp;
pktReleaseOutgoingBuffer(pp);
pp = np;
} while(pp != NULL);
#else
ax25_delete (pp);
#endif
#else
(void)pp;
#endif
}
#endif /* _PKTCONF_H_ */
/** @} */