Microb Technology/2008/Main code/I2C

De Wikidroids

Current page: Microb_Technology / 2008 / Main_code / I2C


Sommaire

Documentation about the inter-microntroller communication

This document describe the communication protocol used in our next robot (2008). This is a very important part of the software. Indeed, this was often a problem in our previous robots.

What do we need ?

  • Simple to use
  • Reliable
  • Bus-based (no point-to-point)
  • Integrated in maby hardware (AVR at least).
  • Bandwidth, about 100 KBits/s

Last year, we had several cards, a mother board (ATmega128), an extension board (ATmega32), a camera board (DSP+FPGA). The communication was heterogenous (2 UART for PC and camera board, i2c between AVRs). It was quite reliable but our i2c driver was a "quick and dirty" code (some unexplained behaviours). This year, we'll probably have more cards : beacons, i2c sensors, ...

We decided to use an I2C communication, because it is quite standard, and we can connect up to 127 devices together.

Communication flows examples

Below are some examples of the send and receive flows in our robot :

From mother board

  • To extention board: motor commands, servo control, LEDs
  • To camera board: commands (start capture, send objects position)

To mother board

  • From extension board: debug strings, sensors status
  • From beacon board: position update
  • From camera board: objects position

Implemenation requirements

A first problem when implementing a communication protocol is to be sure that every device will be able to talk on the bus. Only the software of the motherboard can decide which has the permission to talk.

Moreover we have to be carreful with command sequencing, if they modify a state machine. For instance, if A says to B "give me register 1", then C says to B "give me register 2", then A reads the register, it will read register 2 instead of register 1.

Also, we want to be able to send a command from any software "context". If we send a command from the main loop, the buffer must not be corrupted even if we try to send another command from another task during the tranfer of the first one.

Implementation details

Commands, requests, answers

We decided to have one master (the mother board) and several slaves. This will simplify the sequence between communication frames.

The master can send commands at any time, and in a background task, it will poll the slaves to check if they have something to send.

There are 3 kinds of frames:

  • Commands (from master to slaves): can be send anywhere in the

program, it does not modify any state machine in slaves. An exemple can be a LED_ON command to another board.

  • Requests (from master to slaves): only sent in a background task. We

don't send another request until we have the answer (see below).

  • Answers (from slave to master): after sending a request, the master

reads the value of the slave transmission buffer.

The main program

  • main_loop

Only commands, read status of slaves using global variables.

  • task scheduled from interrupts

Only commands, read status of slaves using global variables.

  • communication-specific task

This task is called periodically (maybe every 5 ms), and try to send a request or to read an answer, then exits. If the transfer successes or fails, it will be notified by a callback before sending the next command.

A typical slave

If a slave receive a command, it can execute the associated action synchronously.

If a slave has something to say to the master, it must store it in a variable, then wait to be requested about it. When it is, it stores the answer in its transmission buffer then wait that the master reads it.

Data structures

To ensure the binary compatibility between the devices, we use a common definition in a common directory. This is an example of the structure for a command:

struct i2c_cmd_led_control {
	struct i2c_cmd_hdr hdr;
	uint8_t led_num:7;
	uint8_t state:1;	
};

A request:

struct i2c_req_read_debug_buffer {
	struct i2c_cmd_hdr hdr;
};

An answer:

struct i2c_ans_write_debug_buffer {
	struct i2c_cmd_hdr hdr;
	uint8_t buf[8];
};

The i2c_hdr contains the ID of the command, request or answer (uniq).

The i2c module

This driver is part of aversive, located in modules/comm/i2c. It provides an i2c support for AVR microcontrollers. Just report to the header file that describes the interface of the module.

The driver code is located here.

The i2c_protocol.c file

This file is part of the microb project, and interfaces the i2c driver with our code. There is one i2c_protocol.c file per i2c device (one for the mother board, one for the extension board, and one for the beacon).

In the slave, there is the i2c callback executed by the i2c driver, from the interrupt routine. There is also the function called for each command/request.

In the master, there are wrapper functions for the user that call the driver function to send commands. Requests are sent in another callback function that is called periodically.

Have a look in the mother board code (master) or in the extension board code (slave).

Conclusion

The master and slave programs are easy to write. If the master wants to know the status of a slave, it already have it in global variables, because it is already synchronized in a background task. If it want to send a command, it can do it at once (maybe it can have to wait that the current transmission is finished).

The slave receives the command from the i2c interruption in a callback function that takes the received buffer as a parameter.

Boîte à outils
LANGUAGES