Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Микроконтроллеры (MCU) и микропроцессоры (MPU) > Помогите новичку выловить ошибку[avr]


Автор: Drakeman 17.3.2014, 18:52
Здравствуйте все! Изучаю самостоятельно Си для МК. Пишу первую программу, моргать светодиодом скучно и не интересно мне да и пройдено уже в примерах, поэтому сразу решил создавать под интересующую меня тему - промоборудование. Сам электрик, электроника хобби, хотя мб и получу соотв. образование в будущем)) Суть проблемы такова(среда Atmel Studio 6.1, МК ATmega16), в дебагере код прошагивается нормально, эмулирую на портах нажатие кнопок, реагирует как задумано, но когда запускаю цикл, спустя некоторое время, даже без дергания ножек в переменные записывается черти-что(в какой момент это происходит не могу выловить). Программа под станок для изготовления сетки рабица, код базовый, то есть требует доработки, добавки функций, но дальше идти нет смысла, пока не выловил ошибку. Повторюсь нуждаюсь именно в помощи по вылавливанию ошибки, мне не нужно "запустить станок", поэтому советы а-ля "опрос кнопок бредовый", или "лучше напиши такой вот код" не воспринимаются как говорится "как есть", почему бредово, чем этот код лучше, я благодарен буду любым советам, подсказкам, но главное для меня понять где ошибка в том, что уже мною написано. Главное для меня - научиться писать, поэтому акцентируйте на моих косяках в понимании операторов, алгоритма и т. д.
Примерный алгоритм:
Вводим число, сколько витков проволоки навить.
вводится число кнопками +10 и +1, то есть ст. и мл. разряд увеличиваем(макс 99).
запускаем старт цикла, идет запуск двигателя навивки, считывается кол-во оборотов и сравнивается с заданым.
при выравнивании чисел идет остановка гл. двигателя, идет опрос концевиков гильетины и запускается двигатель гильетины.
после обрезки проволоки гильетина расходится и цикл повторяется.

Пока проблема возникает еще в холостом режиме, готовности к вводу числа, опроса кнопок и т. д.

П. С. В железе тоже опробовано, всё аналогично, возможно нужно разобраться где запрещать прерывания, но знаний не хватает, да и мб ошибаюсь. МК без кварца юзаю, фьюзы по дефолту, пока рано с этим разбираться. Код закомментирован обильно, но на укр. языке, если будет требование посижу перепишу всё. Датчик оборотов бесконтактный расчитывается(без дребезга) и без необходимости отслеживания направления вращения. Проект полностью тут:
http://webfile.ru/02104dfb5ba36588e4dc88027fbe9fa7

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

Автор: Romikgy 17.3.2014, 20:43
нужно более полное описание алгоритма ... трудно сказать что за косяки... но использовать 
Код

main();//Вихід в гол. функцію
 нельзя!!! 
ибо вы переполняете через время стек и программа идет лесом...

Автор: Drakeman 17.3.2014, 22:33
Посчитал, что так можно, так как этот переход в главную функцию происходит только при зафиксированной ошибке, а значит очень редко, но за замечание спасибо.
По алгоритму:

ISR(TIMER0_COMP_vect)
В таймере выводятся на табло числа и знаки(кол-во витков, введенных и отсчитанных, символы ошибок), в начале проверяется, не насчитало ли устройство больше витков, чем введено в памяти, если больше, то идет уход в функцию вывода ошибки с передачей ей кода ошибки датчика оборотов.

ISR (INT2_vect)
Сюда повешен датчик витков, срабатывает по лог.1.

Rozklad (char Temp)
В функции раскладывается введенное или отсчитанное по датчику число витков на разряды для вывода на табло(ввод в переменные разрядов десяток и единиц). Вначале проверяется наличие флажка ошибки и наличия отсчитанных витков. Если присутствует ошибка, то функция не выполняется, чтобы не убрать сообщение о ней с табло. Дальше, идет проверка отсчета и ввода заданого числа на =0, если обе переменных по нулям, то есть не введено число ни с датчика, ни с клавы, то выводится код готовности "St" на табло. Дальше раскладывается на десятки и единицы введенные числа с записью в соотв. переменные, когда идет отсчет с датчика, то вывод заданого невозможен.

Error (CodErr1,CodErr2)
От этой планировал отказаться, так как по сути она не нужна, но пока она записывает в переменные разрядов индикатора коды ошибок и проверяет не нажата ли кнопка "Меню", для отключения ошибок. Включает светодиод аварии при флажке ошибки. зацикливается пока на кнопке меню не будет пойман лог.1, то есть попытка отключения ошибки, если лог1 пойман, переходит в Vvod.

int Vvod(int Temp2)
Опрос кнопок. В первом IFе проверка флажка ошибки, если было перенаправление с предыдущей функции, проверяются переменные кол-ва опросов включенной и выключенной кнопки, то есть защита от дребезга контактов кнопки, если насчитает 10 раз "ON", флаг ошибки обнуляется, в противном случае возврат в функцию ошибки, то есть ложное срабатывание кнопки "Меню".
Else, то есть если в IF-е код ошибки не обнаружен, проверяется переменная "Данные", если там не ноль, то число раскладывается на десятки и единицы, чтобы к ним добавлять +1 старшего или младшего разряда.
Дальше в свиче опрашиваются кнопки, в зависимости от кода кнопки из соотв. переменной.
1 кнопка(флаг 0)
вход в меню, то есть опрос кнопок ввода десятков и единиц, плюс выход из меню и ею же отключается ошибка, но на это код выше предусмотрен, планировал потом обьединить их, пока забил.
2 кнопка(флаг 1)
прибавка +1 к десяткам к введенному числу витков
3 кнопка(флаг 2)
прибавка +1 к единицам к введенному числу витков
Этих две кнопки закомментированы неплохо, не вижу смысла дублировать тут.
4 кнопка(флаг 3)
кнопка запуска рабочего цикла, если данные введены, ошибок нет, то при нажатии на неё запускается цикл работы исполнительных механизмов.

Start ()
Тут вроде всё понятно, закомментировано и в доп. разъяснении не нуждается.

Compile ()
функция сравнения отсчитанных витков с введенными данными, включения двигателей и опрос концевиков,тоже не вижу смысла объяснять вроде закомментировано нормально, а кода кот наплакал.

Пины:
Порт А
Пины 0, 1, 2, 3 - кнопки управления.
Пин 4 Светодиод аварии(ошибки).
Пин 7 светодиод готовности к работе.
Порт В
все пины на сегменты индикаторов, кроме пина 2(так как точка на индикаторах не используется один пин свободен на порте, освободил от цифр INT2), там опрос внешних прерываний(счетчик витков).
Порт D
Пин 0 привод двигателя гильетины, прямая полярность(сжатие).
Пин 1 включение двигателя гильетины в обратной полярности(расхождение ножей).
Пины 2 и 3 концевики положения гильётины.
Пины 4 и 5 на аноды индикаторов.
Пин 7 главный привод.

Накатал простыню, ибо не нашел спойлера... smile 

Автор: xvr 19.3.2014, 14:08
Ваша функция Vvod рекурсивно вызывает саму себя. При этом условия вызова весьма и весьма развесистые. По первому взгляду на код я не могу утверждать, что рекурсия бесконечная (при каких то данных), но и не могу утверждать обратного. Могу только утверждать, что она довольно глубокая. Стек не переполнился?

Автор: Drakeman 19.3.2014, 16:20
За наводку спасибо, попытаюсь разобраться, как это определить в интерфейсе компилятора, но встречный вопрос, как на практике этого избегать? Break, continue какой-то другой оператор, как грамотно избегать этого? Не обязательно разжевывать, просто скажите каким способом Вы посоветовали бы этого избегать, а я погуглю и по непоняткам уже уточнять буду...

Автор: xvr 19.3.2014, 16:38
Цитата(Drakeman @  19.3.2014,  16:20 Найти цитируемый пост)
 как на практике этого избегать?

На практике не использовать рекурсию там, где она не нужна. IMHO вам нужен простой цикл

Автор: Drakeman 19.3.2014, 17:55
Попробую переписать, но всё же, как понять, что именно в стеке дело, гугл не помогает, есть возможность глянуть в студии именно его переполнение в симе?

Автор: Drakeman 20.3.2014, 01:53
Все-равно не помогает, избавился от вызовов функций, по стеку ничего не нашел, фз как за ним следить в симуляторе. Ладно, похоже надо всё переписывать по-другому, потом поумнев уже разобрать этот. Как бы Вы писали такой алгоритм, хотя бы структурно обрисуйте, разложение числа для вывода и сам вывод на 7-сегм. у нас есть, нужен ввод с кнопок(при чем матричная клава тут лишняя, всего 4 кнопки хватает, мб и меньше...), отсчет по ИНТ2, старт работы до выравнивания этих двух чисел.
Цитата

На практике не использовать рекурсию там, где она не нужна. IMHO вам нужен простой цикл

Циклы бывают разные...

П. С. Интересно, а возможно настроить таймер на работу от инт2 и сброс по совпадению?..

Автор: xvr 20.3.2014, 14:50
Цитата(Drakeman @  20.3.2014,  01:53 Найти цитируемый пост)
Как бы Вы писали такой алгоритм, хотя бы структурно обрисуйте,
  • Во первых сделал бы отдельныю процедуру для ожидания нажатия кнопки (любой).
  • Во вторых не делал бы разложения числа витков на единицы/десятки на каждое нажатие кнопки, а так бы его и хранил (отдельно единицы, отдельно десятки). По выходу из функции собирал в конечный счетчик
  • Упростил схему вызовов функций друг из друга, уж очень у вас это сейчас запутанно сделано
  • В прерывании не должно быть вызовов никаких функций, используемых в остновной части программы (функция Error в TIMER0_COMP_vect)
  • Вывод данных на индикатор сделал бы отдельной процедурой (или несколькими), а не присваиваниями в Rozrjad1/2 по всему коду
  • Убрал по максимуму глобальные переменные, используемые для передачи данных между функциями, по возможности локализовал бы их так, что бы использовались они только в 1й функции, а устанавливались в другой (но тоже в единственном экземпляре).
  • Все манипуляции с битами портов убрал бы в функции (или макросы), сейчас очень сложно понять, что и где делается.
  • Если у вас есть несколько глобальных состояний системы, которые влияют на всё (как у вас сейчас сделано с Warning, ON, OFF), то из них надо сделать State Machine. Но похоже у вас они на самом деле не являются такими уж глобальными, а текущее их частое использование обусловленно плохой структурированностью программы.
Для начала сделайте п1.
Как то так -
Код

unsigned char get_key(void)
{
 unsigned char i,j;
 while(PINA & 0x0F) {;}  // Wait when all keys are off
 __delay(30);                 // 30ms delay - button debouncer
 while((i=(PINA&0x0F))==0) {;} // Wait for button press
 __delay(30);                 // 30ms delay - button debouncer
 for(j=0;j<4;++j,i>>=1) // Find out bit number - button index
  if (i&1) return j;
 return 0; // Should not come here
}


Функция Vvod() должна только вводить количество требуемых витков, и возвращать их (или сохранять в глобале), при нажатии кнопки 0 или 3 - возвращаться. Запуск намотки и/или обработка Warning состояния - не ее дело
Код

// Function modifies counter (Danie) and returns button number, if not 1 or 2
unsigned char Vvod(void)
{
 unsigned char cnt_lo = Danie % 10;
 unsigned char cnt_hi = Danie / 10;
 unsigned char rv;

 for(;;)
  {
    show(cnt_hi, cnt_lo); // Put 2 digits/letters on indicator
    rv=get_key();
    switch(rv)
      {
        case 1: ++cnt_hi; if (cnt_hi>9) cnt_hi=0; break;
        case 2: ++cnt_lo; if (cnt_lo>9) cnt_lo=0; break;
        default: Danie = cnt_hi*10+cnt_lo; return rv;
      }
  }
}


Дальше уж сами  smile 

Автор: Drakeman 22.3.2014, 01:12
Сложно понимается такое построение, но не страшно, смысл ясен, спасибо, появится время попробую дописать остальное. Вопрос по show, что оно делает, не нашел в нормальном виде объяснение?

Автор: xvr 22.3.2014, 15:48
Цитата(Drakeman @  22.3.2014,  01:12 Найти цитируемый пост)
Вопрос по show, что оно делает,

Код

void show(unsigned char sym1, unsigned char sym2)
{
  Rozrjad1 = sym1;
  Rozrjad2 = sym2;
}

Автор: Drakeman 22.3.2014, 20:45
Тьфу блин, а я мозги мучаю, пытаясь допереть... smile 
Цитата

Метод Show делает видимым ранее невидимый компонент. Он задает значение true свойству Visible и проверяет, является ли видимым родительский компонент.

http://cubook.supernew.org/manual-c/methods/169-show.html

Автор: xvr 23.3.2014, 15:34
Цитата(Drakeman @  22.3.2014,  20:45 Найти цитируемый пост)
Метод Show делает видимым ранее невидимый компонент. 

Это цитата из руководства по Borland C++ Builder. Вы пытаетесь на свой ATMega16 Builder запустить?  smile 

Автор: Drakeman 23.3.2014, 16:03
Это был ответ гугла на запрос "Show в Си"))

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)