% 2009-12-18. Remote control of a function-generator by GPIB from Matlab ICT. % Hewlett-Packard 33120A '15MHz function / arbitrary waveform generator'. % 2010-02-16. Much adapted to focus on arbitrary waveforms. % % Nathaniel. % % %% open the function generator instrument fgen = 'GPIB0::10::INSTR'; fprintf('\nOpening connection to Function Generator (HP33120A): %s\n', fgen); ih.fg = visa('ni', fgen); set(ih.fg, 'TimeOut', 3.0, ... 'ByteOrder', 'BigEndian', ... 'InputBufferSize', 2^18, ... 'OutputBufferSize', 2^18, ... 'RecordDetail', 'verbose', ... 'RecordName', 'comms_log_funcgen.txt'); fopen(ih.fg); record(ih.fg, 'on'); % ask the device's opinion of itself fgen_idn = query(ih.fg,'*IDN?\n', '%s\n'); fgen_idn = regexprep(fgen_idn, '[\r\n]', ''); fprintf('I believe I am a: "%s"\n\n', fgen_idn); % reset fprintf(ih.fg, '%s\n', '*RST'); % gets rid of all errors messages %fprintf(ih.fg, '%s\n', '*CLS'); %% check and remove error messages % ask for any error messages fprintf('error messages:\n'); for n=1:20, nexterr = query(ih.fg, 'SYSTEM:ERROR?', '%s\n'); fprintf('%s\n', nexterr); if regexp(nexterr, '^\+0,'), break; end end %% set some simple features % the function generator has output impedance 50ohm: if you % tell it the load is 50ohm then it will double its internal % voltage to make the voltage /at the load/ be correct; if you % tell it the load is INF (open) it will use just the internal % voltage that is set fprintf(ih.fg, '%s\n', 'OUTPUT:LOAD 50'); % setting sync on means that the SYNC connector (just above the % OUTPUT connector) is active, producing a TTL (digital) signal % synchronised with the output waveform fprintf(ih.fg, '%s\n', 'OUTPUT:SYNC ON'); % units to use for waveform voltage (offset voltage is always % just in volts): Vpp means peak relative to zero % {VPP|VRMS|DBM} fprintf(ih.fg, '%s\n', 'VOLT:UNIT VPP'); %% set a simple function as output (an ALTERNATIVE to arbitrary waveform) % type of function: % {SINusoid|SQUare|TRIangle|RAMP|NOISe|DC|USER} fprintf(ih.fg, '%s\n', 'FUNCTION:SHAPE RAMP'); % frequency: % min 0.0001Hz, max 100kHz(ramp|triangle),15MHz(sine|square) fprintf(ih.fg, '%s\n', 'FREQUENCY 60.00'); % waveform amplitude (peak relative to zero, if VOLT:UNIT is VPP): % maximum VPP is 10V for 50ohm output-load, 20V for INF output-load: fprintf(ih.fg, '%s\n', 'VOLTAGE 10.00'); % dc offset, in volts: fprintf(ih.fg, '%s\n', 'VOLTAGE:OFFSET 0.00'); %% arbitrary waveforms: description, list them, delete them % the arbitrary waveforms are vectors of anything from 8 to 16000 points % long, each point having 2^12 possible values; the function generator % can keep repeating this whole set of time-samples at a user-specified % frequency and magnitude % ask about built-in waveforms (other than the basic square/sine etc functions) fprintf(['available "built-in": ', query(ih.fg, 'DATA:CAT?\n', '%s\n')]); % ask about user-loaded waveforms fprintf(['available "user": ', query(ih.fg, 'FUNC:USER?\n', '%s\n')]); % remove a user-loaded waveform %fprintf(ih.fg, '%s', 'DATA:DEL SINC'); % remove all user-saved waveforms and volatile memory %fprintf(ih.fg, '%s', 'DATA:DEL:ALL'); %% copy an 'arbitrary waveform' to the function generator % make an arbitrary waveform % how many points Npoints = 256; % max is 16000; t = linspace(0,1,Npoints); % the waveform s = 2*sin(6*pi*t)+2*cos(12*pi*t).*3.*abs(t-0.5); % convert waveform to be suitable for the transfer methods snorm = s/max(abs(s)); % normalise signal to -1--+1 sint12 = round(2046*snorm); % convert to integers >-2047, <+2047 % choose transfer method method = 3; % to_o = get(ih.fg, 'TimeOut'); % set(ih.fg, 'TimeOut', 10.0); started = clock; switch method case 1, % ascii: floating-point values from -1--+1 s_str_flt = [ sprintf('%6.4f,', snorm(1:end-1)), ... sprintf('%6.4f', snorm(end)) ]; fprintf(ih.fg, 'DATA VOLATILE, %s\n', s_str_flt); case 2, % ascii: integer values from -2047 to +2047 s_str_int = [ sprintf('%d,', sint12(1:end-1)), ... sprintf('%d', sint12(end)) ]; fprintf(ih.fg, 'DATA:DAC VOLATILE, %s\n', s_str_int); case 3, % binary: signed 16bit integers from -2047--+2047 (big-endian) % the 'binary header' is the byte for ascii '#' followed by a % a byte for the ascii character giving the number of following % ascii characters, then the bytes for these following characters % which give the number of following bytes of binary data nnn = sprintf('%d', 2*Npoints); % number of bytes in data (as text) N = sprintf('%d', length(nnn)); % number of characters in 'nnn' binhead = double(['DATA:DAC VOLATILE, #',N,nnn]); % make the header bindata = zeros(1,2*length(s)); % array for the data bytes for ni = 1:length(s), si = sint12(ni); % if the value is positive, just use it directly, as the % dec2bin function can cope with positive but not negative values; % if it is negative, it needs to be converted to the unsigned % positive integer that has the same bytes as the signed % (2's complement) negative integer: this is done by inverting % all the bits then adding 1 if si<0, si = double( uint16(1) + bitxor(uint16(abs(si)),uint16(2^16)) ); end %fprintf(' %d: %+1d*%16s %16s\n', ... % ni, double(sign(s(ni))), dec2bin(abs(s(ni)),16), dec2bin(si,16)) %plot(s); hold on; %plot((bindata(1:2:end)>127).*-2^16+(bindata(1:2:end)*256+bindata(2:2:end)),'g'); % now split the number into high byte and low byte if 0, %convert the number to 16bits binary form (in ascii!) % and split it into high byte and low byte si_txt = dec2bin(si,16); bindata(2*ni-1) = bin2dec(si_txt(1:8)); bindata(2*ni) = bin2dec(si_txt(9:16)); else % or, more elegantly, done numerically bindata(2*ni-1) = (si - mod(si,256))/256; bindata(2*ni) = mod(si,256); end end fwrite(ih.fg, [binhead,bindata], 'uint8'); otherwise error('invalid "method" for copying the waveform'); end fprintf('tranferring %d values by method %d took %.2fs\n', ... Npoints, method, etime(clock,started)); % set(ih.fg, 'TimeOut', to_o); % ask for any error messages fprintf('error messages:\n'); for n=1:20, nexterr = query(ih.fg, 'SYSTEM:ERROR?', '%s\n'); fprintf('%s\n', nexterr); if regexp(nexterr, '^\+0,'), break; end end % copy the waveform now in volatile memory into the named storage 'T_E_S_T' (<=8chars) fprintf(ih.fg, '%s\n', 'DATA:COPY T_E_S_T, VOLATILE') %% select a stored waveform to use % the stored waveforms are of an arbitrary % waveform: built-in ones (always available) are: % {SINC|NEG_RAMP|EXP_RISE|EXP_FALL|CARDIAC|ARB1} % and the waveform that might have been copied into the % volatile memory in the previous section (`copy arbitrary % waveform to function generator') is called VOLATILE: % select one of the arbitrary waveforms %fprintf(ih.fg, '%s\n', 'FUNC:USER VOLATILE') fprintf(ih.fg, '%s\n', 'FUNC:USER T_E_S_T'); % frequency: % min 0.0001Hz % max: 8--8192 points: 5MHz % 8192--12287 points: 2.5MHz % 12288--16000 points: 200kHz fprintf(ih.fg, '%s\n', 'FREQUENCY 60.00'); % waveform amplitude (peak relative to zero, if VOLT:UNIT is VPP); % maximum VPP is 10V for 50ohm output-load, 20V for INF output-load; % VPP corresponds to the +-2047 range that the memory can have, not to % the highest point actually in the selected waveform fprintf(ih.fg, '%s\n', 'VOLTAGE 4.00'); % dc offset, in volts: fprintf(ih.fg, '%s\n', 'VOLTAGE:OFFSET 0.00'); % USE the selected arbitrary waveform as output fprintf(ih.fg, '%s\n', 'FUNC:SHAP USER'); %% close the connection fclose(ih.fg); delete(ih.fg); ih = rmfield(ih, 'fg');