IO323 - Analog Loopback External Triggered
The IO323 I/O module is a fast, sequential-sampling 16-bit analog input and output module, complete with dedicated Simulink® driver blocks. This I/O module also contains digital pins, which are also used by this example to show the possibility to generate trigger signals for the analog part.
This example demonstrates the analog part of an IO323 module, which offers DAC signal generation and ADC measurement functionality. The focus is to demonstrate the setup where the IO323 module generates user-defined analog output signals on the DAC and measures them with the ADCs and uses trigger signals for ADC and DAC generation.
Setup
Prerequisites
You will require the following to run this example:
- Speedgoat real-time target machine with one IO323 I/O module installed
- A Speedgoat configuration file for the IO323 module. Every IO323 configuration file always contains analog functionality
- Connector cable from the I/O module to the terminal boards
- 2x 50-pin terminal board with jumper wires (one for the analog and one for the digital part)
- Optional: Multimeter to measure the DAC voltages
Test Setup - Analog Part
Connect the IO323 module with the analog cable to a 50-pin terminal board, locate the DAC channels 1–8 and connect them to analog inputs ADC1–ADC4, 1 (single-ended) and 3 (single-ended) each. Note that the IO323 ADC channels can be switched between single-ended and differential ADC measurement. Analog ADC1–ADC4 2 (single-ended) and 4 (single-ended) will therefore be connected to GND as well.
Test Setup - Digital Part
Connect the IO323 module with the digital cable to a 50-pin terminal board, locate the DIO channels 20–24 and connect them to the analog trigger IOs. This part is only needed if you want to use external analog triggering.
Initialize and Open the Simulink Model
Open the Simulink model and choose your appropriate connection by commenting out the unwanted configuration block.
modelName = 'sgMdl_IO323_AnalogLoopback_Triggered';
% set the general model time step and the trigger signals to 1ms
set_param(modelName, 'FixedStep', '1e-3');
set_param([modelName, '/ADCTriggers'], 'ts', '1e-3');
set_param([modelName, '/DACTriggers'], 'ts', '1e-3');
% set the ADC and the DAC sampling time to 10ms (so the trigger signals are faster)
set_param([modelName, '/IO32x Analog output'], 'ts', '10e-3');
set_param([modelName, '/IO32x Analog input'], 'ts', '10e-3');
Before the Simulink model can be built, the I/O module and configuration file must be specified in the IO3xx Setup block, as this example can be executed on different configurable I/O modules. First, open the mask of the IO3xx Setup block and select your I/O module from the drop-down. Once the mask has extended, select the configuration file that supports the required functionalities.
With the Pin Mapping button you can now check where the functionalities are located.
Model Description
General Setup
The model generates 8 signals that are sent to the analog outputs. Each signal is a sawtooth; while the first signal is in the range of -10 to 0, the second signal is in the range of -9 to 1, and so on. Given the loopback wiring, the same voltage values are read again on the analog inputs.
ADC (Analog Inputs) Settings
To see the setup and parametrization of the ADC channels, open the IO32x Analog Input block. speedgoat.model.highlight([modelName, '/IO32x Analog input'],'Open',true);
Tab: ADC01
In this tab of the IO32x Analog Input block, you can set the channels to be measured by writing them to Channel vector. Note that the first channel used must always be channel 1 and only the following adjacent channels can be selected (e.g., channel 1, 2, 3, 4 is possible, but channel 2, 4 is not valid). You can select an ADC range for this group using the Range selection. With the Input coupling selection, you can select this ADC group to be either single-ended or differential. The Trigger mode defines if the ADC either shall trigger a new measurement at model step-time (Initiator) or if the dedicated digital input channel shall trigger a new ADC measurement (Target). The tab ADC01 is used to configure the 8 ADC channels of this ADC group. All the other ADC channels of the other 3 ADC groups can be individually configured in the tab ADC02, ADC03 and ADC04. Note that in this example, ADC02 and ADC04 are configured in Differential input coupling mode.
DAC (Analog Outputs) Settings
speedgoat.model.highlight([modelName, '/IO32x Analog output'],'Open',true);
You can set the channels to generate output signals by specifying them in the Channel vector field. The Trigger mode defines if the DAC either shall trigger a new output at model step-time (Initiator) or if the dedicated digital input channel shall trigger a new DAC measurement (Target). With the Initial Values field, you can define the behavior on the digital outputs when the real-time application is loaded, with the Reset to Initial Value on model stop you can choose if this should also apply when stopping the real-time application. In this example, DAC channels 1/3/5/7 reset to their initial value at stop, while the other DAC channels hold their last value.
Analog Input (ADC) and Analog Output (DAC) Blocks
Note that the IO32x Analog Input block is set to the highest priority, to ensure the ADC conversion is done before a new DAC value is generated. % Mandatory: set the Analog Input Block Priority to 1 and the Analog Output Block Priority to 2
if sg.utils.compareMatlabVersion('>', 'R2022a')
set_param([modelName, '/IO32x Analog input'], 'priority', '1');
set_param([modelName, '/IO32x Analog output'], 'priority', '2');
% sorted execution order in the model
Simulink.BlockDiagram.getExecutionOrder(modelName, 1);
warning('The priority setting is not supported in this MATLAB release, you have to enable it manually.');
Note that in case your MATLAB Release is 2022a or earlier, you have to set the priorities manually in the model:
- Right-click on the model-block IO32x Analog input, select Properties... and set the Priority: to 1
- Right-click on the model-block IO32x Analog output, select Properties... and set the Priority: to 2
- Right-click anywhere in the model and select Other Displays -> Blocks -> Sorted Execution Order, your model will now show the execution order of all blocks. The order execution number of the IO32x Analog input (higher priority) must be lower than IO32x Analog output. Note that you would have to select Task 1 in this example.
External Triggered Behavior
This example shall demonstrate the IO323 analog functionality using the trigger signals for the ADC and the DAC unit. This means whenever a trigger input is high...
- the DAC outputs will update to the last value that has been written from the real-time application to the I/O module
- the IO323 samples and converts the current analog inputs, making the ADC values read from the I/O module by the real-time application the most recently converted values.
% Mandatory: set the Trigger Source for ADC and DAC to model
set_param([modelName, '/IO32x Analog output'], 'trigger', 'Target');
set_param([modelName, '/IO32x Analog input'], 'ADC1_trigger', 'Target');
set_param([modelName, '/IO32x Analog input'], 'ADC2_trigger', 'Target');
set_param([modelName, '/IO32x Analog input'], 'ADC3_trigger', 'Target');
set_param([modelName, '/IO32x Analog input'], 'ADC4_trigger', 'Target');
In this case, with all triggers set to Target, the trigger signals are set to Input. For details on the connections, see Test Setup - Digital Part. In this example the triggers are set so that only every 2nd DAC value is passed to the DAC. The trigger signal for ADC01 is set to constant high, which means that ADC01 samples permanently, while ADC02 / ADC03 / ADC04 sample only every 10th value.
Build, Download, and Run the Example
To run the example, either run the following code section or click the Run on Target button in the REAL-TIME tab in the Simulink model.
% Build the Simulink model
slbuild(modelName); % this will create the real-time application file (.mldatx)
% Create and connect to the Speedgoat real-time target machine
% Download and install the real-time application on the target machine
% Define and set stop-time
stoptime = 1.0; % Simulation length [s]
tg.setStopTime(stoptime);
% Start the real-time application
Show and Analyze the Signals
To show the measured signals, either run the following code section or click the Data Inspector button in the REAL-TIME tab in the Simulink model.
% Get and configure SDI Run
SDIRun = Simulink.sdi.Run.getLatest; % get the latest SDI run
SDIRun.name = 'Real-Time Simulation';
% clear and configure plot layout
Simulink.sdi.clearAllSubPlots
Simulink.sdi.setSubPlotLayout(2,2); % layout with two plots
% get signals by name (signal label in Simulink)
ADC_Signal_1 = SDIRun.getSignalsByName('ADC in.CH1_ADC_01_Single');
ADC_Signal_2 = SDIRun.getSignalsByName('ADC in.CH3_ADC_01_Single');
ADC_Signal_3 = SDIRun.getSignalsByName('ADC in.CH9_ADC_02_Diff');
ADC_Signal_4 = SDIRun.getSignalsByName('ADC in.CH11_ADC_02_Diff');
ADC_Signal_5 = SDIRun.getSignalsByName('ADC in.CH17_ADC_03_Single');
ADC_Signal_6 = SDIRun.getSignalsByName('ADC in.CH19_ADC_03_Single');
ADC_Signal_7 = SDIRun.getSignalsByName('ADC in.CH25_ADC_04_Diff');
ADC_Signal_8 = SDIRun.getSignalsByName('ADC in.CH27_ADC_04_Diff');
DAC_Signal = SDIRun.getSignalsByName('DAC out');
ADC_Trigger_Signal = SDIRun.getSignalsByName('ADC0x Trigger');
DAC_Trigger_Signal = SDIRun.getSignalsByName('DAC Trigger');
expand(ADC_Trigger_Signal);
% assign DAC, ADC and trigger signals for ADC01 to subplot 1
plotOnSubPlot(DAC_Signal.Children(1),1,1,true);
plotOnSubPlot(DAC_Signal.Children(2),1,1,true);
plotOnSubPlot(DAC_Trigger_Signal,1,1,true);
plotOnSubPlot(ADC_Signal_1,1,1,true);
plotOnSubPlot(ADC_Signal_2,1,1,true);
% assign DAC, ADC and trigger signals for ADC02 to subplot 2
plotOnSubPlot(DAC_Signal.Children(3),1,2,true);
plotOnSubPlot(DAC_Signal.Children(4),1,2,true);
plotOnSubPlot(DAC_Trigger_Signal,1,1,true);
plotOnSubPlot(ADC_Signal_3,1,2,true);
plotOnSubPlot(ADC_Signal_4,1,2,true);
plotOnSubPlot(ADC_Trigger_Signal.Children(2),1,2,true);
% assign DAC, ADC and trigger signals for ADC03 to subplot 3
plotOnSubPlot(DAC_Signal.Children(5),2,1,true);
plotOnSubPlot(DAC_Signal.Children(6),2,1,true);
plotOnSubPlot(DAC_Trigger_Signal,1,1,true);
plotOnSubPlot(ADC_Signal_5,2,1,true);
plotOnSubPlot(ADC_Signal_6,2,1,true);
plotOnSubPlot(ADC_Trigger_Signal.Children(3),2,1,true);
% assign DAC, ADC and trigger signals for ADC04 to subplot 4
plotOnSubPlot(DAC_Signal.Children(7),2,2,true);
plotOnSubPlot(DAC_Signal.Children(8),2,2,true);
plotOnSubPlot(DAC_Trigger_Signal,1,1,true);
plotOnSubPlot(ADC_Signal_7,2,2,true);
plotOnSubPlot(ADC_Signal_8,2,2,true);
plotOnSubPlot(ADC_Trigger_Signal.Children(4),2,2,true);
Simulink.sdi.setSubplotLimits(1,1,"tMin",-0.05,"tMax",1.05); % set time span
Simulink.sdi.setSubplotLimits(1,1,"yMin",-10.5,"yMax",1.5); % set y-zoom of channel 1
Simulink.sdi.setSubplotLimits(1,2,"yMin",-8.5,"yMax",3.5); % set y-zoom of other channels
Simulink.sdi.setSubplotLimits(2,1,"yMin",-6.5,"yMax",5.5); % set y-zoom of other channels
Simulink.sdi.setSubplotLimits(2,2,"yMin",-4.5,"yMax",7.5); % set y-zoom of other channels
Check the Results
The SDI view shows you the generated DAC output values and the measured ADC input values.
Each signal is a sawtooth with a difference of 10 V. Channel 1 starts at -10 V, and Channel 2 starts at -9 V, and so on.
The upper-left quadrant shows the DAC output values from the real-time application, the DAC trigger and the ADC01 measured values. Since the DAC Trigger only triggers every 40 ms (or every 4th sample @10 ms sampling time), and ADC01 is continuously measuring (@10 ms sampling time), we see that every 4th DAC value from the real-time application is represented in the measured ADC values.
The upper-right quadrant shows the DAC output values from the real-time application, the ADC02 trigger and the ADC02 measured values. Since the ADC02 Trigger only triggers every 12th sample, we see that every 12th DAC value from the real-time application is represented in the measured ADC values. The same is valid for the lower two quadrants that show the ADC03 and ADC04 values. On these two subplots, the sampling is done only every 12th sample, however the time point of the samples are shifted.
We can see more details if we zoom more into the quadrants:
% Zoom in T = 0s ... 0.1s
Simulink.sdi.setSubplotLimits(1,1,"tMin",-0.01,"tMax",0.25); % set time span
Simulink.sdi.setSubplotLimits(1,1,"yMin",-10.5,"yMax",-7.0); % set y-zoom of channel 1
Simulink.sdi.setSubplotLimits(1,2,"yMin",-8.5,"yMax",-5.0); % set y-zoom of other channels
Simulink.sdi.setSubplotLimits(2,1,"yMin",-6.5,"yMax",-3.0); % set y-zoom of other channels
Simulink.sdi.setSubplotLimits(2,2,"yMin",-4.5,"yMax",-1.0); % set y-zoom of other channels
Simulink.sdi.setCursorPositions('left', 0.045, 'right', 0.125); % set two cursors
Note that the Trigger signals are based in a 1 ms sample time and the DAC value writing / ADC value reading are based in a 10 ms sample time. The DAC and ADC trigger signals are generated in the half of a DAC/ADC sample.
Cursor 1 shows the part where the DAC Trigger is set in the upper-left quadrant. The ADC01 values on the next sample step therefore are the same as the current DAC values at Cursor 1. On the lower-left quadrant we can see the same for the ADC03 values, since the ADC03 trigger was also set.
Cursor 2 shows the part where the ADC02 trigger is set in the upper-right quadrant. The ADC02 values on the next sample step are therefore the same as the current DAC values at Cursor 2.
Additional References