РРМ индикатор

PigTail

Для начала вынести ppm_display(); из прерывания в main, пока Вы там отображаете на индикаторе что угодно может происходить, так никто не делает. И кстати на какой частоте у Вас все это происходит и что Вы в действительности смотрите канальный импульс или пакет PPM ?

EagleB3

…Ну и у ног же у Меги8 много, а задач у Вас - мало. Так используйте две ноги внешних прерываний. Перемкните их; одну (скажем, PD2) настройте на прерывание по фронту, другую (удобнее PB0 (=ICP1), но если PortB занят обслуживанием дисплея, то можно и PD3) настройте на прерывание по тылу. В Int0 будете таймер запускать, а в INT1 останавливать и ресетить.

Я когда-то делал чем-то похожий дивайс - хронограф для измерения скорости пули пневматической винтовки. Разница только в том, что у меня были два сигнала - от старт- и стоп-оптопары. Камень - ATMEGA8535, сделано было примерно так:

//Управление прерываниями
#define TOIE1  2 // Бит 2 (в регистре TIMSK)
#define TEC1   5 // Бит 5 (в регистре TIMSK)
#define INT0   6 // Бит 6 (в регистре GICR)
#define TOV1   2 // Бит 2 (в регистре TIFR)
#define ICF1   5 // Бит 5 (в регистре TIFR)
#define INTF0  6 // Бит 6 (в регистре GIFR)

#define I_T1Ovf_Enable  TIMSK |=  (1<<TOIE1) //Разрешить прер. переполнения T1
#define I_T1Ovf_Disable TIMSK &= ~(1<<TOIE1) //Запретить прер. переполнения T1
#define I_T1Cpt_Enable  TIMSK |=  (1<<TEC1)  //Разрешить прер. захвата T1
#define I_T1Cpt_Disable TIMSK &= ~(1<<TEC1)  //Запретить прер. захвата T1
#define I_INT0_Enable  GICR |=  (1<<INT0)    //Разрешить прер. INT0
#define I_INT0_Disable GICR &= ~(1<<INT0)    //Запретить прер. INT0

#define F_T1Ovf_Clr  TIFR |=  (1<<TOV1)      //Сброс флага прер. переполнения T1
#define F_T1Ovf_Set  TIFR &= ~(1<<TOV1)      //Уст. флага  прер. переполнения T1
#define F_T1Cpt_Clr TIFR |=  (1<<ICF1)       //Сброс флага прер. захвата T1
#define F_T1Cpt_Set TIFR &= ~(1<<ICF1)       //Уст. флага прер. захвата T1
#define F_INT0_Clr   GIFR |=  (1<<INTF0)     //Сброс флага прер. INT0
#define F_INT0_Set   GIFR &= ~(1<<INTF0)     //Уст. флага прер. INT0


// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void) {
   //TCCR1A=0x00; //Не нужно, ибо и так ==0.
    #ifdef OnNewton // Тестирование падающим сердечником
        TCCR1B=0x83;   // 288,000 kHz - захват с антидребезгом
    #else
        TCCR1B=0x81;   // 18432,000 kHz - захват с антидребезгом
    #endif

    I_INT0_Disable;     //Запрещаем реагировать на Int0
    I_T1Ovf_Enable;    //Разрешаем прерывание по переполнению - вдруг пропустим стоп...
    I_T1Cpt_Enable;    //Разрешаем прерывание ICP1 - ждем фронт стоп-импульса
    F_T1Ovf_Clr;        //Очищаем флаги прерываний
    F_T1Cpt_Clr;
    F_INT0_Clr;
}


// Timer 1 input capture interrupt service routine
interrupt [TIM1_CAPT] void timer1_capt_isr(void) {
    resT=ICR1;   // Сохраняем подсчитанное значение
    TCCR1B=0x00;  // Останов таймера
    TCCR1A=0x00;
    TCNT1H=0x00; // Обнуляем таймер для последующего запуска
    TCNT1L=0x00;

    /*
        Здесь процедуры обсчета результата и занесения в значения в переменную,
        которая выводится на дисплей в основном цикле программы
   */

    I_INT0_Enable;       //Разрешаем прерывание Int0 - ждем фронт старт-импульса
    I_T1Ovf_Disable;    //Запрещаем прерывание по переполнению - таймер сейчас стоит
    I_T1Cpt_Disable;    //Запрещаем прерывание ICP1 - пока нет старта, стоп не ловим
    F_T1Ovf_Clr;         //Очищаем флаги прерываний
    F_T1Cpt_Clr;
    F_INT0_Clr;
}

Я эмбеддер не больно продвинутый; может, я тут слишком сильно “дую на воду” - но дивайс мой давно и успешно работает.

Mishanya
PigTail:

Для начала вынести ppm_display(); из прерывания в main, пока Вы там отображаете на индикаторе что угодно может происходить, так никто не делает. И кстати на какой частоте у Вас все это происходит и что Вы в действительности смотрите канальный импульс или пакет PPM ?

Если я вынесу ppm_display(); из прерывания в main, как я буду обращаться к сабрутине void ppm_display(void)?
Всё проиходит с чстотой 8MHz.

Я хочу мереть положительную часть импульса от подъема до спада с приёмника, именно она отвечает за угол поворота сервы.

EagleB3

Насчет 8 МГц - это ты маненько погорячился. Не все.
16-ти битный таймер должен натикать 65535 значений не более чем за, скажем, 2.1mS (2mS+5%). Исходя из этого ты выбираешь наименьший допустимый делитель таймера. Если этот делитель =1 - ну, значит, и тик таймера у тебя тоже 8МГц.
А дисплей обновлять - это песня отдельная. Тем более, HD44780 - с собственной оперативной памятью.
Если хочешь обновлять непременно после _каждого_ пойманного импульса, то можешь поставить и в прерывание “тыла”. Но тогда, пока выполняется функция обновления дисплея, все внешние прерывания должны быть запрещены. Успеешь закончить отображение и разрешить INT0 - поймаешь следующий импульс. Не успеешь - пропустишь следующий (или следующие…) импульсы.

Только зачем так часто? Глаз все равно этого не оценит. Чаще 25 раз в секунду - только мультики рисовать.

Взгляни на обычный код, который генерит CVAVR. Там инициализация камня, потом - цикл while.
Вот внутри цикла while у тебя будет вывод на дисплей ( ppm_display(); ). Выводиться будет то, что посчитано в прерывании останова. Так часто, как это позволит содержимое прочих строк внутри while. Так что, скорее всего, еще и замедление (внутри while) ставить придется, чтобы цифры на дисплее не мельтешили. Два - три раза в секунду, а то и за значением следить не сможешь. Будут цифры мелькать, как сотые секунды на электронном секундомере. Оно тебе надо?
А вот во время этих замедлений/простоев ты будешь замерять/обсчитывать свой импульс. “Обсчитывать” - скажем, можешь поставить осреднение медианным фильтром (сохраняешь некоторое число последних замеров (условно, 10); сортируешь по возрастанию, края (условно - по три значения) отбрасываешь; по оставшимся 4-м значениям считаешь среднее арифметическое и его показываешь).

PigTail
Mishanya:

Если я вынесу ppm_display(); из прерывания в main, как я буду обращаться к сабрутине void ppm_display(void)?

Я хочу мереть … отвечает за угол поворота сервы.

Это скорее можно назвать канальным импульсом, но не важно. В основном цикле например делаете проверку lcd_1 и lcd_2, если не нуль, то вызываете вывод на дисплей, прерывание должно коротко отрабатывать событие и выплевывать ход программы обратно, при вашей куче вызываемых в прерывании функций что там творится отследить невозможно.

Boroda

В процедуре дисплея есть задержка на 200 мс. Обычно компиляторы делают её на таймере, уведомляя об это программиста только в документации. Если ppm_display рабочая, я-бы вообще без прерываний сделал, совсем их запретив. Обнуляем таймер и циклом ждём единицу на PPM ноге. По её приходу пускаем таймер, ждём обнуления ноги, и по событию сохраняем значение таймера. Полученое число отправляем в ЖК и терпеливо ждём возврата управления. И так по кольцу.
Когда код заработает правильно, его уже можно “причесать” и усложнить по необходимости.


Чтобы импульс гарантированно измерить с нарастающего фронта, перед ожиданием нарастания надо ещё добавить пустое ожидание спада.