Developing Firmware for Robobricks2 Modules Introduction This document is meant to capture my notes about developing firmware for a Robobricks2 module. The overall goal is to write enough down so that people who are so inclined can take a board that has been designed for Robobricks2 bus and potentially write some working firmware for the module. The audience for this document is people who are comfortable with software development. This document will be a bit PIC centric, but somebody who is more comfortable with another microcontroller vendor is free to develop boards using their preferred microcontroller. Overview CAN Bus Overview The Robobricks bus uses the CAN bus physical layer to transmit data. CAN bus is a two wire differential transmission bus. What this means is that one wire on the bus always carries the inverse signal of the other wire. The two wires of the CAN bus are labeled CANH and CANL for CAN High and CAN Low respectively. When a "1" is sent on the CAN bus, CANH is at 5 volts and CANL is at 0 volts. Conversely, when a "0" is sent on the CAN bus, CANH is at 0 volts and CANL is at 5 volts. The first advantage of CAN bus is that it has 10 volts of noise immunity even though it only uses 5 volts. The *difference* in voltages measured across the CANL and CANH wires is 10 volts between the bit states. In particular, when a "1" is being transmitted, CANH - CANL is 5 volts. When a "0" is being transmitted, CANH - CANL is -5 volts. The difference between those two voltages is 5 volts - (-5 volts) or 10 volts. A CAN bus receiver figures out the state of the bus by computing CANH - CANL. If CANH - CANL is greater than zero, it reports the bus as being in the "1" state and if it is less than zero, the bus is reported to be in the "0" state. The second advantage of the CAN bus is that it has excellent common mode noise rejection. This is a result of always running the CANH and CANL wires side by side. Robobricks2 uses 10 conductor ribbon cable to transmit the CAN bus and the middle two conductors are assigned to CANH and CANL. If there is any electrical noise inducing source close to the CAN bus, the same noise tends to be induced on *both* lines. Since a CAN bus receiver computes the difference from *both* lines, the noise tends to be cancelled out. If N volts of noise is induced on both lines of the CAN bus, CANH + N - (CANH - N) results in CANH - CANL, since the N terms cancel out. This means that CAN bus can be opperated around fairly noisy environments, like electrical motors, and still operate quite reliably. The third advantage of the CAN bus is that it does not require any special care to control who is transmitting data on the bus. For CAN bus, it is possible for more than one module to transmit at the same time. The CAN bus will present the wired OR of the what is being transmittted. This is in contrast to RS-285 differential bus transmission specification. For RS-285, a transceiver chip can be either transmitting signal onto the bus or receiving a signal from the bus, but not both at the same time. What this means is that the microcontroller that connects to the RS-245 bus must provide another control line that tells the transceiver which direction (sending or receiving) it is in. For CAN bus, {discuss this more} A disadvantage of the CAN bus is that it requires proper termination in order to obtain reliable communication. Termination refers to a small resistor that tied between the CANH and CANL lines at each end of the cable. The termination resistor prevents nasty signal reflections that degrade bus bandwidth. Robobricks Bus The Robobricks2 bus is a 10-wire bus. It has 4-wires for battery power, 4-wires for logic power, and 2 wires for the CAN bus signalling - (CANH and CANL). The battery power has 2-wires of ground and 2-wires connectted to a battery that can be between 6 and 12 volts. The battery power bus is not regulated. As the battery is drained, the voltage will slowly lower. The "logic" bus is 2-wires of ground and 2-wires of 5 volts of regulate power. The Robobricks2 bus is organized around a 10-wire insulation displacement cable (IDC). The headers that are crimped onto the 10-wire cable are female with a 2x5 arrangement of pins on a .1 inch spacing. The Robobricks2 wires are arranged such that twisting the cable around by 180 degrees will not cause any shorts. In order to avoid accidentally plugging the bus together incorrectly, Robobricks2 modules use 2x5 polarized shrouded connectors. Since the female connectors on the ribbon cable are polarized, it is basically impossible to plug module into the bus incorrectly. The reason why 10-wire ribbon cable was selected is because it easy to obtain, and easy to crimp connectors onto it. If you have a vice, the female connector can be placed over the desired cable location and squeezed together with a vice. If you do not own a small vice, I strongly recommend that you purchase one. They can be obtained for $10-$20 without really looking very hard. Vices are very versatile tools that can be used for many purposes. There are two reasons why there are two power buses on the RoboBricks bus. First, is that it was hard to find ribbon cable connectors smaller than 10. Since the wires were there, I had to find a reason to use them. Second, numerous people were reporting round bounce problems in their robots. Ground bounce is a fairly involved topic and is discussed immediately below. Ground bounce occurs when their is a voltage difference between two modules that "think" they are sharing the same ground. Ground bounce reduces data transmission noise margin and can cause nasty situations like randomly restarting your microcontroller. When your robot is suffering from ground bounce it just becomes flaky and unreliable. The best way to fix ground bounce is to not have it in the first place. A typical cause of ground bounce is to have a device that suddenly draws a more current (e.g. a motor starting up.) In the case of a motor starting up, the sudden current surge will cause the electronics around the motor to shift their voltage relative to everything else in the system. The Robobricks2 bus is designed to minimize or entirely elminate ground bounce. The first step towards controlling ground bounce is to partition devices into those that experience sudden changes in current draw and those that are more civilized and keep there current draw very constant. For example, all servos, motors, IR sensors, sonar sensors, must live off of the battery bus to keep their nasty habits from interering with the gentile devices that live on the logic bus. A device is allowed onto the logic bus only if it agrees to use a very consistent amount of power draw. Since all the devices on the logic bus draw a consistant amount of power that does not change much over time, the logic power bus does not experience much ground bounce at all. Since all the microcontrollers live on the logic bus, none of them should experience ground bounce problems. The second step towards minimizing ground bounce problems it carefully ensure that the grounds of the battery and logic bus are tied together at exactly one place. The one place is as close to the battery as possible. If there are two batteries being used, one for the logic bus and the other for the battery bus, the two batteries should have their minus (-) leads tied together with a very short wire. By having exactly one ground connection between the two buses, any ground bounce on the battery bus can not "leak" over to the logic bus. The third step towards minimizing ground bounce is to design Robobricks2 modules properly. All devices that live on the logic bus side have small .1uF capacitors near each logic device to control high frequency noise. If a module needs to connect to both buses, an optoisolator is used to ensure that there is no need to have a common ground on the module. For example, a motor controller module will have a microcontroller that lives on the logic bus that controls motor direction and speed. The H-bridge circuit that actually sends current into the motor will get its power from the battery bus (or for really power hungary devices, a private power bus.) The connections between the microcontroller and the H-bridge are done via one or more opto-isolators. The opto-isolators provide electical isolation between the two sides of the circuit. Ultimately, the Robobricks2 bus should provide a *very robust* architecture to build your robot out of. By using CAN bus technology for inter module data transmission, problems from eletrical noise should be a thing of the past. The careful design of the modules and bus to separate battery power and logic power grounds should make ground bounce problems go away. The net result is robot flakyness should be because of software problems, not electrical problems. Addressing All modules on a Robobricks2 bus must have a unique 8-bit address between 0 and 254. Address 255 is reserved for a global reset condition. If two modules on the same bus accidentally happen to have the same address, BAD THINGS HAPPEN. When a module is initially programmed, it has a "factory" address. Using the address assign protocol, it is possible to set the address to any number between 0 and 254. The way a bus is scanned is by having the scanning software iterate between 0 and 254. The scanning software attempts to select module 0. If it gets an acknowledge back from the selectiong, it has found the module. If a timeout occurs, the next module is selected, this repeats until all 255 module addresses have been attempted. Since there are only a maximum of 255 different addresses on the bus, the bus scan can typically be done in a second or less. When a module has been located at an address, the identify protocol is used to identify which module is present. UARTS One way to think of the Robobricks2 bus is that it is like a everybody sitting in a circle in small room. When one person speaks everybody in the room can hear what they say. If two people speak at the same time, nobody can figure out what is said, because the two speakers are interfering with one another. Thus, it is very important to have a protocol that ensures that nobody ever attempts to talk at the same time; otherwise, confusion will ensue. UART stands for Universal Asynchronous Receiver/Transmitter. Many microcontrollers have a hardware UART. Each module connected to the Robobricks2 bus must have a hardware UART. The basic concept behind a UART is that the software in module shoves an 8-bit number between 0 and 255 into its UART, and shortly there after all the UART's listening on the bus receive the exact same value. In fact, it is a little more complicated than that since the Robobricks2 bus requires the use of UART's that support the sending and receiving of 9-bit numbers rather than 8-bit numbers. The 9th bit is used to distinguish between data and addresses. When a module sends an 8-bit value with the 9th bit set, it is basically saying "Hey, I want to talk to module S", where S is the value of the 8-bit number. After the initiating module has got the attention of the desired module, data is transferred between the two modules as sequence of bytes with the 9th bit clear. Now it is time to get into some specifics about UART's. Since not all UART's are the same, I will focus on the addressable UART's (i.e. AUSART) that are on many of the PIC microcontrollers from Microchip. The term "addressable" means that it can send and receive the 9th bit. The UART registers are partitioned into baud rate control, transmit control, and receive control. Baud rate control is discussed a bit further below. For transmitting there is the TXREG for transmitting the 8-bit value, and the TXSTA register for configuring and controlling transmision. The important bits in TXSTA are: TX9: 1 => Enable 9-bit transmission 0 => Disable 9-bit transmission TXEN: 1 => Enable transmission 0 => Disable transmission SYNC: 1 => Select synchronous mode 0 => Select asynchronous mode (use this) TRMT: 1 = Transmit buffer is empty 0 = Transmit buffer is full TX9D: The 9th bit Also, there is another bit in the PIR1 register called TXIF: TXIF: 1 => Transmitter ready for another byte 0 => Transmitter is not ready Prior to transmitssion, SYNC is set to 0 put the USART into into asynchronous mode and it is enabled by setting TXEN to 1: $sync := 0 $txen := 1 Before sending a byte, the 9th bit must be set into TX9D. This bit is not automatically cleared after transmission. So, once TX9D is set, it remains set until it is explicitly cleared. Any time the TXIF flag is set, the UART is ready to transmit another byte. Thus, the sequence for sending a byte looks as follows: $tx9d := (9th bit) while !$txif do_nothing $txreg := (8-bit value) Now changing focus the receiver there are two primary registers -- RCREG for receiving 8-bits of data and the RCSTA receiver control register. The important bits in RCSTA: SPEN: 1 => Serial port enable 0 => Serial port disable RX9: 1 => Enable 9-bit reception 0 => Disable 9-bit reception CREN: 1 => Enable receiver 0 => Disable receiver ADDEN: 1 => Enable address detection on 9th bit 0 => Disable address detection on 9th bit FEFF: 1 = Framing error 0 = No framing error OERR: 1 = Overrun error 0 = No overrun error RX9D: 9th bit of received data Again, the PIR1 register contains the RCIF bit that is set whenever there is a byte of data ready for read out. Prior to reception, both SPEN, RX9 and CREN need to be set. The ADDEN bit is discussed a little later below. $spen := 1 $rx9 := 1 $cren := 1 In order to receive a byte, the RCIF flag is checked to ensure that data is available, the RX9D is read to get the 9th bit, and the 8-bit data byte is read out of RCREG. The code below shows the basic code sequence: while !$rcif do_nothing (9th received bit) := $rx9d (8-bits received data) := $rcreg Now there are two possible errors that occur on reception a framing error or an overrun error. A framing error occurs when the stop bit is not found. An overrun error occurs when a new is received before the previous one is read out. Framing errors are extremely rare in this system. When an overrun error occus, the OERR bit is set. Both the FERR and OERR bits are cleared by clearing CREN and then setting it again. Unfortunately, once OERR is set, it MUST be cleared to receive data again. Now the complete code for receiving a byte that deals with errors looks more like this: while !$rcif do_nothing # Read RX9D, FERR, and OERR all in shot: rcsta_save := $rcsta data := $rcreg if rcsta_save & 6 != 0 # An error occured, clear it: $cren := 0 $cren := 1 Now the time has come to discuss how the ADDEN bit is used. {discuss ADDEN bit here} All Robobricks2 modules must have a UART that supports 9-bit data transmission and reception. This UART's are typically called Addressable UART's or AUART's for short. Robobricks2 uses the 9th bit to specify address selection. If the 9th bit is set, it means "select module". If the 9th bit is clear, it means "transmit command or data". The key thing to understand about the CAN bus, is that anytime a 9byte is sent, the CAN bus will immediately reflect that value back the UART reciever. Thus, If you send the character "A", the next character that is received will be "A". Thus, the first thing you do after transmitting a byte is remember to read and ignore the byte that you just transmitted. This happens so frequently, that it is usefully encoded in a routine. Command Set Design The CAN bus is capable of data transmission that approaches 1 million bits per second. Indeed, many of the Robobricks2 modules are fully capable of transmitting a stream of bytes with no additional spacing between the bytes. Unfortunately, there are many Robobricks2 modules that are not prepared to receive data quite that fast. A UART buffer overrun condition occurs whenever a UART receives another byte before the previous byte has been removed from its buffer. The overarching bus protocol requirement is to ensure that no module experiences a receive buffer overrun condition. The simplest way to ensure that buffer overrun does not occur is always alternate sending a byte and receiving a byte. This kind of exchange is shown below: Master Slave ====== ===== Send [M1] Receive [M1] Send [S1] Receive [S1] Send [M2] Receive [M2] Send [S2] Receive [S2] Send [M3] Receive [M3] Send [S3] Receive [S3] ... There is an important issue that needs to be understood before we proceed. Whatever is being transmitted on the CAN bus is sent to all modules on the bus without exception. There can never be any buffer overrun condition with such a protocol because. Many master modules can not reliably receive back to back bytes. If you need to return two or more bytes of data, be sure to insist that the master send a response byte between each returned byte: However some "master" modules can receive multiple bytes back to back without any problems. For these modules it is possible to design the command protocol to squirt out multiple bytes. As long as you provide both a prompt style of accessing the data, it is perfectly acceptable to provide a method that does not need the intervening prompt bytes: Getting Started Development works best in an increment mode. We start out with the minimal amount of code that works and then slowly expand from there. In particular, we initialize the UART and then implement a tight loop. Program 1: The HeartBeat Test: The purpose of the heartbeat test is to verify that you have powered up the microcontroller properly. It basically takes one output pin and repeatably toggles it. The uCL code looks as follows: ucl 1.0 # First line of uCL program is always this library $pic16f688 # Library that defines with microcontroller # is being used. configure fosc=hs # Force the microcontroller us use the # "High Speed" mode for the 16MHz resonator. package pdip # The 'F688 is in Plastic Dual In-line Package # (i.e. PDIP) pin 1 = power_supply # Pin 1 is a power supply pin 2 = osc1 # Pin 2 is one end of the 16MHz resonator pin 3 = osc2 # Pin 3 is the other end of the the resonator pin 4 = ra3_out, name=heart # Pin 4 is the heart beat output pin pin 5 = rx, name=rx # Pin 5 is the UART receive pin, hooked up # to the CAN tranceiver receive pin pin 6 = tx, name=tx # Pin 6 is the UART transmit pin, hooked up # to the CAN transceiver transmit pin pin 7 = ... pin 14 = ground # Pin 14 is the ground pin origin 0 # Place code starting at address 0 procedure main # Define the main procedure arguments_none # The main procedure has no argments returns_nothing # The main procedure returns nothing # (actually, it never returns) # The uCL compiler insert the code to # initializes the I/O ports right here. loop_forever # Start an infinite loop heart := 1 # Force heartbeat pin high heart := 0 # Force heartbeat pin low # Repeat forever It is not a very long program, but it forces you to download and install the uCL compiler. It also forces you to learn how to take the .hex output file from the compiler and burn it into your 'F688 chip. Once you have burned the program above into your 'F88 chip and powered it up, it will immediately initilize the I/O ports on the chip and generate a signal that rapidly toggles on pin 4 of the chip. On an oscilliscope, the waveform is a square wave with a 25% duty cycle. If you do not have an oscilloscope, a logic probe placed on pin 1 should show changing signal. Program 2: The UART Echo Program The UART echo program is the next step in bringing up a robobrick module. It takes whatever it receives from the UART, adds 1 and send it out. Since this is a CAN bus, whatever is sent out will be immediately echoed back, so we clear the echo as well. The program looks as follows: ucl 1.0 # The first line of every uCL program library $pic16f688 # Library that defines with microcontroller # is being used. library clock16mhz # Define the clock speed library $uart # Define a few UART access routines constant $eusart_clock = clock_rate # Define the oscillator speed for UART constant $eusart_factor = 4 # Define the EUSART clock scake factor library $eusart # Define EUSART baud rates configure fosc=hs # Configure to use 16MHz "high speed" resonator package pdip # Use Plastic Dual In-line Package (PDIP) pin 1 = power_supply # Pin 1 is a power supply pin 2 = osc1 # Pin 2 is one end of the 16MHz resonator pin 3 = osc2 # Pin 3 is the other end of the the resonator pin 4 = ra3_out, name=heart # Pin 4 is the heart beat output pin pin 5 = rx, name=rx # Pin 5 is the UART receive pin, hooked up # to the CAN tranceiver receive pin pin 6 = tx, name=tx # Pin 6 is the UART transmit pin, hooked up # to the CAN transceiver transmit pin pin 7 = ... pin 14 = ground # Pin 14 is the ground pin origin 0 # Place code starting at address 0 procedure main # Define the main procedure arguments_none # The main procedure has no argments returns_nothing # The main procedure returns nothing # (actually, it never returns) # The uCL compiler insert the code to # initializes the I/O ports right here. local command byte # A variable to store received byte into # Initilize the UART: # The UART requires that both the TX # and RX pins be defined as inputs. $trisc@5 := 1 # Force the TX pin to be an input $trisc@4 := 1 # Force the RX pin to be an input # Set up $TXSTA register: $txsta := 0 # First zero it out. $tx9 := 1 # Force UART into 9-bit transmit mode $txen := 1 # Enable the transmitter $brgh := 1 # Use Baud Rate Generator High mode # Set up $RCSTA register: $rcsta := 0 # First zero it out $rx9 := 1 # Force UART into 9-bit receive mode $cren := # ???? $adden := 0 # ??? # Set the baud rate generator stuff: baudctl := 0 # First zero it out $brg16 := 1 # xxx $spbrg := $eusart_625000_low $spbrgh := $eusart_625000_high loop_forever while !$rcif # Wait for a byte to show up do_nothing # Do nothing until it does show up # We have a byte waiting for us command := $rxreg # Store received byte into variable $txreg := command + 1 # Transmit response: # The byte we just transmitted is going # show up in the UART receive buffer. while !$rcif # Wait until "echo" byte shows up. do_nothing # Do nothing until it shows up. command:= $rxreg # Clear it out of the UART This program is pretty straight forward. The first chunk of code intializes the UART: 1) We get both the TX (transmit) and RX (Receive) pins set to be inputs. (Honest, the PIC16F688 documentation says to do that even though TX will actually be an output. Go figure.) 2) Next, we get the UART transmitter set up. 3) Next, we get the UART receiver set up. 4) Finally, we get the baud rate generator set up. The main body of the code is just an infinite loop that does the following: 1) Wait for a byte. 2) Read the byte when it shows up. 3) Increment the received byte 4) Send the incremented byte. 5) Wait for the incremented byte to "echo" into the receive buffer. 6) Clear out the "echo" byte. In order to test this all out, plug your module onto a bus in conjunction with a Wirehost and a Controller28. The WireHost allows us to send and receive bytes using a simple terminal emulator. The Controller28 is just being used as a power supply to provide power for the logic bus. Program 3: A real Robobrick The next program implements ...