Jump to main content

# 1.1.2 Using the Library

- The quantities available in the Metrology Module provide all basic components required to completely define or derive all power system measurement quantities for most currently used definitions of power. All per-phase voltages, currents, and power for fundamental and fundamental + harmonics are available, allowing computation of all modern system or poly-phase power quantities.
- There are 2 different measurement quantities provided by the driver, Integration Period measurements and Per-Cycle measurements:
  - During the integration period, measurement values are accumulated and updated at the end of each integration period. Functions as DRV_METROLOGY_GetEnergyValue and DRV_METROLOGY_GetMeasureValue (with perCycle set to false) return values that are integrated over the entire period. These values are typically used for energy calculations and other accumulated metrics. The integration period duration is configurable, with a default value of 1 second.
  - Per-cycle measurements are updated at the end of each Mains cycle. When DRV_METROLOGY_GetMeasureValue is called with perCycle set to true, it retrieves values from the per-cycle accumulator registers. These registers (e.g., DRV_METROLOGY_REGS_PERCYCLE_ACC) store measurement data for each individual cycle, allowing for higher temporal resolution and more detailed analysis of the waveform.
- Measurement data from one measurement interval is short-lived and does not persist longer than next measurement interval. At the end of a measurement interval, the user is notified by a software interrupt, in which the user must read all pertinent quantities before the next measurement period ends.
- All computed quantities available through the Metrology Module are integrated over a time interval equivalent to an integral number of periods of the fundamental frequency. All integration periods are approximated by the final output sample rate of the DSP filters, 4000 Hz; so, measurement accuracy will increase using longer integration periods. One second is the recommended minimum integration period (50 cycles of 50 Hz or 60 cycles of 60 Hz), but fewer numbers of samples may still yield acceptable results. DSP filters require a settling time before accurate measurements may be used for revenue-quality metering. It is recommended to wait at least 250 ms after start-up before testing to revenue-grade accuracy.
- The Smart Meter DSP Module is specifically designed to accept samples from an ATSENSE Δ/Σ ADC using an OSR = 64, or from an MCP Δ/Σ ADC using an OSR = 128, resulting in a sample rate of 16 kHz. This input data stream is filtered to an internal sample rate of 4 kHz for generation of metrology quantities.
- DSP channel nomenclature defines 3 basic voltage channels: V_A, V_B and V_C, and 4 basic current channels: I_A, I_B, I_C, and measured neutral current, I_ Nm. The current channels are combined to create an accumulated imputed neutral current, I_Ni, and an accumulated sample-by-sample difference between the measured and imputed neutral currents, I_Nmi.
  - This is the 7-channel configuration provided by the ATSENSE AFE
  - When using MCP AFEs, an 8th channel may be available. This channel is named after V_D, this name comes from an special configuration available in this channel to be able to monitor VDC (instead of VAC) on it, but the channel can be used for any other Voltage measurement.
- Values labeled as “_F” indicate values associated with the fundamental frequency component only, while values without the “_F” indicate values associated with fundamental + harmonics

Example application to handle metrology driver based on FreeRTOS application

    void SYS_Initialize ( void* data )
    {
        (...)

        /* Initialize Metrology Driver Instance */
        DRV_METROLOGY_Initialize((SYS_MODULE_INIT *)&drvMetrologyInitData, RSTC_ResetCauseGet());

        (...)
    }

    static void _APP_METROLOGY_IntegrationCallback(void)
    {
        if (app_metrologyData.state == APP_METROLOGY_STATE_RUNNING)
        {
            newMetrologyData.energy = DRV_METROLOGY_GetEnergyValue(true);
            newMetrologyData.Pt = DRV_METROLOGY_GetMeasureValue(MEASURE_PT, false);
            xQueueSend(appEnergyQueueID, &newMetrologyData, (TickType_t) 0);
        }
    }

    static void _APP_METROLOGY_CalibrationCallback(bool result)
    {
        if (app_metrologyData.pCalibrationCallback)
        {
            app_metrologyData.pCalibrationCallback(result);
        }

        /* Signal Metrology to exit calibration status */
        OSAL_SEM_Post(&appMetrologyCalibrationSemID);
    }

    static void _APP_METROLOGY_HarmonicAnalysisCallback(uint32_t harmonicBitmap)
    {
        if (app_metrologyData.pHarmonicAnalysisCallback)
        {
            if (app_metrologyData.sendHarmonicsToConsole)
            {
                app_metrologyData.sendHarmonicsToConsole = false;
                app_metrologyData.pHarmonicAnalysisCallback(harmonicBitmap);
            }
        }
    }

    static void _APP_METROLOGY_HalfCycleCallback(void)
    {
        if (app_metrologyData.state == APP_METROLOGY_STATE_RUNNING)
        {
            /* Signal Metrology thread to update events for a Half/Full Cycle */
            app_metrologyData.halfCycleFlag = true;
            OSAL_SEM_PostISR(&appMetrologySemID);
        }
    }

    void APP_METROLOGY_Initialize (void)
    {
        /* Detection of the WDOG0 Reset */
        if (RSTC_ResetCauseGet() == RSTC_SR_RSTTYP(RSTC_SR_RSTTYP_WDT0_RST_Val))
        {
            app_metrologyData.startMode = DRV_METROLOGY_START_SOFT;
        }
        else
        {
            app_metrologyData.startMode = DRV_METROLOGY_START_HARD;
        }

        /* Get Pointers to metrology data regions */
        app_metrologyData.pMetControl = DRV_METROLOGY_GetControlData();
        app_metrologyData.pMetStatus = DRV_METROLOGY_GetStatusData();
        app_metrologyData.pMetAccData = DRV_METROLOGY_GetAccData();
        app_metrologyData.pMetPerCycleAccData = DRV_METROLOGY_GetPerCycleAccData();
        app_metrologyData.pMetHarData = DRV_METROLOGY_GetHarData();

        /* Set Callback for each metrology integration process */
        DRV_METROLOGY_IntegrationCallbackRegister(_APP_METROLOGY_IntegrationCallback);
        /* Set Callback for calibration process */
        DRV_METROLOGY_CalibrationCallbackRegister(_APP_METROLOGY_CalibrationCallback);
        /* Set Callback for harmonic analysis process */
        DRV_METROLOGY_HarmonicAnalysisCallbackRegister(_APP_METROLOGY_HarmonicAnalysisCallback);
        /* Set Callback for half cycle */
        DRV_METROLOGY_HalfCycleCallbackRegister(_APP_METROLOGY_HalfCycleCallback);

        /* Clear Harmonic Analysis Data */
        app_metrologyData.harmonicAnalysisPending = false;
        app_metrologyData.pHarmonicAnalysisCallback = NULL;
        app_metrologyData.pHarmonicAnalysisResponse = NULL;

        /* Clear Calibration Data */
        app_metrologyData.pCalibrationCallback = NULL;

        /* Initialize integration Flags */
        app_metrologyData.halfCycleFlag = false;
        app_metrologyData.eventFlagsPrev.afeEventsMask = 0;

        /* Create the Metrology Integration Semaphore. */
        if (OSAL_SEM_Create(&appMetrologySemID, OSAL_SEM_TYPE_BINARY, 0, 0) == OSAL_RESULT_FALSE)
        {
            /* Handle error condition. Not sufficient memory to create semaphore */
        }

        /* Create the Metrology Calibration Semaphore. */
        if (OSAL_SEM_Create(&appMetrologyCalibrationSemID, OSAL_SEM_TYPE_BINARY, 0, 0) == OSAL_RESULT_FALSE)
        {
            /* Handle error condition. Not sufficient memory to create semaphore */
        }

        app_metrologyData.state = APP_METROLOGY_STATE_INIT;
    }

    void APP_METROLOGY_Tasks (void)
    {
        APP_EVENTS_QUEUE_DATA newEvent;
        DRV_METROLOGY_AFE_EVENTS_UNION newEventFlags;

        /* Check the application's current state. */
        switch (app_metrologyData.state)
        {
            /* Application's initial state. */
            case APP_METROLOGY_STATE_INIT:
            {
                DRV_METROLOGY_CONTROL * pConfiguration = NULL;

                /* Check if any specific configuration should be applied */
                if (app_metrologyData.setConfiguration)
                {
                    pConfiguration = &app_metrologyData.configuration;
                }

                if (DRV_METROLOGY_Open(app_metrologyData.startMode, pConfiguration) == DRV_METROLOGY_SUCCESS)
                {
                    if (app_metrologyData.startMode == DRV_METROLOGY_START_HARD)
                    {
                        app_metrologyData.state = APP_METROLOGY_STATE_START;
                    }
                    else
                    {
                        app_metrologyData.state = APP_METROLOGY_STATE_RUNNING;
                    }
                }
                else
                {
                    app_metrologyData.state = APP_METROLOGY_STATE_ERROR;
                }

                vTaskDelay(10 / portTICK_PERIOD_MS);
                break;
            }

            case APP_METROLOGY_STATE_START:
            {
                if (DRV_METROLOGY_GetStatus() == DRV_METROLOGY_STATUS_READY)
                {
                    if (DRV_METROLOGY_Start() == DRV_METROLOGY_SUCCESS)
                    {
                        app_metrologyData.state = APP_METROLOGY_STATE_RUNNING;
                    }
                    else
                    {
                        app_metrologyData.state = APP_METROLOGY_STATE_ERROR;
                    }

                    vTaskDelay(10 / portTICK_PERIOD_MS);
                }

                break;
            }

            case APP_METROLOGY_STATE_RUNNING:
            {
                /* Wait for the metrology semaphore to get events in Half Cycle interrupt. */
                OSAL_SEM_Pend(&appMetrologySemID, OSAL_WAIT_FOREVER);

                if (app_metrologyData.state == APP_METROLOGY_STATE_INIT)
                {
                    /* Received Reload Command */
                    break;
                }
                else if (app_metrologyData.state == APP_METROLOGY_STATE_CHECK_CALIBRATION)
                {
                    /* Received Start Calibration Command */
                    break;
                }

                if (app_metrologyData.halfCycleFlag)
                {
                    app_metrologyData.halfCycleFlag = false;
                    // Send new Events to the Events Task
                    app_metrologyData.queueFree = uxQueueSpacesAvailable(appEventsQueueID);
                    if (app_metrologyData.queueFree)
                    {
                        RTC_TimeGet(&newEvent.eventTime);
                        DRV_METROLOGY_GetEventsData(&newEventFlags);
                        if (newEventFlags.afeEventsMask != app_metrologyData.eventFlagsPrev.afeEventsMask)
                        {
                            app_metrologyData.eventFlagsPrev.afeEventsMask = newEventFlags.afeEventsMask;
                            newEvent.eventFlags = newEventFlags.afeEvents;
                            xQueueSend(appEventsQueueID, &newEvent, (TickType_t) 0);
                        }
                    }
                    else
                    {
                        SYS_CMD_MESSAGE("EVENTS Queue is FULL!!!\r\n");
                    }
                }

                break;
            }

            case APP_METROLOGY_STATE_CHECK_CALIBRATION:
            {
                /* Wait for the metrology semaphore to wait calibration ends. */
                OSAL_SEM_Pend(&appMetrologyCalibrationSemID, OSAL_WAIT_FOREVER);

                app_metrologyData.state = APP_METROLOGY_STATE_RUNNING;

                vTaskDelay(10 / portTICK_PERIOD_MS);
                break;
            }

            /* The default state should never be executed. */
            case APP_METROLOGY_STATE_ERROR:
            default:
            {
                /* TODO: Handle error in application's state machine. */
                break;
            }
        }
    }

Rev: E