1,000,000; pretty zippy, but well within the capability of
the Propeller.
mov t1, tocycles
:loop mov bittimer, cnt
test siomask, ina wc
if_nc jmp #receive
djnz t1, #:loop
What's different about the Dynamixel UART employed
in this object is that it doesn't use circular buffers as we'd
typically see. There is no need since communications in
the system are very well defined: We're going to transmit a
structured known-length packet, and optionally receive a
structured packet. Instead of managing head and tail
pointers, the Dynamixel UART code needs to only know
the location of the buffers and packet length values (in the
hub). We'll set the packet length for transmitting, and
when the UART indicates that it's done we will read the
length of the response packet. We do, in fact, need to
know the length of the received packet as the last byte is
the checksum.
wrlong M_TIMEOUT, par
jmp #checkstate
At the top, we check the state; if it was transmit only
(M_TX), we write M_IDLE back to the hub to indicate that
we're done, and jump back to checkstate.
After setting the SIO pin, hub addresses, and baud
rate timing values, the UART code drops into a loop that
waits for the hub to command a transmission:
Dropping through, we prepare to receive by setting
the SIO pin to input mode. A short loop waits for the start
bit of the first byte of the response packet. When
detected, the code jumps to receive and handles the
response. If there is no response, the loop will run through
and cause us to write M_TIMEOUT to the state variable in
the hub. This feature allows us to scan the Dynamixel buss
to determine the IDs of any connected devices.
checkstate rdlong axstate, par
cmps axstate, M_TX wz, wc
if_e jmp #transmit
cmp axstate, M_TXRX wz, wc
if_e jmp #transmit
jmp #checkstate
Like the transmit loop, the receive loop is
straightforward: after receiving a byte, it stuffs it into the rx
buffer, advances the buffer pointer, and then increments
the packet length counter. A timeout loop is used to
detect the end of the response packet. When the end is
detected, the packet length is written to the hub, and the
UART state value returned to M_IDLE.
The loop will run until getting a state value of M_TX
(transmit only) or M_TXTR (transmit and then receive).
When there is a valid state, the code jumps to transmit:
It's actually quite easy and useful in other low
bandwidth command/response systems (e.g., MODBUS).
Now that we have a working UART, let's build up the
application code.
transmit rdlong txcount, txlenpntr wz
if_z wrlong M_IDLE, par
if_z jmp #checkstate
By Your Command
mov txhub, txbufpntr
The Dynamixel understands seven low-level
commands. Most of the time we'll be using the first three.
This section reads and validates the length of the
transmit packet; assuming all is well, it copies the hub
address of the transmit buffer (in txbufpntr) to a temporary
variable (txhub) which is used in the transmit loop. The
transmit loop itself is unremarkable; read a byte from the
hub, bang it out, increment the working buffer pointer,
continue until all bytes are transmitted. As I stated, the
code is unrolled for best speed, hence a little long for
publication.
However, the others are there for good reason and the
driver allows us to put them to use, as well:
• Ping ($01)
• Read Data ($02)
• Write Data ($03)
• Reg Write ($04)
• Action ($05)
• Reset ($06)
• Sync Write ($83)
At the end of that loop, we need to check if the
packet just transmitted expects a response:
cmp axstate, M_TX wc, wz
if_e wrlong M_IDLE, par
if_e jmp #checkstate
Ping is as simple as its name suggests: It is used to
check for the presence of a device on the buss and to
retrieve any error bits from it. Read Data and Write Data
are a little more flexible than the names suggest; both
allow the transmission of any number of bytes (up to the
size of the Dynamixel memory) from or to the actuator.
turnaround andn dira, siomask
The driver includes a few high-level methods that simplify
the use of Read Data and Write Data.
Reg Write is very much like Write Data except the
14 November 2013