1.1.3.2 Using The Library

  • The quantities available in the Metrology Module make observable 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.

  • Measurement data from one measurement interval is short-lived and does not persist longer than one measurement interval. At the end of a measurement interval, the user will be informed by software interrupt at which point 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 50Hz or 60 cycles of 60Hz), 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 250ms after startup before testing when testing to revenue-grade accuracy.

  • The Smart Meter DSP Module is specifically designed to accept samples from an ATSENSE Δ/Σ ADC using an OSR=64, at an input sample rate of 16.000 KHz. This input data stream is further filtered to an internal sample rate of 4.000 KHz for generation of metrology quantities. All computed metrology quantities are available at a rate of approximately 1Hz, when using recommended settings.

  • 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.

  • Values further 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) ||
        (app_metrologyData.state == APP_METROLOGY_STATE_CHECK_CALIBRATION))
    {
        /* Signal Metrology thread to update measurements for an integration period */
        OSAL_SEM_Post(&appMetrologySemID);
    }
}

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(uint8_t harmonicNum)
{
    if (app_metrologyData.pHarmonicAnalysisCallback)
    {
        app_metrologyData.harmonicAnalysisPending = false;
        app_metrologyData.pHarmonicAnalysisCallback(harmonicNum);
    }
}

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_GetControl();
    app_metrologyData.pMetStatus = DRV_METROLOGY_GetStatus();
    app_metrologyData.pMetAccData = DRV_METROLOGY_GetAccData();
    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);
    
    /* Clear Harmonic Analysis Data */
    app_metrologyData.harmonicAnalysisPending = false;
    app_metrologyData.pHarmonicAnalisysCallback = NULL;
    app_metrologyData.pHarmonicAnalysisResponse = NULL;
    
    /* 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_ENERGY_QUEUE_DATA newMetrologyData;
    APP_EVENTS_QUEUE_DATA newEvent;

    /* 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 measurements at the end of the integration period. */
            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)
            {
                /* Calibration process runnning. New measurements should not be reported. */
                break;
            }            

            // Send new Energy values to the Energy Task
            app_metrologyData.queueFree = uxQueueSpacesAvailable(appEnergyQueueID);
            if (app_metrologyData.queueFree)
            {
                newMetrologyData.energy = DRV_METROLOGY_GetEnergyValue(true);
                newMetrologyData.Pt = DRV_METROLOGY_GetRMSValue(RMS_PT);
                xQueueSend(appEnergyQueueID, &newMetrologyData, (TickType_t) 0);
            }
            else
            {
                SYS_CMD_MESSAGE("ENERGY Queue is FULL!!!\r\n");
            }
            
            // Send new Events to the Events Task
            app_metrologyData.queueFree = uxQueueSpacesAvailable(appEventsQueueID);
            if (app_metrologyData.queueFree)
            {
                RTC_TimeGet(&newEvent.eventTime);
                DRV_METROLOGY_GetEventsData(&newEvent.eventFlags);
                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;
        }
    }
}