Page 1 of 1

BrickPi Information

Posted: 20 Oct 2013, 00:35
by mattallen37
This is a list of information about the BrickPi, and how to use it. It will likely be modified and added to in the future.

Terms Explained
"RPi" means "Raspberry Pi"
"RPi A" means "Raspberry Pi Model A"
"RPi B" means "Raspberry Pi Model B"
"V" (e.g. 9V) is volts
"A" (e.g. 1A) is amps
"mA" (e.g. 300mA) is mili-amps (1/1000ths of an amp)
"W" (e.g. 1.6W) is watts
"BrickPi Basic Power" vs. "BrickPi Advanced Power"
There are two versions of the BrickPi. The first is called "BrickPi Basic Power", and uses a linear 5V regulator (part number LM7805). The other, called "BrickPi Advanced Power" uses a switching 5V regulator (part number LM2576).
BrickPi 5V Regulators
The 5V regulator on the BrickPi is what regulates the 9V input down to 5V, to be used by the BrickPi as well as the RPi. This allows for robots to be battery operated, without the need to be tethered for the RPi to be powered.

Linear Regulator
simple, inexpensive, and not efficient. It essentially has a "variable electricity valve" that it adjusts in order to keep the output at exactly 5V. Unfortunately it wastes the extra voltage (anything above the output level), and turns this waste into heat. If it has 9V on the input, and it's regulating at 5V, it wastes the 4V difference. If there is a 400mA load on the regulator, 400mA (the load) times 4V (the difference in voltage), equals 1.6W of electricity that needs to be wasted.

Switching Regulator
rather expensive, very efficient. It sends pulses of 9V through an inductor, which are then filtered with a large capacitor, making the efficiency much greater than that of a linear regulator. The overall power consumption is lower, and the circuit remains cooler.
BrickPi Power Supply
Source
Even through the RPi/BrickPi will run when powered by 5V (e.g. through the RPi USB jack), it isn't recommended. If you are using motors, it is required that you properly power the BrickPi through the power header (otherwise the motor drivers are being used outside of what they are rated for). Supplementing the 9V provided to the BrickPi, with 5V feeding directly to the RPi should be safe, but note that the two 5V supplies will be directly connected.

Voltage
The BrickPi should be powered by 7.2-9.6 VDC, provided through the BrickPi power header.

Current
Ideally the power supply should be able to provide about 1A plus 1A per motor.

Regulator Requirements
The 5V regulators should be powered by a minimum of 7.2V, and not to exceed 35V.

Motor Driver Requirements
The motor drivers are rated for 4.5-36V, and 1.1A continuous (2A peak).

Motor Requirements
NXT motors are considered 9V motors, and I recommend staying at or below this. The motor drivers cause some voltage drop (roughly 2V total, when the motors are at moderate load), so powering the BrickPi at 11V would mean the motors actually receive about 9V. Each NXT motor will draw about 60mA with no load, around 550mA under moderate load, and up to 2A when stalled. I recommend allowing for a minimum of 700mA per motor, but ideally at least 1A.

BrickPi Circuits Requirements
The BrickPi circuit values have been calculated for voltages up to 9.6V. However, the only circuit that this limitation applies to, is the 9V pullup circuit for powering active analog sensors, and the NXT Ultrasonic sensor. It should be safe to go higher than 9.6V, as long as you only enable the 9V pullup when the proper sensor is connected (e.g. do not plug in the Lego NXT Color sensor, when the port is configured for the Lego NXT Ultrasonic sensor). If 9V pullup is enabled for a sensor, input voltages above 9.6V would be exposing the connected sensor to voltages higher than 9V. The power input capacitor on the "BrickPi Advanced Power" model is rated for 16V, so 12V is the highest I would use. Power consumption is minimal.

RPi Requirements
RPi A is typically said to draw 300-500mA, and a RPi B is typically said to draw around 700mA. With a WiFi dongle, 1A should be sufficient to power the RPi, WiFi dongle, and BrickPi circuits.
Using I2C devices on sensor ports 1-4
You can configure the sensor port type as either TYPE_SENSOR_I2C or TYPE_SENSOR_I2C_9V (e.g. BrickPi.SensorType[PORT_1] = TYPE_SENSOR_I2C;). TYPE_SENSOR_I2C_9V sets up the sensor port for I2C (same as TYPE_SENSOR_I2C), and also turns on the 9v pullup on sensor port pin 1.

You can configure the I2C bus speed using BrickPi.SensorI2CSpeed (e.g. BrickPi.SensorI2CSpeed[PORT_1] = 10;). The speed is a delay used to slow down the I2C bus (in units of about 1uS), placed between each I2C bus transition. A value of 0 means no delay (max speed, which is usually around 100k). Because the BrickPi FW supports clock stretching, the maximum speed is dependant on the electrical charactoristics of the I2C bus (i.e. pullup resistors' strength, cable length, etc.). Most I2C slaves that use hardware I2C should support full speed (delay of 0).

You can configure the number of devices on an I2C bus using BrickPi.SensorI2CDevices (e.g. BrickPi.SensorI2CDevices[PORT_1] = 3;). This number can be 1 through 8. While this is typically used for situations of multiple physical slaves on a single I2C bus, it really just indicates how many independant I2C transactions should take place for every call to BrickPiUpdateValues().

For each I2C device, you can set the I2C address (bits 1-7 of 0-7), using BrickPi.SensorI2CAddr (e.g. BrickPi.SensorI2CAddr[PORT_1][2] = 0x10; would set the I2C address of device 3 on sensor port 1 to 0x10).

For each I2C device, you can optionally set some transaction settings using BrickPi.SensorSettings (e.g. BrickPi.SensorSettings[PORT_1][0] = (BIT_I2C_MID | BIT_I2C_SAME); would set the settings of device 1 on sensor port 1 to use BIT_I2C_MID and BIT_I2C_SAME). The supported flags are BIT_I2C_MID and BIT_I2C_SAME. BIT_I2C_MID tells the BrickPi FW to issue a clock pulse between writing and reading bytes (this is contrary to the I2C standard, and as far as I know is only required by the Lego NXT Ultrasonic sensor). BIT_I2C_SAME tells the BrickPi FW that the I2C transactions for this device are always going to be the same, and to not expect to receive the length and output data with the values update (it receives this information during setup). An example for when to use BIT_I2C_SAME is when you always want to write the byte 0x42, and then read 6 bytes (e.g. for polling a sensor).

For each I2C device, you can set the number of bytes to write using BrickPi.SensorI2CWrite (e.g. BrickPi.SensorI2CWrite[PORT_1][1] = 4; would set the number of bytes to write to device 2, to be 4).

For each I2C device, you can set the number of bytes to read using BrickPi.SensorI2CRead (e.g. BrickPi.SensorI2CRead[PORT_1][4] = 0; would set the number of bytes to read from device 5, to be 0).

There is an I2C write buffer for each I2C device, called BrickPi.SensorI2COut. You can access each byte using e.g. BrickPi.SensorI2COut[PORT_1][3][0] = 137; which would set port 1, device 4, write buffer byte 1 to 137.

There is an I2C read buffer for each I2C device, called BrickPi.SensorI2CIn. You can access each byte using e.g. value = BrickPi.SensorI2CIn[PORT_1][5][3]; which would set value to port 1, device 6, read buffer byte 4.

The sensor type (e.g. TYPE_SENSOR_I2C), bus speed, number of devices, device I2C address(es), and device I2C settings, are sent to the BrickPi using function BrickPiSetupSensors(). In the case of the flag BIT_I2C_SAME being used for a device, the number of bytes to write and read, and the bytes to write are sent to the BrickPi when you call BrickPiSetupSensors(). If you don't use flag BIT_I2C_SAME for a device, the number of bytes to write and read, and the bytes to write are sent every time you call BrickPiUpdateValues() (which is more overhead for the communication between the RPi and the BrickPi).
Using Hardware I2C on sensor port 5
Sensor port 5 is connected to the RPi HW I2C bus available on the GPIO header. On RPi Rev 1 it's /dev/i2c-0 (I2C bus 0), and on Rev 2 it's /dev/i2c-1 (I2C bus 1). The configuration/setup of the resources is handled automatically when the BrickPiSetup() function is called.

The RPi HW I2C bus (sensor port 5) has nothing to do with the microcontrollers of the BrickPi, or the other sensor ports. The entire structure and setup is completely different. The following functions use direct calls to the RPi OS, and the actions take place immediately, and are independant from BrickPiUpdateValues().

You can set the I2C bus speed using BrickPiSetupI2C(SPEED), where SPEED is the speed in bits per second. For example, BrickPiSetupI2C(10000); would set the bus speed to 10000 (10k bps). The speed can be anywhere from 5000 (5k bps) to 1000000 (1m bps). Some I2C devices have minimum and/or maximum rated speeds. Note that the I2C bus electrical charactoristics (pullup resistor strength, physical bus length etc.) will affect the communication performance and speed. 100000 (100k bps) is a pretty standard speed supported by most I2C slaves. The default I2C bus speed is 100000 (100k bps), configured by BrickPiSetup().

You can write to an I2C slave using I2C_WriteArray(ADDR, COUNT, ARRAY), where ADDR is the slave I2C bus address (bits 1-7 of 0-7), COUNT is the number of bytes to write, and ARRAY is the array of bytes to write.

You can read from an I2C slave using I2C_ReadArray(ADDR, COUNT, ARRAY), where ADDR is the slave I2C bus address (bits 1-7 of 0-7), COUNT is the number of bytes to read, and ARRAY is the array to store the incoming bytes.

You can perform a write-read with an I2C slave using I2C_WriteReadArray(ADDR, OUT_COUNT, OUT_ARRAY, IN_COUNT, IN_ARRAY), where ADDR is the slave I2C bus address (bits 1-7 of 0-7), OUT_COUNT is the number of bytes to write, OUT_ARRAY is the array of bytes to write, IN_COUNT is the number of bytes to read, and IN_ARRAY is the array to store the incoming bytes.
UART communication between RPi and microcontrollers
Each of the two microcontrollers (MCUs) handles two sensor ports (1 and 2, or 3 and 4), and two motor ports (A and B, or C and D). In the MCU firmware, the MCU doesn't know which sets of ports it's connected to, so the terms sensor port 1 and 2, and motor port A and B are used (even though they might actually be 3 and 4, and C and D). The RPi takes care of differentiating between the MCUs using a communication address (more on this later).

The RPi and BrickPi microcontrollers (MCUs) use UART to communicate. Because there are two MCUs on the BrickPi, the hardware is designed so that both MCUs get the UART signals from the RPi, and the RPi gets the UART signals from both MCUs.

The UART format is 8N1, at 500k baud. The newer versions of the BrickPi FW start out at 9600 baud, and can be changed.

The UART communication from the RPi to the MCUs implements a simple addressing byte. Each of the two MCUs has a unique address that it listens for. If the MCU recognizes it's address, it will act on the message, otherwise it will discard the message. By default the MCUs use addresses 1 and 2, but anything from 1 - 254 should work (just as long as two MCUs don't have the same address). If the RPi transmits with address 0 (for message types MSG_TYPE_E_STOP, MSG_TYPE_CHANGE_ADDR, and MSG_TYPE_BAUD_SETTINGS), all the MCUs will act on the message, but won't send a reply (for MSG_TYPE_CHANGE_ADDR broadcasted to all MCUs, a touch sensor on sensor port 1 or 3 (corresponding to the MCU to be targeted) must be pressed).

This is what the message format (header and data) looks like for all messages:

RPi to MCUs:

Code: Select all

byte   name         description
0      DEST_ADDR    Destination address for the message, or 0 for BROADCAST.
1      CHECKSUM     Checksum computed across the DEST_ADDR, BYTE_COUNT, and data.
2      BYTE_COUNT   The count of data bytes in the message body, excluding the header.
3-n                 The data

MCU to RPi (no need for address):

Code: Select all

byte   name         description
0      CHECKSUM     Checksum computed across the BYTE_COUNT and data.
1      BYTE_COUNT   The count of data bytes in the message body, excluding the header.
2-n                 The data
On top of the header, the first of the data bytes is a MSG_TYPE byte that specifies what type of message is being sent. At the time of writing this, these are the available message types:

Code: Select all

MSG_TYPE_CHANGE_ADDR      Change the UART address.
MSG_TYPE_SENSOR_TYPE      Change/set the sensor type.
MSG_TYPE_VALUES           Set the motor speed and direction, and return the sensors' and encoders' values.
MSG_TYPE_E_STOP           Float motors immediately
MSG_TYPE_TIMEOUT_SETTINGS Set the timeout
MSG_TYPE_BAUD_SETTINGS    Set the baud rate
Some of the MSG_TYPE types make use of a compressed communication format. If for example there are only 3 significant bits for one value, 4 for another, and 7 for another, the message might be packed (depending on the message type) to use only the first 14 bits of two bytes (wasting 2 bits, instead of using 3 bytes with 10 bits wasted), in order to save communication time. When building a message, this is done with the AddBits function, and when parsing a message, this is taken care of using the GetBits function.

Byte array, specific to message type. RPi to MCU:

MSG_TYPE_CHANGE_ADDR:

Code: Select all

1 byte the new address
  
Reply from MCU:
  1 byte MSG_TYPE_CHANGE_ADDR (just the message type, with no following data)
MSG_TYPE_SENSOR_TYPE:

Code: Select all

two bytes, one for each sensor's SensorType.
for each of the two sensor ports (1 and 2, or 3 and 4):
  if the sensor type is TYPE_SENSOR_I2C or TYPE_SENSOR_I2C_9V:
    8 bits of I2C_Speed
    3 bits of I2C_Devices (0-7, for 1-8 devices on the sensor port's I2C bus)
    for each of the I2C_Devices:
      7 bits of I2C_Addr (bits 0-6 of 1-7, which get's left shifted by one automatically)
      2 bits of SensorSettings (BIT_I2C_MID and BIT_I2C_SAME flags)
      if BIT_I2C_SAME:
        4 bits I2C_Out_Bytes
        4 bits I2C_In_Bytes
        for I2C_Out_Bytes:
          8 bits I2C_Out_Array data

Reply from MCU:
  1 byte MSG_TYPE_SENSOR_TYPE (just the message type, with no following data)
MSG_TYPE_VALUES:

Code: Select all

for each of the two motor ports (A and B, or C and D):
  if 1 bit:
    ((5 bits) + 1) bits encoder offset (left shifted one, with bit 0 being the sign)
  for each of the two motor ports (A and B, or C and D):
    10 bits (8 bits of PWM (bits 2-9), 1 bit dir (bit 1), 1 bit enable (bit 0))      
  for each of the two sensor ports (1 and 2, or 3 and 4):
    if the sensor type is TYPE_SENSOR_I2C or TYPE_SENSOR_I2C_9V:
      for each of the I2C_Devices:
        if not BIT_I2C_SAME:
          4 bits I2C_Out_Bytes
          4 bits I2C_In_Bytes
          for I2C_Out_Bytes:
            8 bits I2C_Out_Array data

Reply from MCU:
  1 byte MSG_TYPE_VALUES
  for each of the two motor ports (A and B, or C and D):
    5 bits how many bits are required for each of the compressed encoder values for each motor port
  for each of the two motor ports (A and B, or C and D):
    n bits of encoder data (bit 0 being sign, and bits 1-n being the value)
  for each of the two sensor ports (1 and 2, or 3 and 4):
    switch SensorType
      TYPE_SENSOR_TOUCH:
        1 bit sensor value
      TYPE_SENSOR_ULTRASONIC_CONT:
      TYPE_SENSOR_ULTRASONIC_SS:
        8 bits sensor value
      TYPE_SENSOR_COLOR_FULL:
        3 bits sensor colorAddBits(1, 0, 3, SEN[port]);
        10 bits blank ADC value
        10 bits red ADC value
        10 bits green ADC value
        10 bits blue ADC value
            case TYPE_SENSOR_I2C:
      TYPE_SENSOR_I2C_9V:
        I2C_Devices bits where each bit represents the success of I2C communication with the corresponding I2C device
        for I2C_Devices:
          if I2C transaction with I2C device was successful:
            for I2C_In_Bytes:
              8 bits I2C_In_Array
      TYPE_SENSOR_RCX_LIGHT:
      TYPE_SENSOR_COLOR_RED:
      TYPE_SENSOR_COLOR_GREEN:
      TYPE_SENSOR_COLOR_BLUE:
      TYPE_SENSOR_COLOR_NONE:
      default:
        10 bits sensor value
MSG_TYPE_E_STOP:

Code: Select all

no additional data
  
Reply from MCU:
  1 byte MSG_TYPE_E_STOP (just the message type, with no following data)
MSG_TYPE_TIMEOUT_SETTINGS:

Code: Select all

4 byte unsigned long (least significant byte first) timeout in ms
  
reply from MCU:
  1 byte MSG_TYPE_TIMEOUT_SETTINGS (just the message type, with no following data)
MSG_TYPE_BAUD_SETTINGS:

Code: Select all

3 byte unsigned long (least significant byte first) baud in bps
  
reply from MCU:
  1 byte MSG_TYPE_BAUD_SETTINGS (just the message type, with no following data)