Секция NoInit микроконтроллера STM32

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Открыл глаза
Сообщения: 68
Зарегистрирован: Вс ноя 13, 2011 15:09:58

Сообщение Buffalo »

Здравствуйте, разбираюсь с секцией NoInit микроконтроллера STM32. Как я понял, сначала эта секция с помощью программатора прошивается в некую область FLASH памяти микроконтроллера, а уже при подачи питания на микроконтроллер, эта область FLASH памяти копируется в SRAM. То есть секция NoInit эта секция ОЗУ, которая не инициализируется при старте или рестарте, и переменные живущие в этой области будут сохранять свои значения при сбросе. По крайней мере так понял я.

И вот тут у меня в голове возникает неутыка.

Представим ситуацию, есть переменная uint32_t counter, в которой находится значение 0, и есть микркоконтроллер , который стартовал и начал выполнять некие действия, работал микроконтроллер работал, изменял эту переменную и стал счетчик равным counter = 36558, и вдруг случилось ужасное, у микроконтроллера кратковременно пропало питание, произошел сброс. Получается, что во флеше у нас этот counter до сих пор равен нулю, так как мы его не изменяли, а ОЗУшка у нас сбросилась из-за потери питания. И counter начала увеличиваться с начала. Получается, что переменные не сохраняются, а сбрасываются. Никак не могу понять, где я туплю.

Соответственно вопрос, по какому механизму сохраняются переменные в ОЗУ, после сброса питания ?
Реклама
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

При отключении питания или входе в расширенные режимы сна содержимое ОЗУ теряется. Для сохранения важных данных можно использовать:
-резервную область часового модуля с батарейным питанием;
-внутреннюю EEPROM(в L серии);
-не занятые программой сектора Flash(ресурс сильно ограничен);
-внешнюю микросхему EEPROM;
-внешнюю микросхему FRAM(когда требуется высокая скорость) и т.д.
Реклама
Открыл глаза
Сообщения: 68
Зарегистрирован: Вс ноя 13, 2011 15:09:58

Сообщение Buffalo »

uk8amk писал(а):При отключении питания или входе в расширенные режимы сна содержимое ОЗУ теряется. Для сохранения важных данных можно использовать:
-резервную область часового модуля с батарейным питанием;
-внутреннюю EEPROM(в L серии);
-не занятые программой сектора Flash(ресурс сильно ограничен);
-внешнюю микросхему EEPROM;
-внешнюю микросхему FRAM(когда требуется высокая скорость) и т.д.
Получается, что эта секция нужна для того, сохранить данные в не изменённом виде, например, если контроллер ушел в режим сна "Sleep", а потом проснулся. В то время как , если он уйдет в режим сна "Standby", то там все данные потеряются. Я Вас правильно понял ?
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

А можно какую-нибудь ссылочку на NoInit, чтобы почитать. Я первый раз об этом слышу, а с STM32 только одним типом пользовался. Там такого я не нашел.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Реклама
Эиком - электронные компоненты и радиодетали
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

По всей видимости секция NOINIT потребуется когда часть данных сохраняется во внешней RAM/NVRAM и/или когда используется режим сна Standby. Тогда после выхода из Standby будет переход по вектору Reset и потрутся нужные данные. Noinit пропускает начальную инициализацию части памяти.
Использовать на деле не приходилось, поэтому всех тонкостей неизвестно.
Z_h_e писал(а):А можно какую-нибудь ссылочку на NoInit
http://www.keil.com/support/docs/3290.htm
Также встроенная справка KEIL:
Set Options for Target
Set target parameters in the dialog Options for Target - Target.
Open the dialog via the toolbar icon or the menu Project - Options for Target.

Read/Write Memory Areas
NoInit Specifies areas that should be excluded from zero initialization.
Реклама
Встал на лапы
Аватара пользователя
Сообщения: 127
Зарегистрирован: Пт июн 20, 2008 09:38:05
Откуда: Харьков

Сообщение Shapa »

Господа, вы не оттуда пляшите. NoInit секция не есть "что-то специфичное для STM32 семества". Это обычная секция, здесь вам нужно поискать вообще, что есть секции линковки программы. В соответсвии со стандартом языка С, вся статитка (все глобальные объекты и ститические объекты) должны быть проинициализированны 0 (линкер разместит их в секции .bss). Но! Это обнуление происходит при выполнении стартап секции, что можен занять достаточно длитеьны промежуток времени. Потому, для ускорения запуска (актуально для embedded систем) нечто может быть размещено в .noinit секции. Т.е. не будет проинициализированно перед вашей точкой входа.
С практической точки зрения это значит что

Код: Выделить всё

static int s_val __attribute__ ((section (".noinit"))) = 0xDEADBEAF; // вообще, тут будет ошибка 
...
main() {
 printf("%p", s_val);
распечатает рандомный мусор
и вам придется принудительно присваивать s_val = 0xDEADBEAF; в вашем коде, перед использованием
не инициализируется при старте или рестарте, и переменные живущие в этой области будут сохранять свои значения при сбросе
Совершенно верно, при "сбросе". Но! Сброс и обесточивание - это две большие разницы
ОЗУ есть энергозависимая памаять и она потеряет своё состояние при потере питания.

Конкретный пример, как использовать .noinit в STM32:
о BackupSram слышали?:)
1) Добавляем в скрипт линкера секцию, что отображает эту память. обзываем её backupSramSection.
2) Создаем переменную

Код: Выделить всё

static uint32_t s_mySuperDuperCounter __attribute__ ((section (".backupSramSection")));
3) Юзаем в коде, именно так, как вы ожидали
Контактная информация:
Реклама
Открыл глаза
Сообщения: 68
Зарегистрирован: Вс ноя 13, 2011 15:09:58

Сообщение Buffalo »

Shapa писал(а):Господа, вы не оттуда пляшите. NoInit секция не есть "что-то специфичное для STM32 семества". Это обычная секция, здесь вам нужно поискать вообще, что есть секции линковки программы. В соответсвии со стандартом языка С, вся статитка (все глобальные объекты и ститические объекты) должны быть проинициализированны 0 (линкер разместит их в секции .bss). Но! Это обнуление происходит при выполнении стартап секции, что можен занять достаточно длитеьны промежуток времени. Потому, для ускорения запуска (актуально для embedded систем) нечто может быть размещено в .noinit секции. Т.е. не будет проинициализированно перед вашей точкой входа.
С практической точки зрения это значит что

Код: Выделить всё

static int s_val __attribute__ ((section (".noinit"))) = 0xDEADBEAF; // вообще, тут будет ошибка 
...
main() {
 printf("%p", s_val);
распечатает рандомный мусор
и вам придется принудительно присваивать s_val = 0xDEADBEAF; в вашем коде, перед использованием
не инициализируется при старте или рестарте, и переменные живущие в этой области будут сохранять свои значения при сбросе
Совершенно верно, при "сбросе". Но! Сброс и обесточивание - это две большие разницы
ОЗУ есть энергозависимая памаять и она потеряет своё состояние при потере питания.

Конкретный пример, как использовать .noinit в STM32:
о BackupSram слышали?:)
1) Добавляем в скрипт линкера секцию, что отображает эту память. обзываем её backupSramSection.
2) Создаем переменную

Код: Выделить всё

static uint32_t s_mySuperDuperCounter __attribute__ ((section (".backupSramSection")));
3) Юзаем в коде, именно так, как вы ожидали
Вы не могли бы выложить простой пример, что бы его в отладчике можно было погонять. Опять таки, если Вас это не затруднит, я только начал изучать STM32, поэтому если я, что-то сказал не так или прошу много, просьба не пинать, ибо еще много не осознаю. А вот, то что написали, что эту секцию вводят для ускорения запуска мк, за это отдельное спасибо, теперь по крайней мере понятно, для чего нужна эта секция.
Встал на лапы
Аватара пользователя
Сообщения: 127
Зарегистрирован: Пт июн 20, 2008 09:38:05
Откуда: Харьков

Сообщение Shapa »

Buffalo писал(а):я только начал изучать STM32, поэтому если я, что-то сказал не так или прошу много, просьба не пинать, ибо еще много не осознаю.
Лиха беда начала. Все когда-то начинали и наступали на кучу граблей по не зназнию, разве это то, чего стоит стыдиться? :)
Buffalo писал(а):Вы не могли бы выложить простой пример, что бы его в отладчике можно было погонять.
С удовольствием. Но учтите, это будет псевдо-код. Я предпочитаю GNU GCC (да и вообще проприретарщина - зло). Распишу 2 варианта, 1 - простой. 2 - правильный.
Precondition:
1) У вас верно собрана схема backup питания. Т.е. подключена батарейка к МК
2) Разрешен rw доступ к backup регистрам (по идее надо врубить тактирование RTC, PWR, выполнить PWR_BackupAccessCmd(Enable) но лучше уточните)

Вариант 1 Stm32F100:

Код: Выделить всё

// uint16_t - т.к. у Stm32F100 sizeof(backupReg) == 2.
// Реальный адрес расчитан так: RTC_BASE + RegNo = 0x40006C00 + 0. реальные адреса для вашего камня смотрите начиная от BKP_WriteBackupRegister, там поймете
static uint16_t s_myNonVolatileVariable __attribute__((no_init)) __attribute__((at(0x40006C00))); // по идее в Keil'e заработает

int main (int argc, char *argv[]) {
    YourBSPInit();
    debugTrace("After reset s_myNonVolatileVariable is [%d]\n", s_myNonVolatileVariable);
    while(1) {
        if (someReason())
            s_myNonVolatileVariable++;
    }
    return 0;
}
Вариант 2: успростит размещение регистров в памяти (в коде т.к. размещать будет линкер)
1) в скрипте линкера создаем секцию

Код: Выделить всё

BACKUP (rw) : ORIGIN = 0x40006C00, LENGTH = 40 /* 40 - 20 регистров по 2 байта */
   .backup (NOLOAD) : ALIGN(2)
    {
        _backup = .;
        
        *(.backup .backup.*) 
        
         . = ALIGN(2) ;
        _end_backup = .;   
    } > BACKUP
Код такой же, как в примере 1, только меняем

Код: Выделить всё

static uint16_t s_myNonVolatileVariable __attribute__((no_init)) __attribute__((at(0x40006C00)));
на

Код: Выделить всё

static uint16_t s_myNonVolatileVariable __attribute__((backup));
PS: возможны ошибки компиляции, т.к. не проверял, у меня Keil'я нет
Контактная информация:
Открыл глаза
Сообщения: 68
Зарегистрирован: Вс ноя 13, 2011 15:09:58

Сообщение Buffalo »

Shapa писал(а):
Buffalo писал(а):я только начал изучать STM32, поэтому если я, что-то сказал не так или прошу много, просьба не пинать, ибо еще много не осознаю.
Лиха беда начала. Все когда-то начинали и наступали на кучу граблей по не зназнию, разве это то, чего стоит стыдиться? :)
Buffalo писал(а):Вы не могли бы выложить простой пример, что бы его в отладчике можно было погонять.
С удовольствием. Но учтите, это будет псевдо-код. Я предпочитаю GNU GCC (да и вообще проприретарщина - зло). Распишу 2 варианта, 1 - простой. 2 - правильный.
Precondition:
1) У вас верно собрана схема backup питания. Т.е. подключена батарейка к МК
2) Разрешен rw доступ к backup регистрам (по идее надо врубить тактирование RTC, PWR, выполнить PWR_BackupAccessCmd(Enable) но лучше уточните)

Вариант 1 Stm32F100:

Код: Выделить всё

// uint16_t - т.к. у Stm32F100 sizeof(backupReg) == 2.
// Реальный адрес расчитан так: RTC_BASE + RegNo = 0x40006C00 + 0. реальные адреса для вашего камня смотрите начиная от BKP_WriteBackupRegister, там поймете
static uint16_t s_myNonVolatileVariable __attribute__((no_init)) __attribute__((at(0x40006C00))); // по идее в Keil'e заработает

int main (int argc, char *argv[]) {
    YourBSPInit();
    debugTrace("After reset s_myNonVolatileVariable is [%d]\n", s_myNonVolatileVariable);
    while(1) {
        if (someReason())
            s_myNonVolatileVariable++;
    }
    return 0;
}
Вариант 2: успростит размещение регистров в памяти (в коде т.к. размещать будет линкер)
1) в скрипте линкера создаем секцию

Код: Выделить всё

BACKUP (rw) : ORIGIN = 0x40006C00, LENGTH = 40 /* 40 - 20 регистров по 2 байта */
   .backup (NOLOAD) : ALIGN(2)
    {
        _backup = .;
        
        *(.backup .backup.*) 
        
         . = ALIGN(2) ;
        _end_backup = .;   
    } > BACKUP
Код такой же, как в примере 1, только меняем

Код: Выделить всё

static uint16_t s_myNonVolatileVariable __attribute__((no_init)) __attribute__((at(0x40006C00)));
на

Код: Выделить всё

static uint16_t s_myNonVolatileVariable __attribute__((backup));
PS: возможны ошибки компиляции, т.к. не проверял, у меня Keil'я нет
У меня есть плата stm32vldiscovery. Там как раз стоит stm32f100rbt.

Подойдет ли батарейка типа CR2032 для backup питания ? Но я не могу понять как RTC связан с переменной s_myNonVolatileVariable и Reset'ом.
Ответить

Вернуться в «ARM»