A joint can be in one of following 3 modes:
High-level control is achieved mainly via specific API functions, such as sim.setJointPosition, sim.setJointTargetPosition, sim.setJointTargetVelocity or sim.setJointTargetForce. Depending on the joint mode and control mode, not all functions make sense, e.g. calling sim.setJointTargetForce on a kinematic joint, or calling sim.setJointTargetPosition on a dynamic joint in velocity control
Low-level control is best implemented via a joint callback function from within CoppeliaSim. In that case the joint should be in kinematic mode, or in dynamic mode and CoppeliaSim. The callback function will then constantly called by CoppeliaSim, in order to fetch new regulation values
Joint callback functions are ideal for low-level control of a joint, since the controller will regulate the joint in each time step, i.e. skipping a regulation step for a low-level controller is not an option. For joints in kinematic mode the regulation time step is the same as the simulation time step (50 ms by default). Joints in dynamic mode on the other hand will use a much fined regulation time step as defined by the dynamics module (5 ms by default).
In case of an external application wanting to perform low-level control of a joint, joint callback functions are not available, and so the external application should make sure that it will run synchronously with CoppeliaSim. This happens via the stepped mode, where the external application is in charge of explicitly triggering each simulation step. Additionally, for a fine-grained control, the simulation time step should be the same as the dynamics module time step.