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
;
}