Subversion Repositories idreammicro-avr

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/**************************************************************************//**
 * \brief EEPROM 24C04/24C08/24C16 library.
 * \author Copyright (C) 2012  Julien Le Sech - www.idreammicro.com
 * \version 1.0
 * \date 20121102
 *
 * 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 eeprom_24c04_24c16.c
 ******************************************************************************/


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


#include "../eeprom_24c04_24c16.h"

#include <twi/twi.h>

#include <util/delay.h>

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

/******************************************************************************
 * Private macro definitions.
 ******************************************************************************/


/**************************************************************************//**
 * \def     EEPROM_24C04_24C16__ADDRESS
 * \brief   EEPROM 24C04/24C08/24C16 address. Given by datasheet.
 ******************************************************************************/

#define EEPROM_24C04_24C16__ADDRESS      0xA0

/**************************************************************************//**
 * \def     EEPROM_24C04_24C16__PAGE_SIZE
 * \brief   Size of a page in EEPROM memory.
 * This size is given by EEPROM memory datasheet.
 ******************************************************************************/

#define EEPROM_24C04_24C16__PAGE_SIZE    16

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


/**************************************************************************//**
 * \fn static void eeprom_24c04_24c16__write_page(
 * uint16_t address,
 * uint8_t  length,
 * uint8_t* p_data)
 *
 * \brief Write a page in memory.
 *
 * \param       address Start address of data to write.
 * \param       length  Number of bytes to write.
 * \param[in]   p_data  Bytes to write.
 ******************************************************************************/

static
void
eeprom_24c04_24c16__write_page
(
    uint16_t    address,
    uint8_t     length,
    uint8_t*    p_data
);

/******************************************************************************
 * Private variable declarations.
 ******************************************************************************/


static uint8_t device_address = 0;

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


/**************************************************************************//**
 * \fn void eeprom_24c04_24c16__initialize(uint8_t hardware_address)
 *
 * \brief Initialize EEPROM 24C04/24C08/24C16.
 *
 * \param hardware_address Hardware address. Pins A1 to A2.
 ******************************************************************************/

void
eeprom_24c04_24c16__initialize
(
    uint8_t hardware_address
){
    // Compute device address.
    device_address = EEPROM_24C04_24C16__ADDRESS | ((hardware_address & 0x06) << 1);

    // We don't initialize TWI here: we may have several devices on the bus.
    // Initialize TWI.
    //twi__initialize(EEPROM_24C04_24C16__CLOCK_RATE);
}

/**************************************************************************//**
 * \fn void eeprom_24c04_24c16__write_byte(uint16_t address, uint8_t data)
 *
 * \brief Write a byte into memory.
 *
 * \param   address Address in memory.
 * \param   data    Byte to write.
 ******************************************************************************/

void
eeprom_24c04_24c16__write_byte
(
    uint16_t    address,
    uint8_t     data
){
    twi__start();
    twi__send_slaw(device_address | ((address & 0x30) >> 7));
    twi__send_data(address & 0xFF);
    twi__send_data(data);
    twi__stop();
}

/**************************************************************************//**
 * \fn void eeprom_24c04_24c16__write_bytes(
 * uint16_t address,
 * uint16_t length,
 * uint8_t* p_data)
 *
 * \brief Write bytes into memory.
 *
 * \param       address Start address.
 * \param       length  Number of bytes to write.
 * \param[in]   p_data  Bytes to write.
 ******************************************************************************/

void
eeprom_24c04_24c16__write_bytes
(
    uint16_t    address,
    uint16_t    length,
    uint8_t*    p_data
){
    // Check the preconditions.
    assert(NULL != p_data);

    // Write first page if not aligned.
    uint8_t not_aligned_length = 0;
    uint8_t page_offset = address % EEPROM_24C04_24C16__PAGE_SIZE;
    if (page_offset > 0)
    {
        not_aligned_length = EEPROM_24C04_24C16__PAGE_SIZE - page_offset;
        eeprom_24c04_24c16__write_page(address, not_aligned_length, p_data);
        length -= not_aligned_length;
    }

    if (length > 0)
    {
        address += not_aligned_length;
        p_data += not_aligned_length;

        // Write complete and aligned pages.
        uint16_t page_count = length / EEPROM_24C04_24C16__PAGE_SIZE;
        for (uint16_t i = 0; i < page_count; i++)
        {
            eeprom_24c04_24c16__write_page(address, EEPROM_24C04_24C16__PAGE_SIZE, p_data);
            address += EEPROM_24C04_24C16__PAGE_SIZE;
            p_data += EEPROM_24C04_24C16__PAGE_SIZE;
            length -= EEPROM_24C04_24C16__PAGE_SIZE;
        }

        if (length > 0)
        {
            // Write remaining uncomplete page.
            eeprom_24c04_24c16__write_page(address, EEPROM_24C04_24C16__PAGE_SIZE, p_data);
        }
    }
}

/**************************************************************************//**
 * \fn uint8_t eeprom_24c04_24c16__read_byte(uint16_t address)
 *
 * \brief Read a byte in memory.
 *
 * \param address   Address to read.
 *
 * \return Read byte.
 ******************************************************************************/

uint8_t
eeprom_24c04_24c16__read_byte
(
    uint16_t address
){
    uint8_t data = 0;

    twi__start();
    twi__send_slaw(device_address | ((address & 0x30) >> 7));
    twi__send_data(address & 0xFF);
    twi__repeat_start();
    twi__send_slar(device_address);
    twi__receive_data_nack(&data);
    twi__stop();

    return data;
}

/**************************************************************************//**
 * \fn void eeprom_24c04_24c16__read_bytes(
 * uint16_t address,
 * uint16_t length,
 * uint8_t* p_data)
 *
 * \brief Read bytes in memory.
 *
 * \param       address Start address of data to read.
 * \param       length  Number of bytes to read.
 * \param[in]   p_data  Buffer to fill with read bytes.
 ******************************************************************************/

void
eeprom_24c04_24c16__read_bytes
(
    uint16_t    address,
    uint16_t    length,
    uint8_t*    p_data
){
    // Check the preconditions.
    assert(NULL != p_data);

    twi__start();
    twi__send_slaw(device_address | ((address & 0x30) >> 7));
    twi__send_data(address & 0xFF);
    twi__repeat_start();
    twi__send_slar(device_address);
    for (uint16_t i = 0; i < length - 1; i++)
    {
        twi__receive_data_ack(p_data + i);
    }
    twi__receive_data_nack(p_data + length - 1);
    twi__stop();
}

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


/**************************************************************************//**
 * \fn static void eeprom_24c04_24c16__write_page(
 * uint16_t address,
 * uint8_t  length,
 * uint8_t* p_data)
 *
 * \brief Write a page in memory.
 *
 * \param       address Start address of data to write.
 * \param       length  Number of bytes to write.
 * \param[in]   p_data  Bytes to write.
 ******************************************************************************/

static
void
eeprom_24c04_24c16__write_page
(
    uint16_t    address,
    uint8_t     length,
    uint8_t*    p_data
){
    // Check the preconditions.
    assert(NULL != p_data);

    twi__start();
    twi__send_slaw(device_address | ((address & 0x30) >> 7));
    twi__send_data(address & 0xFF);
    for (uint8_t i = 0; i < length; i++)
    {
        twi__send_data(p_data[i]);
    }
    twi__stop();

    // Write cycle time (tWR). See EEPROM memory datasheet for more details.
    _delay_ms(10);
}