Merge branch 'fw-v0.5.2rc1'

Merge v0.5.2rc1 branch into master
This commit is contained in:
PAJohnson
2021-05-28 22:23:26 -04:00
499 changed files with 94783 additions and 20947 deletions
+2
View File
@@ -45,6 +45,8 @@ sections:
url: /step-direction
- title: RC PWM
url: /rc-pwm
- title: Analog Input
url: /analog-input
- title: Homing & Endstops
url: /endstops
- title: Thermistors
+5
View File
@@ -0,0 +1,5 @@
# Analog Input
Analog inputs can be used to measure voltages between 0 and 3.3V. ODrive uses a 12 bit ADC (4096 steps) and so has a maximum resolution of 0.8 mV. A GPIO must be configured with `<odrv>.config.gpioX_mode = GPIO_MODE_ANALOG_IN` before it can be used as an analog input. To read the voltage on GPIO1 in odrivetool the following would be entered: `odrv0.get_adc_voltage(1)`.
Similar to RC PWM input, analog inputs can also be used to feed any of the numerical properties that are visible in `odrivetool`. This is done by configuring `odrv0.config.gpio3_analog_mapping` and `odrv0.config.gpio4_analog_mapping`. Refer to [RC PWM](rc-pwm) for instructions on how to configure the mappings.
+53
View File
@@ -0,0 +1,53 @@
# Anti-cogging
ODrive supports an anti-cogging algorithm that attempts to compensate for the rather high cogging torques seen in hobby motors.
`<odrv>.<axis>.controller.config.anticogging` is a configuration structure that contains the following items:
Name | Type | Use
-- | -- | --
index | uint32 | The current position being used for calibration
pre_calibrated | bool | If true and using index or absolute encoder, load anticogging map from NVM at startup
calib_anticogging | bool | True when calibration is ongoing
calib_pos_threshold | float32 | (pos_estimate - index) must be < this value to calibrate. Larger values speed up calibration but hurt accuracy
calib_vel_threshold | float32 | (vel_estimate) must be < this value to calibrate. Larger values speed up calibration but hurt accuracy.
cogging_ratio | float32 | Deprecated
anticogging_enabled | bool | Enable or disable anticogging. A valid anticogging map can be ignored by setting this to `false`
## Calibration
To calibrate anticogging, first make sure you can adequately control the motor in . It should respond to position commands.
Start by putting the axis in `AXIS_STATE_CLOSED_LOOP` with `CONTROL_MODE_POSITION_CONTROL` and `INPUT_MODE_PASSTHROUGH`. Make sure you have good control of the motor in this state (it responds to position commands). Now, tune the motor to be very stiff - high `pos_gain` and relatively high `vel_integrator_gain`. This will help in calibration.
Run `controller.start_anticogging_calibration()`. The motor will start turning slowly, calibrating each point. If you like, you can start a liveplotter session before running this command so that you can watch the position move.
Once it's complete (it should take about 1 minute), the motor will return to 0 and the value `controller.anticogging_valid` should report True. If `controller.config.anticogging.anticogging_enabled` == True, anticogging will now be running on this axis.
## Saving to NVM
As of v0.5.1, the anticogging map is saved to NVM after calibrating and calling `odrv0.save_configuration()`
The anticogging map can be reloaded automatically at startup by setting `controller.config.anticogging.pre_calibrated = True` and saving the configuration. However, this map is only valid and will only be loaded for absolute encoders, or encoders with index pins after the index search.
## Example
``` Py
odrv0.axis0.encoder.config.use_index = True
odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE
odrv0.axis0.encoder.config.pre_calibrated = True
odrv0.axis0.motor.config.pre_calibrated = True
odrv0.axis0.controller.config.control_mode = CONTROL_MODE_POSITION_CONTROL
odrv0.axis0.controller.config.input_mode = INPUT_MODE_PASSTHROUGH
odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis0.controller.start_anticogging_calibration()
# Wait until controller.config.anticogging.calib_anticogging == False
odrv0.axis0.controller.config.anticogging.pre_calibrated = True
odrv0.save_configuration()
odrv0.reboot()
```
+2 -1
View File
@@ -4,7 +4,7 @@
## How to send commands
* **Via USB:**
* **Windows:** Use the Zadig utility to set the ODrive's driver to "usbser". Windows will then make the device available as COM port. You can use [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/) to manually send commands or open the COM port using your favorite programming language
* **Windows:** Use [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/) to manually send commands or open the COM port using your favorite programming language
* **Linux/macOS:** Run `/dev/tty*` to list all serial ports. The ODrive will show up as `/dev/ttyACM0` (or similar) on Linux and `/dev/tty.usbmodem[...]` on macOS. Once you know the name, you can use `screen /dev/ttyACM0` (with the correct name) to send commands manually or open the device using your favorite programming language. Serial ports on Unix can be opened, written to and read from like a normal file.
* **Via UART:** Connect the ODrive's TX (GPIO1) to your host's RX. Connect your ODrive's RX (GPIO2) to your host's TX. See [UART](uart) for more info.
* **Arduino:** You can use the [ODrive Arduino library](https://github.com/madcowswe/ODrive/tree/master/Arduino/ODriveArduino) to talk to the ODrive.
@@ -142,3 +142,4 @@ Not all parameters can be accessed via the ASCII protocol but at least all param
* `ss` - Save config
* `se` - Erase config
* `sr` - Reboot
* `sc` - Clear errors
+32 -30
View File
@@ -33,34 +33,36 @@ Be careful that you don't assign too many nodeIDs per PDO group. Four CAN Simpl
### Messages
CMD ID | Name | Signals | Start byte | Signal Type | Bits | Factor | Offset | Byte Order
--: | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :--
0x000 | CANOpen NMT Message\*\* | - | - | - | - | - | - | -
0x001 | ODrive Heartbeat Message | Axis Error<br>Axis Current State | 0<br>4 | Unsigned Int<br>Unsigned Int | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x002 | ODrive Estop Message | - | - | - | - | - | - | -
0x003 | Get Motor Error\* | Motor Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x004 | Get Encoder Error\* | Encoder Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x005 | Get Sensorless Error\* | Sensorless Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x006 | Set Axis Node ID | Axis CAN Node ID | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x007 | Set Axis Requested State | Axis Requested State | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x008 | Set Axis Startup Config | - Not yet implemented - | - | - | - | - | - | -
0x009 | Get Encoder Estimates\* | Encoder Pos Estimate<br>Encoder Vel Estimate | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x00A | Get Encoder Count\* | Encoder Shadow Count<br>Encoder Count in CPR | 0<br>4 | Signed Int<br>Signed Int | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x00B | Set Controller Modes | Control Mode<br>Input Mode | 0<br>4 | Signed Int<br>Signed Int | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x00C | Set Input Pos | Input Pos<br>Vel FF<br>Torque FF | 0<br>4<br>6 | IEEE 754 Float<br>Signed Int<br>Signed Int | 32<br>16<br>16 | 1<br>0.001<br>0.001 | 0<br>0<br>0 | Intel<br>Intel<br>Intel
0x00D | Set Input Vel | Input Vel<br>Torque FF | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x00E | Set Input Torque | Input Torque | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x00F | Set Velocity Limit | Velocity Limit | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x010 | Start Anticogging | - | - | - | - | - | - | -
0x011 | Set Traj Vel Limit | Traj Vel Limit | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x012 | Set Traj Accel Limits | Traj Accel Limit<br>Traj Decel Limit | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x013 | Set Traj Inertia | Traj Inertia | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x014 | Get IQ\* | Iq Setpoint<br>Iq Measured | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x015 | Get Sensorless Estimates\* | Sensorless Pos Estimate<br>Sensorless Vel Estimate | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x016 | Reboot ODrive\*\*\* | - | - | - | - | - | - | -
0x017 | Get Vbus Voltage\*\*\* | Vbus Voltage | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x018 | Clear Errors | - | - | - | - | - | - | -
0x700 | CANOpen Heartbeat Message\*\* | - | - | - | - | - | - | -
CMD ID | Name | Sender | Signals | Start byte | Signal Type | Bits | Factor | Offset | Byte Order
--: | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :--
0x000 | CANOpen NMT Message\*\* | Master | - | - | - | - | - | - | -
0x001 | ODrive Heartbeat Message | Axis | Axis Error<br>Axis Current State | 0<br>4 | Unsigned Int<br>Unsigned Int | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x002 | ODrive Estop Message | Master | - | - | - | - | - | - | -
0x003 | Get Motor Error\* | Axis | Motor Error | 0 | Unsigned Int | 64 | 1 | 0 | Intel
0x004 | Get Encoder Error\* | Axis | Encoder Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x005 | Get Sensorless Error\* | Axis | Sensorless Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x006 | Set Axis Node ID | Master | Axis CAN Node ID | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x007 | Set Axis Requested State | Master | Axis Requested State | 0 | Unsigned Int | 32 | 1 | 0 | Intel
0x008 | Set Axis Startup Config | Master | - Not yet implemented - | - | - | - | - | - | -
0x009 | Get Encoder Estimates\* | Master | Encoder Pos Estimate<br>Encoder Vel Estimate | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x00A | Get Encoder Count\* | Master | Encoder Shadow Count<br>Encoder Count in CPR | 0<br>4 | Signed Int<br>Signed Int | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x00B | Set Controller Modes | Master | Control Mode<br>Input Mode | 0<br>4 | Signed Int<br>Signed Int | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x00C | Set Input Pos | Master | Input Pos<br>Vel FF<br>Torque FF | 0<br>4<br>6 | IEEE 754 Float<br>Signed Int<br>Signed Int | 32<br>16<br>16 | 1<br>0.001<br>0.001 | 0<br>0<br>0 | Intel<br>Intel<br>Intel
0x00D | Set Input Vel | Master | Input Vel<br>Torque FF | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x00E | Set Input Torque | Master | Input Torque | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x00F | Set Limits | Master | Velocity Limit<br>Current Limit | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br> | 1<br>1 | 0<br>0 | Intel
0x010 | Start Anticogging | Master | - | - | - | - | - | - | -
0x011 | Set Traj Vel Limit | Master | Traj Vel Limit | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x012 | Set Traj Accel Limits | Master | Traj Accel Limit<br>Traj Decel Limit | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x013 | Set Traj Inertia | Master | Traj Inertia | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x014 | Get IQ\* | Axis | Iq Setpoint<br>Iq Measured | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x015 | Get Sensorless Estimates\* | Master | Sensorless Pos Estimate<br>Sensorless Vel Estimate | 0<br>4 | IEEE 754 Float<br>IEEE 754 Float | 32<br>32 | 1<br>1 | 0<br>0 | Intel<br>Intel
0x016 | Reboot ODrive | Master\*\*\* | - | - | - | - | - | - | -
0x017 | Get Vbus Voltage | Master\*\*\* | Vbus Voltage | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel
0x018 | Clear Errors | Master | - | - | - | - | - | - | -
0x019 | Set Linear Count | Master | Position | 0 | Signed Int | 32 | 1 | 0 | Intel
0x700 | CANOpen Heartbeat Message\*\* | Slave | - | - | - | - | - | - | -
-|-|-|----------------------------------|-|--------------------|-|-|-|_
\* Note: These messages are call & response. The Master node sends a message with the RTR bit set, and the axis responds with the same ID and specified payload.
\*\* Note: These CANOpen messages are reserved to avoid bus collisions with CANOpen devices. They are not used by CAN Simple.
@@ -70,7 +72,7 @@ CMD ID | Name | Signals | Start byte | Signal Type | Bits | Factor | Offset | By
## Configuring ODrive for CAN
Configuration of the CAN parameters should be done via USB before putting the device on the bus.
To set the desired baud rate, use `<odrv>.can.set_baud_rate(<value>)`. The baud rate can be done without rebooting the device. If you'd like to keep the baud rate, simply call `<odrv>.save_configuration()` before rebooting.
To set the desired baud rate, use `<odrv>.can.config.baud_rate = <value>`.
Each axis looks like a separate node on the bus. Thus, they both have the two properties `can_node_id` and `can_node_id_extended`. The node ID can be from 0 to 63 (0x3F) inclusive, or, if extended CAN IDs are used, from 0 to 16777215 (0xFFFFFF). If you want to connect more than one ODrive on a CAN bus, you must set different node IDs for the second ODrive or they will conflict and crash the bus.
@@ -79,7 +81,7 @@ Each axis looks like a separate node on the bus. Thus, they both have the two pr
```
odrv0.axis0.config.can_node_id = 3
odrv0.axis1.config.can_node_id = 1
odrv0.can.set_baud_rate(500000)
odrv0.can.config.baud_rate = 500000
odrv0.save_configuration()
odrv0.reboot()
```
+11 -8
View File
@@ -31,7 +31,6 @@ The ODrive will sequence all enabled startup actions selected in the order shown
* `<axis>.config.startup_encoder_index_search`
* `<axis>.config.startup_encoder_offset_calibration`
* `<axis>.config.startup_closed_loop_control`
* `<axis>.config.startup_sensorless_control`
See [here](api/odrive.axis.axisstate) for a description of each state.
@@ -81,22 +80,26 @@ All variables that are part of a `[...].config` object can be saved to non-volat
* `<odrv>.hw_version_major`, `<odrv>.hw_version_minor`, `<odrv>.hw_version_revision`: The hardware version of your ODrive.
## Setting up sensorless
The ODrive can run without encoder/hall feedback, but there is a minimum speed, usually around a few hunderd RPM.
The ODrive can run without encoder/hall feedback, but there is a minimum speed, usually around a few hundred RPM. In other words, sensorless mode does not support stopping or changing direction!
Sensorless mode starts by ramping up the motor speed in open loop control and then switches to closed loop control automatically. The sensorless speed ramping parameters are in `axis.config.sensorless_ramp` The `vel` and `accel` (in [radians/s] and [radians/s^2]) control the speed that the ramp tries to reach and how quickly it gets there. When the ramp reaches `sensorless_ramp.vel`, `controller.input_vel` is automatically set to the same velocity, in [turns/s], and the state switches to closed loop control.
If your motor comes to a stop after the ramp, try incrementally raising the `vel` parameter. The goal is to be above the minimum speed necessary for sensorless position and speed feedback to converge - this is not well-parameterized per motor. The parameters suggested below work for the D5065 motor, with 270KV and 7 pole pairs. If your motor grinds and skips during the ramp, lower the `accel` parameter until it is tolerable.
Below are some suggested starting parameters that you can use for the ODrive D5065 motor. Note that you _must_ set the `pm_flux_linkage` correctly for sensorless mode to work. Motor calibration and setup must also be completed before sensorless mode will work.
Below are some suggested starting parameters that you can use. Note that you _must_ set the `pm_flux_linkage` correctly for sensorless mode to work. Motor calibration and setup must also be completed before sensorless mode will work.
```
odrv0.axis0.controller.config.vel_gain = 0.01
odrv0.axis0.controller.config.vel_integrator_gain = 0.05
odrv0.axis0.controller.config.control_mode = 2
odrv0.axis0.controller.input_vel = 10
odrv0.axis0.controller.config.vel_limit = <a value greater than input_vel>
odrv0.axis0.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL
odrv0.axis0.controller.config.vel_limit = <a value greater than axis.config.sensorless_ramp.vel / (2pi * <pole_pairs>)>
odrv0.axis0.motor.config.current_lim = 2 * odrv0.axis0.config.sensorless_ramp.current
odrv0.axis0.motor.config.direction = 1
odrv0.axis0.sensorless_estimator.config.pm_flux_linkage = 5.51328895422 / (<pole pairs> * <motor kv>)
odrv0.axis0.config.enable_sensorless_mode = True
```
To start the motor:
```
<axis>.requested_state = AXIS_STATE_SENSORLESS_CONTROL
<axis>.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
```
+67 -30
View File
@@ -56,6 +56,7 @@ sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa
sudo apt-get update
sudo apt-get install gcc-arm-embedded
sudo apt-get install openocd
sudo apt-get install git-lfs
sudo add-apt-repository ppa:jonathonf/tup && sudo apt-get update && sudo apt-get install tup
sudo apt-get install python3 python3-yaml python3-jinja2 python3-jsonschema
```
@@ -64,6 +65,7 @@ sudo apt-get install python3 python3-yaml python3-jinja2 python3-jsonschema
```bash
sudo apt install gcc-arm-embedded
sudo apt install openocd
sudo apt install git-lfs
sudo apt install tup
sudo apt install python3 python3-yaml python3-jinja2 python3-jsonschema
```
@@ -72,6 +74,7 @@ sudo apt install python3 python3-yaml python3-jinja2 python3-jsonschema
```bash
sudo pacman -S arm-none-eabi-gcc arm-none-eabi-binutils
sudo pacman -S arm-none-eabi-gdb
sudo pacman -S git-lfs
sudo pacman -S tup
sudo pacman -S python python-yaml python-jinja python-jsonschema
```
@@ -81,8 +84,9 @@ sudo pacman -S python python-yaml python-jinja python-jsonschema
First install [Homebrew](https://brew.sh/). Then you can run these commands in Terminal:
```bash
brew install armmbed/formulae/arm-none-eabi-gcc
brew cask install osxfuse && brew install tup
brew install --cask osxfuse && brew install tup
brew install openocd
brew install git-lfs
pip3 install PyYAML Jinja2 jsonschema
```
@@ -109,18 +113,6 @@ To customize the compile time parameters, copy or rename the file `Firmware/tup.
__CONFIG_BOARD_VERSION__: The board version you're using. Can be `v3.1`, `v3.2`, `v3.3`, `v3.4-24V`, `v3.4-48V`, `v3.5-24V`, `v3.5-48V`, etc. Check for a label on the upper side of the ODrive to find out which version you have. Some ODrive versions don't specify the voltage: in that case you can read the value of the main capacitors: 120uF are 48V ODrives, 470uF are 24V ODrives.
__CONFIG_USB_PROTOCOL__: Defines which protocol the ODrive should use on the USB interface.
* `native`: The native ODrive protocol. Use this if you want to use the python tools in this repo. Can maybe work with macOS.
* `native-stream`: Like the native ODrive protocol, but the ODrive will treat the USB connection exactly as if it was a UART connection. __You may need to use this if you're on macOS__. This is necessary because macOS doesn't grant our python tools sufficient low-level access to treat the device as the USB device that it is.
* `none`: Disable USB. The device will still show up when plugged in but it will ignore any commands.
**Note**: There is a second USB interface that is always a serial port.
__CONFIG_UART_PROTOCOL__: Defines which protocol the ODrive should use on the UART interface (GPIO1 and GPIO2). Note that UART is only supported on ODrive v3.3 and higher.
* `native`: The native ODrive protocol. Use this if you're connecting the ODrive to a PC using UART and want to use the python tools to control and setup the ODrive.
* `ascii`: The ASCII protocol. Use this option if you control the ODrive with an Arduino. The ODrive Arduino library is not yet updated to the native protocol.
* `none`: Disable UART.
__CONFIG_DEBUG__: Defines wether debugging will be enabled when compiling the firmware; specifically the `-g -gdwarf-2` flags. Note that printf debugging will only function if your tup.config specifies the `USB_PROTOCOL` or `UART_PROTOCOL` as stdout and `DEBUG_PRINT` is defined. See the IDE specific documentation for more information.
You can also modify the compile-time defaults for all `.config` parameters. You will find them if you search for `AxisConfig`, `MotorConfig`, etc.
@@ -143,11 +135,7 @@ If the flashing worked, you can connect to the board using the [odrivetool](gett
<br><br>
## Testing
The script `tools/run_tests.py` runs a sequence of automated tests for several firmware features as well as high power burn-in tests. Some tests only need one ODrive and one motor/encoder pair while other tests need a back-to-back test rig such as [this one](https://cad.onshape.com/documents/026bda35ad5dff4d73c1d37f/w/ae302174f402737e1fdb3783/e/5ca143a6e5e24daf1fe8e434). In any case, to run the tests you need to provide a YAML file that lists the parameters of your test setup. An example can be found at [`tools/test-rig-parallel.yaml`](tools/test-rig-parallel.yaml`). The programmer serial number can be found by running `Firmware/find_programmer.sh` (make sure it has the latest firmware from STM).
<div class="alert" markdown="span">The test script commands the ODrive to high currents and high motor speeds so if your ODrive is connected to anything other than a stirdy test-rig (or free spinning motors), it will probably break your machine.</div>
Example usage: `./run_tests.py --test-rig-yaml ../tools/test-rig-parallel.yaml`
_Main article: [Testing](testing.md)_
<br><br>
## Debugging
@@ -245,6 +233,34 @@ This happens from time to time.
4. Power on the ODrive
5. Run `make flash` again
### `Warn : Cannot identify target as a STM32 family.` when flashing using openocd
**Problem:** When I try to flash ODrive v4.1 with `make flash` then I get:
```
[...]
** Programming Started **
auto erase enabled
Info : device id = 0x10006452
Warn : Cannot identify target as a STM32 family.
Error: auto_probe failed
embedded:startup.tcl:487: Error: ** Programming Failed **
in procedure 'program'
in procedure 'program_error' called at file "embedded:startup.tcl", line 543
at file "embedded:startup.tcl", line 487
```
**Solution:**
Compile and install a recent version of openocd from source. The latest official release (0.10.0 as of Nov 2020) doesn't support the STM32F722 yet.
```
sudo apt-get install libtool libusb-1.0
git clone https://git.code.sf.net/p/openocd/code openocd
cd openocd/
./bootstrap
./configure --enable-stlink
make
sudo make install
```
## Documentation
All *.md files in the `docs/` directory of the master branch are served up by GitHub Pages on [this domain](https://docs.odriverobotics.com).
@@ -270,23 +286,27 @@ bundle exec jekyll serve --incremental --host=0.0.0.0
On Ubuntu 18.04, prerequisites are: `ruby ruby-dev zlib1g-dev`.
## Modifying libfibre
If you need to modify libfibre add `CONFIG_BUILD_LIBFIBRE=true` to your tup.config and rerun `make`. After this you can start `odrivetool` (on your local PC) and it will use the updated libfibre.
To cross-compile libfibre for the Raspberry Pi, run `make libfibre-linux-armhf` or `make libfibre-all`. This will require a docker container. See [fibre-cpp readme](../Firmware/fibre-cpp/README.md) for details.
docker run -it -v "$(pwd)":/build -v /tmp/build:/build/build -w /build fibre-compiler configs/linux-armhf.config
If you're satisfied with the changes don't forget to generate binaries for all
supported systems using `make libfibre-all`.
## Releases
We use GitHub Releases to provide firmware releases.
1. Cut off the changelog to reflect the new release
2. Merge the release candidate into master.
3. Push a (lightweight) tag to the master branch.
- Releases should be named like `fw-v0.1.23`
- Prereleases should be named like `fw-v0.1.23-rc` or `fw-v0.1.23-rc4`
4. Go to the GitHub Releases page and review the draft that will have been created by a GitHub Action by now. If you're satisfied, go ahead and publish it by pressing Edit > **Publish Release**.
5. Download `python-odrive-*.tar.gz` from the Releases page. The following step assumes that the version is `0.1.2`
6. Run
```
mv {python-,}odrive-0.1.23.tar.gz
pip3 install twine
python3 -m twine upload odrive-0.1.23.tar.gz # Only works once per version!
```
3. Push a (lightweight) tag to the master branch. Follow the existing naming convention.
4. If you changed something in libfibre, regenerate the binaries using `make libfibre-all`. See [Modifying libfibre](#modifying-libfibre) for details.
5. Push the python tools to PyPI (see setup.py for details).
6. Edit the release on GitHub to add a title and description (copy&paste from changelog).
## Other code maintenance notes
The cortex M4F processor has hardware single precision float unit. However double precision operations are not accelerated, and hence should be avoided. The following regex is helpful for cleaning out double constants:
@@ -294,5 +314,22 @@ find: `([-+]?[0-9]+\.[0-9]+(?:[eE][-+]?[0-9]+)?)([^f0-9e])`
replace: `\1f\2`
<br><br>
## Notes for Contributors
In general the project uses the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), except that the default indendtation is 4 spaces, and that the 80 character limit is not very strictly enforced, merely encouraged.
In general the project uses the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), with a few exceptions:
- The default indentation is 4 spaces.
- The 80 character limit is not very strictly enforced, merely encouraged.
- The file extensions *.cpp and *.hpp are used instead of *.cc and *.h.
Your help is welcome! However before you start working on a feature/change that will take you a non-negligible amount of time and that you plan to upstream please discuss your plans with us on GitHub or Discord. This will ensure that your implementation is in line with the direction that ODrive is going.
When filing a PR please go through this checklist:
- Make sure you adhere to the same coding style that we use (see note above).
- Update CHANGELOG.md.
- If you removed/moved/renamed things in `odrive-interface.yaml` make sure to add corresponding bullet points tp the "API migration notes" section in the changelog. Use git to compare against the `devel` branch.
- Also, for each removed/moved/renamed API item use your IDE's search feature to search for occurrences of this name. Update the places you found (this will usually be documentation and test scripts).
- If you added things to `odrive-interface.yaml` make sure the new things have decent documentation in the YAML file. We don't expect 100% coverage but use good sense of what to document.
- Make sure your PR doesn't contain spurious changes that unnecessarily add or remove whitespace. These add noise and make the reviewer's lifes harder.
- If you changed any enums in `odrive-interface.yaml`, make sure you update [enums.py](../tools/odrive/enums.py). The file includes instructions on how to do this. Check the diff to verify that none of the existing enumerators changed their value.
+28 -1
View File
@@ -21,7 +21,7 @@ To verify everything went well, check the following variables:
* `<axis>.error` should be 0.
* `<axis>.encoder.config.offset` - This should print a number, like -326 or 1364.
* `<axis>.motor.config.direction` - This should print 1 or -1.
* `<axis>.encoder.config.direction` - This should print 1 or -1.
### Encoder with index signal
If you have an encoder with an index (Z) signal, you can avoid doing the offset calibration on every startup, and instead use the index signal to re-sync the encoder to a stored calibration.
@@ -62,6 +62,8 @@ When the encoder mode is set to hall feedback, the pinout on the encoder port is
| B | Hall B |
| Z | Hall C |
To use hall effect encoders, the calibration sequence is different than incremental or absolute encoders. You must first run `AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION` before `AXIS_STATE_ENCODER_OFFSET_CALIBRATION` The hall polarity calibration will automatically determine the order and polarity of the hall signals. When using `AXIS_STATE_FULL_CALIBRATION_SEQUENCE`, these steps are automatically used if the encoder is set to hall mode.
### Startup sequence notes
The following are variables that MUST be set up for your encoder configuration. Your values will vary depending on your encoder:
@@ -148,6 +150,30 @@ If you are using an encoder with an index signal, another problem that has been
* when performing an index_search, the motor does not return to the same position each time.
One easy step that _might_ fix the noise on the Z input is to solder a 22nF-47nF capacitor to the Z pin and the GND pin on the underside of the ODrive board.
## Hall feedback pinout
If position accuracy is not a concern, you can use A/B/C hall effect encoders for position feedback.
To use this mode, configure the corresponding encoder mode: `<encoder>.config.mode = ENCODER_MODE_HALL`. Configure the corresponding GPIOs as digital inputs:
For encoder 0:
<odrv>.config.gpio9_mode = GPIO_MODE_DIGITAL
<odrv>.config.gpio10_mode = GPIO_MODE_DIGITAL
<odrv>.config.gpio11_mode = GPIO_MODE_DIGITAL
For encoder 1:
<odrv>.config.gpio12_mode = GPIO_MODE_DIGITAL
<odrv>.config.gpio13_mode = GPIO_MODE_DIGITAL
<odrv>.config.gpio14_mode = GPIO_MODE_DIGITAL
In this mode, the pinout on the encoder port is as follows:
| Label on ODrive | Hall feedback |
|-----------------|---------------|
| A | Hall A |
| B | Hall B |
| Z | Hall C |
## SPI Encoders
@@ -181,3 +207,4 @@ If you are having calibration problems, make sure that your magnet is centered o
Sometimes the encoder takes longer than the ODrive to start, in which case you need to clear the errors after every restart.
If you are having calibration problems - make sure your magnet is centered on the axis of rotation on the motor, some users report this has a significant impact on calibration. Also make sure your magnet height is within range of the spec sheet.
+1
View File
@@ -65,6 +65,7 @@ Match the pullup value to the configuration. If `true`, it enables the GPIO pul
If we want to configure a 3D printer-style (configuration 4) minimum endstop for homing on GPIO 5 and we want our motor to move away from the endstop about a quarter turn with a 8192 cpr encoder, we would set:
```
<odrv>.config.gpio5_mode = GPIO_MODE_DIGITAL
<odrv>.<axis>.min_endstop.config.gpio_num = 5
<odrv>.<axis>.min_endstop.config.is_active_high = False
<odrv>.<axis>.min_endstop.config.offset = -1.0*(8912/4)
+25 -14
View File
@@ -19,7 +19,6 @@ permalink: /
- [Other control modes](#other-control-modes)
- [Watchdog Timer](#watchdog-timer)
- [What's next?](#whats-next)
- [Upgrading from 0.4.12](#upgrading-from-0412)
<!-- /TOC -->
@@ -90,9 +89,6 @@ Most instructions in this guide refer to a utility called `odrivetool`, so you s
* __Anaconda__: In the start menu, type `Anaconda Prompt` <kbd>Enter</kbd>
* __Standalone Python__: In the start menu, type `cmd` <kbd>Enter</kbd>
3. Install the ODrive tools by typing `pip install --upgrade odrive` <kbd>Enter</kbd>
4. Plug in a USB cable into the microUSB connector on ODrive, and connect it to your PC.
5. Use the [Zadig](http://zadig.akeo.ie/) utility to set ODrive driver to libusb-win32.
* Check 'List All Devices' from the options menu, and select 'ODrive 3.x Native Interface (Interface 2)'. With that selected in the device list choose 'libusb-win32' from the target driver list and then press the large 'install driver' button.
### OSX
@@ -120,16 +116,22 @@ pip3 install --upgrade odrive
__Troubleshooting__
1. Permission Errors: Just run the previous command in sudo
```bash
sudo pip3 install --upgrade odrive
```
```bash
sudo pip3 install --upgrade odrive
```
2. Dependency Errors: If the installer doesn't complete and you get a dependency
error (Ex. "No module..." or "module_name not found")
```bash
sudo pip3 install module_name
```
Try step 5 again
```bash
sudo pip3 install module_name
```
Try step 5 again
3. Other Install Errors: If the installer fails at installing dependencies, try
```bash
sudo pip3 install odrive --no-deps
```
If you do this, brace yourself for runtime errors when you run `odrivetool` (the basic functionality should work though).
### Linux
@@ -151,7 +153,7 @@ Your board should come preflashed with firmware. If you run into problems, follo
Your board does **not** come preflashed with any firmware. Follow the instructions [here](odrivetool.md#device-firmware-update) on the ST Link procedure before you continue.
## Start `odrivetool`
To launch the main interactive ODrive tool, type `odrivetool` <kbd>Enter</kbd>. Connect your ODrive and wait for the tool to find it. Now you can, for instance type `odrv0.vbus_voltage` <kbd>Enter</kbd> to inpect the boards main supply voltage.
To launch the main interactive ODrive tool, type `odrivetool` <kbd>Enter</kbd>. Connect your ODrive and wait for the tool to find it. If it doesn't connect after a few seconds refer to the [troubleshooting page](troubleshooting.md#usb-connectivity-issues). Now you can, for instance type `odrv0.vbus_voltage` <kbd>Enter</kbd> to inpect the boards main supply voltage.
It should look something like this:
```text
@@ -168,6 +170,9 @@ The tool you're looking at is a fully capable Python command prompt, so you can
You can read more about `odrivetool` [here](odrivetool.md).
## Debugging
If any of the following steps fail, print the errors by running `dump_errors(odrv0)` in `odrivetool`. You can clear errors by running `odrv0.clear_errors()`.
## Configure M0
<div class="alert" markdown="span">Read this section carefully, else you risk breaking something.</div>
<div class="note" markdown="span">There is a [separate guide](hoverboard.md) specifically for hoverboard motors.</div>
@@ -198,8 +203,14 @@ The motor will be limited to this speed. Again the default value is quite slow.
You can change `odrv0.axis0.motor.config.calibration_current` [A] to the largest value you feel comfortable leaving running through the motor continuously when the motor is stationary. If you are using a small motor (i.e. 15A current rated) you may need to reduce `calibration_current` to a value smaller than the default.
### 2. Set other hardware parameters
`odrv0.config.enable_brake_resistor`
Set this to `True` if using a brake resistor. You need to save the ODrive configuration and reboot the ODrive for this to take effect.
`odrv0.config.brake_resistance` [Ohm]
This is the resistance of the brake resistor. If you are not using it, you may set it to `0`. Note that there may be some extra resistance in your wiring and in the screw terminals, so if you are getting issues while braking you may want to increase this parameter by around 0.05 ohm.
This is the resistance of the brake resistor. You can leave this at the default setting if you are not using a brake resistor. Note that there may be some extra resistance in your wiring and in the screw terminals, so if you are getting issues while braking you may want to increase this parameter by around 0.05 ohm.
`odrv0.config.dc_max_negative_current` [Amps]
This is the amount of current allowed to flow back into the power supply. The convention is that it is negative. By default, it is set to a conservative value of 10mA. If you are using a brake resistor and getting `DC_BUS_OVER_REGEN_CURRENT` errors, raise it slightly. If you are not using a brake resistor and you intend to send braking current back to the power supply, set this to a safe level for your power source. Note that in that case, it should be higher than your motor current limit + current limit margin.
`odrv0.axis0.motor.config.pole_pairs`
This is the number of **magnet poles** in the rotor, **divided by two**. To find this, you can simply count the number of permanent magnets in the rotor, if you can see them.
@@ -258,7 +269,7 @@ Let's get motor 0 up and running. The procedure for motor 1 is exactly the same,
Check the encoder wiring and that the encoder is firmly connected to the motor. Check the value of `dump_errors(odrv0)` and then refer to the [error code documentation](troubleshooting.md#error-codes) for details.
Once you understand the error and have fixed its cause, you may clear the error state with (`dump_errors(odrv0, True)` <kbd>Enter</kbd>) and retry.
Once you understand the error and have fixed its cause, you may clear the error state with (`odrv0.clear_errors()` <kbd>Enter</kbd>) and retry.
</div></details>
2. Type `odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL` <kbd>Enter</kbd>. From now on the ODrive will try to hold the motor's position. If you try to turn it by hand, it will fight you gently. That is unless you bump up `odrv0.axis0.motor.config.current_lim`, in which case it will fight you more fiercely. If the motor begins to vibrate either immediately or after being disturbed you will need to [lower the controller gains](control.md).
+29
View File
@@ -45,6 +45,9 @@ The hall feedback has 6 states for every pole pair in the motor. Since we have 1
odrv0.axis0.encoder.config.mode = ENCODER_MODE_HALL
odrv0.axis0.encoder.config.cpr = 90
odrv0.axis0.encoder.config.calib_scan_distance = 150
odrv0.config.gpio9_mode = GPIO_MODE_DIGITAL
odrv0.config.gpio10_mode = GPIO_MODE_DIGITAL
odrv0.config.gpio11_mode = GPIO_MODE_DIGITAL
```
Since the hall feedback only has 90 counts per revolution, we want to reduce the velocity tracking bandwidth to get smoother velocity estimates.
@@ -92,6 +95,22 @@ odrv0.axis0.motor.config.pre_calibrated = True
Next step is to check the alignment between the motor and the hall sensor.
Because of this step you are allowed to plug the motor phases in random order and also the hall signals can be random. Just don't change it after calibration.
Make sure the motor is free to move and run:
```txt
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION
```
Check the status of the encoder object:
```txt
odrv0.axis0.encoder
```
Check that there are no errors.
```txt
error = 0x0000 (int)
```
If the hall encoder polarity calibration was successful, run the encoder offset calibration.
```txt
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
```
@@ -132,6 +151,7 @@ Lets use GPIO 3/4 for the velocity inputs so that we don't have to disable UART.
Then let's map the full stick range of these inputs to some suitable velocity setpoint range.
We also have to reboot to activate the PWM input.
```txt
<<<<<<< HEAD
odrv0.config.gpio3_pwm_mapping.min = -2
odrv0.config.gpio3_pwm_mapping.max = 2
odrv0.config.gpio3_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['input_vel']
@@ -139,6 +159,15 @@ odrv0.config.gpio3_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attribu
odrv0.config.gpio4_pwm_mapping.min = -2
odrv0.config.gpio4_pwm_mapping.max = 2
odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis1.controller._remote_attributes['input_vel']
=======
odrv0.config.gpio3_pwm_mapping.min = -200
odrv0.config.gpio3_pwm_mapping.max = 200
odrv0.config.gpio3_pwm_mapping.endpoint = odrv0.axis0.controller._input_vel_property
odrv0.config.gpio4_pwm_mapping.min = -200
odrv0.config.gpio4_pwm_mapping.max = 200
odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis1.controller._input_vel_property
>>>>>>> fw-v0.5.2rc1
odrv0.save_configuration()
odrv0.reboot()
+61
View File
@@ -0,0 +1,61 @@
# Mechanical Brake
Some systems employ mechanical brakes on motors as a safety feature. These brakes can also be engaged as a power-saving function if the motor is not moving, but still under load.
ODrive supports the use of its GPIO pins to connect to external brake drive electronics.
When the ODrive engages the drive electronics, the brake will be disabled. When the drive enters a fault or idle state, the brake will be re-engaged.
---
## Mechanical Brake Configuration
Each axis supports one mechanical brake. The following properties are accessible through `odrivetool`:
Name | Type | Default
--- | -- | --
gpio_num | int | 0
is_active_low | boolean | true
### gpio_num
The GPIO pin number, according to the silkscreen labels on ODrive. Set with these commands:
```
<odrv>.<axis>.mechanical_brake.config.gpio_num = <1, 2, 3, 4, 5, 6, 7, 8>
```
After GPIO pin number is changed, you'll need to run `<odrv>.save_configuration()` and `<odrv>.reboot()` for changes to take effect.
### is_active_low
Most safety braking systems are active low, e.g. when the power is off, the brake is on. If the system uses brake drive electronics which use active high logic, flip this bit then reconsider the safety implications of your design...
### Enabling
The configuration of the mechanical brake will enable the brake functionality. There's no need to specifically 'enable' this feature.
### Example
Let's say we're hacking away on an old ABB robotic arm. We've wired a 24V brake drive circuit triggered by GPIO5. When GPIO5 is driven, it will release the brakes on the axis we're moving.
We need to notify the axis of the GPIO number we've attached our brake to, and configure the ODrive pin mode to `GPIO_MODE_MECH_BRAKE`:
```
<odrv>.<axis>.mechanical_brake.config.gpio_num = 5
<odrv>.config.gpio5_mode = GPIO_MODE_MECH_BRAKE
```
Pin configurations only take effect after a save/reboot so don't forget to run:
```
<odrv>.save_configuration()
<odrv>.reboot()
```
### Testing The Mechanical Brakes
Depending on your system this could be a dangerous experiment. Ensure that you have taken all necessary precautions to confirm if the wrong brake were inadvertently released it would not lead to injury or damage to equipment.
```
<odrv>.<axis>.mechanical_brake.release()
```
Note: If a brake is configured, it will be automatically engaged/disengaged during the next state machine step.
After you're satisfied with the testing, you can re-enable the brake using the command
```
<odrv>.<axis>.mechanical_brake.engage()
```
+39
View File
@@ -1,5 +1,44 @@
# Migration Guide
## v0.5.1 -> v0.5.2
The change from v0.5.1 to v0.5.2 had fewer breaking changes than the v0.4.12 to v0.5.1 change.
## GPIO modes
The GPIO configuration is now more explicit. For example, to use `gpio1` for for step signals (as part of a step/dir interface), it must be set to
```
odrv0.config.gpio1_mode = GPIO_MODE_DIGITAL
```
## Braking behavior
Before using the brake resistor, it must be explicitly enabled as follows:
```
odrv0.config.enable_brake_resistor = True
```
and then save the configuration and reboot for the setting to take effect.
## Step/direction settings
Previously, steps were added incrementally to `input_pos`. This caused issues with accumulated floating point rounding error. Now, an absolute step count is used. This change requires that the circular setpoints mode is used `odrv0.axis0.controller.config.circular_setpoints = True` when step/dir signals are used.
In addition, `odrv0.axis0.config.turns_per_step` has been removed and `odrv0.axis0.controller.config.steps_per_circular_range` is used. For example:
```
# previous
odrv0.axis0.config.turns_per_step = 1.0/1024.0
# v0.5.2
odrv0.axis0.controller.config.circular_setpoints = True
odrv0.axis0.controller.config.circular_setpoint_range = 1.0
odrv0.axis0.controller.config.steps_per_circular_range = 1024
```
For best results, set both the circular range and steps per circular range to powers of 2.
## API changes
For other API changes, see the Changelog file on github.
## v0.4.12 -> v0.5.1
Certain changes occurred between firmware versions v0.4.12 and v0.5.1 that will break existing configurations. This document is a guide for how to take a working v0.4.12 ODrive config and change it to work with firmware v0.5.1.
## Unit Changes
+1 -1
View File
@@ -158,7 +158,7 @@ sudo dfu-util -a 0 -s 0x08000000 -D build/ODriveFirmware.bin
First, you need to install the arm development tools to copy the binary into the appropriate format.
```text
$ brew cask install gcc-arm-embedded
$ brew install --cask gcc-arm-embedded
```
Then convert the binary to .bin format
+36 -29
View File
@@ -1,35 +1,42 @@
# Pinout
| GPIO | primary | step/dir | other |
|-----------|-----------|---------------|-------------------------|
| GPIO1 | UART TX | Axis0 Step | Analog input, PWM input |
| GPIO2 | UART RX | Axis0 Dir | Analog input, PWM input |
| GPIO3 | | Axis1 Step (+)| Analog input, PWM input |
| GPIO4 | | Axis1 Dir (+) | Analog input, PWM input |
| GPIO5 | | | Analog input (*) |
| GPIO6 (*) | | | |
| GPIO7 (*) | | Axis1 Step (*)| |
| GPIO8 (*) | | Axis1 Dir (*) | |
## ODrive v4.1
(+) on ODrive v3.4 and earlier <br>
(*) ODrive v3.5 and later
**TODO**
Notes:
## ODrive v3.x
| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART_A` | `GPIO_MODE_UART_B` | `GPIO_MODE_PWM` | `GPIO_MODE_CAN_A` | `GPIO_MODE_I2C_A` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` |
|----|---------------|------------------------|-----------------------|--------------------|--------------------|-----------------|------------------|-------------------|------------------|------------------|------------------------|
| 0 | _not a pin_ | | | | | | | | | | |
| 1 | GPIO1 (+) | general purpose | analog input | **UART_A.TX** | | PWM0.0 | | | | | mechanical brake |
| 2 | GPIO2 (+) | general purpose | analog input | **UART_A.RX** | | PWM0.1 | | | | | mechanical brake |
| 3 | GPIO3 | general purpose | **analog input** | | **UART_B.TX** | PWM0.2 | | | | | mechanical brake |
| 4 | GPIO4 | general purpose | **analog input** | | **UART_B.RX** | PWM0.3 | | | | | mechanical brake |
| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | | mechanical brake |
| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | | mechanical brake |
| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | | mechanical brake |
| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | | mechanical brake |
| 9 | M0.A | general purpose | | | | | | | **ENC0.A** | | |
| 10 | M0.B | general purpose | | | | | | | **ENC0.B** | | |
| 11 | M0.Z | **general purpose** | | | | | | | | | |
| 12 | M1.A | general purpose | | | | | | I2C.SCL | | **ENC1.A** | |
| 13 | M1.B | general purpose | | | | | | I2C.SDA | | **ENC1.B** | |
| 14 | M1.Z | **general purpose** | | | | | | | | | |
| 15 | _not exposed_ | general purpose | | | | | **CAN_A.RX** | I2C.SCL | | | |
| 16 | _not exposed_ | general purpose | | | | | **CAN_A.TX** | I2C.SDA | | | |
(*) ODrive v3.5 and later <br>
(+) On ODrive v3.5 and later these pins have noise suppression filters. This is useful for step/dir input. <br>
## Notes
* Changes to the pin configuration only take effect after `odrv0.save_configuration()` and `odrv0.reboot()`
* Bold font marks the default configuration.
* If a GPIO is set to an unsupported mode it will be left uninitialized.
* When setting a GPIO to a special purpose mode (e.g. `GPIO_MODE_UART_A`) you must also enable the corresponding feature (e.g. `<odrv>.config.enable_uart_a`).
* Digital mode is a general purpose mode that can be used for these functions: step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS.
* You must also connect GND between ODrive and your other board.
* ODrive v3.3 and onward have 5V tolerant GPIO pins.
* ODrive v3.5 and later have some noise suppression filters on the default step/dir pins
* You can change the step/dir pins using `axis.config.<step/dir>_gpio_pin`.
### Pin function priorities
1. PWM in, if enabled. Disabled by default.
1. UART, **Enabled by default**.
1. Step/Dir, if enabled. Disabled by default.
1. Analog, default behavior if not overridden (only on supported pins).
1. Digital in, default behavior on pins not capable of analog input.
For predictable results, try to have only one feature enabled for any one pin. When changing pin assignments you must:
* `odrv0.save_configuration()`
* `odrv0.reboot()`
### Analog input
Analog inputs can be used to measure voltages between 0 and 3.3V. Odrive uses a 12 bit ADC (4096 steps) and so has a maximum resolution of 0.8 mV. Some GPIO pins require the appropriate pin priority (see above) to be set before they can be used as an analog input. To read the voltage on GPIO1 in odrive tool the following would be entered: `odrv0.get_adc_voltage(1)`
* Simultaneous operation of UART_A and UART_B is currently not supported.
+8 -10
View File
@@ -1,24 +1,22 @@
# RC PWM input
You can control the ODrive directly from an hobby RC receiver.
You can control the ODrive directly from a hobby RC receiver.
Some GPIO pins can be used for PWM input, if they are not allocated to other functions. For example, you must disable the UART to use GPIO 1,2. See the [pin function priorities](pinout.md#pin-function-priorities) for more detail.
Any of the numerical parameters that are writable from the ODrive Tool can be hooked up to a PWM input.
As an example, we'll configure GPIO4 to control the angle of axis 0. We want the axis to move within a range of -2 to 2 turns.
Any of the numerical parameters that are writable from the ODrive Tool can be hooked up to a PWM input. The [Pinout](#pinout) tells you which pins are PWM input capable. As an example, we'll configure GPIO4 to control the angle of axis 0. We want the axis to move within a range of -2 to 2 turns.
1. Make sure you're able control the axis 0 angle by writing to `odrv0.axis0.controller.input_pos`. If you need help with this follow the [getting started guide](getting-started.md).
2. If you want to control your ODrive with the PWM input without using anything else to activate the ODrive, you can configure the ODrive such that axis 0 automatically goes operational at startup. See [here](commands.md#startup-procedure) for more information.
3. In ODrive Tool, configure the PWM input mapping
```
In [1]: odrv0.config.gpio4_pwm_mapping.min = -2
In [2]: odrv0.config.gpio4_pwm_mapping.max = 2
In [3]: odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['input_pos']
odrv0.config.gpio4_mode = GPIO_MODE_PWM
odrv0.config.gpio4_pwm_mapping.min = -2
odrv0.config.gpio4_pwm_mapping.max = 2
odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._input_pos_property
```
Note: you can disable the input by setting `odrv0.config.gpio4_pwm_mapping.endpoint = None`
4. Save the configuration and reboot
```
In [4]: odrv0.save_configuration()
In [5]: odrv0.reboot()
odrv0.save_configuration()
odrv0.reboot()
```
5. With the ODrive powered off, connect the RC receiver ground to the ODrive's GND and one of the RC receiver signals to GPIO4. You may try to power the receiver from the ODrive's 5V supply if it doesn't draw too much power. Power up the the RC transmitter. You should now be able to control axis 0 from one of the RC sticks.
+132
View File
@@ -0,0 +1,132 @@
Most information in this file can be reproduced by running `dump_interrupts(odrv0)`, `dump_dma(odrv0)` and `dump_threads(odrv0)` in `odrivetool` (minor manual postprocessing was applied to the output of those functions).
Take this info with a grain of salt as we might forget to update it from time to time. When in doubt check the file history.
# ODrive v3.6
## Interrupt Vectors
- lowest priority: 15
- highest priority: 0
| # | Name | Prio |
|-----|-------------------------|------|
| -12 | MemoryManagement_IRQn | 0 |
| -11 | BusFault_IRQn | 0 |
| -10 | UsageFault_IRQn | 0 |
| -5 | SVCall_IRQn | 3 |
| -4 | DebugMonitor_IRQn | 0 |
| -2 | PendSV_IRQn | 15 |
| -1 | SysTick_IRQn | 15 |
| 6 | EXTI0_IRQn | 1 |
| 7 | EXTI1_IRQn | 1 |
| 8 | EXTI2_IRQn | 1 |
| 9 | EXTI3_IRQn | 1 |
| 10 | EXTI4_IRQn | 1 |
| 11 | DMA1_Stream0_IRQn | 4 |
| 13 | DMA1_Stream2_IRQn | 10 |
| 15 | DMA1_Stream4_IRQn | 10 |
| 16 | DMA1_Stream5_IRQn | 10 |
| 17 | DMA1_Stream6_IRQn | 10 |
| 19 | CAN1_TX_IRQn | 9 |
| 20 | CAN1_RX0_IRQn | 9 |
| 21 | CAN1_RX1_IRQn | 9 |
| 22 | CAN1_SCE_IRQn | 9 |
| 23 | EXTI9_5_IRQn | 1 |
| 40 | EXTI15_10_IRQn | 1 |
| 44 | TIM8_UP_TIM13_IRQn | 0 |
| 45 | TIM8_TRG_COM_TIM14_IRQn | 6 |
| 47 | DMA1_Stream7_IRQn | 3 |
| 50 | TIM5_IRQn | 1 |
| 52 | UART4_IRQn | 10 |
| 67 | OTG_FS_IRQn | 6 |
| 77 | OTG_HS_IRQn (aka ControlLoop_IRQn) | 5 |
## DMA Streams
- lowest priority: 0
- highest priority: 3
| Name | Prio | Channel | High Level Func |
|--------------|------|----------------------------------|-----------------|
| DMA1_Stream0 | 1 | 0 (SPI3_RX) | SPI_A |
| DMA1_Stream2 | 0 | 4 (UART4_RX) | UART_A |
| DMA1_Stream4 | 0 | 4 (UART4_TX) | UART_A |
| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART_B |
| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART_B |
| DMA1_Stream7 | 2 | 0 (SPI3_TX) | SPI_A |
| DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC |
## Threads
- lowest priority: -3
- highest priority: 3
| Name | Stack Size [B] | Prio |
|---------|----------------|------|
| axis0 | 2048 | 3 |
| axis1 | 2048 | 2 |
| can | 1024 | 0 |
| startup | 2048 | 0 |
| uart | 4096 | 0 |
| usb | 4096 | 0 |
# ODrive v4.0
## Interrupt Vectors
- lowest priority: 15
- highest priority: 0
| # | Name | Prio |
|-----|-------------------------|------|
| -12 | MemoryManagement_IRQn | 0 |
| -11 | BusFault_IRQn | 0 |
| -10 | UsageFault_IRQn | 0 |
| -5 | SVCall_IRQn | 0 |
| -4 | DebugMonitor_IRQn | 0 |
| -2 | PendSV_IRQn | 15 |
| -1 | SysTick_IRQn | 15 |
| 11 | DMA1_Stream0_IRQn | 5 |
| 14 | DMA1_Stream3_IRQn | 5 |
| 15 | DMA1_Stream4_IRQn | 5 |
| 16 | DMA1_Stream5_IRQn | 5 |
| 17 | DMA1_Stream6_IRQn | 5 |
| 18 | ADC_IRQn | 1 |
| 19 | CAN1_TX_IRQn | 6 |
| 20 | CAN1_RX0_IRQn | 6 |
| 21 | CAN1_RX1_IRQn | 6 |
| 22 | CAN1_SCE_IRQn | 6 |
| 26 | TIM1_TRG_COM_TIM11_IRQn | 2 |
| 35 | SPI1_IRQn | 5 |
| 36 | SPI2_IRQn | 5 |
| 38 | USART2_IRQn | 5 |
| 45 | TIM8_TRG_COM_TIM14_IRQn | 0 |
| 47 | DMA1_Stream7_IRQn | 0 |
| 51 | SPI3_IRQn | 5 |
| 59 | DMA2_Stream3_IRQn | 5 |
| 77 | OTG_HS_IRQn | 5 |
## DMA Streams
- lowest priority: 0
- highest priority: 3
| Name | Prio | Channel | High Level Func |
|--------------|------|----------------------------------|-----------------|
| DMA1_Stream0 | 1 | 0 (SPI3_RX) | Onboard SPI |
| DMA1_Stream3 | 0 | 0 (SPI2_RX) | Offboard SPI |
| DMA1_Stream4 | 0 | 0 (SPI2_TX) | Offboard SPI |
| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART1 |
| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART1 |
| DMA1_Stream7 | 1 | 0 (SPI3_TX) | Onboard SPI |
| DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC |
| DMA2_Stream3 | 0 | 3 (SPI1_TX) | Status LED |
## Threads
**TODO**
+31 -6
View File
@@ -1,14 +1,39 @@
# Step/direction
This is the simplest possible way of controlling the ODrive. It is also the most primitive and fragile one. So don't use it unless you must interoperate with other hardware that you don't control.
Pinout:
* Step/dir signals: see [Pinout](pinout.md). Note in that section how to reassign the pins.
### Pinout
* Step/dir signals: Any GPIOs can be used. Also see [Pinout](pinout.md) for more info.
* GND: you must connect the grounds of the devices together. Use any GND pin on J3 of the ODrive.
To enable step/dir mode for the GPIO, set `<axis>.config.enable_step_dir` to true for each axis that you wish to use this on.
Axis 0 step/dir pins conflicts with UART, and the UART takes priority. So to be able to use step/dir on Axis 0, you must also set `odrv0.config.enable_uart = False`. See the [pin function priorities](pinout.md#pin-function-priorities) for more detail. Don't forget to save configuration and reboot.
### How to configure
There is also a config variable called `<axis>.config.turns_per_step`, which specifies how many turns a "step" corresponds to. The default value is 1.0f/1024.0f. It can be any floating point value.
The recommended maximum step rate is 50kHz
1. Choose any two of the unused GPIOs for step/dir input. Let's say you chose GPIO7 for the step signal and GPIO8 for the dir signal.
2. Configure the GPIO modes:
<odrv>.config.gpio7_mode = GPIO_MODE_DIGITAL_PULL_DOWN
<odrv>.config.gpio8_mode = GPIO_MODE_DIGITAL
3. Configure the axis:
<axis>.config.step_gpio_pin = 7
<axis>.config.dir_gpio_pin = 8
<axis>.config.enable_step_dir = True
4. Enable circular setpoints
<axis>.controller.config.circular_setpoints = True
After this, step and direction will be enabled when you put the axis into closed loop control. Note that to change out of step/dir, you need to set `<axis>.config.enable_step_dir = False`, go to `AXIS_STATE_IDLE`, and then back into closed loop control.
Circular setpoints are used to keep floating point error at a manageable error for systems where the motor can rotate large amounts. If the motor is commanded out of the circular range, the position setpoint automatically wraps around to stay in the range. Two parameters are used to control this behavior: `<odrv>.<axis>.controller.config.circular_setpoint_range` and `<odrv>.<axis>.controller.config.steps_per_circular_range`. The circular setpoint range sets the operating range of input_pos, starting at 0.0. The `steps per circular range` setting controls how many steps are needed to traverse the entire range. For example, to use 1024 steps per 1 full motor turn, set
```
<odrv>.<axis>.controller.config.circular_setpoint_range = 1.0 [turns]
<odrv>.<axis>.controller.config.steps_per_circular_range = 1024 [steps]
```
The circular range is a floating point value and the steps per circular range parameter is an integer. For best results, set both parameters to powers of 2.
The maximum step rate is pending tests, but 250kHz step rates with both axes in closed loop has been achieved.
Please be aware that there is no enable line right now, and the step/direction interface is enabled by default, and remains active as long as the ODrive is in position control mode. To get the ODrive to go into position control mode at bootup, see how to configure the [startup procedure](commands.md#startup-procedure).
+27 -18
View File
@@ -44,7 +44,7 @@ If your test rig differs, you may be able to run some but not all of the tests.
## How to set up a Raspberry Pi as testing host
1. Install Raspbian Lite on a Raspberry Pi 4.0. I used the NOOBS installer for this.
1. Install Raspbian Lite on a Raspberry Pi 4.0. This is easiest if you have a keyboard, mouse and screen (micro-HDMI!). I used the [NOOBS Lite installer](https://www.raspberrypi.org/downloads/noobs/) for this. Paste the ZIP-file's contents onto a FAT32 formatted SD card (fs type `0b` in `fdisk`) and boot it. Then follow the on-screen instructions.
2. Prepare the installation:
sudo systemctl enable ssh
@@ -52,6 +52,7 @@ If your test rig differs, you may be able to run some but not all of the tests.
# Transfer your public key for passwordless SSH. All subsequent steps can be done via SSH.
sudo apt-get update
sudo apt-get upgrade
# Change /etc/hostname to something meaningful
3. Add the following lines to `/boot/config.txt`:
- `enable_uart=1`
@@ -62,42 +63,50 @@ If your test rig differs, you may be able to run some but not all of the tests.
4. Remove the following arguments from `/boot/cmdline.txt`:
- `console=serial0,115200`
5. Reboot.
5. Append `ODRIVE_TEST_RIG_NAME=[test-rig-name]` to `/etc/environment`. The HWIL tests use this to look up the the file `[test-rig-name].yaml` which is supposed to describe your test rig.
6. Install the prerequisites:
6. Reboot.
sudo apt-get install ipython3 python3-appdirs python3-yaml python3-usb python3-serial python3-can python3-scipy git openocd
7. Install the prerequisites:
sudo apt-get install ipython3 python3-appdirs python3-yaml python3-jinja2 python3-usb python3-serial python3-can python3-scipy python3-matplotlib python3-ipdb git openocd
# Optionally, to be able to compile the firmware:
sudo apt-get install gcc-arm-none-eabi
7. Install Teensyduino and teensy-loader-cli:
8. Install Teensyduino and teensy-loader-cli:
sudo apt-get install libfontconfig libxft2 libusb-dev
wget https://downloads.arduino.cc/arduino-1.8.12-linuxarm.tar.xz
tar -xf arduino-1.8.12-linuxarm.tar.xz
wget https://www.pjrc.com/teensy/td_151/TeensyduinoInstall.linuxarm
wget https://downloads.arduino.cc/arduino-1.8.13-linuxarm.tar.xz
tar -xf arduino-1.8.13-linuxarm.tar.xz
wget https://www.pjrc.com/teensy/td_153/TeensyduinoInstall.linuxarm
chmod +x TeensyduinoInstall.linuxarm
./TeensyduinoInstall.linuxarm --dir=arduino-1.8.12
sudo cp -R arduino-1.8.12 /usr/share/arduino
./TeensyduinoInstall.linuxarm --dir=arduino-1.8.13
sudo cp -R arduino-1.8.13 /usr/share/arduino
sudo ln -s /usr/share/arduino/arduino /usr/bin/arduino
git clone https://github.com/PaulStoffregen/teensy_loader_cli
pushd teensy_loader_cli
make
sudo cp teensy_loader_cli /usr/bin/
sudo ln -s /usr/bin/teensy_loader_cli /usr/bin/teensy-loader-cli
popd
curl https://www.pjrc.com/teensy/49-teensy.rules | sudo tee /etc/udev/rules.d/49-teensy.rules
8. Add the following lines to `/etc/udev/rules.d/49-stlinkv2`:
9. Add the following lines to `/etc/udev/rules.d/49-stlinkv2.rules`:
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE:="0666"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE:="0666"
9. `sudo ../../odrivetool udev-setup`
10. `sudo mkdir /opt/odrivetest && sudo chown $USER /opt/odrivetest`
10. `sudo udevadm trigger`
11. At this point you need the ODrive repository. See next section to sync it from your main PC. We assume now that you navigated to `tools/odrive/tests/`.
11. Run once after every reboot: `sudo ipython3 --pdb test_runner.py -- --setup-host --test-rig-yaml ../../test-rig-rpi.yaml`
12. `sudo ../../odrivetool udev-setup`
13. `sudo udevadm trigger`
14. Run once after every reboot: `sudo -E ipython3 --pdb test_runner.py -- --setup-host`
## SSH testing flow
@@ -109,14 +118,14 @@ To flash and start remote debugging:
1. Start OpenOCD remotely, along with a tunnel to localhost: `ssh -t odrv -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink-v2.cfg' '-f' 'target/stm32f4x_stlink.cfg'\""`
You can keep this open for multiple debug sessions. Press <kbd>Ctrl</kbd>+<kbd>C</kbd> to quit.
2. Compile the firmware
3. In VSCode, select the run configuration "Debug ODrive via external server" and press Run. In contrast to the other configurations, this will flash the new firmware before dropping you into the debugger.
2. Compile the firmware.
3. In VSCode, select the run configuration "Debug ODrive v3.x/v4.x - Remote" and press Run. This will flash the new firmware before dropping you into the debugger.
To run a test:
rsync -avh -e ssh /path/to/ODriveFirmware/ odrv:/opt/odrivetest --exclude="Firmware/build" --exclude="Firmware/.tup" --exclude=".git" --delete
rsync -avh -e ssh /path/to/ODriveFirmware/ odrv:/opt/odrivetest --exclude="Firmware/build" --exclude="Firmware/.tup" --exclude=".git" --exclude="GUI" --delete
ssh odrv
> cd /opt/odrivetest/tools/odrive/tests/
> ipython3 --pdb uart_ascii_test.py -- --test-rig-yaml ../../test-rig-rpi.yaml
> ipython3 --pdb uart_ascii_test.py
+3 -3
View File
@@ -4,11 +4,11 @@
Thermistors are elements that change their resistance based on the temperature. They can be used to electrically measure temperature. The ODrive itself has thermistors on board near the FETs to ensure that they don't burn themselves out. In addition to this it's possible to connect your own thermistor to measure the temperature of the connected motors. There are two types of thermistors, Negative Temperature Coefficient (NTC) and Positive Temperature Coefficient (PTC). This indicates whether the resistance goes up or down when the temperature goes up or down. The ODrive only supports the NTC type thermistor.
## FET thermistor
The temperature of the onboard FET thermistors can be read out by using the `odrivetool` under `<axis>.fet_thermistor.temp`. The odrive will automatically start current limiting the motor when the `<axis>.fet_thermistor.config.temp_limit_lower` threshold is exceeded and once `<axis>.fet_thermistor.config.temp_limit_upper` is exceeded the ODrive will stop controlling the motor and set an error. The lower and upper threshold can be changed, but this is not recommended.
The temperature of the onboard FET thermistors can be read out by using the `odrivetool` under `<axis>.motor.fet_thermistor.temperature`. The odrive will automatically start current limiting the motor when the `<axis>.motor.fet_thermistor.config.temp_limit_lower` threshold is exceeded and once `<axis>.motor.fet_thermistor.config.temp_limit_upper` is exceeded the ODrive will stop controlling the motor and set an error. The lower and upper threshold can be changed, but this is not recommended.
## Connecting motor thermistors
To use your own thermistors with the ODrive a few things have to be clarified first. The use of your own thermistor requires one analog input pin. Under `<axis>.motor_thermistor.config` the configuration of your own thermistor is available with the following fields:
To use your own thermistors with the ODrive a few things have to be clarified first. The use of your own thermistor requires one analog input pin. Under `<axis>.motor.motor_thermistor.config` the configuration of your own thermistor is available with the following fields:
* `gpio_pin`: The GPIO input in used for this thermistor.
* `poly_coefficient_0` to `poly_coefficient_3`: Coefficient that needs to be set for your specific setup more on that in [Thermistor coefficients](#thermistor-coefficients).
@@ -25,7 +25,7 @@ The way this works is that the thermistor is connected in series with a known re
To use a thermistor with the ODrive a voltage divider circuit has to be made that uses `VCCA` as the power source with `GNDA` as the ground. The voltage divider output can be connected to a GPIO pin that supports analog input.
## Thermistor coefficients
Every thermistor and voltage divider circuit is different and thus it's necessary to let the ODrive know how to relate a voltage it measures at the GPIO pin to a temperature. The `poly_coefficient_0` to `poly_coefficient_3` under `<axis>.motor_thermistor.config` are used for this. The `odrivetool` has a convenience function `set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, Tmax)` which can be used to calculate and set these coefficients.
Every thermistor and voltage divider circuit is different and thus it's necessary to let the ODrive know how to relate a voltage it measures at the GPIO pin to a temperature. The `poly_coefficient_0` to `poly_coefficient_3` under `<axis>.motor.motor_thermistor.config` are used for this. The `odrivetool` has a convenience function `set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, Tmax)` which can be used to calculate and set these coefficients.
* `axis`: Which axis do set the motor thermistor coefficients for (`odrv0.axis0` or `odrv0.axis1`).
* `Rload`: The Ohm value of the resistor used in the voltage divider circuit.
+2 -2
View File
@@ -11,7 +11,7 @@ Table of Contents:
<!-- /TOC -->
## Error codes
If your ODrive is not working as expected, run `odrivetool` and type `dump_errors(odrv0)` <kbd>Enter</kbd>. This will dump a list of all the errors that are present. To also clear all the errors, you can run `dump_errors(odrv0, True)`.
If your ODrive is not working as expected, run `odrivetool` and type `dump_errors(odrv0)` <kbd>Enter</kbd>. This will dump a list of all the errors that are present. To clear all the errors, you can run `odrv0.clear_errors()`.
With this information you can look up the API documentation for your error(s):
* Axis error flags documented [here](api/odrive.axis.error).
@@ -59,7 +59,7 @@ when you call `dump_errors()`, you have a version mismatch between odrivetool an
* **Linux**: Type `lsusb` to list all USB devices. Verify that your ODrive is listed.
* **Linux**: Make sure you [set up your udev rules](getting-started#downloading-and-installing-tools) correctly.
* **Windows**: Right-click on the start menu and open "Device Manager". Verify that your ODrive is listed.
* **Windows**: Use the [Zadig utility](http://zadig.akeo.ie/) to verify the driver is set to `libusb-win32`. Note that there are two options listed in Zadig for Odrive: `ODrive 3.x Native Interface (Interface 2)` and `ODrive 3.x CDC Interface (Interface 0)`. Only the native interface should have `libusb-win32` while the CDC interface should use `WinUSB`.
* **Windows**: Use the [Zadig utility](http://zadig.akeo.ie/) to verify the driver is set to `WinUSB` or `libusb-win32`. Note that there are two options listed in Zadig for ODrive: `ODrive 3.x Native Interface (Interface 2)` and `ODrive 3.x CDC Interface (Interface 0)`. Only the driver setting of the native interface is important to `odrivetool`.
* Ensure that no other ODrive program is running
* Run `odrivetools` with the `--verbose` option.
* Run `PYUSB_DEBUG=debug odrivetools` to get even more log output.
+19 -6
View File
@@ -1,12 +1,25 @@
# UART Interface
The ODrive's UART interface is enabled by default (see `odrv0.config.enable_uart`). Currently it runs both the [Native Protocol](native-protocol) and the [ASCII Protocol](ascii-protocol) at the same time.
The ODrive's UART_A interface is enabled by default with a baudrate of 115200 on the pins as shown in [Pinout](pinout).
The baudrate is 115200 by default and can be changed using `odrv0.config.uart_baudrate`. Changes to the UART configuration require a reboot to take effect.
To use UART connect it like this:
* Tx of the ODrive <=> Rx of other device
* Rx of the ODrive <=> Tx of other device
* GND of the ODrive (use any GND pin on J3 of the ODrive) <=> GND of the other device
The logic level of the ODrive is 3.3V. The GPIOs are 5V tolerant.
Pinout:
* GPIO 1: Tx (connect to Rx of other device)
* GPIO 2: Rx (connect to Tx of other device)
* GND: you must connect the grounds of the devices together. Use any GND pin on J3 of the ODrive.
You can use `odrv0.config.uart_a_baudrate` to change the baudrate and `odrv0.config.enable_uart_a` to disable/reenable UART_A. The UART_A port can run the [Native Protocol](native-protocol) or the [ASCII Protocol](ascii-protocol), but not both at the same time. You can configure this by setting `odrv0.config.uart0_protocol` to either `STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT` for the ASCII protocol or `STREAM_PROTOCOL_TYPE_FIBRE` for the native protocol.
### How to use UART on GPIO3/4
If you need GPIO1/2 for some function other than UART you can disable UART_A and instead use UART_B on GPIO3/4. Here's how you do it:
odrv0.config.enable_uart_a = False
odrv0.config.gpio1_mode = GPIO_MODE_DIGITAL
odrv0.config.gpio2_mode = GPIO_MODE_DIGITAL
odrv0.config.enable_uart_b = True
odrv0.config.gpio3_mode = GPIO_MODE_UART_B
odrv0.config.gpio4_mode = GPIO_MODE_UART_B
odrv0.reboot()