mirror of
https://github.com/pengwon/epd42.git
synced 2026-03-19 09:29:49 +08:00
315 lines
9.2 KiB
C
315 lines
9.2 KiB
C
/**
|
|
* Copyright (c) 2012 - 2017, Nordic Semiconductor ASA
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* 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, except as embedded into a Nordic
|
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
|
* such product, 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. Neither the name of Nordic Semiconductor ASA nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* 4. This software, with or without modification, must only be used with a
|
|
* Nordic Semiconductor ASA integrated circuit.
|
|
*
|
|
* 5. Any software provided in binary form under this license must not be reverse
|
|
* engineered, decompiled, modified and/or disassembled.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
|
|
*
|
|
*/
|
|
|
|
#include "ble_flash.h"
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "nrf_soc.h"
|
|
#include "nordic_common.h"
|
|
#include "nrf_error.h"
|
|
#include "nrf.h"
|
|
#include "app_util.h"
|
|
|
|
|
|
static volatile bool m_radio_active = false; /**< TRUE if radio is active (or about to become active), FALSE otherwise. */
|
|
|
|
|
|
uint16_t ble_flash_crc16_compute(uint8_t * p_data, uint16_t size, uint16_t * p_crc)
|
|
{
|
|
uint16_t i;
|
|
uint16_t crc = (p_crc == NULL) ? 0xffff : *p_crc;
|
|
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
crc = (unsigned char)(crc >> 8) | (crc << 8);
|
|
crc ^= p_data[i];
|
|
crc ^= (unsigned char)(crc & 0xff) >> 4;
|
|
crc ^= (crc << 8) << 4;
|
|
crc ^= ((crc & 0xff) << 4) << 1;
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
|
|
/**@brief Function for erasing a page in flash.
|
|
*
|
|
* @param[in] p_page Pointer to first word in page to be erased.
|
|
*/
|
|
static void flash_page_erase(uint32_t * p_page)
|
|
{
|
|
// Turn on flash erase enable and wait until the NVMC is ready.
|
|
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos);
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
|
|
// Erase page.
|
|
NRF_NVMC->ERASEPAGE = (uint32_t)p_page;
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
|
|
// Turn off flash erase enable and wait until the NVMC is ready.
|
|
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing
|
|
}
|
|
}
|
|
|
|
|
|
/**@brief Function for writing one word to flash. Unprotected write, which can interfere with radio communication.
|
|
*
|
|
* @details This function DOES NOT use the m_radio_active variable, but will force the write even
|
|
* when the radio is active. To be used only from @ref ble_flash_page_write.
|
|
*
|
|
* @note Flash location to be written must have been erased previously.
|
|
*
|
|
* @param[in] p_address Pointer to flash location to be written.
|
|
* @param[in] value Value to write to flash.
|
|
*/
|
|
static void flash_word_unprotected_write(uint32_t * p_address, uint32_t value)
|
|
{
|
|
// Turn on flash write enable and wait until the NVMC is ready.
|
|
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
*p_address = value;
|
|
|
|
// Wait flash write to finish
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
|
|
// Turn off flash write enable and wait until the NVMC is ready.
|
|
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
}
|
|
|
|
|
|
/**@brief Function for writing one word to flash.
|
|
*
|
|
* @note Flash location to be written must have been erased previously.
|
|
*
|
|
* @param[in] p_address Pointer to flash location to be written.
|
|
* @param[in] value Value to write to flash.
|
|
*/
|
|
static void flash_word_write(uint32_t * p_address, uint32_t value)
|
|
{
|
|
// If radio is active, wait for it to become inactive.
|
|
while (m_radio_active)
|
|
{
|
|
// Do nothing (just wait for radio to become inactive).
|
|
(void) sd_app_evt_wait();
|
|
}
|
|
|
|
// Turn on flash write enable and wait until the NVMC is ready.
|
|
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
|
|
*p_address = value;
|
|
// Wait flash write to finish
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing.
|
|
}
|
|
// Turn off flash write enable and wait until the NVMC is ready.
|
|
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
|
|
{
|
|
// Do nothing
|
|
}
|
|
}
|
|
|
|
|
|
uint32_t ble_flash_word_write(uint32_t * p_address, uint32_t value)
|
|
{
|
|
flash_word_write(p_address, value);
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
|
|
uint32_t ble_flash_block_write(uint32_t * p_address, uint32_t * p_in_array, uint16_t word_count)
|
|
{
|
|
uint16_t i;
|
|
|
|
for (i = 0; i < word_count; i++)
|
|
{
|
|
flash_word_write(p_address, p_in_array[i]);
|
|
p_address++;
|
|
}
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
|
|
uint32_t ble_flash_page_erase(uint8_t page_num)
|
|
{
|
|
uint32_t * p_page = (uint32_t *)(BLE_FLASH_PAGE_SIZE * page_num);
|
|
flash_page_erase(p_page);
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
|
|
uint32_t ble_flash_page_write(uint8_t page_num, uint32_t * p_in_array, uint8_t word_count)
|
|
{
|
|
int i;
|
|
uint32_t * p_page;
|
|
uint32_t * p_curr_addr;
|
|
uint16_t in_data_crc;
|
|
uint16_t flash_crc;
|
|
uint32_t flash_header;
|
|
|
|
p_page = (uint32_t *)(BLE_FLASH_PAGE_SIZE * page_num);
|
|
p_curr_addr = p_page;
|
|
|
|
// Calculate CRC of the data to write.
|
|
in_data_crc = ble_flash_crc16_compute((uint8_t *)p_in_array,
|
|
word_count * sizeof(uint32_t),
|
|
NULL);
|
|
|
|
// Compare the calculated to the one in flash.
|
|
flash_header = *p_curr_addr;
|
|
flash_crc = (uint16_t)flash_header;
|
|
|
|
if (flash_crc == in_data_crc)
|
|
{
|
|
// Data is the same as the data already stored in flash, return without modifying flash.
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
// Erase flash page
|
|
flash_page_erase(p_page);
|
|
|
|
// Reserve space for magic number (for detecting if flash content is valid).
|
|
p_curr_addr++;
|
|
|
|
// Reserve space for saving word_count.
|
|
p_curr_addr++;
|
|
|
|
// Write data
|
|
for (i = 0; i < word_count; i++)
|
|
{
|
|
flash_word_unprotected_write(p_curr_addr, p_in_array[i]);
|
|
p_curr_addr++;
|
|
}
|
|
|
|
// Write number of elements.
|
|
flash_word_write(p_page + 1, (uint32_t)(word_count));
|
|
|
|
// Write magic number and CRC to indicate that flash content is valid.
|
|
flash_header = BLE_FLASH_MAGIC_NUMBER | (uint32_t)in_data_crc;
|
|
flash_word_write(p_page, flash_header);
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
|
|
uint32_t ble_flash_page_read(uint8_t page_num, uint32_t * p_out_array, uint8_t * p_word_count)
|
|
{
|
|
int byte_count;
|
|
uint32_t * p_page;
|
|
uint32_t * p_curr_addr;
|
|
uint32_t flash_header;
|
|
uint32_t calc_header;
|
|
uint16_t calc_crc;
|
|
uint32_t tmp;
|
|
|
|
p_page = (uint32_t *)(BLE_FLASH_PAGE_SIZE * page_num);
|
|
p_curr_addr = p_page;
|
|
|
|
// Check if block is valid
|
|
flash_header = *p_curr_addr;
|
|
tmp = flash_header & 0xFFFF0000;
|
|
if (tmp != BLE_FLASH_MAGIC_NUMBER)
|
|
{
|
|
*p_word_count = 0;
|
|
return NRF_ERROR_NOT_FOUND;
|
|
}
|
|
p_curr_addr++;
|
|
|
|
// Read number of elements
|
|
*p_word_count = (uint8_t)(*(p_curr_addr));
|
|
p_curr_addr++;
|
|
|
|
// Read data
|
|
byte_count = (*p_word_count) * sizeof(uint32_t);
|
|
memcpy(p_out_array, p_curr_addr, byte_count);
|
|
|
|
// Check CRC
|
|
calc_crc = ble_flash_crc16_compute((uint8_t *)p_out_array,
|
|
(*p_word_count) * sizeof(uint32_t),
|
|
NULL);
|
|
calc_header = BLE_FLASH_MAGIC_NUMBER | (uint32_t)calc_crc;
|
|
|
|
if (calc_header != flash_header)
|
|
{
|
|
return NRF_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
|
|
uint32_t ble_flash_page_addr(uint8_t page_num, uint32_t ** pp_page_addr)
|
|
{
|
|
*pp_page_addr = (uint32_t *)(BLE_FLASH_PAGE_SIZE * page_num);
|
|
return NRF_SUCCESS;
|
|
}
|
|
|
|
|
|
void ble_flash_on_radio_active_evt(bool radio_active)
|
|
{
|
|
m_radio_active = radio_active;
|
|
}
|