mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-10 21:24:12 +01:00
443 lines
13 KiB
C
443 lines
13 KiB
C
|
/* Copyright (c) 2002, 2003, 2004, 2007 Marek Michalkiewicz
|
||
|
Copyright (c) 2005, 2006 Bjoern Haase
|
||
|
Copyright (c) 2008 Atmel Corporation
|
||
|
Copyright (c) 2008 Wouter van Gulik
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
|
||
|
* Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
* 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.
|
||
|
* Neither the name of the copyright holders nor the names of
|
||
|
contributors may be used to endorse or promote products derived
|
||
|
from this software without specific prior written permission.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. */
|
||
|
|
||
|
/* $Id: eeprom.h,v 1.21.2.6 2008/08/19 22:10:39 arcanum Exp $ */
|
||
|
|
||
|
#ifndef _AVR_EEPROM_H_
|
||
|
#define _AVR_EEPROM_H_ 1
|
||
|
|
||
|
#include <avr/io.h>
|
||
|
#include <stddef.h> /* size_t */
|
||
|
#include <stdint.h>
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif
|
||
|
|
||
|
#ifndef __ATTR_PURE__
|
||
|
# ifdef __DOXYGEN__
|
||
|
# define __ATTR_PURE__
|
||
|
# else
|
||
|
# define __ATTR_PURE__ __attribute__((__pure__))
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
#if (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) )
|
||
|
uint16_t __eerd_word (const uint16_t *, uint8_t (*)(const uint8_t *))
|
||
|
__ATTR_PURE__;
|
||
|
uint32_t __eerd_dword (const uint32_t *, uint8_t (*)(const uint8_t *))
|
||
|
__ATTR_PURE__;
|
||
|
void __eerd_block (void *, const void *, size_t, uint8_t (*)(const uint8_t *));
|
||
|
|
||
|
void __eewr_word (uint16_t *, uint16_t, void (*)(uint8_t *, uint8_t));
|
||
|
void __eewr_dword (uint32_t *, uint32_t, void (*)(uint8_t *, uint8_t));
|
||
|
void __eewr_block (void *, const void *, size_t, void (*)(uint8_t *, uint8_t));
|
||
|
#endif /* (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) ) */
|
||
|
|
||
|
#if !E2END && !defined(__DOXYGEN__)
|
||
|
# ifndef __COMPILING_AVR_LIBC__
|
||
|
# warning "Device does not have EEPROM available."
|
||
|
# endif
|
||
|
/* Omit below for chips without EEPROM. */
|
||
|
|
||
|
#else
|
||
|
|
||
|
/** \defgroup avr_eeprom <avr/eeprom.h>: EEPROM handling
|
||
|
\code #include <avr/eeprom.h> \endcode
|
||
|
|
||
|
This header file declares the interface to some simple library
|
||
|
routines suitable for handling the data EEPROM contained in the
|
||
|
AVR microcontrollers. The implementation uses a simple polled
|
||
|
mode interface. Applications that require interrupt-controlled
|
||
|
EEPROM access to ensure that no time will be wasted in spinloops
|
||
|
will have to deploy their own implementation.
|
||
|
|
||
|
\note All of the read/write functions first make sure the EEPROM
|
||
|
is ready to be accessed. Since this may cause long delays if a
|
||
|
write operation is still pending, time-critical applications
|
||
|
should first poll the EEPROM e. g. using eeprom_is_ready() before
|
||
|
attempting any actual I/O. But this functions are not wait until
|
||
|
SELFPRGEN in SPMCSR becomes zero. Do this manually, if your
|
||
|
softwate contains the Flash burning.
|
||
|
|
||
|
\note As these functions modify IO registers, they are known to be
|
||
|
non-reentrant. If any of these functions are used from both,
|
||
|
standard and interrupt context, the applications must ensure
|
||
|
proper protection (e.g. by disabling interrupts before accessing
|
||
|
them).
|
||
|
|
||
|
\note All write functions force erase_and_write programming mode.
|
||
|
*/
|
||
|
|
||
|
/** \def EEMEM
|
||
|
\ingroup avr_eeprom
|
||
|
Attribute expression causing a variable to be allocated within the
|
||
|
.eeprom section. */
|
||
|
#define EEMEM __attribute__((section(".eeprom")))
|
||
|
|
||
|
|
||
|
/* Register definitions */
|
||
|
|
||
|
/* Check for aliases. */
|
||
|
#if !defined(EEWE) && defined(EEPE)
|
||
|
# define EEWE EEPE
|
||
|
#endif
|
||
|
|
||
|
#if !defined(EEMWE) && defined(EEMPE)
|
||
|
# define EEMWE EEMPE
|
||
|
#endif
|
||
|
|
||
|
#if !defined(EECR) && defined(DEECR)
|
||
|
/* AT86RF401 */
|
||
|
# define EECR DEECR
|
||
|
# define EEAR DEEAR
|
||
|
# define EEARL DEEAR
|
||
|
# define EEDR DEEDR
|
||
|
# define EERE EER
|
||
|
# define EEWE EEL
|
||
|
# define EEMWE EEU
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if !defined(EECR) || !defined(EEDR) || !defined(EEARL)
|
||
|
|
||
|
# if !defined(__EEPROM_REG_LOCATIONS__) \
|
||
|
&& !defined(EEPROM_REG_LOCATIONS_OVERRIDE)
|
||
|
/* 6-byte string denoting where to find the EEPROM registers in memory
|
||
|
space. Adresses denoted in hex syntax with uppercase letters. Used
|
||
|
by the EEPROM subroutines.
|
||
|
First two letters: EECR address.
|
||
|
Second two letters: EEDR address.
|
||
|
Last two letters: EEAR address.
|
||
|
*/
|
||
|
# error "Unknown EEPROM register(s) location."
|
||
|
# endif
|
||
|
|
||
|
/* If needed, override the locations defined in the IO headers. */
|
||
|
# ifdef EEPROM_REG_LOCATIONS_OVERRIDE
|
||
|
# undef __EEPROM_REG_LOCATIONS__
|
||
|
# define __EEPROM_REG_LOCATIONS__ EEPROM_REG_LOCATIONS_OVERRIDE
|
||
|
# endif
|
||
|
|
||
|
# define CONCAT1(a, b) CONCAT2(a, b)
|
||
|
# define CONCAT2(a, b) a ## b
|
||
|
# define HEXNR CONCAT1(0x, __EEPROM_REG_LOCATIONS__)
|
||
|
|
||
|
# undef EECR
|
||
|
# define EECR _SFR_IO8((HEXNR >> 16) & 0xFF)
|
||
|
|
||
|
# undef EEDR
|
||
|
# define EEDR _SFR_IO8((HEXNR >> 8) & 0xFF)
|
||
|
|
||
|
# undef EEAR
|
||
|
# define EEAR _SFR_IO8(HEXNR & 0xFF)
|
||
|
|
||
|
# undef EEARH
|
||
|
|
||
|
# undef EEARL
|
||
|
# define EEARL EEAR
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/** \def eeprom_is_ready
|
||
|
\ingroup avr_eeprom
|
||
|
\returns 1 if EEPROM is ready for a new read/write operation, 0 if not.
|
||
|
*/
|
||
|
#if defined(__DOXYGEN__)
|
||
|
# define eeprom_is_ready()
|
||
|
#elif defined(DEECR)
|
||
|
# define eeprom_is_ready() bit_is_clear(DEECR, BSY)
|
||
|
#else
|
||
|
# define eeprom_is_ready() bit_is_clear(EECR, EEWE)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/** \def eeprom_busy_wait
|
||
|
\ingroup avr_eeprom
|
||
|
Loops until the eeprom is no longer busy.
|
||
|
\returns Nothing.
|
||
|
*/
|
||
|
#define eeprom_busy_wait() do {} while (!eeprom_is_ready())
|
||
|
|
||
|
|
||
|
/** \ingroup avr_eeprom
|
||
|
Read one byte from EEPROM address \a __p.
|
||
|
*/
|
||
|
__ATTR_PURE__ static __inline__ uint8_t eeprom_read_byte (const uint8_t *__p)
|
||
|
{
|
||
|
do {} while (!eeprom_is_ready ());
|
||
|
#if E2END <= 0xFF
|
||
|
EEARL = (uint8_t)__p;
|
||
|
#else
|
||
|
EEAR = (uint16_t)__p;
|
||
|
#endif
|
||
|
/* Use inline assembly below as some AVRs have problems with accessing
|
||
|
EECR with STS instructions. For example, see errata for ATmega64.
|
||
|
The code below also assumes that EECR and EEDR are in the I/O space.
|
||
|
*/
|
||
|
uint8_t __result;
|
||
|
__asm__ __volatile__
|
||
|
(
|
||
|
"/* START EEPROM READ CRITICAL SECTION */ \n\t"
|
||
|
"sbi %1, %2 \n\t"
|
||
|
"in %0, %3 \n\t"
|
||
|
"/* END EEPROM READ CRITICAL SECTION */ \n\t"
|
||
|
: "=r" (__result)
|
||
|
: "i" (_SFR_IO_ADDR(EECR)),
|
||
|
"i" (EERE),
|
||
|
"i" (_SFR_IO_ADDR(EEDR))
|
||
|
);
|
||
|
return __result;
|
||
|
}
|
||
|
|
||
|
/** \ingroup avr_eeprom
|
||
|
Read one 16-bit word (little endian) from EEPROM address \a __p.
|
||
|
*/
|
||
|
__ATTR_PURE__ static __inline__ uint16_t eeprom_read_word (const uint16_t *__p)
|
||
|
{
|
||
|
#if (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) )
|
||
|
return __eerd_word (__p, eeprom_read_byte);
|
||
|
#else
|
||
|
/* If ATmega256x device, do not call function. */
|
||
|
union
|
||
|
{
|
||
|
uint16_t word;
|
||
|
struct
|
||
|
{
|
||
|
uint8_t lo;
|
||
|
uint8_t hi;
|
||
|
} byte;
|
||
|
} x;
|
||
|
|
||
|
x.byte.lo = eeprom_read_byte ((const uint8_t *)__p);
|
||
|
x.byte.hi = eeprom_read_byte ((const uint8_t *)__p + 1);
|
||
|
return x.word;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/** \ingroup avr_eeprom
|
||
|
Read one 32-bit double word (little endian) from EEPROM address \a __p.
|
||
|
*/
|
||
|
__ATTR_PURE__ static __inline__
|
||
|
uint32_t eeprom_read_dword (const uint32_t *__p)
|
||
|
{
|
||
|
#if (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) )
|
||
|
return __eerd_dword (__p, eeprom_read_byte);
|
||
|
#else
|
||
|
/* If ATmega256x device, do not call function. */
|
||
|
union
|
||
|
{
|
||
|
uint32_t dword;
|
||
|
struct
|
||
|
{
|
||
|
uint8_t byte0;
|
||
|
uint8_t byte1;
|
||
|
uint8_t byte2;
|
||
|
uint8_t byte3;
|
||
|
} byte;
|
||
|
} x;
|
||
|
|
||
|
x.byte.byte0 = eeprom_read_byte ((const uint8_t *)__p);
|
||
|
x.byte.byte1 = eeprom_read_byte ((const uint8_t *)__p + 1);
|
||
|
x.byte.byte2 = eeprom_read_byte ((const uint8_t *)__p + 2);
|
||
|
x.byte.byte3 = eeprom_read_byte ((const uint8_t *)__p + 3);
|
||
|
return x.dword;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/** \ingroup avr_eeprom
|
||
|
Read a block of \a __n bytes from EEPROM address \a __src to SRAM
|
||
|
\a __dst.
|
||
|
*/
|
||
|
static __inline__ void
|
||
|
eeprom_read_block (void *__dst, const void *__src, size_t __n)
|
||
|
{
|
||
|
#if (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) )
|
||
|
__eerd_block (__dst, __src, __n, eeprom_read_byte);
|
||
|
#else
|
||
|
/* If ATmega256x device, do not call function. */
|
||
|
char *_myDstPtr;
|
||
|
char *_mySrcPtr;
|
||
|
|
||
|
_myDstPtr = (char *)__dst;
|
||
|
_mySrcPtr = (char *)__src;
|
||
|
while (__n--)
|
||
|
{
|
||
|
//* Jul 6, 2010 modifed by Mark Sproul to work with the 2560
|
||
|
// *(char *)__dst++ = eeprom_read_byte((const uint8_t *)__src++);
|
||
|
*_myDstPtr = eeprom_read_byte((const uint8_t *)_mySrcPtr);
|
||
|
_myDstPtr++;
|
||
|
_mySrcPtr++;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/** \ingroup avr_eeprom
|
||
|
Write a byte \a __value to EEPROM address \a __p.
|
||
|
*/
|
||
|
static __inline__ void eeprom_write_byte (uint8_t *__p, uint8_t __value)
|
||
|
{
|
||
|
do {} while (!eeprom_is_ready ());
|
||
|
|
||
|
#if defined(EEPM0) && defined(EEPM1)
|
||
|
EECR = 0; /* Set programming mode: erase and write. */
|
||
|
#elif defined(EEPM0) || defined(EEPM1)
|
||
|
# warning "Unknown EECR register, eeprom_write_byte() has become outdated."
|
||
|
#endif
|
||
|
|
||
|
#if E2END <= 0xFF
|
||
|
EEARL = (unsigned)__p;
|
||
|
#else
|
||
|
EEAR = (unsigned)__p;
|
||
|
#endif
|
||
|
EEDR = __value;
|
||
|
|
||
|
__asm__ __volatile__ (
|
||
|
"/* START EEPROM WRITE CRITICAL SECTION */\n\t"
|
||
|
"in r0, %[__sreg] \n\t"
|
||
|
"cli \n\t"
|
||
|
"sbi %[__eecr], %[__eemwe] \n\t"
|
||
|
"sbi %[__eecr], %[__eewe] \n\t"
|
||
|
"out %[__sreg], r0 \n\t"
|
||
|
"/* END EEPROM WRITE CRITICAL SECTION */"
|
||
|
:
|
||
|
: [__eecr] "i" (_SFR_IO_ADDR(EECR)),
|
||
|
[__sreg] "i" (_SFR_IO_ADDR(SREG)),
|
||
|
[__eemwe] "i" (EEMWE),
|
||
|
[__eewe] "i" (EEWE)
|
||
|
: "r0"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/** \ingroup avr_eeprom
|
||
|
Write a word \a __value to EEPROM address \a __p.
|
||
|
*/
|
||
|
static __inline__ void eeprom_write_word (uint16_t *__p, uint16_t __value)
|
||
|
{
|
||
|
#if (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) )
|
||
|
__eewr_word (__p, __value, eeprom_write_byte);
|
||
|
#else
|
||
|
/* If ATmega256x device, do not call function. */
|
||
|
union
|
||
|
{
|
||
|
uint16_t word;
|
||
|
struct
|
||
|
{
|
||
|
uint8_t lo;
|
||
|
uint8_t hi;
|
||
|
} byte;
|
||
|
} x;
|
||
|
|
||
|
x.word = __value;
|
||
|
eeprom_write_byte ((uint8_t *)__p, x.byte.lo);
|
||
|
eeprom_write_byte ((uint8_t *)__p + 1, x.byte.hi);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/** \ingroup avr_eeprom
|
||
|
Write a 32-bit double word \a __value to EEPROM address \a __p.
|
||
|
*/
|
||
|
static __inline__ void eeprom_write_dword (uint32_t *__p, uint32_t __value)
|
||
|
{
|
||
|
#if (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) )
|
||
|
__eewr_dword (__p, __value, eeprom_write_byte);
|
||
|
#else
|
||
|
/* If ATmega256x device, do not call function. */
|
||
|
union
|
||
|
{
|
||
|
uint32_t dword;
|
||
|
struct
|
||
|
{
|
||
|
uint8_t byte0;
|
||
|
uint8_t byte1;
|
||
|
uint8_t byte2;
|
||
|
uint8_t byte3;
|
||
|
} byte;
|
||
|
} x;
|
||
|
|
||
|
x.dword = __value;
|
||
|
eeprom_write_byte ((uint8_t *)__p, x.byte.byte0);
|
||
|
eeprom_write_byte ((uint8_t *)__p + 1, x.byte.byte1);
|
||
|
eeprom_write_byte ((uint8_t *)__p + 2, x.byte.byte2);
|
||
|
eeprom_write_byte ((uint8_t *)__p + 3, x.byte.byte3);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/** \ingroup avr_eeprom
|
||
|
Write a block of \a __n bytes to EEPROM address \a __dst from \a __src.
|
||
|
\note The argument order is mismatch with common functions like strcpy().
|
||
|
*/
|
||
|
static __inline__ void
|
||
|
eeprom_write_block (const void *__src, void *__dst, size_t __n)
|
||
|
{
|
||
|
#if (! (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)) )
|
||
|
__eewr_block (__dst, __src, __n, eeprom_write_byte);
|
||
|
#else
|
||
|
/* If ATmega256x device, do not call function. */
|
||
|
uint8_t *_myDstPtr;
|
||
|
uint8_t *_mySrcPtr;
|
||
|
|
||
|
//* Jul 6, 2010 modifed by Mark Sproul to work with the 2560
|
||
|
_myDstPtr = (uint8_t *)__dst;
|
||
|
_mySrcPtr = (uint8_t *)__src;
|
||
|
|
||
|
while (__n--)
|
||
|
{
|
||
|
// eeprom_write_byte((uint8_t *)__dst++, *(uint8_t *)__src++);
|
||
|
eeprom_write_byte(_myDstPtr++, *_mySrcPtr++);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/** \name IAR C compatibility defines */
|
||
|
/*@{*/
|
||
|
|
||
|
/** \def _EEPUT
|
||
|
\ingroup avr_eeprom
|
||
|
Write a byte to EEPROM. Compatibility define for IAR C. */
|
||
|
#define _EEPUT(addr, val) eeprom_write_byte ((uint8_t *)(addr), (uint8_t)(val))
|
||
|
|
||
|
/** \def _EEGET
|
||
|
\ingroup avr_eeprom
|
||
|
Read a byte from EEPROM. Compatibility define for IAR C. */
|
||
|
#define _EEGET(var, addr) (var) = eeprom_read_byte ((const uint8_t *)(addr))
|
||
|
|
||
|
/*@}*/
|
||
|
|
||
|
#endif /* E2END || defined(__DOXYGEN__) */
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#endif /* !_AVR_EEPROM_H */
|