/** * \brief Function for programming data to Flash * * This function will check whether the data is greater than Flash page size. * If it is greater, it splits and writes pagewise. * * \param address address of the Flash page to be programmed * \param buffer pointer to the buffer containing data to be programmed * \param len length of the data to be programmed to Flash */ static void program_memory(uint32_t address, uint8_t *buffer, uint32_t len) { /* Check if length is greater than Flash page size */ if (len > NVMCTRL_PAGE_SIZE) { uint32_t offset = 0; while (len > NVMCTRL_PAGE_SIZE) { /* Check if it is first page of a row */ if ((address & 0xFF) == 0) { /* Erase row */ nvm_erase_row(address); } /* Write one page data to flash */ nvm_write_buffer(address, buffer + offset, NVMCTRL_PAGE_SIZE); /* Increment the address to be programmed */ address += NVMCTRL_PAGE_SIZE; /* Increment the offset of the buffer containing data */ offset += NVMCTRL_PAGE_SIZE; /* Decrement the length */ len -= NVMCTRL_PAGE_SIZE; } /* Check if there is data remaining to be programmed*/ if (len > 0) { /* Write the data to flash */ nvm_write_buffer(address, buffer + offset, len); } } else { /* Check if it is first page of a row) */ if ((address & 0xFF) == 0) { /* Erase row */ nvm_erase_row(address); } /* Write the data to flash */ nvm_write_buffer(address, buffer, len); } }
/* if not, save new thing token to memory and use it */ uint8_t thethingsio_example_write_thing_token_nvm(char * thing_token) { uint8_t token_saved = true; uint8_t writeBuffer_Temp[NVMCTRL_PAGE_SIZE] = {0, }; // erase flash memory area enum status_code status = nvm_erase_row(NVM_ADDR_THING_TOKEN_SAVED); if(status == STATUS_OK) status = nvm_erase_row(NVM_ADDR_THING_TOKEN); // rewrite flash memory area if(status == STATUS_OK) status = nvm_write_buffer(NVM_ADDR_THING_TOKEN_SAVED, &token_saved, 1); if(status == STATUS_OK) status = nvm_write_buffer(NVM_ADDR_THING_TOKEN, thing_token, THETHINGSIO_EXAMPLE_THING_TOKEN_LENGTH); // check if all the operations went through correctly if(status != STATUS_OK) printf("set thing token, write thing token error!!\r\n"); // update thingsiO read write URL uint8_t thing_token_offset = sizeof(gau8TheThingsiOHttpRWUrl) - THETHINGSIO_EXAMPLE_THING_TOKEN_LENGTH - 1; memcpy((gau8TheThingsiOHttpRWUrl+thing_token_offset),thing_token, THETHINGSIO_EXAMPLE_THING_TOKEN_LENGTH); return true; }
/*---------------------------------------------------------------------------*/ static int erase(unsigned long from, unsigned long to) { enum status_code ret; unsigned long addr; if (!opened) { return -1; } from = (from / SECTOR_SIZE) * SECTOR_SIZE; to = (to / SECTOR_SIZE) * SECTOR_SIZE; for(addr = from; addr < to; addr += SECTOR_SIZE) { TRACE("addr: %ld", addr); if ((ret = nvm_erase_row(addr)) != STATUS_OK) { WARN("nvm_erase_row error: %d", ret); return -1; } watchdog_periodic(); } while (!nvm_is_ready()); return 1; }
/** * \brief Test NVM erasing * * This test erases test row and verifies it * * \param test Current test case. */ static void run_nvm_erase_test(const struct test_case *test) { uint16_t offset = 0; volatile uint32_t test_page_address; test_page_address = TEST_PAGE_ADDR; volatile uint32_t *test_address; test_address = (uint32_t *) test_page_address; enum status_code status; /* Skip test if NVM initialization failed */ test_assert_true(test, nvm_init_success == true, "NVM initialization failed, skipping test"); /* Erase the row at test address */ status = nvm_erase_row(test_page_address); /* Validate erase operation is complete */ test_assert_true(test, status == STATUS_OK, "Erase operation not completed"); /* Validate whether the data in NVM is 0xff after erasing */ for (offset = 0; offset < ((NVMCTRL_ROW_PAGES * NVMCTRL_PAGE_SIZE) / 4); offset++) { test_assert_true(test, ((uint32_t)test_address[offset]) == 0xffffffff, "Flash erase error @ %d offset", offset); } }
/*---------------------------------------------------------------------------*/ static int write(unsigned long addr, const unsigned char * buffer, unsigned long len) { enum status_code ret; unsigned long page_addr; int copy_len, i; if (!opened) { return -1; } /* Check if the row address is valid */ if ((addr + len) > ((uint32_t)parameters.page_size * parameters.nvm_number_of_pages)) { WARN("addr is outside flash region"); return -1; } while (len > 0) { /* Determine start address of page */ page_addr = (addr / SECTOR_SIZE) * SECTOR_SIZE; /* Copy content of current page to tmp_buffer */ memcpy(tmp_buffer, (void *)page_addr, SECTOR_SIZE); /* Wait until flash is ready */ while (!nvm_is_ready()); /* Erase current page */ nvm_erase_row(page_addr); /* Determine number of bytes to copy */ copy_len = min(len, (page_addr + SECTOR_SIZE) - addr); /* Update tmp_buffer with content from buffer */ memcpy(&tmp_buffer[addr - page_addr], buffer, copy_len); /* Update buffer, len and addr */ len -= copy_len; addr += copy_len; buffer += copy_len; /* Wait until flash is ready */ while (!nvm_is_ready()); /* Write contents of tmp_buffer to flash in NVMCTRL_ROW_PAGES steps */ for (i = 0; i < NVMCTRL_ROW_PAGES; i++) { if ((ret = nvm_write_buffer(page_addr + i * PAGE_SIZE, &tmp_buffer[i * PAGE_SIZE], PAGE_SIZE)) != STATUS_OK) { WARN("nvm_write_buffer error: %d", ret); return -1; } } } /* Return OK */ return 1; }
otError utilsFlashErasePage(uint32_t aAddress) { otError error = OT_ERROR_NONE; if (nvm_erase_row(aAddress) != STATUS_OK) { error = OT_ERROR_FAILED; } return error; }
int main(void) { //! [setup_init] configure_nvm(); //! [setup_init] //! [main] //! [main_1] uint8_t page_buffer[NVMCTRL_PAGE_SIZE]; //! [main_1] //! [main_2] for (uint32_t i = 0; i < NVMCTRL_PAGE_SIZE; i++) { page_buffer[i] = i; } //! [main_2] //! [main_3] enum status_code error_code; //! [main_3] //! [main_4] do { error_code = nvm_erase_row( 100 * NVMCTRL_ROW_PAGES * NVMCTRL_PAGE_SIZE); } while (error_code == STATUS_BUSY); //! [main_4] //! [main_5] do { error_code = nvm_write_buffer( 100 * NVMCTRL_ROW_PAGES * NVMCTRL_PAGE_SIZE, page_buffer, NVMCTRL_PAGE_SIZE); } while (error_code == STATUS_BUSY); //! [main_5] //! [main_6] do { error_code = nvm_read_buffer( 100 * NVMCTRL_ROW_PAGES * NVMCTRL_PAGE_SIZE, page_buffer, NVMCTRL_PAGE_SIZE); } while (error_code == STATUS_BUSY); //! [main_6] //! [main] while (true) { /* Do nothing */ } }
static int samd21_flash_erase_sector(uint32_t sector_address) { struct nvm_parameters params; int rc; int i; nvm_get_parameters(¶ms); /* erase all rows in the sector */ for(i = 0; i < SAMD21_FLASH_ROWS_PER_SECTOR; i++) { uint32_t row_address = sector_address + i*SAMD21_FLASH_PAGES_PER_ROW*params.page_size; rc = nvm_erase_row(row_address); if(rc != STATUS_OK) { return -1; } } return 0; }
/** * \brief Updates an arbitrary section of a page with new data. * * Writes from a buffer to a given page in the NVM memory, retaining any * unmodified data already stored in the page. * * \note If manual write mode is enable, write command must be executed after * this function, otherwise the data will not write to NVM from page buffer. * * \warning This routine is unsafe if data integrity is critical; a system reset * during the update process will result in up to one row of data being * lost. If corruption must be avoided in all circumstances (including * power loss or system reset) this function should not be used. * * \param[in] destination_address Destination page address to write to * \param[in] buffer Pointer to buffer where the data to write is * stored * \param[in] offset Number of bytes to offset the data write in * the page * \param[in] length Number of bytes in the page to update * * \return Status of the attempt to update a page. * * \retval STATUS_OK Requested NVM memory page was successfully * read * \retval STATUS_BUSY NVM controller was busy when the operation * was attempted * \retval STATUS_ERR_BAD_ADDRESS The requested address was outside the * acceptable range of the NVM memory region * \retval STATUS_ERR_INVALID_ARG The supplied length and offset was invalid */ enum status_code nvm_update_buffer( const uint32_t destination_address, uint8_t *const buffer, uint16_t offset, uint16_t length) { enum status_code error_code = STATUS_OK; uint8_t row_buffer[NVMCTRL_ROW_PAGES][NVMCTRL_PAGE_SIZE]; /* Ensure the read does not overflow the page size */ if ((offset + length) > _nvm_dev.page_size) { return STATUS_ERR_INVALID_ARG; } /* Calculate the starting row address of the page to update */ uint32_t row_start_address = destination_address & ~((_nvm_dev.page_size * NVMCTRL_ROW_PAGES) - 1); /* Read in the current row contents */ for (uint32_t i = 0; i < NVMCTRL_ROW_PAGES; i++) { do { error_code = nvm_read_buffer( row_start_address + (i * _nvm_dev.page_size), row_buffer[i], _nvm_dev.page_size); } while (error_code == STATUS_BUSY); if (error_code != STATUS_OK) { return error_code; } } /* Calculate the starting page in the row that is to be updated */ uint8_t page_in_row = (destination_address % (_nvm_dev.page_size * NVMCTRL_ROW_PAGES)) / _nvm_dev.page_size; /* Update the specified bytes in the page buffer */ for (uint32_t i = 0; i < length; i++) { row_buffer[page_in_row][offset + i] = buffer[i]; } system_interrupt_enter_critical_section(); /* Erase the row */ do { error_code = nvm_erase_row(row_start_address); } while (error_code == STATUS_BUSY); if (error_code != STATUS_OK) { system_interrupt_leave_critical_section(); return error_code; } /* Write the updated row contents to the erased row */ for (uint32_t i = 0; i < NVMCTRL_ROW_PAGES; i++) { do { error_code = nvm_write_buffer( row_start_address + (i * _nvm_dev.page_size), row_buffer[i], _nvm_dev.page_size); } while (error_code == STATUS_BUSY); if (error_code != STATUS_OK) { system_interrupt_leave_critical_section(); return error_code; } } system_interrupt_leave_critical_section(); return error_code; }