Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум разработчиков электроники ELECTRONIX.ru _ Keil _ Непонятный рестарт программы

Автор: Вячик13 Jun 18 2018, 07:53

Имеется устройство на базе микроконтроллера STM32F100RC. Для него разработано программное обеспечение под Keil 4.22a:
Total RO Size (Code + RO Data) 76796 ( 75.00kB)
Total RW Size (RW Data + ZI Data) 7776 ( 7.59kB)
Total ROM Size (Code + RO Data + RW Data) 76944 ( 75.14kB)
Устройство (весовой терминал ротационных промышленных весов) установлено на промышленном объекте у заказчика в количестве 3 штук.

Наблюдается следующий эффект. Через произвольное время (от 3 до 30 минут) происходит сброс программы. Причём сброс странный. Статические переменные обнуляются, но сброс не "ловится" при отладке (установка breakpoint как в в начале SystemInit(), так и в начале main()). Мало того, при рестарте программы должно производиться тестирование дисплеев из 7-сегментных индикаторов (в начале main()), но оно не происходит.
В программе используется WDT IWDG, но это не его рук дело. Эти явления происходят на всех 3 устройствах. До этого я разработал более 20 аналогичных проектов на этом же устройстве с аналогичной структурой программы, но подобного никогда не наблюдал.

Буду благодарен за любой совет.


Автор: kovigor Jun 18 2018, 08:11

Цитата(Вячик13 @ Jun 18 2018, 10:53) *
Буду благодарен за любой совет.

Мощную помеху в линии питания (для эксперимента запитайте устройство от аккумулятора, а не от сети) или радиопомеху не исключаете ?

Автор: aaarrr Jun 18 2018, 08:13

Цитата(Вячик13 @ Jun 18 2018, 10:53) *
Наблюдается следующий эффект. Через произвольное время (от 3 до 30 минут) происходит сброс программы. Причём сброс странный. Статические переменные обнуляются, но сброс не "ловится" при отладке (установка breakpoint как в в начале SystemInit(), так и в начале main()). Мало того, при рестарте программы должно производиться тестирование дисплеев из 7-сегментных индикаторов (в начале main()), но оно не происходит.

Так может это и не сброс, а именно обнуление ОЗУ вследствие программной ошибки?

Автор: kovigor Jun 18 2018, 08:14

Цитата(aaarrr @ Jun 18 2018, 11:13) *
Так может это и не сброс, а именно обнуление ОЗУ вседствие программной ошибки?

Или стек переполняется, например, при интенсивных прерываниях, и собой затирает данные в ОЗУ ...

Автор: Вячик13 Jun 18 2018, 09:27

Цитата(aaarrr @ Jun 18 2018, 10:13) *
Так может это и не сброс, а именно обнуление ОЗУ вследствие программной ошибки?

Обнуляются все статические переменные (по крайней мере я проверил 7-8 разного типа). А какая может быть ошибка чтобы выйти на инициализирующий сброс статических переменных?

Автор: aaarrr Jun 18 2018, 09:50

Цитата(Вячик13 @ Jun 18 2018, 12:27) *
А какая может быть ошибка чтобы выйти на инициализирующий сброс статических переменных?

memset(x, 0, y), например. Не выходит оно никуда, просто самостоятельно стирает содержимое памяти.

Автор: Вячик13 Jun 18 2018, 09:53

Цитата(kovigor @ Jun 18 2018, 10:14) *
Или стек переполняется, например, при интенсивных прерываниях, и собой затирает данные в ОЗУ ...

Нет. Во-первых в данной системе нет интенсивных прерываний:
1) системные часы (каждую миллисекунду);
2) АЦП - 1 раз в 10 миллисекунд;
3) КПДП передатчика SPI (каждую миллисекунду);
4) 2 прерывания UART (не работали, поскольку не было компьютера верхнего уровня).
Во-вторых, как я уже писал, аналогичная система и в других проектах (даже есть более нагруженные).

Автор: kovigor Jun 18 2018, 10:11

Цитата(Вячик13 @ Jun 18 2018, 12:53) *
Во-вторых, как я уже писал, аналогичная система и в других проектах (даже есть более нагруженные).

Хорошо. Ловим сброс чисто аппаратно. Подпаиваем на плату светодиод и при сбросе зажигаем его. И в программе делаем так, чтобы выключить его можно было только оператору, например, нажатием кнопочки ...

Автор: Вячик13 Jun 18 2018, 10:19

Цитата(aaarrr @ Jun 18 2018, 11:50) *
memset(x, 0, y), например. Не выходит оно никуда, просто самостоятельно стирает содержимое памяти.

Проверил. Самое подозрительное:
memset(Display->Code,'\0',strlen((char *)Display->Code)); //Очистка кодовой строки
где Display->Code указатель на массив байт (unsigned char *).
Остальные memset все просты и явны.

Может быть функция strlen(), а тем более с аргументом-указателем на член структуры может глючить? Хотя в аналогичных проектах всё это работает безукоризненно.

Автор: kovigor Jun 18 2018, 10:21

Цитата(Вячик13 @ Jun 18 2018, 13:19) *
... ничего кроме трёхфазных асинхронников там нет.

Страшны не асинхроники, а контакторы, которые их включают. В момент коммутации возникают радиопомехи. И они могут быть особенно мощными и сокрушительными, если контакты не задемпфированы. Как у вас с этим, а ?
Если дело в этом, то запитка от аккумулятора в качестве эксперимента (а я вам настоятельно советую попробовать этот вариант) может и не помочь, т.к. помехи проходят сквозь эфир. Да, вместо аккумулятора можно использовать обычный UPS, физически (вилкой) отключенный от розетки и питающийся от аккумулятора. Это в том, например, случае, если ваш прибор питается от 220В и запитать его прямо от аккумулятора затруднительно или неудобно ...

Автор: Вячик13 Jun 18 2018, 10:27

Цитата(kovigor @ Jun 18 2018, 12:11) *
Хорошо. Ловим сброс чисто аппаратно. Подпаиваем на плату светодиод и при сбросе зажигаем его. И в программе делаем так, чтобы выключить его можно было только оператору, например, нажатием кнопочки ...

Спасибо, но вряд ли. Если это аппаратный сброс, то в любом случае должна была сработать подпрограмма тестирования индикаторов. А она гоняет по индикатору "палочки", а затем отображает дату и версию программы. я бы это увидел.

Цитата(kovigor @ Jun 18 2018, 12:21) *
Страшны не асинхроники, а контакторы, которые их включают. В момент коммутации возникают радиопомехи. И они могут быть особенно мощными и сокрушительными, если контакты не задемпфированы. Как у вас с этим, а ?
Если дело в этом, то запитка от аккумулятора в качестве эксперимента (а я вам настоятельно советую попробовать этот вариант) может и не помочь, т.к. помехи проходят сквозь эфир. Да, вместо аккумулятора можно использовать обычный UPS, физически (вилкой) отключенный от розетки и питающийся от аккумулятора. Это в том, например, случае, если ваш прибор питается от 220В и запитать его прямо от аккумулятора затруднительно или неудобно ...

В период наблюдения явления на площадке ещё шли монтажные работы и ничего такого не включалось

Автор: kovigor Jun 18 2018, 10:28

Цитата(Вячик13 @ Jun 18 2018, 13:27) *
В период наблюдения явления на площадке ещё шли монтажные работы и ничего такого не включалось

Болгарка, дрель, перфоратор, сварочник ? Нет ? Вы уверены ?
И еще, я бы все-таки стек попробовал увеличить ...

Автор: Вячик13 Jun 18 2018, 10:37

Цитата(kovigor @ Jun 18 2018, 12:28) *
Болгарка, дрель, перфоратор, сварочник ? Нет ? Вы уверены ?
И еще, я бы все-таки стек попробовал увеличить ...

Тогда проконсультируйте, пожалуйста, как. Я в Keile этого никогда не делал. Где это находится?

Автор: kovigor Jun 18 2018, 10:40

Цитата(Вячик13 @ Jun 18 2018, 13:37) *
Тогда проконсультируйте, пожалуйста, как. Я в Keile этого никогда не делал. Где это находится?

Сходу не припомню, давно не занимался. Вот, что нашлось за первую же минуту:

http://electronix.ru/redirect.php?http://www.keil.com/support/man/docs/rlarm/rlarm_ar_cfgstack.htm

Автор: jcxz Jun 18 2018, 10:47

Цитата(Вячик13 @ Jun 18 2018, 13:19) *
Остальные memset все просты и явны.

Вам указали на одну из тысячи возможностей. Не надо понимать буквально. Любой цикл в вашей программе, который что-то пишет или просто копирует в память, может снести всю вашу ОЗУ например при разрушении переменных, от которых зависит адрес назначения или счётчик цикла.
Симптомы указывают на вполне типичный баг в работе с указателями. Странно не знать этого после "20 проектов"...
И в борьбе с такими багами часто помогает и защита памяти и обработка всех fault-ов. Чего у Вас также явно нет. Что опять же странно после "20 проектов"... laughing.gif

Цитата(aaarrr @ Jun 18 2018, 12:50) *
memset(x, 0, y), например. Не выходит оно никуда, просто самостоятельно стирает содержимое памяти.

У меня в таких ситуациях срабатывает ловушка по записи по недопустимому адресу (записи в регион где нет ОЗУ, и поэтому закрытый от записи в MPU) когда указатель такого цикла доходит до границы ОЗУ. Автор явно не знает про MPU. laughing.gif
.... Или STM32F100 - это не Cortex-M3? Лень смотреть в даташит....

Автор: Вячик13 Jun 18 2018, 11:07

Цитата(jcxz @ Jun 18 2018, 12:47) *
Вам указали на одну из тысячи возможностей. Не надо понимать буквально. Любой цикл в вашей программе, который что-то пишет или просто копирует в память, может снести всю вашу ОЗУ например при разрушении переменных, от которых зависит адрес назначения или счётчик цикла.
Симптомы указывают на вполне типичный баг в работе с указателями. Странно не знать этого после "20 проектов"...
И в борьбе с такими багами часто помогает и защита памяти и обработка всех fault-ов. Чего у Вас также явно нет. Что опять же странно после "20 проектов"... laughing.gif


У меня в таких ситуациях срабатывает ловушка по записи по недопустимому адресу (записи в регион где нет ОЗУ, и поэтому закрытый от записи в MPU) когда указатель такого цикла доходит до границы ОЗУ. Автор явно не знает про MPU. laughing.gif
.... Или STM32F100 - это не Cortex-M3? Лень смотреть в даташит....


Нет, не лень. И могу читать даже в оригинале. Но, знаете, как в известном анекдоте про немого мальчика: "А раньше я не разговаривал, потому что всё было в порядке". Представьте себе, за 20 проектов у меня не возникало даже потребности в каких-либо ловушках, поскольку всё работало нормально.


Автор: jcxz Jun 18 2018, 11:14

Цитата(Вячик13 @ Jun 18 2018, 14:07) *
Представьте себе, за 20 проектов у меня не возникало даже потребности в каких-либо ловушках, поскольку всё работало нормально.

Ну значит - поздравляю! - Вы доросли до серьёзных проектов, сложнее мигания парой светодиодов rolleyes.gif

Автор: Вячик13 Jun 18 2018, 11:20

Цитата(jcxz @ Jun 18 2018, 13:14) *
Ну значит - поздравляю! - Вы доросли до серьёзных проектов, сложнее мигания парой светодиодов rolleyes.gif

Спасибо, за поздравление. Наконец то оценили. А то с 1983 года, когда я сделал свой первый проект на К1-20 никто ни одного доброго словечка!

Автор: jcxz Jun 18 2018, 11:38

Цитата(Вячик13 @ Jun 18 2018, 14:20) *
Спасибо, за поздравление. Наконец то оценили. А то с 1983 года, когда я сделал свой первый проект на К1-20 никто ни одного доброго словечка!

Понятно, что воспоминания о молодости и какие раньше были деревья зелёные процессоры хорошие и изученные - Вам дороги.
Но чтобы не жить воспоминаниями о былом, а делать устройства на современном уровне, надо всё-таки стараться изучать эти самые современные МК. А в используемых Вами Cortex-M уже лет так 10 точно (не 83-й канеш, куда там wink.gif наличествует MPU, который подобные ошибки с заполнением памяти константой часто позволяет находить на раз.
И другие разработчики, уже тоже лет 10 как, его успешно используют. А также есть fault-ы, которые тоже помогают в ловле багов.

Автор: k155la3 Jun 18 2018, 12:56

Цитата(Вячик13 @ Jun 18 2018, 10:53) *
. . .В программе используется WDT IWDG, но это не его рук дело. Эти явления происходят на всех 3 устройствах. . . .
Пока не найдена причина, я не был бы так уверен. Отключите вообще WD или задаайте часовой таймаут.
При рестарте анализируйте соотв-ий регистр, где фиксируется причина рестарта. (не знаю как ОНО в ARM называется).
Причиной, кроме WD, может быть авария по питанию (при этом узел контроля питания сгенерирует прерывание или ресет) и, возможно, и.т.д.

Автор: Obam Jun 19 2018, 14:47

Цитата(jcxz @ Jun 18 2018, 14:38) *
А в используемых Вами Cortex-M уже лет так 10 точно (не 83-й канеш, куда там wink.gif наличествует MPU...

В STM32F100, используемом ТСом, MPU нет wink.gif 10 лет, не 10, а вот нет sad.gif

Автор: jcxz Jun 19 2018, 15:05

Цитата(Obam @ Jun 19 2018, 17:47) *
В STM32F100, используемом ТСом, MPU нет wink.gif 10 лет, не 10, а вот нет sad.gif

Это почему это? STM32F100 - это же вроде обычный Cortex-M3. Почему тогда там нет MPU?

Автор: Obam Jun 19 2018, 15:14

Цитата(jcxz @ Jun 19 2018, 18:05) *
Это почему это? STM32F100 - это же вроде обычный Cortex-M3. Почему тогда там нет MPU?

Дык не реализовали; 100-й, 103-й, 105-й, 107-й (надысь ds смотрел) без MPU. В ProgrammingGuide на F100xx/F20xx/L1xxx английским по белому - "за наличие MPU смотрите в ds на конкретный контроллер". Вот L152 с MPU, о чём и в "features" и в общем описании отмечено.

Автор: редактор Jun 19 2018, 16:59

склоняюсь к версии "дикого" указателя (неверное значение адреса или размера блока для копирования). Уж больно симптомы к этому располагают - ОЗУ зачищается (частично), а код не перезапускается (нет начального тестирования по словам ТС -значит сработал не RESET - и WDT с питанием скорее всего ни при чем). Возможно, неверный указатель на функцию забрасывает исполнение в случайное место.

Автор: Михась Jun 21 2018, 12:34

Цитата(редактор @ Jun 19 2018, 23:59) *
склоняюсь к версии "дикого" указателя (неверное значение адреса или размера блока для копирования). Уж больно симптомы к этому располагают - ОЗУ зачищается (частично), а код не перезапускается (нет начального тестирования по словам ТС -значит сработал не RESET - и WDT с питанием скорее всего ни при чем). Возможно, неверный указатель на функцию забрасывает исполнение в случайное место.


Есть полезная функция в Кейле
http://electronix.ru/redirect.php?http://www.keil.com/support/man/docs/armcc/armcc_chr1359124940593.htm

7.136 --protect_stack, --no_protect_stack
Inserts a guard variable onto the stack frame for each vulnerable function.

The guard variable is inserted between any buffers and the return address entry.
A function is considered vulnerable if it contains a vulnerable array. A vulnerable array is one that has:
Automatic storage duration.
A character type (char or wchar_t).
In addition to inserting the guard variable and check, the compiler also moves vulnerable arrays to the top of the stack, immediately preceding the guard variable. The compiler stores a copy of the guard variable's value at another location, and uses the copy to check that the guard has not been overwritten, indicating a buffer overflow.

Код
/*******************************************************************************
*     Функции проверки переполнения стека
        Parameter: none
        Return:
КЛЮЧ КОМПИЛЯТОРА
--protect_stack
*******************************************************************************/
void * __stack_chk_guard = (void *)(0xDEADBEEF);     // initialize guard variable

// Called by stack checking code if guard variable is corrupted
void __stack_chk_fail(void)
{
    // переполнение стека
}

// тестер
void teststack(void)
{
    char buf[4];
    strcpy(buf, "123456"); // больше размера буфера
}





Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)