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


Автор: seatouch 5.1.2009, 22:26
Уважаемые инженеры, поздравляю Всех с наступившим Новым Годом и с наступающем Рождеством!!!

Предлагаю приколоться над работой AVR Studio и AVR-gcc. Как только программа проводит 15 измерений, проходит все проверки, посылает управляющий импульс и доходит до конца функции ISR(TIMER1_COMPA_vect), она «уходит в себя» на неопределенное время, и не останавливается нигде. Только если нажать «паузу» почему-то останавливается на  sei() ; хотя если там же поставить BreakPoint сама там не останавливается. Глюки? Или специфика работы AVR-Studio. Программа написана для atMega644.

Код

//05 января 2009
#include <avr\io.h>
#include <avr\signal.h>
#include <avr\interrupt.h>
//#include <avr\delay.h>

#define INERTZONE 3 // Зона нечувствительности.
#define LIGHTIME 40 // Задержка включения светодиода после поворота ручки (умножить на время срабатывания таймера = сек).
#define IZMNUM 15 // Проводим такое кол-во измерений перед тем как начать вращение
#define COEFF 0.0732422


volatile unsigned int iDelay, iADCResult, iADCResultOld, iADCResultMin, iADCResultMax, i, iLightDelay ;  //время задержки моргания светодиода.
volatile unsigned char cIzmCount ; //исчетчик количества измерений перед тем как начать вращение
unsigned int iADCResultArray[IZMNUM] ; //Массив в котором будут хранится измерения. 
double fCoeff ;
unsigned char cGoodToRottate ; //Устраивает ли нас точность измерений и будем ли вращать машинку.
unsigned char cRottating ; //Если 0xFF идет вращение, если 0x00 вращение не идет.
unsigned int iADCResultLoc ; 
unsigned char cADCReg ;


int main(void)
{
cRottating = 0x00 ;
fCoeff = 0.0732422 ;

//---------Настройка Таймера 0-------  
//  TCCR0A = 0x00; 
//  TCCR0B = 0x00; //стоп

//  TCNT0 = 0x00; 
//  OCR0A  = 0x4F;

//  TIMSK0 = 0x02; //прерывание по совпадению с OCR0A
//  TCCR0B = 0x04; //старт с делителем
//---------------------------------  

//---------Настройка Таймера 1-------  
//  TCCR1A = 0x00; 
  TCCR1B = 0x00; //стоп


  TCNT1 = 0x00; 
//  OCR1AH  = 0x04;
//  OCR1AL  = 0x00;
//  OCR1A = 976 ; //1 сек с делителем на 1024
//  OCR1A = 48 ; //0.05 сек с делителем на 1024
  OCR1A = 20 ; //0.02 сек с делителем на 1024


  TIMSK1 = 0x02; //прерывание по совпадению с OCR0A
  TCCR1B = 0x05; //старт с делителем на 1024
//---------------------------------  

//----------Настройка АЦП----------
DDRD = 0x0 ;
PORTD = 0x0 ;
DIDR0=0x01 ; // Использовать для АЦП будем PA0
//ADMUX = 0b11100000 ; //Внутреннее опорное напряжение 2.56В и результат со сдвигом влево.
ADMUX = 0b11000000 ; //Внутреннее опорное напряжение 2.56В и результат со сдвигом вправо.
ADCSRA = 0b10000000 ;  //Включение АЦП без прерывания.
ADCSRA |= 0x40 ; //Запускаем преобразование.
//----------------------------------

  DDRB = 0x05 ;
  PORTB = 0x01 ; //Выключаем с.д.
  iLightDelay = 0 ; //Эта переменная считает длительность горения с.д.
  cIzmCount = 0 ;  //Считает количество проведенных измерений.

  sei() ;
  while(1)
  {
  }
}

unsigned int Izmerenie(void)  //Функция измерения АЦП
{

        TCNT1 = 0x00; //reload counter value

     //Запускаем АЦП
        ADCSRA|=0x40; // как только измерение закончится 
                              //этот бит пропадет, зато установится
                              // бит ADIF.
        
        //Ждем завершения преобразования
        cADCReg = 0b01010000 & ADCSRA ;

        while (cADCReg != 0b00010000)
        {
            cADCReg = 0b01010000 & ADCSRA ;  //Делаем поразрядное "и" 
                                             //что бы оставить только 
                                             //нужные нам биты ADSC и ADIF.
        }

        //Считываем данные ADC
        iADCResultLoc = ADC ;

        return (iADCResultLoc) ;
}


ISR(TIMER1_COMPA_vect)
{
                
cGoodToRottate = 0x00 ; //
        
        
        if (cRottating == 0x00)  //Если еще идет предыдущее вращение, не делаем ничего.
        {
            if (cIzmCount<IZMNUM)  //Проводим несколько измерений (IZMNUM) и смотрим устраивает ли нас точность.
            {
             iADCResultArray[cIzmCount] = Izmerenie() ;
                cIzmCount ++ ;
            }
            else
            {  //Если измерения выполнены смотрим устраивает ли нас точность измерений.
                iADCResultMax = 0 ;
                iADCResultMin = 0xFFFF ;

                for (cIzmCount=0; cIzmCount<IZMNUM; cIzmCount++)
                {
                   //все результаты не должны сильно отличатся друг от друга
                   //Ищем максимум и минимум
                    if (iADCResultMax < iADCResultArray[cIzmCount] )
                        iADCResultMax = iADCResultArray[cIzmCount] ;
                    if (iADCResultMin > iADCResultArray[cIzmCount] )
                        iADCResultMin = iADCResultArray[cIzmCount] ;
                }
                //Пусть измерения укладываются в 15 ;
                if (iADCResultMax-iADCResultMin<15)
                {  // Если точность устраивает, расчитываем среднеарифметическое 
                   //и сравниваем со старым значением
                    iADCResult=0 ;
                    for (cIzmCount=0; cIzmCount<IZMNUM; cIzmCount++)
                    {
                        iADCResult = iADCResult + iADCResultArray[cIzmCount] ;
                    }
                    iADCResult = iADCResult/IZMNUM ;
                    cIzmCount = 0 ;
                    //Проверяем на сколько полученое значение отличается от пошлого. 
                    //Если превышает зону не чувствительности (INERTZONE) запускаем вращение машинки
                    if (iADCResult > iADCResultOld+INERTZONE || iADCResult < iADCResultOld-INERTZONE)  //Зона не чувствительности.
                    {
                        cGoodToRottate = 0xFF ; //Если все ОК запускаем вращение сервы.
                        iADCResultOld = iADCResult ;
                    }
                    
                }
            }
        }

        if (cGoodToRottate==0xFF)
        {
            //Расчиттываем задержку моргания светодиода
            iDelay = (iADCResult+1)*COEFF+37 ; //это будет время задержки моргания светодиода 

            cRottating = 0xFF ; //Устанавливаем переменную в режим вращения ;
            PORTB=PORTB & 0x04 ; //включаем светодиод (лог 0 на 1ую ножку).
        }    

        if ( cRottating == 0xFF && iLightDelay<LIGHTIME)  //Если идет вращение 
        {
            i=0;
            PORTB=PORTB | 0x04 ;  //Запускаем управляющий импульс (лог 1 на 3ю ножку)
            while(i<iDelay)
            {
                i++ ;
            }
            PORTB=PORTB & 0x01 ;    //Останавливаем управляющий импульс (лог 1 на 3ю ножку, значение ножки 1 (с.д. не меняется))
            iLightDelay ++ ;    
        }
        else
        {
            cRottating = 0x00 ;  //Выключаем вращение
            PORTB=PORTB | 0x01 ; //выключаем с.д. (лог 1 на 1ую ножку) если время его горения вышло (значение бита управления сервой останется неизменным)
            iLightDelay = 0 ;
        }

}


Автор: Mazzi 6.1.2009, 09:44
Если отладка проводится на микросхеме, то скорее всего у тебя неправильно установлены фьюзы,
зайди сюда, почитай http://ulei.by.ru/Fuses_Ru.htm

Если в симуляторе, то скорее всего ошибка в коде.

Автор: cardinal 6.1.2009, 10:38
А в main у тебя нет return 1... smile 

Автор: seatouch 6.1.2009, 12:54
Отлаживаю без использования реальной микросхемы. Только AVR-Studio.
Оказалось функция прерывания длится дольше чем интервал времени. Получается переполнение и задержка в 67108864 циклов что для отладки равно бесконечности. smile 
Поторопился тему открывать. Правильно говорят: "Утро вечера мудренее"!
Всем спасибо!

To cardinal
А принципиально, что бы был именно return 1? Обычно вроде 0 ставят? Если задумываться о хорошем тоне программирования то конечно return нужен. Но может ли он как-нибудь повлиять на данную программу?
Спасибо!

Автор: Mazzi 6.1.2009, 13:09
Цитата(seatouch @ 6.1.2009,  12:54)
А принципиально, что бы был именно return 1? Обычно вроде 0 ставят? Если задумываться о хорошем тоне программирования то конечно return нужен. Но может ли он как-нибудь повлиять на данную программу?
Спасибо!

Да это он пошутил. В контроллерных программах нет выхода из main.

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