You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
10 KiB
365 lines
10 KiB
|
|
#include <AP_HAL.h> |
|
#include "Dataflash.h" |
|
|
|
using namespace AP_HAL_AVR; |
|
|
|
extern const AP_HAL::HAL& hal; |
|
|
|
// 0: When reach the end page stop, 1: Start overwriting from page 1 |
|
#define DF_OVERWRITE_DATA true |
|
|
|
void CommonDataflash::erase_all() { |
|
for (uint16_t i = 1; i <= (_num_pages+1)/8; i++) { |
|
_block_erase(i); |
|
hal.scheduler->delay(1); |
|
} |
|
start_write(_num_pages+1); |
|
write_dword(DF_LOGGING_FORMAT); |
|
finish_write(); |
|
} |
|
|
|
bool CommonDataflash::need_erase() { |
|
start_read(_num_pages+1); |
|
uint32_t fmtbyte = read_dword(); |
|
if (fmtbyte != DF_LOGGING_FORMAT) { |
|
hal.console->printf_P( |
|
PSTR("logging fmt got %lX expected %lX\r\n"), |
|
fmtbyte, DF_LOGGING_FORMAT); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
void CommonDataflash::start_write(int16_t page) { |
|
_buffer_num = 1; |
|
_buffer_idx = 4; |
|
_page_addr = page; |
|
_stop_write = false; |
|
|
|
_wait_ready(); |
|
|
|
_buffer_write(_buffer_num, 0, _file_num >> 8 ); |
|
_buffer_write(_buffer_num, 1, _file_num&0xFF ); |
|
_buffer_write(_buffer_num, 2, _file_page >> 8 ); |
|
_buffer_write(_buffer_num, 3, _file_page&0xFF ); |
|
} |
|
|
|
void CommonDataflash::finish_write() { |
|
_buffer_idx = 0; |
|
/* Write buffer to memory, no wait. */ |
|
_buffer_to_page(_buffer_num, _page_addr, false); |
|
|
|
_page_addr++; |
|
if (DF_OVERWRITE_DATA) { |
|
if (_page_addr > _num_pages) { |
|
_page_addr = 1; |
|
} |
|
} else { |
|
if (_page_addr > _num_pages) { |
|
_stop_write = true; |
|
} |
|
} |
|
|
|
/* switch buffer to continue writing */ |
|
_buffer_num = (_buffer_num == 1) ? 2 : 1; |
|
} |
|
|
|
void CommonDataflash::write_byte(uint8_t data) { |
|
if (_stop_write) return; |
|
_buffer_write( _buffer_num, _buffer_idx, data ); |
|
_buffer_idx++; |
|
/* end of buffer? */ |
|
if ( _buffer_idx > _page_size) { |
|
/* 4 bytes for filenumber, filepage */ |
|
_buffer_idx = 4; |
|
/* write buffer to memory, no waiting */ |
|
_buffer_to_page(_buffer_num, _page_addr, false); |
|
_page_addr++; |
|
if (DF_OVERWRITE_DATA) { |
|
if (_page_addr > _num_pages) { |
|
_page_addr = 1; |
|
} |
|
} else { |
|
if (_page_addr > _num_pages) { |
|
_stop_write = true; |
|
} |
|
} |
|
/* switch buffer to continue writing */ |
|
_buffer_num = (_buffer_num == 1) ? 2 : 1; |
|
|
|
/* We are starting a new page. write filenumber and filepage. */ |
|
_buffer_write(_buffer_num, 0, _file_num >> 8 ); |
|
_buffer_write(_buffer_num, 1, _file_num&0xFF ); |
|
_buffer_write(_buffer_num, 2, _file_page >> 8 ); |
|
_buffer_write(_buffer_num, 3, _file_page&0xFF ); |
|
} |
|
} |
|
|
|
void CommonDataflash::write_word(uint16_t data) { |
|
write_byte( data >> 8 ); /* high byte */ |
|
write_byte( data & 0xFF ); /* low byte */ |
|
} |
|
|
|
void CommonDataflash::write_dword(uint32_t data) { |
|
write_byte( data >> 24 ); /* high byte */ |
|
write_byte( data >> 16 ); |
|
write_byte( data >> 8 ); |
|
write_byte( data & 0xFF ); /* low byte */ |
|
} |
|
|
|
void CommonDataflash::start_read(int16_t page) { |
|
_read_buffer_num = 1; |
|
_read_buffer_idx = 4; |
|
_read_page_addr = page; |
|
_wait_ready(); |
|
/* Write memory page to buffer. */ |
|
_page_to_buffer(_read_buffer_num, _read_page_addr); |
|
_read_page_addr++; |
|
|
|
/* We are starting a new page. Read file number and file page */ |
|
_file_num = _buffer_read(_read_buffer_num, 0); |
|
_file_num = (_file_num << 8) | _buffer_read(_read_buffer_num, 1); |
|
_file_page = _buffer_read(_read_buffer_num, 2); |
|
_file_page = (_file_page << 8 ) | _buffer_read(_read_buffer_num, 3); |
|
} |
|
|
|
uint8_t CommonDataflash::read_byte() { |
|
_wait_ready(); |
|
uint8_t result = _buffer_read( _read_buffer_num, _read_buffer_idx); |
|
_read_buffer_idx++; |
|
/* Check if we reached the end of buffer */ |
|
if ( _read_buffer_idx >= _page_size ) { |
|
/* 4 bytes for file number, file page */ |
|
_read_buffer_idx = 4; |
|
/* Write memory page to buffer */ |
|
_page_to_buffer(_read_buffer_num, _read_page_addr); |
|
_read_page_addr++; |
|
/* If we reach the end o fmemory, start from the beginning */ |
|
if (_read_page_addr > _num_pages) { |
|
_read_page_addr = 0; |
|
} |
|
/* We are starting a new page. read file number and file page. */ |
|
_file_num = _buffer_read(_read_buffer_num, 0); |
|
_file_num = (_file_num << 8) | _buffer_read(_read_buffer_num, 1); |
|
_file_page = _buffer_read(_read_buffer_num, 2); |
|
_file_page = (_file_page << 8) | _buffer_read(_read_buffer_num, 3); |
|
|
|
} |
|
return result; |
|
} |
|
|
|
uint16_t CommonDataflash::read_word() { |
|
uint16_t result = read_byte(); /* High byte */ |
|
result = (result << 8) | read_byte(); /* Low byte */ |
|
return result; |
|
} |
|
|
|
uint32_t CommonDataflash::read_dword() { |
|
uint32_t result = read_byte(); /* High byte */ |
|
result = (result << 8) | read_byte(); |
|
result = (result << 8) | read_byte(); |
|
result = (result << 8) | read_byte(); /* Low byte */ |
|
return result; |
|
} |
|
|
|
void CommonDataflash::set_file(uint16_t filenum) { |
|
_file_num = filenum; |
|
_file_page = 1; |
|
} |
|
|
|
int16_t CommonDataflash::find_last_log() { |
|
int16_t last_page = _find_last_page(); |
|
/* start_read will populate _file_num. */ |
|
start_read(last_page); |
|
return _file_num; |
|
} |
|
|
|
void CommonDataflash::get_log_boundaries(uint8_t log, |
|
int16_t &startpage, int16_t &endpage) { |
|
/* XXX Here be dragons. I transliterated this code from DataFlash_Class:: |
|
* get_log_boundaries - pch 04sept12 */ |
|
int16_t num_logs = get_num_logs(); |
|
if ( num_logs == 1 ) { |
|
/* Read the file number from the last page. */ |
|
start_read(_num_pages); |
|
/* invariant: find_last_page_of_log does not change _file_num */ |
|
endpage = _find_last_page_of_log((uint16_t)log); |
|
if (_file_num == 0xFFFF) { |
|
startpage = 1; |
|
} else { |
|
startpage = endpage + 1; |
|
} |
|
} else { |
|
if (log == 1) { |
|
start_read(_num_pages); |
|
if (_file_num == 0xFFFF) { |
|
startpage = 1; |
|
} else { |
|
startpage = _find_last_page() + 1; |
|
} |
|
} else { |
|
if ( log == (find_last_log() - num_logs + 1) ) { |
|
startpage = _find_last_page() + 1; |
|
} else { |
|
int16_t look = num_logs - 1; |
|
do { startpage = _find_last_page_of_log(look) + 1; |
|
look--; |
|
} while (startpage <= 0 && look >= 1); |
|
} |
|
} |
|
} |
|
if (startpage == ( (int16_t) _num_pages + 1 ) || startpage == 0 ) { |
|
startpage = 1; |
|
} |
|
endpage = _find_last_page_of_log((uint16_t) log); |
|
if (endpage <= 0) { |
|
endpage = startpage; |
|
} |
|
} |
|
|
|
uint8_t CommonDataflash::get_num_logs() { |
|
/* First try _find_last_page */ |
|
int16_t last_page = _find_last_page(); |
|
if (last_page == 1) { |
|
return 0; |
|
} |
|
/* Read _file_num from page 1 */ |
|
start_read(1); |
|
if (_file_num == 0xFFFF) { |
|
return 0; |
|
} |
|
|
|
/* Read _file_num from last page */ |
|
start_read(last_page); |
|
uint16_t last = get_file(); |
|
/* XXX bounds check on last_page+2? */ |
|
/* Read _file_num from last_page+2 */ |
|
start_read(last_page+2); |
|
uint16_t first = _file_num; |
|
if (first > last) { |
|
/* We wrapped aroung, so get the file_num from page 1. */ |
|
start_read(1); |
|
first = _file_num; |
|
} |
|
|
|
if (last == first) { |
|
return 1; |
|
} else { |
|
return (last - first + 1); |
|
} |
|
} |
|
|
|
void CommonDataflash::start_new_log() { |
|
uint16_t last_page = _find_last_page(); |
|
start_read(last_page); |
|
/* XXX I'm pretty sure there's a bug here - find_last_log will overwrite |
|
* the _file_num invariant from start_read(last_page). |
|
* However, I'm reproducing the existing DataFlash_Class faithfully. */ |
|
if (find_last_log() == 0 || _file_num == 0xFFFF) { |
|
set_file(1); |
|
start_write(1); |
|
return; |
|
} |
|
/* Check for log of length 1 page and suppress */ |
|
if (get_file() <= 1) { |
|
/* Last log is too short, reuse its number */ |
|
set_file(_file_num); |
|
/* and overwrite it */ |
|
start_write(last_page); |
|
} else { |
|
/* XXX shouldn't we have checked (== 0xFFFF) before using last_page |
|
* in the case above? */ |
|
if (last_page == 0xFFFF) { |
|
last_page = 0; |
|
} |
|
set_file(get_file() + 1); |
|
start_write(last_page + 1); |
|
} |
|
} |
|
|
|
int16_t CommonDataflash::_find_last_page() { |
|
uint16_t top = _num_pages; |
|
uint16_t bottom = 1; |
|
start_read(bottom); |
|
uint32_t bottom_hash = ((uint32_t) _file_num) << 16 | _file_page; |
|
|
|
while ( top - bottom > 1 ) { |
|
uint16_t look = (top + bottom) / 2; |
|
start_read(look); |
|
uint32_t look_hash = ((uint32_t) _file_num) << 16 | _file_page; |
|
if (look_hash >= 0xFFFF0000) { |
|
look_hash = 0; |
|
} |
|
if (look_hash < bottom_hash) { |
|
/* move down */ |
|
top = look; |
|
} else { |
|
/* move up */ |
|
bottom = look; |
|
bottom_hash = look_hash; |
|
} |
|
} |
|
|
|
start_read(top); |
|
uint32_t top_hash = ((uint32_t) _file_num) << 16 | _file_page; |
|
if (top_hash >= 0xFFFF0000) { |
|
top_hash = 0; |
|
} |
|
if (top_hash > bottom_hash) { |
|
return top; |
|
} else { |
|
return bottom; |
|
} |
|
} |
|
|
|
int16_t CommonDataflash::_find_last_page_of_log(uint16_t log_num) { |
|
|
|
uint16_t bottom, top; |
|
|
|
if (_check_wrapped()) { |
|
start_read(1); |
|
bottom = _file_num; |
|
if (bottom > log_num) { |
|
bottom = _find_last_page(); |
|
top = _num_pages; |
|
} else { |
|
bottom = 1; |
|
top = _find_last_page(); |
|
} |
|
} else { |
|
bottom = 1; |
|
top = _find_last_page(); |
|
} |
|
|
|
uint32_t check_hash = ((int32_t) log_num) << 16 | 0xFFFF; |
|
while (top - bottom > 1) { |
|
uint16_t look = (top + bottom) / 2; |
|
start_read(look); |
|
uint32_t look_hash = ((uint32_t) _file_num) << 16 | _file_page; |
|
if (look_hash >= 0xFFFF0000) { |
|
look_hash = 0; |
|
} |
|
if (look_hash > check_hash) { |
|
top = look; /* move down */ |
|
} else { |
|
bottom = look; /* move up */ |
|
} |
|
} |
|
|
|
start_read(top); |
|
if (_file_num == log_num) { |
|
return top; |
|
} |
|
start_read(bottom); |
|
if (_file_num == log_num) { |
|
return bottom; |
|
} |
|
return -1; |
|
} |
|
|
|
bool CommonDataflash::_check_wrapped() { |
|
start_read(_num_pages); |
|
return (_file_num != 0xFFFF); |
|
}
|
|
|