Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adi:Generic: Add a generic driver #13

Merged
merged 2 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 248 additions & 0 deletions +adi/+Generic/Rx.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
classdef Rx < adi.common.Rx & matlabshared.libiio.base & ...
adi.common.Attribute & adi.common.DeviceAttribute & adi.common.Channel
% Generic Precision ADC Class
% adi.Generic.Rx Receives data from the connected ADC
% The adi.Generic.Rx System object is a signal source that can receive
% data from any generic ADI precision ADC.
%
% rx = adi.Generic.Rx('adxxxx', 'ip:analog.local');
%
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this extra comment specifier needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be needed for generating the documentation using the scripts in CI/scripts/gen_docs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, do you mean line no. 9 specifically? I guess it's not required. Will remove it and see if documentation generates fine without it.


properties
ribdp marked this conversation as resolved.
Show resolved Hide resolved
% DeviceAttributeNames Device Attribute Names
% Array of device IIO attribute names as defined in the
% driver/firmware, and queried from the hardware
% at initialization time
DeviceAttributeNames

% ChannelAttributeNames Device Attribute Names
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: Channel attributes names

% Array of channel IIO attribute names as defined in the
% driver/firmware, and queried from the hardware
% at initialization time
ChannelAttributeNames
end

properties (Nontunable)
mphalke marked this conversation as resolved.
Show resolved Hide resolved
% SamplesPerFrame Samples Per Frame
% Number of samples per frame, specified as an even positive
% integer.
SamplesPerFrame = 1024
end

properties (Nontunable, Hidden)
ribdp marked this conversation as resolved.
Show resolved Hide resolved
channel_names = {'voltage0'}
Timeout = Inf
kernelBuffersCount = 1
dataTypeStr = 'int32'
phyDevName = 'adxxxx'
devName = 'adxxxx'
end

properties (Nontunable, Hidden, Constant)
Type = 'Rx'
ComplexData = false
end

properties (Hidden, Nontunable, Access = protected)
isOutput = false
end

properties(Hidden, Access = protected)
cachedAttrWrites = {}
end

methods

%% Constructor
function obj = Rx(Args)
arguments
Args.devName
Args.phyDevName
Args.uri
end

obj = [email protected]();
obj.enableExplicitPolling = false;
obj.EnabledChannels = 1;
obj.BufferTypeConversionEnable = true;
mphalke marked this conversation as resolved.
Show resolved Hide resolved

% Set device name, phyDevName, uri from input arguments
try
obj.devName = Args.devName;
obj.phyDevName = Args.phyDevName;
obj.uri = Args.uri;
catch
e = MException('MATLAB:notEnoughInputs',['Not enough input ' ...
'arguments. Specify devName, phyDevName and uri']);
throw(e)
end

% Determine datatype using trial and error
possibleDataTypeStr = {'int8', 'int16', 'int32', 'int64'};
for k = 1:length(possibleDataTypeStr)
try
obj.dataTypeStr = possibleDataTypeStr{k};
obj.setup();
break;
catch
end
end
assert(obj.ConnectedToDevice == 1, "Connection could not be established with the specified device")

% Check is the platform is running ADI Kuiper Linux.
% Presence of 'local,kernel' context attribute would
% indicate usage of Linux platform
isLinuxPLatform = false;
try
obj.iio_context_get_attr_value(obj.iioCtx, 'local,kernel');
isLinuxPLatform = true;
catch
end

release(obj);

% If platform is running ADI Kuiper Linux, kernelBuffersCount
% can be increased
if isLinuxPLatform
obj.kernelBuffersCount = 4;
mphalke marked this conversation as resolved.
Show resolved Hide resolved
disp("Detected ADI Kuiper Linux platform. Changing buffer count to 4..")
end

end

function flush(obj)
% Flush the buffer contents
flushBuffers(obj);
end

function SetDeviceAttrValue(obj, attr, val)
% Apply the device attribute value when device is
% connected
% rx.SetDeviceAttrValue('sampling_frequency', '1000000')
obj.cachedAttrWrites{end+1} = {attr, val};
end


function val = GetDeviceAttrValue(obj, attr)
% Fetch the device attribute value if device is
% connected.
% rx.GetDeviceAttrValue('sampling_frequency')
if obj.ConnectedToDevice
val = obj.getDeviceAttributeRAW(attr, 128);
end
end

function SetChannelAttrValue(obj, chnID, attr, val)
% Apply the channel attribute value when device is
% connected. To write to the first channel attribute use
% rx.SetChannelAttrValue('voltage0', 'scale', '2') or
% rx.SetChannelAttrValue(1, 'scale', '2')
if isnumeric(chnID)
chnID = obj.channel_names(chanID);
end
obj.cachedAttrWrites{end+1} = {chnID, attr, val};
end

function val = GetChannelAttrValue(obj, chnID, attr)
% Fetch the channel attribute value if device is
% connected. To get the first channel attribute value, use
% rx.GetChannelAttrValue('voltage0', 'scale') or
% rx.GetChannelAttrValue(1, 'scale')
if obj.ConnectedToDevice
if isnumeric(chnID)
chnID = obj.channel_names(chanID);
end
val = obj.getAttributeRAW(attr, chnID, obj.isOutput);
end
end

end

%% API Functions
methods (Hidden, Access = protected)

function setupInit(obj)

% Write all attributes to device once connected through set
ribdp marked this conversation as resolved.
Show resolved Hide resolved
% methods
% Do writes directly to hardware without using set methods.
% This is required since Simulink support doesn't support
% modification to nontunable variables at SetupImpl

%Fetch all the attributes and channel names
if ~isempty(obj.channel_names)
obj.fetch_channel_names();
end

if isempty(obj.DeviceAttributeNames)
obj.fetch_device_attributes();
end

if isempty(obj.ChannelAttributeNames)
obj.fetch_channel_attributes();
end

% Apply attribute settings in hardware
if ~isempty(obj.cachedAttrWrites)
for i = 1:length(obj.cachedAttrWrites)
if length(obj.cachedAttrWrites{i}) == 2
% Apply device attribute
attr = obj.cachedAttrWrites{i}{1};
val = obj.cachedAttrWrites{i}{2};
obj.setDeviceAttributeRAW(attr, val);
elseif length(obj.cachedAttrWrites{i}) == 3
% Apply channel attribute
chan = obj.cachedAttrWrites{i}{1};
attr = obj.cachedAttrWrites{i}{2};
val = obj.cachedAttrWrites{i}{3};
obj.setAttributeRAW(chan, attr, val, obj.isOutput);
end
end
end

obj.cachedAttrWrites = {};

end

function fetch_channel_names(obj)
% Update the channel names
obj.channel_names = {};
phydev = getDev(obj, obj.devName);
chanCount = obj.iio_device_get_channels_count(phydev);
for c = 1:chanCount
chanPtr = obj.iio_device_get_channel(phydev, c - 1);
obj.channel_names{end + 1} = obj.iio_channel_get_name(chanPtr);
end
end

function fetch_device_attributes(obj)
% Store the attribute names
devPtr = obj.getDev(obj.phyDevName);
devAttrCnt = obj.iio_device_get_attrs_count(devPtr);
for i=1:devAttrCnt
obj.DeviceAttributeNames{end+1} = obj.iio_device_get_attr(devPtr, i-1);
end
end

function fetch_channel_attributes(obj)
% Store channel attribute names, if there's channels present
devPtr = obj.getDev(obj.phyDevName);

if ~isempty(obj.channel_names)
% Fetch first channel
chanPtr = calllib(obj.libName, 'iio_device_get_channel', devPtr, 0);

% Fetch count of channel attributes
chanAttrCnt = obj.iio_channel_get_attrs_count(chanPtr);

% Fetch each attribute for the channel
for j=1:chanAttrCnt
obj.ChannelAttributeNames{j} = obj.iio_channel_get_attr(chanPtr, j-1);
end
end

end


end
end
37 changes: 37 additions & 0 deletions examples/Generic_DataCapture.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
%% Script for capturing buffers of data from a connected IIO device

% Instantiate the Generic Rx system object, and specify the device name, uri
% and phyDevName in the manner shown below
rx = adi.Generic.Rx("devName", 'ad4630-24', 'phyDevName', 'ad4630-24',...
'uri', 'ip:analog.local');

rx.SamplesPerFrame = 4096; % Using values less than 3660 can yield poor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should comment be at top of expression similar to other comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will update this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious how did we come up with value 3660?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's just a heuristic obtained from testing parts in the HSX/TRX toolbox, and I thought to adopt the same here. @tfcollins - is this comment valid in only specific conditions/parts? Is it okay to keep it here?

% performance, generally

% Enable channels for data capture
rx.EnabledChannels = [1];

% Display device and channel attribute names
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are attributes always displayed and is it necessary to display them? Should this be enabled just for debug purpose?

Copy link
Contributor Author

@ribdp ribdp Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought to leave these here so that when running the example script out of the box for a specific part, the attributes are also printed out for the user to take note of and use subsequently. Anyone using the generic example would definitely also be able to modify the flow as per their needs.

rx.DeviceAttributeNames
rx.ChannelAttributeNames

% Read and write attribute values
% For example, with ad4630-24
% rx.GetDeviceAttrValue('sampling_frequency')
% rx.SetDeviceAttrValue('sampling_frequency', '2000000');
% rx.SetChannelAttrValue('differential0', 'hardwaregain', '2');

% Capture data
data = rx();

% Plot samples
enabledChannels = size(data, 2);
figure(1);
for i = 1:enabledChannels
subplot(enabledChannels, 1, i);
plot(data(1:rx.SamplesPerFrame, i));
mphalke marked this conversation as resolved.
Show resolved Hide resolved
title("Channel " + num2str(rx.EnabledChannels(i)));
end

% Delete the system object
release(rx);
Loading