pecanpico10/tracker/software/source/pkt/channels/rxpwm.h

300 wiersze
9.9 KiB
C

/*
Aerospace Decoder - Copyright (C) 2018 Bob Anderson (VK2GJ)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
/**
* @file rxpwm.h
* @brief ICU user macros and structures.
*
* @addtogroup channels
* @{
*/
#ifndef PKT_CHANNELS_RXPWM_H_
#define PKT_CHANNELS_RXPWM_H_
#include "pktconf.h"
/*===========================================================================*/
/* Module constants. */
/*===========================================================================*/
/* Limit of ICU and PWM count for packed format. */
#if USE_12_BIT_PWM == TRUE
#define PWM_MAX_COUNT 0xFFF
#define MAX_PWM_BITS 12
#else
#define PWM_MAX_COUNT 0xFFFF
#define MAX_PWM_BITS 16
#endif
/* PWM stream in-band prefix. */
#define PWM_IN_BAND_PREFIX 0
/* PWM stream terminate in-band reason codes. */
#define PWM_TERM_CCA_CLOSE 0
#define PWM_TERM_QUEUE_FULL 1
#define PWM_TERM_ICU_OVERFLOW 2
#define PWM_TERM_QUEUE_ERR 3
#define PWM_ACK_DECODE_END 4
#define PWM_TERM_DECODE_STOP 5
#define PWM_TERM_NO_DATA 6
#define PWM_TERM_QUEUE_LOCK 7
#define PWM_INFO_QUEUE_SWAP 8
#define PWM_ACK_DECODE_ERROR 9
/* ICU will be stopped if no activity for this number of seconds. */
#define ICU_INACTIVITY_TIMEOUT 60
/*===========================================================================*/
/* Module data structures and types. */
/*===========================================================================*/
typedef uint8_t pwm_code_t;
typedef enum ICUStates {
PKT_PWM_INIT = 0,
PKT_PWM_READY,
PKT_PWM_ACTIVE,
PKT_PWM_STOP
} rx_icu_state_t;
/* Types for ICU and PWM data. */
typedef uint16_t min_icucnt_t;
typedef uint16_t min_pwmcnt_t;
typedef uint8_t packed_pwm_data_t;
#if USE_12_BIT_PWM == TRUE
typedef packed_pwm_data_t packed_pwmcnt_t;
typedef packed_pwm_data_t packed_pwmxtn_t;
/* Structure containing packed PWM results. */
typedef struct {
packed_pwmcnt_t impulse;
packed_pwmcnt_t valley;
packed_pwmxtn_t xtn;
} packed_pwm_counts_t;
#else
typedef min_pwmcnt_t packed_pwmcnt_t;
/* Structure containing packed PWM results. */
typedef struct {
packed_pwmcnt_t impulse;
packed_pwmcnt_t valley;
} packed_pwm_counts_t;
#endif
/* Union of packed PWM results and byte array representation. */
typedef union {
packed_pwm_counts_t pwm;
packed_pwm_data_t bytes[sizeof(packed_pwm_counts_t)];
} byte_packed_pwm_t;
/* Structure holding PWM entries created from ICU results. */
typedef struct {
min_pwmcnt_t impulse;
min_pwmcnt_t valley;
} min_pwm_counts_t;
/* Union of PWM results and byte array representation. */
typedef union {
min_pwm_counts_t pwm;
min_pwmcnt_t array[sizeof(min_pwm_counts_t)
/ sizeof(min_pwmcnt_t)];
} array_min_pwm_counts_t;
/* Union of packed PWM data buffer and byte array representation. */
typedef union {
byte_packed_pwm_t pwm_buffer[PWM_DATA_SLOTS];
packed_pwm_data_t pwm_bytes[sizeof(byte_packed_pwm_t)
* PWM_DATA_SLOTS];
} radio_pwm_buffer_t;
#if USE_HEAP_PWM_BUFFER == TRUE
/* Forward declare struct. */
typedef struct PWMobject radio_pwm_object_t;
typedef struct PWMobject {
radio_pwm_buffer_t buffer;
/* In linked mode the reference to the next PWM queue is saved here.
* The decoder will continue to process linked PWM queues until completion.
*/
input_queue_t queue;
radio_pwm_object_t *next;
} radio_pwm_object_t;
#endif
/*
* PWM FIFO object. Path between ICU and decoder during an AFSK decode.
*/
typedef struct {
/* For safety keep clear - where FIFO pool stores its free link. */
struct pool_header link;
#if USE_HEAP_PWM_BUFFER == TRUE
/*
* There are two PWM object pointers in a PWM stream record.
* One for the radio (producer) side.
* And one for the decoder (consumer) side.
*/
radio_pwm_object_t *radio_pwm_queue;
radio_pwm_object_t *decode_pwm_queue;
uint8_t in_use;
uint8_t rlsd;
uint8_t peak;
#else
/* Allocate a PWM buffer in the queue object. */
radio_pwm_buffer_t packed_buffer;
/*
* This is the current radio queue object.
* In single queue mode PWM is written to a single queue only.
* The queue has a single large buffer and used for the entire PWM session.
*
* In linked buffer mode PWM can chain multiple smaller input buffers.
* After getting a new PWM buffer object the queue is re-initialized.
* The queue fill with further PWM then continues.
* As PWM buffers are consumed by the decoder they are recycled back to the pool.
* The radio PWM can then re-use those buffers which in theory reduces memory utilisation.
*/
input_queue_t radio_pwm_queue;
#endif
/*
* The semaphore controls the release of the PWM buffer and FIFO resources.
* In non-linked mode the buffer is enclosed within the FIFO object.
* In linked mode the last PWM buffer is protected along with the FIFO.
* The semaphore prevents any release during trailing PWM buffering.
* Trailing PWM is not used but the object(s) are still in use by the radio.
*/
binary_semaphore_t sem;
volatile eventflags_t status;
} radio_pwm_fifo_t;
/*===========================================================================*/
/* Module inline functions. */
/*===========================================================================*/
/**
* @brief Convert ICU data to PWM data and pack into minimized buffer.
* @note This function deals with ICU data packed into 12 bits or 16 bits.
*
* @param[in] icup pointer to ICU driver.
* @param[in] dest pointer to the object for PWM data.
*
* @api
*/
static inline void pktConvertICUtoPWM(ICUDriver *icup,
byte_packed_pwm_t *dest) {
icucnt_t impulse = icuGetWidthX(icup);
icucnt_t valley = icuGetPeriodX(icup) - impulse;
#if USE_12_BIT_PWM == TRUE
dest->pwm.impulse = (packed_pwmcnt_t)impulse & 0xFFU;
dest->pwm.valley = (packed_pwmcnt_t)valley & 0xFFU;
/*
* Pack extension bits 8-11 of impulse and valley into a byte.
* Impulse goes into low nibble and valley into high nibble.
*/
valley >>= 4;
impulse >>= 8;
dest->pwm.xtn = ((packed_pwmxtn_t)(impulse) & 0x000FU);
dest->pwm.xtn |= ((packed_pwmxtn_t)(valley) & 0x00F0U);
#else
dest->pwm.impulse = (packed_pwmcnt_t)impulse & 0xFFFFU;
dest->pwm.valley = (packed_pwmcnt_t)valley & 0xFFFFU;
#endif
}
/**
* @brief Unpack PWM data into PWM structure.
* @note This function deals with ICU data up to 12 bits.
*
* @param[in] src Buffer containing packed impulse and valley data.
* @param[in] dest pointer to the destination object for PWM data.
*
* @api
*/
static inline void pktUnpackPWMData(byte_packed_pwm_t src,
array_min_pwm_counts_t *dest) {
#if USE_12_BIT_PWM == TRUE
min_icucnt_t duration = src.pwm.impulse;
duration |= ((min_icucnt_t)(src.pwm.xtn & 0x0FU) << 8);
dest->pwm.impulse = duration;
duration = src.pwm.valley;
duration |= ((min_icucnt_t)(src.pwm.xtn & 0xF0U) << 4);
dest->pwm.valley = duration;
#else
dest->pwm.impulse = src.pwm.impulse;
dest->pwm.valley = src.pwm.valley;
#endif
}
/**
* @brief Write PWM data into input queue.
* @note This function deals with PWM data packed in sequential bytes.
*
* @param[in] queue pointer to an input queue object.
* @param[in] pack PWM packed data object.
*
* @return The operation status.
* @retval MSG_OK The PWM entry has been queued.
* @retval MSG_RESET One slot remains which is reserved for an in-band signal.
* @retval MSG_TIMEOUT The queue is full for normal PWM data writes.
*
*
* @api
*/
static inline msg_t pktWritePWMQueueI(input_queue_t *queue,
byte_packed_pwm_t pack) {
/* Check if there is only one slot left. */
if(iqGetEmptyI(queue) == sizeof(byte_packed_pwm_t)) {
array_min_pwm_counts_t data;
pktUnpackPWMData(pack, &data);
if(data.pwm.impulse != PWM_IN_BAND_PREFIX)
return MSG_RESET;
}
/* Data is normal PWM or an in-band. */
for(uint8_t b = 0; b < sizeof(pack.bytes); b++) {
if(MSG_TIMEOUT == iqPutI(queue, pack.bytes[b]))
return MSG_TIMEOUT;
}
return MSG_OK;
}
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#ifdef __cplusplus
extern "C" {
#endif
ICUDriver *pktAttachICU(radio_unit_t radio_id);
void pktDetachICU(ICUDriver *myICU);
void pktICUStart(ICUDriver *myICU);
void pktRadioICUWidth(ICUDriver *myICU);
void pktRadioICUPeriod(ICUDriver *myICU);
void PktRadioICUOverflow(ICUDriver *myICU);
void pktRadioCCAInput(ICUDriver *myICU);
void pktStopAllICUTimersI(ICUDriver *myICU);
void pktSleepICUI(ICUDriver *myICU);
msg_t pktQueuePWMDataI(ICUDriver *myICU);
void pktClosePWMChannelI(ICUDriver *myICU, eventflags_t evt,
pwm_code_t reason);
void pktICUInactivityTimeout(ICUDriver *myICU);
void pktPWMInactivityTimeout(ICUDriver *myICU);
#ifdef __cplusplus
}
#endif
#endif /* PKT_CHANNELS_RXPWM_H_ */
/** @} */