#%TITLE% NECST.MAC
#
#%NAME%
# Macros to control ESRF NECST device over ethernet.
# The NECST can be used as "Counter" or "Counter/Timer".
# Tested with embedded firmware 00.02
#
#%DESCRIPTION%
#%DL%
#%DT%Configuring counters%DD%
#
# Macro counters can be configured to read board counters
# (counters 1->12 have physical inputs, 13->16 are internals only).
#
# Configure first in SCALERS a new "Macro Counter" controller with
# DEVICE field set to string "necst" and ADDR field set to the
# hostname of NECST device. The "NUM" field must be set to 16%BR%
#
# Then configure counters using the previous controller, the "channel"
# is used to select the counter to read (from 1 to 16)%BR%
#
#%DT%Configuring the timer%DD%
#
# Configure first in SCALERS a new "Macro Counter/Timer" controller with
# DEVICE field set to string "necst" and ADDR field set to the
# hostname of NECST device. The "NUM" field must be set to 16%BR%
#
# Configure the "timebase" counter ("sec") to use the previous controller,
# the "Channel" must be set to 0, the "Scale Factor" must can be
# 2e4 1e5 1e6 1e7 1e8 2e8 (the internal NECST clock will be set
# according to this value)
#
# Then configure counters using the previous controller, the "Channel"
# is used to select the counter to read (from 1 to 16)%BR%
#
#%DT%Electrical signals%DD%
#
# When used as "counter/timer, the gate signal of the timer is available
# on output "DO1" of the front end.
#
# When used as "pure counters", the counters are gated by the signal
# incoming in input "DI1" of the front end.
#
# Currently, these signals are not configurable but hardcoded in
# the macros, sorry.
#
#%XDL%
#
#%END%
#
# Mandatory dependencies
need deepdevice
#%UU% hostname
#%MDESC%
# Launch an interactive command line interface.
# This allows for intance to check/setup the instrument configuration
# using direct command like ?HELP
#
def necst '{
local cmd
local pyc
if($# != 1) {
print "Usage: $0 hostname"
exit
}
print "NOT IMPLEMENT YET, sorry"
exit
pyc = "pepuconsole.py"
p sprintf("%s/bin/%s", BLISSADM, pyc)
if(!file_info(sprintf("%s/bin/%s", BLISSADM, pyc), "-x")) {
printf("Missing \"%s\" script, hint use blissinstaller\n", pyc)
exit
}
cmd = sprintf("%s %s", pyc, "$1")
unix(cmd)
}'
#%IU%()
#%MDESC%
# Called by spec after reading the config file
#
def necst_config(num, type, p1, p2, p3) '{
global NECST[]
global NECST_CLK[]
#
# p1==controller index p2==number of motors supported
#
if(type == "ctrl") {
local dev
local ans
local par
local idx
# get hostname from config
dev = necst_ADDR
# minimum check on controller
ans = necst_comm(dev, "?APPNAME")
if(!index(ans, "NECST")) {
_necst_err
printf("not a NECST instrument: \"%s\"\n", dev)
return ".error."
}
ans = necst_comm(dev, "?VERSION")
printf("Using \"%s\" for NECST instrument (version %s)\n", dev, ans)
# cleanup any previous configuration
idx = sprintf("%s#%d", dev, p1)
for(par in NECST[idx]) {
delete NECST[idx][par]
}
for(par in NECST_CLK) {
delete NECST_CLK[par]
}
# TODO: add optional controller parameters using necst_CONPAR
NECST[idx]["timer_channel"] = 16
NECST[idx]["timer_clock"] = 0
NECST[idx]["timer_gateout"] = "DO1"
NECST[idx]["timer_gatein"] = "DI1"
NECST[idx]["timer_used"] = 0
# board capabilities for firmware version 00.01
NECST_CLK[2e4] = "20KHZ"
NECST_CLK[1e5] = "100KHZ"
NECST_CLK[1e6] = "1MHZ"
NECST_CLK[1e7] = "10MHZ"
NECST_CLK[1e8] = "100MHZ"
NECST_CLK[2e8] = "200MHZ"
# normal end
return
}
#
# p1==unit p2==0 p3==channel
#
if(type == "cnt") {
local dev
local cha
local cmd
local ans
local cntcfg
local timcha
local timgdo
local src
local idx
cntcfg = "#"
cntcfg = cntcfg "CNTCFG CNT%d "
cntcfg = cntcfg "50OHM ON FILT OFF "
cntcfg = cntcfg "SRC %s "
cntcfg = cntcfg "TRIG OUT1 FALL "
cntcfg = cntcfg "GATE %s "
cntcfg = cntcfg "START SOFT STOP SOFT CLEAR "
cntcfg = cntcfg "OMODE GATE"
# retrieve hardware location
dev = counter_par(num, "address")
cha = counter_par(num, "channel")
idx = sprintf("%s#%d", dev, counter_par(num, "unit"))
# retrieve counter to be used as timer
timcha = NECST[idx]["timer_channel"]
timgdo = NECST[idx]["timer_gateout"]
# the timer counter is identified by channel 0
if(cha == 0) {
# handle the timer counter
ch = timcha
src = "CLK1"
gate = "OFF"
# automatically adjust clock
clk = NECST_CLK[counter_par(num, "scale")]
if(clk == 0) {
_necst_err
printf("invalid scale factor for channel 0 on NECST : \"%s\"\n", dev)
return ".error."
}
NECST[idx]["timer_clock"] = clk
NECST[idx]["timer_used"] = 1
} else {
# handle normal counter
ch = cha
src = "INPUT"
# if the controller is not a counter/timer,
# configure an external gating throung a digital input
# NOTE: as the timer counter has to have channel 0, we can
# consider that it has already be configured and therefore that
# we can trust the global var
if(NECST[idx]["timer_used"]) {
# handle counter/timer controller
gate = sprintf("OUT%d", timcha)
} else {
# handle pure counter controller
gate = NECST[idx]["timer_gatein"]
# some cleanup, for cosmetics only
delete NECST[idx]["timer_channel"]
delete NECST[idx]["timer_clock"]
delete NECST[idx]["timer_gateout"]
}
}
# configure the counter
cmd = sprintf(cntcfg, ch, src, gate)
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to configure channel %d on NECST : \"%s\"\n", cha, dev)
return ".error."
}
# enable the counter
cmd = sprintf("#SOFTSIG ENB CNT%d", ch)
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to enable channel %d on NECST : \"%s\"\n", cha, dev)
return ".error."
}
# update list of counters used
NECST[idx]["counters"] = sprintf("CNT%d %s", ch, NECST[idx]["counters"])
# from here, timer specific configuration
if(cha != 0) {
return
}
# timer internal clock
cmd = sprintf("#CLKCFG CLK1 %s", NECST[idx]["timer_clock"])
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to set internal clock on NECST : \"%s\"\n", dev)
return ".error."
}
# timer gate routed to digital ouput
cmd = sprintf("#DOCFG %s SRC OUT%d", timgdo, timcha)
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to set channel %d to DO on NECST : \"%s\"\n", cha, dev)
return ".error."
}
return
}
}'
#%IU%()
#%MDESC%
# Called by spec on motor or counter operations
#
def necst_cmd(num, key, p1, p2, p3) '{
global NECST[]
local mne
local dev
local cha
local cmd ans val
local idx
#
# Handle actions at controller level
#
if (key == "prestart_all") {
# p1==countring tim1 p2==counting mode p3==unit
dev = necst_ADDR
idx = sprintf("%s#%d", dev, p3)
# TODO: remove this once ?CNTSTATUS is available
NECST[idx]["timer_preset"] = 0
cmd = sprintf("SOFTSIG CLR %s", NECST[idx]["counters"])
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to clear counters on NECST : \"%s\"\n", dev)
return ".error."
}
return
}
#
# Handle actions at counter level
#
if(num == "..") {
return
}
dev = counter_par(num, "address")
cha = counter_par(num, "channel")
idx = sprintf("%s#%d", dev, counter_par(num, "unit"))
# called for any counter,
# the timer is the last one to be called
if (key == "start_one") {
# the timer counter is identified by channel 0
if(cha == 0) {
local cntcfg
# for the timer only, p1 is preset time in sec and p2 is counting mode
cha = NECST[idx]["timer_channel"]
val = p1 * counter_par(num, "scale")
# TODO: remove this once ?CNTSTATUS is available
NECST[idx]["timer_preset"] = val
# configure the counter preset time
cntcfg = "#"
cntcfg = cntcfg "CNTCFG CNT%d "
cntcfg = cntcfg "PRESET %d "
cmd = sprintf(cntcfg, cha, val)
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to configure channel %d on NECST : \"%s\"\n", cha, dev)
return ".error."
}
}
cmd = sprintf("#SOFTSIG START CNT%d", cha)
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to start channel %d on NECST : \"%s\"\n", cha, dev)
return ".error."
}
return
}
# returns non null if timer still running
if (key == "get_status") {
# called only for the timer,
cha = NECST[idx]["timer_channel"]
cmd = sprintf("?CNTSTAT CNT%d", cha)
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to read channel status %d on NECST : \"%s\"\n", cha, dev)
return ".error."
}
# WARNING: the value is returned in hexadecimal
if(sscanf(ans, "%x", val) != 1) {
_necst_err
print "unable to parse channel value"
return ".error."
}
# bit0: counter/timer running (1) or not (0)
# bit31: timer (1) or counter (0)
return (val&0x01)
# TODO: missing command ?CNTSTATUS in firmware
val = necst_cmd(0, "counts")
return !(val >= NECST[idx]["timer_preset"])
}
# called for any counter,
# returns the current counter value
if (key == "counts") {
local lat
# the timer counter is identified by channel 0
if(cha == 0) {
cha = NECST[idx]["timer_channel"]
lat = ""
} else {
lat = "LATCH"
}
cmd = sprintf("?CNTVAL CNT%d %s", cha, lat)
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to read channel %d on NECST : \"%s\"\n", cha, dev)
return ".error."
}
# WARNING: the value is returned in hexadecimal
if(sscanf(ans, "%x", val) != 1) {
_necst_err
print "unable to parse channel value"
return ".error."
}
# timer must return the value in seconds
if(cha == 0) {
val = val / counter_par(num, "scale")
}
return val
}
}'
#%IU%()
#%MDESC%
# Called by spec on motor_par() or counter_par()
#
def necst_par(num, key, todo, p1) '{
local dev
local cha
local cmd ans val
# return new counter_par() argins handled
if (key == "?" && todo == "get") {
return("config")
}
dev = counter_par(num, "address")
cha = counter_par(num, "channel")
# counter configuration
if (key == "config") {
if(todo == "set") {
_necst_err
printf("not a implemented yet\n")
return ".error."
}
#TODO: bug to be fixed in firmware
cmd = sprintf("#?CNTCFG CNT%d", cha)
ans = necst_comm(dev, cmd)
if(index(ans, "ERROR")) {
_necst_err
printf("unable to get config of channel %d on NECST : \"%s\"\n", cha, dev)
return ".error."
}
return(ans)
}
}'
#%IU%(hostname, command)
#%MDESC%
# Send a command to the given device and returns the answer if any
#
def necst_comm(hn, cmd) '{
local dev ans
# initialize communication if not already done
if(deepdev_check(hn) == -1) {
local dev
dev = sprintf("%s%s", hn, index(hn,":")?"":":5000")
deepdev_add(hn, dev)
}
# send the command to the device
ans = deepdev_comm(hn, cmd)
# handle and translate errors
if(ans == DEEPDEV_ERRANSW) {
return sprintf("ERROR: %s", DEEPDEV_ERRMSG)
}
if(ans == DEEPDEV_ERR) {
return sprintf("ERROR: fail to talk to \"%s\"", hn)
}
# normal end
return ans
}'
#%IU%
#%MDESC%
# Pure cosmetic macro
#
def _necst_err '{
tty_cntl("md")
printf("NECST ERROR: ")
tty_cntl("me")
}'
#%IU%
#%MDESC%
# Test macro
# Expect a 100MHz incoming signal on cnt1 counter.
#
def _necst_test '{
local tbeb tend texe
local tct
local i
local cnt
cnt = cnt1
for(i=0;;i++) {
tct = (i%2)?.1:.6
tbeg = time()
ct tct
tend = time()
texe = (tend-tbeg)*1000
printf("%s:%04d:Execution time: %10.2fms\n",date(), i, texe)
if(0) {
tct *= 1000
if(fabs(tct - texe) > 70) {
exit
}
}
if(1) {
if(fabs(S[cnt] - (tct*1e8)) > 1000) {
exit
}
}
}
}'
#%MACROS%
#%IMACROS%
#%AUTHOR% MP BLISS (Original 2/2017).
# %BR%$Revision: 1.1 $ / $Date: 2017/03/20 10:19:59 $
#%TOC%
|