5. Code Overview

This section provides the code blocks needed to successfully execute this tutorial.

5.1. Header Files

In main.c file, add the following header files:

#include "ad_i2c.h"
#include "peripheral_setup.h"
#include "platform_devices.h"

5.2. System Init Code

In main.c file, add the task initialization code in the system_init() routine:

/* Start main task(s) here (text menu available via UART1 to control application) */
      OS_TASK_CREATE( "I2C master",            /* The text name assigned to the task, for
                                                 debug only; not used by the kernel. */
              i2c_master_task,                /* The function that implements the task. */
              NULL,                           /* The parameter passed to the task. */
              256 * OS_STACK_WORD_SIZE,
              /* The number of bytes to allocate to the
                                                         stack of the task. */
              mainTEMPLATE_TASK_PRIORITY,     /* The priority assigned to the task. */
              i2c_task_m );                       /* The task handle */
      OS_ASSERT(i2c_task_m);

      /* the work of the SysInit task is done */
      OS_TASK_DELETE( xHandle );

5.3. Hardware Initialization

In main.c file, replace the prvSetupHardware() routine with the following code used for configuring the pins utilized by the wake-up controller.

/**
 * @brief Hardware Initialization
 */
 static void prvSetupHardware( void )
 {

     /* Init hardware */
     pm_system_init(periph_init);
     AD_IO_ERROR ok = ad_i2c_io_config(((ad_i2c_controller_conf_t *)I2C_MASTER_DEVICE)->id,
                      ((ad_i2c_controller_conf_t *)I2C_MASTER_DEVICE)->io, AD_IO_CONF_OFF);
     OS_ASSERT(AD_IO_ERROR_NONE == ok);
 }

5.4. Macro Definitions

In config/custom_config_xxx.h file, add the following macro definitions to enable the I2C related API:

/*******************************************************
 * Peripheral specific config
 */
#define dg_configI2C_ADAPTER                    (1)
#define dg_configUSE_HW_I2C                     (1)

5.5. Device Configurations

In peripheral_setup.h file, add macro definitions for the GPIO pins used by the target application:

/* I2X Interface */
#define I2C_GPIO_LEVEL HW_GPIO_POWER_V33


/* I2C */
#define I2C_MASTER_SCL_PIN  HW_GPIO_PIN_14
#define I2C_MASTER_SCL_PORT HW_GPIO_PORT_0

#define I2C_MASTER_SDA_PIN  HW_GPIO_PIN_2
#define I2C_MASTER_SDA_PORT HW_GPIO_PORT_1

#define I2C_SLAVE_ADDRESS    ( 0x44 )

#define I2C_AD_CONFIG_MASTER_I2C_CTRL       (HW_I2C1)
#define I2C_AD_CONFIG_MASTER_DMA_CH         (HW_DMA_CHANNEL_2)

In platform_devices.h file, add the following definitions:

#include <ad_i2c.h>
#include "peripheral_setup.h"

/**
 * \brief I2C device handle
 */
typedef const void* i2c_device;


/*
 * I2C DEVICES
 *****************************************************************************************
 */
#if dg_configI2C_ADAPTER || dg_configUSE_HW_I2C

/**
 * \brief EEPROM 24FC256 device
 */
extern i2c_device EEPROM_24FC256;

#endif /* dg_configI2C_ADAPTER || dg_configUSE_HW_I2C */

In platform_devices.c file, add IO bus as well as driver configurations for the I2C device(s) used. These settings will be applied when an application task attempts to interact with the target I2C slave device.

#include <ad_i2c.h>
#include "peripheral_setup.h"
#include "platform_devices.h"

/*
* PLATFORM PERIPHERALS GPIO CONFIGURATION
*****************************************************************************************
*/

#if dg_configI2C_ADAPTER

/* I2C I/O configuration */
const ad_i2c_io_conf_t i2c_master_io = {
        .scl = {
                .port = I2C_MASTER_SCL_PORT, .pin = I2C_MASTER_SCL_PIN,
                .on =  { HW_GPIO_MODE_OUTPUT_PUSH_PULL, HW_GPIO_FUNC_I2C_SCL, false },
                .off = { HW_GPIO_MODE_INPUT,             HW_GPIO_FUNC_GPIO,    true  }
        },
        .sda = {
                .port = I2C_MASTER_SDA_PORT, .pin = I2C_MASTER_SDA_PIN,
                .on =  { HW_GPIO_MODE_OUTPUT_PUSH_PULL, HW_GPIO_FUNC_I2C_SDA, false },
                .off = { HW_GPIO_MODE_INPUT,             HW_GPIO_FUNC_GPIO,    true  }
        },
};


/*
* PLATFORM PERIPHERALS CONTROLLER CONFIGURATION
*****************************************************************************************
*/

const ad_i2c_driver_conf_t master_driver_config = {
                I2C_DEFAULT_CLK_CFG,
                .i2c.speed = HW_I2C_SPEED_STANDARD,
                .i2c.mode = HW_I2C_MODE_MASTER,
                .i2c.addr_mode = HW_I2C_ADDRESSING_7B,
                .i2c.address = I2C_SLAVE_ADDRESS,
                .dma_channel = I2C_AD_CONFIG_MASTER_DMA_CH
            };


/* I2C controller configuration */
const ad_i2c_controller_conf_t master_config = {
        .id = I2C_AD_CONFIG_MASTER_I2C_CTRL,
        .io = &i2c_master_io,
        .drv = &master_driver_config
        };

i2c_device I2C_MASTER_DEVICE = &master_config;

#endif /* dg_configI2C_ADAPTER */

5.6. Task Code for I2C Master

/*
* Callback function for the I2C master asynchronous write transaction:
*
* \param  error      Error code returned at the end of an I2C transaction.
* \param  user_data  User data that can be passed and used within the function.
*/

void _i2c_master_async_write_done(void *user_data, HW_I2C_ABORT_SOURCE error)
    {
        /* Read the error status code */
        I2C_error_code = error;

        /* Signal the master task that time for resuming has elapsed. */
        OS_EVENT_SIGNAL_FROM_ISR(i2c_event);
    }

/*
* Callback function for the I2C master asynchronous read transaction:
*
* \param  error      Error code returned at the end of an I2C transaction.
* \param  user_data  User data that can be passed and used within the function.
*/

void _i2c_master_async_read_done(void *user_data, HW_I2C_ABORT_SOURCE error)
    {
        /* Read the error status code */
        I2C_error_code = error;

        /* Signal the master task that time for resuming has elapsed. */
        OS_EVENT_SIGNAL_FROM_ISR(i2c_event);

    }

/**
* @brief I2C master task: sends requests, reads responses and prints them
*/

OS_TASK_FUNCTION (i2c_master_task, pvParameters )
    {

        printf("I2C init \n\r");
        uint8_t resp[I2C_RESPONSE_SIZE];
        uint16_t hum_data, temp_data;
        ad_i2c_handle_t _master_handle = ad_i2c_open(I2C_MASTER_DEVICE);

        OS_TICK_TIME xNextWakeTime;
        static uint32_t loop_counter=0;
        static uint32_t transaction_counter=0;


        /* Initializes i2c_event semaphore*/
        OS_EVENT_CREATE(i2c_event);

        /* Initialise xNextWakeTime - this only needs to be done once. */
        xNextWakeTime = OS_GET_TICK_COUNT();

        for ( ;; ) {

                vTaskDelayUntil( &xNextWakeTime, COUNTER_FREQUENCY_MS );
                loop_counter++;
                if (loop_counter % (1000 / OS_TICKS_2_MS(COUNTER_FREQUENCY_MS)) == 0) {
                        transaction_counter++;
                        unsigned char * _req = (unsigned char *)"0";
                        printf("Write I2C: %s \n\r", _req); //Wake Up HS3001

    #if (I2C_ASYNC_EN)
                        I2C_error_code = ad_i2c_write_async(_master_handle,
                                                _req,
                                                I2C_REQUEST_SIZE,
                                                _i2c_master_async_write_done,
                                                NULL,
                                                HW_I2C_F_ADD_STOP);

        /*
        * In the meantime and while I2C transactions are performed in the background,
        *ad_i2c_user_cb application task can proceed to other operations/calculation.
        * It is essential that, the new operations do not involve I2C transactions
        * on the already occupied bus!!!
        */
                        OS_EVENT_WAIT(i2c_event, OS_EVENT_FOREVER);
            vTaskDelayUntil(&xNextWakeTime, OS_TICKS_2_MS(40)); //delay wait as per datasheet

            I2C_error_code = ad_i2c_read_async(_master_handle,
                                                resp,
                                                I2C_RESPONSE_SIZE,
                                                _i2c_master_async_read_done,
                                                NULL,
                                                HW_I2C_F_ADD_STOP);
            OS_EVENT_WAIT(i2c_event, OS_EVENT_FOREVER);
    #else
                I2C_error_code = ad_i2c_write_read(_master_handle,
                                                _req,
                                                I2C_REQUEST_SIZE,//reduce this to simulate incomplete send
                                                resp,
                                                I2C_RESPONSE_SIZE,
                                                HW_I2C_F_ADD_STOP);
    #endif
                        if(0 == I2C_error_code){
                                /*
                                * Data conversion according to the formulas provided
                                * in the HS3001 datasheet.
                                */
                                hum_data = (resp[0]<<8) + resp[1];
                                uint32_t hum_data_result = hum_data*100000/16383;
                                hum_data_result /=1000;
                                temp_data = (resp[2]<<8) + resp[3];
                                temp_data >>=2;
                                uint32_t temp_data_result = temp_data*1000*165/16383;
                                temp_data_result /= 1000;
                                temp_data_result -=40;
                                printf("Successfully read I2C \n\r");
                                printf("Hum: %ld Temp: %ld \n\r", hum_data_result, temp_data_result);

                        } else {
                                printf("i2c read error(%d)\n", I2C_error_code);
                        }
                        fflush(stdout);
                }
        }
        ad_i2c_close(_master_handle, true);
        OS_TASK_DELETE( NULL ); //should never get here
    }