/** * Test nvm_eeprom_load_byte_to_buffer(), nvm_eeprom_flush_buffer() and * nvm_eeprom_atomic_write_page() * * Test procedure: * - Erase the TEST_ATOMIC_WRITE_PAGE page. * - Load byte and then flush the buffer at one location. * - Load two more bytes at a different location. * - Write the page. * - Verify that only two bytes are written and the rest of the page is erased. * * \return STATUS_OK if test succeeded, otherwise ERR_BAD_DATA */ static status_code_t test_atomic_write(void) { nvm_eeprom_erase_page(TEST_ATOMIC_WRITE_PAGE); /* Load a dummy byte and then flush the buffer to remove the byte from * the buffer. The byte should not be written to the EEPROM. */ nvm_eeprom_load_byte_to_buffer(0, 55); nvm_eeprom_flush_buffer(); /* Load some additional bytes */ set_buffer(buffer, EEPROM_ERASED); buffer[1] = 0xaa; buffer[2] = 0x19; nvm_eeprom_load_byte_to_buffer(1, buffer[1]); nvm_eeprom_load_byte_to_buffer(2, buffer[2]); /* Erase and then write page */ nvm_eeprom_atomic_write_page(TEST_ATOMIC_WRITE_PAGE); if (is_eeprom_page_equal_to_buffer(TEST_ATOMIC_WRITE_PAGE, buffer)) { return STATUS_OK; } return ERR_BAD_DATA; }
/** * \brief Fill temporary EEPROM page buffer with value. * * This fills the the EEPROM page buffers with a given value. * If memory mapped EEPROM is enabled, this function will not work. * * \note Only the lower part of the address is used to address the buffer. * Therefore, no address parameter is needed. In the end, the data * is written to the EEPROM page given by the address parameter to the * EEPROM write page operation. * * \param value Value to copy to the page buffer. */ void nvm_eeprom_fill_buffer_with_value(uint8_t value) { uint8_t old_cmd; old_cmd = NVM.CMD; nvm_eeprom_flush_buffer(); // Wait until NVM is ready nvm_wait_until_ready(); NVM.CMD = NVM_CMD_LOAD_EEPROM_BUFFER_gc; /* Set address to zero, as only the lower bits matters. ADDR0 is * maintained inside the loop below. */ NVM.ADDR2 = 0x00; NVM.ADDR1 = 0x00; // Load multible bytes into page buffer uint8_t i; for (i = 0; i < EEPROM_PAGE_SIZE; ++i) { NVM.ADDR0 = i; NVM.DATA0 = value; } NVM.CMD = old_cmd; }
/** * \brief Write one byte to EEPROM using IO mapping. * * This function writes one byte to EEPROM using IO-mapped access. * This function will cancel all ongoing EEPROM page buffer loading * operations, if any. * * \param address EEPROM address (max EEPROM_SIZE) * \param value Byte value to write to EEPROM. */ void nvm_eeprom_write_byte(eeprom_addr_t address, uint8_t value) { uint8_t old_cmd; Assert(address <= EEPROM_SIZE); /* Flush buffer to make sure no unintentional data is written and load * the "Page Load" command into the command register. */ old_cmd = NVM.CMD; nvm_eeprom_flush_buffer(); // Wait until NVM is ready nvm_wait_until_ready(); nvm_eeprom_load_byte_to_buffer(address, value); // Set address to write to NVM.ADDR2 = 0x00; NVM.ADDR1 = (address >> 8) & 0xFF; NVM.ADDR0 = address & 0xFF; /* Issue EEPROM Atomic Write (Erase&Write) command. Load command, write * the protection signature and execute command. */ NVM.CMD = NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc; nvm_exec(); NVM.CMD = old_cmd; }
/** * \brief Test EEPROM atomic write * * This test erases test page \ref TEST_ATOMIC_WRITE_PAGE, then writes the first * byte in the page buffer before flushing it -- this byte should then not be * written to the EEPROM -- and writing different values to the two successive * bytes. An atomic write is then executed before the write of the latter two * bytes is verified. * * \param test Current test case. */ static void run_eeprom_atomic_write_test(const struct test_case *test) { uint8_t buffer[EEPROM_PAGE_SIZE]; bool success; nvm_eeprom_erase_page(TEST_ATOMIC_WRITE_PAGE); /* Load a dummy byte and then flush the buffer to remove the byte from * the buffer. The byte should not be written to the EEPROM. */ nvm_eeprom_load_byte_to_buffer(0, 55); nvm_eeprom_flush_buffer(); /* Load some additional bytes */ set_buffer(buffer, EEPROM_ERASED); buffer[1] = 0xaa; buffer[2] = 0x19; nvm_eeprom_load_byte_to_buffer(1, buffer[1]); nvm_eeprom_load_byte_to_buffer(2, buffer[2]); /* Erase and then write page */ nvm_eeprom_atomic_write_page(TEST_ATOMIC_WRITE_PAGE); success = is_eeprom_page_equal_to_buffer(TEST_ATOMIC_WRITE_PAGE, buffer); test_assert_true(test, success, "Page buffer flush, byte load or atomic write failed"); }
/** * \brief Fill temporary EEPROM page buffer with value. * * This fills the the EEPROM page buffers with a given value. * If memory mapped EEPROM is enabled, this function will not work. * * \note Only the lower part of the address is used to address the buffer. * Therefore, no address parameter is needed. In the end, the data * is written to the EEPROM page given by the address parameter to the * EEPROM write page operation. * * \param value Value to copy to the page buffer. */ void nvm_eeprom_fill_buffer_with_value(uint8_t value) { nvm_eeprom_flush_buffer(); // Wait until NVM is ready nvm_wait_until_ready(); // Load multiple bytes into page buffer uint8_t i; for (i = 0; i < EEPROM_PAGE_SIZE; ++i) { nvm_eeprom_load_byte_to_buffer(i, value); } }