Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Несколько прерываний
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
Ruffian
Нужно ловить два сигнала от датчиков (для простоты прямоугольные).
Первый сигнал нужно измерить его длительность.
Для второго сигнала просто считать количество (при каждом импульсе инкремент некоей переменной).
Решил повесить эти два сигнала на INT0 и INT1. Но как разрулить - не пойму.
Если по INT0 начинать считать длительность, то для этого нужен ещё и таймер. Значит третье прерывание добавляется. А что будет, если придет прерывание INT1?
В общем каша образовалась.
Может проще сделать вообще без прерываний, тупо в цикле опрашивать порт и смотреть, появились импульсы или нет и реагировать, если они появились?
Что посоветуете?
AHTOXA
Цитата(Ruffian @ Mar 30 2011, 18:12) *
Что посоветуете?

Для измерения длительности сигналов есть специально предназначенные модули захвата (input capture).
Ruffian
Цитата(AHTOXA @ Mar 30 2011, 16:40) *
Для измерения длительности сигналов есть специально предназначенные модули захвата (input capture).


Нашёл пример, перевел его с AVRStudio на mikroc. Работать отказывается. Весь вечер просидел с ним.
Думаю, может ошибся когда переводил, взял оригинальный код и собрал в AVRStudio.

Код
#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h>

// Input: Square wave on ICP PIN
// This program determines the pulse width of a square wave and represents that pulse width
//in a certain form in port D

double overflow_counter, rising_edge, falling_edge, pulse_width;
double TOP = 65535;

//This interrupt service routine calculates the pulse width of a square wave on the ICP pin
//and displays that in a certain form on PORT D

ISR(TIMER1_CAPT_vect){
    
    if (PORTA == 0xAA) PORTA = 0x55;
    else PORTA == 0xAA;

    // If ICP pin is set, there was a rising edge else if its low there must have been a falling edge /
    if (bit_is_set(PINB,1)){
        rising_edge = ICR1;
        TCCR1B &= ~(1<<ICES1); //Capture now on falling edge
        overflow_counter = 0;
    }
    else{
        falling_edge = ICR1;
        TCCR1B |= (1<<ICES1);//Capture now on rising edge
        pulse_width =falling_edge - rising_edge + TOP*overflow_counter;

        //Test to see if correct pulse width being calculated
        //if pulse width is greater than 35 than PD2 goes high otherwise PD3 IS HIGH
        if (pulse_width>35){
            PORTD |= (1<<PD2);
            PORTD &= ~(1<<PD3);
        }
        else{
            PORTD |= (1<<PD3);
            PORTD &= ~(1<<PD2);
        }
    }
}


ISR(TIMER1_OVF_vect){
    overflow_counter++; //increment counter when overflow occurs
}


int main(void){

    DDRB = 0;// ICP pin as input which is pin 0 on port B/

    DDRD = 0XFF; // ALL PINS on port D are set as output.

    DDRA = 0xFF;
    PORTA = 0xAA;


    sei();//enable global interrupt

    //setting up Timer control register 1

    //TOP VALUE IS 0XFFFF; (NORMAL MODE)
    TCCR1A = 0;
    TCCR1B = 0;


    //SETS PRESCALER ON 1
    TCCR1B |= (1<<CS10);
    TCCR1B &= ~(1<<CS11) & ~(1<<CS12);

    // Enable Input noise canceller and capture time on rising edge
    TCCR1B |= (1<<ICES1) | (1<<ICNC1);

    TIMSK = 0;
    TIMSK |= (1<<TICIE1); //Enable Input Capture Interrupt
    TIMSK |= (1<<TOIE1); //Enable timer overflow interrupt

    while(1){}

}



Проект настроен на 8 МГц, Атмега16. В оригинале примера сигнал читался с PB0, хотя для Timer1 должен быть вход PB1. PB0 это для Timer0. Пробовал и так и так. В конечном счёте тупо взял и запараллелил оба входа, чтобы не гадать.

Счётчик работает, прерывание TIMER1_OVF_vect срабатывает исправно, а прерывание TIMER1_CAPT_vect срабатывает только один раз, когда запускается main. И больше не вызывается. Независимо от состояния порта В.
В чём прикол - не могу понять blink.gif


-SANYCH-
Цитата
ISR(TIMER1_CAPT_vect){

if (PORTA == 0xAA) PORTA = 0x55;
else PORTA == 0xAA;


Здесь наверное лишнее одно равно. Дмаю должно быть так:

Цитата
ISR(TIMER1_CAPT_vect){

if (PORTA == 0xAA) PORTA = 0x55;
else PORTA = 0xAA;
Ruffian
Да, очепятка.
Правда это ничего не меняет. Прерывание срабатывает только раз
nk@
Цитата(Ruffian @ Mar 30 2011, 23:54) *
Да, очепятка.
Правда это ничего не меняет. Прерывание срабатывает только раз

Важное уточнение требуется. С каким периодом и с какой скважностью у Вас импульсы идут? Чтоб захват сигнала работал корректно, нужно, чтоб частота захватываемых импульсов была как минимум в 2 раза меньше тактовой частоты AVR.
Ruffian
Я конечно тупой, но не настолько rolleyes.gif чтобы об этом не думать.
Тактовая процика 8МГц. Измеряемые импульсы - меандр частотой около 10 КГц.
Какая бы частота не была, прерывание должно срабатывать при перепадах на РВ0/РВ1. Если я правильно понял суть input capture.
AHTOXA
Попробуйте для начала настроить захват на фронт, не переконфигурируя в прерывании. Получите хотя бы период.
Ruffian
К сожалению, не удается. Даже период.
Сделал иначе: подаю сигнал на INT0 и его же через инвертор на INT1. В обработчике INT0 обнуляю таймер/счётчик, в обработчике INT1 останавливаю таймер, снимаю с него количество тиков, сохраняю их и перезапуск таймера.
Правда используется два входа прерывания.
-SANYCH-
Попробуйте вычитать данные с регистра захвата таймера. Когда то у мнея была проблема с АЦП. Когда неправильно читал с регистров то потом АЦП вобще отказывалось запускаться
AHTOXA
Цитата(Ruffian @ Apr 2 2011, 01:49) *
К сожалению, не удается. Даже период.

Странно, там же нет ничего сложного:
Код
static u16    long_time;
u32 impulse_len;

void ic_init(void)
{
    TCCR1A = 0;
    TCCR1B |= _BV(ICNC1) | _BV(CS10) | _BV(ICES1);;
    TIMSK |= _BV(TICIE1) | _BV(TOIE1);
    TIFR = _BV(ICF1) | _BV(TOV1);
}

SIGNAL(SIG_OVERFLOW1)
{
    long_time++;
}

SIGNAL(SIG_INPUT_CAPTURE1)
{
    static u32 prev_ticks = 0;    
    u16 icr = ICR1;
    u32 ticks;

    LoWord(ticks) = icr;
    HiWord(ticks) = long_time;

    if ((ticks < prev_ticks) && HiWord(ticks))
        HiWord(ticks)++;

    impulse_len = ticks - prev_ticks;
    prev_ticks = ticks;
}
nk@
Цитата(AHTOXA @ Apr 2 2011, 18:10) *
Странно, там же нет ничего сложного:

Согласен, только нужно измерять длительность импульса, а не период. Правильнее будет так:
CODE

volatile uint16_t long_time;
volatile uint32_t pulse_time;
volatile uint32_t pause_time;
volatile uint8_t flag = 0;

void ic_init(void)
{
TCCR1A = 0;
TCCR1B |= (1<<ICNC1)|(1<<ICES1)|(1<<CS10); // здесь, возможно, нужно выбрать другой prescaler
TIMSK |= (1<<TICIE1)|(1<<TOIE1);
TIFR = (1<<ICF1)|(1<<TOV1);
asm("sei");
}

SIGNAL(SIG_OVERFLOW1)
{
long_time++;
if(long_time==0)
{
// overflow detected! Обработка переполнения (если нужно)
}
}

SIGNAL(SIG_INPUT_CAPTURE1)
{
uint16_t ticks;

TCNT1=0;
ticks = ICR1;
long_time = 0;

if(TCCR1B & (1<<ICES1))
{ //rising edge detected
pause_time = long_time;
pause_time = (pause_time<<16) | ticks;
TCCR1B &= ~(1<<ICES1); // switch to detect falling edge
}
else
{ //falling edge detected
pulse_time = long_time;
pulse_time = (pulse_time<<16) | ticks;
TCCR1B |= 1<<ICES1; // switch to detect rising edge
}
flag = 1; // new data arrived
}

void main_loop( void ){

while(1)
{
if(flag)
{
// Здесь обрабатываем поступившие данные
flag=0;
}
}
}

Ruffian
Дело не в том, как посчитать длительность или период.
Проблема в том, что прерывание INPUT CAPTURE не происходит вообще.
Если бы оно происходило и вызывалась функция обработки прерывания, я бы там уже разобрался бы, что и как подсчитать.
У меня заряжены два прерывания: TIMER1_CAPT_vect и TIMER1_OVF_vect.
По переполнению TIMER1_OVF_vect исправно срабатывает, а TIMER1_CAPT_vect не происходит.

Прерывания настроены так:
Код
    DDRB = 0;// ICP pin as input which is pin 0 on port B/
    sei();//enable global interrupt

    //setting up Timer control register 1

    //TOP VALUE IS 0XFFFF; (NORMAL MODE)
    TCCR1A = 0;
    TCCR1B = 0;
    //SETS PRESCALER ON 1
    TCCR1B |= (1<<CS10);
    TCCR1B &= ~(1<<CS11) & ~(1<<CS12);
    // Enable Input noise canceller and capture time on rising edge
    TCCR1B |= (1<<ICES1)| (1<<ICNC1); //| (1<<ICNC1)

    TIMSK = 0;
    TIMSK |= (1<<TICIE1); //Enable Input Capture Interrupt
    TIMSK |= (1<<TOIE1); //Enable timer overflow interrupt
    TIFR = _BV(ICF1) | _BV(TOV1);


может ещё какие флаги не установлены? В даташите вроде всё прочитал, больше никаких управляющих регистров не задействуется.
nk@
Странно. С инициализацией вроде все правильно. Может сам сигнал "плохой" - маленькая амплитуда или очень короткие импульсы?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.