Перейти к содержанию
    

Размещение переменой по заданному адресу и ее значение после ресета

Потребление RTC - 5 uA. Если питание пропадет на сутки, чтобы RTC не сбросился.....

Какие сутки? Вы же писали про ресеты, а не выключение питания.

Если всё-таки выключение питания, то либо ставить батарейку, либо писать надо во флеш.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А зачем нужно это инкрементирование переменной? Возможно есть решения получше, которые Вы не замечаете.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Как я и думал.... :laughing:

Вы смотрите совсем не туда. __no_init тут не нужен. Как не нужен и инкремент. __no_init для этого адреса должно быть в бутлоадере.

Общий алгоритм таков:

В бутлоадере объявляете этот адрес:

__no_init char flag @ ...;

при старте бутлоадера проверяете сначала флаг причины перезагрузки МК (искать где он нужно в даташите), если сброс был не по причине внутреннего WDT - передаёте управление в основную прогу. Если причина == внутренний WDT и flag != 0 - прошиваете прошивку и flag = 0.

В основной программе объявляете:

char flag @ ...;

А когда нужно обновить прошивку делаете flag = 1 и вызываете срабатывание внутреннего WDT.

Всё.

 

PS: И я бы лучше указал имя определённой секции (в тех местах где ...), а не абсолютный адрес. А эту секцию в .icf смаппировал на нужный адрес. И в основной программе и в бутлоадере.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы смотрите совсем не туда. __no_init тут не нужен. Как не нужен и инкремент. __no_init для этого адреса должно быть в бутлоадере.

Инкремент я привел для примера. В реальности там флаг, примерно как вы и описали (спасибо за подробный пример). К тому же флаг "подписан" контрольной суммой.

Самое важное - что линкер под это выделяет место и что с очередной прошивкой там не окажется случайно не то что ожидаемо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Самое важное - что линкер под это выделяет место и что с очередной прошивкой там не окажется случайно не то что ожидаемо.

В Вашем случае важно чтобы линкёр и в бутлоадере зарезервировал тот же самый адрес. Затереть может именно стартап-код бутлоадера.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Какие сутки? Вы же писали про ресеты, а не выключение питания.

Если всё-таки выключение питания, то либо ставить батарейку, либо писать надо во флеш.

А разве батарейка нужна только для BkpSram? Часы же сбросятся. Даже и не представляю, какой сценарий использования без батарейки, но с конденсатором на Vbat.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А есть ли стандартный, т.е. не выходящий за синтаксис С/С++ способ инициализации объекта по точно указанному адресу? Под объектом понимается объект данных любого типа - простого встроенного или составного пользовательского. Например целое число, перечисление, массив или структура.

Вот пример кода, где объявляется и инициализируется константная структура с тремя полями данных. Из неё можно считывать данные - см. в main(). Но адрес её размещения в памяти выбирает линкер.

#include <stdint.h>

// Некая структура для примера
struct  MyConfig_st {
  uint8_t Field1;
  uint8_t Field2;
  uint8_t Field3;
};

// Инициализация константной структуры
const MyConfig_st MyConfig = {.Field1=0x66,.Field2=0x41,.Field3=0x7};

uint8_t tmp;		// Сюда будем считывать данные структуры

int main () {
  tmp=MyConfig.Field1;
  tmp=MyConfig.Field2;
  tmp=MyConfig.Field3;
  while(1);
}

Как штатными средствами C/C++ задать для этой структуры точный адрес?

Я могу сделать указатель на неё и присвоить ему значение адреса при помощи приведения типа целого к указателю на структуру. Но инициализировать структуру по такому указателю я не могу. На строку с инициализацией компилятор (Keil Arm Compiler V5.06) ругается.

#include <stdint.h>

// Некая структура для примера
struct  MyConfig_st {
  uint8_t Field1;
  uint8_t Field2;
  uint8_t Field3;
};

#define CONFIG_ADR	0x20000				// Адрес стр-ры конфигурации в памяти EEPROM
#define MyConfig_ptr	((MyConfig_st*)CONFIG_ADR)	// Указатель на стр-ру конфигурации в памяти EEPROM

// Объявляем и инициализируем стр-ру в памяти EEPROM
const MyConfig_st *MyConfig_ptr = {.Field1=0x66,.Field2=0x41,.Field3=0x7}; // Компилятор ругается !!!

  
int main() {
  while(1);  
}

Есть ли возможность сделать такое без использования директив размещения, которые в разных IDE и компиляторах/линкерах разные? Хотелось бы написать универсальный код, одинаково работающий везде, вне зависимости от среды разработки и компилятора.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На плюсах - написать конструктор для этой структуры и разместить ее через placement new(). На "голых" сях - написать функцию инициализации, в которую передавать указатель на структуру, т.е. то же самое, но "закат солнца вручную".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 hours ago, Сергей Борщ said:

На "голых" сях - написать функцию инициализации, в которую передавать указатель на структуру

Что-то типа такого?

#include <stdint.h>

// Простая структура для примера
struct  MyConfig_st {
  uint8_t Field1;
  uint8_t Field2;
  uint8_t Field3;
};

#define CONFIG_ADR      0x20000                     // Адрес стр-ры конфигурации в памяти EEPROM
#define MyConfig_ptr    ((MyConfig_st*)CONFIG_ADR)  // Указатель на стр-ру конфигурации в памяти EEPROM

// Заполнение стр-ры значениями
// S - указатель на стр-ру
// F1, F2, F3 - значения полей
void MyConfigInic (MyConfig_st* S,uint8_t F1,uint8_t F2,uint8_t F3) {
    S->Field1 = F1;
    S->Field2 = F2;
    S->Field3 = F3;
}

uint8_t tmp;        //  Сюда будем считывать данные из стр-ры 

int main () {
  
  // Присваиваем значение полям стр-ры
  MyConfigInic (MyConfig_ptr,0x66,0x41,0x7);
  
  // Считываем значения полей стр-ры
  tmp = MyConfig_ptr->Field1;	// Прочитали значение 0x66
  tmp = MyConfig_ptr->Field2;	// Прочитали значение 0x41
  tmp = MyConfig_ptr->Field3;	// Прочитали значение 0x7
}

Такое нормально сработает, если область размещения стр-ры будет в ОЗУ. При вызове функции MyConfigInic() с указателем на стр-ру и значениями для заполнения полей произойдёт присваивание полям стр-ры новых значений из аргументов ф-ции. Далее с полями стр-ры можно будет работать, обращаясь к ним через указатель.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

С какой стати голые Си, С++ должны знать структурную организацию памяти конкретного процессора/контроллера?

Хотите работать с флэшью конкретного процессора, используйте специфические директивы конкретного IDE. Например,в Кейле для АРМ:

uint32_t Dev __attribute__(at Addr) =	100;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 часов назад, Darth Vader сказал:

Но если стр-ра должна размещаться в области флеш-памяти

На этот случай стандартного решения не существует.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 hours ago, V_G said:

С какой стати голые Си, С++ должны знать структурную организацию памяти конкретного процессора/контроллера?

Для описанной задачи этого не требуется.

Всё, что надо для этого знать я сообщил компилятору двумя макросами:

#define CONFIG_ADR      0x20000                     // Адрес стр-ры конфигурации в памяти EEPROM
#define MyConfig_ptr    ((MyConfig_st*)CONFIG_ADR)  // Указатель на стр-ру конфигурации в памяти EEPROM

и описанием типа (структуры)  MyConfig_st.

От языка/компилятора требуется лишь поддержка инициализации объекта по указателю на него (тот самый (MyConfig_st*)CONFIG_ADR ).

А с этим, как оказалось, засада. Указатель создать можно. Его можно использовать в коде для обращения к объектам: записи/чтения в/из них значений. На этом принципе построено обращение ко всем регистрам периферии. Но вот инициализировать объект по нему списком инициализации почему-то оказалось нельзя. По имени объекта можно, а по указателю на него нельзя. Было бы можно - задача решилась бы.

8 hours ago, Сергей Борщ said:

На этот случай стандартного решения не существует.

Жаль.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Пришлось-таки использовать директивы.

Чтобы скрыть компиляторозависимую часть кода - обернул её макросом. Пока что определил только часть для Кейла. Когда понадобится использовать код в других IDE с другими компиляторами - надо будет взамен #error определить соответствующие директивы размещения.

#include <stdint.h>

// Простая структура для примера
struct  MyConfig_st {
  uint8_t Field1;
  uint8_t Field2;
  uint8_t Field3;
};

#define CONFIG_ADR      0x1E800                     // Адрес структуры конфигурации в памяти EEPROM

// Макрос атрибута размещения объекта по адресу ADR_VAL
#if   defined ( __CC_ARM )                                // for ARM Compiler 
  #define __AT_ADDR(ADR_VAL) __attribute__((at(ADR_VAL)))
#elif defined ( __ICCARM__ )                              // for IAR Compiler 
  #error __AT_ADDR(ADR_VAL) не определён для компилятора IAR!
#elif defined ( __GNUC__ )                                // for GNU Compiler 
  #error __AT_ADDR(ADR_VAL) не определён для компилятора GNU!
#elif defined ( __TASKING__ )                             // for TASKING Compiler
  #error __AT_ADDR(ADR_VAL) не определён для компилятора TASKING!
#elif defined ( __CMCARM__ )                              // for Phyton CMC-ARM Compiler
  #error __AT_ADDR(ADR_VAL) не определён для компилятора Phyton CMC-ARM!
#else 
  #error __AT_ADDR(ADR_VAL) не определён для используемого компилятора!
#endif

// Объявляем и инициализируем константную структуру конфигурации по адресу CONFIG_ADR
const MyConfig_st MyConfig __AT_ADDR(CONFIG_ADR) ={0x66,0x41,0x7};

 

Изменено пользователем Darth Vader

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Чтобы сохранять переменную между ресетами, её недостаточно разместить в неинициализируемой секции, которую линкер найдёт где конкретно разместить в ОЗУ? Зачем задавать ей точный адрес?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

48 минут назад, firew0rker сказал:

Чтобы сохранять переменную между ресетами, её недостаточно разместить в неинициализируемой секции, которую линкер найдёт где конкретно разместить в ОЗУ? Зачем задавать ей точный адрес?

Если хочется иметь константу во флеше, и ее изредка модифицировать (настроечные операции), лучше знать адрес.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...