nes-proj/arch/platform/z1/dev/i2cmaster.c

229 lines
8.8 KiB
C

/*
* Copyright (c) 2010, Swedish Institute of Computer Science.
* 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 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* I2C communication device drivers for Zolertia Z1 sensor node.
* \author
* Enric M. Calvo, Zolertia <ecalvo@zolertia.com>
* Marcus Lundén, SICS <mlunden@sics.se>
*/
#include "i2cmaster.h"
#include "isr_compat.h"
signed char tx_byte_ctr, rx_byte_ctr;
unsigned char rx_buf[2];
unsigned char *tx_buf_ptr;
unsigned char *rx_buf_ptr;
unsigned char receive_data;
unsigned char transmit_data1;
unsigned char transmit_data2;
unsigned char prescale_lsb = I2C_PRESC_Z1_LSB;
unsigned char prescale_msb = I2C_PRESC_Z1_MSB;
volatile unsigned int i; /* volatile to prevent optimization */
/* ------------------------------------------------------------------------------
* Change the data rate prior initializing transmission or reception
* ----------------------------------------------------------------------------- */
void
i2c_setrate(uint8_t p_lsb, uint8_t p_msb)
{
prescale_lsb = p_lsb;
prescale_lsb = p_msb;
}
/* ------------------------------------------------------------------------------
* This function initializes the USCI module for master-receive operation.
* ----------------------------------------------------------------------------- */
void
i2c_receiveinit(uint8_t slave_address)
{
UCB1CTL1 = UCSWRST; /* Enable SW reset */
UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC; /* I2C Master, synchronous mode */
UCB1CTL1 = UCSSEL_2 | UCSWRST; /* Use SMCLK, keep SW reset */
UCB1BR0 = prescale_lsb; /* prescaler (default 400 kHz) */
UCB1BR1 = prescale_msb;
UCB1I2CSA = slave_address; /* set slave address */
UCB1CTL1 &= ~UCTR; /* I2C Receiver */
UCB1CTL1 &= ~UCSWRST; /* Clear SW reset, resume operation */
UCB1I2CIE = UCNACKIE;
#if I2C_RX_WITH_INTERRUPT
UC1IE = UCB1RXIE; /* Enable RX interrupt if desired */
#endif
}
/* ------------------------------------------------------------------------------
* Initializes USCI for master-transmit operation.
* ------------------------------------------------------------------------------ */
void
i2c_transmitinit(uint8_t slave_address)
{
UCB1CTL1 |= UCSWRST; /* Enable SW reset */
UCB1CTL0 |= (UCMST | UCMODE_3 | UCSYNC); /* I2C Master, synchronous mode */
UCB1CTL1 = UCSSEL_2 + UCSWRST; /* Use SMCLK, keep SW reset */
UCB1BR0 = prescale_lsb; /* prescaler (default 400 kHz) */
UCB1BR1 = prescale_msb;
UCB1I2CSA = slave_address; /* Set slave address */
UCB1CTL1 &= ~UCSWRST; /* Clear SW reset, resume operation */
UCB1I2CIE = UCNACKIE;
UC1IE = UCB1TXIE; /* Enable TX ready interrupt */
}
/* ------------------------------------------------------------------------------
* This function is used to start an I2C communication in master-receiver mode WITHOUT INTERRUPTS
* for more than 1 byte
* ------------------------------------------------------------------------------ */
static volatile uint8_t rx_byte_tot = 0;
uint8_t
i2c_receive_n(uint8_t byte_ctr, uint8_t *rx_buf)
{
rx_byte_tot = byte_ctr;
rx_byte_ctr = byte_ctr;
rx_buf_ptr = rx_buf;
while((UCB1CTL1 & UCTXSTT) || (UCB1STAT & UCNACKIFG)) /* Slave acks address or not? */
PRINTFDEBUG("____ UCTXSTT not clear OR NACK received\n");
#if I2C_RX_WITH_INTERRUPT
PRINTFDEBUG(" RX Interrupts: YES \n");
/* SPECIAL-CASE: Stop condition must be sent while receiving the 1st byte for 1-byte only read operations */
if(rx_byte_tot == 1) { /* See page 537 of slau144e.pdf */
dint();
UCB1CTL1 |= UCTXSTT; /* I2C start condition */
while(UCB1CTL1 & UCTXSTT) /* Waiting for Start bit to clear */
PRINTFDEBUG("____ STT clear wait\n");
UCB1CTL1 |= UCTXSTP; /* I2C stop condition */
eint();
} else { /* all other cases */
UCB1CTL1 |= UCTXSTT; /* I2C start condition */
}
return 0;
#else
uint8_t n_received = 0;
PRINTFDEBUG(" RX Interrupts: NO \n");
UCB1CTL1 |= UCTXSTT; /* I2C start condition */
while(rx_byte_ctr > 0) {
if(UC1IFG & UCB1RXIFG) { /* Waiting for Data */
rx_buf[rx_byte_tot - rx_byte_ctr] = UCB1RXBUF;
rx_byte_ctr--;
UC1IFG &= ~UCB1RXIFG; /* Clear USCI_B1 RX int flag */
n_received++;
}
}
UCB1CTL1 |= UCTXSTP; /* I2C stop condition */
return n_received;
#endif
}
/* ------------------------------------------------------------------------------
* This function is used to check if there is communication in progress.
* ------------------------------------------------------------------------------ */
uint8_t
i2c_busy(void)
{
return UCB1STAT & UCBBUSY;
}
/*----------------------------------------------------------------------------
* Setup ports and pins for I2C use.
* ------------------------------------------------------------------------------ */
void
i2c_enable(void)
{
I2C_PxSEL |= (I2C_SDA | I2C_SCL); /* Secondary function (USCI) selected */
I2C_PxSEL2 |= (I2C_SDA | I2C_SCL); /* Secondary function (USCI) selected */
I2C_PxDIR |= I2C_SCL; /* SCL is output (not needed?) */
I2C_PxDIR &= ~I2C_SDA; /* SDA is input (not needed?) */
I2C_PxREN |= (I2C_SDA | I2C_SCL); /* Activate internal pull-up/-down resistors */
I2C_PxOUT |= (I2C_SDA | I2C_SCL); /* Select pull-up resistors */
}
void
i2c_disable(void)
{
I2C_PxSEL &= ~(I2C_SDA | I2C_SCL); /* GPIO function selected */
I2C_PxSEL2 &= ~(I2C_SDA | I2C_SCL); /* GPIO function selected */
I2C_PxREN &= ~(I2C_SDA | I2C_SCL); /* Deactivate internal pull-up/-down resistors */
I2C_PxOUT &= ~(I2C_SDA | I2C_SCL); /* Select pull-up resistors */
}
/* ------------------------------------------------------------------------------
* This function is used to start an I2C communication in master-transmit mode.
* ------------------------------------------------------------------------------ */
static volatile uint8_t tx_byte_tot = 0;
void
i2c_transmit_n(uint8_t byte_ctr, uint8_t *tx_buf)
{
tx_byte_tot = byte_ctr;
tx_byte_ctr = byte_ctr;
tx_buf_ptr = tx_buf;
UCB1CTL1 |= UCTR + UCTXSTT; /* I2C TX, start condition */
}
/*----------------------------------------------------------------------------*/
ISR(USCIAB1TX, i2c_tx_interrupt)
{
/* TX Part */
if(UC1IFG & UCB1TXIFG) { /* TX int. condition */
if(tx_byte_ctr == 0) {
UCB1CTL1 |= UCTXSTP; /* I2C stop condition */
UC1IFG &= ~UCB1TXIFG; /* Clear USCI_B1 TX int flag */
} else {
UCB1TXBUF = tx_buf_ptr[tx_byte_tot - tx_byte_ctr];
tx_byte_ctr--;
}
}
/* RX Part */
#if I2C_RX_WITH_INTERRUPT
else if(UC1IFG & UCB1RXIFG) { /* RX int. condition */
rx_buf_ptr[rx_byte_tot - rx_byte_ctr] = UCB1RXBUF;
rx_byte_ctr--;
if(rx_byte_ctr == 1) { /* stop condition should be set before receiving last byte */
/* Only for 1-byte transmissions, STOP is handled in receive_n_int */
if(rx_byte_tot != 1) {
UCB1CTL1 |= UCTXSTP; /* I2C stop condition */
}
UC1IFG &= ~UCB1RXIFG; /* Clear USCI_B1 RX int flag. XXX Just in case, check if necessary */
}
}
#endif
}
ISR(USCIAB1RX, i2c_rx_interrupt)
{
if(UCB1STAT & UCNACKIFG) {
PRINTFDEBUG("!!! NACK received in RX\n");
UCB1CTL1 |= UCTXSTP;
UCB1STAT &= ~UCNACKIFG;
}
}