Subversion Repositories idreammicro-avr

Rev

Rev 17 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/**************************************************************************//**
 * \brief TWI library
 * \author Copyright (C) 2009  Julien Le Sech - www.idreammicro.com
 * \version 1.0
 * \date 20090501
 *
 * This file is part of the iDreamMicro library.
 *
 * This library is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 ******************************************************************************/


/**************************************************************************//**
 * \file twi.c
 ******************************************************************************/


/******************************************************************************
 * Header file inclusions.
 ******************************************************************************/


#include <twi/twi.h>
#include <useful/bits.h>

#include <avr/io.h>
#include <util/twi.h>

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

/******************************************************************************
 * Private macros.
 ******************************************************************************/


/**************************************************************************//**
 * \def     TWI__MAX_FREQUENCY
 * \brief   Max frequency of TWI bus.
 ******************************************************************************/

#define TWI__MAX_FREQUENCY  400000

/******************************************************************************
 * Private types.
 ******************************************************************************/


/**************************************************************************//**
 * \enum    twi__prescaler_values
 * \brief   Available TWI bus prescaler values.
 *
 * \typedef twi__prescaler_value_t
 * \brief   TWI bus prescaler value.
 ******************************************************************************/

typedef enum twi__prescaler_values
{
    TWI__PRESCALER_VALUE__1,
    TWI__PRESCALER_VALUE__4,
    TWI__PRESCALER_VALUE__16,
    TWI__PRESCALER_VALUE__64,
    TWI__PRESCALER_VALUE__INVALID
} twi__prescaler_value_t;

/**************************************************************************//**
 * \struct  twi__prescaler_configuration
 * \brief   TWI bus prescaler configuration.
 *
 * \typedef twi__prescaler_configuration_t
 * \brief   TWI bus prescaler configuration.
 ******************************************************************************/

typedef struct twi__prescaler_configuration
{
    uint8_t value;
    uint8_t bits;
} twi__prescaler_configuration_t;

/******************************************************************************
 * Private constant definitions.
 ******************************************************************************/


/**************************************************************************//**
 * \var     prescaler_configurations
 * \brief   Available prescaler configurations.
 ******************************************************************************/

static const twi__prescaler_configuration_t prescaler_configurations[] =
{
    [TWI__PRESCALER_VALUE__1] =
    {
        .value  = 1,
        .bits   = 0
    },
    [TWI__PRESCALER_VALUE__4] =
    {
        .value  = 4,
        .bits   = _BV(TWPS0)
    },
    [TWI__PRESCALER_VALUE__16] =
    {
        .value  = 16,
        .bits   = _BV(TWPS1)
    },
    [TWI__PRESCALER_VALUE__64] =
    {
        .value  = 64,
        .bits   = _BV(TWPS1) | _BV(TWPS0)
    }
};

/******************************************************************************
 * Private function prototypes.
 ******************************************************************************/


/**************************************************************************//**
 * \fn static inline uint8_t twi__compute_bit_rate(
 * uint32_t f_scl,
 * uint8_t  prescaler_value)
 *
 * \brief
 *
 * \param   f_scl           SCL frequency.
 * \param   prescaler_value Prescaler value.
 ******************************************************************************/

static inline
uint8_t
twi__compute_bit_rate
(
    uint32_t    f_scl,
    uint8_t     prescaler_value
);

/******************************************************************************
 * Public function definitions.
 ******************************************************************************/


/**************************************************************************//**
 * \fn void twi__initialize(uint32_t frequency)
 *
 * \brief Initialize TWI.
 *
 * \param   frequency   TWI frequency (up to 400 kHz).
 ******************************************************************************/

void twi__initialize
(
    uint32_t frequency
){
    // Check the preconditions.
    assert(TWI__MAX_FREQUENCY >= frequency);

    uint16_t bit_rate = UINT16_MAX;
    twi__prescaler_value_t prescaler_value = TWI__PRESCALER_VALUE__1;

    // Compute bit rate and prescaler value.
    do
    {
        bit_rate = twi__compute_bit_rate
        (
            frequency,
            prescaler_configurations[prescaler_value].value
        );
    } while (UINT8_MAX < bit_rate && TWI__PRESCALER_VALUE__INVALID > prescaler_value++);

    // Set bit rate.
    TWBR = bit_rate;

    // Set prescaler value.
    TWSR = prescaler_configurations[prescaler_value].bits;
}

/**************************************************************************//**
 * \fn twi__error_t twi__start(void)
 *
 * \brief
 ******************************************************************************/

twi__error_t
twi__start
(
    void
){
    // Send start condition.
    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);

    // Wait for TWINT flag set. This indicates that the START condition has
    // been transmitted.
    while (!(TWCR & (1 << TWINT)));

    // Check value of TWI Status Register. Mask prescaler bits.
    if (TWSR & TW_NO_INFO != TW_START)
    {
        // If status is different from START (TW_START), return error.
        return TWI__ERROR__ERROR;
    }

    return TWI__ERROR__NONE;
}

/**************************************************************************//**
 * \fn twi__error_t twi__repeat_start(void)
 *
 * \brief
 ******************************************************************************/

twi__error_t
twi__repeat_start
(
    void
){
    // TODO: check if TWSTO must be set.
    // Send start condition.
    TWCR = (1 << TWEN) | (1 << TWINT) | (1 << TWSTA) | (1 << TWSTO);

    // Wait for TWINT flag set. This indicates that the START condition has
    // been transmitted.
    while (!(TWCR & (1 << TWINT)));

    // Check value of TWI Status Register. Mask prescaler bits.
    if (TWSR & TW_NO_INFO != TW_REP_START)
    {
        //  If status different from START (TW_REP_START), return error.
        return TWI__ERROR__ERROR;
    }

    return TWI__ERROR__NONE;
}

/**************************************************************************//**
 * \fn twi__error_t twi__send_slar(uint8_t sla)
 *
 * \brief
 *
 * \param   sla
 ******************************************************************************/

twi__error_t
twi__send_slar
(
    uint8_t sla
){
    // Load SLA into TWDR register.
    TWDR = sla | TW_READ;

    // Clear TWINT bit in TWCR to start transmission of address.
    TWCR = (1 << TWINT) | (1 << TWEN);

    // Wait for TWINT flag set. This indicates that the SLA+W (TW_WRITE) has
    // been transmitted, and ACK/NACK has been received.
    while (!(TWCR & (1 << TWINT)));

    // Check value of TWI Status Register. Mask prescaler bits. If status
    // different from MT_SLA_ACK (TW_MR_SLA_ACK), return error.
    if (TWSR & TW_NO_INFO != TW_MR_SLA_ACK)
    {
        return TWI__ERROR__ERROR;
    }

    return TWI__ERROR__NONE;
}

/**************************************************************************//**
 * \fn twi__error_t twi__send_slaw(uint8_t sla)
 *
 * \brief
 *
 * \param   sla
 ******************************************************************************/

twi__error_t
twi__send_slaw
(
    uint8_t sla
){
    // Load SLA into TWDR register.
    TWDR = sla | TW_WRITE;

    // Clear TWINT bit in TWCR to start transmission of address.
    TWCR = (1 << TWINT) | (1 << TWEN);

    // Wait for TWINT flag set. This indicates that the SLA+W (TW_WRITE) has
    // been transmitted, and ACK/NACK has been received.
    while (!(TWCR & (1 << TWINT)));

    // Check value of TWI Status Register. Mask prescaler bits. If status
    // different from MT_SLA_ACK (TW_MT_SLA_ACK), return error.
    if (TWSR & TW_NO_INFO != TW_MT_SLA_ACK)
    {
        return TWI__ERROR__ERROR;
    }

    return TWI__ERROR__NONE;
}

/**************************************************************************//**
 * \fn twi__error_t twi__send_data(uint8_t data)
 *
 * \brief
 *
 * \param   data    Data to send.
 ******************************************************************************/

twi__error_t
twi__send_data
(
    uint8_t data
){
    // Load data into TWDR register.
    TWDR = data;

    // Clear TWINT bit in TWCR to start transmission of data.
    TWCR = (1 << TWINT) | (1 << TWEN);

    // Wait for TWINT flag set. This indicates that the data has been
    // transmitted, and ACK/NACK has been receiver.
    while (!(TWCR & (1 << TWINT)));

    // Check value of TWI status register. Mask prescaler bits. If status
    // different from MT_DATA_ACK (TW_MT_DATA_ACK), return error.
    if (TWSR & TW_NO_INFO != TW_MT_DATA_ACK)
    {
        return TWI__ERROR__ERROR;
    }

    return TWI__ERROR__NONE;
}

/**************************************************************************//**
 * \fn twi__error_t twi__receive_data_ack(uint8_t* p_data)
 *
 * \brief
 *
 * \param   p_data
 ******************************************************************************/

twi__error_t
twi__receive_data_ack
(
    uint8_t* p_data
){
    // Check the preconditions.
    assert(NULL != p_data);

    TWCR = (1 << TWEN) | (1 << TWINT) | (1 << TWEA);

    while (!(TWCR & (1 << TWINT)));

    if ((TWSR & TW_NO_INFO) != TW_MR_DATA_ACK)
    {
        return TWI__ERROR__ERROR;
    }

    *p_data = TWDR;

    return TWI__ERROR__NONE;
}

/**************************************************************************//**
 * \fn twi__error_t twi__receive_data_nack(uint8_t* p_data)
 *
 * \brief
 *
 * \param   p_data
 ******************************************************************************/

twi__error_t
twi__receive_data_nack
(
    uint8_t* p_data
){
    // Check the preconditions.
    assert(NULL != p_data);

    TWCR = (1 << TWEN) | (1 << TWINT);

    while (!(TWCR & (1 << TWINT)));

    if ((TWSR & TW_NO_INFO) != TW_MR_DATA_NACK)
    {
        return TWI__ERROR__ERROR;
    }

    *p_data = TWDR;

    return TWI__ERROR__NONE;
}

/**************************************************************************//**
 * \fn void twi__stop(void)
 *
 * \brief Stop TWI.
 ******************************************************************************/

void
twi__stop
(
    void
){
    // Transmit stop operation.
    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);

    // Wait for stop.
    while (TWCR & (1 << TWSTO));
}

/******************************************************************************
 * Private function definitions.
 ******************************************************************************/


/**************************************************************************//**
 * \fn static inline uint8_t twi__compute_bit_rate(
 * uint32_t f_scl,
 * uint8_t  prescaler_value)
 *
 * \brief
 *
 * \param   f_scl           SCL frequency.
 * \param   prescaler_value Prescaler value.
 ******************************************************************************/

static inline
uint8_t
twi__compute_bit_rate
(
    uint32_t    f_scl,
    uint8_t     prescaler_value
){
    uint8_t bit_rate = (F_CPU - 16 * f_scl) / (2 * f_scl * prescaler_value);

    return bit_rate;
}