Reciprocal Frequency counter only for STM32F401、F411 and F466. Reciprocal Mode -> Pulse width measurement

Dependents:   Frequency_Counter_Recipro_for_STM32F4xx

fc_recipro.cpp

Committer:
kenjiArai
Date:
2020-01-19
Revision:
8:c9ed197ce270
Parent:
7:7fdff925855e

File content as of revision 8:c9ed197ce270:

/*
 * mbed Library / Frequency Counter / Recipro type
 *      Frequency Counter program
 *      Only for Nucleo-F401RE,-F411RE,-F446RE
 *
 * Copyright (c) 2014,'15,'16,'20 Kenji Arai / JH1PJL
 *      http://www7b.biglobe.ne.jp/~kenjia/
 *      https://os.mbed.com/users/kenjiArai/
 *      Started:    October   18th, 2014
 *      Revised:    January   19th, 2020
 */

#include    "fc_recipro.h"

#if (MBED_MAJOR_VERSION == 2)
#   define WAIT(x) wait_ms(x)
#elif (MBED_MAJOR_VERSION == 5)
#   define WAIT(x) ThisThread::sleep_for(x)
#endif

#if DEBUG
#define PRINTF(...)     printf(__VA_ARGS__)
#define SET0            {tstp0=1;}
#define CLR0            {tstp0=0;}
#define SET1            {tstp1=1;}
#define CLR1            {tstp1=0;}
#define SET2            {tstp2=1;}
#define CLR2            {tstp2=0;}
#else
#define PRINTF(...)     {;}
#define SET0            {;}
#define CLR0            {;}
#define SET1            {;}
#define CLR1            {;}
#define SET2            {;}
#define CLR2            {;}
#endif

typedef union {
    struct {
        int64_t    f_64bit_dt;  // not uint but int
    };
    struct {
        uint32_t    freq_dt;
        int32_t     f_sw_dt;    // not uint but int
    };
} freq_one;

namespace Frequency_counter
{

#if DEBUG
// Check interrupt handler processing time
DigitalOut  tstp0(D13, 1);
DigitalOut  tstp1(D12, 1);
DigitalOut  tstp2(D11, 1);
#endif

// TIM2 IC (Reciprocal) + OverFlow
static int32_t sw_ovrflw_tim2;
static uint8_t rise_cnt;
static uint8_t fall_cnt;

// buffer for captured data
freq_one captured_dt;               // frequency data in pack (interrupt)
int64_t rise_buf[2];
int64_t fall_buf[2];
uint32_t rise_pointer;
uint32_t fall_pointer;

//------------------------------------------------------------------------------
//  Frequency Counter Library
//------------------------------------------------------------------------------
FRQ_CUNTR::FRQ_CUNTR(PinName pin) : _input_pin(pin)
{
    rise_pointer = 0;
    fall_pointer = 0;
    initialize_TIM2();      // Use for reciprocal
}

//------------------------------------------------------------------------------
//  Initialize TIM2
//------------------------------------------------------------------------------
// IC1->PA0 for Reciprocal frequency counting mode (Interrupt) ->Rising Edge
// IC2->PA1 for Reciprocal frequency counting mode (Interrupt) ->Falling Edge
void FRQ_CUNTR::initialize_TIM2(void)
{
    // Initialize Timer2(32bit) for an internal(90MHz/F446) 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
    // 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_0); // filter -> N=2
    // Rising Edge   <------------------------------
    TIM2->CCER    &= (uint16_t)~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
    // enable capture + Rising Edge(0)
    TIM2->CCER    |= (uint16_t)TIM_CCER_CC1E;
    // 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
    // input filter + input select
    TIM2->CCMR1   &= ~(TIM_CCMR1_IC2F | TIM_CCMR1_CC2S);
    TIM2->CCMR1   |= (TIM_CCMR1_CC2S_0 + TIM_CCMR1_IC2F_0); // filter -> N=2
    // Falling Edge  <------------------------------
    TIM2->CCER    &= (uint16_t)~(TIM_CCER_CC2P | TIM_CCER_CC2NP);
    // enable capture + Falling Edge(1)
    TIM2->CCER    |= (uint16_t)TIM_CCER_CC2E | TIM_CCER_CC2P;
    // 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);
    // Timer2 Overflow
    TIM2->DIER = 0;                 // Disable all interrupt
    sw_ovrflw_tim2 = 0;
    TIM2->CCR1 = 0;
    TIM2->CCR2 = 0;
    uint32_t dummy = TIM2->CCR1;
    dummy = TIM2->CCR1;
    dummy = TIM2->CCR2;
    dummy = TIM2->CCR2;
    TIM2->CNT = 0;
    TIM2->SR = 0;                   // clear all IC/OC flag
    // set clock source then enable
    TIM2->SMCR     = 0;                         // Internal clock
    TIM2->CR1     |= (uint16_t)TIM_CR1_CEN;     // Enable the TIM Counter
    // interrupt
    NVIC_SetVector(TIM2_IRQn, (uint32_t)irq_ic_TIM2);
    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 | TIM_SR_CC2IF);     // clear IC flag
    TIM2->DIER |= TIM_DIER_CC1IE | TIM_DIER_CC2IE;  // Enable IC1+IC2
    __enable_irq();
}

void FRQ_CUNTR::stop_action(void)
{
    __disable_irq();
    TIM2->SR &= ~(TIM_SR_CC1IF | TIM_SR_CC2IF);     // clear IC flag
    __enable_irq();
}

void FRQ_CUNTR::recipro_start_measurement()
{
    SystemCoreClockUpdate();
    _base_clock = (float)read_base_clock_frequency();
    rise_cnt = 0;
    fall_cnt = 0;
    _data_ready = false;
    rise_buf[0] = 0;
    rise_buf[1] = 0;
    fall_buf[0] = 0;
    fall_buf[1] = 0;
    start_action();
    _t.reset();
    _t.start();
}

void FRQ_CUNTR::recipro_stop_measurement()
{
    _t.reset();
    _t.stop();
    stop_action();
}

bool FRQ_CUNTR::recipro_check_status(Recipro_status_TypeDef *status)
{
    freq_one _temp;

    bool _ready = false;
    _data_buf[0] = rise_buf[0];
    _data_buf[1] = rise_buf[1];
    _data_buf[2] = fall_buf[0];
    _data_buf[3] = fall_buf[1];
    uint8_t sum = 0;
    if (_data_buf[0] == 0) {   ++sum;}
    if (_data_buf[1] == 0) {   ++sum;}
    if (_data_buf[2] == 0) {   ++sum;}
    if (_data_buf[3] == 0) {   ++sum;}
    if (sum != 4) {
        _ready = true;
        // change order
        if (_data_buf[0] > _data_buf[1]){
            _temp.f_64bit_dt = _data_buf[0];
            _data_buf[0] = _data_buf[1];
            _data_buf[1] = _temp.f_64bit_dt;
        }
        _tp0 = _data_buf[1] - _data_buf[0];
        if (_tp0 < 0) {
            _tp0 = 0;
        }
        if (_data_buf[2] > _data_buf[3]){
            _temp.f_64bit_dt = _data_buf[2];
            _data_buf[2] = _data_buf[3];
            _data_buf[3] = _temp.f_64bit_dt;
        }
        _tp1 = _data_buf[3] - _data_buf[2];
        if (_tp1 < 0) {
            _tp1 = 0;
        }
        // calculate diff
        if (_data_buf[3] > _data_buf[1]) {
            //type A
            _tp2 = _data_buf[2] - _data_buf[0];
            _tp3 = _data_buf[1] - _data_buf[2];
        } else {
            //type B
            _tp2 = _data_buf[3] - _data_buf[0];
            _tp3 = _data_buf[0] - _data_buf[2];
        }
        if (_tp2 < 0) {
            _tp2 = 0;
        }
        if (_tp3 < 0) {
            _tp3 = 0;
        }
    }
    PRINTF("rise_buf[0] = %.0f\r\n", (float)rise_buf[0]);
    PRINTF("rise_buf[1] = %.0f\r\n", (float)rise_buf[1]);
    PRINTF("fall_buf[0] = %.0f\r\n", (float)fall_buf[0]);
    PRINTF("fall_buf[1] = %.0f\r\n", (float)fall_buf[1]);
    PRINTF("data_buf0[0]= %.0f\r\n", (float)_data_buf[0]);
    PRINTF("data_buf0[1]= %.0f\r\n", (float)_data_buf[1]);
    PRINTF("data_buf1[0]= %.0f\r\n", (float)_data_buf[2]);
    PRINTF("data_buf1[1]= %.0f\r\n", (float)_data_buf[3]);
    PRINTF("tp0= %.0f\r\n", (float)_tp0);
    PRINTF("tp1= %.0f\r\n", (float)_tp1);
    PRINTF("tp2= %.0f\r\n", (float)_tp2);
    PRINTF("tp3= %.0f\r\n", (float)_tp3);
    if (_ready == true) {
        _freq_rise2rise = _base_clock / (float)_tp0;
        _freq_fall2fall = _base_clock / (float)_tp1;
        _time_us_rise2fall = (float)_tp2 / _base_clock * 1.0e3;
        _time_us_fall2rise = (float)_tp3 / _base_clock * 1.0e3;
        _data_ready = true;
        PRINTF("freq_rise2rise= %f [Hz]\r\n", _freq_rise2rise);
        PRINTF("freq_fall2fall= %f [Hz]\r\n", _freq_fall2fall);
        PRINTF("time_us_rise2fall= %f [mS]\r\n", _time_us_rise2fall);
        PRINTF("time_us_fall2rise= %f [mS]\r\n", _time_us_fall2rise);
    }
    status->rise_cnt = rise_cnt;
    status->fall_cnt = fall_cnt;
    status->input_level = _input_pin.read();
    status->passed_time = (float)_t.read_us() / 1.0e6;
    return _data_ready;
}

void FRQ_CUNTR::recipro_get_result(Recipro_result_TypeDef *fq)
{
    fq->freq_rise2rise    = _freq_rise2rise;
    fq->freq_fall2fall    = _freq_fall2fall;
    fq->time_us_rise2fall = _time_us_rise2fall;
    fq->time_us_fall2rise = _time_us_fall2rise;
}

void FRQ_CUNTR::recipro_get_raw_data(int64_t *buf)
{
    int64_t *pointer = buf;
    *pointer++ = rise_buf[0];
    *pointer++ = fall_buf[0];
    *pointer++ = rise_buf[1];
    *pointer++ = fall_buf[1];
    pointer = buf;
    for (uint32_t i = 0; i < 4; i++) {
        float dt = (float)*pointer++;
        PRINTF("%2d = %12.0f \r\n", i, dt);
    }
}

//------------------------------------------------------------------------------
//  Interrupt Handlers
//------------------------------------------------------------------------------
// Reciprocal data (TIM2 IC1+IC2)
void irq_ic_TIM2(void)
{
    // IC1 (for reciprocal measurement / Rising edge)
    uint32_t reg = TIM2->SR;
    if (reg & TIM_SR_CC1IF) {
        SET0;
        TIM2->SR &= ~TIM_SR_CC1IF;  // clear IC flag
        captured_dt.freq_dt = TIM2->CCR1;
        captured_dt.f_sw_dt = sw_ovrflw_tim2;
        rise_buf[rise_pointer % 2] = captured_dt.f_64bit_dt;
        ++rise_pointer;
        ++rise_cnt;
        CLR0;
    }
    // IC2 (for reciprocal measurement / Falling edge)
    if (reg & TIM_SR_CC2IF) {
        SET1;
        TIM2->SR &= ~TIM_SR_CC2IF;  // clear IC flag
        captured_dt.freq_dt = TIM2->CCR2;
        captured_dt.f_sw_dt = sw_ovrflw_tim2;
        fall_buf[fall_pointer % 2] = captured_dt.f_64bit_dt;
        ++fall_pointer;
        ++fall_cnt;
        CLR1;
    }
    // TIM2 overflow
    if (reg & TIM_SR_UIF) {            // 32bit counter overflow
        SET2;
        TIM2->SR &= ~TIM_SR_UIF;  // clear UIF(overflow) flag
        ++sw_ovrflw_tim2;
        CLR2
    }
}

//------------------------------------------------------------------------------
//  Frequency check for test & debug purpose
//------------------------------------------------------------------------------
// Read TIM2 Clock frequency
uint32_t FRQ_CUNTR::read_base_clock_frequency(void)
{
    TIM2->CNT = 0;
    wait_us(1000000);           // Gate time = 1seconds
    uint32_t freq = TIM2->CNT;  // read counter
    PRINTF("Clock Frequency= %10d, gate= %4.2f [Sec]\r\n", freq);
    return freq;                // return counter data
}

//
uint32_t FRQ_CUNTR::read_tm2_overflow(void)
{
    return sw_ovrflw_tim2;
}

}   // Frequency_counter