I2C
The I2C module has been upgraded from version 1, the archived information relating to PIC32 Basic V1.xx can be found here.
| i2cstest() I2cscan() |
i2copen i2close i2cput() i2cputs i2cget() i2cgetb() i2cread |
i2cstart i2cstop i2ctimeout |
The I2C (I squared C) interface includes both channels 1 and 2. Channel 2 is used for the SPI interface and so would not normally be available in a system that uses this to read and write to the SD Card (BV513, BV523). Nearly all I2C commands will require which channle is being referred to.
| i2ctest |
| i2ctest(chan,adr) a=i2ctest(1,0x42) |
The I2C test command will return 1 if the device is present otherwise it returns 0. The I2C channel must be opened first otherwise the command will return 0 regardless of the device presence or not.
dim j%
i2copen 100000
for j%=0x02 to 0xf0 step 2
if i2ctest(1,j%) <> 0 then
print "device at ";j%
endif
next
endf
The above code will scan through all I2C even addresses from 2 to 0xf0 and report if any devices are found at that address. Run this routine if you have an I2C device but not sure of its address.
This is the output from the above code when there is a BV4237 connected
that has a RTC, temperature device and I/O device:
device at 0x62
device at 0x90
device at 0xd0
| i2cscan |
| i2cscan(chan,start_ad,end_adrr) a=i2cscan(1,0x10,0xf0) |
This command does a similar function to the example given above, however it returns the address of the first found device whilst scanning. For example if there are two devices on the bus 0x42 and 0xd0, i2cscan(1,0x10,0xf0) will return 0x42. To discover the next device repeat the command using i2cscan(1,0x44,0xf0), the command will now return 0xd0 as ox42 is not now in range.
Return values:
0 no device found
1 device found
-1 bus not free
I2C Usage Examples
This BASIC combines the start condition with an address as in I2C, an address ALWAYS follows the start condition. It also uses an 8 bit addressing scheme which simply means that if the address is an even number (2,4,0x42 etc.) then it is writing to the slave. If the address is an odd number (17,19,0x43 etc.) then it is a request to read form the slave. Some manufacturers quote a 7 bit address, if this is the case then multiply the address by 2. In other words a 7bit address of 0x31 is an 8 bit address 0x62.
Writing
The sequence for writing is start(with even address), write, stop. A practical example of this using the simplest device available is a port expander PCF8574.
function write(data)
i2cstart
1 0x40 // assumes address to be 0x40
i2cput(1,data)
i2cstop
1
endf
The address is sent along with the start condition and the data is written at i2cputc(1,data). This command can be repeated any number of times before sending the stop condition which is useful for memory devices and the like. The i2cputc command will also return a value and so can be used in the form a=i2cputc(1,data). The value of a can be inspected for errors. For more details about this see the command itself.
Reading
Reading is carried out in a similar way.
function read
dim a
i2cstart 1 0x41 // assumes address to be 0x40
a=i2get(1,1)
i2cstop 1
result a
endf
In the above example the odd address is used which effectively sets the read write bit to 1. Again multiple reads can take place before the stop is issued. However when reading from a slave the correct protocol is to send an NACK when reading the last byte and so if only one byte is being read, last is set to 1. For example, say three bytes are read:
a=i2cget(1,0)
b=i2cget(1,0)
c=i2cget(1,1)
Only the last read has the last parameter set to 1. Some slave devices will ignore this whilst others rely on it being there for correct operation.
Write / Read
Very often an I2C device requires a write first followed by read. A good example of this is an EEPROM where an address can be specified to read from. In this case use of the restart command is made.
// read from EEPROM
function read(address)
dim a
i2cstart 1 0xA0 // write address
i2cput(1,address) // set eeprom address
i2crestart 1 0xA1 // read address
a=i2get(1,1)
i2cstop 1
result a
endf
Note the sequence is start with the write address followed by the EEPROM address then restart using the read address. If a 16 bit EEPROM address is required then two 'i2cput' commands can be sent.
| i2copen |
| i2copen <chan speed> i2copen 2 100000 |
This sets the appropriate registers and serial rate to enable I2C communications. This must be actioned first before any other I2C command. The speed is in HZ, in the above example it has been opened for a slow speed device of 100k. Note that for the BV523 channel 1 is already opened for 400k because of the real time clock.
| i2ctimeout |
| i2ctimeout <delay> i2ctimeout 1 |
Adjusts the time out for use with slower devices. The default is 1 and a
value of 5000 will give about 1.5 seconds
| i2close |
| i2close chan |
The purpose of this keyword is to free up the ports used for the I2C communications so that they may be used for other purposes.
| i2cstart |
| i2cstart chan address i2cstart 1 0x42 |
This will send the start condition and an address, 8 bit addressing is used and so for writing it will be an even address and for reading it will be an off address. This command is used to initiate reading or writing to the I2C bus.
| i2cstop |
| i2cstop chan i2cstop 1 |
This should always be used after a command sequence otherwise the bus may be held in a "frozen" state preventing access to any other devices.
| i2cput |
| i2cput( chan, value) a=i2cput( 1, 'k') |
This is the basic command for sending byte to a slave device. The channel must be specified and the value should have the range 0 to 255. This command will return the following values:
-1 This means that there is a collision on the bus, either the
slave device has not released the bus or there is another device trying
to gain access.
1 When ACK has been received from the slave, this could mean that the
slave has not responded by holding the data line low on the 9th clock
and would normally be an error. On some devices though it is possible
that this is the normal operation of the slave.
0 Is normally what to expect for correct operation.
Using this error mechanism is optional, if an error does occur then it will not be reported back to Basic, i.e. it will be a silent error.
| i2cputs |
| i2cputs chan string i2cputs 1 a$ |
An extension to i2cput, this performs exactly the same function but this time will output a number of bytes that are contained in the string. No error value is returned but if an error does occur then this will cause a Basic error message.
| i2cget |
| i2cget( chan last) a=i2cget(1,0) |
The basic read from the slave, it will get one byte from the slave.
When reading from an I2C bus the correct protocol is to send NACK at each read so that the slave knows the byte has been read correctly. The last read however an ACK '1' should be sent. Some slave devices do not require this but others do and use this as an indicator that the master does not require any more data. Take for example an EEPROM where any number of bytes may be read, using ACK on the last byte tells the EEPROM that no more bytes are required.
| i2cgetb |
| i2cgetb( <int variable>chan last) a=i2cgetb(e,1,0) |
** This is experimental, please report bugs.
This command is exactly the same as the i2cget command except that you can supply an integer variable that will report on any errors that have occurred rather then the command exiting the interpreter. It will then be up to the user to deal with the error. The values expected in variable 'e' are:
0 no error
1 no ACK from slave
2 time out, no response from slave
| i2cread |
| i2cread
<adr>[cmd>..],ary[start];number i2cread rtc, a[1];4 i2cread 0x42 0x55, a[1];3 |
This command will only work with channel 1 and is depreciated, i.e. it will be removed form future releases.
It is recognised that some I2C devices require one or more commands prior to obtaining input. The serial EEPROM for example may have the memory address following the input. This statement can take care of that as an optional command or commands can be sent prior to reading from the i2c
Example 1:
i2cread 0x90, a&[1];2
This is an example of reading a temperature device with an address of 0x90. Two bytes are returned by the device. An address always follows the statement, this should be the write address (an even number). The comma tells the statement that an integer or character array follows, this is where the data will go. The number of bytes to read comes after the semicolon. This statement will therefore address the I2C device at address 0x90 and read into a&[1] and a&[2] two bytes.
The index used in the array will be the first ‘slot’ to write the data to. In I2C speak this would be s 90 r g-2 p where s is the start condition, 90 is a byte sent in hex r is a restart condition using 0x91, g-2 gets 2 bytes from the device and p is the stop condition. In this example a character array ‘&’ has been used, an integer array could also be used.
Example 2:
i2cread 0x62 0x55, a[1];3
A more complex example is used to check that a I2C BV device is working or connected. These device have a test command whereby if 0x55 is sent, subsequent reads returns an incrementing number. Here the command 0x55 is sent before reading 3 bytes.
The rules for this statement are as follows:
- An address must always follow the statement
- None or up to 10 commands can be sent after the address
- A comma indicates the start of the read section, i.e. it must go after the address or last command.
- A single dimension integer or character array must always be used to gather the data from the I2C device. The index given will be the first byte, the next byte will be +1.
- The number of bytes to be read is specified after the semicolon.