% Copyright (c) 2023 SpinCore Technologies, Inc.
% http://www.spincore.com
% 
% This software is provided 'as-is', without any express or implied warranty. 
% In no event will the authors be held liable for any damages arising from the 
% use of this software.
% 
% Permission is granted to anyone to use this software for any purpose, 
% including commercial applications, and to alter it and redistribute it
% freely, subject to the following restrictions:
% 
% 1. The origin of this software must not be misrepresented; you must not
% claim that you wrote the original software. If you use this software in a
% product, an acknowledgment in the product documentation would be appreciated
% but is not required.
% 2. Altered source versions must be plainly marked as such, and must not be
% misrepresented as being the original software.
% 3. This notice may not be removed or altered from any source distribution.
%

%
% \file pbesr_pro_long_delay_test.m adapted from pbesr_pro_long_delay_test.c
% \brief PulseBlasterESR-PRO example program
% 
% The following program tests the functionality of the long delay opcode. The
% long delay opcode determines what the delay value is by multipling the given
% delay value by the data field. Thus, this program will output a pulse train
% with period (100ns*long_delay)
% 
% This program takes two optional command-line arguments.
% The first is the clock frequency, the default is 400MHz.  
% The second is the number of delay loops in the pulse train, the default is 5.  
% 
% The largest value for the delay field of pb_inst is 850ns.
% For longer delays, use the LONG_DELAY instruction.  The maximum value
% for the data field of the LONG_DELAY is 1048576.  Even longer delays can
% be achieved using the LONG_DELAY instruction inside of a loop.
%  
% \ingroup ESRPRO
clc; clear;

global SPINAPI_DLL_NAME

SPINAPI_DLL_NAME = 'spinapi64';

addpath('../MATLAB_SpinAPI'); % MATLAB_SpinAPI Folder path address

SpinAPI_Drive = 'C:'; % Modify to the hard drive letter that contains SpinAPI

% Loads spinapi64 library and headers
% All files are located in SpinAPI folder
if ~libisloaded('spinapi64')  % Needs the MinGW-w64 add-on
    loadlibrary([SpinAPI_Drive,'\SpinCore\SpinAPI\lib\spinapi64.dll'], ...
                 [SpinAPI_Drive,'\SpinCore\SpinAPI\include\spinapi.h'], 'addheader',...
                 [SpinAPI_Drive,'\SpinCore\SpinAPI\include\pulseblaster.h'], 'addheader',...
                 [SpinAPI_Drive,'\SpinCore\SpinAPI\include\radioprocessor.h'], 'addheader',...
                 [SpinAPI_Drive,'\SpinCore\SpinAPI\include\dds.h']);
end

% Load constants to the workspace as variables
load('CONST.mat');

argv = [1];
argc = arg(argv);
clock_freq = 500;
DELAY_LOOP = 5;

% Uncommenting the line below will generate a debug log in your current
% directory that can help debug any problems that you may be experiencing   
% pb_set_debug(1);

fprintf('Copyright (c) 2023 SpinCore Technologies, Inc.\n\n');

fprintf('Using SpinAPI library version %s \n', pb_get_version());


numBoards = detect_boards(); % If there is more than one board in the system, have the user specify.
if numBoards > 1             % Request the board number to use from the user
    select_boards(numBoards)
end

if pb_init() ~= 0
  error('Error initializing board: %s\n', pb_get_error());
end


% Set the clock frequency.
if length(argv) > 1
    clock_freq = (argv(2));
else
    % User input clock
    clock_freq = input_clock();
    fprintf('\n');
end
 	
if (length(argv) > 2)
    delay_loops = (argv(3));
else
    fprintf(['Please enter the number of loops for the long delay instruction.\n',...
            'The minimum number of loops for this program to run is 3.\n',...
            'The resulting period will be 200*(number of loops)ns.\n']);
    delay_loops = input('Number of long delay loops: ');
    fprintf('\n');
    while delay_loops < 3
        fprintf('The number of loops has to be greater that 2.\n');
        delay_loops = input('Number of long delay loops: ');
    end
end 

fprintf('Clock frequency: %.2fMHz\n\n', clock_freq);
fprintf('BNCs 0-3 should output a 50%% duty cycle square wave with period %uns\n\n', delay_loops * 100 * 2);

% Tell driver what clock frequency the board uses
pb_core_clock(clock_freq);

% Begin pulse program
pb_start_programming(PULSE_PROGRAM);

% Instruction format
% int pb_inst(int flags, int inst, int inst_data, int length)

% Instruction 0 - continue to inst 1 in (100ns*delay_loop)
start = pb_inst_pbonly(bitor(hex2dec(ON),hex2dec('F')), LONG_DELAY, delay_loops, 100.0 * ns);

% Instruction 1 - continue to instruction 2 in (100ns*(delay_loop-1))
pb_inst_pbonly(0, LONG_DELAY, delay_loops - 1, 100.0 * ns);

% Instruction 2 - branch to start
pb_inst_pbonly(0, BRANCH, start, 100.0 * ns);

% End of programming registers and pulse program
pb_stop_programming();

%Trigger pulse program
pb_reset();
pb_start();

pb_close();

fprintf('Press enter to continue...\n')
pause;

% Unload library and free memory
unloadlibrary('spinapi64');


function argc = arg(varargin)
    argc = nargin;
end

function numBoards = detect_boards()
    numBoards = pb_count_boards();
    if numBoards < 1
        error(['No Boards were detected in your system. Verify that the board is', ...
            ' firmly secured in the PCI slot.']);
    end
end

function select_boards(numBoards)
    while true
        fprintf('Found %d boards in your system. ', numBoards);
        fprintf('Which board should be used? (0-%d): ', (numBoards-1));
        choice = input('');
        if choice < 0 || choice > numBoards-1
            fprintf('Invalid Board Number (%d).\n', choice);
        else 
            pb_select_board(choice);
            fprintf('Board %d selected.\n',choice);
            break
        end 
    end 
end 

function clock = input_clock()
    clock = input('\nPlease enter internal clock frequency (MHz): ');
end