diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 09094161..42796c58 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -45,8 +45,8 @@ interfaces: a very large PSU or are running of a battery you may encounter this error when executing high speed movements with a high current limit. To limit your PSU power draw you can limit your motor - current and/or velocity limit `controller.config.vel_limit` and - `motor.config.current_lim`. + current and/or velocity limit `Axis:controller.config.vel_limit` and + `Axis:motor.config.current_lim`. DC_BUS_OVER_VOLTAGE: brief: The DC voltage exceeded the limit configured in `config.dc_bus_overvoltage_trip_level`. doc: | @@ -67,12 +67,12 @@ interfaces: resistor value. DC_BUS_OVER_REGEN_CURRENT: doc: | - Current flowing back into the power supply exceeded `odrv.config.dc_max_negative_current`. + Current flowing back into the power supply exceeded `config.dc_max_negative_current`. This can happen if your brake resistor is unable to handle the braking current. Check that `(V_power_supply / Brake_resistance) > (total motor.config.current_lim + total motor.config.current_lim_margin)`. DC_BUS_OVER_CURRENT: doc: | - Too much current was pulled from the power supply. `odrv.ibus` exceeded `odrv.config.dc_max_positive_current`. + Too much current was pulled from the power supply. `ibus` exceeded `config.dc_max_positive_current`. BRAKE_DEADTIME_VIOLATION: BRAKE_DUTY_CYCLE_NAN: INVALID_BRAKE_RESISTANCE: {doc: '`config.brake_resistance` is non-positive or NaN. Make sure that `config.brake_resistance` is a positive number.'} @@ -182,7 +182,7 @@ interfaces: - A GPIO was used as an interrupt input for two internal components or two GPIOs that are mutually exclusive in their interrupt capability were both used as interrupt input. - Example: `step_gpio_pin` of both axes were set to the same GPIO. + Example: `Axis:config.step_gpio_pin` of both axes were set to the same GPIO. oscilloscope: {type: Oscilloscope} can: {type: Can} @@ -344,8 +344,8 @@ interfaces: The brake duty cycle is increased by the following amount: - * `vbus_voltage` == `dc_bus_overvoltage_ramp_start` => brake_duty_cycle += 0% - * `vbus_voltage` == `dc_bus_overvoltage_ramp_end` => brake_duty_cycle += 100% + * `ODrive:vbus_voltage` == `dc_bus_overvoltage_ramp_start` => brake_duty_cycle += 0% + * `ODrive:vbus_voltage` == `dc_bus_overvoltage_ramp_end` => brake_duty_cycle += 100% Remarks: - This feature is active even when all motors are disarmed. @@ -354,7 +354,7 @@ interfaces: type: float32 status: experimental brief: See `enable_dc_bus_overvoltage_ramp`. - doc: Do not set this lower than your usual `vbus_voltage`, + doc: Do not set this lower than your usual `ODrive:vbus_voltage`, unless you like fried brake resistors. dc_bus_overvoltage_ramp_end: type: float32 @@ -414,7 +414,7 @@ interfaces: bit: 11 brief: The axis watchdog timer expired. doc: | - An amount of time greater than `axis.config.watchdog_timeout` passed + An amount of time greater than `config.watchdog_timeout` passed without the watchdog being fed. MIN_ENDSTOP_PRESSED: brief: The min endstop was pressed @@ -662,15 +662,15 @@ interfaces: bit: 10 doc: | The current sense circuit saturated the current sense amplifier. - This can be caused by setting `motor.config.current_lim` higher than - `motor.config.requested_current_range`. If this happens, increase the + This can be caused by setting `config.current_lim` higher than + `config.requested_current_range`. If this happens, increase the requested current range, save the configuration, and reboot the controller. CURRENT_LIMIT_VIOLATION: bit: 12 doc: | The motor current exceeded `motor.config.current_lim + motor.config.current_lim_margin`. The current controller is a PI controller, so it can experience overshoot. The PI gains - are automatically calculated based on `motor.config.current_control_bandwidth` and the + are automatically calculated based on `config.current_control_bandwidth` and the motor resistance and inductance (pole placement). Some overshoot is normal, so a sensible solution is to increase the current limit margin if your current limit is large. MODULATION_IS_NAN: {bit: 16} @@ -682,8 +682,8 @@ interfaces: I_BUS_OUT_OF_RANGE: doc: | The DC current sourced/sunk by this motor exceeded the configured - hard limits. More specifically `i_bus` fell outside of the range - `config.i_bus_hard_min` ... `config.i_bus_hard_max`. + hard limits. More specifically `I_bus` fell outside of the range + `config.I_bus_hard_min` ... `config.I_bus_hard_max`. BRAKE_RESISTOR_DISARMED: doc: | An attempt was made to run the motor PWM while the brake resistor was enabled but disarmed. @@ -693,14 +693,14 @@ interfaces: SYSTEM_LEVEL: doc: | The motor had to be disarmed because of a system level error. - See `ODrive.Error` for more details. + See `ODrive:error` for more details. BAD_TIMING: {doc: The main control loop got out of sync with the motor control loop. This could indicate that the main control loop got stuck.} UNKNOWN_PHASE_ESTIMATE: {doc: The current controller did not get a valid angle input. Maybe you didn't calibrate the encoder.} UNKNOWN_PHASE_VEL: {doc: The motor controller did not get a valid phase velocity input.} UNKNOWN_TORQUE: {doc: The motor controller did not get a valid torque input.} UNKNOWN_CURRENT_COMMAND: {doc: The current controller did not get a valid current setpoint. Maybe you didn't configure the controller correctly.} UNKNOWN_CURRENT_MEASUREMENT: {doc: The current controller did not get a valid current measurement.} - UNKNOWN_VBUS_VOLTAGE: {doc: The current controller did not get a valid `vbus_voltage` measurement.} + UNKNOWN_VBUS_VOLTAGE: {doc: The current controller did not get a valid `ODrive:vbus_voltage` measurement.} UNKNOWN_VOLTAGE_COMMAND: {doc: The current controller did not get a valid feedforward voltage setpoint.} UNKNOWN_GAINS: {doc: The current controller gains were not configured. Run motor calibration or set `config.phase_resistance` and `config.phase_inductance` manually.} CONTROLLER_INITIALIZING: {doc: Internal value used while the controller is not yet ready to generate PWM timings.} @@ -779,7 +779,7 @@ interfaces: type: float32 unit: A doc: | - If the controller fails to keep this motor's DC current (`I_bus`) + If the controller fails to keep this motor's DC current (`ODrive.Motor:I_bus`) above this value the motor gets disarmed immediately. Most likely you want a negative value here. Set to -inf to disable. Take noise into account when chosing a value. @@ -787,7 +787,7 @@ interfaces: type: float32 unit: A doc: | - If the controller fails to keep this motor's DC current (`I_bus`) + If the controller fails to keep this motor's DC current (`ODrive.Motor:I_bus`) below this value the motor gets disarmed immediately. Usually this is set in conjunction with `I_bus_hard_min`. Set to inf to disable. Take noise into account when chosing a value. @@ -859,7 +859,7 @@ interfaces: the speed limit. You can set `config.enable_overspeed_error` to False to disable this error. INVALID_INPUT_MODE: - brief: The `controller.config.input_mode` setting was set to an invalid value. See InputMode for available values + brief: The `config.input_mode` setting was set to an invalid value. See InputMode for available values doc: | Input modes and control modes are separate concepts. A control mode sets the type of control to be used, like position, velocity, or torque control. Input modes modify the input given (`input_pos`, etc) to @@ -1027,12 +1027,12 @@ interfaces: measure the CPR. So you should also double check this value. If you are still having issues, you can try to increase - `encoder.config.calib_scan_distance` up to a factor of 4 above the default. + `config.calib_scan_distance` up to a factor of 4 above the default. If your encoder cpr and motor pole pair settings are correct, this error can be caused because motor cogging makes the motor move less or more than commanded. You can fix this by increasing - `encoder.config.calib_scan_distance`. + `config.calib_scan_distance`. Note that the AMT encoders are configurable using the micro- switches on the encoder PCB and so you may need to check that @@ -1223,7 +1223,7 @@ valuetypes: ANALOG_IN: doc: | The pin can be used for one or more of these functions: - Sin/cos encoders, analog input, `get_adc_voltage`. + Sin/cos encoders, analog input, `get_adc_voltage()`. UART_A: {doc: See `config.enable_uart_a`.} UART_B: {doc: This mode is not supported on ODrive v3.x.} UART_C: {doc: This mode is not supported on ODrive v3.x.} @@ -1232,7 +1232,7 @@ valuetypes: SPI_A: {doc: Note that the SPI pins on ODrive v3.x are hardwired so they cannot be configured through software. Consequently, even though SPI_A is exposed, this mode is of no use on ODrive v3.x.} - PWM: {doc: See `config.gpio0_pwm_mapping`.} + PWM: {doc: See `config.gpio1_pwm_mapping`.} ENC0: {doc: The pin is used by quadrature encoder 0.} ENC1: {doc: The pin is used by quadrature encoder 1.} ENC2: {doc: This mode is not supported on ODrive v3.x.} @@ -1299,7 +1299,7 @@ valuetypes: brief: Run lockin spin. doc: | Can only be entered if the motor is calibrated (`motor.is_calibrated`) - or the motor direction is unspecified (`motor.config.direction` == 1) + or the motor direction is unspecified (`encoder.config.direction` == 1) ENCODER_DIR_FIND: brief: Run encoder direction search. doc: | @@ -1359,7 +1359,7 @@ valuetypes: ### Valid Inputs: * `input_pos` * `input_vel` - * `input_current` + * `input_torque` ### Valid Control modes: * `CONTROL_MODE_VOLTAGE_CONTROL` @@ -1404,9 +1404,9 @@ valuetypes: ![Trapezoidal Planner Response](../TrapTrajPosVel.PNG) ### Configuration Values: - * `trap_traj.config.vel_limit` - * `trap_traj.config.accel_limit` - * `trap_traj.config.decel_limit` + * `Axis:trap_traj.config.vel_limit` + * `Axis:trap_traj.config.accel_limit` + * `Axis:trap_traj.config.decel_limit` * `config.inertia` ### Valid Inputs: @@ -1421,7 +1421,7 @@ valuetypes: * `config.torque_ramp_rate` ### Valid Inputs: - * `input_current` + * `input_torque` ### Valid Control Modes: * `CONTROL_MODE_TORQUE_CONTROL` diff --git a/docs/_layouts/api_documentation_template.j2 b/docs/_layouts/api_documentation_template.j2 index 53fd127a..d4a7a423 100644 --- a/docs/_layouts/api_documentation_template.j2 +++ b/docs/_layouts/api_documentation_template.j2 @@ -7,6 +7,14 @@ download: text: 'download as YAML' --- +[% set attr_lookup_table = +{ + 'ODrive': '', + 'ODrive.Axis': '.', + 'ODrive.Motor': '..motor', +} +%] + [%- macro interface_ref(type) -%] **[['[']][[type.name]][[']']]([[type.fullname | lower]])** [%- endmacro %] @@ -19,8 +27,8 @@ download: [%- endif %] [%- endmacro %] -[% macro attr_ref(token, attr) -%] -**[['[']][[token]][[']']]([[attr.parent.fullname | lower]]#[[attr.name]])** +[% macro attr_ref(token, scope, intf, attr) -%] +**[['[']][% if intf != scope %][[attr_lookup_table[intf.fullname] | html_escape]].[% endif %][[token]][[']']]([[attr.parent.fullname | lower]]#[[attr.name]])** [%- endmacro %] [% if interface %] @@ -131,7 +139,7 @@ This interface has no functions. ## [% if enum.is_flags %]Flags[% else %]Values[% endif %] [% for k, value in enum['values'].items() %] -**[[(enum.name + value.name) | to_macro_case]]**  —  [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %] +**[[enum.name | to_macro_case]]_[[value.name]]**  —  [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %] [[-status_badge(value.status)]]
    diff --git a/tools/fibre-tools/interface_generator.py b/tools/fibre-tools/interface_generator.py index 63def69d..f27300f2 100644 --- a/tools/fibre-tools/interface_generator.py +++ b/tools/fibre-tools/interface_generator.py @@ -624,6 +624,7 @@ for _, item in list(interfaces.items()): toplevel_interfaces = [] for k, item in list(interfaces.items()): k = split_name(k) + item.parent = None if len(k) == 1: toplevel_interfaces.append(item) else: @@ -673,36 +674,49 @@ def tokenize(text, interface, interface_transform, value_type_transform, attribu and returns a string. value_type_transform: A function that takes a value type object as an argument and returns a string. - attribute_transform: A function that takes the token strin and an attribute - object as arguments and returns a string. + attribute_transform: A function that takes the token string, an interface object + and an attribute object as arguments and returns a string. """ if text is None or isinstance(text, jinja2.runtime.Undefined): return text def token_transform(token): token = token.groups()[0] + + if ':' in token: + intf_name, _, attr_name = token.partition(':') + intf = InterfaceRefElement(interface.fullname, None, intf_name, []).resolve() + token = attr_name + else: + intf = interface + token_list = split_name(token) # Check if this is an attribute reference - scope = interface + scope = intf attr = None - while attr is None and not scope is None: - attr_intf = scope - for name in token_list: - if not name in attr_intf['attributes']: - attr = None - break - attr = attr_intf['attributes'][name] - attr_intf = attr['type'] - scope = scope.get('parent', None) + + for name in token_list: + if scope.fullname == 'ODrive': # TODO: this is a temporary hack + scope = interfaces['ODrive3'] + if (not name.endswith('()')) and name in scope.get_all_attributes(): + attr = scope.get_all_attributes()[name] + scope = attr['type'] + elif name.endswith('()') and name[:-2] in scope.get_all_functions(): + attr = scope.get_all_functions()[name[:-2]] + scope = None # TODO + else: + print('Warning: cannot resolve "{}" in {}'.format(token, intf.fullname)) + return "`" + token + "`" - if not attr is None: - return attribute_transform(token, attr) + return attribute_transform(token, interface, intf, attr) - print('Warning: cannot resolve "{}" in {}'.format(token, interface.fullname)) - return "`" + token + "`" - return re.sub(r'`([A-Za-z\._]+)`', token_transform, text) + return re.sub(r'`([A-Za-z0-9\.:_]+)`', token_transform, text) + +def html_escape(text): + import html + return html.escape(str(text)) env.filters['to_pascal_case'] = to_pascal_case env.filters['to_camel_case'] = to_camel_case @@ -713,6 +727,7 @@ env.filters['first'] = lambda x: next(iter(x)) env.filters['skip_first'] = lambda x: list(x)[1:] env.filters['to_c_string'] = lambda x: '\n'.join(('"' + line.replace('"', '\\"') + '"') for line in json.dumps(x, separators=(',', ':')).replace('{"name"', '\n{"name"').split('\n')) env.filters['tokenize'] = tokenize +env.filters['html_escape'] = html_escape env.filters['diagonalize'] = lambda lst: [lst[:i + 1] for i in range(len(lst))] env.filters['debug'] = lambda x: print(x)