Overview

In case you want to add additional hardware to your MIRO robot, you can do so by using the provided Expansion Port.

Expansion Port

The P3 board has an Expansion Port at UC13:J10 that can be configured in various combinations. It is the 12-way connector marked in blue in the photo alongside. You can access this port from your own software running on-board P3, or provide an interface to expose it to the network allowing you to access it from off-board.

Pinout

The pinout of the port is as follows (note that the physical port is numbered 1-12 from top to bottom, which is upside-down in comparison with this table):

Pinning for UC13:J10
pin nopin nameAM335x ballAM335x functionsoffsetAM335x function at boot
1VDD3V3
2GND0V
3IO0M17 (MDIO)uart5_rxd(2), gpio0_0(7)0x148uart5 rx
4IO1M18 (MDC)uart5_txd(2), gpio0_1(7)0x14Cuart5 tx
5IO2A17 (SPI0_SCLK)spi0_sclk(0), uart2_rxd(1), I2C2_SDA(2), gpio0_2(7)0x150uart2 rx
6IO3B17 (SPI0_D0)spi0_d0(0), uart2_txd(1), I2C2_SCL(2), gpio0_3(7)0x154uart2 tx
7IO4B16 (SPI0_D1)spi0_d1(0), I2C1_SDA(2), gpio0_4(7)0x158gpio0_4
8IO5A16 (SPI0_CS0)spi0_cs0(0), I2C1_SCL(2), gpio0_5(7)0x15Cgpio0_5
9IO6J18 (MII1_TXD3)uart4_rxd(3), gpio0_16(7)0x11Cgpio0_16
10IO7K15 (MII1_TXD2)uart4_txd(3), gpio0_17(7)0x120gpio0_17
11GND0V
12VDD3V3

The numbers in brackets after AM335x functions are the indices of the function into the pinmux (used below when reconfiguring the port). Thus, UC13:J10 can be configured as an SPI port and two UARTs, as three UARTs and an I2C, as eight digital I/O ports, or as some other combination of these ingredients.

More specialised functions are also available on AM3358, but are less likely to be useful; for complete listings, see TI-provided datasheet.

Default configuration

By default, pins IO0 through IO3 are configured as two UARTs: UART5 (IO0/1) is connected to the bluetooth module, and UART2 (IO2/3) is available for use. Pins IO4 through IO7 are configured as GPIO inputs.

UART0 is also enabled by default, and serves the Debug Port (above the Expansion Port) which can be used to login if a network connection is not available; it appears at /dev/ttyO0.

Test UART2

You can configure UART2 physically as a "loop back" by simply connecting pins IO2 and IO3 together. Data sent on IO3 will be received at IO2. Open two terminals on P3, and proceed as follows.

In the first, configure the port (disable echo and text translation, set speed) and start listening.

root@miro:~> stty raw -echo 57600 < /dev/ttyO2 root@miro:~> stty < /dev/ttyO2 speed 57600 baud; line = 0; min = 1; time = 0; -brkint -icrnl -imaxbel -opost -isig -icanon -echo root@miro:~> cat /dev/ttyO2 ...

In the second, send some messages into the port.

root@miro:~> echo ABC > /dev/ttyO2 root@miro:~> echo DEF > /dev/ttyO2 root@miro:~> echo GHI > /dev/ttyO2

If all is well, you'll see the messages appear in the first terminal. If, meanwhile, you scope the loop back line, you'll see bursts of characters such as that shown in the image alongside for "ABC".

Test GPIO in

To listen to the four GPIO inputs, as they are initially configured, on IO4 through IO7, you can run the prepared script, as follows.

root@miro:~> test_gpio_in.sh IO = [ 1 1 1 1 ] IO = [ 1 1 1 1 ] IO = [ 1 1 1 1 ] ... IO = [ 0 1 1 1 ] IO = [ 1 1 1 1 ] IO = [ 1 1 1 1 ] IO = [ 1 0 1 1 ] IO = [ 1 0 1 1 ] IO = [ 1 1 1 1 ] IO = [ 1 1 1 1 ] IO = [ 1 1 0 1 ] IO = [ 1 1 0 1 ] IO = [ 1 1 1 1 ] IO = [ 1 1 1 1 ] IO = [ 1 1 1 1 ] IO = [ 1 1 1 0 ] IO = [ 1 1 1 1 ] ... ^C

If you connect any of the pins IO4 through IO7 to a GND pin (either pin 2 or pin 11 of the Expansion Port) you will see the corresponding indicator go to "0"; during the above recording, each pin was touched in turn by a ground wire. The GPIO pins are configured, by default, with a pull-up resistor, so they read "1" when nothing is connected to them.

Test GPIO out

Any or all of IO4 through IO7 can be reconfigured as outputs easily without rebuilding the device tree. Connect together pins IO4 and IO5, and run the prepared script, as follows.

root@miro:~> test_gpio_out.sh /usr/share/miro/bin/test_gpio_out.sh: line 41: echo: write error: Device or resource busy ... IO = [ 1 1 1 ] IO = [ 0 1 1 ] IO = [ 1 1 1 ] IO = [ 0 1 1 ] IO = [ 1 1 1 ] IO = [ 0 1 1 ] IO = [ 1 1 1 ] IO = [ 0 1 1 ] IO = [ 1 1 1 ] IO = [ 0 1 1 ] IO = [ 1 1 1 ] IO = [ 0 1 1 ] IO = [ 1 1 1 ] IO = [ 0 1 1 ] ^C

The script configures IO4 as an output, and listens to IO5 through IO7. Since IO4 is connected to IO5, you can see the state toggling on each cycle.

The warning "Device or resource busy" will occur if you enable GPIO pin interfaces more than once; this can be ignored.

Reconfiguring Expansion Port

The Expansion Port is configured through the Linux Device Tree mechanism.

If you are using the bluetooth module, you should leave pins IO0/1 in their default configuration as UART5. If not, you can repurpose these pins. In that case, you should stop the daemon from running at startup by commenting out the line towards the bottom of /usr/share/miro/event/system_ready.sh that runs start_daemon.sh.
Ensure that two peripherals that use the same output lines are not enabled at the same time; consult the table above to decide in advance which peripheral set you will use.
Making connections to the Expansion Port can cause electrical damage to your robot; ensure you understand how to connect safely to the port before use.

Review pin configuration

Since device tree operation is somewhat opaque, you may wish to follow the effect that your configuration changes have on the physical device. Proceed as follows to review the pin control registers for pins IO0 through IO7.

root@miro:~> get_expansion_pins_config.sh IO0 (44e10948.0) 00000032 pinctrl-single IO1 (44e1094c.0) 00000002 pinctrl-single IO2 (44e10950.0) 00000031 pinctrl-single IO3 (44e10954.0) 00000001 pinctrl-single IO4 (44e10958.0) 00000037 pinctrl-single IO5 (44e1095c.0) 00000037 pinctrl-single IO6 (44e1091c.0) 00000037 pinctrl-single IO7 (44e10920.0) 00000037 pinctrl-single

The above command queries the state of the pinmux interface of the AM3358 control module. See Chapter 9 of the device Technical Manual (the register addresses are listed in table 9-10) and Chapter 2 of the device datasheet (Table 2-7 lists the eight multiplexed functions available on each physical pin). The above is the default configuration for P3.

For instance, pin IO0 is at offset 0x148 (from base address 0x44E10800), which TM 9-10 indicates is for pin mdio; pin mdio appears in DS 2-7 at ZCZ ball number M17; function 2 of that pin is uart5_rxd. Therefore, the pin control register at offset 0x148 needs to have muxed function 2 selected. Table 9-60 indicates how to read the register value: "0x32" reads back as "receiver enabled, pullup selected, pullup/pulldown enabled, mux function 2", which amounts to a GPIO input pin.

Note that seeing the correct pin control register value in this way is only part of enabling a peripheral; the peripheral's interface (and, thereby, the peripheral) has also to be enabled. Using the device tree performs both tasks, and the peripheral's interface will typically appear under /sys somewhere (see the documentation for the particular peripheral interface for details).

Rebuild device tree

The device tree is stored in binary form on the SD card in /boot.

root@miro:~> ls /boot/am335x-miro.dtb /boot/am335x-miro.dtb

To modify it, you will decompile the device tree binary (.dtb) to a device tree source (.dts) file, make your changes, and then recompile back to binary, before installing the resulting file back to /boot. The device tree compiler dtc is available on-board, but it does not interact with the system so you may find it easier to use your workstation to make modifications. Either way, the procedure is as follows.

Decompile binary

To decompile the .dtb to a .dts, use a command of this form.

$ ls am335x-miro.dtb $ dtc -I dtb am335x-miro.dtb > am335x-miro.dts $ ls am335x-miro.dtb am335x-miro.dts

Modify source file

The source file is plain text—modify it in any text editor.

Recompile binary

To recompile the modified .dts to a .dtb, use a command of this form.

$ ls am335x-miro.dts $ dtc -I dts -O dtb am335x-miro.dtb > am335x-miro.dts $ ls am335x-miro.dtb am335x-miro.dts

Install new binary

Copy the new .dtb to /boot on the SD card, and reboot MIRO to effect the changes.

If you make a mistake, you can restore the original .dtb file to /mnt/rootfs/boot/am335x-miro.dtb from the copy in the MDK at ~/mdk/extern/yocto/am335x-miro.dtb.

Modify device tree

Below are a few examples for modifying your device tree.

UART4

Before we can enable UART4, we first need to disable GPIO pins gpio0_16 and gpio0_17, which share physical pins IO6/7. To do this, search in the .dts for the token expansion_port_pins. Within that section, find the field pinctrl-single,pins, which by default reads <0x158 0x37 0x15c 0x37 0x11c 0x37 0x120 0x37>. Each enabled GPIO pin is represented by a pair in this field, and the first value in each pair is the offset value from the table at the top of this page. Delete the last four entries in this field to deconfigure gpio0_16 and gpio0_17.

am335x-miro.dts
... expansion_port_pins { pinctrl-single,pins = <0x158 0x37 0x15c 0x37>; linux,phandle = <0x3e>; phandle = <0x3e>; }; ...

To enable UART4, we first have to configure the pinmux. Find the token pinmux_uart5_pins in the .dts, and copy it to a section just above. Rename the section to pinmux_uart4_pins: pinmux_uart4_pins, delete the two lines containing the token phandle, and change the pinctrl-single,pins field to read <0x11c 0x33 0x120 0x3>. Note that we have based the value for this field on that used for UART5, with the offsets changes to 0x11c and 0x120 (see table) and the pinmux fields both changed to 3, the mux index (see table).

am335x-miro.dts
... pinmux_uart4_pins: pinmux_uart4_pins { pinctrl-single,pins = <0x11c 0x33 0x120 0x3>; }; ...

Next we have to enable the peripheral UART4; internally, this is known as uart5 (ti,hwmods = "uart5"), which you'll find listed under the token serial@481a8000. Simply change the status field to read okay rather than disabled, and add a reference to the pinmux section at the bottom (two lines starting pinctrl) so that the pinmux gets actioned.

am335x-miro.dts
... serial@481a8000 { compatible = "ti,omap3-uart"; ti,hwmods = "uart5"; clock-frequency = <0x2dc6c00>; reg = <0x481a8000 0x2000>; interrupts = <0x2d>; status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&pinmux_uart4_pins>; }; ...

Recompile and install the device tree to /boot, then reboot your MIRO. When it starts up again, confirm the new pinmux configuration:

root@miro:~> get_expansion_pins_config.sh IO0 (44e10948.0) 00000032 pinctrl-single IO1 (44e1094c.0) 00000002 pinctrl-single IO2 (44e10950.0) 00000031 pinctrl-single IO3 (44e10954.0) 00000001 pinctrl-single IO4 (44e10958.0) 00000037 pinctrl-single IO5 (44e1095c.0) 00000037 pinctrl-single IO6 (44e1091c.0) 00000033 pinctrl-single IO7 (44e10920.0) 00000003 pinctrl-single

Note that the last two pins have changed from pinmux 7 to pinmux 3, reflecting their switch over to the UART peripheral.

To test the new UART port, connect a loop back cable between pins IO6 and IO7, and then run the transmitter and receiver in two separate P3 login terminals, as follows.

root@miro:~> stty < /dev/ttyO4 speed 9600 baud; line = 0; -brkint -imaxbel root@miro:~> stty 57600 raw -echo < /dev/ttyO4 root@miro:~> stty < /dev/ttyO4 speed 57600 baud; line = 0; min = 1; time = 0; -brkint -icrnl -imaxbel -opost -isig -icanon -echo root@miro:~> cat /dev/ttyO4 Hello MIRO! ^C
root@miro:~> echo "Hello MIRO!" > /dev/ttyO4

More or less GPIO

You can disable all the UARTs and just have eight GPIO pins. Update the field pinctrl-single,pins under expansion_port_pins to read <0x148 0x37 0x14C 0x37 0x150 0x37 0x154 0x37 0x158 0x37 0x15c 0x37 0x11c 0x37 0x120 0x37>. Disable UART2 and UART5, which are known internally as "uart3" and "uart6"; set the field status to read disabled under sections serial@48024000 and serial@481aa000. Reboot, and see the scripts test_gpio_*.sh to get started.

To have less GPIO, you can remove entries from the pinctrl-single,pins field. To have none at all, however, you can simply find the section expansion_port_pins_helper and set status to disabled; this frees all GPIO pins for other uses.

With UART5 disabled, the Bluetooth interface will not function (and the Bluetooth module should be physically disconnected from the P3 board to avoid any possible electrical faults).

I2C

I2C1 is section i2c@4802a000 and I2C2 is section i2c@4819c000. To enable the former, first remove the entries 0x158 0x37 0x15c 0x37 from pinctrl-single,pins of expansion_port_pins to release the pins from use by the GPIO module, then change status under i2c@4802a000 to okay. Rebuild, reboot, and IO4/5 are now configured as I2C1, the interface for which will appear at /dev/i2c-1.

root@miro:~> get_expansion_pins_config.sh IO0 (44e10948.0) 00000032 pinctrl-single IO1 (44e1094c.0) 00000002 pinctrl-single IO2 (44e10950.0) 00000031 pinctrl-single IO3 (44e10954.0) 00000001 pinctrl-single IO4 (44e10958.0) 00000032 pinctrl-single IO5 (44e1095c.0) 00000032 pinctrl-single IO6 (44e1091c.0) 00000037 pinctrl-single IO7 (44e10920.0) 00000037 pinctrl-single

We can test this interface by running the detection program i2cdetect.

root@miro:~> i2cdetect -r -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
Attach a scope to pins IO4 and IO5 to see the physical device become active.
Both I2C interfaces are set to run at 100kHz—change the speed by setting the field clock-frequency (for example, use clock-frequency = <0x61a80> for 400kHz).
i2c@4802a000 is known internally as "i2c2", and i2c@4819c000 as "i2c3"; try to ignore this, these refer to physical devices I2C1 and I2C2, respectively. I2C0 (aka "i2c1") is used on-board UC13 and its configuration should not be changed.

SPI

To use the SPI port, find spi@48030000 and set the field status to okay; find serial@48024000 (UART2) and set status to disabled. After installing the .dtb and rebooting, you'll see a pin configuration like this:

root@miro:~> get_expansion_pins_config.sh IO0 (44e10948.0) 00000032 pinctrl-single IO1 (44e1094c.0) 00000002 pinctrl-single IO2 (44e10950.0) 00000010 pinctrl-single IO3 (44e10954.0) 00000030 pinctrl-single IO4 (44e10958.0) 00000010 pinctrl-single IO5 (44e1095c.0) 00000010 pinctrl-single IO6 (44e1091c.0) 00000000 pinctrl-single IO7 (44e10920.0) 00000000 pinctrl-single

If you followed the instructions above exactly, you'll see that SPI is now configured on IO2-IO5, but that IO6 and IO7 are not configured as GPIO anymore. That's because the device tree is still requesting pins IO4 through IO7 for GPIO, and IO4 and IO5 are not available (they are configured as SPI). To reinstate the GPIO function on IO6 and IO7, you need to remove the IO4 and IO5 configuration from expansion_port_pins, as above (remove the first four entries of eight from pinctrl-single,pins).

You will now find a device at /dev/spidev*. Learn more about that device here. You can test the SPI interface as follows.

$ test_spi.sh 01 02 04 08 00 00 00 00
If you watch on pins IO2 and IO4, you'll see the clock and the outgoing data, as shown here.

Daemon

One possible way to use the Expansion Port would be to write a daemon to run on P3 that exposes the port to the network, publishing its state on a ROS node and/or receiving signals through the ROS interface to push onto the port. More generally, you could incorporate use of the GPIO port into the wider control system of your robot or robots. Of course, you can exchange data to or from any of the available peripherals in a similar way.

A very simple port reading daemon is supplied; try it out as follows. Pin IO4 should be configured as a GPIO input (the default configuration for this pin).

First, on your workstation, run roscore. Next, on MIRO, enable and test the GPIO input pin.

$ echo 4 > /sys/class/gpio/export $ echo in > /sys/class/gpio/gpio4/direction $ cat /sys/class/gpio/gpio4/value 1

Thirdly and finally, compile and start the daemon on MIRO:

$ cd /usr/share/miro/daemon $ g++ gpio_daemon.cpp -I/opt/ros/kinetic/include -o gpio_daemon -L/opt/ros/kinetic/lib -lroscpp -lrosconsole -lroscpp_serialization $ export ROS_IP=`get_ROBOT_IP.sh` $ ./gpio_daemon

Now you can run rostopic echo /miro_robot_daemon_test on your workstation to observe the outgoing signal. If you touch a wire between pin IO4 and GND (available on pin 2 or pin 11 of the Expansion Port), you'll see the signal change at your workstation (pin IO4 is configured with a pull-up resistor, so connecting it to ground will change its level).

$ rostopic echo /miro_robot_daemon_test data: 1 --- data: 1 --- data: 0 --- data: 0 ...