Sequencer功能用于在DSP层面直接连接各操作序列,由于在DSP层面操作,因此可以达到实时的效果,比通过EzFlow或Eza效果更佳直观,典型应用比如9通道连接各个阶段的Ramper、无缝连接各个Replayer的输出,基本可以操作所有DSP object模块。
在数据库中只需要配置Sequencer的名称和允许的最大连接Function的数量,Sequencer模块需要通过Eza实现,在Pulsar界面没有相关的显示选项。
该模块的步骤为:
Sequencer通过SequencerModel和其下的SequencerFunction进行操作,每一个SequencerModel为数据库中的Sequencers下的实例。
// true/false;启动或停止Sequencer
// 设置 true时会立即启动Sequencer,不会等待,因此第一个Sequencer并不好直接精确控制,
Seq.Started = false ;
//清空当前Sequencer的操作函数,一般用于初始化
Seq.Clear();
// 将下一个SequencerFunction 添加到Sequencer待执行的SequencerFunction List的队尾
Seq.SetNextFunction(SequencerFunction f0);
SequencerFunction为Sequencer的关键操作函数,添加到Sequencer队列中时都需要新建:
//dspCodeIterationFrequency 可通过 (float) MultiSystemController.Instance.Dsp.DSPTimersCounters.DSPCodeIterationFrequency获取
SequencerFunction f0 = new SequencerFunction(float dspCodeIterationFrequency);
// 该时间后该SequencerFunction会强制退出执行下一个SequencerFunction,如果不设置,默认为0ms,该步函数直接跳过
f0.TimeoutPeriodMilliseconds = 10000;
//PointerDataPair setTrig = SGTrig.GetControlRegisterWithStopCleared();
// SequencerFunction 每次可操作两个Word,通过SetDataAtIndex实现,第二个参数为指针对,每个指针对包含指针及数据两部分,通常指针是查找到对应的DSP对象后,根据所需要的数据获取指针,数据则为每次需要设置到该指针的具体数值或状态确定的
// 设置的每个字可以为浮点量数据,也可以为32bit的状态字,甚至可以通过两个字来组成一个64bit的double数据。
f0.SetDataAtIndex(1,PointerDataPair setTrig);
f0.SetDataAtIndex(2,PointerDataPair setTrig);
// WatchMask用于监测某个指针所指向的数据与掩码是否对应,一旦对应上则立即结束当前SequencerFunction,比如用于监测Signal Generator或Replayer的停止位。
f0.SetWatchMask(IPointer pointer, uint mask);
// 直接设置状态位,也可以通过SetDataAtIndex实现,效果一致
f0.SetSetMask(IPointer pointer, uint mask)
// 清除某个状态位,设置0
f0.SetClearMask(IPointer pointer, uint mask)
其中后几项可以设置也可以不设置,但第一项(TimeoutPeriodMilliseconds)必须设置。
由于该模块是直接对DSP的数据进行操作,因此需要引用Pulsar中的一些库:
#using Servotest.D2R.Multisystem ;
#using Servotest.D2R.Dsp;
#using Servotest.D2R.DspUsb;
#using Servotest.D2R.MemoryManagement ;
#include Servotest.Dsp.dll;
根据需要定义一些状态控制字
// Signal Generator
const int CtrlWord =1 ;
const int KFromHost1= 4;
const int KFromHost2 = KFromHost1 +1;
const int Scale = 14;
const int QLength = 18;
const int QCount = 19;
const uint Stop = 0x00000001;
const uint Reset = 0x00000002;
const uint SoftStop = 0x00000008;
const uint SoftStart = 0x00000010;
const uint ChangingK = 0x00004000;
const uint QuarterCycle = 0x00000100;
// Replayer的状态字
const int CtrlWordIndex = 1;
const uint StopBit = 0x00000001;
const uint EndOfFileBit = 0x00000002;
const uint WindDownBit = 0x00000004;
const uint TriggeredBit = 0x00000008;
const uint WaitingForTrigger = 0x00000010;
如果知道数据库中的Sequencer具体的名字,可直接确定:
Seq = objectCollection.SequencerModels["TestSequencer"];
如果不确定或需要在Eza中通过入参的形式则可以先定义方法,
SequencerModel FindSequencer (string name)
{
SequencerModel obj = objectCollection.SequencerModels[name];
if (obj== null)
{
throw new Exception( "Sequencer not found :"+ name);
}
return obj;
}
然后在Eza中通过方法进行查找
SequencerModel Seq = FindSequencer("%Sequencer%");
Seq.Started = false;
Seq.Clear();
由于SequencerFunction是对DSP操作,因此本步骤重点是要确定出操作目标的指针及数据(状态字或数值)
SequencerFunction f0= new SequencerFunction(dspCodeIterationFrequency);
// 确定需要设定的指针对(指针,数据),以下根据需要进行选用
// 类型一:如果是设置Signal Generator的状态位,可以直接调用其方法
// $Sig Gen name$的激活状态指针对
PointerDataPair startSG = $Sig Gen name$.GetControlRegisterWithStopCleared();// this function used by damper
// 对其SoftStart(0x00000010)的位标记为1
startSG.SetDataBits(SoftStart);
// 执行指针对操作
f0.SetDataAtIndex(1,startSG);
// 如果本次不在进行其他操作,则需要将另一个操作的指针对设置为一样的即可,如不设置对导致退出。
f0.SetDataAtIndex(2,startSG);
// 添加状态观测位,一旦停止立即执行下一SequenceFunction
f0.SetWatchMask(setTrig.Pointer,Stop); // use the reset bit on siggen1 to trigger
// 添加到对应Sequencer中待执行的SequencerFunction的队末。
Seq.SetNextFunction(f0);
// 类型二:如果不是 Sig Gen的状态字操作,比如需要对频率操作,需要先确定对应模块的DSP单元,然后根据偏移确定该操作所在的指针,需要额外定义一个函数 FindDSPObject(string name)
IDSPObject o = FindDSPObject(SG1.Name);
if (o== null)
{
throw new Exception("object not found :"+ SG1.Name) ;
}
// 获取DSP的内存单元地址块
IMemoryBlock block = o.Block;
// 设置Sig Generator的频率
SG1.Frequency = Frequency;
// 由于下位机执行信号发生器是根据k值直接执行的,而K值是在上位机设置频率时会更新到DSP中,下位机不会立即开始动作,因此此时设置频率只时获取一个临时的k值,将来通过Sequencer执行。
// 同时由于k是double类型,因此需要占据两个字,因此可以先获取回来,通过SetDataAtIndex两次分别写入高低位,同样可行。
// KFromHost1 为k的偏移位KFromHost的高位,KFromHost2 为k的偏移位KFromHost的低位,【高低位的描述可能反了,但保证从哪个位读,将来就写到哪个位即可】
Word k1 = block[KFromHost1];
Word k2 = block[KFromHost2];
PointerDataPair setFreq1 = new PointerDataPair (block.PointerTo(KFromHost1),k1);
PointerDataPair setFreq2 = new PointerDataPair (block.PointerTo(KFromHost2),k2);
f2.SetDataAtIndex(1,setFreq1);
f2.SetDataAtIndex(2,setFreq2);
// 如果此时Signa Generator已经开始允许了,则通过改变状态字ChangingK来强行触发,更新DSP中的K(即上两步写入的k),注意:ChangingK下位机判断一次后如果为True,更新k后会再改为False,便于下次判断使用。
f2.SetSetMask(block.PointerTo(CtrlWord),ChangingK ); // force the change
double milliSecPerCycle = 1000.0/Frequency;
double Points = nCycles * milliSecPerCycle;
// 通过Timeout退出
f2.TimeoutPeriodMilliseconds = (int) Points ;
// 添加到对应Sequencer中待执行的SequencerFunction的队末。
Seq.SetNextFunction(f0);
// 对于FloatVariable的修改要更加简单
IDSPObject objFvCurrent = FindDSPObject($Tri Fv 1$.Name);
IMemoryBlock blockFvCurrent = objFvCurrent.Block;
// 下一步需要设定的Float Variable,当前的Fv需要复位为1,Float Variable DSP只有一个字
PointerDataPair setFvCurrent = new PointerDataPair(blockFvCurrent.PointerTo(0), new Word(1f));
SequencerFunction fi = new SequencerFunction(dspCodeIterationFrequency);
fi.TimeoutPeriodMilliseconds = (long)(timeout);
fi.SetDataAtIndex(1, setFvCurrent);
fi.SetDataAtIndex(2, setFvCurrent);
// 添加到对应Sequencer中待执行的SequencerFunction的队末。
Seq.SetNextFunction(fi);
IDSPObject FindDSPObject(string name) // 根据名称查找对应的DSP模块指针位置
{
IDSP Dsp = MultiSystemController.Instance.Dsp;
var memory = ((MultiSystemController.Instance.Dsp as IDSPObjectSupport).DriverMemory as DSPDriver);
foreach (IDSPObject o in Dsp.DSPObjects) {
if (name == o.Name)
{
return o;
}
}
// to get here must be an error
throw new Exception("Unable to find DSP object :"+name);
return null;
}
Seq.SetNextFunction(f1);
Seq.Started = true;
Seq.Clear();
Sequencer是预先设计,按计划执行,因此在编写Eza时需要注意在Eza设置的一些控制量属于上位机,当Sequence执行到这个SequencerFunction的时候,这个控制量的状态是什么,eza可能一次写很多SequencerFunction,eza设置好后,运行先结束,后续交给Sequencer自动执行。
在执行过程中仍然可以添加SequencerFunction到队尾,比如用于Replayer的轮询调用。
SequencerFunction注意添加的数量不超过MaximumFunctions指定的数量,MaximumFunctions没有严格限制,较多后打开DSP Tools-> PokeN读取Sequencer模块时会比较卡,不建议超过200。