Raspberry Pi® HAT Erweiterungsmodul für Spannungsmessung
Das MCC 118 DAQ HAT bietet acht Kanäle der analogen Spannungsmessung für Raspberry Pi-basierte Datenerfassungs-/Datenloggersysteme. Die maximale Datenrate des MCC 118 beträgt 100 kS/s pro Board für die Messung von Einzelpunkten oder Wellenformen. Bis zu acht MCC 118-Geräte können gestapelt werden - mit bis zu 64 Datenkanälen und einem maximalen Durchsatz von 320 kS/s.
https://www.mccdaq.de/DAQ-HAT/MCC-118.aspx
Dieses Handbuch und das fertige Datenerfassungssystem wurden zusammen mit dem Elektroniklabor des Fritz-Haber-Instituts entwickelt und werden dort eingesetzt.
http://www.fhi-berlin.mpg.de/elab/pub/index_e.html
Insbesondere wurde dieses Projekt von Abdulrhman Moshantaf als Werkstudent begleitet.
moshantaf@fhi-berlin.mpg.de
Laden Sie zunächst die Daqhat-Bibliothek von der Github-Seite herunter und baue diese.
Bau des IOC
- Baue einen example IOC
- Database iocmcc118App/Db
- Makefile
-
DB += mcc118.db
DB += channelmapping.db
-
- 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")
}
-
- Makefile
- source
- Hier werden die Quelldateien aus der Daqhat-Bibliothek eingefügt.
-
daqhats.h
daqhats_utils.h
mcc118.h
-
- 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
- Füge die folgenden Zeilen hinzu:
-
iocmcc118_DBD += mcc118dev.dbd
iocmcc118_SRCS += devMcc118.c
USR_SYS_LIBS += daqhats
- Hier werden die Quelldateien aus der Daqhat-Bibliothek eingefügt.
- st.cmd
-
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")
-
Spannungsausgang und DIO DAQ HAT für Raspberry Pi®
Der MCC 152 Spannungsausgang und DIO HAT bietet zwei Kanäle für die analoge Spannungsausgabe und acht digitale E/A für Raspberry Pi-basierte Systeme. Die 12-Bit-Spannungsausgänge haben einen Bereich von 0-5 V und können mit bis zu 5 kS/s aktualisiert werden. Bis zu acht MCC DAQ HAT-Bausteine können auf eine Raspberry Pi gestapelt werden.
https://www.mccdaq.com/DAQ-HAT/MCC-152.aspx
Built the IOC
- Built an example IOC
- Database iocmccApp
- Makefile
-
DB += mcc152.db
-
- 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") }
-
- Makefile
- source
- The source files from the Daqhat library are inserted here.
-
daqhats.h
daqhats_utils.h
mcc152.h
-
- 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
- The source files from the Daqhat library are inserted here.
- st.cmd
-
dbLoadRecords("db/mcc152.db","HA=1, CH=0, SCAN=0")
-