mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-01-06 16:08:28 -04:00
06441b4117
* Need to get my APM1 board from home to test it on there...
365 lines
10 KiB
C++
365 lines
10 KiB
C++
|
|
#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
|
|
|
|
// the last page holds the log format in first 4 bytes. Please change
|
|
// this if (and only if!) the low level format changes
|
|
#define DF_LOGGING_FORMAT 0x28122011
|
|
|
|
// we use an invalie logging format to test the chip erase
|
|
#define DF_LOGGING_FORMAT_INVALID 0x28122012
|
|
|
|
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);
|
|
return (read_dword() != DF_LOGGING_FORMAT);
|
|
}
|
|
|
|
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);
|
|
}
|