In this post we will create an utility program that returns the address of the devices connected to the I2C BUS.
I2C communication is a serial protocol in which are used two lines: the data line (SDA) and the clock line (SCL). I2C is a master-slave protocol, the master (the one that starts the dialogue) in our example is the PIC microcontroller, a laser sensor will be the slave; up to 112 slave devices can be connected to a single I2C line. In the I2C protocol, each slave has an address that can be expressed by a 7-bit digit or a 10-bit digit, it is necessary to know the slave's address to establish the connection.
The I2C bus requires the use of only two discrete components, two pull-up resistors connected on SDA and SCL. In our case, the test card that mounts the laser sensor is already equipped with the two resistors, so the connection to the micro would not require other components. It is also true that for our purpose, we must consider the hypothesis that even no slave is connected; in this case the I2C bus would float not having the pull-up resistors connected and the program would return casual values. Then we mount two 10Kohm pull-up resistors on the breadboard that will go in parallel with those of the laser sensor.
From the datasheet of the sensor (code of the test board: CJVL53L0XV2) we can read the I2C address, that is 0x52, and that the bus can work in "fast" mode or at a frequency of 400kHz. We will use the knowledge of the address to verify the correct response of the program.
We start the MPLABX IDE and the MCC tool. First you need to enable the Master Synchronous Serial Port (MSSP); click twice on the item shown in the figure:
There is only one value to touch: the transmission speed. Change the value of the "Baud Rate Generator" to 0x27 and press enter, the field below will update accordingly showing 400kHz (it is supposed a 64MHz system clock -> tutorial_1_step1). Then press the "Generate" button.
This operation creates two new files: i2c1.h and i2c1.c also changes the files related to pin and interrupt configuration.
The interrupt file is modified to launch the new functions defined in the i2c.* files when the irq is generated on the I2C registers. The pin_manager.* files are updated to appropriately initialize the ports to the new functionality, aliases and macros are updated too. Finally we have the two new files i2c1.* with the functions necessary to manage the I2C serial, send and receive data.
The biggest and most repetitive work (like the configuration of all the micro registers) was performed by MCC, leaving us free to focus on the object of the program.
The logic of the program follows the following flow chart:
In this case, the data that we send to the device is irrelevant because we focus on the "ACK" response that the device send back when it is correctly addressed.
Luckily the code created by the MCC tool does not allow us to enter into the detail of the "ACK" signal, as it separates the various sending phases with more comprehensible labels such as: I2C1_MESSAGE_COMPLETE or I2C1_MESSAGE_PENDING. In detail, the flow chart relating to the verification of the "ACK" response is as follows:
Than this is the code of the i2c_detect function:
Comparing the code to the flow charts shown above, jumps out a difference: the loop while..MESSAGE_PENDING has a "toPending" variable which, if it reaches the value 1000, interrupts the loop with a message indicating a problem on the bus. Here we deal with a rare circumstance that is when the bus is connected but the device is not powered; in this case the program would remain blocked indefinitely in the PENDING state.
Another element to be pointed out is that the function I2C1_MasterWrite(..) created by the MCC tool is the one that in the flow chart I have indicated with "read status" because it is what it does once it has written the data on the bus.
Now you can see through the RS232 terminal the value returned by i2c_detect, which if my memory does not deceive is definitely a surprise (in the datasheet of the sensor is reported the address 0x52).
The explanation is simple: these are conventions. It must be remembered that in the I2C protocol the least significant bit, the right one, indicates if the command is writing (bit = 0) or reading (bit = 1) so the sensor address is 0x52 if writing, 0x53 if reading. The function created by Microchip considers the address of the device as a net value and add the write and read bit during the process of sending by shifting to the left the address and by inserting the bit to the right appropriately. From the following table we can see that by shifting the value 0x29 to the left of one bit, we get exactly 0x52.
The complete program is available as usual on github.
With the latest update the version of the MCC tool 3.45.1 presents a correction of the code generated automatically in the file i2c1.c which makes this tutorial not working. I show a piece of the code of the working file in which you can see the unusual presence of the ';' at the end of the while statement:
It would seem logical to remove the character ';' and in the new version it was done; however, by putting the IRQ flag to true in the condition specified by the while statement, it means to declare that the transmission/reception is finished, affirmation that even not knowing all the rest of the code seems to be an error, and indeed the program does not work. It is therefore necessary to manually restore the ';' character.