/* * Copyright (c) 2011, Hedde Bosman * * I2C communication device drivers for mc1322x * * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. * */ #include "i2c.h" #include static int8_t tx_byte_ctr; static int8_t rx_byte_ctr; static uint8_t* tx_buf_ptr; static uint8_t* rx_buf_ptr; volatile unsigned int i; // volatile to prevent optimization static inline void i2c_send_byte(void) { *I2CDR = *(tx_buf_ptr++); // set new byte, MCF is automatically cleared tx_byte_ctr--; } static inline void i2c_recv_byte(void) { *(rx_buf_ptr++) = *I2CDR; rx_byte_ctr--; } //------------------------------------------------------------------------------ // void i2c_receiveinit(uint8_t slave_address, // uint8_t prescale) // // This function initializes the USCI module for master-receive operation. // // IN: uint8_t slave_address => Slave Address // uint8_t prescale => SCL clock adjustment //----------------------------------------------------------------------------- //static volatile uint8_t rx_byte_tot = 0; void i2c_receiveinit(uint8_t slave_address, uint8_t byte_ctr, uint8_t *rx_buf) { // wait for bus to be free before setting MSTA (assuming we're in a multi-master environment or still sending something else) while(i2c_busy()) /* wait */; // assert: rx_byte_ctr <= 0 // assert: tx_byte_ctr <= 0 tx_buf_ptr = 0; tx_byte_ctr = 0; // indicate that nothing is to be received rx_byte_ctr = byte_ctr; rx_buf_ptr = rx_buf; // clockdiv //*I2CFDR = 0x20; // 150 khz for redbee econotag // assume being master, thus no addres has to be set *I2CCR = I2C_MEN | #ifdef I2C_NON_BLOCKING I2C_MIEN | #endif I2C_MSTA | I2C_MTX | I2C_RXAK; // start condition is triggered // write out address of slave *I2CDR = (slave_address & 0x7f) <<1 | 0x01; #ifndef I2C_NON_BLOCKING i2c_receive(); #endif } //------------------------------------------------------------------------------ // void i2c_transmitinit(uint8_t slave_address, // uint8_t prescale) // // Initializes USCI for master-transmit operation. // // IN: uint8_t slave_address => Slave Address // uint8_t prescale => SCL clock adjustment //------------------------------------------------------------------------------ //static volatile uint8_t tx_byte_tot = 0; void i2c_transmitinit(uint8_t slave_address, uint8_t byte_ctr, uint8_t *tx_buf) { // wait for bus to be free before setting MSTA (assuming we're in a multi-master environment or still sending something else) while(i2c_busy()) /* wait */; // assert: rx_byte_ctr <= 0 // assert: tx_byte_ctr <= 0 rx_buf_ptr = 0; rx_byte_ctr = 0; // indicate that nothing is to be received tx_byte_ctr = byte_ctr; tx_buf_ptr = tx_buf; // clockdiv //*I2CFDR = 0x20; // 150 khz for redbee econotag // assume being master, thus no addres has to be set *I2CCR = I2C_MEN | #ifdef I2C_NON_BLOCKING I2C_MIEN | #endif I2C_MSTA | I2C_MTX ; // start condition is triggered // write out address of slave *I2CDR = (slave_address & 0x7f) <<1; #ifndef I2C_NON_BLOCKING i2c_transmit(); #endif } /*----------------------------------------------------------------------------*/ /*- blocking counterparts of interrupt hanlder function ---------------------*/ /*----------------------------------------------------------------------------*/ #ifndef I2C_NON_BLOCKING /*----------------------------------------------------------------------------*/ uint8_t i2c_receive() { while(rx_byte_ctr > 0) { // busy wait while(!(*I2CSR & I2C_MCF) || !(*I2CSR & I2C_MIF)) /*wait*/; if (rx_byte_ctr == 1) { // receiving next-to-last byte, thus turn off auto-ack for stop condition *I2CCR |= I2C_TXAK; } if (*I2CSR & I2C_MCF) { i2c_recv_byte(); // read new byte } if (*I2CSR & I2C_MAL) { *I2CSR &= ~I2C_MAL; // should be cleared in software printf("*** ERROR I2C: Arbitration lost\n"); // Arbitration lost; ERROR? } } while(!(*I2CSR & I2C_MCF) || !(*I2CSR & I2C_MIF)) /*wait*/; if (*I2CSR & I2C_RXAK) { // NO acknoledge byte received printf("*** ERROR I2C: No ack received\n"); } if (*I2CSR & I2C_MAL) { *I2CSR &= ~I2C_MAL; // should be cleared in software printf("*** ERROR I2C: Arbitration lost\n"); // Arbitration lost; ERROR? } *I2CCR &= ~I2C_MSTA; // stop condition } /*----------------------------------------------------------------------------*/ void i2c_transmit() { while(tx_byte_ctr > 0) { // busy wait while(!(*I2CSR & I2C_MCF) || !(*I2CSR & I2C_MIF)) /*wait*/; if (*I2CSR & I2C_RXAK) { // NO acknoledge byte received printf("*** ERROR I2C: No ack received\n"); } if (*I2CSR & I2C_MCF) { i2c_send_byte(); } if (*I2CSR & I2C_MAL) { *I2CSR &= ~I2C_MAL; // should be cleared in software printf("*** ERROR I2C: Arbitration lost\n"); // Arbitration lost; ERROR? } // clear MIF *I2CSR &= ~I2C_MIF; } while(!(*I2CSR & I2C_MCF) || !(*I2CSR & I2C_MIF)) /*wait*/; if (*I2CSR & I2C_RXAK) { // NO acknoledge byte received printf("*** ERROR I2C: No ack received\n"); } if (*I2CSR & I2C_MAL) { *I2CSR &= ~I2C_MAL; // should be cleared in software printf("*** ERROR I2C: Arbitration lost\n"); // Arbitration lost; ERROR? } *I2CCR &= ~I2C_MSTA; // stop condition } #endif /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ /* force SCL to become bus master when sda is still low (can occur after sys reset */ /*----------------------------------------------------------------------------*/ void i2c_force_reset(void) { uint8_t tmp; *I2CCR = 0x20; *I2CCR = 0xA0; tmp = *I2CDR; // return to module state Master? } /*----------------------------------------------------------------------------*/ /*- check if we're still in the process of sending something ----------------*/ /*----------------------------------------------------------------------------*/ uint8_t i2c_transferred(void) { return (!i2c_busy() && rx_byte_ctr == 0 && tx_byte_ctr == 0); } /*----------------------------------------------------------------------------*/ /* check if there is communication in progress on the i2c bus. */ /*----------------------------------------------------------------------------*/ uint8_t i2c_busy(void) { return ((*I2CSR & I2C_MBB) > 0); // bit 5 high = busy } /*----------------------------------------------------------------------------*/ /* Setup ports and pins for I2C use. */ /*----------------------------------------------------------------------------*/ void i2c_enable(void) { // enable clock signal to i2c module *I2CCKER = I2C_CKEN; // enable enable_irq(I2C); *I2CFDR = 0x20; // clockdiv: 150 khz for redbee econotag *I2CADR = 0x01; // our slave address (not used; we're master) // then enable i2c module *I2CCR |= I2C_MEN | // module-enable, auto-ack = on #ifdef I2C_NON_BLOCKING I2C_MIEN | //module-interrupt-enable #endif 0; // then switch gpio pins to i2c *GPIO_FUNC_SEL0 |= (0x01 << (I2C_SCL*2)) | (0x01 << (I2C_SDA*2)); // GPIO 12, 13 to i2c // and enable pull-up resistors *GPIO_PAD_PU_EN0 |= (0x01 << I2C_SCL) | (0x01 << I2C_SDA); // Activate internal pull-up/-down resistors *GPIO_PAD_PU_SEL0 |= (0x01 << I2C_SCL) | (0x01 << I2C_SDA); // select pull-up resistors for ports } /*----------------------------------------------------------------------------*/ /* Reset ports and pins for GPIO use. */ /*----------------------------------------------------------------------------*/ void i2c_disable(void) { // all control values are set off *I2CCR = 0; // clock is turned off *I2CCKER = ~I2C_CKEN; // then switch gpio pins to gpio *GPIO_FUNC_SEL0 &= ~(0x01 << (I2C_SCL*2)) | (0x01 << (I2C_SDA*2)); // GPIO 12, 13 to i2c // and disable resistors *GPIO_PAD_PU_EN0 &= ~(0x01 << I2C_SCL) | (0x01 << I2C_SDA); // Deactivate internal pull-up/-down resistors disable_irq(I2C); } /*----------------------------------------------------------------------------*/ /*- i2c interrupt handler --------------------------------------------------*/ /*----------------------------------------------------------------------------*/ #ifdef I2C_NON_BLOCKING void i2c_isr (void) { uint8_t dummy; if (*I2CSR & I2C_MIF) { // interrupt is from i2c if (*I2CSR & I2C_MCF) { // one byte transferred/received. will be cleared automatically when I2CDR is written or I2CSR read if (tx_buf_ptr != 0) { // we're sending if (*I2CSR & I2C_RXAK) { // NO acknoledge byte received printf("*** ERROR I2C: No ack received\n"); } if (tx_byte_ctr > 0) { // tx i2c_send_byte(); // set new byte, MCF is automatically cleared } else { *I2CCR &= ~I2C_MSTA; // generate stop condition } } else { //if (rx_buf_ptr != 0) { // receive if (rx_byte_ctr == 1) { // receiving next-to-last byte, thus turn off auto-ack for stop condition *I2CCR |= I2C_TXAK; } if (*I2CCR & I2C_MTX) { // address byte was just sent *I2CCR &= ~I2C_MTX; // switch to receive mode dummy = *I2CDR; // dummy read to throw away the address from register } else if (rx_byte_ctr > 0) { i2c_recv_byte(); // read new byte } else { *I2CCR &= ~I2C_MSTA; // generate stop condition } } } if (*I2CSR & I2C_MAL) { *I2CSR &= ~I2C_MAL; // should be cleared in software printf("*** ERROR I2C: Arbitration lost\n"); // Arbitration lost; reset.. rx_byte_ctr = tx_byte_ctr = 0; *I2CCR &= ~I2C_MSTA; // generate stop condition } // clear MIF *I2CSR &= ~I2C_MIF; } } #endif