diff --git a/.gitignore b/.gitignore index 5dea49c8f1dba63b0c0c1669c828824e1549aad2..57204b7edc1d8be1576f1914a200d8db42731ba0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ Python/test.ipynb __pycache__/ -Python/lib/AWG.py *.pyc *.png *.npz @@ -138,4 +137,7 @@ FakesAssemblies/ **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml -_Pvt_Extensions \ No newline at end of file +_Pvt_Extensions +/Cpp/.vscode +/Cpp/test.cpp +/Cpp/test.exe diff --git a/Cpp/Cpp.vcxproj b/Cpp/Cpp.vcxproj index 2a4f15c9992528bfbff9c94cf7024f35b9fbb18a..83cb67cda0787aa8971f2e0bce3dd009054f2eed 100644 --- a/Cpp/Cpp.vcxproj +++ b/Cpp/Cpp.vcxproj @@ -72,7 +72,7 @@ <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <RunCodeAnalysis>true</RunCodeAnalysis> - <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> + <CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -109,10 +109,13 @@ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> <EnablePREfast>true</EnablePREfast> + <LanguageStandard>stdcpp20</LanguageStandard> </ClCompile> <Link> <SubSystem>Console</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalLibraryDirectories>C:\Lab\awg-control\Cpp\lib\driver_header;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>spcm_win64_msvcpp.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> @@ -137,11 +140,14 @@ </ItemGroup> <ItemGroup> <ClInclude Include="lib\AWG.h" /> - <ClInclude Include="lib\dlltyp.h" /> - <ClInclude Include="lib\errors.h" /> - <ClInclude Include="lib\regs.h" /> - <ClInclude Include="lib\spcerr.h" /> - <ClInclude Include="lib\spcm_drv.h" /> + <ClInclude Include="lib\driver_header\dlltyp.h" /> + <ClInclude Include="lib\driver_header\errors.h" /> + <ClInclude Include="lib\driver_header\regs.h" /> + <ClInclude Include="lib\driver_header\spcerr.h" /> + <ClInclude Include="lib\driver_header\spcm_drv.h" /> + </ItemGroup> + <ItemGroup> + <None Include="cpp.hint" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/Cpp/Cpp.vcxproj.filters b/Cpp/Cpp.vcxproj.filters index 61a75a94cd699cdb352036ea102559958aa57940..41493f74438abd4e921d69707dcee7131abf41d9 100644 --- a/Cpp/Cpp.vcxproj.filters +++ b/Cpp/Cpp.vcxproj.filters @@ -26,20 +26,23 @@ <ClInclude Include="lib\AWG.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="lib\dlltyp.h"> + <ClInclude Include="lib\driver_header\dlltyp.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="lib\errors.h"> + <ClInclude Include="lib\driver_header\errors.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="lib\regs.h"> + <ClInclude Include="lib\driver_header\regs.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="lib\spcerr.h"> + <ClInclude Include="lib\driver_header\spcerr.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="lib\spcm_drv.h"> + <ClInclude Include="lib\driver_header\spcm_drv.h"> <Filter>Header Files</Filter> </ClInclude> </ItemGroup> + <ItemGroup> + <None Include="cpp.hint" /> + </ItemGroup> </Project> \ No newline at end of file diff --git a/Cpp/cpp.hint b/Cpp/cpp.hint new file mode 100644 index 0000000000000000000000000000000000000000..155867e17bb32ef3fec900aca26f58c5be394448 --- /dev/null +++ b/Cpp/cpp.hint @@ -0,0 +1,6 @@ +// Hint files help the Visual Studio IDE interpret Visual C++ identifiers +// such as names of functions and macros. +// For more information see https://go.microsoft.com/fwlink/?linkid=865984 +#define drv_handle +#define int32 +#define int64 diff --git a/Cpp/lib/AWG.cpp b/Cpp/lib/AWG.cpp index 164b11b7d4a99a5961925f5107b5e60669ef600e..263270151d9c26090754a05052ceadac14275319 100644 --- a/Cpp/lib/AWG.cpp +++ b/Cpp/lib/AWG.cpp @@ -1,6 +1,1023 @@ #include "AWG.h" +#include <iostream> +#include <cmath> -AWG::AWG(int cardID) noexcept -{ +AWG::AWG() noexcept { + this->pCardHandle = nullptr; + this->cardIdx = -1; + this->serialNumber = -1; + this->instMemSize = -1; + this->bytesPerSample = -1; + this->maxSampleRate = -1; +} -}; +AWG::~AWG() { + // TODO: do this + return; +} + +void AWG::checkError() { + /** + * printout error if detected. + * + */ + if (this->pCardHandle == nullptr) { return; } + char errorMsg[ERRORTEXTLEN]; + if ( + spcm_dwGetErrorInfo_i32(this->pCardHandle, NULL, NULL, errorMsg) + != ERR_OK + ) { + std::cout << "AWG error" << std::string(errorMsg) + << "\ncard closed\n"; + this->close(); + } +} + +void* AWG::getPageAlignedMem(uint64 memSize) { + /** + * returns a pointer to a page aligned mem location. + * + * @param memSize size of memory to allocate, bytes + * @return pointer to start of a page aligned mem location + */ + + // for unknown reasons VirtualAlloc/VirtualFree leaks memory if memSize < 4096 (page size) + // therefore use _aligned_malloc () to get small amounts of page aligned memory + if (memSize >= 4096) + return VirtualAlloc(NULL, (size_t)memSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + else { + void* pMem = _aligned_malloc((size_t)memSize, 4096); + if (pMem == NULL) + return NULL; + memset(pMem, 0, (size_t)memSize); + return pMem; + } +} + +void AWG::freePageAlignedMem(void* pMem, uint64 memSize) { + /** + * frees a page aligned memory space. + * + * @param pMem pointer to a page aligned memory location + * @param memSize memory size in bytes to deallocate + */ + if (memSize >= 4096) + VirtualFree(pMem, 0, MEM_RELEASE); + else + _aligned_free(pMem); +} + +void AWG::open(int openIndex) { + /** + * opens a connection to AWG by index. + * + * @param openIndex card index, 0 or 1 + */ + if (!this->isOpen()) { + std::cout << "card already opened" << std::endl; + return; + } + + auto openMsg = "/dev/spcm" + std::to_string(openIndex); + this->pCardHandle = spcm_hOpen(openMsg.c_str()); + if (pCardHandle == nullptr) { + std::cout << "card open failed" << std::endl; + return; + } + + this->cardIdx = openIndex; + spcm_dwGetParam_i32( + this->pCardHandle, + SPC_PCISERIALNO, + &this->serialNumber + ); + spcm_dwGetParam_i64( + this->pCardHandle, + SPC_PCISAMPLERATE, + &this->maxSampleRate + ); + spcm_dwGetParam_i64( + this->pCardHandle, + SPC_PCIMEMSIZE, + &this->instMemSize + ); + spcm_dwGetParam_i32( + this->pCardHandle, + SPC_MIINST_BYTESPERSAMPLE, + &this->bytesPerSample + ); + + this->checkError(); +} + +bool AWG::isOpen() { + /** + * check if a connection to AWG exists. + * + * @return true or false + */ + if (this->pCardHandle == nullptr) { return false; } + return true; + this->checkError(); +} + +void AWG::close() { + /** + * close the connection to AWG. + * + */ + if (!this->isOpen()) { return; } + spcm_vClose(this->pCardHandle); + this->pCardHandle = nullptr; +} + +void AWG::reset() { + /** + * resets the AWG card, equivalent to a power reset. + * + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_CARD_RESET + ); + this->checkError(); +} + +int AWG::getCardIdx() { + return this->cardIdx; +} + +int AWG::getSerialNumber() { + return this->serialNumber; +} + +int64 AWG::getInstMemSize() { + return this->instMemSize; +} + +int AWG::getBytesPerSample() { + return this->bytesPerSample; +} + +int64 AWG::getMaxSampleRate() { + return this->maxSampleRate; +} + +void AWG::setSampleRate(int64 sampleRate) { + if (!this->isOpen()) { return; } + if (sampleRate < 0 or sampleRate > this->maxSampleRate) { + std::cout << sampleRate << "exceeds max sample rate" << std::endl; + return; + } + spcm_dwSetParam_i64( + this->pCardHandle, + SPC_SAMPLERATE, + sampleRate + ); +} + +int64 AWG::getSampleRate() { + if (!this->isOpen()) { return 0; } + int64 sr; + spcm_dwGetParam_i64( + this->pCardHandle, + SPC_SAMPLERATE, + &sr + ); + this->checkError(); + return sr; +} + +void AWG::setActiveChannels(std::set<int> channels) { + /** + * activate channels for next run, resets last set. + * + * @param channels vector of channel index (0-3) + */ + if (!this->isOpen()) { return; } + int64 chMask = 0; + for (auto ch : channels) { + chMask = chMask | int64(pow(2,ch)); + } + spcm_dwSetParam_i64( + this->pCardHandle, + SPC_CHENABLE, + chMask + ); + this->checkError(); + this->activeChannels = channels; +} + +void AWG::setChannelAmp(int ch, int amp) { + /** + * set channel amplitude. + * + * @param ch channel index + * @param amp amplitude (mV) + */ + if (!this->isOpen()) { return; } + const auto regStep = SPC_AMP1 - SPC_AMP0; + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_AMP0 + ch * regStep, + amp + ); + this->checkError(); +} + +void AWG::setChannelStopLvl(int ch, CHANNEL_STOPLVL stopLvl) { + /** + * set channel stop level. + * + * @param ch channel index + * @param stopLvl options: ZERO,LOW,HIGH,HOLDLAST + */ + if (!this->isOpen()) { return; } + const auto regStep = SPC_CH1_STOPLEVEL - SPC_CH0_STOPLEVEL; + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_CH0_STOPLEVEL + ch * regStep, + int32(stopLvl) + ); + this->checkError(); +} + +void AWG::toggleChannelOutput(int ch, bool enable) { + /** + * toggle channel output. + * + * @param ch channel index + * @param enable true or false + */ + if (!this->isOpen()) { return; } + if (ch < 0 or ch > 3) { + std::cout << ch << "is not an allowed channel index" + << std::endl; + return; + } + const auto regStep = SPC_ENABLEOUT1 - SPC_ENABLEOUT0; + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_ENABLEOUT0 + ch * regStep, + enable + ); + this->checkError(); +} + +void AWG::setChannel(int ch, int amp, CHANNEL_STOPLVL stopLvl, bool enable) { + /** + * channel macro set amplitude, stop level, and enables output. + * + * @param ch channel index + * @param amp amplitude (mV) + * @param stopLvl options: ZERO, LOW, HIGH, HOLDLAST + */ + this->setChannelAmp(ch, amp); + this->setChannelStopLvl(ch, stopLvl); + this->toggleChannelOutput(ch, enable); +} + +int AWG::getChannelCount() { + if (!this->isOpen()) { return 0; } + int32 numCh; + spcm_dwGetParam_i32( + this->pCardHandle, + SPC_CHCOUNT, + &numCh + ); + this->checkError(); + return numCh; +} + +std::set<int> AWG::getChannelActivated() { + return this->activeChannels; +} + +void AWG::setChannelDiffMode(int chPair, bool enable) { + /** + * set channel 0(2) and channel 1(3) to differential output mode. + * + * @param chPair 0 for channel 0,1, 1 for channel 2,3 + * @param enable true or false + */ + if (!this->isOpen()) { return; } + if (chPair != 0 && chPair != 1) { + std::cout << "unable to parse input chPair: " + << chPair << std::endl; + return; + } + if (enable) { + if (chPair == 0 && this->activeChannels.contains(1)) { + std::cout << "channel 1 must disabled for chPair=0" + "in differential output mode" << std::endl; + return; + } + if (chPair == 1 && this->activeChannels.contains(3)) { + std::cout << "channel 3 must disabled for chPair=1" + "in differential output mode" << std::endl; + return; + } + } + const auto regStep = SPC_DIFF2 - SPC_DIFF0; + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_DIFF0 + chPair * regStep, + enable + ); + this->checkError(); +} + +void AWG::setChannelDoubleMode(int chPair, bool enable) { + /** + * set channel 0(2) and channel 1(3) to differential output mode. + * + * @param chPair 0 for channel 0,1, 1 for channel 2,3 + * @param enable true or false + */ + if (!this->isOpen()) { return; } + if (chPair != 0 && chPair != 1) { + std::cout << "unable to parse input chPair: " + << chPair << std::endl; + return; + } + if (enable) { + if (chPair == 0 && this->activeChannels.contains(1)) { + std::cout << "channel 1 must disabled for chPair=0" + "in double output mode" << std::endl; + return; + } + if (chPair == 1 && this->activeChannels.contains(3)) { + std::cout << "channel 3 must disabled for chPair=1" + "in double output mode" << std::endl; + return; + } + } + const auto regStep = SPC_DOUBLEOUT2 - SPC_DOUBLEOUT0; + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_DOUBLEOUT0 + chPair * regStep, + enable + ); + this->checkError(); +} + +void AWG::writeSetup() { + /** + * write current setup to card without starting hardware, + * some settings may not be changed while card is running. + * + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_CARD_WRITESETUP + ); + this->checkError(); +} + +void AWG::cardRun() { + /** + * starts the card with all selected settings. + * + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_CARD_START + ); + this->checkError(); +} + +void AWG::cardStop() { + /** + * stops the card, no effect if not running. + * + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_CARD_STOP + ); + this->checkError(); +} + +void AWG::waitReady() { + /** + * wait until card finished current output run. + * TODO: check if this blocks. + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_CARD_WAITREADY + ); + this->checkError(); +} + +void AWG::setTimeOut(int time) { + /** + * set a timeout time in ms, 0 to disable. + * + * @param time + */ + if (!this->isOpen()) { return; } + if (time < 0) { + std::cout << time << "is an invalid timeout time" << std::endl; + } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_TIMEOUT, + time + ); + this->checkError(); +} + +int AWG::getTimeOut() { + if (!this->isOpen()) { return 0; } + int32 time; + spcm_dwGetParam_i32( + this->pCardHandle, + SPC_TIMEOUT, + &time + ); + return time; +} + +void AWG::printCardStatus() { + if (!this->isOpen()) { return; } + int32 status; + spcm_dwGetParam_i32( + this->pCardHandle, + SPC_M2STATUS, + &status + ); + std::cout << STATUS_NAMES.at(status) << std::endl; +} + +void AWG::toggleTrigger(bool enable) { + /** + * toggle the trigger detection. + * + * @param enable + */ + if (!this->isOpen()) { return; } + auto command = enable + ? M2CMD_CARD_ENABLETRIGGER + : M2CMD_CARD_DISABLETRIGGER; + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + command + ); + this->checkError(); +} + +void AWG::forceTrigger() { + /** + * sends a software trigger, equivalent to an actual trigger. + * + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_CARD_FORCETRIGGER + ); + this->checkError(); +} + +void AWG::waitTrigger() { + /** + * wait until trigger event. + * TODO: check if this blocks. + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_CARD_WAITTRIGGER + ); + this->checkError(); +} + +void AWG::setTrigMaskOr(std::initializer_list<TRIGGER_MASK> trigMasks) { + /** + * program the OR trigger mask. + * + * @param trigMasks 1 or more TRIGGER_MASK + */ + if (!this->isOpen()) { return; } + auto mask = 0; + for (auto m : trigMasks) { + mask = mask | int32(m); + } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_TRIG_ORMASK, + mask + ); + this->checkError(); +} + +void AWG::setTrigMaskAnd(std::initializer_list<TRIGGER_MASK> trigMasks) { + /** + * program the AND trigger mask. + * + * @param trigMasks 1 or more TRIGGER_MASK + */ + if (!this->isOpen()) { return; } + int mask = 0; + for (auto m : trigMasks) { + mask = mask | int32(m); + } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_TRIG_ANDMASK, + mask + ); + this->checkError(); +} + +void AWG::setTrigMode(int trigChannel, TRIGGER_MODE trigMode) { + /** + * sets the trigger mode of trigger channel. + * + * @param trigChannel 0 or 1 (labeled EXT0 and EXT1 on board) + * @param trigMode see TRIGGER_MODE for options + */ + if (!this->isOpen()) { return; } + if (trigChannel != 0 and trigChannel != 1) { + std::cout << "invalid trigger channel: " + << trigChannel << std::endl; + return; + } + auto trigRegister = trigChannel + ? SPC_TRIG_EXT1_MODE + : SPC_TRIG_EXT0_MODE; + + spcm_dwSetParam_i32( + this->pCardHandle, + trigRegister, + int32(trigMode) + ); + this->checkError(); +} + +void AWG::setTrigTerm(int term) { + /** + * sets the trigger input termination. + * + * \param term 0 for high impedance, 1 for 50 Ohm. + */ + + if (!this->isOpen()) { return; } + if (term != 0) { + std::cout << "invalid trigger input termination setting: " + << term << std::endl; + return; + } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_TRIG_TERM, + term + ); + this->checkError(); +} + +int AWG::getTrigTerm() { + if (!this->isOpen()) { return -1; } + int32 term; + spcm_dwGetParam_i32( + this->pCardHandle, + SPC_TRIG_TERM, + &term + ); + this->checkError(); + return term; +} + +void AWG::setTrigCoupling(int channel, int coupling) { + /** + * sets trigger coupling to DC or AC. + * + * @param coupling 1 for AC, 0 for DC + */ + if (!this->isOpen()) { return; } + if (coupling != 0 and coupling != 1) { + std::cout << "invalid trigger coupling setting :" << coupling + << std::endl; + return; + } + auto trigChannelReg = channel ? + SPC_TRIG_EXT1_ACDC : SPC_TRIG_EXT0_ACDC; + spcm_dwSetParam_i32( + this->pCardHandle, + trigChannelReg, + coupling + ); + this->checkError(); +} + +int AWG::getTrigCoupling(int channel) { + if (!this->isOpen()) { return -1; } + auto trigChannelReg = channel ? + SPC_TRIG_EXT1_ACDC : SPC_TRIG_EXT0_ACDC; + int32 coupling; + spcm_dwGetParam_i32( + this->pCardHandle, + trigChannelReg, + &coupling + ); + this->checkError(); + return coupling; +} + +void AWG::setTrigLvl(int channel, int level) { + /** + * set trigger level. + * + * @param channel trigger channel + * @param level -10000mV to 10000mV + */ + if (!this->isOpen()) { return; } + if (level < -10000 or level > 10000) { + std::cout << "invalid trigger level: " << level << std::endl; + return; + } + auto trigChannelReg = channel ? + SPC_TRIG_EXT1_LEVEL0 : SPC_TRIG_EXT0_LEVEL0; + spcm_dwSetParam_i64( + this->pCardHandle, + trigChannelReg, + level + ); + this->checkError(); +} + +int AWG::getTrigLvl(int channel) { + if (!this->isOpen()) { return 0; } + auto trigChannelReg = channel ? + SPC_TRIG_EXT1_LEVEL0 : SPC_TRIG_EXT0_LEVEL0; + int32 level; + spcm_dwGetParam_i32( + this->pCardHandle, + trigChannelReg, + &level + ); + this->checkError(); + return level; +} + +void AWG::setTrigRearmLvl(int channel, int level) { + /** + * sets rearm trigger level. + * + * @param channel trigger channel + * @param level -10000mV to +10000mV + */ + if (!this->isOpen()) { return; } + auto trigChannelReg = channel ? + SPC_TRIG_EXT1_LEVEL1 : SPC_TRIG_EXT0_LEVEL1; + spcm_dwSetParam_i64( + this->pCardHandle, + trigChannelReg, + level + ); + this->checkError(); +} + +int64 AWG::getTrigRearmLvl(int channel) { + if (!this->isOpen()) { return 0; } + auto trigChannelReg = channel ? + SPC_TRIG_EXT1_LEVEL1 : SPC_TRIG_EXT0_LEVEL1; + int64 level; + spcm_dwGetParam_i64( + this->pCardHandle, + trigChannelReg, + &level + ); + return level; +} + +//void AWG::setReplayMode(REPLAY_MODE mode) { +// /** +// * set the data replay mode. +// * +// * a macro for sequence replay mode is setup in +// * initReplayModeSeq(), if you wish to use other modes, you must +// * consult the manual for mode specific settings. +// * +// * @param mode SINGLE, MULTI, GATE, SINGLERESTART, SEQUENCE, +// * FIFO_{SINGLE, MULTI, GATE}. +// * +// */ +// if (!this->isOpen()) { return; } +// spcm_dwSetParam_i32( +// this->pCardHandle, +// SPC_CARDMODE, +// mode +// ); +// this->checkError(); +//} +// +//void AWG::setMemSize(int64 sampleSize) { +// /** +// * set memory size in samples in SINGLE, SINGLE_RESTART, +// * MULTI, and GATE mode. +// * Please consult the manual for # of channel dependent +// * minimum/maximum sample size considerations. +// * +// * @param sampleSize size of sample +// * +// */ +// if (!this->isOpen()) { return; } +// if (sampleSize < 0 or sampleSize > 2e9) { +// std::cout << "invalid sampleSize: " << sampleSize << std::endl; +// return; +// } +// spcm_dwSetParam_i64( +// this->pCardHandle, +// SPC_MEMSIZE, +// sampleSize +// ); +// this->checkError(); +//} +// +//int64 AWG::getMemsize() { +// int64 ms; +// spcm_dwGetParam_i64( +// this->pCardHandle, +// SPC_MEMSIZE, +// &ms +// ); +// this->checkError(); +// return ms; +//} +// +//void AWG::setLoop(int nLoop) { +// /** +// * set number of replay loops in SINGLE, SINGLE_RESTART, +// * MULTI, and GATE mode. +// * +// * @param nLoop +// */ +// if (!this->isOpen()) { return; } +// if (nLoop < 0) { +// std::cout << "invalid number of loop: " << nLoop << std::endl; +// return; +// } +// spcm_dwSetParam_i32( +// this->pCardHandle, +// SPC_LOOPS, +// nLoop +// ); +//} +// +//int64 AWG::getLoop() { +// if (!this->isOpen()) { return -1; } +// int64 nl; +// spcm_dwGetParam_i64( +// this->pCardHandle, +// SPC_LOOPS, +// &nl +// ); +// return nl; +//} + +void AWG::initReplayModeSeq(int nSeg) { + /** + * initialize sequence replay mode. + * + * @param nSeg number of segments memory is divided into + */ + if (!this->isOpen()) { return; } + if (nSeg % 2 != 0) { + std::cout << "invalid number of segments for SEQUENCE mode: " + << nSeg << std::endl; + return; + } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_CARDMODE, + int32(REPLAY_MODE::SEQUENCE) + ); + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_SEQMODE_MAXSEGMENTS, + nSeg + ); + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_SEQMODE_STARTSTEP, + 0 + ); + this->checkError(); +} + +void AWG::setSeqModeStep( + uint64 step, + uint64 segment, + uint64 nextStep, + uint64 nLoop, + SEQ_LOOPCONDITION condition) { + /** + * configure a step in sequence replay mode. + * + * @param step step to configure + * @param segment memory segment step is associated with + * @param nextStep index of next step after end loop condition is met + * @param condition end loop condition + */ + if (!this->isOpen()) { return; } + int64 mask = + (uint64(condition) << 32) + | (nLoop << 32) + | (nextStep << 16) + | segment; + spcm_dwSetParam_i64( + this->pCardHandle, + SPC_SEQMODE_STEPMEM0 + step, + mask + ); +} + +void AWG::writeSeqModeSegment( + void* pDataBuffer, + int size, + int segment) { + /** + * write buffered data into a memory segment in sequence replay mode. + * + * @param pDataBuffer pointer to data memory, must be page aligned + * @param size size of data buffer in number of samples + * @param segment memory segment to write to + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_SEQMODE_WRITESEGMENT, + segment + ); + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_SEQMODE_SEGMENTSIZE, + size + ); + this->prepDataTransfer(pDataBuffer, size); + this->startDataTransfer(); + this->checkError(); +} + +void AWG::prepDataTransfer( + void* dataBuffer, + uint64 bufferLen, + BUFFER_TYPE bufType, + TRANSFER_DIR dir, + uint32 notifySize, + uint64 brdMemOffs +) { + /** + * prepares the board for data transfer. + * + * @param dataBuffer pointer to the data buffer + * @param bufferLen length of data buffer, bytes + * @param bufType buffer type, use only DATA for replay + * @param dir direction of data transfer + * @param notifySize number of bytes after which an event is sent + * @param brdMemOffs offset for transfer in board memory + */ + if (!this->isOpen()) { return; } + spcm_dwDefTransfer_i64( + this->pCardHandle, + int32(bufType), + int32(dir), + notifySize, + dataBuffer, + brdMemOffs, + bufferLen + ); + this->checkError(); +} + +void AWG::startDataTransfer() { + /** + * starts data transfer for a defined buffer. + * + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_DATA_STARTDMA + ); + this->checkError(); +} + +void AWG::waitDataTransfer() { + /** + * wait for data transfer to complete, blocks, + * timeout is considered. + * + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_DATA_WAITDMA + ); + this->checkError(); +} + +void AWG::stopDataTransfer() { + /** + * stops a running data transfer. + * + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_M2CMD, + M2CMD_DATA_STOPDMA + ); + this->checkError(); +} + +void AWG::setClockMode(CLOCK_MODE cm) { + /** + * set the clock mode. + * + * @param cm INTPLL or EXTREFCLK + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_CLOCKMODE, + int32(cm) + ); + this->checkError(); +} + +int32 AWG::getClockMode() { + if (!this->isOpen()) { return -1; } + int32 cm; + spcm_dwGetParam_i32( + this->pCardHandle, + SPC_CLOCKMODE, + &cm + ); + this->checkError(); + return cm; +} + +void AWG::setRefClkFreq(int64 frequency) { + /** + * if using EXTREFCLK mode, set the external clock frequency. + * + * @param frequency Hz + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i64( + this->pCardHandle, + SPC_REFERENCECLOCK, + frequency + ); + this->checkError(); +} + +void AWG::setClockOut(bool enable) { + /** + * enable/disable clock output. + * + * @param enable true/false + */ + if (!this->isOpen()) { return; } + spcm_dwSetParam_i32( + this->pCardHandle, + SPC_CLOCKOUT, + enable + ); + this->checkError(); +} + +int64 AWG::getClockOutFreq() { + /** + * Read out frequency of internally synthesized clock. + * + * @return frequency (Hz) + */ + if (!this->isOpen()) { return -1; } + int64 freq; + spcm_dwGetParam_i64( + this->pCardHandle, + SPC_CLOCKOUTFREQUENCY, + &freq + ); + this->checkError(); + return freq; +} diff --git a/Cpp/lib/AWG.h b/Cpp/lib/AWG.h index 06082170659ec4b8c7e7bc1049a924904c087e93..af28ce0b815b4bc921fe4550ae4b257d1ac3dab3 100644 --- a/Cpp/lib/AWG.h +++ b/Cpp/lib/AWG.h @@ -1,19 +1,194 @@ #pragma once -#include "dlltyp.h" -#include "regs.h" -#include "spcerr.h" -#include "spcm_drv.h" +#include "driver_header/dlltyp.h" +#include "driver_header/regs.h" +#include "driver_header/spcerr.h" +#include "driver_header/spcm_drv.h" +#include <string> +#include <set> +#include <map> + +class AWG { + /** + * Class for handling AWG M4i.6622-x8 . + * Manual can be found at + * https://spectrum-instrumentation.com/products/details/M4i6622-x8.php + */ + + // collection of special constants to help with setting options + const enum class CHANNEL_STOPLVL : int32 { + ZERO = SPCM_STOPLVL_ZERO, + LOW = SPCM_STOPLVL_LOW, + HIGH = SPCM_STOPLVL_HIGH, + HOLDLAST = SPCM_STOPLVL_HOLDLAST, + }; + const enum class REPLAY_MODE : int32 { + SINGLE = SPC_REP_STD_SINGLE, + MULTI = SPC_REP_STD_MULTI, + GATE = SPC_REP_STD_GATE, + SINGLERESTART = SPC_REP_STD_SINGLERESTART, + SEQUENCE = SPC_REP_STD_SEQUENCE, + FIFO_SINGLE = SPC_REP_FIFO_SINGLE, + FIFO_MULTI = SPC_REP_FIFO_MULTI, + FIFO_GATE = SPC_REP_FIFO_GATE, + }; + const enum class BUFFER_TYPE : int32 { + DATA = SPCM_BUF_DATA, + ABA = SPCM_BUF_ABA, + TIMESTAMP = SPCM_BUF_TIMESTAMP, + }; + const enum class TRANSFER_DIR : int32 { + PCTOCARD = SPCM_DIR_PCTOCARD, + CARDTOPC = SPCM_DIR_CARDTOPC, + CARDTOGPU = SPCM_DIR_CARDTOGPU, + GPUTOCARD = SPCM_DIR_GPUTOCARD, + }; + const enum class CLOCK_MODE : int32 { + INTPLL = SPC_CM_INTPLL, + EXTREFCLK = SPC_CM_EXTREFCLOCK, + }; + const enum class TRIGGER_MASK : int32 { + NONE = SPC_TMASK_NONE, + SOFTWARE = SPC_TMASK_SOFTWARE, + EXT0 = SPC_TMASK_EXT0, + EXT1 = SPC_TMASK_EXT1, + }; + const enum class TRIGGER_MODE : int32 { + NONE = SPC_TM_NONE, + POS = SPC_TM_POS, + NEG = SPC_TM_NEG, + POS_REARM = SPC_TM_POS | SPC_TM_REARM, + NEG_REARM = SPC_TM_NEG | SPC_TM_REARM, + BOTH = SPC_TM_BOTH, + HIGH = SPC_TM_HIGH, + LOW = SPC_TM_LOW, + WINENTER = SPC_TM_WINENTER, + WINLEAVE = SPC_TM_WINLEAVE, + INWIN = SPC_TM_INWIN, + OUTSIDEWIN = SPC_TM_OUTSIDEWIN, + }; + const enum class SEQ_LOOPCONDITION : int64 { + ALWAYS = SPCSEQ_ENDLOOPALWAYS, + ONTRIG = SPCSEQ_ENDLOOPONTRIG, + END = SPCSEQ_END, + }; + + // collection of card status for printouts + const std::map<int, std::string> STATUS_NAMES = { + {M2STAT_CARD_TRIGGER, "card received trigger"}, + {M2STAT_CARD_READY, "card ready"}, + {M2STAT_DATA_BLOCKREADY, "data block ready"}, + {M2STAT_DATA_END, "data transfer ended"}, + {M2STAT_DATA_OVERRUN, "data transfer overrun"}, + {M2STAT_DATA_ERROR, "data transfer error"}, + }; -class AWG -{ private: - typedef struct // dirty struct containing much card info - { - drv_handle cardHandle; + // card information + drv_handle pCardHandle; + int32 cardIdx; + int32 serialNumber; + int64 instMemSize; + int32 bytesPerSample; + int64 maxSampleRate; - } CARDINFO; + // setting information + std::set<int> activeChannels; + void checkError(); + void* getPageAlignedMem(uint64 memSize); + void freePageAlignedMem(void* pMem, uint64 memSize); public: - AWG(int cardID) noexcept; + // functions + AWG() noexcept; + ~AWG(); + + // basic card setting + void open(int openIndex); + bool isOpen(); + void close(); + void reset(); + int getCardIdx(); + int getSerialNumber(); + int64 getInstMemSize(); + int getBytesPerSample(); + int64 getMaxSampleRate(); + void setSampleRate(int64 sampleRate); + int64 getSampleRate(); + + // channel output control + void setActiveChannels(std::set<int> channels); + void setChannelAmp(int ch, int amp); + void setChannelStopLvl(int ch, CHANNEL_STOPLVL stopLvl); + void toggleChannelOutput(int ch, bool enable); + void setChannel(int ch, int amp, CHANNEL_STOPLVL stopLvl, bool enable); + int getChannelCount(); + std::set<int> getChannelActivated(); + void setChannelDiffMode(int chPair, bool enable); + void setChannelDoubleMode(int chPair, bool enable); + + // card status control + void writeSetup(); + void cardRun(); + void cardStop(); + void waitReady(); + void setTimeOut(int time); // ms + int getTimeOut(); + void printCardStatus(); + + // card trigger control + void toggleTrigger(bool enable); + void forceTrigger(); + void waitTrigger(); + void setTrigMaskOr(std::initializer_list<TRIGGER_MASK> trigMasks); + void setTrigMaskAnd(std::initializer_list<TRIGGER_MASK> trigMasks); + void setTrigMode(int trigChannel, TRIGGER_MODE trigMode); + void setTrigTerm(int term); + int getTrigTerm(); + void setTrigCoupling(int channel, int coupling); + int getTrigCoupling(int channel); + void setTrigLvl(int channel, int level); + int getTrigLvl(int channel); + void setTrigRearmLvl(int channel, int level); + int64 getTrigRearmLvl(int channel); + + // replay mode control + //void setReplayMode(REPLAY_MODE mode); + //void setMemSize(int64 sampleSize); + //int64 getMemsize(); + //void setLoop(int nLoop); + //int64 getLoop(); + void initReplayModeSeq(int nSeg); + void setSeqModeStep( + uint64 step, + uint64 segment, + uint64 nextStep, + uint64 nLoop, + SEQ_LOOPCONDITION condition + ); + void writeSeqModeSegment( + void* pDataBuffer, + int size, + int segment + ); + + // data transfer + void prepDataTransfer( + void* dataBuffer, + uint64 bufferLen, + BUFFER_TYPE bufType = BUFFER_TYPE::DATA, + TRANSFER_DIR dir = TRANSFER_DIR::PCTOCARD, + uint32 notifySize = 0, + uint64 brdMemOffs = 0 + ); + void startDataTransfer(); + void waitDataTransfer(); + void stopDataTransfer(); + + // clock control + void setClockMode(CLOCK_MODE cm); + int32 getClockMode(); + void setRefClkFreq(int64 frequency); + void setClockOut(bool enable); + int64 getClockOutFreq(); }; diff --git a/Cpp/lib/driver_header/SpcStdNT.lib b/Cpp/lib/driver_header/SpcStdNT.lib new file mode 100644 index 0000000000000000000000000000000000000000..50f6693e5246032570317a8b12db430d42499262 Binary files /dev/null and b/Cpp/lib/driver_header/SpcStdNT.lib differ diff --git a/Cpp/lib/dlltyp.h b/Cpp/lib/driver_header/dlltyp.h similarity index 100% rename from Cpp/lib/dlltyp.h rename to Cpp/lib/driver_header/dlltyp.h diff --git a/Cpp/lib/errors.h b/Cpp/lib/driver_header/errors.h similarity index 100% rename from Cpp/lib/errors.h rename to Cpp/lib/driver_header/errors.h diff --git a/Cpp/lib/driver_header/readme.txt b/Cpp/lib/driver_header/readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..0f2ebceb1bf87aa82ab71791e7b04b11e906c07d --- /dev/null +++ b/Cpp/lib/driver_header/readme.txt @@ -0,0 +1,71 @@ +************************************************************************** +c_header directory (c) Spectrum GmbH +************************************************************************** + +The directory contains all header and library files for all Spectrum +drivers for ISA, PCI, PCI-X, PCIe, cPCI and PXI cards. + +************************************************************************** + + + +Common header files used by all drivers +--------------------------------------- + +dlltyp.h: definitions common for all Spectrum drivers and card types. This + header tries to examine the type of compiler and then defines common + data types that have the same length under all compilers and all + operating systems. + +regs.h: software register and constants definition for all Spectrum + drivers. + +spcerr.h: error codes of all Spectrum drivers. Until may 2004 this file was + errors.h. Name has been changed because errors.h has been already in + use by windows. + + + +Library and Header files of driver for ISA/PCI/MI/MC/MX cards +------------------------------------------------------------- + +spcioctl.inc: linux include file to access driver functions via kernel + calls. Is needed by all linux based programs that access one of the + ISA/PCI/MI/MC/MX Spectrum cards + +errors.h: former error file. Thsi file is just included because of + compatibility reasons with old projects. Please use spcerr.h + +spectrum.h: header file that contains all the prototypes of the driver + functions + +spectrum.lib: library file for Microsoft Visual C++ for the spectrum + driver DLL. Calling type is c-call. + +SpcStdNT.lib: library file for other compilers for the spectrum + driver DLL. Calling type is stdcall. + +spclib_bcc.lib: library for Borland C++ Builder for the spectrum + driver DLL. + + + +Library and Header files of driver for SPCM driver based cards +------------------------------------------------------------- + +spcm_drv.h: header file that contains all the prototypes of the + driver functions of the spcm driver + +spcm_win32_msvcpp.lib: library file for the Microsoft Visual C++ + compiler. Calling type is stdcall. + +spcm_win32_bcppb.lib: library file for the Borland C++ Builder + compiler + +spcm_win32_cvi.lib: library file for National Instruments + LabWindows/CVI compiler + +spectrum_comp.lib: library file of the compatibility DLL that + simulates MI cards when findng M2i cards. Please include + this file instead of spectrum.lib for all projects that + should use M2i cards with the MI software interface diff --git a/Cpp/lib/regs.h b/Cpp/lib/driver_header/regs.h similarity index 100% rename from Cpp/lib/regs.h rename to Cpp/lib/driver_header/regs.h diff --git a/Cpp/lib/spcerr.h b/Cpp/lib/driver_header/spcerr.h similarity index 100% rename from Cpp/lib/spcerr.h rename to Cpp/lib/driver_header/spcerr.h diff --git a/Cpp/lib/driver_header/spcioctl.inc b/Cpp/lib/driver_header/spcioctl.inc new file mode 100644 index 0000000000000000000000000000000000000000..cf7db32c93114894a7ce83b151179d60dfe3248f --- /dev/null +++ b/Cpp/lib/driver_header/spcioctl.inc @@ -0,0 +1,93 @@ +// **************************************************************************** +// spcioctl.inc +// **************************************************************************** +// include source code for easy access of the ioctl and data transfer commands +// of the spectrum driver. +// **************************************************************************** + + + +// ----- ioctl_GetParam ----------------------------------- +int16 SpcGetParam (int hDrv, int32 lReg, int32 *plValue) + { + int32 lData[2]; + int16 nRet; + + lData[0] = lReg; + nRet = ioctl (hDrv, GETPARAM, lData); + *plValue = lData[1]; + + return nRet; + } + +// ----- ioctl_SetParam ----------------------------------- +int16 SpcSetParam (int hDrv, int32 lReg, int32 lValue) + { + int32 lData[2]; + + lData[0] = lReg; + lData[1] = lValue; + + return ioctl(hDrv, SETPARAM, lData); + } + + + +// ----- ioctl_GetAdr ----------------------------------- +int16 SpcGetAdr (int hDrv, int32 lReg, void** ppvAdr) + { + int16 nRet; + _SETGETADR stSetGetAdr; + + stSetGetAdr.lReg = lReg; + nRet = ioctl (hDrv, GETADR, &stSetGetAdr); + (*ppvAdr) = stSetGetAdr.pvAdr; + + return nRet; + } + +// ----- ioctl_SetAdr ----------------------------------- +int16 SpcSetAdr (int hDrv, int32 lReg, void* pvAdr) + { + int16 nRet; + _SETGETADR stSetGetAdr; + + stSetGetAdr.lReg = lReg; + stSetGetAdr.pvAdr = pvAdr; + nRet = ioctl (hDrv, SETADR, &stSetGetAdr); + + return nRet; + } + + + +// ----- ioctl_GetData ------------------------------------ +int32 SpcGetData (int hDrv, int32 lCh, int32 lStart, int32 lLen, int16 nBytesPerSample, dataptr pvData) + { + int32 lRet; + int32 lData[1]; + lData[0] = lCh; + + ioctl (hDrv, SETCH, lData); + + lseek (hDrv, lStart * nBytesPerSample, SEEK_SET); + lRet = read (hDrv, pvData, nBytesPerSample * lLen); + + return lRet; + } + +// ----- ioctl_SetData ------------------------------------ +int32 SpcSetData (int hDrv, int32 lCh, int32 lStart, int32 lLen, int16 nBytesPerSample, dataptr pvData) + { + int32 lRet; + int32 lData[1]; + + lData[0] = lCh; + + ioctl (hDrv, SETCH, lData); + + lseek (hDrv, lStart * nBytesPerSample, SEEK_SET); + lRet = write (hDrv, pvData, nBytesPerSample * lLen); + + return lRet; + } diff --git a/Cpp/lib/driver_header/spclib_bcc.lib b/Cpp/lib/driver_header/spclib_bcc.lib new file mode 100644 index 0000000000000000000000000000000000000000..fe98a9d8b0a8f32f9a7d839251502e2fb362778a Binary files /dev/null and b/Cpp/lib/driver_header/spclib_bcc.lib differ diff --git a/Cpp/lib/spcm_drv.h b/Cpp/lib/driver_header/spcm_drv.h similarity index 100% rename from Cpp/lib/spcm_drv.h rename to Cpp/lib/driver_header/spcm_drv.h diff --git a/Cpp/lib/driver_header/spcm_win32_bcppb.lib b/Cpp/lib/driver_header/spcm_win32_bcppb.lib new file mode 100644 index 0000000000000000000000000000000000000000..2f022cdce6c5c1bb719ffbe9b8aca3821eba47f1 Binary files /dev/null and b/Cpp/lib/driver_header/spcm_win32_bcppb.lib differ diff --git a/Cpp/lib/driver_header/spcm_win32_cvi.lib b/Cpp/lib/driver_header/spcm_win32_cvi.lib new file mode 100644 index 0000000000000000000000000000000000000000..db3b2cd1fdaba87b14e2229f07063d7f8f34600b Binary files /dev/null and b/Cpp/lib/driver_header/spcm_win32_cvi.lib differ diff --git a/Cpp/lib/driver_header/spcm_win32_msvcpp.lib b/Cpp/lib/driver_header/spcm_win32_msvcpp.lib new file mode 100644 index 0000000000000000000000000000000000000000..33b1f5db3cf4072985782304eaf0e473598e706a Binary files /dev/null and b/Cpp/lib/driver_header/spcm_win32_msvcpp.lib differ diff --git a/Cpp/lib/driver_header/spcm_win64_msvcpp.lib b/Cpp/lib/driver_header/spcm_win64_msvcpp.lib new file mode 100644 index 0000000000000000000000000000000000000000..07dee37fde851192cad0f13cb88f61af9a7d26bd Binary files /dev/null and b/Cpp/lib/driver_header/spcm_win64_msvcpp.lib differ diff --git a/Cpp/lib/driver_header/spectrum.h b/Cpp/lib/driver_header/spectrum.h new file mode 100644 index 0000000000000000000000000000000000000000..77ca4fb47d8439b1b7707cd8010388c578b66b90 --- /dev/null +++ b/Cpp/lib/driver_header/spectrum.h @@ -0,0 +1,11 @@ +HEAD SpcInitPCIBoards (int16 *pnCount, int16 *pnPCIVersion); +HEAD SpcInitBoard (int16 nNr, int16 nTyp); +HEAD SpcSetParam (int16 nNr, int32 lReg, int32 lValue); +HEAD SpcGetParam (int16 nNr, int32 lReg, int32 *plValue); +HEAD SpcGetData (int16 nNr, int16 nCh, int32 lStart, int32 lLen, dataptr pvData); +HEAD SpcSetData (int16 nNr, int16 nCh, int32 lStart, int32 lLen, dataptr pvData); +HEAD SpcGetVersionInfo (char *pszBuffer, int nBufferLen); + +// these functions are only needed under 64 bit Linux to set FIFO adresses +HEAD SpcSetAdr (int16 nNr, int32 lReg, void* pvAdr); +HEAD SpcGetAdr (int16 nNr, int32 lReg, void** ppvAdr); diff --git a/Cpp/lib/driver_header/spectrum.lib b/Cpp/lib/driver_header/spectrum.lib new file mode 100644 index 0000000000000000000000000000000000000000..ac4873d485442d2a30c930fb1f86c29d5eae0528 Binary files /dev/null and b/Cpp/lib/driver_header/spectrum.lib differ diff --git a/Cpp/lib/driver_header/spectrum_comp.lib b/Cpp/lib/driver_header/spectrum_comp.lib new file mode 100644 index 0000000000000000000000000000000000000000..881e1c83ab1b5879c5d1de215b399b95a46c322b Binary files /dev/null and b/Cpp/lib/driver_header/spectrum_comp.lib differ diff --git a/Cpp/run.cpp b/Cpp/run.cpp index a048923511b6c8b4ea1adeaad485c69ccf93d3ce..1c912f7d4be16b094986425b5d59b1ecaeaf536b 100644 --- a/Cpp/run.cpp +++ b/Cpp/run.cpp @@ -2,10 +2,13 @@ // #include <iostream> +#include <set> +#include "lib/AWG.h" int main() { - std::cout << "Hello World!\n"; + auto b = 10; + std::cout << b << std::endl; } // Run program: Ctrl + F5 or Debug > Start Without Debugging menu