Voltage Measurement DAQ HAT for Raspberry Pi®

The MCC 118 DAQ HAT provides eight channels of analog voltage measurement for Raspberry Pi based data acquisition / data logger systems. The MCC 118 maximum data rate is 100 kS/s per board for taking single point or waveform voltage measurements. Up to eight MCC 118 devices can be stacked - providing up to 64 channels of data and a maximum throughput of 320 kS/s.

https://www.mccdaq.com/DAQ-HAT/MCC-118.aspx

This manual and the finished data acquisition system were developed together with the electronics laboratory of the Fritz-Haber-Institute and are used there.

http://www.fhi-berlin.mpg.de/elab/pub/index_e.html

In particular, this project was accompanied by Abdulrhman Moshantaf as a working student.

moshantaf@fhi-berlin.mpg.de

First download the Daqhat library from the Github page and build it.

Built the IOC

  • Built an example IOC
  • Database iocmcc118App/Db
    • Makefile
    • mcc118.db
      • record(ai, "$(PVPREFIX):HA$(HA):CH$(CH)"){
        field(DTYP, "devMcc118")
        field(INP, "#C$(HA) S$(CH) @whatever")
        # field(SCAN, "$(SCAN)") # do not scan, let the converting calc record scan and PP the connected INP
        field(PREC, "2")
        }
    • channelmapping.db
      • record("calcout", "$(P):$(R):GUN"){
        field("DESC", "connected to CH1")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA0:CH1 PP")
        field("CALC", "A*49.5 - 61.3")
        field("EGU", "dBm")
        field("LOPR", "-30")
        field("HOPR", "10")
        }

        record("calcout", "$(P):$(R):BUNCHER"){
        field("DESC", "connected to CH2")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA0:CH2 PP")
        field("CALC", "A*52.2 - 65.5")
        field("EGU", "dBm")
        field("LOPR", "-30")
        field("HOPR", "10")
        }

        record("calcout", "$(P):$(R):K1"){
        field("DESC", "connected to CH3")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA0:CH3 PP")
        field("CALC", "A*52.7 - 52.1")
        field("EGU", "dBm")
        field("LOPR", "-30")
        field("HOPR", "10")
        }

        record("calcout", "$(P):$(R):K2"){
        field("DESC", "connected to CH4")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA0:CH4 PP")
        field("CALC", "A*55.5 - 61.6")
        field("EGU", "dBm")
        field("LOPR", "-30")
        field("HOPR", "10")
        }

        record("calcout", "$(P):$(R):KICKER"){
        field("DESC", "connected to CH0")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA0:CH0 PP")
        field("CALC", "A*49.5 - 71.5")
        field("EGU", "dBm")
        field("LOPR", "-30")
        field("HOPR", "10")
        }

        record("calcout", "$(P):$(R):PSU_TEMP"){
        field("DESC", "connected to CH5")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA0:CH5 PP")
        field("CALC", "A*100")
        field("EGU", "degC")
        field("LOPR", "0")
        field("HOPR", "100")
        }

        record("calcout", "$(P):$(R):VOLT:5VP"){
        field("DESC", "connected to HAT1 CH0")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA1:CH0 PP")
        field("CALC", "A")
        field("EGU", "V")
        field("LOPR", "0")
        field("HOPR", "20")
        }

        record("calcout", "$(P):$(R):VOLT:5VN"){
        field("DESC", "connected to HAT1 CH1")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA1:CH1 PP")
        field("CALC", "A")
        field("EGU", "V")
        field("LOPR", "-20")
        field("HOPR", "0")
        }

        record("calcout", "$(P):$(R):VOLT:15VP"){
        field("DESC", "connected to HAT1 CH2")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA1:CH2 PP")
        field("CALC", "A*2")
        field("EGU", "V")
        field("LOPR", "0")
        field("HOPR", "20")
        }

        record("calcout", "$(P):$(R):VOLT:15VN"){
        field("DESC", "connected to HAT1 CH3")
        field("SCAN", ".1 second")
        field("INPA", "$(P):$(R):HA1:CH3 PP")
        field("CALC", "A*2")
        field("EGU", "V")
        field("LOPR", "-20")
        field("HOPR", "0")
        }
  • source
    • The source files from the Daqhat library are inserted here.
    • Build DEV-files
      • devMcc118.c
      • #include <stdio.h>
        #include <stdlib.h>
        #include <math.h>
        #include <dbScan.h>
        #include <dbAccess.h>
        #include <devSup.h>
        #include <recGbl.h>
        #include <alarm.h>
        #include "daqhats_utils.h"

        #include <aiRecord.h>

        #include <epicsExport.h>

        struct mcc118State {
        unsigned int address;
        unsigned int channel;
        uint8_t hat_address;
        IOSCANPVT scan;

        uint32_t user_buffer_size;
        double read_buf[8000]; // 1000 * 8 channels
        };

        static long init_record(aiRecord *prec)
        {
        //printf("begin mcc118 init\n");

        struct mcc118State* priv;
        // char configString = {0};

        priv=malloc(sizeof(struct mcc118State));
        if(!priv){
        recGblRecordError(S_db_noMemory, (void*)prec,
        "devAiMcc118 failed to allocate private struct");
        return S_db_noMemory;
        }

        if (VME_IO != prec->inp.type){
        return(S_dev_badBus);
        }
        priv->address = prec->inp.value.vmeio.card;
        priv->channel = prec->inp.value.vmeio.signal;
        priv->user_buffer_size = 1000 * 8; // always 8 channels

        printf("priv address: %d\n", priv->address);
        printf("priv channel: %d\n", priv->channel);

        priv->hat_address = priv->address; //just copy the address from the record
        printf("priv hat_address: %d\n", priv->hat_address);

        int result;
        uint8_t channel_mask = {CHAN0 | CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7}; //since there can only be one connection to the HAT, always read all channels
        uint32_t options = OPTS_CONTINUOUS;
        uint32_t samples_per_channel = 0; //continous scan ignores this it just needs to be set
        double scan_rate = 1000.0;

        // Open a connection to the device, only if its not already open
        if(mcc118_is_open(priv->hat_address) == 0){
        result = mcc118_open(priv->hat_address);
        STOP_ON_ERROR(result);
        result = mcc118_a_in_scan_start(priv->hat_address, channel_mask, samples_per_channel,
        scan_rate, options);
        STOP_ON_ERROR(result);
        };

        prec->dpvt=priv;
        return 0;

        //handler for STOP_ON_ERROR
        stop:
        print_error(mcc118_a_in_scan_stop(priv->hat_address));
        print_error(mcc118_a_in_scan_cleanup(priv->hat_address));
        print_error(mcc118_close(priv->hat_address));

        return 0;
        }

        static long read_ai(aiRecord *prec)
        {
        //printf("read ai start!\n");
        struct mcc118State* priv=prec->dpvt;
        if(!priv) {
        (void)recGblSetSevr(prec, COMM_ALARM, INVALID_ALARM);
        return 0;
        }
        //printf("read ai!\n");

        double value = 0.0;
        int result;
        uint16_t read_status = 0;
        uint32_t samples_read_per_channel = 0;
        int32_t read_request_size = READ_ALL_AVAILABLE;
        double timeout = 5.0;


        //actually read data from the device, all available points
        usleep(50000); //sleep a bit so more samples can accumulate // TODO BAD HACK IDK?
        result = mcc118_a_in_scan_read(priv->hat_address, &read_status, read_request_size,
        timeout, priv->read_buf, priv->user_buffer_size, &samples_read_per_channel);
        //printf("issued read command for HA%d CH%d\n", priv->hat_address, priv->channel);
        STOP_ON_ERROR(result);

        if (read_status & STATUS_HW_OVERRUN){
        printf("Hardware overrun\n");
        }else if (read_status & STATUS_BUFFER_OVERRUN){
        printf("Buffer overrun\n");
        }

        //printf("samples_read_per_channel: %d\n", samples_read_per_channel);
        if (samples_read_per_channel > 0)
        {
        //printf("read values. HA%d CH%d: ", priv->hat_address, priv->channel);
        int index = samples_read_per_channel * 8 - 8; // 8 channels
        value = priv->read_buf[index + priv->channel]; //only get the data for the records' channel
        //printf("%f\n", value);
        prec->val = floorf(value * 100) / 100;
        }

        return 2; //correct return value is 2. zero does not process the record

        //handler for STOP_ON_ERROR
        stop:
        print_error(mcc118_a_in_scan_stop(priv->hat_address));
        print_error(mcc118_a_in_scan_cleanup(priv->hat_address));
        print_error(mcc118_close(priv->hat_address));

        return 0;
        }

        static long get_ioint_info(int dir,dbCommon*prec,IOSCANPVT*io)
        {
        //printf("get_ioint_info starts\n");
        struct mcc118State* priv=prec->dpvt;
        //printf("get_ioint_info after dpvt assignment\n");
        if(priv) {
        *io = priv->scan;
        }
        //printf("get_ioint_info after putting back IOSCANPVT\n");
        return 0;
        }

        struct {
        long num;
        DEVSUPFUN report;
        DEVSUPFUN init;
        DEVSUPFUN init_record;
        DEVSUPFUN get_ioint_info;
        DEVSUPFUN read_ai;
        DEVSUPFUN special_linconv;
        } devMcc118 = {
        6, /* space for 6 functions */
        NULL,
        NULL,
        init_record,
        get_ioint_info,
        read_ai,
        NULL
        };
        epicsExportAddress(dset,devMcc118);
      • mcc118dev.dbd
      • device(ai, VME_IO, devMcc118, "devMcc118")
    • Makefile
      • Add the following lines:
      • iocmcc118_DBD += mcc118dev.dbd
        iocmcc118_SRCS += devMcc118.c
        USR_SYS_LIBS += daqhats
  • st.cmd
    • ## Load record instances
      #dbLoadRecords("db/xxx.db","user=pi")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=0, CH=0")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=0, CH=1")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=0, CH=2")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=0, CH=3")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=0, CH=4")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=0, CH=5")
      #dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=0, CH=6") # nicht belegt
      #dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=0, CH=7") # nicht belegt

      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=1, CH=0")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=1, CH=1")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=1, CH=2")
      dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=1, CH=3")
      #dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=1, CH=4") # nicht belegt
      #dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=1, CH=5") # nicht belegt
      #dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=1, CH=6") # nicht belegt
      #dbLoadRecords("db/mcc118.db","PVPREFIX=${PVPREFIX}, HA=1, CH=7") # nicht belegt

      dbLoadRecords("db/channelmapping.db", "P=FHIFEL, R=LLRFSRC")


Voltage Output and DIO DAQ HAT for Raspberry Pi®

The MCC 152 Voltage Output and DIO HAT provides two channels of analog voltage output and eight digital I/O for Raspberry Pi based systems. The 12-bit voltage outputs feature a 0-5 V range and can be updated at up to 5 kS/s. Up to eight MCC DAQ HAT devices can be stacked onto one Raspberry Pi.

https://www.mccdaq.com/DAQ-HAT/MCC-152.aspx

Built the IOC

  • Built an example IOC
  • Database iocmccApp
    • Makefile
    • mcc152.db
      • record(ao,"MCC152:HA$(HA):$(CH)") {
        field(DESC,"set analog output")
        field(DTYP,"devMcc152")
        field(OUT, "#C$(HA) S$(CH) @whatever)
        field(OMSL, "supervisory")
        field(SCAN,"$(SCAN)")
        field(OROC,"0")
        field(PREC,"2")
        field(EGU,"VOLT")
        field(DRVH,"5")
        field(DRVL,"0")
        field(HOPR,"5")
        field(LOPR,"0") }
  • source
    • The source files from the Daqhat library are inserted here.
    • Build DEV-files
      • devMcc.c (include MCC118/152)
      • #include <stdio.h>
        #include <stdlib.h>
        #include <dbAccess.h>
        #include <devSup.h>
        #include <recGbl.h>
        #include <alarm.h>

        #include "daqhats_utils.h"

        #include <aoRecord.h>
        #include <aiRecord.h>

        #include <epicsExport.h>

        struct mcc118State {
        unsigned int address;
        unsigned int channel;
        uint8_t hat_address;
        };
        struct mcc152State {
        unsigned int address;
        unsigned int channel;
        uint8_t hat_address;
        };

        static long init_record_input(aiRecord *prec)
        {
        struct mcc118State* priv;
        // char configString = {0};

        priv=malloc(sizeof(struct mcc118State));
        if(!priv){
        recGblRecordError(S_db_noMemory, (void*)prec,
        "devAiMcc118 failed to allocate private struct");
        return S_db_noMemory;
        }

        if (VME_IO != prec->inp.type){
        return(S_dev_badBus);
        }
        priv->address = prec->inp.value.vmeio.card;
        priv->channel = prec->inp.value.vmeio.signal;

        // Determine the address of the device to be used
        //if (select_hat_device(priv->address, &(priv->hat_address)) != 0)
        //{
        //return -1;
        //}

        priv->hat_address= priv->address;
        // printf("%d", priv->hat_address);

        prec->dpvt=priv;
        return 0;
        }

        static long init_record_output(aoRecord *prec)
        {
        struct mcc152State* priv;
        // char configString = {0};

        priv=malloc(sizeof(struct mcc152State));
        if(!priv){
        recGblRecordError(S_db_noMemory, (void*)prec,
        "devAiMcc152 failed to allocate private struct");
        return S_db_noMemory;
        }

        if (VME_IO != prec->out.type){
        return(S_dev_badBus);
        }
        priv->address = prec->out.value.vmeio.card;
        priv->channel = prec->out.value.vmeio.signal;

        //printf("%d", priv->address);
        // Determine the address of the device to be used
        //if (select_hat_device(priv->address, &(priv->hat_address)) != 0)
        //{
        //return -1;
        //}
        priv->hat_address = priv->address;

        // printf("%d", priv->hat_address);

        prec->dpvt=priv;
        return 0;
        }


        static long read_ai(aiRecord *prec)
        {
        struct mcc118State* priv=prec->dpvt;
        if(!priv) {
        (void)recGblSetSevr(prec, COMM_ALARM, INVALID_ALARM);
        return 0;
        }

        // mcc118_a_in_read() variables
        uint32_t options = OPTS_DEFAULT;
        double value;

        int result;
        result = mcc118_open(priv->hat_address);
        result = mcc118_a_in_read(priv->hat_address, priv->channel, options, &value);
        //printf("val: %f\n", value);
        value = floorf(value * 100) / 100;
        prec->val=value;

        result = mcc118_close(priv->hat_address);
        return 2;
        }

        static long write_ao(aoRecord *prec)
        {
        struct mcc152State* priv=prec->dpvt;
        if(!priv) {
        (void)recGblSetSevr(prec, COMM_ALARM, INVALID_ALARM);
        return 0;
        }

        // mcc152_a_out_write() variables
        uint32_t options = OPTS_DEFAULT;
        double value = prec->oval ;
        //value = floorf(value * 100) / 100;
        int result;
        result = mcc152_open(priv->hat_address);
        result = mcc152_a_out_write(priv->hat_address, priv->channel, options, value);
        //printf("val: %f\n", value);


        result = mcc152_close(priv->hat_address);
        return 2;
        }



        struct {
        long num;
        DEVSUPFUN report;
        DEVSUPFUN init;
        DEVSUPFUN init_record_output;
        DEVSUPFUN get_ioint_info;
        DEVSUPFUN write_ao;
        DEVSUPFUN special_linconv;
        } devMcc152 = {
        6, /* space for 6 functions */
        NULL,
        NULL,
        init_record_output,
        NULL,
        write_ao,
        NULL
        };
        epicsExportAddress(dset,devMcc152);



        struct {
        long num;
        DEVSUPFUN report;
        DEVSUPFUN init;
        DEVSUPFUN init_record_input;
        DEVSUPFUN get_ioint_info;
        DEVSUPFUN read_ai;
        DEVSUPFUN special_linconv;
        } devMcc118 = {
        6, /* space for 6 functions */
        NULL,
        NULL,
        init_record_input,
        NULL,
        read_ai,
        NULL
        };
        epicsExportAddress(dset,devMcc118);
      • mcc152dev.dbd
      • device(ao, VME_IO, devMcc152, "devMcc152")
    • Makefile
      • Add the following lines:
      • iocmcc152_DBD += mcc152dev.dbd
        iocmcc152_SRCS += devMcc152.c
        USR_SYS_LIBS += daqhats
  • st.cmd
    • dbLoadRecords("db/mcc152.db","HA=1, CH=0, SCAN=0")
Page last modified on March 12, 2021, at 10:18 AM
Powered by PmWiki