Frequency counter library only for DISCO-F746NG & NucleoF411RE +F446RE

Dependencies:   RingBuff

Dependents:   FreqCntr_GPS1PPS_F746F4xx_w_recipro Freq_Cntr_GPS1PPS_F746NG_GUI

Fork of Frq_cuntr_full by Kenji Arai

fc_hw_f411.h

Committer:
kenjiArai
Date:
2016-11-16
Revision:
6:be7123d400ae
Parent:
5:783b039f9119

File content as of revision 6:be7123d400ae:

/*
 * mbed Library / Frequency Counter using GPS 1PPS gate pulse
 *      Frequency Counter Hardware relataed program
 *      Only for ST Nucleo-F411RE+F446RE
 *
 *      Re-started: October    5th, 2016    Change board -> DISCO-F746NG
 *      Re-started: October   17th, 2016    Back to F411
 *      Revised:    November   8th, 2016
 *
 */

#if defined(TARGET_NUCLEO_F411RE) || defined(TARGET_NUCLEO_F446RE)

// Following ring buffers are very big!! 1024 x 8(bytes) x 2(buff) = 16KB
RingBuff<uint64_t> fdt_buffer(1024);    // RinBuffer for TIM1+3
freq_one fdt;                             // frequency data in pack (interrupt)
RingBuff<uint64_t> onepps_buf(1024);    // RinBuffer for TIM2
freq_one onepps_dt;                       // frequency data in pack (interrupt)

//------------------------------------------------------------------------------
//  Initialize TIM1+3 and TIM2
//------------------------------------------------------------------------------
// Initialize TIM1 & TIM3 as 32bit counter (TIM1(Low 16bit) + TIM3(High 16bit))
//   TIM1 clock input is unkown freq.(measuring freq.) and TIM3 is slave counter
//   1PPS gate signal connected both TIM1 IC2, TIM3 IC3 (& TIM2 IC2)
void FRQ_CUNTR::initialize_TIMxPy(void)
{
    // Timer1 input max freq.= 48MHz (@SystemCoreClock = 96MHz)
    // PA8 -> Unkown frequency input pin as Timer1 CH1/TI1
    RCC->AHB1ENR  |= (RCC_AHB1ENR_GPIOAEN);
    GPIOA->AFR[1] &= 0xfffffff0;
    GPIOA->AFR[1] |= GPIO_AF1_TIM1 << 0;        // (8-8)bit x 4
    GPIOA->MODER  &= ~(GPIO_MODER_MODER8);      // AF
    GPIOA->MODER  |= GPIO_MODER_MODER8_1;       //  alternate function mode
    GPIOA->PUPDR  &= ~(GPIO_PUPDR_PUPDR8);      // PU
    GPIOA->PUPDR  |= GPIO_PUPDR_PUPDR8_0;       //  Pull-up mode
    // Initialize Timer1(16bit) for an external up counter mode
    RCC->APB2ENR  |= RCC_APB2ENR_TIM1EN;
                                // count_up + div by 1
    TIM1->CR1     &= (uint16_t)(~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD));
                                // update request only by overflow
    TIM1->CR1     |= (uint16_t)TIM_CR1_URS;
    TIM1->ARR      = 0xffff;                    // Use 16bit full
    TIM1->CCER     = 0;                         // Reset all
                                // input filter + input select
    TIM1->CCMR1   &= ~(TIM_CCMR1_IC1F | TIM_CCMR1_CC1S);
    TIM1->CCMR1   |= TIM_CCMR1_CC1S_0;          // CC1 channel = TI1
                                // external mode 1
    TIM1->SMCR    &= ~(TIM_SMCR_ECE | TIM_SMCR_TS | TIM_SMCR_SMS);
                                // ECE must be ZERO!!!!
    TIM1->SMCR    |= ( TIM_TS_TI1FP1 | TIM_SLAVEMODE_EXTERNAL1);
    TIM1->CR2     &= (uint16_t)~(TIM_CR2_TI1S | TIM_CR2_MMS);
    TIM1->CR2     |= (uint16_t)TIM_CR2_MMS_1;   // TRGO update
    TIM1->CR1     |= (uint16_t)TIM_CR1_CEN;     // Enable the TIM Counter
    // PA9 -> Input Capture pin as Timer1 IC2
    GPIOA->AFR[1] &= 0xffffff0f;
    GPIOA->AFR[1] |= GPIO_AF1_TIM1 << 4;        // (9-8)bit x 4
    GPIOA->MODER  &= ~(GPIO_MODER_MODER9);      // AF
    GPIOA->MODER  |= GPIO_MODER_MODER9_1;       //  alternate function mode
    GPIOA->PUPDR  &= ~(GPIO_PUPDR_PUPDR9);      // PU
    GPIOA->PUPDR  |= GPIO_PUPDR_PUPDR9_0;       //  Pull-up mode
    // Initialize Timer1 IC2
    TIM1->CCER    &= (uint16_t)~TIM_CCER_CC2E;  // Disable the CC2
                                // input filter + input select
    TIM1->CCMR1   &= ~(TIM_CCMR1_IC2F | TIM_CCMR1_CC2S);
    TIM1->CCMR1   |= TIM_CCMR1_CC2S_0;
                                // positive edge
    TIM1->CCER    &= (uint16_t)~(TIM_CCER_CC2P | TIM_CCER_CC2NP);
    TIM1->CCER    |= (uint16_t)TIM_CCER_CC2E;   // enable capture
    // Initialize Timer3(16bit) as slave counter of TIM1
    // TIM1 overflow event -> ITR0 as Timer3 clock
    RCC->APB1ENR  |= RCC_APB1ENR_TIM3EN;
    TIM3->CR1     &= (uint16_t)(~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD));
                                // only counter overflow for interrupt
    TIM3->CR1     |= (uint16_t)TIM_CR1_URS;
                                // Up counter uses from 0 to max(32bit)
    TIM3->ARR      = 0xffff;                    // Use 16bit full
    TIM3->CCER     = 0;                         // Reset all
                                // external mode 1
    TIM3->SMCR    &= ~(TIM_SMCR_ECE | TIM_SMCR_TS | TIM_SMCR_SMS);
    TIM3->SMCR    |= (TIM_TS_ITR0);             // Set Internal Triger 0 (ITR0)
    TIM3->SMCR    |= (TIM_SLAVEMODE_EXTERNAL1); // ECE must be ZERO!!!!
    TIM3->CR2     &= (uint16_t)~(TIM_CR2_TI1S | TIM_CR2_MMS);
    TIM3->CR2     |= (uint16_t)TIM_CR2_MMS_1;   // TRGO update
    TIM3->CR1     |= (uint16_t)TIM_CR1_CEN;     // Enable the TIM Counter
    // PB0 -> Input Capture pin as Timer3 IC3
    GPIOB->AFR[0] &= 0xfffffff0;
    GPIOB->AFR[0] |= GPIO_AF2_TIM3 << 0;        // 0bit x 4
    GPIOB->MODER  &= ~(GPIO_MODER_MODER0);      // AF
    GPIOB->MODER  |= GPIO_MODER_MODER0_1;       //  alternate function mode
    GPIOB->PUPDR  &= ~(GPIO_PUPDR_PUPDR0);      // PU
    GPIOB->PUPDR  |= GPIO_PUPDR_PUPDR0_0;       //  Pull-up mode
    // Initialize Timer3 IC3
    TIM3->CCER    &= (uint16_t)~TIM_CCER_CC2E;
    TIM3->CCMR2   &= ~(TIM_CCMR2_CC3S | TIM_CCMR2_IC3F);
    TIM3->CCMR2   |= TIM_CCMR2_CC3S_0;
                                // positive edge
    TIM3->CCER    &= (uint16_t)~(TIM_CCER_CC3P | TIM_CCER_CC3NP);
    TIM3->CCER    |= (uint16_t)TIM_CCER_CC3E;   // enable capture
    // Up counter based on gate time period
    time_count = 0;
    // Only for Debug purpose
    PRINTF("\r\n// Timer1(16bit high(max48MHz) clock)\r\n");
    PRINTF("//  and Timer3(16bit low clock) combined up counter\r\n");
    PRINTF("// Set GPIO for Timer1&3\r\n");
    //  PA
    PRINTF("// PA8 -> unkown frequency input pin as Timer1 CH1/TI1\r\n");
    PRINTF("// PA9 -> Input Capture pin as Timer1 CH2/TI2\r\n");
    PRINTF("GPIOA->AFRL    0x%08x:0x%08x\r\n",&GPIOA->AFR[0], GPIOA->AFR[0]);
    PRINTF("GPIOA->AFRH    0x%08x:0x%08x\r\n",&GPIOA->AFR[1], GPIOA->AFR[1]);
    PRINTF("GPIOA->MODER   0x%08x:0x%08x\r\n",&GPIOA->MODER, GPIOA->MODER);
    PRINTF("GPIOA->PUPDR   0x%08x:0x%08x\r\n",&GPIOA->PUPDR, GPIOA->PUPDR);
    PRINTF("GPIOA->OTYPER  0x%08x:0x%08x\r\n",&GPIOA->OTYPER, GPIOA->OTYPER);
    PRINTF("GPIOA->OSPEEDR 0x%08x:0x%08x\r\n",&GPIOA->OSPEEDR, GPIOA->OSPEEDR);
    //  PB
    PRINTF("// PB0 -> Input Capture pin as Timer3 CH3/TI3\r\n");
    PRINTF("GPIOB->AFRL    0x%08x:0x%08x\r\n",&GPIOB->AFR[0], GPIOB->AFR[0]);
    PRINTF("GPIOB->AFRH    0x%08x:0x%08x\r\n",&GPIOB->AFR[1], GPIOB->AFR[1]);
    PRINTF("GPIOB->MODER   0x%08x:0x%08x\r\n",&GPIOB->MODER, GPIOB->MODER);
    PRINTF("GPIOB->PUPDR   0x%08x:0x%08x\r\n",&GPIOB->PUPDR, GPIOB->PUPDR);
    PRINTF("GPIOB->OTYPER  0x%08x:0x%08x\r\n",&GPIOB->OTYPER, GPIOB->OTYPER);
    PRINTF("GPIOB->OSPEEDR 0x%08x:0x%08x\r\n",&GPIOB->OSPEEDR, GPIOB->OSPEEDR);
    //  TIM1
    PRINTF("// PA8 -> Timer1(16bit) for an external up counter mode\r\n");
    PRINTF("// PA9 -> Timer1 IC2\r\n");
    PRINTF("TIM1->CR1      0x%08x:0x%08x\r\n",&TIM1->CR1, TIM1->CR1);
    PRINTF("TIM1->CR2      0x%08x:0x%08x\r\n",&TIM1->CR2, TIM1->CR2);
    PRINTF("TIM1->ARR      0x%08x:0x%08x\r\n",&TIM1->ARR, TIM1->ARR);
    PRINTF("TIM1->PSC      0x%08x:0x%08x\r\n",&TIM1->PSC, TIM1->PSC);
    PRINTF("TIM1->CCMR1    0x%08x:0x%08x\r\n",&TIM1->CCMR1, TIM1->CCMR1);
    PRINTF("TIM1->CCMR2    0x%08x:0x%08x\r\n",&TIM1->CCMR2, TIM1->CCMR2);
    PRINTF("TIM1->CCER     0x%08x:0x%08x\r\n",&TIM1->CCER, TIM1->CCER);
    PRINTF("TIM1->SMCR     0x%08x:0x%08x\r\n",&TIM1->SMCR, TIM1->SMCR);
    PRINTF("TIM1->DIER     0x%08x:0x%08x\r\n",&TIM1->DIER, TIM1->DIER);
    //  TIM3
    PRINTF("// PB0 -> Timer3 IC3\r\n");
    PRINTF("TIM3->CR1      0x%08x:0x%08x\r\n",&TIM3->CR1, TIM3->CR1);
    PRINTF("TIM3->CR2      0x%08x:0x%08x\r\n",&TIM3->CR2, TIM3->CR2);
    PRINTF("TIM3->ARR      0x%08x:0x%08x\r\n",&TIM3->ARR, TIM3->ARR);
    PRINTF("TIM3->PSC      0x%08x:0x%08x\r\n",&TIM3->PSC, TIM3->PSC);
    PRINTF("TIM3->CCMR1    0x%08x:0x%08x\r\n",&TIM3->CCMR1, TIM3->CCMR1);
    PRINTF("TIM3->CCMR2    0x%08x:0x%08x\r\n",&TIM3->CCMR2, TIM3->CCMR2);
    PRINTF("TIM3->CCER     0x%08x:0x%08x\r\n",&TIM3->CCER, TIM3->CCER);
    PRINTF("TIM3->SMCR     0x%08x:0x%08x\r\n",&TIM3->SMCR, TIM3->SMCR);
    // Interrupt Timer3 IC3 (NOT Timer1 IC2) & Overflow
    timxpy_ready_flg = 0;
    timxpy_cnt_data = 0;
    sw_ovrflw_timxpy = 0;
    TIM1->SR = 0;           // clear all IC/OC flag
    TIM3->SR = 0;           // clear all IC/OC flag
    TIM3->DIER = 0;         // Reset all interrupt flag
    TIM3->DIER = TIM_DIER_CC3IE + TIM_DIER_UIE;
    NVIC_SetVector(TIM3_IRQn, (uint32_t)irq_ic_TIMxPy);
    NVIC_ClearPendingIRQ(TIM3_IRQn);
    NVIC_EnableIRQ(TIM3_IRQn);
    PRINTF("TIM3->DIER     0x%08x:0x%08x\r\n\r\n",&TIM3->DIER, TIM3->DIER);
    PRINTF("RCC->APB1ENR   0x%08x:0x%08x\r\n\r\n",&RCC->APB1ENR, RCC->APB1ENR);
}

// Initialize TIM2
// IC2->PA1 for GPS 1pps signal
// IC1->PA0 for Reciprocal frequency counting mode (Interrupt)
void FRQ_CUNTR::initialize_TIMz(void)
{
    // Initialize Timer2(32bit) for an internal(100MHz) up counter mode
    RCC->APB1ENR  |= RCC_APB1ENR_TIM2EN;
                                // count_up + div by 1
    TIM2->CR1     &= (uint16_t)(~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD));
                                // only counter overflow for interrupt
    TIM2->CR1     |= (uint16_t)TIM_CR1_URS;
                                // Up counter uses from 0 to max(32bit)
    TIM2->ARR      = 0xffffffff;
    TIM2->PSC      = 0x0000;                    // 1/1
    TIM2->CCER     = 0;                         // Reset all
                                // input filter + input select
    TIM2->CCMR1   &= ~(TIM_CCMR1_IC1F | TIM_CCMR1_CC1S);
    TIM2->CCMR1   |= TIM_CCMR1_CC1S_0;
    TIM2->SMCR     = 0;                         // Internal clock
    TIM2->CR1     |= (uint16_t)TIM_CR1_CEN;     // Enable the TIM Counter
    // PA0 -> Input Capture pin as Timer2 IC1 for Reciprocal
    GPIOA->AFR[0] &= 0xfffffff0;
    GPIOA->AFR[0] |= GPIO_AF1_TIM2 << 0;        // 0bit x 4
    GPIOA->MODER  &= ~(GPIO_MODER_MODER0);      // AF
    GPIOA->MODER  |= GPIO_MODER_MODER0_1;       //  alternate function mode
    GPIOA->PUPDR  &= ~(GPIO_PUPDR_PUPDR0);      // PU
    GPIOA->PUPDR  |= GPIO_PUPDR_PUPDR0_0;       //  Pull-up mode
    // Initialize Timer2 IC1
                                // input filter + input select
    TIM2->CCMR1   &= ~(TIM_CCMR1_IC1F | TIM_CCMR1_CC1S);
    TIM2->CCMR1   |= (TIM_CCMR1_CC1S_0 + TIM_CCMR1_IC1F_1); // filter = fCLK/4
                                // positive edge
    TIM2->CCER    &= (uint16_t)~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
    TIM2->CCER    |= (uint16_t)TIM_CCER_CC1E;   // enable capture
    // PA1 -> Input Capture pin as Timer2 IC2
    GPIOA->AFR[0] &= 0xffffff0f;
    GPIOA->AFR[0] |= GPIO_AF1_TIM2 << 4;        // 1bit x 4
    GPIOA->MODER  &= ~(GPIO_MODER_MODER1);      // AF
    GPIOA->MODER  |= GPIO_MODER_MODER1_1;       //  alternate function mode
    GPIOA->PUPDR  &= ~(GPIO_PUPDR_PUPDR1);      // PU
    GPIOA->PUPDR  |= GPIO_PUPDR_PUPDR1_0;       //  Pull-up mode
    // Initialize Timer2 IC2
    TIM2->CCER    &= (uint16_t)~TIM_CCER_CC2E;  // Disable the CC
    TIM2->CCMR1   &= ~(TIM_CCMR1_IC2F | TIM_CCMR1_CC2S);
    //TIM2->CCMR1   |= TIM_CCMR1_CC2S_0; // filter none
    TIM2->CCMR1   |= (TIM_CCMR1_CC2S_0 + TIM_CCMR1_IC2F_1); // filter = fCLK/4
                                // positive edge
    TIM2->CCER    &= (uint16_t)~(TIM_CCER_CC2P | TIM_CCER_CC2NP);
    TIM2->CCER    |= (uint16_t)TIM_CCER_CC2E;   // enable capture
    // Up counter based on gate time period
    time_count_onepps = 0;
    // Only for Debug purpose
    //  PA
    PRINTF("\r\n// Timer2(32bit) for an internal up counter mode\r\n");
    PRINTF("// Set GPIO for Timer2\r\n");
    PRINTF("// PA0  -> Input Capture pin as Timer2 CH1/TI1\r\n");
    PRINTF("// PA1  -> Input Capture pin as Timer2 CH2/TI2\r\n");
    PRINTF("GPIOA->AFRL  0x%08x:0x%08x\r\n",&GPIOA->AFR[0], GPIOA->AFR[0]);
    PRINTF("GPIOA->AFRH  0x%08x:0x%08x\r\n",&GPIOA->AFR[1], GPIOA->AFR[1]);
    PRINTF("GPIOA->MODER 0x%08x:0x%08x\r\n",&GPIOA->MODER, GPIOA->MODER);
    PRINTF("GPIOA->PUPDR 0x%08x:0x%08x\r\n",&GPIOA->PUPDR, GPIOA->PUPDR);
    //  TIM2
    PRINTF("// PA0 -> Timer2 IC1\r\n");
    PRINTF("// PA1 -> Timer2 IC2\r\n");
    PRINTF("TIM2->CR1    0x%08x:0x%08x\r\n",&TIM2->CR1, TIM2->CR1);
    PRINTF("TIM2->CR2    0x%08x:0x%08x\r\n",&TIM2->CR2, TIM2->CR2);
    PRINTF("TIM2->ARR    0x%08x:0x%08x\r\n",&TIM2->ARR, TIM2->ARR);
    PRINTF("TIM2->PSC    0x%08x:0x%08x\r\n",&TIM2->PSC, TIM2->PSC);
    PRINTF("TIM2->CCMR1  0x%08x:0x%08x\r\n",&TIM2->CCMR1, TIM2->CCMR1);
    PRINTF("TIM2->CCMR2  0x%08x:0x%08x\r\n",&TIM2->CCMR2, TIM2->CCMR2);
    PRINTF("TIM2->CCER   0x%08x:0x%08x\r\n",&TIM2->CCER, TIM2->CCER);
    PRINTF("TIM2->SMCR   0x%08x:0x%08x\r\n",&TIM2->SMCR, TIM2->SMCR);
    // Interrupt Timer2 IC4(disable at this moment) & Overflow
    timz_cnt_data = 0;
    sw_ovrflw_timz = 0;
    TIM2->SR = 0;                   // clear all IC/OC flag
    TIM2->DIER = 0;                 // Disable all interrupt
    TIM2->DIER = TIM_DIER_UIE;      // set only overflow
    NVIC_SetVector(TIM2_IRQn, (uint32_t)irq_ic_TIMz);
    NVIC_ClearPendingIRQ(TIM2_IRQn);
    NVIC_EnableIRQ(TIM2_IRQn);
    PRINTF("TIM2->DIER   0x%08x:0x%08x\r\n\r\n",&TIM2->DIER, TIM2->DIER);
}

//------------------------------------------------------------------------------
//  Reciprocal measuremt
//------------------------------------------------------------------------------
void FRQ_CUNTR::start_action(void)
{
    __disable_irq();
    TIM2->SR &= ~TIM_SR_CC1IF;      // clear IC flag
    TIM2->DIER |= TIM_DIER_CC1IE;   // Enable IC1
    __enable_irq();
}

//------------------------------------------------------------------------------
//  Frequency check for test & debug purpose
//------------------------------------------------------------------------------
// Read TIM1(+TIM3) Input frequency
uint32_t FRQ_CUNTR::debug_read_input_frequency(double gatetime)
{
    TIM1->CR1 &= ~(uint16_t)TIM_CR1_CEN;    // disable the TIM1 Counter
    TIM3->CNT  = 0;
    TIM1->CNT  = 0;
    TIM1->CR1 |= (uint16_t)TIM_CR1_CEN;     // Enable the TIM1 Counter
    wait(gatetime);                         // Gate time for count
    TIM1->CR1 &= ~(uint16_t)TIM_CR1_CEN;    // disable the TIM1 Counter
    uint32_t freq = (TIM3->CNT << 16) + TIM1->CNT;
    TIM1->CR1 |= (uint16_t)TIM_CR1_CEN;     // Enable the TIM1 Counter
    PRINTF("Input freq.=%10d [Hz], gate= %4.2f [Sec]\r\n", freq, gatetime);
    return freq;                            // read counter
}

// Read TIM2 Clock frequency
uint32_t FRQ_CUNTR::debug_read_base_clock_frequency(double gatetime)
{
    TIM2->CNT = 0;
    wait(gatetime);                         // Gate time for count
    uint32_t freq = TIM2->CNT;              // read counter
    PRINTF("Clock Counter=%10d, gate= %4.2f [Sec]\r\n", freq, gatetime);
    return freq;                            // return counter data
}

// Dump all buffered data
void FRQ_CUNTR::debug_printf_all_buffer(void)
{
    fdt_buffer.debug_print_all_buffer();
}

//------------------------------------------------------------------------------
//  Interrupt Handlers
//------------------------------------------------------------------------------
// GPS 1PPS signal interrupt (connected to TIM3 IC3, TIM1 IC2 and TIM2 IC2)
// TIM3 IC3 Interrupt control
void irq_ic_TIMxPy(void)
{
    // IC3 (GPS 1PPS)
    if (TIM3->SR & TIM_SR_CC3IF){
        TIM1->SR &= ~TIM_SR_CC2IF;      // clear IC flag
        TIM3->SR &= ~TIM_SR_CC3IF;      // clear IC flag
        TIM2->SR &= ~TIM_SR_CC2IF;      // clear IC flag
        timxpy_cnt_data = (TIM3->CCR3 << 16) + TIM1->CCR2;  // 16+16 bit
        timxpy_ready_flg = 1;
        timz_cnt_data = TIM2->CCR2;                         // 32 bit
        // data saves into the ring buffer / Unknown Freq.
        fdt.freq_dt = timxpy_cnt_data;
        fdt.f_sw_dt = sw_ovrflw_timxpy;
        fdt.t_cnt   = ++time_count;
        fdt_buffer.ring_put_dt(fdt.f_1sec_dt);
        // data saves into the ring buffer / F411 internal clock
        onepps_dt.freq_dt = timz_cnt_data;
        onepps_dt.f_sw_dt = sw_ovrflw_timz;
        onepps_dt.t_cnt   = ++time_count_onepps;
        onepps_buf.ring_put_dt(onepps_dt.f_1sec_dt);
        #if ACTIVE_LED_TIMX
        irq_led1 = !irq_led1;
        #endif // ACTIVE_LED
    }
    // TIM3 overflow
    if (TIM3->SR & TIM_SR_UIF){
        TIM3->SR &= ~TIM_SR_UIF;
        ++sw_ovrflw_timxpy;
    }
}

// Reciprocal data (TIM2 IC1)
void irq_ic_TIMz(void)
{
    // IC1 (for reciprocal measurement)
    if (TIM2->SR & TIM_SR_CC1IF){
        TIM2->SR &= ~TIM_SR_CC1IF;  // clear IC flag
        uint32_t data = TIM2->CCR1;
        if (recipro_step == 0){             // start measuring
            recipro_start = data;
            recipro_step = 1;
        } else if (recipro_step == 1){      // stop
            TIM2->DIER &= ~TIM_DIER_CC1IE;  // disable IC4 interrupt
            recipro_stop = data;
            recipro_step = 2;
        } else {                            // jsut in case
            TIM2->DIER &= ~TIM_DIER_CC1IE;  // disable IC4 interrupt
            recipro_step = 0;
        }
        #if ACTIVE_LED_TIMZ
        irq_led1 = !irq_led1;
        #endif // ACTIVE_LED
    }
    // TIM2 overflow
    if (TIM2->SR & TIM_SR_UIF){             // 32bit counter overflow
        TIM2->SR &= ~TIM_SR_UIF;
        ++sw_ovrflw_timz;
    }
}

#endif  // #if defined(TARGET_NUCLEO_F411RE) || defined(TARGET_NUCLEO_F446RE)