/* * Copyright (c) 2015, Mehdi Migault * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * \addtogroup cc2538-i2c cc2538 I2C Control * @{ * * \file * Implementation file of the I2C Control module * * \author * Mehdi Migault */ #include "i2c.h" #include #include "clock.h" #include "sys-ctrl.h" /*---------------------------------------------------------------------------*/ void i2c_init(uint8_t port_sda, uint8_t pin_sda, uint8_t port_scl, uint8_t pin_scl, uint32_t bus_speed) { /* Enable I2C clock in different modes */ REG(SYS_CTRL_RCGCI2C) |= 1; /* Run mode */ /* Reset I2C peripheral */ REG(SYS_CTRL_SRI2C) |= 1; /* Reset position */ /* Delay for a little bit */ clock_delay_usec(50); REG(SYS_CTRL_SRI2C) &= ~1; /* Normal position */ /* Set pins in input */ GPIO_SET_INPUT(GPIO_PORT_TO_BASE(port_sda), GPIO_PIN_MASK(pin_sda)); GPIO_SET_INPUT(GPIO_PORT_TO_BASE(port_scl), GPIO_PIN_MASK(pin_scl)); /* Set peripheral control for the pins */ GPIO_PERIPHERAL_CONTROL(GPIO_PORT_TO_BASE(port_sda), GPIO_PIN_MASK(pin_sda)); GPIO_PERIPHERAL_CONTROL(GPIO_PORT_TO_BASE(port_scl), GPIO_PIN_MASK(pin_scl)); /* Set the pad to no drive type */ ioc_set_over(port_sda, pin_sda, IOC_OVERRIDE_DIS); ioc_set_over(port_scl, pin_scl, IOC_OVERRIDE_DIS); /* Set pins as peripheral inputs */ REG(IOC_I2CMSSDA) = ioc_input_sel(port_sda, pin_sda); REG(IOC_I2CMSSCL) = ioc_input_sel(port_scl, pin_scl); /* Set pins as peripheral outputs */ ioc_set_sel(port_sda, pin_sda, IOC_PXX_SEL_I2C_CMSSDA); ioc_set_sel(port_scl, pin_scl, IOC_PXX_SEL_I2C_CMSSCL); /* Enable the I2C master module */ i2c_master_enable(); /* t the master clock frequency */ i2c_set_frequency(bus_speed); } /*---------------------------------------------------------------------------*/ void i2c_master_enable(void) { REG(I2CM_CR) |= 0x10; /* Set MFE bit */ } /*---------------------------------------------------------------------------*/ void i2c_master_disable(void) { REG(I2CM_CR) &= ~0x10; /* Reset MFE bit */ } /*---------------------------------------------------------------------------*/ void i2c_set_frequency(uint32_t freq) { /* Peripheral clock setting, using the system clock */ REG(I2CM_TPR) = ((SYS_CTRL_SYS_CLOCK + (2 * 10 * freq) - 1) / (2 * 10 * freq)) - 1; } /*---------------------------------------------------------------------------*/ void i2c_master_set_slave_address(uint8_t slave_addr, uint8_t access_mode) { if(access_mode) { REG(I2CM_SA) = ((slave_addr << 1) | 1); } else { REG(I2CM_SA) = (slave_addr << 1); } } /*---------------------------------------------------------------------------*/ void i2c_master_data_put(uint8_t data) { REG(I2CM_DR) = data; } /*---------------------------------------------------------------------------*/ uint8_t i2c_master_data_get(void) { return REG(I2CM_DR); } /*---------------------------------------------------------------------------*/ void i2c_master_command(uint8_t cmd) { REG(I2CM_CTRL) = cmd; /* Here we need a delay, otherwise the I2C module keep the receiver mode */ clock_delay_usec(1); } /*---------------------------------------------------------------------------*/ uint8_t i2c_master_busy(void) { return REG(I2CM_STAT) & I2CM_STAT_BUSY; } /*---------------------------------------------------------------------------*/ uint8_t i2c_master_error(void) { uint8_t temp = REG(I2CM_STAT); /* Get all status */ if(temp & I2CM_STAT_BUSY) { /* No valid if BUSY bit is set */ return I2C_MASTER_ERR_NONE; } else if(temp & (I2CM_STAT_ERROR | I2CM_STAT_ARBLST)) { return temp; /* Compare later */ } return I2C_MASTER_ERR_NONE; } /*---------------------------------------------------------------------------*/ uint8_t i2c_single_send(uint8_t slave_addr, uint8_t data) { i2c_master_set_slave_address(slave_addr, I2C_SEND); i2c_master_data_put(data); i2c_master_command(I2C_MASTER_CMD_SINGLE_SEND); while(i2c_master_busy()); /* Return the STAT register of I2C module if error occured, I2C_MASTER_ERR_NONE otherwise */ return i2c_master_error(); } /*---------------------------------------------------------------------------*/ uint8_t i2c_single_receive(uint8_t slave_addr, uint8_t *data) { uint8_t temp; i2c_master_set_slave_address(slave_addr, I2C_RECEIVE); i2c_master_command(I2C_MASTER_CMD_SINGLE_RECEIVE); while(i2c_master_busy()); temp = i2c_master_error(); if(temp == I2C_MASTER_ERR_NONE) { *data = i2c_master_data_get(); } return temp; } /*---------------------------------------------------------------------------*/ uint8_t i2c_burst_send(uint8_t slave_addr, uint8_t *data, uint8_t len) { uint8_t sent; if((len == 0) || (data == NULL)) { return I2CM_STAT_INVALID; } if(len == 1) { return i2c_single_send(slave_addr, data[0]); } i2c_master_set_slave_address(slave_addr, I2C_SEND); i2c_master_data_put(data[0]); i2c_master_command(I2C_MASTER_CMD_BURST_SEND_START); while(i2c_master_busy()); if(i2c_master_error() == I2C_MASTER_ERR_NONE) { for(sent = 1; sent <= (len - 2); sent++) { i2c_master_data_put(data[sent]); i2c_master_command(I2C_MASTER_CMD_BURST_SEND_CONT); while(i2c_master_busy()); } /* This should be the last byte, stop sending */ i2c_master_data_put(data[len - 1]); i2c_master_command(I2C_MASTER_CMD_BURST_SEND_FINISH); while(i2c_master_busy()); } /* Return the STAT register of I2C module if error occurred, I2C_MASTER_ERR_NONE otherwise */ return i2c_master_error(); } /*---------------------------------------------------------------------------*/ uint8_t i2c_burst_receive(uint8_t slave_addr, uint8_t *data, uint8_t len) { uint8_t recv = 0; if((len == 0) || data == NULL) { return I2CM_STAT_INVALID; } if(len == 1) { return i2c_single_receive(slave_addr, &data[0]); } i2c_master_set_slave_address(slave_addr, I2C_RECEIVE); i2c_master_command(I2C_MASTER_CMD_BURST_RECEIVE_START); while(i2c_master_busy()); if(i2c_master_error() == I2C_MASTER_ERR_NONE) { data[0] = i2c_master_data_get(); /* If we got 2 or more bytes pending to be received, keep going*/ for(recv = 1; recv <= (len - 2); recv++) { i2c_master_command(I2C_MASTER_CMD_BURST_RECEIVE_CONT); while(i2c_master_busy()); data[recv] = i2c_master_data_get(); } /* This should be the last byte, stop receiving */ i2c_master_command(I2C_MASTER_CMD_BURST_RECEIVE_FINISH); while(i2c_master_busy()); data[len - 1] = i2c_master_data_get(); } return i2c_master_error(); } /*---------------------------------------------------------------------------*/ /** @} */