Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Эмуляция EEPROM в первых двух секторах STM32F4
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
AlexUT4
Всем добрый день.
Для камня STM32F407 взял эмулятор EEPROM с сайта st. Вложил в свой проект с оригинальными настройками как в примере. Всё вроде как работает нормально. Но там EEPROM эмулируется во втором и третьем секторах FLASH памяти (которые по 16к). Я в своём проекте перенёс эмуляцию EEPROM в первые два сектора и соответственно сдвинул таблицу векторов прерывания на второй сектор:
Код
define symbol __ICFEDIT_intvec_start__ = 0x08008000; (в stm32f4xx_flash.icf)
#define VECT_TAB_OFFSET  0x8000 (в system_stm32f4xx.c)

Необходимые изменения внёс в eeprom.h.

В своём проекте в начале вставил пример записи трёх переменных в память. Всё как бы работает, НО только с отладчиком из IAR контроллер стартует как надо. Если сделать сброс кнопкой, то уже не стартует.
Я пока перенёс эмуляцию EEPROM в последние два сектора, но они по 128к и откусывать пол флеша как-то не охота. В такой конфигурации всё работает как надо.

Чувствую, что надо указать в прерывании сброса откуда стартовать программе. Помогите разобраться с проблемкой.
Спасибо.
Сергей Борщ
QUOTE (AlexUT4 @ Aug 28 2013, 06:48) *
Чувствую, что надо указать в прерывании сброса откуда стартовать программе.
Программа стартует с адреса, указанного в векторе по адресу 4. Из адреса 0 берется начальное значение указателя стека. Эти адреса прибиты гвоздями. Переносите свою EEPROM в последние сектора. В первом (нулевом) должна находиться таблица прерываний.
scifi
Цитата(AlexUT4 @ Aug 28 2013, 08:48) *
Но там EEPROM эмулируется во втором и третьем секторах FLASH памяти (которые по 16к). Я в своём проекте перенёс эмуляцию EEPROM в первые два сектора

Как уже сказано, первый сектор занять для эмуляции EEPROM не получится.
А чем не устраивают второй и третий секторы?
AlexUT4
Цитата(Сергей Борщ @ Aug 28 2013, 08:40) *
Программа стартует с адреса, указанного в векторе по адресу 4. Из адреса 0 берется начальное значение указателя стека. Эти адреса прибиты гвоздями. Переносите свою EEPROM в последние сектора. В первом (нулевом) должна находиться таблица прерываний.


Про таблицу векторов понял. Тогда в нулевом секторе надо разместить начало таблицы прерываний. В прерывании сброса сделать переход на начало программы расположенной после Sector 1 и Sector 2, которые заняты под EEPROM. То есть основную программу расположить, начиная с Sector 3. Я правильно мыслю?

Цитата(scifi @ Aug 28 2013, 10:02) *
Как уже сказано, первый сектор занять для эмуляции EEPROM не получится.
А чем не устраивают второй и третий секторы?


Вы имели ввиду Sector 2 Sector 3 или Sector 1 Sector 2 ? В примере эмуляция сделана в Sector 2 и Sector 3, а основной код закладывается с самого начала FLASH и, как я думаю, есть опасность перекрытия кода программы с секторами EEPROM. В линковщике я никаких мер по избеганию наложения не увидел (может плохо смотрел).
scifi
Цитата(AlexUT4 @ Aug 28 2013, 12:04) *
есть опасность перекрытия кода программы с секторами EEPROM. В линковщике я никаких мер по избеганию наложения не увидел (может плохо смотрел).

Я именно об этом. У линкера есть все необходимые средства.
Сергей Борщ
QUOTE (AlexUT4 @ Aug 28 2013, 10:04) *
То есть основную программу расположить, начиная с Sector 3. Я правильно мыслю?
В целом - да. Фактически в нулевом секторе вам надо расположить два первых элемента таблицы (указатель стека и адрес вектора сброса). А в основной программе все равно надо будет иметь полную таблицу векторов и в процессе запуска программы записать ее адрес в регистр VTOR. Но ради чего все это городить? Почему не выделить под EEPROM последние сектора? Тогда вам достаточно будет в скрипте линкера уменьшить доступный программе объем флеша.
toweroff
Цитата(AlexUT4 @ Aug 28 2013, 12:04) *
есть опасность перекрытия кода программы с секторами EEPROM


вариантов месколько. Можно линковщику сказать, что флеш располагается тут и тут, а еепром-сектора обойти


также при эмуляции eeprom можно на получившийся адрес накладывать маску, которая позволит не "выпрыгнуть" за диапазон отведенных секторов



Цитата(Сергей Борщ @ Aug 28 2013, 13:18) *
Тогда вам достаточно будет в скрипте линкера уменьшить доступный программе объем флеша.
и loadregion разбивать не придется. Честно говоря, даже и не пробовал никогда собирать с "дырками"


хотя... эту область также можно в loadregion включить и получится без разрывов, только код в ней не размещать

scifi
Цитата(Сергей Борщ @ Aug 28 2013, 13:18) *
Почему не выделить под EEPROM последние сектора?

Длительность стирания сектора 16К и 128К - 250 мс и 1000 мс соответственно (typ). Есть над чем подумать. Ну и кого-то жаба задушит: зачем разбазаривать 256К, когда можно обойтись 32К?
AlexUT4
Цитата(Сергей Борщ @ Aug 28 2013, 12:18) *
В целом - да. Фактически в нулевом секторе вам надо расположить два первых элемента таблицы (указатель стека и адрес вектора сброса). А в основной программе все равно надо будет иметь полную таблицу векторов и в процессе запуска программы записать ее адрес в регистр VTOR. Но ради чего все это городить? Почему не выделить под EEPROM последние сектора? Тогда вам достаточно будет в скрипте линкера уменьшить доступный программе объем флеша.


Два последних сектора по 128к. А это половина памяти МК. Не выгодно. Первые четыре сектора по 16к.

Цитата(toweroff @ Aug 28 2013, 12:26) *
вариантов месколько. Можно линковщику сказать, что флеш располагается тут и тут, а еепром-сектора обойти


также при эмуляции eeprom можно на получившийся адрес накладывать маску, которая позволит не "выпрыгнуть" за диапазон отведенных секторов



и loadregion разбивать не придется. Честно говоря, даже и не пробовал никогда собирать с "дырками"


хотя... эту область также можно в loadregion включить и получится без разрывов, только код в ней не размещать



Я сейчас линковщику указал что доступная память начиная с 0x0800C000 это Sector 3.
Код
define symbol __ICFEDIT_region_ROM_start__    = 0x0800C000

Вектора по умолчанию в начало флеша. В настройках эмулятора EEPROM
Код
#define EEPROM_START_ADDRESS  ((uint32_t)0x08004000)

это Sector 1.

В памяти основной код лёг с 0x0800C000. Основная таблица прерываний легла с 0x08000000.
По адресу 4 указатель на вектор прерывания, который расположен выше 0x0800C000. А там дальше SystemInit и стар основной программы.
Пока всё завелось и работает. Тестирую….

Но как-то не выгодно с этими секторами – получается, что первый сектор в 16к «гуляет почти свободным». Там сейчас всего не более 512 байт записано. Не рационально))))

Как указать линковщику, что бы он ничего не располагал с адреса 0x08004000 по 0x0800BFFF ? Тогда память будет выгоднее использоваться.
toweroff
Я с Кейлом бы помог, но наверняка Ваш линкер имеет аналогичные механизмы
Код
LoadRegion 0x08000000
{
    VectorRegion +0 0x4000
    {
        STARTUP.o (RESET, +First)
        *(InRoot$$Sections)
        .ANY (+RO)
    }
    EEROM_region +0 EMPTY 0x1000
    {
    }
    CODE_region +0 0xZZZZZZZZ
    {
        .ANY (+RO)
    }
}

как-то так. То есть указали, что начало загрузки - 0x08000000, сразу (+0) с длиной 4000 идет секция, в которой располагается вся root секция с таблицей векторов и любой код, который поместится (.ANY(+RO)), потом пустая часть, и потом еще секция под код
scifi
Цитата(AlexUT4 @ Aug 28 2013, 13:45) *
Но как-то не выгодно с этими секторами – получается, что первый сектор в 16к «гуляет почти свободным». Там сейчас всего не более 512 байт записано. Не рационально))))

В "старом" яровском линкере (XLINK) можно было сказать линкеру, чтобы размещал в разных кусках памяти, типа так: -Z(CODE)CODE=0x08000000-0x08003FFF, 0x0800C000-0x0807FFFF. И ведь реально работало.
В "новом" линкере (ILINK) я не разбирался, но думаю, что он тоже это умеет (заглядывал в мануал). Попробуйте подправить файл конфигурации линкера:
define region ROM_region = mem:[from 0x08000000 to 0x08003FFF] mem:[from 0x0800C000 to 0x0807FFFF];
AlexUT4
Цитата(scifi @ Aug 28 2013, 14:48) *
В "старом" яровском линкере (XLINK) можно было сказать линкеру, чтобы размещал в разных кусках памяти, типа так: -Z(CODE)CODE=0x08000000-0x08003FFF, 0x0800C000-0x0807FFFF. И ведь реально работало.
В "новом" линкере (ILINK) я не разбирался, но думаю, что он тоже это умеет (заглядывал в мануал). Попробуйте подправить файл конфигурации линкера:
define region ROM_region = mem:[from 0x08000000 to 0x08003FFF] mem:[from 0x0800C000 to 0x0807FFFF];



Большое спасибо за наводку. Почитал доку на линковщик. Там много чего можно сделать, в том числе и регион с дырой. Я переделал так stm32f4xx_flash.icf :

Код
..........
define symbol __ICFEDIT_region_ROM_start__    = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__      = 0x0807FFFF;
define symbol __ICFEDIT_region_RAM_start__    = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__      = 0x2001FFFF;
define symbol __ICFEDIT_region_CCMRAM_start__ = 0x10000000;
define symbol __ICFEDIT_region_CCMRAM_end__   = 0x1000FFFF;

define symbol __EEPROM_region_ROM_start__ = 0x08004000;    /* Начало и конец памяти под эмуляцию EEPROM */
define symbol __EEPROM_region_ROM_end__   = 0x0800BFFF;

/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x800;
define symbol __ICFEDIT_size_heap__   = 0x400;
/**** End of ICF editor section. ###ICF###*/


define memory mem with size = 4G;
define region ROM_region      = mem:[from __ICFEDIT_region_ROM_start__ to (__EEPROM_region_ROM_start__ - 0x00000001)]
                              | mem:[from (__EEPROM_region_ROM_end__ + 0x00000001) to __ICFEDIT_region_ROM_end__];
..........


Полёт нормальный. Но только в Sector 0 линковщик совсем немного кода добавил. Всё остальное уже после выделенной EEPROM памяти.

Всем спасибо за ликбезы))))
scifi
Цитата(AlexUT4 @ Aug 28 2013, 23:40) *
Но только в Sector 0 линковщик совсем немного кода добавил. Всё остальное уже после выделенной EEPROM памяти.

Наверное, это потому, что весь программный код складывается в одну большую сплошную секцию. Соответственно, в нулевой сектор попадают оставшиеся крохи: таблица векторов прерываний, константы, начальные значения статических переменных. Можете проверить это предположение, заглянув в MAP файл. Помню, что GCC умеет выделять отдельную секцию для каждой функции, а вот йар - не похоже...
AlexUT4
Цитата(scifi @ Aug 29 2013, 10:20) *
Наверное, это потому, что весь программный код складывается в одну большую сплошную секцию. Соответственно, в нулевой сектор попадают оставшиеся крохи: таблица векторов прерываний, константы, начальные значения статических переменных. Можете проверить это предположение, заглянув в MAP файл. Помню, что GCC умеет выделять отдельную секцию для каждой функции, а вот йар - не похоже...


Да, разложил линковщик так:


*******************************************************************************
*** PLACEMENT SUMMARY
***

"A1": place at 0x08000000 { ro section .intvec };
"P1": place in [from 0x08000000 to 0x08003fff] |
[from 0x0800c000 to 0x0807ffff] { ro };
"P2": place in [from 0x20000000 to 0x2001ffff] { rw, block CSTACK, block HEAP };

Section Kind Address Size Object
------- ---- ------- ---- ------
"A1": 0x188
.intvec ro code 0x08000000 0x188 startup_stm32f40xx.o [1]
- 0x08000188 0x188

"P1", part 1 of 2: 0x250
.rodata const 0x08000188 0x0 zero_init3.o [5]
.rodata const 0x08000188 0x0 lz77_init.o [5]
Initializer bytes ro data 0x08000188 0x24f <for P2 s0>
- 0x080003d7 0x24f

"P1", part 2 of 2: 0x10cdc
.text ro code 0x0800c000 0x2060 shell.o [1]
.text ro code 0x0800e060 0x1bc ip_addr.o [1]
.text ro code 0x0800e21c 0xc errno.o [3]
.text ro code 0x0800e228 0x110 xstrerro.o [3]
.text ro code 0x0800e338 0x36 strlen.o [5]
.text ro code 0x0800e36e 0x22 zero_init3.o [5]
.text ro code 0x0800e390 0x3f4 api_lib.o [1]
.....

будет место для констант и инициализаций. Их планируется не мало.

vlad_new
Я тут то же ковырялся с дырой для флеша в кейле. Ничего у меня с дыркой в регионе не вышло. Взял и тупо в ассемблерный файл, сразу после таблицы описания векторов, влепил SPACE(0x8000). Не красиво конечно, но времени не было.
A. Fig Lee
С огнем играете. Сколько там циклов перезаписи?
У нас в старых STM32F107 уже ошибки записи выдает.
Если EEPROM интенсивно не использовать, то ничего, конечно..
scifi
Цитата(vlad_new @ Aug 30 2013, 14:18) *
Я тут то же ковырялся с дырой для флеша в кейле. Ничего у меня с дыркой в регионе не вышло.

Дык Кейл этого и не умеет. Там нужно руками раскладывать секции сверху и снизу от дырки.
Rash
Цитата(AlexUT4 @ Aug 28 2013, 22:40) *
Большое спасибо за наводку....


сделал по такому примеру, всё работает, но есть следующий вопрос:
При такой записи регион с дырой получается, но есть одно НО, вектора прерываний разместятся с адреса 0x08000000, а код весь будет размещён после выделенных регионом под EEPROM. Т.е. такой записи (где используются сектора 1 и 2) код разместится с адреса 0x0800c000, а если назначить для эмуляции EEPROM сектора 2 и 3, то код разместится уже по адресу 0x08010000. Т.о. получается, если делать дыру во флеши для EEPROM надо использовать всегда сектора начиная с 1-го иначе будет потеря секторов флеши. И также 0 сектор тоже практически будет потерян по памяти, т.к. в нём кроме таблицы прерываний ничего не размещается (т.е. для эмуляции EEPROM будут потеряны 3 сектора, 2 сектора на EEPROM, а один будет гулять практически пустым). Можно ли это как то обойти, может ещё нужно в линкере чего-то прописывать? Прибивать ф-ции или массивы констант по конкретным адресам желания нет.
В данном проект памяти хватает и потеря 3-х секторов по 16К не критична, но на будущее хочется уметь использовать всю память
Сергей Борщ
Цитата(Rash @ Oct 7 2014, 09:41) *
И также 0 сектор тоже практически будет потерян по памяти, т.к. в нём кроме таблицы прерываний ничего не размещается
Мне сейчас неохота рыть документацию, но у F1xx при включении защиты от чтения первые два сектора автоматически защищаются и от записи. И переписать их можно только стерев весь кристалл. Здесь нет чего-то подобного? Как бы не получилось, что включив защиту от чтения вы поломаете работу этой эмулированной ЭСППЗУ. А в целом я бы отдал нулевой сектор загрузчику и забыл про него, а программу размещал бы целым куском уже после секторов эмуляции ЭСППЗУ.
adnega
Цитата(Сергей Борщ @ Oct 7 2014, 13:34) *
А в целом я бы отдал нулевой сектор загрузчику и забыл про него, а программу размещал бы целым куском уже после секторов эмуляции ЭСППЗУ.

+1. Собственно, с такой картой распределения секторов ничего лучше и не придумаешь.

Код
MEMORY
{
  RAM (xrw)  : ORIGIN = 0x20000000, LENGTH = 128K
  CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  BKPSRAM(rw!x): ORIGIN = 0x40024000, LENGTH = 4K
  
  BOOTLOADER (rx) : ORIGIN = 0x08000000, LENGTH = 16K
  EEPROM (rw!x) : ORIGIN = 0x08004000, LENGTH = 16K + 16K
  IMAGE (rw!x) : ORIGIN = 0x0800C000, LENGTH = 16K + 64K
  FLASH (rwx) : ORIGIN = 0x08020000, LENGTH = 128K
}
scifi
Цитата(Rash @ Oct 7 2014, 10:41) *
Можно ли это как то обойти, может ещё нужно в линкере чего-то прописывать? Прибивать ф-ции или массивы констант по конкретным адресам желания нет.

ИМХО, такой фичи нет.

Цитата(Rash @ Oct 7 2014, 10:41) *
В данном проект памяти хватает и потеря 3-х секторов по 16К не критична, но на будущее хочется уметь использовать всю память

Ну так и не надо дёргаться раньше времени. Страшный день, когда кончится память, скорее всего никогда не наступит. Кроме того, как известно, "преждевременная оптимизация - корень всех зол".
А если когда-нибудь действительно припрёт, то ручками разложить секции не так уж и сложно.
kolobok0
Цитата(Сергей Борщ @ Oct 7 2014, 13:34) *
...я бы отдал нулевой сектор загрузчику и забыл про него, а программу размещал бы целым куском уже после секторов эмуляции ЭСППЗУ.


ещё можно добавить, что если обернуть функциями по динамичеки равномерному заполнению и стиранию этой эмулированной эспэпэзэу, то
мелкие сегменты в 16килл идеально ложаться на чанки оперирования этой памятью, при данном распределении.
Rash

не много не по секторам, но про эмуляцию.

Модернизировал то, что написано STM, добавил возможность писать не только 16, но и 8 и 32 битные данные (выбирается через дефайн).
И запустил их тестовый проект. Так вот, при записи 16 битных и 32 битных данных, проблем нет, а при записи 8-ми битных данных при переходе с одной страницы на другую страницу, заполненная страница не всегда полностью очищается, что приводит к дальнейшему неверному чтению данных.
Это происходит в ф-ции EE_PageTransfer(...) , где страница очищается ф-цией
Код
flash_status = FLASH_EraseSector(old_page_Id, VOLTAGE_RANGE);

Если ставить точки останова в ф-ции EE_PageTransfer(...), не важно в каких местах, то такой проблемы нет.
Другой вариант решения этой проблемы добавлением инструкции __DSB() или задержки (хватило где-то 35 NOPов)
Код
static uint16_t EE_PageTransfer(uint32_t v_addr, uint32_t data)
{
                                 ...
  flash_status = FLASH_EraseSector(old_page_Id, VOLTAGE_RANGE);
  __DSB();
                                 ...
}

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