Files
bacnet_stack/ports/xplained/ASF/xmega/drivers/nvm/nvm.h
T
2019-10-08 23:47:53 -05:00

1094 lines
35 KiB
C

/**
* \file
*
* \brief Non Volatile Memory controller driver
*
* Copyright (c) 2010-2013 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* 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. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
*
* \asf_license_stop
*
*/
#ifndef NVM_H
#define NVM_H
#include <compiler.h>
#include <ccp.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* \defgroup nvm_group NVM driver
*
* See \ref xmega_nvm_quickstart
*
* \brief Low-level driver implementation for the AVR XMEGA Non Volatile
* Memory Controller (NVM).
*
* The XMEGA NVM controller interfaces the internal non-volatile memories
* in the XMEGA devices. Program memory, EEPROM and signature row is can be
* interfaced by the module. See the documentation of each sub-module for
* more information.
*
* \note If using GCC and the flash sub-module, remember to configure
* the boot section in the make file. More information in the sub-module
* documentation.
*
* \section xmega_nvm_quickstart_section Quick Start Guide
* See \ref xmega_nvm_quickstart
*/
/**
* \defgroup nvm_generic_group NVM driver generic module handling
* \ingroup nvm_group
* \brief Support functions for the NVM driver.
*
* These functions are helper functions for the functions of the
* \ref nvm_group "NVM driver".
*
* @{
*/
/**
* \brief Wait for any NVM access to finish.
*
* This function is blocking and waits for any NVM access to finish.
* Use this function before any NVM accesses, if you are not certain that
* any previous operations are finished yet.
*/
static inline void nvm_wait_until_ready (void)
{
do
{
// Block execution while waiting for the NVM to be ready
}
while ((NVM.STATUS & NVM_NVMBUSY_bm) == NVM_NVMBUSY_bm);
}
/**
* \brief Non-Volatile Memory Execute Command
*
* This function sets the CCP register before setting the CMDEX bit in the
* NVM.CTRLA register.
*
* \note The correct NVM command must be set in the NVM.CMD register before
* calling this function.
*/
static inline void nvm_exec (void)
{
ccp_write_io ((uint8_t *) & NVM.CTRLA, NVM_CMDEX_bm);
}
/**
* \brief Non-Volatile Memory Execute Specific Command
*
* This function sets a command in the NVM.CMD register, then performs an
* execute command by writing the CMDEX bit to the NVM.CTRLA register.
*
* \note The function saves and restores the NVM.CMD register, but if this
* function is called from an interrupt, interrupts must be disabled
* before this function is called.
*
* \param nvm_command NVM Command to execute.
*/
static inline void nvm_issue_command (NVM_CMD_t nvm_command)
{
uint8_t old_cmd;
old_cmd = NVM.CMD;
NVM.CMD = nvm_command;
ccp_write_io ((uint8_t *) & NVM.CTRLA, NVM_CMDEX_bm);
NVM.CMD = old_cmd;
}
/**
* \brief Read one byte using the LDI instruction
* \internal
*
* This function sets the specified NVM_CMD, reads one byte using at the
* specified byte address with the LPM instruction. NVM_CMD is restored after
* use.
*
* \note Interrupts should be disabled before running this function
* if program memory/NVM controller is accessed from ISRs.
*
* \param nvm_cmd NVM command to load before running LPM
* \param address Byte offset into the signature row
*/
uint8_t nvm_read_byte (uint8_t nvm_cmd, uint16_t address);
/**
* \brief Perform SPM write
* \internal
*
* This function sets the specified NVM_CMD, sets CCP and then runs the SPM
* instruction to write to flash.
*
* \note Interrupts should be disabled before running this function
* if program memory/NVM controller is accessed from ISRs.
*
* \param addr Address to perform the SPM on.
* \param nvm_cmd NVM command to use in the NVM_CMD register
*/
void nvm_common_spm (uint32_t addr, uint8_t nvm_cmd);
//! @}
/**
* \defgroup nvm_signature_group NVM driver signature handling
* \ingroup nvm_group
* \brief Handling of signature rows
*
* Functions for handling signature rows. The following is supported:
* - Reading values from production and user signature row
* - Reading device id
* - Reading device revision
* - Reading device serial
*
* \note Some of these functions are modifying the NVM.CMD register.
* If the application are using program space access in interrupts
* (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts
* needs to be disabled when running EEPROM access functions. If not,
* the program space reads will be corrupted. See documentation for
* each individual function.
* \note Do not use the functions of this module in an interrupt service
* routine (ISR), since the functions can take several milliseconds to
* complete and hence block the interrupt for several milliseconds.
* In addition the functions of this module are modifying the page buffer
* which will corrupt any ongoing EEPROM handing used outside an ISR.
* @{
*/
/**
* \brief Structure containing the device ID
*
* This structure can be used to store the device ID of a device.
*/
struct nvm_device_id
{
union
{
struct
{
uint8_t devid0;
uint8_t devid1;
uint8_t devid2;
};
uint8_t byte[3];
};
};
/**
* \brief Structure containing the device serial
*
* This structure can be used to store the serial number of a device.
*/
struct nvm_device_serial
{
union
{
struct
{
uint8_t lotnum0;
uint8_t lotnum1;
uint8_t lotnum2;
uint8_t lotnum3;
uint8_t lotnum4;
uint8_t lotnum5;
uint8_t wafnum;
uint8_t coordx0;
uint8_t coordx1;
uint8_t coordy0;
uint8_t coordy1;
};
uint8_t byte[11];
};
};
/**
* \brief Get offset of calibration bytes in the production signature row
*
* \param regname Name of register within the production signature row
* \retval Offset of register into the production signature row
*/
#if defined(__GNUC__)
# define nvm_get_production_signature_row_offset(regname) \
offsetof(NVM_PROD_SIGNATURES_t, regname)
#elif defined(__ICCAVR__)
# define nvm_get_production_signature_row_offset(regname) (regname##_offset)
#else
# error Unknown compiler
#endif
/**
* \brief Read one byte from the production signature row
*
* This function reads one byte from the production signature row of the device
* at the given address.
*
* \note This function is modifying the NVM.CMD register.
* If the application are using program space access in interrupts
* (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts
* needs to be disabled when running EEPROM access functions. If not
* the program space reads will be corrupted.
*
* \param address Byte offset into the signature row
*/
static inline uint8_t nvm_read_production_signature_row (uint8_t address)
{
return nvm_read_byte (NVM_CMD_READ_CALIB_ROW_gc, address);
}
/**
* \brief Read one byte from the user signature row
*
* This function reads one byte from the user signature row of the device
* at the given address.
*
* \note This function is modifying the NVM.CMD register.
* If the application are using program space access in interrupts
* (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts
* needs to be disabled when running EEPROM access functions. If not
* the program space reads will be corrupted.
*
* \param address Byte offset into the signature row
*/
static inline uint8_t nvm_read_user_signature_row (uint16_t address)
{
return nvm_read_byte (NVM_CMD_READ_USER_SIG_ROW_gc, address);
}
/**
* \brief Read the device id
*
* This function returns the device ID stored in the device.
*
* \retval storage Pointer to the structure where to store the device id
*/
static inline void nvm_read_device_id (struct nvm_device_id *storage)
{
storage->devid0 = MCU.DEVID0;
storage->devid1 = MCU.DEVID1;
storage->devid2 = MCU.DEVID2;
}
/**
* \brief Read the device revision
*
* This function returns the device revision stored in the device.
*
* \retval unsigned 8 bit value with the current device revision.
*/
static inline uint8_t nvm_read_device_rev (void)
{
return MCU.REVID;
}
void nvm_read_device_serial (struct nvm_device_serial *storage);
//! @}
/**
* \defgroup nvm_eeprom_group NVM driver EEPROM handling
* \ingroup nvm_group
* \brief Functions for handling internal EEPROM memory.
*
* The internal EEPROM can be used to store data that will persist after
* power is removed. This can typically be used to store calibration data,
* application state, encryption keys or other data that need to be preserved
* when power is removed.
*
* The functions in this module uses IO register access to manipulate the
* EEPROM.
*
* \note The functions in this module are modifying the NVM.CMD register.
* If the application are using program space access in interrupts
* (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts
* needs to be disabled when running EEPROM access functions. If not
* the program space reads will be corrupted.
* @{
*/
#ifndef EEPROM_PAGE_SIZE
# if XMEGA_A || XMEGA_AU || XMEGA_B || XMEGA_C || XMEGA_D || XMEGA_E
# define EEPROM_PAGE_SIZE 32
# else
# error Unknown EEPROM page size
# endif
#endif
#ifndef CONFIG_NVM_IGNORE_XMEGA_A3_D3_REVB_ERRATA
# if XMEGA_A3 || XMEGA_D3
# error This NVM driver does not support rev B of XMEGA A3/D3 devices. \
Set CONFIG_NVM_IGNORE_XMEGA_A3_D3_REVB_ERRATA to disable this message
# endif
#endif
/**
* Data type for holding eeprom memory addresses.
*/
typedef uint16_t eeprom_addr_t;
/*! \brief Enable EEPROM mapping into data space.
*
* This macro enables mapping of EEPROM into data space.
* EEPROM starts at EEPROM_START in data memory. Read access
* can be done similar to ordinary SRAM access.
*
* \note This disables IO-mapped access to EEPROM, although page erase and
* write operations still needs to be done through IO register.
*/
static inline void eeprom_enable_mapping (void)
{
#if !XMEGA_E
NVM_CTRLB = NVM_CTRLB | NVM_EEMAPEN_bm;
#endif
}
/*! \brief Disable EEPROM mapping into data space.
*
* This macro disables mapping of EEPROM into data space.
* IO mapped access is now enabled.
*/
static inline void eeprom_disable_mapping (void)
{
#if !XMEGA_E
NVM_CTRLB = NVM_CTRLB & ~NVM_EEMAPEN_bm;
#endif
}
uint8_t nvm_eeprom_read_byte (eeprom_addr_t addr);
void nvm_eeprom_write_byte (eeprom_addr_t address, uint8_t value);
void nvm_eeprom_read_buffer (eeprom_addr_t address, void *buf,
uint16_t len);
void nvm_eeprom_erase_and_write_buffer (eeprom_addr_t address,
const void *buf, uint16_t len);
void nvm_eeprom_flush_buffer (void);
void nvm_eeprom_load_byte_to_buffer (uint8_t byte_addr, uint8_t value);
void nvm_eeprom_load_page_to_buffer (const uint8_t * values);
void nvm_eeprom_atomic_write_page (uint8_t page_addr);
void nvm_eeprom_split_write_page (uint8_t page_addr);
void nvm_eeprom_fill_buffer_with_value (uint8_t value);
void nvm_eeprom_erase_bytes_in_page (uint8_t page_addr);
void nvm_eeprom_erase_page (uint8_t page_addr);
void nvm_eeprom_erase_bytes_in_all_pages (void);
void nvm_eeprom_erase_all (void);
//! @}
/**
* \defgroup nvm_flash_group NVM driver flash handling
* \ingroup nvm_group
* \brief Functions for handling internal flash memory.
*
* The internal flash memory on the XMEGA devices consists of the application
* section, the application table section and the bootloader section.
* All these sections can store program code for the MCU, but if there is
* available space, they can be used for storing other persistent data.
*
* Writing the flash memory can only be done one page at a time. It consists
* of loading the data to the internal page buffer and then running one of
* the write commands. If the page has not been erased before writing, the
* data will not be written correctly.
*
* In order to be able to write to flash memory the programming commands need
* to be run from the boot section.
* - When using IAR this is handled automatically by the linker script.
* - When using GCC this needs to be specified manually in the make files. For
* example the ATxmega128A1 has the boot section at the word address 0x10000
* the corresponding byte address of 0x20000 needs to be added to the
* config.mk makefile:
* LDFLAGS += -Wl,--section-start=.BOOT=0x20000
* See the device datasheet for the correct address for other devices.
*
* \note If using GCC and the flash sub-module, remember to configure
* the boot section in the make file.
*
* \note The functions in this module are modifying the NVM.CMD register.
* If the application are using program space access in interrupts
* (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts
* needs to be disabled when running EEPROM access functions. If not
* the program space reads will be corrupted.
* @{
*/
/**
* \brief Size of a flash page in bytes
*
* The page size in bytes taken from the toolchain header files.
*
* \note Page size is currently missing from the IAR header files, so it needs
* to be defined in the driver until it is fixed.
*/
#ifdef __DOXYGEN__
# define FLASH_SIZE
# define FLASH_PAGE_SIZE
#else
// 8K devices
# if AVR8_PART_IS_DEFINED(ATxmega8E5)
# define FLASH_SIZE (8*1024L)
# define FLASH_PAGE_SIZE (128)
// 16K devices
# elif AVR8_PART_IS_DEFINED(ATxmega16A4) | \
AVR8_PART_IS_DEFINED(ATxmega16A4U) | \
AVR8_PART_IS_DEFINED(ATxmega16D4) | \
AVR8_PART_IS_DEFINED(ATxmega16C4)
# define FLASH_SIZE (16*1024L)
# define FLASH_PAGE_SIZE (256)
# elif AVR8_PART_IS_DEFINED(ATxmega16E5)
# define FLASH_SIZE (16*1024L)
# define FLASH_PAGE_SIZE (128)
// 32K devices
# elif AVR8_PART_IS_DEFINED(ATxmega32A4) | \
AVR8_PART_IS_DEFINED(ATxmega32A4U) | \
AVR8_PART_IS_DEFINED(ATxmega32D4) | \
AVR8_PART_IS_DEFINED(ATxmega32C4)
# define FLASH_SIZE (32*1024L)
# define FLASH_PAGE_SIZE (256)
# elif AVR8_PART_IS_DEFINED(ATxmega32E5)
# define FLASH_SIZE (32*1024L)
# define FLASH_PAGE_SIZE (128)
// 64K devices
# elif AVR8_PART_IS_DEFINED(ATxmega64A1) | \
AVR8_PART_IS_DEFINED(ATxmega64A1U) | \
AVR8_PART_IS_DEFINED(ATxmega64A3) | \
AVR8_PART_IS_DEFINED(ATxmega64A3U) | \
AVR8_PART_IS_DEFINED(ATxmega64A4U) | \
AVR8_PART_IS_DEFINED(ATxmega64B1) | \
AVR8_PART_IS_DEFINED(ATxmega64B3) | \
AVR8_PART_IS_DEFINED(ATxmega64C3) | \
AVR8_PART_IS_DEFINED(ATxmega64D3) | \
AVR8_PART_IS_DEFINED(ATxmega64D4)
# define FLASH_SIZE (64*1024L)
# define FLASH_PAGE_SIZE (256)
// 128K devices
# elif AVR8_PART_IS_DEFINED(ATxmega128A1) | \
AVR8_PART_IS_DEFINED(ATxmega128A1U) | \
AVR8_PART_IS_DEFINED(ATxmega128A3) | \
AVR8_PART_IS_DEFINED(ATxmega128A3U) | \
AVR8_PART_IS_DEFINED(ATxmega128C3) | \
AVR8_PART_IS_DEFINED(ATxmega128D3) | \
AVR8_PART_IS_DEFINED(ATxmega128D4)
# define FLASH_SIZE (128*1024L)
# define FLASH_PAGE_SIZE (512)
# elif AVR8_PART_IS_DEFINED(ATxmega128A4U) | \
AVR8_PART_IS_DEFINED(ATxmega128B1) | \
AVR8_PART_IS_DEFINED(ATxmega128B3)
# define FLASH_SIZE (128*1024L)
# define FLASH_PAGE_SIZE (256)
// 192K devices
# elif AVR8_PART_IS_DEFINED(ATxmega192A3U) | \
AVR8_PART_IS_DEFINED(ATxmega192C3) | \
AVR8_PART_IS_DEFINED(ATxmega192D3)
# define FLASH_SIZE (192*1024L)
# define FLASH_PAGE_SIZE (512)
// 256K devices
# elif AVR8_PART_IS_DEFINED(ATxmega256A3) | \
AVR8_PART_IS_DEFINED(ATxmega256A3U) | \
AVR8_PART_IS_DEFINED(ATxmega256A3B) | \
AVR8_PART_IS_DEFINED(ATxmega256A3BU) | \
AVR8_PART_IS_DEFINED(ATxmega256C3) | \
AVR8_PART_IS_DEFINED(ATxmega256D3)
# define FLASH_SIZE (256*1024L)
# define FLASH_PAGE_SIZE (512)
// 384K devices
# elif AVR8_PART_IS_DEFINED(ATxmega384C3)
# define FLASH_SIZE (384*1024L)
# define FLASH_PAGE_SIZE (512)
# elif AVR8_PART_IS_DEFINED(ATxmega384D3)
# define FLASH_SIZE (384*1024L)
# define FLASH_PAGE_SIZE (512)
# else
# error Flash page size needs to be defined.
# endif
#endif
/**
* Data type for holding flash memory addresses.
*
*/
#if (FLASH_SIZE >= 0x10000UL) // Note: Xmega with 64KB of flash have 4KB boot flash
typedef uint32_t flash_addr_t;
#else
typedef uint16_t flash_addr_t;
#endif
/**
* Flash pointer type to use for accessing flash memory with IAR
*/
#if (FLASH_SIZE >= 0x10000UL) // Note: Xmega with 64KB of flash have 4KB boot flash
# define IAR_FLASH_PTR __farflash
#else
# define IAR_FLASH_PTR __flash
#endif
/**
* \brief Load byte from flash memory
*
* Load one word of flash using byte addressing. IAR has __flash pointers
* and GCC have pgm_read_byte_xx functions which load data from flash memory.
* This function used for compatibility between the compilers.
*
* \param addr Byte address to load
* \return Byte from program memory
*/
static inline uint8_t nvm_flash_read_byte (flash_addr_t addr)
{
#if defined(__GNUC__)
return pgm_read_byte_far (addr);
#elif defined(__ICCAVR__)
uint8_t IAR_FLASH_PTR *flashptr = (uint8_t IAR_FLASH_PTR *) addr;
return *flashptr;
#else
# error Unknown compiler
#endif
}
/**
* \brief Load word from flash memory
*
* Load one word of flash using byte addressing. IAR has __flash pointers
* and GCC have pgm_read_byte_xx functions which load data from flash memory.
* This function used for compatibility between the compilers.
*
* \param addr Byte address to load (last bit is ignored)
* \return Word from program memory
*/
static inline uint16_t nvm_flash_read_word (flash_addr_t addr)
{
#if defined(__GNUC__)
return pgm_read_word_far (addr);
#elif defined(__ICCAVR__)
uint16_t IAR_FLASH_PTR *flashptr = (uint16_t IAR_FLASH_PTR *) addr;
return *flashptr;
#endif
}
/**
* \brief Flush flash page buffer
*
* Clear the NVM controller page buffer for flash. This needs to be called
* before using \ref nvm_flash_load_word_to_buffer if it has not already been
* cleared.
*
*/
static inline void nvm_flash_flush_buffer (void)
{
nvm_wait_until_ready ();
nvm_common_spm (0, NVM_CMD_ERASE_FLASH_BUFFER_gc);
}
/**
* \brief Load word into flash page buffer
*
* Clear the NVM controller page buffer for flash. This needs to be called
* before using \ref nvm_flash_load_word_to_buffer if it has not already been
* cleared.
*
* \param word_addr Address to store data. The upper bits beyond the page size
* is ignored. \ref FLASH_PAGE_SIZE
* \param data Data word to load into the page buffer
*/
void nvm_flash_load_word_to_buffer (uint32_t word_addr, uint16_t data);
/**
* \brief Erase entire application section
*
* Erase all of the application section.
*/
static inline void nvm_flash_erase_app (void)
{
nvm_wait_until_ready ();
nvm_common_spm (0, NVM_CMD_ERASE_APP_gc);
}
/**
* \brief Erase a page within the application section
*
* Erase one page within the application section
*
* \param page_addr Byte address to the page to delete
*/
static inline void nvm_flash_erase_app_page (flash_addr_t page_addr)
{
nvm_wait_until_ready ();
nvm_common_spm (page_addr, NVM_CMD_ERASE_APP_PAGE_gc);
}
/**
* \brief Write a page within the application section
*
* Write a page within the application section with the data stored in the
* page buffer. The page needs to be erased before the write to avoid
* corruption of the data written.
*
* \param page_addr Byte address to the page to delete
*/
static inline void nvm_flash_split_write_app_page (flash_addr_t page_addr)
{
nvm_wait_until_ready ();
nvm_common_spm (page_addr, NVM_CMD_WRITE_APP_PAGE_gc);
}
/**
* \brief Erase and write a page within the application section
*
* Erase and the write a page within the application section with the data
* stored in the page buffer. Erase and write is done in an atomic operation.
*
* \param page_addr Byte address to the page to delete
*/
static inline void nvm_flash_atomic_write_app_page (flash_addr_t page_addr)
{
nvm_wait_until_ready ();
nvm_common_spm (page_addr, NVM_CMD_ERASE_WRITE_APP_PAGE_gc);
}
void nvm_issue_flash_range_crc (flash_addr_t start_addr,
flash_addr_t end_addr);
void nvm_flash_read_buffer (flash_addr_t address, void *buf, uint16_t len);
void nvm_flash_erase_and_write_buffer (flash_addr_t address,
const void *buf, uint16_t len,
bool b_blank_check);
/**
* \brief Erase a page within the boot section
*
* Erase one page within the boot section
*
* \param page_addr Byte address to the page to delete
*/
static inline void nvm_flash_erase_boot_page (flash_addr_t page_addr)
{
nvm_wait_until_ready ();
nvm_common_spm (page_addr, NVM_CMD_ERASE_BOOT_PAGE_gc);
}
/**
* \brief Write a page within the boot section
*
* Write a page within the boot section with the data stored in the
* page buffer. The page needs to be erased before the write to avoid
* corruption of the data written.
*
* \param page_addr Byte address to the page to delete
*/
static inline void nvm_flash_split_write_boot_page (flash_addr_t page_addr)
{
nvm_wait_until_ready ();
nvm_common_spm (page_addr, NVM_CMD_WRITE_BOOT_PAGE_gc);
}
/**
* \brief Erase and write a page within the boot section
*
* Erase and the write a page within the boot section with the data
* stored in the page buffer. Erase and write is done in an atomic operation.
*
* \param page_addr Byte address to the page to delete
*/
static inline void nvm_flash_atomic_write_boot_page (flash_addr_t page_addr)
{
nvm_wait_until_ready ();
nvm_common_spm (page_addr, NVM_CMD_ERASE_WRITE_BOOT_PAGE_gc);
}
void nvm_user_sig_read_buffer (flash_addr_t address, void *buf,
uint16_t len);
void nvm_user_sig_write_buffer (flash_addr_t address, const void *buf,
uint16_t len, bool b_blank_check);
/**
* \brief Erase the user calibration section page
*
* Erase the user calibration section page. There is only one page, so no
* parameters are needed.
*/
static inline void nvm_flash_erase_user_section (void)
{
nvm_wait_until_ready ();
nvm_common_spm (0, NVM_CMD_ERASE_USER_SIG_ROW_gc);
}
/**
* \brief Write the user calibration section page
*
* Write a the user calibration section page with the data stored in the
* page buffer. The page needs to be erased before the write to avoid
* corruption of the data written. There is only one page, so no
* parameters are needed.
*/
static inline void nvm_flash_write_user_page (void)
{
nvm_wait_until_ready ();
nvm_common_spm (0, NVM_CMD_WRITE_USER_SIG_ROW_gc);
}
//! @}
/**
* \defgroup nvm_fuse_lock_group NVM driver fuse and lock bits handling
* \ingroup nvm_group
* \brief Functions for reading fuses and writing lock bits.
*
* The Fuses are used to set important system functions and can only be written
* from an external programming interface. The application software can read
* the fuses. The fuses are used to configure reset sources such as Brown-out
* Detector and Watchdog, Start-up configuration, JTAG enable and JTAG user ID.
*
* The Lock bits are used to set protection level on the different flash
* sections. They are used to block read and/or write on the different flash
* sections. Lock bits can be written from en external programmer and from the
* application software to set a more strict protection level, but not to set a
* less strict protection level. Chip erase is the only way to erase the lock
* bits. The lock bits are erased after the rest of the flash memory is erased.
* An unprogrammed fuse or lock bit will have the value one, while a programmed
* fuse or lock bit will have the value zero.
* Both fuses and lock bits are reprogrammable like the Flash Program memory.
*
* \note The functions in this module are modifying the NVM.CMD register.
* If the application are using program space access in interrupts
* (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts
* needs to be disabled when running EEPROM access functions. If not
* the program space reads will be corrupted.
* @{
*/
// The different fuse bytes
enum fuse_byte_t
{
FUSEBYTE0 = 0,
FUSEBYTE1 = 1,
FUSEBYTE2 = 2,
FUSEBYTE3 = 3, // not used on current devices
FUSEBYTE4 = 4,
FUSEBYTE5 = 5,
};
uint8_t nvm_fuses_read (enum fuse_byte_t fuse);
/**
* \brief Program the lock bits.
*
* Program the lock bits to the given values. Lock bits can only be programmed
* to a more secure setting than previously programmed. To clear lock bits, a
* flash erase has to be issued.
*
* \param blbb_lock Boot loader section lock bits to program
* \param blba_lock Application section lock bits to program
* \param blbat_lock Application table section lock bits to program
* \param lb_lock Flash/eeprom lock bits to program
*/
static inline void nvm_lock_bits_write (enum NVM_BLBB_enum blbb_lock,
enum NVM_BLBA_enum blba_lock,
enum NVM_BLBAT_enum blbat_lock,
enum NVM_LB_enum lb_lock)
{
nvm_wait_until_ready ();
NVM.DATA0 =
(uint8_t) blbb_lock | (uint8_t) blba_lock | (uint8_t) blbat_lock |
(uint8_t) lb_lock;
nvm_issue_command (NVM_CMD_WRITE_LOCK_BITS_gc);
}
/**
* \brief Program the BLBB lock bits.
*
* Program the lock bits for the boot loader section (BLBB). Other lock bits
* (BLBA, BLBAT and LB) are not altered (ie. programmed to NOLOCK).
*
* \param blbb_lock Boot loader section lock bits to program
*/
static inline void nvm_blbb_lock_bits_write (enum NVM_BLBB_enum blbb_lock)
{
nvm_lock_bits_write (blbb_lock, NVM_BLBA_NOLOCK_gc, NVM_BLBAT_NOLOCK_gc,
NVM_LB_NOLOCK_gc);
}
/**
* \brief Program the BLBA lock bits.
*
* Program the lock bits for the application section (BLBA). Other lock bits
* (BLBB, BLBAT and LB) are not altered (ie. programmed to NOLOCK).
*
* \param blba_lock Application section lock bits to program
*/
static inline void nvm_blba_lock_bits_write (enum NVM_BLBA_enum blba_lock)
{
nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, blba_lock, NVM_BLBAT_NOLOCK_gc,
NVM_LB_NOLOCK_gc);
}
/**
* \brief Program the BLBAT lock bits.
*
* Program the lock bits for the application table section (BLBAT). Other lock
* bits (BLBB, BLBA and LB) are not altered (ie. programmed to NOLOCK).
*
* \param blbat_lock Application table section lock bits to program
*/
static inline void nvm_blbat_lock_bits_write (enum NVM_BLBAT_enum
blbat_lock)
{
nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, NVM_BLBA_NOLOCK_gc, blbat_lock,
NVM_LB_NOLOCK_gc);
}
/**
* \brief Program the LB lock bits.
*
* Program the lock bits for the flash and eeprom (LB). Other lock bits
* (BLBB, BLBA and BLBAT) are not altered (ie. programmed to NOLOCK).
*
* \param lb_lock Flash/eeprom lock bits to program
*/
static inline void nvm_lb_lock_bits_write (enum NVM_LB_enum lb_lock)
{
nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, NVM_BLBA_NOLOCK_gc,
NVM_BLBAT_NOLOCK_gc, lb_lock);
}
//! @}
/**
* \page xmega_nvm_quickstart Quick Start Guide for the XMEGA NVM Driver
*
* This is the quick start guide for the \ref nvm_group "NVM Driver", with
* step-by-step instructions on how to configure and use the driver for
* specific use cases.
*
* The section described below can be compiled into e.g. the main application
* loop or any other function that will need to interface non-volatile memory.
*
* \section xmega_nvm_quickstart_basic Basic usage of the NVM driver
* This section will present three use cases of the NVM driver. The first will
* write a page to EEPROM and verify that it has been written, the second will
* access the BOD-level fuse to verify that the level is correctly set, and the
* third will read a chunk from the user signature row.
*
* \section xmega_nvm_quickstart_eeprom_case Use case 1: EEPROM
*
* The NVM driver has functions for interfacing many types of non-volatile
* memory, including flash, EEPROM, fuses and lock bits. The example code
* below will write a page to the internal EEPROM, and read it back to verify,
* using memory mapped I/O.
*
* \section xmega_nvm_quickstart_eeprom_case_setup_steps Setup steps
* There are no setup steps required for this use case.
*
* \subsection nvm_quickstart_eeprom_case_example_code Example code
*
* \code
* #define EXAMPLE_PAGE 2
* #define EXAMPLE_ADDR EXAMPLE_PAGE * EEPROM_PAGE_SIZE
*
* uint8_t write_page[EEPROM_PAGE_SIZE];
* uint8_t read_page[EEPROM_PAGE_SIZE];
*
* fill_page_with_known_data(write_page);
* fill_page_with_zeroes(read_page);
*
* nvm_eeprom_load_page_to_buffer(write_page);
* nvm_eeprom_atomic_write_page(EXAMPLE_PAGE);
*
* nvm_eeprom_read_buffer(EXAMPLE_ADDR,
* read_page, EEPROM_PAGE_SIZE);
*
* check_if_pages_are_equal(write_page, read_page);
* \endcode
*
* \subsection nvm_quickstart_eeprom_case_workflow Workflow
*
* -# We define where we would like to store our data, and we arbitrarily
* choose page 2 of EEPROM:
* - \code
* #define EXAMPLE_PAGE 2
* #define EXAMPLE_ADDR EXAMPLE_PAGE * EEPROM_PAGE_SIZE
* \endcode
* -# Define two tables, one which contains the data which we will write,
* and one which we will read the data into:
* - \code
* uint8_t write_page[EEPROM_PAGE_SIZE];
* uint8_t read_page[EEPROM_PAGE_SIZE];
* \endcode
* -# Fill the tables with our data, and zero out the read table:
* - \code
* fill_page_with_known_data(write_page);
* fill_page_with_zeroes(read_page);
* \endcode
* - \note These functions are undeclared, you should replace them with
* your own appropriate functions.
* -# We load our page into a temporary EEPROM page buffer:
* - \code
* nvm_eeprom_load_page_to_buffer(write_page);
* \endcode
* - \attention The function used above will not work if memory mapping
* is enabled.
* -# Do an atomic write of the page from buffer into the specified page:
* - \code
* nvm_eeprom_atomic_write_page(EXAMPLE_PAGE);
* \endcode
* - \note The function \ref nvm_eeprom_atomic_write_page() erases the
* page before writing the new one. For non-atomic (split)
* writing without deleting, see \ref nvm_eeprom_split_write_page()
* -# Read the page back into our read_page[] table:
* - \code
* nvm_eeprom_read_buffer(EXAMPLE_ADDR,
* read_page, EEPROM_PAGE_SIZE);
* \endcode
* -# Verify that the page is equal to the one that was written earlier:
* - \code
* check_if_pages_are_equal(write_page, read_page);
* \endcode
* - \note This function is not declared, you should replace it with your
* own appropriate function.
*
* \section xmega_nvm_quickstart_fuse_case Use case 2: Fuses
*
* The NVM driver has functions for reading fuses.
* See \ref nvm_fuse_lock_group.
*
* We would like to check whether the Brown-out Detection level is set to
* 2.1V. This is set by programming the fuses when the chip is connected
* to a suitable programmer. The fuse is a part of FUSEBYTE5. If the BODLVL
* is correct, we turn on LED0.
*
* \section xmega_nvm_quickstart_fuse_case_setup_steps Setup steps
* There are no setup steps required for this use case.
*
* \subsection nvm_quickstart_fuse_case_example_code Example code
* \code
* uint8_t fuse_value;
* fuse_value = nvm_fuses_read(FUSEBYTE5);
*
* if ((fuse_value & NVM_FUSES_BODLVL_gm) == BODLVL_2V1_gc) {
* gpio_set_pin_low(LED0_GPIO);
* }
* \endcode
*
* \subsection nvm_quickstart_fuse_case_workflow Workflow
*
* -# Create a variable to store the fuse contents:
* - \code
* uint8_t fuse_value;
* \endcode
* -# The fuse value we are interested in, BODLVL, is stored in FUSEBYTE5.
* We call the function \ref nvm_fuses_read() to read the fuse into our
* variable:
* - \code
* fuse_value = nvm_fuses_read(FUSEBYTE5);
* \endcode
* -# This ends the reading portion, but we would like to see whether the
* BOD-level is correct, and if it is, light up an LED:
* - \code
* if ((fuse_value & NVM_FUSES_BODLVL_gm) == BODLVL_2V1_gc) {
* gpio_set_pin_low(LED0_GPIO);
* }
* \endcode
*
* \section xmega_nvm_quickstart_signature_case Use case 3: Signature row
*
* The NVM driver has functions for reading the signature row of the device.
* Here we will simply read 16 bytes from the user signature row, assuming
* we need what is stored there.
*
* \section xmega_nvm_quickstart_signature_row_setup_steps Setup steps
* There are no setup steps required for this use case.
*
* \subsection xmega_nvm_quickstart_signature_row_example_code Example code
*
* \code
* #define START_ADDR 0x10
* #define DATA_LENGTH 16
*
* uint8_t values[LENGTH];
* uint8_t i;
*
* for (i = 0; i < DATA_LENGTH; i++) {
* values[i] = nvm_read_user_signature_row(START_ADDR + i);
* }
* \endcode
*
* \subsection nvm_quickstart_signature_case_workflow Workflow
*
* -# Define starting address and length of data segment, and create
* variables needed to store and process the data:
* - \code
* #define START_ADDR 0x10
* #define DATA_LENGTH 16
*
* uint8_t values[LENGTH];
* uint8_t i;
* \endcode
* -# Iterate through the user signature row, and store our desired data:
* - \code
* for (i = 0; i < DATA_LENGTH; i++) {
* values[i] = nvm_read_user_signature_row(START_ADDR + i);
* }
* \endcode
*
*/
#ifdef __cplusplus
}
#endif
#endif /* NVM_H */