diff --git a/platform/srf06-cc26xx/launchpad/Makefile.launchpad b/platform/srf06-cc26xx/launchpad/Makefile.launchpad index 54bd81ced3b..4ff89376142 100644 --- a/platform/srf06-cc26xx/launchpad/Makefile.launchpad +++ b/platform/srf06-cc26xx/launchpad/Makefile.launchpad @@ -3,7 +3,7 @@ CFLAGS += -DBOARD_LAUNCHPAD=1 CONTIKI_TARGET_DIRS += launchpad common BOARD_SOURCEFILES += board.c launchpad-sensors.c leds-arch.c button-sensor.c -BOARD_SOURCEFILES += ext-flash.c board-spi.c +BOARD_SOURCEFILES += ext-flash.c board-spi.c board-i2c.c ### Signal that we can be programmed with cc2538-bsl BOARD_SUPPORTS_BSL=1 diff --git a/platform/srf06-cc26xx/launchpad/board-i2c.c b/platform/srf06-cc26xx/launchpad/board-i2c.c new file mode 100644 index 00000000000..69881e81f61 --- /dev/null +++ b/platform/srf06-cc26xx/launchpad/board-i2c.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (c) 2017, University of Bristol - http://www.bris.ac.uk/ + * 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 sensortag-cc26xx-i2c + * @{ + * + * \file + * Board-specific I2C driver for the Sensortags + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "ti-lib.h" +#include "board-i2c.h" +#include "lpm.h" +#include "rtimer.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +#define I2C_MAX_WAIT_TIME (RTIMER_SECOND / 10) + +#define LIMITED_BUSYWAIT(cond) do { \ + rtimer_clock_t end_time = RTIMER_NOW() + I2C_MAX_WAIT_TIME; \ + while(cond) { \ + if(!RTIMER_CLOCK_LT(RTIMER_NOW(), end_time)) { \ + return false; \ + } \ + } \ + } while(0) +/*---------------------------------------------------------------------------*/ +#define NO_INTERFACE 0xFF +/*---------------------------------------------------------------------------*/ +static uint8_t slave_addr = 0x00; +static uint8_t interface = NO_INTERFACE; +/*---------------------------------------------------------------------------*/ +static bool +accessible(void) +{ + /* First, check the PD */ + if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL) + != PRCM_DOMAIN_POWER_ON) { + return false; + } + + /* Then check the 'run mode' clock gate */ + if(!(HWREG(PRCM_BASE + PRCM_O_I2CCLKGR) & PRCM_I2CCLKGR_CLK_EN)) { + return false; + } + + return true; +} +/*---------------------------------------------------------------------------*/ +void +board_i2c_wakeup() +{ + /* First, make sure the SERIAL PD is on */ + ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL); + while((ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL) + != PRCM_DOMAIN_POWER_ON)); + + /* Enable the clock to I2C */ + ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_I2C0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* Enable and initialize the I2C master module */ + ti_lib_i2c_master_init_exp_clk(I2C0_BASE, ti_lib_sys_ctrl_clock_get(), + true); +} +/*---------------------------------------------------------------------------*/ +static bool +i2c_status() +{ + uint32_t status; + + status = ti_lib_i2c_master_err(I2C0_BASE); + if(status & (I2C_MSTAT_DATACK_N_M | I2C_MSTAT_ADRACK_N_M)) { + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP); + } + + return status == I2C_MASTER_ERR_NONE; +} +/*---------------------------------------------------------------------------*/ +void +board_i2c_shutdown() +{ + interface = NO_INTERFACE; + + if(accessible()) { + ti_lib_i2c_master_disable(I2C0_BASE); + } + + ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_I2C0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* + * Set all pins to GPIO Input and disable the output driver. Set internal + * pull to match external pull + * + * SDA and SCL: external PU resistor + */ + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_IOPULL_UP); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_IOPULL_UP); +} +/*---------------------------------------------------------------------------*/ +bool +board_i2c_write(uint8_t *data, uint8_t len) +{ + uint32_t i; + bool success; + + /* Write slave address */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false); + + /* Write first byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, data[0]); + + /* Check if another master has access */ + LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + + /* Assert RUN + START */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START); + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + + for(i = 1; i < len && success; i++) { + /* Write next byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, data[i]); + if(i < len - 1) { + /* Clear START */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT); + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + } + } + + /* Assert stop */ + if(success) { + /* Assert STOP */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH); + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + } + + return success; +} +/*---------------------------------------------------------------------------*/ +bool +board_i2c_write_single(uint8_t data) +{ + /* Write slave address */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false); + + /* Write first byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, data); + + /* Check if another master has access */ + LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + + /* Assert RUN + START + STOP */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND); + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + + return i2c_status(); +} +/*---------------------------------------------------------------------------*/ +bool +board_i2c_read(uint8_t *data, uint8_t len) +{ + uint8_t i; + bool success; + + /* Set slave address */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, true); + + /* Check if another master has access */ + LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + + /* Assert RUN + START + ACK */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START); + + i = 0; + success = true; + while(i < (len - 1) && success) { + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + if(success) { + data[i] = ti_lib_i2c_master_data_get(I2C0_BASE); + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT); + i++; + } + } + + if(success) { + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + if(success) { + data[len - 1] = ti_lib_i2c_master_data_get(I2C0_BASE); + LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + } + } + + return success; +} +/*---------------------------------------------------------------------------*/ +bool +board_i2c_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata, uint8_t rlen) +{ + uint32_t i; + bool success; + + /* Set slave address for write */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false); + + /* Write first byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, wdata[0]); + + /* Check if another master has access */ + LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + + /* Assert RUN + START */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START); + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + + for(i = 1; i < wlen && success; i++) { + /* Write next byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, wdata[i]); + + /* Clear START */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT); + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + } + if(!success) { + return false; + } + + /* Set slave address for read */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, true); + + /* Assert ACK */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START); + + i = 0; + while(i < (rlen - 1) && success) { + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + if(success) { + rdata[i] = ti_lib_i2c_master_data_get(I2C0_BASE); + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT); + i++; + } + } + + if(success) { + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + if(success) { + rdata[rlen - 1] = ti_lib_i2c_master_data_get(I2C0_BASE); + LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + } + } + + return success; +} +/*---------------------------------------------------------------------------*/ +void +board_i2c_select(uint8_t address) +{ + slave_addr = address; + + if(accessible() == false) { + board_i2c_wakeup(); + } + + ti_lib_i2c_master_disable(I2C0_BASE); + + ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_NO_IOPULL); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_NO_IOPULL); + ti_lib_ioc_pin_type_i2c(I2C0_BASE, BOARD_IOID_SDA, BOARD_IOID_SCL); + + /* Enable and initialize the I2C master module */ + ti_lib_i2c_master_init_exp_clk(I2C0_BASE, ti_lib_sys_ctrl_clock_get(), + true); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/launchpad/board-i2c.h b/platform/srf06-cc26xx/launchpad/board-i2c.h new file mode 100644 index 00000000000..72f9519d2da --- /dev/null +++ b/platform/srf06-cc26xx/launchpad/board-i2c.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-i2c SensorTag 2.0 I2C functions + * @{ + * + * \file + * Header file for the Sensortag I2C Driver + */ +/*---------------------------------------------------------------------------*/ +#ifndef BOARD_I2C_H_ +#define BOARD_I2C_H_ +/*---------------------------------------------------------------------------*/ +#include +#include +/*---------------------------------------------------------------------------*/ +#define BOARD_I2C_INTERFACE_0 0 +#define BOARD_I2C_INTERFACE_1 1 +/*---------------------------------------------------------------------------*/ +/** + * \brief Put the I2C controller in a known state + */ +#define board_i2c_deselect() board_i2c_select(0) +/*---------------------------------------------------------------------------*/ +/** + * \brief Select an I2C slave + * \param slave_addr The slave's address + */ +void board_i2c_select(uint8_t slave_addr); + +/** + * \brief Burst read from an I2C device + * \param buf Pointer to a buffer where the read data will be stored + * \param len Number of bytes to read + * \return True on success + */ +bool board_i2c_read(uint8_t *buf, uint8_t len); + +/** + * \brief Burst write to an I2C device + * \param buf Pointer to the buffer to be written + * \param len Number of bytes to write + * \return True on success + */ +bool board_i2c_write(uint8_t *buf, uint8_t len); + +/** + * \brief Single write to an I2C device + * \param data The byte to write + * \return True on success + */ +bool board_i2c_write_single(uint8_t data); + +/** + * \brief Write and read in one operation + * \param wdata Pointer to the buffer to be written + * \param wlen Number of bytes to write + * \param rdata Pointer to a buffer where the read data will be stored + * \param rlen Number of bytes to read + * \return True on success + */ +bool board_i2c_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata, + uint8_t rlen); + +/** + * \brief Enables the I2C peripheral with defaults + * + * This function is called to wakeup and initialise the I2C. + * + * This function can be called explicitly, but it will also be called + * automatically by board_i2c_select() when required. One of those two + * functions MUST be called before any other I2C operation after a chip + * sleep / wakeup cycle or after a call to board_i2c_shutdown(). Failing to do + * so will lead to a bus fault. + */ +void board_i2c_wakeup(void); + +/** + * \brief Stops the I2C peripheral and restores pins to s/w control + * + * This function is called automatically by the board's LPM logic, but it + * can also be called explicitly. + */ +void board_i2c_shutdown(void); +/*---------------------------------------------------------------------------*/ +#endif /* BOARD_I2C_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/launchpad/board.c b/platform/srf06-cc26xx/launchpad/board.c index 11a213cf1c2..a88278683d8 100644 --- a/platform/srf06-cc26xx/launchpad/board.c +++ b/platform/srf06-cc26xx/launchpad/board.c @@ -42,6 +42,7 @@ #include "ti-lib.h" #include "board-peripherals.h" #include "rf-core/rf-switch.h" +#include "board-i2c.h" #include #include @@ -56,6 +57,13 @@ wakeup_handler(void) != PRCM_DOMAIN_POWER_ON); } /*---------------------------------------------------------------------------*/ +static void +shutdown_handler(uint8_t mode) +{ + /* Stop the I2C */ + board_i2c_shutdown(); +} +/*---------------------------------------------------------------------------*/ /* * Declare a data structure to register with LPM. * We don't care about what power mode we'll drop to, we don't care about @@ -108,6 +116,9 @@ board_init() if(!int_disabled) { ti_lib_int_master_enable(); } + + /* I2C controller */ + board_i2c_wakeup(); } /*---------------------------------------------------------------------------*/ /** @} */