fcap cleaning, added new features and reorganized the unit

remotes/github/direct
Ondřej Hruška 6 years ago
parent 805e47594a
commit b678a8f2b7
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 38
      platform/ll_extension.c
  2. 3
      platform/ll_extension.h
  3. 194
      units/fcap/_fcap_core.c
  4. 34
      units/fcap/_fcap_init.c
  5. 41
      units/fcap/_fcap_internal.h
  6. 74
      units/fcap/_fcap_settings.c
  7. 272
      units/fcap/unit_fcap.c
  8. 24
      utils/str_utils.c

@ -5,6 +5,44 @@
#include "platform.h"
#include "ll_extension.h"
const uint32_t LL_TIM_IC_FILTERS[] = {
LL_TIM_IC_FILTER_FDIV1,
LL_TIM_IC_FILTER_FDIV1_N2,
LL_TIM_IC_FILTER_FDIV1_N4,
LL_TIM_IC_FILTER_FDIV1_N8,
LL_TIM_IC_FILTER_FDIV2_N6,
LL_TIM_IC_FILTER_FDIV2_N8,
LL_TIM_IC_FILTER_FDIV4_N6,
LL_TIM_IC_FILTER_FDIV4_N8,
LL_TIM_IC_FILTER_FDIV8_N6,
LL_TIM_IC_FILTER_FDIV8_N8,
LL_TIM_IC_FILTER_FDIV16_N5,
LL_TIM_IC_FILTER_FDIV16_N6,
LL_TIM_IC_FILTER_FDIV16_N8,
LL_TIM_IC_FILTER_FDIV32_N5,
LL_TIM_IC_FILTER_FDIV32_N6,
LL_TIM_IC_FILTER_FDIV32_N8,
};
const uint32_t LL_TIM_ETR_FILTERS[] = {
LL_TIM_ETR_FILTER_FDIV1,
LL_TIM_ETR_FILTER_FDIV1_N2,
LL_TIM_ETR_FILTER_FDIV1_N4,
LL_TIM_ETR_FILTER_FDIV1_N8,
LL_TIM_ETR_FILTER_FDIV2_N6,
LL_TIM_ETR_FILTER_FDIV2_N8,
LL_TIM_ETR_FILTER_FDIV4_N6,
LL_TIM_ETR_FILTER_FDIV4_N8,
LL_TIM_ETR_FILTER_FDIV8_N6,
LL_TIM_ETR_FILTER_FDIV8_N8,
LL_TIM_ETR_FILTER_FDIV16_N5,
LL_TIM_ETR_FILTER_FDIV16_N6,
LL_TIM_ETR_FILTER_FDIV16_N8,
LL_TIM_ETR_FILTER_FDIV32_N5,
LL_TIM_ETR_FILTER_FDIV32_N6,
LL_TIM_ETR_FILTER_FDIV32_N8,
};
const uint32_t LL_SYSCFG_EXTI_PORTS[PORTS_COUNT] = {
LL_SYSCFG_EXTI_PORTA,
LL_SYSCFG_EXTI_PORTB,

@ -13,7 +13,8 @@ extern GPIO_TypeDef * const GPIO_PERIPHS[PORTS_COUNT];
extern const uint32_t LL_GPIO_PINS[16];
extern const uint32_t LL_EXTI_LINES[16];
extern const uint32_t LL_ADC_SAMPLETIMES[8];
extern const uint32_t LL_TIM_IC_FILTERS[16];
extern const uint32_t LL_TIM_ETR_FILTERS[16];
static inline bool LL_DMA_IsActiveFlag_G(uint32_t isr_snapshot, uint8_t channel)
{

@ -9,8 +9,8 @@
#include "_fcap_internal.h"
static void UFCAP_StopMeasurement(Unit *unit);
static void UFCAP_ConfigureForPWMCapture(Unit *unit);
static void UFCAP_ConfigureForDirectCapture(Unit *unit);
static void UFCAP_ConfigureForIndirectCapture(Unit *unit);
static void UFCAP_ConfigureForDirectCapture(Unit *unit, uint16_t msec);
static void UFCAP_ConfigureForFreeCapture(Unit *unit);
uint32_t UFCAP_GetFreeCounterValue(Unit *unit)
@ -20,7 +20,22 @@ uint32_t UFCAP_GetFreeCounterValue(Unit *unit)
return TIMx->CNT;
}
static void UFCAP_PWMBurstReportJob(Job *job)
uint32_t UFCAP_FreeCounterClear(Unit *unit)
{
struct priv * const priv = unit->data;
TIM_TypeDef * const TIMx = priv->TIMx;
// this isn't perfect, we can miss one clock
// but it's probably the best we can do here ...
vPortEnterCritical();
uint32_t val = TIMx->CNT;
TIMx->CNT = 0;
vPortExitCritical();
return val;
}
static void UFCAP_IndirectBurstReportJob(Job *job)
{
Unit *unit = job->unit;
struct priv * const priv = unit->data;
@ -29,9 +44,9 @@ static void UFCAP_PWMBurstReportJob(Job *job)
PayloadBuilder pb = pb_start(buf, 20, NULL);
pb_u16(&pb, PLAT_AHB_MHZ);
pb_u16(&pb, priv->pwm_burst.n_count);
pb_u64(&pb, priv->pwm_burst.period_acu);
pb_u64(&pb, priv->pwm_burst.ontime_acu);
pb_u16(&pb, priv->ind_burst.n_count);
pb_u64(&pb, priv->ind_burst.period_acu);
pb_u64(&pb, priv->ind_burst.ontime_acu);
assert_param(pb.ok);
@ -41,15 +56,38 @@ static void UFCAP_PWMBurstReportJob(Job *job)
priv->opmode = OPMODE_IDLE;
}
static void UFCAP_CountBurstReportJob(Job *job)
static void UFCAP_SinglePulseReportJob(Job *job)
{
Unit *unit = job->unit;
struct priv * const priv = unit->data;
uint8_t buf[6];
PayloadBuilder pb = pb_start(buf, 20, NULL);
PayloadBuilder pb = pb_start(buf, 6, NULL);
pb_u16(&pb, PLAT_AHB_MHZ);
pb_u32(&pb, job->data1);
assert_param(pb.ok);
com_respond_pb(priv->request_id, MSG_SUCCESS, &pb);
// timer is already stopped, now in OPMODE_BUSY
priv->opmode = OPMODE_IDLE;
}
/**
* Count is passed in data1
* @param job
*/
static void UFCAP_DirectBurstReportJob(Job *job)
{
Unit *unit = job->unit;
struct priv * const priv = unit->data;
uint8_t buf[8];
PayloadBuilder pb = pb_start(buf, 8, NULL);
pb_u16(&pb, priv->cnt_burst.msec);
pb_u8(&pb, priv->direct_presc);
pb_u16(&pb, priv->dir_burst.msec);
pb_u32(&pb, job->data1);
assert_param(pb.ok);
@ -69,40 +107,56 @@ void UFCAP_TIMxHandler(void *arg)
TIM_TypeDef * const TIMx = priv->TIMx;
if (priv->opmode == OPMODE_PWM_CONT) {
if (priv->opmode == OPMODE_INDIRECT_CONT) {
if (LL_TIM_IsActiveFlag_CC1(TIMx)) {
if (priv->n_skip > 0) {
priv->n_skip--;
} else {
priv->pwm_cont.last_period = LL_TIM_IC_GetCaptureCH1(TIMx);
priv->pwm_cont.last_ontime = priv->pwm_cont.ontime;
priv->ind_cont.last_period = LL_TIM_IC_GetCaptureCH1(TIMx);
priv->ind_cont.last_ontime = priv->ind_cont.ontime;
}
LL_TIM_ClearFlag_CC1(TIMx);
LL_TIM_ClearFlag_CC1OVR(TIMx);
}
if (LL_TIM_IsActiveFlag_CC2(TIMx)) {
priv->pwm_cont.ontime = LL_TIM_IC_GetCaptureCH2(TIMx);
priv->ind_cont.ontime = LL_TIM_IC_GetCaptureCH2(TIMx);
LL_TIM_ClearFlag_CC2(TIMx);
LL_TIM_ClearFlag_CC2OVR(TIMx);
}
}
else if (priv->opmode == OPMODE_PWM_BURST) {
else if (priv->opmode == OPMODE_SINGLE_PULSE) {
if (LL_TIM_IsActiveFlag_CC2(TIMx)) {
// single pulse - does not wait for the second edge
uint32_t len = LL_TIM_IC_GetCaptureCH2(TIMx);
priv->opmode = OPMODE_BUSY;
UFCAP_StopMeasurement(unit);
Job j = {
.cb = UFCAP_SinglePulseReportJob,
.unit = unit,
.data1 = len,
};
scheduleJob(&j);
}
}
else if (priv->opmode == OPMODE_INDIRECT_BURST) {
if (LL_TIM_IsActiveFlag_CC1(TIMx)) {
const uint32_t period = LL_TIM_IC_GetCaptureCH1(TIMx);
const uint32_t ontime = priv->pwm_burst.ontime;
const uint32_t ontime = priv->ind_burst.ontime;
if (priv->n_skip > 0) {
priv->n_skip--;
} else {
priv->pwm_burst.ontime_acu += ontime;
priv->pwm_burst.period_acu += period;
if (++priv->pwm_burst.n_count == priv->pwm_burst.n_target) {
priv->ind_burst.ontime_acu += ontime;
priv->ind_burst.period_acu += period;
if (++priv->ind_burst.n_count == priv->ind_burst.n_target) {
priv->opmode = OPMODE_BUSY;
UFCAP_StopMeasurement(unit);
Job j = {
.cb = UFCAP_PWMBurstReportJob,
.cb = UFCAP_IndirectBurstReportJob,
.unit = unit,
};
scheduleJob(&j);
@ -114,7 +168,7 @@ void UFCAP_TIMxHandler(void *arg)
}
if (LL_TIM_IsActiveFlag_CC2(TIMx)) {
priv->pwm_burst.ontime = LL_TIM_IC_GetCaptureCH2(TIMx);
priv->ind_burst.ontime = LL_TIM_IC_GetCaptureCH2(TIMx);
LL_TIM_ClearFlag_CC2(TIMx);
LL_TIM_ClearFlag_CC2OVR(TIMx);
}
@ -139,18 +193,23 @@ void UFCAP_TIMyHandler(void *arg)
TIM_TypeDef * const TIMy = priv->TIMy;
uint32_t cnt = TIMx->CNT; // TIMx should be stopped now
dbg("> TIMy Handler, TIMx cntr is %d", cnt);
priv->cnt_cont.last_count = cnt;
// dbg("> TIMy Handler, TIMx cntr is %d", cnt);
priv->dir_cont.last_count = cnt;
if (priv->opmode == OPMODE_COUNTER_CONT) {
if (priv->opmode == OPMODE_DIRECT_CONT) {
LL_TIM_DisableCounter(TIMx);
LL_TIM_DisableCounter(TIMy);
LL_TIM_SetCounter(TIMx, 0);
LL_TIM_SetCounter(TIMy, 0);
LL_TIM_EnableCounter(TIMy); // next loop
} else if (priv->opmode == OPMODE_COUNTER_BURST) {
LL_TIM_EnableCounter(TIMx);
}
else if (priv->opmode == OPMODE_DIRECT_BURST) {
priv->opmode = OPMODE_BUSY;
UFCAP_StopMeasurement(unit);
Job j = {
.cb = UFCAP_CountBurstReportJob,
.cb = UFCAP_DirectBurstReportJob,
.unit = unit,
.data1 = cnt,
};
@ -215,35 +274,42 @@ void UFCAP_SwitchMode(Unit *unit, enum fcap_opmode opmode)
UFCAP_StopMeasurement(unit);
break;
case OPMODE_PWM_CONT:
priv->pwm_cont.last_ontime = 0;
priv->pwm_cont.last_period = 0;
priv->pwm_cont.ontime = 0;
case OPMODE_INDIRECT_CONT:
priv->ind_cont.last_ontime = 0;
priv->ind_cont.last_period = 0;
priv->ind_cont.ontime = 0;
priv->n_skip = 1; // discard the first cycle (will be incomplete)
UFCAP_ConfigureForPWMCapture(unit); // is also stopped and restarted
UFCAP_ConfigureForIndirectCapture(unit); // is also stopped and restarted
break;
case OPMODE_PWM_BURST:
priv->pwm_burst.ontime = 0;
priv->pwm_burst.n_count = 0;
priv->pwm_burst.period_acu = 0;
priv->pwm_burst.ontime_acu = 0;
case OPMODE_INDIRECT_BURST:
priv->ind_burst.ontime = 0;
priv->ind_burst.n_count = 0;
priv->ind_burst.period_acu = 0;
priv->ind_burst.ontime_acu = 0;
priv->n_skip = 1; // discard the first cycle (will be incomplete)
UFCAP_ConfigureForPWMCapture(unit); // is also stopped and restarted
UFCAP_ConfigureForIndirectCapture(unit); // is also stopped and restarted
break;
case OPMODE_SINGLE_PULSE:
priv->n_skip = 0;
UFCAP_ConfigureForIndirectCapture(unit); // is also stopped and restarted
break;
case OPMODE_COUNTER_CONT:
priv->cnt_cont.last_count = 0;
case OPMODE_DIRECT_CONT:
// msec is set by caller
priv->dir_cont.last_count = 0;
priv->n_skip = 1; // discard the first cycle (will be incomplete)
UFCAP_ConfigureForDirectCapture(unit);
UFCAP_ConfigureForDirectCapture(unit, priv->direct_msec);
break;
case OPMODE_COUNTER_BURST:
case OPMODE_DIRECT_BURST:
// msec is set by caller
priv->n_skip = 0; // no skip here (if there was any)
UFCAP_ConfigureForDirectCapture(unit);
UFCAP_ConfigureForDirectCapture(unit, (uint16_t) priv->dir_burst.msec);
break;
case OPMODE_COUNTER_FREERUNNING:
case OPMODE_FREE_COUNTER:
UFCAP_ConfigureForFreeCapture(unit);
break;
@ -256,7 +322,7 @@ void UFCAP_SwitchMode(Unit *unit, enum fcap_opmode opmode)
* Configure peripherals for an indirect capture (PWM measurement) - continuous or burst
* @param unit
*/
static void UFCAP_ConfigureForPWMCapture(Unit *unit)
static void UFCAP_ConfigureForIndirectCapture(Unit *unit)
{
struct priv * const priv = unit->data;
TIM_TypeDef * const TIMx = priv->TIMx;
@ -282,14 +348,26 @@ static void UFCAP_ConfigureForPWMCapture(Unit *unit)
// It's possible to select the other channel, which we use to connect both TIx to the shame CHx.
LL_TIM_IC_SetActiveInput(TIMx, ll_ch_a, priv->a_direct ? LL_TIM_ACTIVEINPUT_DIRECTTI : LL_TIM_ACTIVEINPUT_INDIRECTTI);
LL_TIM_IC_SetActiveInput(TIMx, ll_ch_b, priv->a_direct ? LL_TIM_ACTIVEINPUT_INDIRECTTI : LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPolarity(TIMx, ll_ch_a, priv->active_level ? LL_TIM_IC_POLARITY_RISING : LL_TIM_IC_POLARITY_FALLING);
LL_TIM_IC_SetPolarity(TIMx, ll_ch_b, priv->active_level ? LL_TIM_IC_POLARITY_FALLING : LL_TIM_IC_POLARITY_RISING);
if (priv->dfilter > 15) priv->dfilter = 15;
uint32_t filter = LL_TIM_IC_FILTERS[priv->dfilter];
LL_TIM_IC_SetFilter(TIMx, ll_ch_a, filter);
LL_TIM_IC_SetFilter(TIMx, ll_ch_b, filter);
LL_TIM_CC_EnableChannel(TIMx, ll_ch_a | ll_ch_b);
LL_TIM_IC_SetPolarity(TIMx, ll_ch_a, LL_TIM_IC_POLARITY_RISING);
LL_TIM_IC_SetPolarity(TIMx, ll_ch_b, LL_TIM_IC_POLARITY_FALLING);
LL_TIM_SetSlaveMode(TIMx, LL_TIM_SLAVEMODE_RESET);
LL_TIM_SetTriggerInput(TIMx, LL_TIM_TS_TI1FP1); // Use Filtered Input 1 (TI1)
LL_TIM_EnableMasterSlaveMode(TIMx);
LL_TIM_ClearFlag_CC1(TIMx);
LL_TIM_ClearFlag_CC1OVR(TIMx);
LL_TIM_ClearFlag_CC2(TIMx);
LL_TIM_ClearFlag_CC2OVR(TIMx);
LL_TIM_EnableIT_CC1(TIMx);
LL_TIM_EnableIT_CC2(TIMx);
LL_TIM_EnableCounter(TIMx);
@ -300,18 +378,19 @@ static void UFCAP_ConfigureForPWMCapture(Unit *unit)
* Configure peripherals for an indirect capture (PWM measurement) - continuous or burst
* @param unit
*/
static void UFCAP_ConfigureForDirectCapture(Unit *unit)
static void UFCAP_ConfigureForDirectCapture(Unit *unit, uint16_t msec)
{
struct priv * const priv = unit->data;
// dbg("Configuring Direct capture...");
UFCAP_ClearTimerConfig(unit);
{
TIM_TypeDef *const TIMy = priv->TIMy;
assert_param(PLAT_AHB_MHZ<=65);
uint16_t presc = PLAT_AHB_MHZ*1000;
uint32_t count = priv->cnt_cont.msec+1; // it's one tick longer because we generate OCREF on the exact msec count - it must be at least 1 tick long
uint32_t count = msec+1; // it's one tick longer because we generate OCREF on the exact msec count - it must be at least 1 tick long
LL_TIM_SetPrescaler(TIMy, (uint32_t) (presc - 1));
LL_TIM_SetAutoReload(TIMy, count - 1);
@ -327,6 +406,7 @@ static void UFCAP_ConfigureForDirectCapture(Unit *unit)
LL_TIM_OC_SetCompareCH1(TIMy, count-1);
LL_TIM_CC_EnableChannel(TIMy, LL_TIM_CHANNEL_CH1); // enable the output channel that produces a trigger
LL_TIM_ClearFlag_UPDATE(TIMy);
LL_TIM_EnableIT_UPDATE(TIMy);
}
@ -337,6 +417,24 @@ static void UFCAP_ConfigureForDirectCapture(Unit *unit)
LL_TIM_SetTriggerInput(TIMx, LL_TIM_TS_ITR3); // ITR3 is TIM14 which we use as TIMy
LL_TIM_EnableMasterSlaveMode(TIMx);
uint32_t presc = LL_TIM_ETR_PRESCALER_DIV1;
switch (priv->direct_presc) {
case 1: presc = LL_TIM_ETR_PRESCALER_DIV1; break;
case 2: presc = LL_TIM_ETR_PRESCALER_DIV2; break;
case 4: presc = LL_TIM_ETR_PRESCALER_DIV4; break;
case 8: presc = LL_TIM_ETR_PRESCALER_DIV8; break;
default:
priv->direct_presc = 1; // will be sent with the response
}
if (priv->dfilter > 15) priv->dfilter = 15;
uint32_t filter = LL_TIM_ETR_FILTERS[priv->dfilter];
LL_TIM_ConfigETR(TIMx,
priv->active_level ? LL_TIM_ETR_POLARITY_NONINVERTED : LL_TIM_ETR_POLARITY_INVERTED,
presc,
filter);
LL_TIM_EnableExternalClock(TIMx); // TODO must check and deny this mode if the pin is not on CH1 = external trigger input
LL_TIM_SetCounter(TIMx, 0);
@ -355,6 +453,8 @@ static void UFCAP_ConfigureForFreeCapture(Unit *unit)
{
struct priv * const priv = unit->data;
UFCAP_ClearTimerConfig(unit);
TIM_TypeDef *const TIMx = priv->TIMx;
LL_TIM_EnableExternalClock(TIMx);
LL_TIM_SetCounter(TIMx, 0);

@ -14,10 +14,14 @@ error_t UFCAP_preInit(Unit *unit)
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
if (priv == NULL) return E_OUT_OF_MEM;
priv->signal_pname = 'A';
priv->signal_pnum = 0;
priv->conf.signal_pname = 'A';
priv->conf.signal_pnum = 0;
priv->opmode = OPMODE_PWM_CONT;
priv->conf.active_level = 1;
priv->conf.direct_presc = 1;
priv->conf.dfilter = 0;
priv->conf.direct_msec = 1000;
priv->conf.startmode = OPMODE_IDLE;
return E_SUCCESS;
}
@ -39,9 +43,9 @@ error_t UFCAP_init(Unit *unit)
uint32_t ll_ch_a = 0;
uint32_t ll_ch_b = 0;
switch (priv->signal_pname) {
switch (priv->conf.signal_pname) {
case 'A':
switch (priv->signal_pnum) {
switch (priv->conf.signal_pnum) {
case 5:
case 15:
case 0: ll_ch_a = LL_TIM_CHANNEL_CH1; break;
@ -52,7 +56,7 @@ error_t UFCAP_init(Unit *unit)
}
break;
case 'B':
switch (priv->signal_pnum) {
switch (priv->conf.signal_pnum) {
case 3: ll_ch_a = LL_TIM_CHANNEL_CH2; break;
default:
dbg("Bad signal pin!");
@ -80,7 +84,7 @@ error_t UFCAP_init(Unit *unit)
// ---- CLAIM ----
TRY(rsc_claim_pin(unit, priv->signal_pname, priv->signal_pnum));
TRY(rsc_claim_pin(unit, priv->conf.signal_pname, priv->conf.signal_pnum));
TRY(rsc_claim(unit, timRsc));
TRY(rsc_claim(unit, tim2Rsc));
@ -93,10 +97,17 @@ error_t UFCAP_init(Unit *unit)
priv->ll_ch_b = ll_ch_b;
priv->a_direct = a_direct;
TRY(hw_configure_gpio_af(priv->signal_pname, priv->signal_pnum, ll_timpin_af));
// Load defaults
priv->active_level = priv->conf.active_level;
priv->direct_presc = priv->conf.direct_presc;
priv->dfilter = priv->conf.dfilter;
priv->direct_msec = priv->conf.direct_msec;
priv->opmode = priv->conf.startmode;
GPIO_TypeDef *gpio = hw_port2periph(priv->signal_pname, &suc);
uint32_t ll_pin = hw_pin2ll(priv->signal_pnum, &suc);
TRY(hw_configure_gpio_af(priv->conf.signal_pname, priv->conf.signal_pnum, ll_timpin_af));
GPIO_TypeDef *gpio = hw_port2periph(priv->conf.signal_pname, &suc);
uint32_t ll_pin = hw_pin2ll(priv->conf.signal_pnum, &suc);
LL_GPIO_SetPinPull(gpio, ll_pin, LL_GPIO_PULL_DOWN); // XXX change to pull-up if the polarity is inverted
hw_periph_clock_enable(TIMx);
@ -104,8 +115,7 @@ error_t UFCAP_init(Unit *unit)
irqd_attach(TIMx, UFCAP_TIMxHandler, unit);
irqd_attach(TIMy, UFCAP_TIMyHandler, unit);
// UFCAP_SwitchMode(unit, OPMODE_IDLE);
UFCAP_SwitchMode(unit, OPMODE_COUNTER_CONT);
UFCAP_SwitchMode(unit, priv->opmode); // switch to the default opmode
return E_SUCCESS;
}

@ -14,18 +14,28 @@
enum fcap_opmode {
OPMODE_IDLE = 0,
OPMODE_BUSY = 1, // used after capture is done, before it's reported
OPMODE_PWM_CONT = 2,
OPMODE_PWM_BURST = 3, // averaging
OPMODE_COUNTER_CONT = 4,
OPMODE_COUNTER_BURST = 5,
OPMODE_COUNTER_FREERUNNING = 6,
OPMODE_INDIRECT_CONT = 2,
OPMODE_INDIRECT_BURST = 3, // averaging
OPMODE_DIRECT_CONT = 4,
OPMODE_DIRECT_BURST = 5,
OPMODE_FREE_COUNTER = 6,
OPMODE_SINGLE_PULSE = 7,
};
/** Private data structure */
struct priv {
// settings
char signal_pname; // the input pin - one of TIM2 channels
uint8_t signal_pnum;
struct {
char signal_pname; // the input pin - one of TIM2 channels
uint8_t signal_pnum;
bool active_level;
uint8_t direct_presc;
uint8_t dfilter;
uint16_t direct_msec;
enum fcap_opmode startmode;
} conf;
// internal state
TIM_TypeDef *TIMx;
@ -39,12 +49,17 @@ struct priv {
TF_ID request_id;
uint8_t n_skip; //!< Periods to skip before starting the real capture
bool active_level; // in PWM mode, the first part that is measured. (if 1, HHHLLL, else LLLHHH). In direct mode, clock polarity
uint8_t direct_presc;
uint16_t direct_msec;
uint8_t dfilter;
union {
struct {
uint32_t ontime; // length of the captured positive pulse in the current interval
uint32_t last_period; //!< length of the captured interval between two rising edges
uint32_t last_ontime; //!< length of the last captured ontime
} pwm_cont;
} ind_cont;
struct {
uint32_t ontime; // length of the captured positive pulse in the current interval
@ -52,16 +67,15 @@ struct priv {
uint64_t ontime_acu; //!< length of the last captured ontime, sum
uint16_t n_count; //!< Periods captured
uint16_t n_target; //!< Periods captured - requested count
} pwm_burst;
} ind_burst;
struct {
uint32_t last_count; //!< Pulse count in the last capture window
uint16_t msec; //!< Configured nbr of milliseconds to count
} cnt_cont;
} dir_cont;
struct {
uint16_t msec; //!< Configured nbr of milliseconds to count
} cnt_burst;
uint16_t msec; // capture window length (used in the report callback) - different from the cont time, which is a semi-persistent config
} dir_burst;
};
};
@ -98,5 +112,6 @@ void UFCAP_TIMxHandler(void *arg);
void UFCAP_TIMyHandler(void *arg);
uint32_t UFCAP_GetFreeCounterValue(Unit *unit);
uint32_t UFCAP_FreeCounterClear(Unit *unit);
#endif //GEX_F072_FCAP_INTERNAL_H

@ -16,8 +16,16 @@ void UFCAP_loadBinary(Unit *unit, PayloadParser *pp)
uint8_t version = pp_u8(pp);
(void)version;
priv->signal_pname = pp_char(pp);
priv->signal_pnum = pp_u8(pp);
priv->conf.signal_pname = pp_char(pp);
priv->conf.signal_pnum = pp_u8(pp);
if (version >= 1) {
priv->conf.active_level = pp_bool(pp);
priv->conf.dfilter = pp_u8(pp);
priv->conf.direct_presc = pp_u8(pp);
priv->conf.direct_msec = pp_u16(pp);
priv->conf.startmode = (enum fcap_opmode) pp_u8(pp);
}
}
/** Write to a binary buffer for storing in Flash */
@ -25,10 +33,17 @@ void UFCAP_writeBinary(Unit *unit, PayloadBuilder *pb)
{
struct priv *priv = unit->data;
pb_u8(pb, 0); // version
pb_u8(pb, 1); // version
pb_char(pb, priv->signal_pname);
pb_u8(pb, priv->signal_pnum);
pb_char(pb, priv->conf.signal_pname);
pb_u8(pb, priv->conf.signal_pnum);
// V1
pb_bool(pb, priv->conf.active_level);
pb_u8(pb, priv->conf.dfilter);
pb_u8(pb, priv->conf.direct_presc);
pb_u16(pb, priv->conf.direct_msec);
pb_u8(pb, priv->conf.startmode);
}
// ------------------------------------------------------------------------
@ -39,10 +54,30 @@ error_t UFCAP_loadIni(Unit *unit, const char *key, const char *value)
bool suc = true;
struct priv *priv = unit->data;
if (streq(key, "signal-pin")) {
suc = parse_pin(value, &priv->signal_pname, &priv->signal_pnum);
if (streq(key, "pin")) {
suc = parse_pin(value, &priv->conf.signal_pname, &priv->conf.signal_pnum);
}
else if (streq(key, "active-level")) {
priv->conf.active_level = (bool) avr_atoi(value);
}
else if (streq(key, "input-filter")) {
priv->conf.dfilter = (uint8_t) avr_atoi(value);
}
else if (streq(key, "direct-presc")) {
priv->conf.direct_presc = (uint8_t) avr_atoi(value);
}
else if (streq(key, "direct-time")) {
priv->conf.direct_msec = (uint16_t) avr_atoi(value);
}
else {
else if (streq(key, "initial-mode")) {
priv->conf.startmode = (enum fcap_opmode) str_parse_4(value,
"N", OPMODE_IDLE,
"I", OPMODE_INDIRECT_CONT,
"D", OPMODE_DIRECT_CONT,
"F", OPMODE_FREE_COUNTER,
&suc);
}
else{
return E_BAD_KEY;
}
@ -58,6 +93,27 @@ void UFCAP_writeIni(Unit *unit, IniWriter *iw)
iw_comment(iw, "Signal input pin - one of:");
iw_comment(iw, " Full support: A0, A5, A15");
iw_comment(iw, " Indirect only: A1, B3");
iw_entry(iw, "signal-pin", "%c%d", priv->signal_pname, priv->signal_pnum);
iw_entry(iw, "pin", "%c%d", priv->conf.signal_pname, priv->conf.signal_pnum);
iw_cmt_newline(iw);
iw_comment(iw, "Active level or edge (0-low,falling; 1-high,rising)");
iw_entry(iw, "active-level", "%d", (int)priv->conf.active_level);
iw_comment(iw, "Input filtering (0-15)");
iw_entry(iw, "input-filter", "%d", (int)priv->conf.active_level);
iw_comment(iw, "Pulse counter pre-divider (1,2,4,8)");
iw_entry(iw, "direct-presc", "%d", (int)priv->conf.direct_presc);
iw_comment(iw, "Pulse counting interval (ms)");
iw_entry(iw, "direct-time", "%d", (int)priv->conf.direct_msec);
iw_cmt_newline(iw);
iw_comment(iw, "Mode on startup: N-none, I-indirect, D-direct, F-free count");
iw_entry(iw, "initial-mode", "%s", str_4(priv->conf.startmode,
OPMODE_IDLE, "N",
OPMODE_INDIRECT_CONT, "I",
OPMODE_DIRECT_CONT, "D",
OPMODE_FREE_COUNTER, "F"));
}

@ -12,62 +12,134 @@
enum FcapCmd_ {
CMD_STOP = 0,
CMD_INDIRECT_CONT_START = 1,
CMD_INDIRECT_BURST_START = 2,
CMD_DIRECT_CONT_START = 3,
CMD_DIRECT_BURST_START = 4,
CMD_FREERUNNING_START = 5,
// Measuring a waveform
CMD_INDIRECT_CONT_START = 1, // keep measuring, read on demand
CMD_INDIRECT_BURST_START = 2, // wait and reply
// Counting pulses
CMD_DIRECT_CONT_START = 3, // keep measuring, read on demand
CMD_DIRECT_BURST_START = 4, // wait and reply
CMD_FREECOUNT_START = 5, // keep counting pulses until stopped, read on reply
CMD_MEASURE_SINGLE_PULSE = 6,
CMD_FREECOUNT_CLEAR = 7,
// Results readout for continuous modes
CMD_INDIRECT_CONT_READ = 10,
CMD_DIRECT_CONT_READ = 11,
CMD_FREERUNNING_READ = 12,
CMD_FREECOUNT_READ = 12,
CMD_SET_POLARITY = 20,
CMD_SET_DIR_PRESC = 21,
CMD_SET_INPUT_FILTER = 22,
CMD_SET_DIR_MSEC = 23,
CMD_RESTORE_DEFAULTS = 30,
};
/** Handle a request message */
static error_t UFCAP_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
{
struct priv *priv = unit->data;
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
uint8_t presc;
uint16_t msec;
struct priv *priv = unit->data;
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
const char* msg_denied_on_pin = "Not available on the selected pin!";
switch (command) {
/**
* Stop any ongoing measurement and return to base state.
*/
case CMD_STOP:
UFCAP_SwitchMode(unit, OPMODE_IDLE);
return E_SUCCESS;
case CMD_INDIRECT_CONT_START:
if (priv->opmode == OPMODE_PWM_CONT) return E_SUCCESS; // no-op
if (priv->opmode != OPMODE_IDLE) return E_BUSY;
// ----------------------- CONFIG --------------------------
UFCAP_SwitchMode(unit, OPMODE_PWM_CONT);
/**
* Set the active polarity, or triggering edge (for direct)
*
* pld: pol:u8 (0,1)
*/
case CMD_SET_POLARITY:
{
priv->active_level = pp_bool(pp);
}
return E_SUCCESS;
case CMD_INDIRECT_BURST_START:
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
uint16_t count = pp_u16(pp);
priv->pwm_burst.n_target = count;
priv->request_id = frame_id;
UFCAP_SwitchMode(unit, OPMODE_PWM_BURST);
/**
* Set the direct measurement prescaller 1,2,4,8
*
* pld: presc:u8
*/
case CMD_SET_DIR_PRESC:
{
presc = pp_u8(pp);
if (presc != 1 && presc != 2 && presc != 4 && presc != 8) return E_BAD_VALUE;
priv->direct_presc = presc;
}
return E_SUCCESS;
case CMD_INDIRECT_CONT_READ:
if (priv->opmode != OPMODE_PWM_CONT) {
return E_BAD_MODE;
/**
* Set the input filter for all modes
*
* pld: filter:u8 (0-15)
*/
case CMD_SET_INPUT_FILTER:
{
uint8_t input_filter = pp_u8(pp);
if (input_filter >= 16) return E_BAD_VALUE;
priv->dfilter = input_filter;
}
if (priv->pwm_cont.last_period == 0) {
return E_BUSY;
return E_SUCCESS;
/**
* Set the direct sampling time.
*
* pld: msec:u16
*/
case CMD_SET_DIR_MSEC:
{
msec = pp_u16(pp);
priv->direct_msec = msec;
}
return E_SUCCESS;
pb_u16(&pb, PLAT_AHB_MHZ);
pb_u32(&pb, priv->pwm_cont.last_period);
pb_u32(&pb, priv->pwm_cont.last_ontime);
/**
* Reset all SET* settings to their default values, stop any ongoing measure.
*/
case CMD_RESTORE_DEFAULTS:
UFCAP_SwitchMode(unit, OPMODE_IDLE);
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
priv->active_level = priv->conf.active_level;
priv->direct_presc = priv->conf.direct_presc;
priv->direct_msec = priv->conf.direct_msec;
priv->dfilter = priv->conf.dfilter;
return E_SUCCESS;
// ------------------ COMMANDS ------------------------
/**
* Start indirect continuous measurement.
*/
case CMD_INDIRECT_CONT_START:
if (priv->opmode == OPMODE_INDIRECT_CONT) return E_SUCCESS; // no-op
if (priv->opmode != OPMODE_IDLE) return E_BUSY;
UFCAP_SwitchMode(unit, OPMODE_INDIRECT_CONT);
return E_SUCCESS;
/**
* Start a continuous direct measurement (counting pulses in fixed time intervals)
*
* - meas_time_ms 0 = no change
* - prescaller 0 = no change
*
* pld: meas_time_ms:u16, prescaller:u8
* - prescaller is 1,2,4,8; 0 = no change
*/
case CMD_DIRECT_CONT_START:
if (!priv->a_direct) {
// This works only if we use the ETR pin. TIM2 shares CH1 with ETR.
@ -75,45 +147,147 @@ static error_t UFCAP_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command,
com_respond_str(MSG_ERROR, frame_id, msg_denied_on_pin);
return E_FAILURE;
}
if (priv->opmode == OPMODE_COUNTER_CONT) return E_SUCCESS; // no-op
if (priv->opmode == OPMODE_DIRECT_CONT) return E_SUCCESS; // no-op
if (priv->opmode != OPMODE_IDLE) return E_BUSY;
msec = pp_u16(pp);
priv->cnt_cont.msec = msec;
presc = pp_u8(pp);
if (msec != 0) priv->direct_msec = msec;
if (presc != 0) priv->direct_presc = presc;
UFCAP_SwitchMode(unit, OPMODE_COUNTER_CONT);
UFCAP_SwitchMode(unit, OPMODE_DIRECT_CONT);
return E_SUCCESS;
case CMD_DIRECT_CONT_READ:
if (!priv->a_direct) { // see above
com_respond_str(MSG_ERROR, frame_id, msg_denied_on_pin);
return E_FAILURE;
}
if (priv->opmode != OPMODE_COUNTER_CONT) return E_BAD_MODE;
if (priv->cnt_cont.last_count == 0) return E_BUSY;
pb_u32(&pb, priv->cnt_cont.last_count);
pb_u16(&pb, priv->cnt_cont.msec);
/**
* Start a burst of direct measurements with averaging.
* The measurement is performed on N consecutive pulses.
*
* pld: count:u16
*
* resp: core_mhz:u16, count:u16, period_sum:u64, ontime_sum:u64
*/
case CMD_INDIRECT_BURST_START:
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
priv->ind_burst.n_target = pp_u16(pp);
priv->request_id = frame_id;
UFCAP_SwitchMode(unit, OPMODE_INDIRECT_BURST);
return E_SUCCESS;
/**
* Start a single direct measurement of the given length (pulses in time period)
* If 'prescaller' is not 0, it is changed via the param field.
*
* pld: meas_time_ms:u16, prescaller:u8
* - prescaller is 1,2,4,8; 0 = no change
*
* resp: prescaller:u8, meas_time_ms:u16, pulse_count:u32
*/
case CMD_DIRECT_BURST_START:
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
msec = pp_u16(pp);
priv->cnt_burst.msec = msec;
priv->dir_burst.msec = pp_u16(pp);
presc = pp_u8(pp);
if (presc != 0) priv->direct_presc = presc;
priv->request_id = frame_id;
UFCAP_SwitchMode(unit, OPMODE_COUNTER_BURST);
UFCAP_SwitchMode(unit, OPMODE_DIRECT_BURST);
return E_SUCCESS;
case CMD_FREERUNNING_START:
/**
* Measure a single pulse length of the given polarity.
* Measures time from a rising to a falling edge (or falling to rising, if polarity is 0)
*
* resp: core_mhz:u16, ontime:u32
*/
case CMD_MEASURE_SINGLE_PULSE:
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
UFCAP_SwitchMode(unit, OPMODE_COUNTER_FREERUNNING);
priv->request_id = frame_id;
UFCAP_SwitchMode(unit, OPMODE_SINGLE_PULSE);
return E_SUCCESS;
/**
* Start a free-running pulse counter.
*
* pld: prescaller:u8
* - prescaller is 1,2,4,8; 0 = no change
*/
case CMD_FREECOUNT_START:
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
presc = pp_u8(pp);
if (presc != 0) priv->direct_presc = presc;
UFCAP_SwitchMode(unit, OPMODE_FREE_COUNTER);
return E_SUCCESS;
/**
* Reset the free-running pulse counter.
*
* resp: last_val:u32
*/
case CMD_FREECOUNT_CLEAR:
if (priv->opmode != OPMODE_FREE_COUNTER) {
return E_BAD_MODE;
}
pb_u32(&pb, UFCAP_FreeCounterClear(unit));
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
return E_SUCCESS;
// ------------------ READING ---------------------
/**
* Read the most recent pulse measurement during continuous indirect measure.
*
* resp: core_mhz:u16, period:u32, ontime:u32
*/
case CMD_INDIRECT_CONT_READ:
if (priv->opmode != OPMODE_INDIRECT_CONT) {
return E_BAD_MODE;
}
if (priv->ind_cont.last_period == 0) {
return E_BUSY;
}
pb_u16(&pb, PLAT_AHB_MHZ);
pb_u32(&pb, priv->ind_cont.last_period);
pb_u32(&pb, priv->ind_cont.last_ontime);
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
return E_SUCCESS;
/**
* Read the most recent result of a continuous direct measurement.
*
* resp: prescaller:u8, meas_time_ms:u16, pulse_count:u32
*/
case CMD_DIRECT_CONT_READ:
if (!priv->a_direct) { // see above
com_respond_str(MSG_ERROR, frame_id, msg_denied_on_pin);
return E_FAILURE;
}
if (priv->opmode != OPMODE_DIRECT_CONT) return E_BAD_MODE;
if (priv->dir_cont.last_count == 0) return E_BUSY;
pb_u8(&pb, priv->direct_presc);
pb_u16(&pb, priv->direct_msec);
pb_u32(&pb, priv->dir_cont.last_count);
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
return E_SUCCESS;
case CMD_FREERUNNING_READ:
if (priv->opmode != OPMODE_COUNTER_FREERUNNING) {
/**
* Read the current value of the free-running pulse counter.
*
* The timing may have a significant jitter, this function is practically useful only for
* slow pulse sources (like a geiger counter, item counting etc)
*
* resp: count:u32
*/
case CMD_FREECOUNT_READ:
if (priv->opmode != OPMODE_FREE_COUNTER) {
return E_BAD_MODE;
}

@ -74,41 +74,41 @@ const char *str_4(uint32_t n,
return a;
}
uint32_t str_parse_2(const char *tpl,
uint32_t str_parse_2(const char *value,
const char *a, uint32_t na,
const char *b, uint32_t nb,
bool *suc)
{
if (streq(tpl, a)) return na;
if (streq(tpl, b)) return nb;
if (streq(value, a)) return na;
if (streq(value, b)) return nb;
*suc = false;
return na;
}
uint32_t str_parse_3(const char *tpl,
uint32_t str_parse_3(const char *value,
const char *a, uint32_t na,
const char *b, uint32_t nb,
const char *c, uint32_t nc,
bool *suc)
{
if (streq(tpl, a)) return na;
if (streq(tpl, b)) return nb;
if (streq(tpl, c)) return nc;
if (streq(value, a)) return na;
if (streq(value, b)) return nb;
if (streq(value, c)) return nc;
*suc = false;
return na;
}
uint32_t str_parse_4(const char *tpl,
uint32_t str_parse_4(const char *value,
const char *a, uint32_t na,
const char *b, uint32_t nb,
const char *c, uint32_t nc,
const char *d, uint32_t nd,
bool *suc)
{
if (streq(tpl, a)) return na;
if (streq(tpl, b)) return nb;
if (streq(tpl, c)) return nc;
if (streq(tpl, d)) return nd;
if (streq(value, a)) return na;
if (streq(value, b)) return nb;
if (streq(value, c)) return nc;
if (streq(value, d)) return nd;
*suc = false;
return na;
}

Loading…
Cancel
Save