Difference between revisions of "Optimizing SimpleMotion V2 performance"

From Granite Devices Knowledge Wiki
Jump to: navigation, search
[checked revision][checked revision]
(Low level queued commands)
(Low level queued commands)
Line 71: Line 71:
 
This is our optimized code that goes into high frequency loop. It writes 3 different parameters and reads 3 parameters with single bus transfer.
 
This is our optimized code that goes into high frequency loop. It writes 3 different parameters and reads 3 parameters with single bus transfer.
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
/* Variables:
+
    /* Variables:
-paramValue1-3 are the parameter values written to parameter numbers defined in paramAddr1-3
+
    -paramValue1-3 are the parameter values written to parameter numbers defined in paramAddr1-3
-readOutParamAddr1-3 will define the parameter numbers to read out into readout1-3
+
    -readOutParamAddr1-3 will define the parameter numbers to read out into readout1-3
*/
+
    */
smint32 nul, readout1, readout2, readout3;
+
  
//WRITING PARAMETERS
+
    //VALUES DEFINED
//set an parameter number where next values are written. This line consumes 2 bytes of outbound payload
+
    int paramAddr1=SMP_DEBUGPARAM4;
smAppendSMCommandToQueue( handle, SMPCMD_SETPARAMADDR, paramAddr1 );
+
    int paramAddr2=SMP_DEBUGPARAM5;
 +
    int paramAddr3=SMP_DEBUGPARAM6;
 +
    int paramValue1=100;
 +
    int paramValue2=200;
 +
    int paramValue3=300;
 +
    int readOutParamAddr1=SMP_ACTUAL_TORQUE;
 +
    int readOutParamAddr2=SMP_ACTUAL_POSITION_FB;
 +
    int readOutParamAddr3=SMP_BUS_SPEED;
 +
    smint32 nul, readout1, readout2, readout3;
  
//write the parameter value to previously set parameter address. SMPCMD_16B consumes 2 bytes of outbound payload
+
    //WRITING PARAMETERS
smAppendSMCommandToQueue( handle, SMPCMD_16B, paramValue1);
+
    //set an parameter number where next values are written. This line consumes 2 bytes of outbound payload
 +
    smAppendSMCommandToQueue( busHandle, SMPCMD_SETPARAMADDR, paramAddr1 );
  
//set an new parameter number where next values are written. This line consumes 2 bytes of outbound payload
+
    //write the parameter value to previously set parameter address. SMPCMD_24B consumes 3 bytes of outbound payload
smAppendSMCommandToQueue( handle, SMPCMD_SETPARAMADDR, paramAddr2 );
+
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, paramValue1);
  
//write the parameter value to previously set parameter address. SMPCMD_24B consumes 3 bytes of outbound payload
+
    //set an new parameter number where next values are written. This line consumes 2 bytes of outbound payload
smAppendSMCommandToQueue( handle, SMPCMD_24B, paramValue2);
+
    smAppendSMCommandToQueue( busHandle, SMPCMD_SETPARAMADDR, paramAddr2 );
  
//set an new parameter number where next values are written. This line consumes 2 bytes of outbound payload
+
    //write the parameter value to previously set parameter address. SMPCMD_24B consumes 3 bytes of outbound payload
smAppendSMCommandToQueue( handle, SMPCMD_SETPARAMADDR, paramAddr3 );
+
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, paramValue2);
  
//write the parameter value to previously set parameter address. SMPCMD_32B consumes 4 bytes of outbound payload
+
    //set an new parameter number where next values are written. This line consumes 2 bytes of outbound payload
smAppendSMCommandToQueue( handle, SMPCMD_32B, paramValue3);
+
    smAppendSMCommandToQueue( busHandle, SMPCMD_SETPARAMADDR, paramAddr3 );
  
//READING PARAMETERS
+
    //write the parameter value to previously set parameter address. SMPCMD_32B consumes 4 bytes of outbound payload
//set parameter parameter number which will be returned from each command executed after this
+
    smAppendSMCommandToQueue( busHandle, SMPCMD_32B, paramValue3);
smAppendSMCommandToQueue( handle, SMPCMD_SETPARAMADDR, SMP_RETURN_PARAM_ADDR ); //consumes 2 bytes
+
/*write new values to parameter SMP_RETURN_PARAM_ADDR. after execution these commands return the parameter values
+
defined by readOutParamAddrN*/
+
smAppendSMCommandToQueue( handle, SMPCMD_16B, readOutParamAddr1 ); //consumes 2 bytes
+
smAppendSMCommandToQueue( handle, SMPCMD_16B, readOutParamAddr2 ); //consumes 2 bytes
+
smAppendSMCommandToQueue( handle, SMPCMD_16B, readOutParamAddr3 ); //consumes 2 bytes
+
  
//execute commands over SM bus
+
    //READING PARAMETERS
smExecuteCommandQueue( handle, nodeAddress );
+
    //set parameter parameter number which will be returned from each command executed after this
 +
    smAppendSMCommandToQueue( busHandle, SMPCMD_SETPARAMADDR, SMP_RETURN_PARAM_ADDR ); //consumes 2 bytes
 +
    /*write new values to parameter SMP_RETURN_PARAM_ADDR. after execution these commands return the parameter values
 +
    defined by readOutParamAddrN*/
 +
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, readOutParamAddr1 ); //consumes 3 bytes
 +
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, readOutParamAddr2 ); //consumes 3 bytes
 +
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, readOutParamAddr3 ); //consumes 3 bytes
  
//read value commands (one per each append command). Size of inbound payload depends on how large TODOTODO
+
    //execute commands over SM bus
smGetQueuedSMCommandReturnValue( handle,&nul );
+
    smExecuteCommandQueue( busHandle, deviceAddress );
smGetQueuedSMCommandReturnValue( handle,&nul );
+
smGetQueuedSMCommandReturnValue( handle,&nul );
+
smGetQueuedSMCommandReturnValue( handle,&nul );
+
smGetQueuedSMCommandReturnValue( handle,&nul );
+
smGetQueuedSMCommandReturnValue( handle,&nul );
+
  
/*the next readouts reflect to the last three commands appended before smExecuteCommandQueue
+
    //read value commands (one per each append command). Size of inbound payload depends on how large TODOTODO
and here we get the readout values*/
+
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
smGetQueuedSMCommandReturnValue( handle,&readout1 );
+
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
smGetQueuedSMCommandReturnValue( handle,&readout2 );
+
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
smGetQueuedSMCommandReturnValue( handle,&readout3 );
+
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
 +
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
 +
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
  
 +
 +
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
 +
 +
    /*the next readouts reflect to the last three commands appended before smExecuteCommandQueue
 +
    and here we get the readout values*/
 +
 +
    smGetQueuedSMCommandReturnValue( busHandle,&readout1 );
 +
    smGetQueuedSMCommandReturnValue( busHandle,&readout2 );
 +
    smGetQueuedSMCommandReturnValue( busHandle,&readout3 );
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
[[Category:Development]]
 
[[Category:Development]]

Revision as of 00:08, 8 January 2014

This article lists the ways to optimize throughput and reduce latency of SimpleMotion V2.

Factors affecting to SMV2 speed

The speed of SMV2 is limited by:

  • Latency of RS485 communication device on the host side (i.e. SimpleMotion V2 USB adapter). This is the time delay of the communication device has during one write-read cycle, typically in range of milliseconds.
  • Command execution time of SMV2 slave device (servo drive). This is the delay how soon slave sends the return data after it has received the command from the SMV2 host. On Argon this is typically around 1-2 ms for a single command and 0.4 ms on each additional command in the queue (see later sections for info about queued commands).
  • Baud rate of RS485 bus. By default this is 460800 bits per second which is not typically the bottle neck. It is possible to change baud rate by one SMV2 command if necessary (TODO: how).
  • Usage of SM library commands. Using higher level commands usually produce more bus traffic and lower performance compared to lower level code.

Improving performance

RS485 adapter latency

One major source of latency is the RS485 adapter. Two ways to improve this:

  • If using SimpleMotion V2 USB adapter on Windows, go to Control panel->Device manager and find the USB Serial Port of the adapter. In the driver settings you will find the latency timer setting which defaults to 16 ms. Reduce this to 1 ms and click Ok. See detailed instructions at SMV2USB adapter latency settings.
  • Another option is to use a PCI/PCI-E based RS485 adapter which may have near zero latency and also better real-time capabilities than USB devices.

Optimizing code

The easiest way to use SMV2 library is to use smSetParameter and smRead1Parameter commands. However these commands are not optimal as they set and read only one parameter at the time and all latencies are added to this single call.

Queued calls

The more optimal way is to use queued SM commands. Queued command is a list of commands that are transferred to a slave in single data transfer, executed as a batch in the slave and finally all return data is returned as list. In this case bus latencies occur only once per multiple commands.

Instead of doing this...

smSetParameter( handle, deviceAddress, paramId1, paramVal1 );
smSetParameter( handle, deviceAddress, paramId2, paramVal1 );
smSetParameter( handle, deviceAddress, paramId3, paramVal1 );
smSetParameter( handle, deviceAddress, paramId4, paramVal1 );

...one may do this:

smint32 nul;
//append commands to queue
smAppendSetParamCommandToQueue( handle, paramId1, paramVal1 );
smAppendSetParamCommandToQueue( handle, paramId2, paramVal2 );
smAppendSetParamCommandToQueue( handle, paramId3, paramVal3 );
smAppendSetParamCommandToQueue( handle, paramId4, paramVal4 );
 
//execute commands over SM bus
smExecuteCommandQueue( handle,deviceAddress );
 
/*read return packet values. This is dummy as we don't need to read any parameters 
in this example now. However it's always required to read all return data from buffer 
before appending new commands. So each smAppendSetParamCommandToQueue will need 
one smGetQueuedSetParamReturnValue after smExecuteCommandQueue.*/
smGetQueuedSetParamReturnValue(  handle, &nul );
smGetQueuedSetParamReturnValue(  handle, &nul );
smGetQueuedSetParamReturnValue(  handle, &nul );
smGetQueuedSetParamReturnValue(  handle, &nul );

In the above example, the smExecuteCommandQueue is the only line where RS485 is being used thus generating only one write-read latency. smAppendSetParamCommandToQueue and smGetQueuedSetParamReturnValue transfer data only inside SMV2 library buffers and are fast calls.

Queued size limit

One SMV2 command or return data packet can hold up to 120 bytes of payload data. This means that one should not add too many commands to the queue to avoid exceeding the 120 byte limit in outbound or inbound direction.

Single smAppendSetParamCommandToQueue commands consumes maximum of 6 bytes in outbound data and smGetQueuedSetParamReturnValue maximum of 4 bytes. So one can safely queue 20 set parameter commands in the one transfer.

Low level queued commands

This section shall describe how to further optimize communication by using lower level queued commands instead of smAppendSetParamCommandToQueue and smGetQueuedSetParamReturnValue which do some dummy assumptions (such as all parameters are written as 30 bit values and every time parameter address is set even if it's not changed from the previous set parameter command). Also reading variables while writing one is possible without separate command which reduces the amount of SM commands needed.

Following example will:

  • Initialize read-out parameter address so that all further SM commands will return the desired parameter as return value
  • Use custom parameter lengths to reduce amount of data transferred

Initialization code where we specify readout variable length.

/*setting return data lenght to 24 bits (22 bits maximum returned integer value 
as 2 bits are used by SM protocol). 16 bit data will fit in return value without clipping. */
smSetParameter( handle, deviceAddress,SMP_RETURN_PARAM_LEN, SMPRET_24B);

This is our optimized code that goes into high frequency loop. It writes 3 different parameters and reads 3 parameters with single bus transfer.

    /* Variables:
    -paramValue1-3 are the parameter values written to parameter numbers defined in paramAddr1-3
    -readOutParamAddr1-3 will define the parameter numbers to read out into readout1-3
    */
 
    //VALUES DEFINED
    int paramAddr1=SMP_DEBUGPARAM4;
    int paramAddr2=SMP_DEBUGPARAM5;
    int paramAddr3=SMP_DEBUGPARAM6;
    int paramValue1=100;
    int paramValue2=200;
    int paramValue3=300;
    int readOutParamAddr1=SMP_ACTUAL_TORQUE;
    int readOutParamAddr2=SMP_ACTUAL_POSITION_FB;
    int readOutParamAddr3=SMP_BUS_SPEED;
    smint32 nul, readout1, readout2, readout3;
 
    //WRITING PARAMETERS
    //set an parameter number where next values are written. This line consumes 2 bytes of outbound payload
    smAppendSMCommandToQueue( busHandle, SMPCMD_SETPARAMADDR, paramAddr1 );
 
    //write the parameter value to previously set parameter address. SMPCMD_24B consumes 3 bytes of outbound payload
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, paramValue1);
 
    //set an new parameter number where next values are written. This line consumes 2 bytes of outbound payload
    smAppendSMCommandToQueue( busHandle, SMPCMD_SETPARAMADDR, paramAddr2 );
 
    //write the parameter value to previously set parameter address. SMPCMD_24B consumes 3 bytes of outbound payload
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, paramValue2);
 
    //set an new parameter number where next values are written. This line consumes 2 bytes of outbound payload
    smAppendSMCommandToQueue( busHandle, SMPCMD_SETPARAMADDR, paramAddr3 );
 
    //write the parameter value to previously set parameter address. SMPCMD_32B consumes 4 bytes of outbound payload
    smAppendSMCommandToQueue( busHandle, SMPCMD_32B, paramValue3);
 
    //READING PARAMETERS
    //set parameter parameter number which will be returned from each command executed after this
    smAppendSMCommandToQueue( busHandle, SMPCMD_SETPARAMADDR, SMP_RETURN_PARAM_ADDR ); //consumes 2 bytes
    /*write new values to parameter SMP_RETURN_PARAM_ADDR. after execution these commands return the parameter values
    defined by readOutParamAddrN*/
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, readOutParamAddr1 ); //consumes 3 bytes
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, readOutParamAddr2 ); //consumes 3 bytes
    smAppendSMCommandToQueue( busHandle, SMPCMD_24B, readOutParamAddr3 ); //consumes 3 bytes
 
    //execute commands over SM bus
    smExecuteCommandQueue( busHandle, deviceAddress );
 
    //read value commands (one per each append command). Size of inbound payload depends on how large TODOTODO
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
 
 
    smGetQueuedSMCommandReturnValue( busHandle,&nul );
 
    /*the next readouts reflect to the last three commands appended before smExecuteCommandQueue
    and here we get the readout values*/
 
    smGetQueuedSMCommandReturnValue( busHandle,&readout1 );
    smGetQueuedSMCommandReturnValue( busHandle,&readout2 );
    smGetQueuedSMCommandReturnValue( busHandle,&readout3 );