This simple example demonstrates how to communicate with an I2C bus between two microcontrollers (here with Carbides).
Here is the wiring to follow :
The master is on the left and the slave on the right. They must share the ground potential and their SDA (PB00) and SCL (PB01) pins must be connected. These lines must also be pulled-up by 2.2k resistors. These pull-ups are connected to the master's Vcc, which can also be shared with the slave (in this case, make sure to not turn on the slave to avoid electrical conflicts, it will be powered by the master).
The master and the slave each have their own code :
master.cpp
[download]wget http://libtungsten.io/static/code/examples/i2c_simple_transfer/i2c_master/i2c_master.cpp
#include <core.h> #include <gpio.h> #include <i2c.h> #include <carbide.h> // I2C address of the slave const uint8_t ADDR = 0x10; // I2C port const I2C::Port PORT = I2C::Port::I2C1; // Input and output buffers const int BUFFER_SIZE = 10; uint8_t bufferIn[BUFFER_SIZE]; uint8_t bufferOut[BUFFER_SIZE]; // Every time the user presses the button, send some data to the I2C slave // at the specified address and ask for an anwser int main() { // Initialize the microcontroller Carbide::init(); // Enable the I2C port in Master mode I2C::enableMaster(PORT); // Initial value of the counter uint8_t counter = 0x80; while (true) { // Fill the output buffer with dummy data for (int i = 0; i < BUFFER_SIZE; i++) { bufferOut[i] = counter; counter++; } // Wait until the user presses the button while (!Carbide::buttonRisingEdge()); // Send the content of the output buffer to the slave I2C::write(PORT, ADDR, bufferOut, BUFFER_SIZE); // Ask the slave for an answer and store it in the input buffer I2C::read(PORT, ADDR, bufferIn, BUFFER_SIZE); } }
Makefile
[download]wget http://libtungsten.io/static/code/examples/i2c_simple_transfer/i2c_master/Makefile
NAME=i2c_master BOOTLOADER=true CARBIDE=true # Available modules : adc dac eic gloc i2c spi tc trng usart # Some modules such as gpio and flash are already compiled by default # and must not be added here. MODULES=i2c # The toolchain's bin/ path, don't forget to customize it # If this directory is already in your PATH, comment this line. TOOLCHAIN_PATH=/opt/arm-none-eabi/bin/ # Include the main lib makefile include libtungsten/Makefile
slave.cpp
[download]wget http://libtungsten.io/static/code/examples/i2c_simple_transfer/i2c_slave/i2c_slave.cpp
#include <core.h> #include <gpio.h> #include <i2c.h> #include <carbide.h> // I2C address of the slave const uint8_t ADDR = 0x10; // I2C port const I2C::Port PORT = I2C::Port::I2C1; // Input and output buffers const int BUFFER_SIZE = 10; uint8_t bufferIn[BUFFER_SIZE]; uint8_t bufferOut[BUFFER_SIZE]; int availableOutput = 0; // Callback called with the asynchronous I2C::read() has finished, // meaning that the master sent some data which has been stored in // the input buffer void onI2CReadFinished() { // Arbitrarily fill the output buffer based on the received content // At this stage I2C::getAsyncReadCursor() returns the number of // bytes that has been stored in the input buffer int n = I2C::getAsyncReadCounter(PORT); for (int i = 0; i < n; i++) { bufferOut[i] = 0xFF - bufferIn[i]; } // Update the content of the output buffer availableOutput = n; I2C::write(PORT, bufferOut, availableOutput, true); // Start a new read sequence to receive the next frame of data I2C::read(PORT, bufferIn, BUFFER_SIZE, true); } // Callback called with the asynchronous I2C::write() has finished, // meaning that the master has asked for some data and the content // of the output buffer has been sent void onI2CWriteFinished() { // Send the same output buffer again the next time the master // asks for some data I2C::write(PORT, bufferOut, availableOutput, true); } // Start I2C slave callbacks to asynchronously send and receive some data // from a master int main() { // Initialize the microcontroller Carbide::init(); // Enable the I2C port in Slave mode I2C::enableSlave(PORT, ADDR); // Register a callback to be triggered when an asynchronous read has finished I2C::enableInterrupt(PORT, onI2CReadFinished, I2C::Interrupt::ASYNC_READ_FINISHED); // Initialize a read into the input buffer. This is necessary to be able to correctly receive // the first frame the master will send, and to tell the driver in which buffer to store it. // The next reads will be started inside the onI2CReadFinished() callback registered above. I2C::read(PORT, bufferIn, BUFFER_SIZE, true); // Register a callback to be triggered when an asynchronous write has finished I2C::enableInterrupt(PORT, onI2CWriteFinished, I2C::Interrupt::ASYNC_WRITE_FINISHED); // There is no need to initialize a write from the output buffer, because it does not // contain meaningful data yet. If the master requests some data the driver will automatically // send as many padding bytes (0xFF) as requested. // Act like we're busy bool ledState = false; while (true) { Carbide::setLedR(ledState); ledState = !ledState; Core::sleep(300); } }
Makefile
[download]wget http://libtungsten.io/static/code/examples/i2c_simple_transfer/i2c_slave/Makefile
NAME=i2c_slave BOOTLOADER=true CARBIDE=true # Available modules : adc dac eic gloc i2c spi tc trng usart # Some modules such as gpio and flash are already compiled by default # and must not be added here. MODULES=i2c # The toolchain's bin/ path, don't forget to customize it # If this directory is already in your PATH, comment this line. TOOLCHAIN_PATH=/opt/arm-none-eabi/bin/ # Include the main lib makefile include libtungsten/Makefile