The Inter Integrated Circuit (I2C) bus was developed by Philips (now NXP) for the transfer of data between integrated circuits.
7 and 8 Bit Addressing
The standard addresses used in these commands are 7-bit addresses (without the read/write bit). MMBasic will add the read/write bit and manipulate it accordingly during transfers.
Some vendors provide 8-bit addresses which include the read/write bit. You can determine if this is the case because they will provide one address for writing to the slave device and another for reading from the slave. In these situations you should only use the top seven bits of the address.
For example: If the read address is 9B (hex) and the write address is 9A (hex) then using only the top seven bits will give you an address of 4D (hex). A simple way of finding the address is to take the 8 bit write address and divide it by 2.
Another indicator that a vendor is using 8-bit addresses instead of 7-bit addresses is to check the address range. All 7-bit addresses should be in the range of 08 to 77 (hex). If your slave address is greater than this range then probably your vendor has specified an 8-bit address.
10 Bit Addressing
10-bit addressing was designed to be compatible with 7-bit addresses, allowing developers to mix the two types of devices on a single bus. Devices that use 10-bit addresses will be clearly identified as such in their data sheets.
In 10-bit addressing the slave address is sent in two bytes with the first byte beginning with a special bit pattern to indicate that a 10 bit address is being used. This process is automatically managed by MMBasic when the ‘option’ argument is set for 10-bit addressing. 10-bit addresses can be in the range of 0 to 3FF (hex).
Master/Slave Modes
The master and slave modes can be enabled simultaneously; however, once a master command is in progress, the slave function will be “idle” until the master releases the bus. Similarly, if a slave command is in progress, the master commands will be unavailable until the slave transaction completes.
In master mode, the I2C send and receive commands will not return until the command completes or a timeout occurs (if the timeout option has been specified).
The slave mode uses an MMBasic interrupt to signal a change in status and in this routine the program should write/read the data as specified by the I2C master. This operates the same as a general interrupt on an external I/O pin. Return from the interrupt is via the IRETURN statement except where a user defined subroutine is used (in that case END SUB or EXIT SUB is used).
I/O Pins
On the Maximite pin 12 becomes the I2C data line (SDA) and pin 13 the clock (SCL). For the DuinoMite see the “DuinoMite MMBasic ReadMe.pdf” document for details.
Both of these pins should have external pullup resistors installed (typical values are 10KΩ for 100KHz or 2KΩ for 400 kHz). When the I2C CLOSE command is used the I/O pins are reset to a “not configured” state. Then can then be configured as per normal using SETPIN.
When running the I2C bus at above 150 kHz the cabling between the devices becomes important. Ideally the cables should be as short as possible (to reduce capacitance) and also the data and clock lines should not run next to each other but have a ground wire between them (to reduce crosstalk).
If the data line is not stable when the clock is high, or the clock line is jittery, the I2C peripherals can get “confused” and end up locking the bus (normally by holding the clock line low). If you do not need the higher speeds then operating at 100 kHz is the safest choice.
Example
I2C is ideally suited for communications between integrated circuits. As an example, there might be an occasion when a Maximite does not have enough serial ports, I/O pins, or whatever for a particular application. In that case a Micromite‡ or Maximite could be used as a slave to provide the extra facilities.
This example converts a Micromite into a general purpose I/O expansion chip with 17 I/O pins that can be dynamically configured (by the master) as analog inputs or digital input/outputs. The routines on the master are simple to use (SSETPIN to configure the slave I/O and SPIN() to control it) and the program running on the master need not know that the physical I/O pins reside on another chip. All communications are done via I2C.
The following illustration shows the connections required:
Program Running On the Slave:
The slave must first set up its I2C interface to respond to requests from the master. With that done it can then drop into an infinite loop while the job of responding to the master is handled by the I2C interrupts.
In the program below the slave will listen on I2C address 26 (hex) for a three byte command from the master.
The format of this message is:
- Byte 1 is the command type. It can have one of three values; 1 means configure the pin, 2 means set the output of the pin and 3 means read the input of the pin.
- Byte 2 is the pin number to operate on.
- Byte 3 is the configuration number (if the command byte is 1), the output of the pin (if the command byte is 2) or a dummy number (if the command byte is 3).
The configuration number used when configuring a slave’s I/O pin is the same as used in earlier versions of Maximite MMBasic (with the SETPIN command) and can be any one of:
0 Not configured or inactive
1 Analog input
2 Digital input
3 Frequency input
4 Period input
5 Counting input
8 Digital output
9 Open collector digital output. In this mode SPIN() will also return the value on the output pin .
Following a command from the master that requests an input, the master must then issue a second I2C command to read 12 bytes. The slave will respond by sending the value as a 12 character string.
This program can fall over if the master issues an incorrect command. For example, by trying to read from a pin that is not an input. If that occurs, an error will be generated and MMBasic will exit to the command prompt.
Rather than trap all the possible errors that the master can make, this program uses the watchdog timer. If an error does occur the watchdog timer will simply reboot the Micromite and the program will restart (because AUTORUN is on) and wait for the next message from the master. The master can tell that something was wrong because it would get a timeout.
This is the complete program running on the slave:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
OPTION AUTORUN ON DIM msg(2) ' array used to hold the message I2C SLAVE OPEN &H26, 0, 0, WriteD, ReadD ' slave's address is 26 (hex) DO ' the program loops forever WATCHDOG 1000 ' this will recover from errors LOOP ReadD: ' received a message I2C SLAVE READ 3, msg(0), recvd ' get the message into the array IF msg(0) = 1 THEN ' command = 1 SETPIN msg(1), msg(2) ' configure the I/O pin ELSEIF msg(0) = 2 THEN ' command = 2 PIN(msg(1)) = msg(2) ' set the I/O pin's output ELSE ' the command must be 3 s$ = str$(pin(msg(1))) + Space$(12) ' get the input on the I/O pin ENDIF IRETURN ' return from the interrupt WriteD: ' request from the master I2C SLAVE WRITE 12, s$ ' send the last measurement IRETURN ' return from the interrupt |
Interface Routines On the Master:
These routines run on the Maximite. They assume that the slave Micromite is listening on I2C address 26 (hex).
If necessary these can be modified to access multiple Micromites (with different addresses), all acting as expansion chips and providing an almost unlimited expansion capability.
There are two subroutines and one function that together are used to control the slave:
SSETPIN pin, cfg This subroutine will setup an I/O pin on the slave. It operates the same as the MMBasic SETPIN command and the possible values for ‘cfg’ are listed above.
SPIN pin, output This subroutine will set the output of the slave’s pin to ‘output’ (ie, high or low).
nn = SPIN(pin) This function will return the value of the input on the slave’s I/O pin.
For example, to display the voltage on pin 3 of the slave you would use:
1 2 |
SSETPIN 3, 1 PRINT SPIN(3) |
As another example, to flash a LED connected to pin 15 of the slave you would use:
1 2 3 4 |
SSETPIN 15, 8 SPIN 15, 1 PAUSE 300 SPIN 15, 0 |
These are the three routines:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
' configure an I/O pin on the slave SUB SSETPIN pinnbr, cfg I2C OPEN 100, 1000 I2C WRITE &H26, 0, 3, 1, pinnbr, cfg IF MM.I2C THEN ERROR "Slave did not respond" I2C CLOSE END SUB ' set the output of an I/O pin on the slave SUB SPIN pinnbr, dat I2C OPEN 100, 1000 I2C WRITE &H26, 0, 3, 2, pinnbr, dat IF MM.I2C THEN ERROR "Slave did not respond" I2C CLOSE END SUB ' get the input of an I/O pin on the slave FUNCTION SPIN(pinnbr) LOCAL t$ I2C OPEN 100, 1000 I2C WRITE &H26, 0, 3, 3, pinnbr, 0 I2C READ &H26, 0, 12, t$ IF MM.I2C THEN ERROR "Slave did not respond" I2C CLOSE SPin = VAL(t$) END FUNCTION |
Information from Geoff Graham http://geoffg.net
Leave a Reply