Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Шаблон (tamplete ) для работы с EEPROM
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
paravozru
Возникла проблема при работе с EEPROM на STM32L-Discovery
Как можно создать адресацию EEPROM в заголовочном файле. Вот грабли на которые я наткнулся: в заголовочном файле .h сначала объявил указатели и им присвоил адреса вот примерно так
1.Мысля:
uint16_t *ModBusAddr = (uint16_t *)0x08080000;
в каком то .с файле вызов функции чтения EEPROM
uint16_t ReadEEPROM(uint16_t *addr) { // функция чтения EEPROM принимающая адрес указателя, и возвращающая uint16_t
uint16_t *ptr;
uint16_t result;
ptr = (uint16_t *)(addr);
result = *ptr;
return result;
}
Вот грабли №1: Keil выдал ошибку что множественное объявление «ModBusAddr » , оказывается можно только объявить переменные в .h но присвоить значения можно только в .с файлах.

2. Мысля
Решил адресацию EEPROM через #define
#define ModBusAddr 0
в таком случае функция чтения EEPROM приняла вид
uint16_t ReadEEPROM(uint16_t addr) { // функция чтения EEPROM принимающая позицию в памяти , и возвращающая uint16_t
uint16_t *ptr;
uint16_t result;
ptr = (uint16_t *)(eepromADR + addr ); // где #define eepromADDR 0x08080000 - начало EEPROM в STM32L-discovery
result = *ptr;
return result;
}
вот грабли №2: Да все работает, но во первых придется рассчитать каждый short, int, переменную в адресном пространстве EEPROM и задавать последовательность 0,1,2,3,4 и т п
Например для
#define ModBusAddr 0 //так в голове держу 0x08080000 , выделю два байта для этой переменной
#define ModBusFunc 2 // так в голове держу 0x08080002 выделю два байта для этой переменной
и т д.
Вот еще проблема функция uint16_t ReadEEPROM(uint16_t addr) имеет тип uint16 что принимаемый что возвращаемый, как быть если я хочу выделить для одной переменной например только байт, для другой два байта, а с float что делать ?

Тоже подумал и решил так создам Шаблон функции которая будет принимать различные типы данных и в зависимости от этого читать определенное количество байт с EEPROM/

template<typemane eeType>

eeType ReadEEPROM(eeType addr) {
eeType *ptr;
eeType result;
ptr = (eeType *)(addrEEPROM + adrr);
result = *ptr;
return result;
}

Проошу помощи, каким образом Вы распределяете память и как описаны функции чтения и записи c различными типами данных с EEPROM.

Забыл указать, при создании шаблона Keil выдал ошибку "this declaration has no storage class or type specifier"
jcxz
1. При объявлении переменной, задаёте ей альтернативный сегмент. Для IAR:
static __no_init int var[N] @ ".eeprom";
или
static const int var[N] @ ".eeprom";
2. В командном файле линкёра компонуете ваш сегмент (.eeprom) в нужные адреса EEPROM.
=F8=
А теперь подставьте вместо eeType к примеру char и что вы получите?

char ReadEEPROM(char addr) {
char *ptr;
char result;
ptr = (int *)(addrEEPROM + adrr);
result = *ptr;
return result;
}
Вас не смущает что адрес задается как char?

Тут есть 3 варианта:
1. C выравниванием.
template<typemane eeType>
eeType ReadEEPROM(addr_t addr){
return(*((eeType *)(addrEEPROM + addr)));
}

2.Если все хранится без выравнивания

template<typemane eeType>
eeType ReadEEPROM(addr_t addr){
eeType res;
memcpy(&res, addrEEPROM + addr, sizeof(eeType));
return res;
}

3 Тоже с выравниванием. Мне этот вариант не нравится в силу нелогичности.
template<typemane eeType>
eeType ReadEEPROM(eeType *addr){
return(*((eeType *)addrEEPROM + addr));
}
paravozru
Цитата(jcxz @ Jul 29 2013, 12:46) *
1. При объявлении переменной, задаёте ей альтернативный сегмент. Для IAR:
static __no_init int var[N] @ ".eeprom";
или
static const int var[N] @ ".eeprom";
2. В командном файле линкёра компонуете ваш сегмент (.eeprom) в нужные адреса EEPROM.

Не понятно, можно пример, или объяснить.
jcxz
Так я же привёл пример. wacko.gif
=F8=
Цитата(paravozru @ Jul 29 2013, 12:57) *
Не понятно, можно пример, или объяснить.


в .icf
define symbol __region_SEGMENT_NAME_start__= 0xXXXXXXXX;
define symbol __region_SEGMENT_NAME_end__ = 0xXXXXXXXX;
define region SEGMENT_NAME_region= mem:[from __region_SEGMENT_NAME_start__ to __region_SEGMENT_NAME_end__];
place in SEGMENT_NAME_region { section SEGMENT_NAME };

в .с
#pragma segment="SEGMENT_NAME"

#pragma location="SEGMENT_NAME"
int a;
или
int b @ "SEGMENT_NAME";

а еще лучше

struct eeprom_t{
Сюда добавляем все, что хотим хранит в еепром
};
и затем
#pragma location="SEGMENT_NAME"
struct eeprom_t eeprom;
jcxz
Цитата(=F8= @ Jul 29 2013, 16:52) *
в .с
#pragma segment="SEGMENT_NAME"

А это зачем? Этого не надо.
=F8=
Цитата(jcxz @ Jul 29 2013, 14:03) *
А это зачем? Этого не надо.

Мдя... а ведь точно не надо. Когда-то давно то-ли в описании линкера увидел, то-ли в примере каком, ну в общем оно появилось, да так и пошло из проекта в проект sm.gif.
Вспомнил, мне надо было кусок памяти из датафлеш в эту секцию кидать, соответственно нужны были __section_begin/end. Вот так и остался этот некрокусок.
paravozru
Цитата(=F8= @ Jul 29 2013, 15:23) *
Мдя... а ведь точно не надо. Когда-то давно то-ли в описании линкера увидел, то-ли в примере каком, ну в общем оно появилось, да так и пошло из проекта в проект sm.gif.
Вспомнил, мне надо было кусок памяти из датафлеш в эту секцию кидать, соответственно нужны были __section_begin/end. Вот так и остался этот некрокусок.

Вот по поводу примера "=F8=" я сталкивался что переменные хранят как struct:, но все равно переменной uint8_t выделяется 2 байта в памяти. Я и думал возможность применения универсальной функции "template" для оптимизации пространства EEPROM, что бы каждому типу переменной выделялся соответствующий размер. И кстати функция чтения и записи в данном примере оперирует только с uint16_t
Вот пример действующий :
struct eeprom { // Структура разнотипных данных
vu16 MyModBusAdr; //0
vu16 MyHartAdr; //2
vu16 MyCanAdr; //4
vu8 IPadr[4]; //6
vu8 Result[4]; //8
}

uint8_t SetEEPROMData (uint16_t address, uint16_t Value) { функция записи в struct eeprom
тело функции;
}

Ох спасибо, начинает доходить, к сож с подобным образом хранения данных в EEPROM я не встречался, ( по опыту разбора чужих прошивок), я постараюсь разобраться. И способ c template возможен ?
Golikov A.
я сделал файл EEPROM который пихает данные и читает данные из еепром.

функции у него

Код
read_data(void * var, unsigned adr,  unsigned len);
write_data(void * var, unsigned adr,  unsigned len);

и все... Дальше дефайнами размечаю память

Код
#define BASE_ADDR 0x0000
#define VAR1_ADDR BASE_ADDR
#define VAR2_ADDR (BASE_ADDR + sizeof(Var1))


или просто напрямую без sizeof в уме высчитываю и все...


запись-чтение
Код
read_data((void *)&var1, VAR1_ADDR, sizeof(var1));
write_data((void *)&var2, VAR2_ADDR, sizeof(var2));



зачем шаблоны то? Для некоторых частых переменных делаю макросы.



paravozru
Цитата(Golikov A. @ Jul 29 2013, 17:43) *
я сделал файл EEPROM который пихает данные и читает данные из еепром.
...

Просто хочется более рационального подхода и по больше использовать возможности C++. Кстати кто какие С++ возможности использует ? например class ? знаю как работает но не знаю как полезно применить.
=F8=
Цитата(paravozru @ Jul 29 2013, 15:27) *
но все равно переменной uint8_t выделяется 2 байта в памяти.

Если беспокоит плотность упаковки то

#pragma pack(1)
struct eetrom_t{
...........
};
#pragma pack()

В этом случае все будет паковаться байт к байту. В этом случае работать с переменными можно только напрямую т.е. eeprom.var = xx или xx = eeprom.var. Но не int *p = &eeprom.var; *p = 10;
paravozru
Цитата(=F8= @ Jul 29 2013, 12:01) *
А теперь подставьте вместо eeType к примеру char и что вы получите?...

Да вот проблема Keil ругается на template <typename eeType> "this declaration has no storage class or type specifier". не могу заставить работать, хотя в Visual Studio без пролбем.

На сколько я понимаю __no_init, segment, location, pack это директивы препроцессора ? Огромное спасибо всем по поводу советов размещения переменных в EEPROM.
paravozru
в .icf
define symbol __region_EEPROM_start__= 0x08080000; // __region_EEPROM_start__= начало EEPROM таак можно записать ? и что значит symbol ?
define symbol __region_EEPROM_end__ = 0x08080FFFF; // допустим так
define region SEGMENT_NAME_region= mem:[from __region_EEPROM_start__= to __region_EEPROM_end__]; // что значит эта запись region
place in SEGMENT_NAME_region { section SEGMENT_NAME };

в .с
#pragma segment="SEGMENT_NAME"

#pragma location="SEGMENT_NAME"
int a;
или
int b @ "SEGMENT_NAME";

а еще лучше

struct eeprom_t{
Сюда добавляем все, что хотим хранит в еепром
};
и затем
#pragma location="SEGMENT_NAME" // а что указывается тут ?! адрес куда размещается struct ? .
struct eeprom_t eeprom;[/quote]
Объясните плиз, если честно не знакомо такое объявление и где можно прочитать о таком способе ?
MrYuran
Цитата(paravozru @ Jul 30 2013, 14:34) *
#pragma location="SEGMENT_NAME" // а что указывается тут ?! адрес куда размещается struct ? .

Имя секции, в которой будет размещена структура.
Секция либо одна из предопределенных, либо размеченная самостоятельно.
Golikov A.
Цитата(paravozru @ Jul 29 2013, 17:48) *
Просто хочется более рационального подхода и по больше использовать возможности C++. Кстати кто какие С++ возможности использует ? например class ? знаю как работает но не знаю как полезно применить.


Классы - черные ящики с защитой работы. Они хороши не для конкретного проекта, а для повторного использования в будущих проектах или при разработке проекта группой программистов.

Класс позволяет наделить его функционалом, абстрактно проверить его работоспособность и закрыв все возможные будущие ошибки внутри класса передавать его по проектам или по команде разработчиков. По большей части практически все что дает класс можно реализовать на С без плюсов, последние время так и делаю. Класс у меня это отдельный файл, все переменные в нем делают с доступом через функции, все глобальное от модуля - замкнуто внутри файла и снаружи недоступно.

Что не возможно: наследование, полиморфизм и прочие прелести С++, это приходится делать ручками, создавая новый файл, включая в него старый и дублируя функции, за приватностью функций приходиться следить руками... Обращение через класс родитель к потомкам - вот единственное что не удается симулировать, но как-то обхожусь...%)


DASM
Когда-то задавлся таким вопросом, вкратце тут http://electronix.ru/forum/index.php?s=&am...st&p=384003

Впрочем я не согласен.

Цитата(Golikov A. @ Jul 31 2013, 11:31) *
включая в него старый и дублируя функции, за приватностью функций приходиться следить руками... Обращение через класс родитель к потомкам - вот единственное что не удается симулировать, но как-то обхожусь...%)

Зачем все это ? Создавайте указатели на потомков, не надо ничего дублировать. Неплохой пример имхо ядро Линукса. Чистый С со всеми признаками ООП.
Сергей Борщ
QUOTE (Golikov A. @ Jul 31 2013, 09:31) *
Они хороши не для конкретного проекта, а для повторного использования в будущих проектах
Почему же? И в конкретном проекте можно использовать все его прелести.
QUOTE (Golikov A. @ Jul 31 2013, 09:31) *
По большей части практически все что дает класс можно реализовать на С без плюсов, последние время так и делаю. Класс у меня это отдельный файл, все переменные в нем делают с доступом через функции, все глобальное от модуля - замкнуто внутри файла и снаружи недоступно.
То есть private - члены вы реализуете через static? Как при этом вы реализуете встраиваемые (inline) функции? Как получаете sizeof() этого "класса", как заводите в других файлах экземпляры этого "класса"? Как-то не тянет на "практически все", скорее на "кое что, да и то через ж".
И зачем вообще такой велосипед с квадратными колесами если можно использовать специально созданный для этого инструмент - язык C++?
Golikov A.
приват можно делать не только через статик, а просто не указывать его в хедере, например.
Велосипед для встраивания в старые проекты, и для компиляторов не дружащих с С++.

а как в С++ инлайн сделан?
сайзоф через функцию член, например.


Да понятно что это все извращения, просто иногда нужен С, а хочется приятностей....

Наверное у меня проекты слишком маленькие, прелестей от С++ внутри одного замкнутого проекта не ощущаю...
Сергей Борщ
QUOTE (Golikov A. @ Aug 2 2013, 12:53) *
приват можно делать не только через статик, а просто не указывать его в хедере, например.

CODE
file1.c:

int a;


file2.c:

extern int a;   // какой такой private???

file3.c:

int a;             // приятной отладки
=F8=
Цитата(Golikov A. @ Jul 31 2013, 10:31) *
Что не возможно: наследование, полиморфизм и прочие прелести С++, это приходится делать ручками, создавая новый файл, включая в него старый и дублируя функции, за приватностью функций приходиться следить руками... Обращение через класс родитель к потомкам - вот единственное что не удается симулировать, но как-то обхожусь...%)


Все можно.
Код
typedef struct{
int(*vfunc_a)(void *this);//Полиморфизм
int b;
}class_a_t;

int fa(void *this){};

//вызов "метода"
class_a_t ttt;
int class_a_func(class_a_t *this){};
class_a_func(&ttt);
//вызов "виртуального" метода ttt.vfunc_a(&ttt);

class_a_t *a_class_constructor(class_a_t *this)
{
   if(this->vfunc_a == NULL)this->vfunc_a = fa;
   return this;
}

typedef struct{
class_a_t class_a;//Наследование
int c;
}class_b_t;

можно и динамическое создание структур в "конструктор" добавить.
Вопрос только нафига этот закат солнца вручную.
Golikov A.
Цитата(Сергей Борщ @ Aug 2 2013, 18:47) *
Код
file1.c:

int a;


file2.c:

extern int a;   // какой такой private???

file3.c:

int a;             // приятной отладки


не ну не надо же самому себе засовывать... Если бы не было разницы, то чего С++ придумывать было... Немного самодисциплины и компилятор уже здорово начинает помогать.

Цитата(=F8= @ Aug 3 2013, 12:55) *
Все можно.
можно и динамическое создание структур в "конструктор" добавить.
Вопрос только нафига этот закат солнца вручную.


больше для того чтобы мозг не ржавел...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.