Ответ 1
Могут быть два случая:
- Аппаратное обеспечение таково, что определенный диапазон адресов сопоставляется с SD-картой, и данные могут быть записаны/прочитаны с SD-карты с использованием обычных инструкций по доступу к памяти в пределах этого диапазона. Используются обычные указатели.
- Для чтения/записи на SD-карту необходимо использовать специальные инструкции/функции. Например, функция
SDCard::read
, которая вызывает специальную функцию в ОС (если она есть) или инструкцию прерывания.
__flash
является расширением GCC. Он использует другую инструкцию для доступа к памяти и находит данные static
в другом сегменте. Но он не может быть обобщен таким образом, используя только С++. Он также не может использоваться с динамическим распределением.
Первый случай (диапазон адресов)
Чтобы использовать обычные указатели для чтения/записи данных на SD-карту, их необходимо пометить volatile
. Таким образом, компилятор не оптимизирует и не читает/не пишет. volatile
означает, что память может быть изменена/использоваться извне программы, например, аппаратное обеспечение, записывающее ее на SD-карту. См. http://en.cppreference.com/w/cpp/language/cv.
Например
volatile char* data = 0x00010000;
memcpy(data, "test", 5);
записывает "test"
на SD-карту, если, например, отображен диапазон памяти 0x00010000
.. 0x0001ffff
.
Чтобы динамически выделять память на SD-карте, например, с помощью malloc
и free
для обычной рабочей памяти, потребуется специальный распределитель. Он должен будет обрабатывать сегментирование самой памяти, т.е. Ему нужно отображать, какие области памяти свободны или распределены, а allocate(len)
должен найти свободный сегмент длины не менее len
. Обычно это обрабатывается операционной системой.
Это может быть написано на С++ как класс распределителя, класс, который должен удовлетворять требованиям Allocator
(концепция): http://en.cppreference.com/w/cpp/concept/Allocator. Например (неполное):
template<typename T>
class SD_allocator {
using value_type = T;
using pointer = T*;
pointer allocate(std::size_t len) {} // allocate segment of len bytes
void deallocate(pointer, std::size_t) {}
};
Если затем можно использовать контейнеры STL, например:
std::vector<int, SD_allocator<int>> vec;
использует память на SD-карте для элементов vec
. Здесь они не volatile
и предназначены только для использования внутри программы, а не для постоянного хранения на SD-карте.
Стандартный распределитель в С++ - это std::allocator
, который выделяет регулярную память, например malloc
и free
.
Boost, похоже, предоставляет распределитель, который обрабатывает сегментирование в пользовательской определенной области памяти:
http://www.boost.org/doc/libs/1_35_0/doc/html/interprocess/managed_memory_segments.html http://www.boost.org/doc/libs/1_55_0/doc/html/boost/interprocess/allocator.html
Для постоянного хранения, такого как SD-карта, может быть лучше определить фиксированную структуру и макет для данных на SD-карте, а затем прочитать/записать на него.
struct SDCard_data {
std::int32_t int1;
std::int32_t buffer1[500];
std::int8_t padding_[34];
int four_bit1 : 4;
int four_bit2 : 4;
bool bit1:1;
bool bit2:1;
bool bit3:1;
bool bit4:1;
};
static volatile SDCard_data* sdcard
= reinterpret_cast<volatile SDCard_data*>(0x0001000);
int write_to_card() {
// writes to the card
card->int1 = 32;
card->bit3 = true;
}
Второй случай (специальные инструкции)
Если чтение/запись на SD-карту не соответствуют обычным инструкциям по доступу к памяти на аппаратном обеспечении, данные по ней не могут быть доступны напрямую с помощью указателей raw volatile
.
Если целью является доступ к нему таким образом, потребуется класс, например MemDef
. Возможно, лучше обработать SD-карту, такую как файл/поток, и вместо этого записать/прочитать целые куски данных в/из нее, используя такие функции, как fopen
, fread
, fprintf
или аналогичные.
Для этого объекты должны быть сериализованы/неэтериализованы. Копирование struct
в качестве необработанной памяти, например
struct A;
A a;
write_to_card(reinterpret_cast<void*>(&a), sizeof(A))
работает до тех пор, пока struct
является PODType
и не содержит указателей/ссылок, то есть типов, внутреннее представление которых зависит на адресах памяти. Он также зависит от компоновки памяти платформы (выравнивание, прокладка структуры), конкретизации, представления float
, CHAR_BIT
и т.д. Для кросс-платформенной поддержки (например, когда SD-карта считывается с другого устройства с другим микроконтроллером, вместо этого необходимо будет использовать некоторый формат файла.
Также может быть (но сложно) определить пользовательский класс Allocator
, который использует класс типа MemDef
как тип указателя.