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

EagleB3
Mishanya:

Хочу сделать РРМ индикатор с тестером серво машинок.
На данный момент я стою в самом начале изучения AVRов.

Загляни сюда. Исходники там есть, а превратить декодер в сервотестер, КМК, задачка в самый раз для начинающего.

Mishanya

Спасибо, почитаю. Жаль, что коментарии не видно, белеберда какая-то.

VRV
Mishanya:

Спасибо, почитаю. Жаль, что коментарии не видно, белеберда какая-то.

в блокноте открой-скорее всего с кодировкой трабл
а вооще сам проект для разбора не самый удачный(хотя если разберешся-останеться ток жки подцепить)

8 days later
Mishanya

Таймер бежит, результат на LCD каждый раз другой и разброс большой.
Где-то ошибаюсь. Может, подскажите где?
Прошу сильно не ругать.

#include <mega8.h>
#include <delay.h>
#include <stdio.h>
#include <lcd.h>

void ppm_display (void);

char lcd_buffer[33];
int lcd_1,lcd_2;

// Alphanumeric LCD Module functions
#asm
.equ __lcd_port=0x18 ;PORTB
#endasm

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
if (PIND.2 == 1)
{
TIMSK=0x04; // Timer1 Overflow Interrupt Enable
TCCR1A=0x00; // Timer1 Control Register A
TCCR1B=0x01; // Start Timer1
TCNT1H=0x00; // Timer1 TCNT1H
TCNT1L=0x00; // Timer1 TCNT1L
}
else
{
TCCR1B=0x00; // stop Timer1
lcd_1 = TCNT1L;
lcd_2 = TCNT1H;
ppm_display();
}
}

//Timer 0 interrupt
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{

}

void main(void)
{
// Port B initialization
PORTB=0xFF;
DDRB=0xFF;

// Port C,D initialization
PORTC=0x00;
DDRC=0x00;
PORTD=0x00;
DDRD=0x00;

// Timer/Counter 1 initialization
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// External Interrupt(s) initialization
GICR|=0x40; // INT0: On
MCUCR=0x01; // INT0 Mode: Any change
GIFR=0x40; // INT1: Off

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x04;

lcd_init(16); // LCD module initialization

#asm(“sei”) // Global enable interrupts

while (1)
{
}
}

void ppm_display (void)
{
#asm(“cli”)
lcd_gotoxy(0,0);
sprintf(lcd_buffer,“PPM L=%dms”,lcd_1);
lcd_puts(lcd_buffer);
lcd_gotoxy(0,1);
sprintf(lcd_buffer,“PPM H=%dms”,lcd_2);
lcd_puts(lcd_buffer);
delay_ms(200); //delay 200ms
lcd_clear(); //Lcd clear
lcd_1 = 0x00;
lcd_2 = 0x00;
#asm(“sei”)
}

VRV

прочитай 10 пост.
а вобще то сервртестер сам предполагает генерацию импульсов(те передатчик ненужен!), т.о. осушествляется генерация импульсов длительностью 1-2 мкс(например от данных крутилки->ацп) и периодом 20 мс.

Mishanya
VRV:

а вобще то сервртестер сам предполагает генерацию импульсов(те передатчик ненужен!), т.о. осушествляется генерация импульсов длительностью 1-2 мкс(например от данных крутилки->ацп) и периодом 20 мс.

В том-то и дело, что мне передатчик нужен. А 10й пост я читал, но только мне он не как не помог.

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 ноге. По её приходу пускаем таймер, ждём обнуления ноги, и по событию сохраняем значение таймера. Полученое число отправляем в ЖК и терпеливо ждём возврата управления. И так по кольцу.
Когда код заработает правильно, его уже можно “причесать” и усложнить по необходимости.


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