From b678a8f2b7ef4f3ffecc2f6dd242ff9aafd17dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 22 Feb 2018 13:42:42 +0100 Subject: [PATCH] fcap cleaning, added new features and reorganized the unit --- platform/ll_extension.c | 38 +++++ platform/ll_extension.h | 3 +- units/fcap/_fcap_core.c | 194 ++++++++++++++++++------- units/fcap/_fcap_init.c | 34 +++-- units/fcap/_fcap_internal.h | 41 ++++-- units/fcap/_fcap_settings.c | 74 ++++++++-- units/fcap/unit_fcap.c | 272 +++++++++++++++++++++++++++++------- utils/str_utils.c | 24 ++-- 8 files changed, 537 insertions(+), 143 deletions(-) diff --git a/platform/ll_extension.c b/platform/ll_extension.c index cec9d9f..c7fbc14 100644 --- a/platform/ll_extension.c +++ b/platform/ll_extension.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, diff --git a/platform/ll_extension.h b/platform/ll_extension.h index ddb85af..6f0b384 100644 --- a/platform/ll_extension.h +++ b/platform/ll_extension.h @@ -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) { diff --git a/units/fcap/_fcap_core.c b/units/fcap/_fcap_core.c index 8186f95..c48fedc 100644 --- a/units/fcap/_fcap_core.c +++ b/units/fcap/_fcap_core.c @@ -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); diff --git a/units/fcap/_fcap_init.c b/units/fcap/_fcap_init.c index 21df121..cc79e41 100644 --- a/units/fcap/_fcap_init.c +++ b/units/fcap/_fcap_init.c @@ -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; } diff --git a/units/fcap/_fcap_internal.h b/units/fcap/_fcap_internal.h index 162f707..9111322 100644 --- a/units/fcap/_fcap_internal.h +++ b/units/fcap/_fcap_internal.h @@ -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 diff --git a/units/fcap/_fcap_settings.c b/units/fcap/_fcap_settings.c index 28401d7..9d61a66 100644 --- a/units/fcap/_fcap_settings.c +++ b/units/fcap/_fcap_settings.c @@ -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")); } diff --git a/units/fcap/unit_fcap.c b/units/fcap/unit_fcap.c index a78d21f..f01c400 100644 --- a/units/fcap/unit_fcap.c +++ b/units/fcap/unit_fcap.c @@ -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; } diff --git a/utils/str_utils.c b/utils/str_utils.c index 1f67552..7a24869 100644 --- a/utils/str_utils.c +++ b/utils/str_utils.c @@ -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; }