-
Notifications
You must be signed in to change notification settings - Fork 0
I2C Example
This page contains information describing the I2C example program. All example files can be located within the example
directory in the repository.
/********************************************************
* File Name: i2c_ex.c
*
* Description:
* Main file
*
*
*********************************************************/
/*************************************************************************
System Includes
************************************************************************/
#include "sublibinal.h"
#include "sublibinal_config.h"
//forward declarations
void timer_callback(void);
void i2c_callback(I2C_Node node);
//global variables
uint8 sensor_data[6]; //buffer for sensor data to be stored
/*************************************************************************
Main Function
************************************************************************/
int main(void) {
//buffer for uart ISRs
uint8 uart_tx_buffer[128], uart_rx_buffer[128];
uint8 i2c_tx_buffer[128], i2c_rx_buffer[128];
//structures for configuring peripherals
UART_Config uart_config = {0};
Timer_Config timer_config = {0};
Packetizer_Config packet_config = {0};
I2C_Config i2c_config = {0};
//temp buffer
uint8 blah;
//setup peripherals
timer_config.frequency = 1000; //Set the timer to 1KHz
timer_config.pbclk = PB_CLK; //Specify the speed of the peripheral bus clock
timer_config.which_timer = Timer_1; //Specify timer 1 as our selected device
timer_config.callback = &timer_callback; //Specify the timer callback function
timer_config.enabled = 1; //enable the timer
initialize_Timer(timer_config); //Initialize the timer with our specifications
uart_config.which_uart = UART_CH_1; //Specify uart channel 1 for our use
uart_config.pb_clk = PB_CLK; //tell the module the clock speed
uart_config.speed = 115200; //inform the module of the desired baud rate
uart_config.tx_buffer_ptr = uart_tx_buffer; //Provide a pointer to a data buffer
uart_config.tx_buffer_size = sizeof(uart_tx_buffer); //provide the size of the data buffer
uart_config.tx_en = 1; //enable the uart
uart_config.tx_pin = Pin_RPB15; //select the TX pin
packet_config.control_byte = 0x0A; //set the control byte for the packetizer
packet_config.which_channel = PACKET_UART_CH_1; //specify use of UART channel 1 for the packetizer
packet_config.uart_config = uart_config; //specify the UART configuration to reference
packet_config.callback = NULL; //Set there to be no callback
initialize_packetizer(packet_config); //Initialize the packetizer module
i2c_config.callback = NULL; //No I2C callback function
i2c_config.channel = I2C_CH_1; //Using I2C channel 1
i2c_config.pb_clk = PB_CLK; //specify the peripheral bus clock
i2c_config.rx_buffer_ptr = i2c_rx_buffer; //provide a pointer to rx buffer
i2c_config.rx_buffer_size = sizeof(i2c_rx_buffer); //provide size of rx buffer
i2c_config.tx_buffer_ptr = i2c_tx_buffer; //provide a pointer to the tx buffer
i2c_config.tx_buffer_size = sizeof(i2c_tx_buffer); //provide the size of the tx buffer
initialize_I2C(i2c_config); //initialize the I2C
//Global interrupt enable. Do this last!
enable_Interrupts();
while (1) {
//put background processes here
bg_process_I2C();
}
return 0;
}
void timer_callback(void)
{
I2C_Node node = {0};
node.device_id = 0x01; //arbitrary-ish device ID for clarification of data results
node.device_address = 0x45; //address of the sensor that we are talking to
node.sub_address = 0x12; //sub address on sensor to read in the sensors memory
node.data_buffer = sensor_data; //provide a buffer to some data for the sensor, data must remain in scope!!!
node.data_size = 6; //reading 6 bytes from the sensor
node.mode = READ; //we are reading and not writing
node.callback = &i2c_callback; //provide a callback for the data
send_I2C(I2C_CH_1, node); //enqueue a read of the device on the I2C
}
void i2c_callback(I2C_Node node)
{
uint8 send_data[7];
uint8 i;
//form data to go in the packet
send_data[0] = node.device_id;
for(i = 1; i < 7; ++i)
{
send_data[i] = node.data_buffer[i-1];
}
//send the packet
send_packet(PACKET_UART_CH_1, send_data, sizeof(send_data));
}
In this program, a timer specifies the rate at which an external sensor is read using the I2C protocol. When the I2C transaction has finished, the sensor data is transmitted upon the UART in a packetized format.
There are a number of important subsections of this code that will be described on. Other peripherals not pertaining to I2C have been omitted. Please refer to other peripheral examples for an in-depth description of their functionality.
i2c_config.callback = NULL; //No I2C callback function
i2c_config.channel = I2C_CH_1; //Using I2C channel 1
i2c_config.pb_clk = PB_CLK; //specify the peripheral bus clock
i2c_config.rx_buffer_ptr = i2c_rx_buffer; //provide a pointer to rx buffer
i2c_config.rx_buffer_size = sizeof(i2c_rx_buffer); //provide size of rx buffer
i2c_config.tx_buffer_ptr = i2c_tx_buffer; //provide a pointer to the tx buffer
i2c_config.tx_buffer_size = sizeof(i2c_tx_buffer); //provide the size of the tx buffer
initialize_I2C(i2c_config); //initialize the I2C
In this code section, we fill out the I2C configuration structure and then initialize the I2C peripheral using the initialization function. We specify that there is no callback. This is because callbacks for I2C should be specified with an I2C node and not the I2C module itself. A callback function is not needed to be implemented within the I2C module configuration structure. We then provide the peripheral bus clock speed and pointers to the RX and TX buffers, along with their respective sizes. These buffers must remain in scope throughout execution of the program. Any modification of the contents of these buffers during program execution can cause a malfunction of the I2C module.
//global variables
uint8 sensor_data[6]; //buffer for sensor data to be stored
//...
void timer_callback(void)
{
I2C_Node node = {0};
node.device_id = 0x01; //arbitrary-ish device ID for clarification of data results
node.device_address = 0x45; //address of the sensor that we are talking to
node.sub_address = 0x12; //sub address on sensor to read in the sensors memory
node.data_buffer = sensor_data; //provide a buffer to some data for the sensor, data must remain in scope!!!
node.data_size = 6; //reading 6 bytes from the sensor
node.mode = READ; //we are reading and not writing
node.callback = &i2c_callback; //provide a callback for the data
send_I2C(I2C_CH_1, node); //enqueue a read of the device on the I2C
}
The first important point is to notice that sensor_data
is declared as a global variable. This is to ensure that the data does not go out of scope. The next point to notice is that an I2C Node structure is created within the callback. The provided device_id is used for end-user differentiation of I2C Nodes within the I2C node callback function. The device address is specified in external device data sheets and determines which I2C device should be talked to. The sub_address
parameter specifies the location in device specific memory that should be read. The data_buffer
parameter is a pointer to where the data will be placed after the I2C transaction has taken place. The data_size
parameter specifies the size in bytes that sensor_data
points to. We also tell the node that this will be a read and will utilize the i2c_callback
function. Finally, we enqueue the node in the I2C work buffer by calling the send_I2C
function.
The next portion of code that is of importance is the i2c callback function.
void i2c_callback(I2C_Node node)
{
uint8 send_data[7];
uint8 i;
//form data to go in the packet
send_data[0] = node.device_id;
for(i = 1; i < 7; ++i)
{
send_data[i] = node.data_buffer[i-1];
}
//send the packet
send_packet(PACKET_UART_CH_1, send_data, sizeof(send_data));
}
In this portion of code, the first piece of information to make note of is the definition of the function, void i2c_callback(I2C_Node node)
. The callback function does not return any values and accepts a single parameter of an I2C_Node
structure. This node is actually the node that contains a pointer to the callback function. We then take the data that has been placed into the data buffer and construct a packet to be transmitted across the UART.
while (1) {
//put background processes here
bg_process_I2C();
}
This is the last portion of the program that is important. The background process for the I2C is crucial. The background process removes nodes from the results buffer of the I2C and calls the callback functions that the nodes have. If no callback function is specified within the node, all data within the node is lost. The callback function must be in the embedded loop to be able to remove nodes from the buffer and process the data.
SUBLIBinal was created by the Palouse Robosub Club.