mirror of
https://github.com/grblHAL/core.git
synced 2026-03-23 11:32:44 +08:00
Added option to homing enable setting ($22) for per axis homing feedrates. !! Backup and restore settings over an update is recommended since all settings will be reset to default. Any odometer data will also be lost.
469 lines
16 KiB
C
469 lines
16 KiB
C
/*
|
|
nvs_buffer.c - RAM based non-volatile storage buffer/emulation
|
|
|
|
Part of grblHAL
|
|
|
|
Copyright (c) 2017-2024 Terje Io
|
|
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
|
|
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
|
|
|
grblHAL is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
grblHAL is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License.
|
|
along with grblHAL. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
//
|
|
// Can be used by MCUs with no nonvolatile storage options, be sure to allocate enough heap memory before use
|
|
//
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#include "hal.h"
|
|
#include "nvs_buffer.h"
|
|
#include "protocol.h"
|
|
#include "settings.h"
|
|
#include "gcode.h"
|
|
#include "crc.h"
|
|
#include "nvs.h"
|
|
|
|
static uint8_t *nvsbuffer = NULL;
|
|
static nvs_io_t physical_nvs;
|
|
static bool dirty;
|
|
uint32_t nvs_size_max = NVS_SIZE;
|
|
settings_dirty_t settings_dirty;
|
|
|
|
typedef struct {
|
|
uint16_t addr;
|
|
uint8_t type;
|
|
uint8_t offset;
|
|
} emap_t;
|
|
|
|
#define NVS_GROUP_GLOBAL 0
|
|
#define NVS_GROUP_TOOLS 1
|
|
#define NVS_GROUP_PARAMETERS 2
|
|
#define NVS_GROUP_STARTUP 3
|
|
#define NVS_GROUP_BUILD 4
|
|
|
|
#define PARAMETER_ADDR(n) (NVS_ADDR_PARAMETERS + n * (sizeof(coord_data_t) + NVS_CRC_BYTES))
|
|
#define STARTLINE_ADDR(n) (NVS_ADDR_STARTUP_BLOCK + n * (sizeof(stored_line_t) + NVS_CRC_BYTES))
|
|
#if N_TOOLS
|
|
#define TOOL_ADDR(n) (NVS_ADDR_TOOL_TABLE + n * (sizeof(tool_data_t) + NVS_CRC_BYTES))
|
|
#endif
|
|
|
|
static const emap_t target[] = {
|
|
{NVS_ADDR_GLOBAL, NVS_GROUP_GLOBAL, 0},
|
|
|
|
{PARAMETER_ADDR(0), NVS_GROUP_PARAMETERS, 0},
|
|
{PARAMETER_ADDR(1), NVS_GROUP_PARAMETERS, 1},
|
|
{PARAMETER_ADDR(2), NVS_GROUP_PARAMETERS, 2},
|
|
{PARAMETER_ADDR(3), NVS_GROUP_PARAMETERS, 3},
|
|
{PARAMETER_ADDR(4), NVS_GROUP_PARAMETERS, 4},
|
|
{PARAMETER_ADDR(5), NVS_GROUP_PARAMETERS, 5},
|
|
{PARAMETER_ADDR(6), NVS_GROUP_PARAMETERS, 6},
|
|
{PARAMETER_ADDR(7), NVS_GROUP_PARAMETERS, 7},
|
|
{PARAMETER_ADDR(8), NVS_GROUP_PARAMETERS, 8},
|
|
{PARAMETER_ADDR(9), NVS_GROUP_PARAMETERS, 9},
|
|
{PARAMETER_ADDR(10), NVS_GROUP_PARAMETERS, 10},
|
|
{PARAMETER_ADDR(11), NVS_GROUP_PARAMETERS, 11},
|
|
{STARTLINE_ADDR(0), NVS_GROUP_STARTUP, 0},
|
|
{STARTLINE_ADDR(1), NVS_GROUP_STARTUP, 1},
|
|
#if N_STARTUP_LINE > 2
|
|
#error Increase number of startup line entries!
|
|
#endif
|
|
{NVS_ADDR_BUILD_INFO, NVS_GROUP_BUILD, 0},
|
|
#if N_TOOLS
|
|
{TOOL_ADDR(0), NVS_GROUP_TOOLS, 0},
|
|
{TOOL_ADDR(1), NVS_GROUP_TOOLS, 1},
|
|
{TOOL_ADDR(2), NVS_GROUP_TOOLS, 2},
|
|
{TOOL_ADDR(3), NVS_GROUP_TOOLS, 3},
|
|
{TOOL_ADDR(4), NVS_GROUP_TOOLS, 4},
|
|
{TOOL_ADDR(5), NVS_GROUP_TOOLS, 5},
|
|
{TOOL_ADDR(6), NVS_GROUP_TOOLS, 6},
|
|
{TOOL_ADDR(7), NVS_GROUP_TOOLS, 7},
|
|
#if N_TOOLS > 8
|
|
{TOOL_ADDR(8), NVS_GROUP_TOOLS, 8},
|
|
{TOOL_ADDR(9), NVS_GROUP_TOOLS, 9},
|
|
{TOOL_ADDR(10), NVS_GROUP_TOOLS, 10},
|
|
{TOOL_ADDR(11), NVS_GROUP_TOOLS, 11},
|
|
{TOOL_ADDR(12), NVS_GROUP_TOOLS, 12},
|
|
{TOOL_ADDR(13), NVS_GROUP_TOOLS, 13},
|
|
{TOOL_ADDR(14), NVS_GROUP_TOOLS, 14},
|
|
{TOOL_ADDR(15), NVS_GROUP_TOOLS, 15},
|
|
#endif
|
|
#if N_TOOLS > 16
|
|
{TOOL_ADDR(16), NVS_GROUP_TOOLS, 16},
|
|
{TOOL_ADDR(17), NVS_GROUP_TOOLS, 17},
|
|
{TOOL_ADDR(18), NVS_GROUP_TOOLS, 18},
|
|
{TOOL_ADDR(19), NVS_GROUP_TOOLS, 19},
|
|
{TOOL_ADDR(20), NVS_GROUP_TOOLS, 20},
|
|
{TOOL_ADDR(21), NVS_GROUP_TOOLS, 21},
|
|
{TOOL_ADDR(22), NVS_GROUP_TOOLS, 22},
|
|
{TOOL_ADDR(23), NVS_GROUP_TOOLS, 23},
|
|
{TOOL_ADDR(24), NVS_GROUP_TOOLS, 24},
|
|
{TOOL_ADDR(25), NVS_GROUP_TOOLS, 25},
|
|
{TOOL_ADDR(26), NVS_GROUP_TOOLS, 26},
|
|
{TOOL_ADDR(27), NVS_GROUP_TOOLS, 27},
|
|
{TOOL_ADDR(28), NVS_GROUP_TOOLS, 28},
|
|
{TOOL_ADDR(29), NVS_GROUP_TOOLS, 29},
|
|
{TOOL_ADDR(30), NVS_GROUP_TOOLS, 30},
|
|
{TOOL_ADDR(31), NVS_GROUP_TOOLS, 31},
|
|
#endif
|
|
#endif
|
|
{0, 0, 0} // list termination - do not remove
|
|
};
|
|
|
|
inline static uint8_t ram_get_byte (uint32_t addr)
|
|
{
|
|
return nvsbuffer[addr];
|
|
}
|
|
|
|
inline static void ram_put_byte (uint32_t addr, uint8_t new_value)
|
|
{
|
|
if(addr == 0)
|
|
settings_dirty.version = true;
|
|
dirty = dirty || nvsbuffer[addr] != new_value || addr == 0;
|
|
nvsbuffer[addr] = new_value;
|
|
}
|
|
|
|
static nvs_transfer_result_t memcpy_to_ram (uint32_t destination, uint8_t *source, uint32_t size, bool with_checksum)
|
|
{
|
|
if(hal.nvs.driver_area.address && destination > hal.nvs.driver_area.address + hal.nvs.driver_area.size)
|
|
return physical_nvs.memcpy_to_nvs(destination, source, size, with_checksum);
|
|
|
|
uint32_t dest = destination;
|
|
uint16_t checksum = with_checksum ? calc_checksum(source, size) : 0;
|
|
|
|
dirty = false;
|
|
|
|
for(; size > 0; size--)
|
|
ram_put_byte(dest++, *(source++));
|
|
|
|
if(with_checksum) {
|
|
ram_put_byte(dest, checksum & 0xFF);
|
|
#if NVS_CRC_BYTES > 1
|
|
ram_put_byte(++dest, checksum >> 8);
|
|
#endif
|
|
}
|
|
|
|
if(settings_dirty.version || source == hal.nvs.driver_area.mem_address)
|
|
dirty = true;
|
|
|
|
if(dirty && physical_nvs.type != NVS_None) {
|
|
|
|
uint8_t idx = 0;
|
|
|
|
settings_dirty.is_dirty = true;
|
|
|
|
if(hal.nvs.driver_area.address && destination >= hal.nvs.driver_area.address)
|
|
settings_dirty.driver_settings = true;
|
|
|
|
else {
|
|
|
|
do {
|
|
if(target[idx].addr == destination)
|
|
break;
|
|
} while(target[++idx].addr);
|
|
|
|
if(target[idx].addr) switch(target[idx].type) {
|
|
|
|
case NVS_GROUP_GLOBAL:
|
|
settings_dirty.global_settings = true;
|
|
break;
|
|
#if N_TOOLS
|
|
case NVS_GROUP_TOOLS:
|
|
settings_dirty.tool_data |= (1 << target[idx].offset);
|
|
break;
|
|
#endif
|
|
case NVS_GROUP_PARAMETERS:
|
|
settings_dirty.coord_data |= (1 << target[idx].offset);
|
|
break;
|
|
|
|
case NVS_GROUP_STARTUP:
|
|
settings_dirty.startup_lines |= (1 << target[idx].offset);
|
|
break;
|
|
|
|
case NVS_GROUP_BUILD:
|
|
settings_dirty.build_info = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NVS_TransferResult_OK;
|
|
}
|
|
|
|
static nvs_transfer_result_t memcpy_from_ram (uint8_t *destination, uint32_t source, uint32_t size, bool with_checksum)
|
|
{
|
|
if(hal.nvs.driver_area.address && source > hal.nvs.driver_area.address + hal.nvs.driver_area.size)
|
|
return physical_nvs.memcpy_from_nvs(destination, source, size, with_checksum);
|
|
|
|
uint16_t checksum = with_checksum ? calc_checksum(&nvsbuffer[source], size) : 0;
|
|
|
|
for(; size > 0; size--)
|
|
*(destination++) = ram_get_byte(source++);
|
|
|
|
#if NVS_CRC_BYTES == 1
|
|
return with_checksum ? (checksum == ram_get_byte(source) ? NVS_TransferResult_OK : NVS_TransferResult_Failed) : NVS_TransferResult_OK;
|
|
#else
|
|
return with_checksum ? (checksum == (ram_get_byte(source) | (ram_get_byte(source + 1) << 8)) ? NVS_TransferResult_OK : NVS_TransferResult_Failed) : NVS_TransferResult_OK;
|
|
#endif
|
|
}
|
|
|
|
// Try to allocate RAM from heap for buffer/emulation.
|
|
bool nvs_buffer_alloc (void)
|
|
{
|
|
static uint32_t nvs_size = NVS_SIZE;
|
|
|
|
if(hal.nvs.size_max > nvs_size) {
|
|
nvs_size_max = min(4096, hal.nvs.size_max); // Limit to 4K for now
|
|
if(nvsbuffer)
|
|
free(nvsbuffer);
|
|
}
|
|
|
|
assert(nvs_size_max >= GRBL_NVS_SIZE);
|
|
|
|
if((nvsbuffer = malloc(nvs_size_max))) {
|
|
nvs_size = nvs_size_max;
|
|
memset(nvsbuffer, 0xFF, nvs_size_max);
|
|
}
|
|
|
|
return nvsbuffer != NULL;
|
|
}
|
|
|
|
void nvs_buffer_free (void)
|
|
{
|
|
if(nvsbuffer) {
|
|
nvs_buffer_sync_physical();
|
|
free(nvsbuffer);
|
|
}
|
|
}
|
|
//
|
|
// Switch over to RAM based copy.
|
|
// Changes to RAM based copy will be written to physical storage when grblHAL is in IDLE state.
|
|
bool nvs_buffer_init (void)
|
|
{
|
|
hal.nvs.size = ((hal.nvs.size - 1) | 0x03) + 1; // Ensure NVS area ends on a word boundary
|
|
|
|
if(nvsbuffer) {
|
|
|
|
memcpy(&physical_nvs, &hal.nvs, sizeof(nvs_io_t)); // save pointers to physical storage handler functions
|
|
|
|
// Copy physical storage content to RAM when available
|
|
if(physical_nvs.type == NVS_Flash)
|
|
physical_nvs.memcpy_from_flash(nvsbuffer);
|
|
else if(physical_nvs.type != NVS_None)
|
|
physical_nvs.memcpy_from_nvs(nvsbuffer, 0, GRBL_NVS_SIZE + hal.nvs.driver_area.size, false);
|
|
|
|
// Switch hal to use RAM version of non-volatile storage data
|
|
hal.nvs.type = NVS_Emulated;
|
|
hal.nvs.get_byte = &ram_get_byte;
|
|
hal.nvs.put_byte = &ram_put_byte;
|
|
hal.nvs.memcpy_to_nvs = &memcpy_to_ram;
|
|
hal.nvs.memcpy_from_nvs = &memcpy_from_ram;
|
|
hal.nvs.memcpy_from_flash = NULL;
|
|
hal.nvs.memcpy_to_flash = NULL;
|
|
|
|
// If no physical storage available or if NVS import fails copy default settings to RAM
|
|
// and write out to physical storage when available.
|
|
if(physical_nvs.type == NVS_None || ram_get_byte(0) != SETTINGS_VERSION) {
|
|
settings_restore(settings_all);
|
|
if(physical_nvs.type == NVS_Flash)
|
|
physical_nvs.memcpy_to_flash(nvsbuffer);
|
|
else if(physical_nvs.memcpy_to_nvs)
|
|
physical_nvs.memcpy_to_nvs(0, nvsbuffer, GRBL_NVS_SIZE + hal.nvs.driver_area.size, false);
|
|
if(physical_nvs.type != NVS_None)
|
|
grbl.report.status_message(Status_SettingReadFail);
|
|
}
|
|
} else
|
|
protocol_enqueue_foreground_task(report_warning, "Not enough heap for NVS buffer!");
|
|
|
|
// Clear settings dirty flags
|
|
memset(&settings_dirty, 0, sizeof(settings_dirty_t));
|
|
|
|
return nvsbuffer != NULL;
|
|
}
|
|
|
|
// Allocate NVS block for driver settings.
|
|
// NOTE: allocation has to be done before content is copied from physical storage.
|
|
nvs_address_t nvs_alloc (size_t size)
|
|
{
|
|
static uint8_t *mem_address;
|
|
|
|
nvs_address_t addr = 0;
|
|
|
|
// Check if already switched to emulation or buffer allocation failed, return NULL if so.
|
|
if(hal.nvs.type == NVS_Emulated || nvsbuffer == NULL)
|
|
return 0;
|
|
|
|
if(hal.nvs.driver_area.address == 0) {
|
|
hal.nvs.driver_area.address = GRBL_NVS_SIZE;
|
|
hal.nvs.driver_area.mem_address = mem_address = nvsbuffer + GRBL_NVS_SIZE;
|
|
}
|
|
|
|
size += NVS_CRC_BYTES; // add room for checksum.
|
|
if(hal.nvs.driver_area.size + size < (nvs_size_max - GRBL_NVS_SIZE)) {
|
|
mem_address = (uint8_t *)((uint32_t)(mem_address - 1) | 0x03) + 1; // Align to word boundary
|
|
addr = mem_address - nvsbuffer;
|
|
mem_address += size;
|
|
hal.nvs.driver_area.size = mem_address - hal.nvs.driver_area.mem_address;
|
|
hal.nvs.size = GRBL_NVS_SIZE + hal.nvs.driver_area.size + 1;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
// Write RAM changes to physical storage
|
|
void nvs_buffer_sync_physical (void)
|
|
{
|
|
if(!settings_dirty.is_dirty)
|
|
return;
|
|
|
|
if(physical_nvs.memcpy_to_nvs) {
|
|
|
|
if(settings_dirty.version)
|
|
settings_dirty.version = physical_nvs.memcpy_to_nvs(0, nvsbuffer, 1, false) != NVS_TransferResult_OK;
|
|
|
|
if(settings_dirty.global_settings)
|
|
settings_dirty.global_settings = physical_nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)(nvsbuffer + NVS_ADDR_GLOBAL), sizeof(settings_t) + NVS_CRC_BYTES, false) != NVS_TransferResult_OK;
|
|
|
|
if(settings_dirty.build_info)
|
|
settings_dirty.build_info = physical_nvs.memcpy_to_nvs(NVS_ADDR_BUILD_INFO, (uint8_t *)(nvsbuffer + NVS_ADDR_BUILD_INFO), sizeof(stored_line_t) + NVS_CRC_BYTES, false) != NVS_TransferResult_OK;
|
|
|
|
uint_fast8_t idx = N_STARTUP_LINE, offset;
|
|
if(settings_dirty.startup_lines) do {
|
|
idx--;
|
|
if(bit_istrue(settings_dirty.startup_lines, bit(idx))) {
|
|
bit_false(settings_dirty.startup_lines, bit(idx));
|
|
offset = NVS_ADDR_STARTUP_BLOCK + idx * (sizeof(stored_line_t) + NVS_CRC_BYTES);
|
|
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer + offset), sizeof(stored_line_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
|
|
bit_false(settings_dirty.startup_lines, bit(idx));
|
|
}
|
|
} while(idx);
|
|
|
|
idx = N_CoordinateSystems;
|
|
if(settings_dirty.coord_data) do {
|
|
if(bit_istrue(settings_dirty.coord_data, bit(idx))) {
|
|
offset = NVS_ADDR_PARAMETERS + idx * (sizeof(coord_data_t) + NVS_CRC_BYTES);
|
|
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer + offset), sizeof(coord_data_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
|
|
bit_false(settings_dirty.coord_data, bit(idx));
|
|
}
|
|
} while(idx--);
|
|
|
|
if(settings_dirty.driver_settings) {
|
|
if(hal.nvs.driver_area.size > 0)
|
|
settings_dirty.driver_settings = physical_nvs.memcpy_to_nvs(hal.nvs.driver_area.address, (uint8_t *)(nvsbuffer + hal.nvs.driver_area.address), hal.nvs.driver_area.size, false) != NVS_TransferResult_OK;
|
|
else
|
|
settings_dirty.driver_settings = false;
|
|
}
|
|
|
|
#if N_TOOLS
|
|
idx = N_TOOLS;
|
|
if(settings_dirty.tool_data) do {
|
|
idx--;
|
|
if(bit_istrue(settings_dirty.tool_data, bit(idx))) {
|
|
offset = NVS_ADDR_TOOL_TABLE + idx * (sizeof(tool_data_t) + NVS_CRC_BYTES);
|
|
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer + offset), sizeof(tool_data_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
|
|
bit_false(settings_dirty.tool_data, bit(idx));
|
|
}
|
|
} while(idx);
|
|
#endif
|
|
settings_dirty.is_dirty = settings_dirty.coord_data ||
|
|
settings_dirty.global_settings ||
|
|
settings_dirty.driver_settings ||
|
|
settings_dirty.startup_lines ||
|
|
#if N_TOOLS
|
|
settings_dirty.tool_data ||
|
|
#endif
|
|
settings_dirty.build_info;
|
|
|
|
} else if(physical_nvs.memcpy_to_flash) {
|
|
uint_fast8_t retries = 4;
|
|
do {
|
|
if(physical_nvs.memcpy_to_flash(nvsbuffer))
|
|
retries = 0;
|
|
else if(--retries == 0)
|
|
report_message("Settings write failed!", Message_Warning);
|
|
} while(retries);
|
|
memset(&settings_dirty, 0, sizeof(settings_dirty_t));
|
|
}
|
|
}
|
|
|
|
nvs_io_t *nvs_buffer_get_physical (void)
|
|
{
|
|
return hal.nvs.type == NVS_Emulated ? &physical_nvs : &hal.nvs;
|
|
}
|
|
|
|
#ifdef DEBUGOUT
|
|
|
|
#include "report.h"
|
|
|
|
void nvs_memmap (void)
|
|
{
|
|
char buf[30];
|
|
|
|
report_message("NVS Area: addr size end", Message_Plain);
|
|
|
|
strcpy(buf, "Global: ");
|
|
strcat(buf, uitoa(NVS_ADDR_GLOBAL));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(sizeof(settings_t) + NVS_CRC_BYTES));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_GLOBAL + sizeof(settings_t) + NVS_CRC_BYTES));
|
|
report_message(buf, Message_Plain);
|
|
|
|
strcpy(buf, "Parameters: ");
|
|
strcat(buf, uitoa(NVS_ADDR_PARAMETERS));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(N_CoordinateSystems * (sizeof(coord_data_t) + NVS_CRC_BYTES)));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_PARAMETERS + N_CoordinateSystems * (sizeof(coord_data_t) + NVS_CRC_BYTES)));
|
|
report_message(buf, Message_Plain);
|
|
|
|
strcpy(buf, "Startup block: ");
|
|
strcat(buf, uitoa(NVS_ADDR_STARTUP_BLOCK));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(N_STARTUP_LINE * (sizeof(stored_line_t) + NVS_CRC_BYTES)));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_STARTUP_BLOCK + N_STARTUP_LINE * (sizeof(stored_line_t) + NVS_CRC_BYTES)));
|
|
report_message(buf, Message_Plain);
|
|
|
|
strcpy(buf, "Build info: ");
|
|
strcat(buf, uitoa(NVS_ADDR_BUILD_INFO));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(sizeof(stored_line_t) + NVS_CRC_BYTES));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_BUILD_INFO + sizeof(stored_line_t) + NVS_CRC_BYTES));
|
|
report_message(buf, Message_Plain);
|
|
|
|
#if N_TOOLS
|
|
strcpy(buf, "Tool table: ");
|
|
strcat(buf, uitoa(NVS_ADDR_TOOL_TABLE));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(N_TOOLS * (sizeof(tool_data_t) + NVS_CRC_BYTES)));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_TOOL_TABLE + N_TOOLS * (sizeof(tool_data_t) + NVS_CRC_BYTES)));
|
|
report_message(buf, Message_Plain);
|
|
#endif
|
|
|
|
strcpy(buf, "Driver: ");
|
|
strcat(buf, uitoa(hal.nvs.driver_area.address));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(hal.nvs.driver_area.size));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(hal.nvs.driver_area.address + hal.nvs.driver_area.size));
|
|
report_message(buf, Message_Plain);
|
|
}
|
|
|
|
#endif
|