РРМ индикатор
16-битный таймер, кварца для Ваших целей не надо, фузы ставить под внутрений генератор на 8 мгц, таймер тактировать 1000 кгц. 1тик=1 микросекунда. Из-за двухбайтовой арифметики таймер можно не сбрасывать, а просто вычислять duration=timer-old_timer; (переменные int)
Как мне в одном прирывании отличить возрастающий фронт, от спадающего?
Прочитать значение PIND.2 (INT0)
Как эта штука называется в даташитах? Чтобы поиском в даташите найти…
Input Capture Unit
скажу пару слов:
даже если с таймером разберетесь и он правилоьно определит длительность каналов- показания всеравно будут прыгать, т.к в пачке идет 6-8 импульсов, соответствующих каждому каналу. Т.о. примерный алгоритм следующий:
1- найти синхроимпульс, длительность которого больше 2.5 мс(у отдельных передатчиков на канал может идти больше 2 мс)
2-начать измерение длительности импулсов, до седующего синхро. данные измерения писать в массив,
3- вывести значения длительности для выбранного канала.
Сама програма расчета(не считая сервиса вывода на жки) займет от силы 10-20 строк и уместится в обработчике прерывания.
Для отладки попробуйте симуляцию в протеусе(отлов ошибок, да и жки тоже отладить можно:))
Хочу сделать РРМ индикатор с тестером серво машинок.
На данный момент я стою в самом начале изучения AVRов.
Загляни сюда. Исходники там есть, а превратить декодер в сервотестер, КМК, задачка в самый раз для начинающего.
Спасибо, почитаю. Жаль, что коментарии не видно, белеберда какая-то.
Спасибо, почитаю. Жаль, что коментарии не видно, белеберда какая-то.
в блокноте открой-скорее всего с кодировкой трабл
а вооще сам проект для разбора не самый удачный(хотя если разберешся-останеться ток жки подцепить)
Таймер бежит, результат на 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”)
}
прочитай 10 пост.
а вобще то сервртестер сам предполагает генерацию импульсов(те передатчик ненужен!), т.о. осушествляется генерация импульсов длительностью 1-2 мкс(например от данных крутилки->ацп) и периодом 20 мс.
а вобще то сервртестер сам предполагает генерацию импульсов(те передатчик ненужен!), т.о. осушествляется генерация импульсов длительностью 1-2 мкс(например от данных крутилки->ацп) и периодом 20 мс.
В том-то и дело, что мне передатчик нужен. А 10й пост я читал, но только мне он не как не помог.
Для начала вынести ppm_display(); из прерывания в main, пока Вы там отображаете на индикаторе что угодно может происходить, так никто не делает. И кстати на какой частоте у Вас все это происходит и что Вы в действительности смотрите канальный импульс или пакет PPM ?
…Ну и у ног же у Меги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;
}
Я эмбеддер не больно продвинутый; может, я тут слишком сильно “дую на воду” - но дивайс мой давно и успешно работает.
Для начала вынести ppm_display(); из прерывания в main, пока Вы там отображаете на индикаторе что угодно может происходить, так никто не делает. И кстати на какой частоте у Вас все это происходит и что Вы в действительности смотрите канальный импульс или пакет PPM ?
Если я вынесу ppm_display(); из прерывания в main, как я буду обращаться к сабрутине void ppm_display(void)?
Всё проиходит с чстотой 8MHz.
Я хочу мереть положительную часть импульса от подъема до спада с приёмника, именно она отвечает за угол поворота сервы.
Насчет 8 МГц - это ты маненько погорячился. Не все.
16-ти битный таймер должен натикать 65535 значений не более чем за, скажем, 2.1mS (2mS+5%). Исходя из этого ты выбираешь наименьший допустимый делитель таймера. Если этот делитель =1 - ну, значит, и тик таймера у тебя тоже 8МГц.
А дисплей обновлять - это песня отдельная. Тем более, HD44780 - с собственной оперативной памятью.
Если хочешь обновлять непременно после _каждого_ пойманного импульса, то можешь поставить и в прерывание “тыла”. Но тогда, пока выполняется функция обновления дисплея, все внешние прерывания должны быть запрещены. Успеешь закончить отображение и разрешить INT0 - поймаешь следующий импульс. Не успеешь - пропустишь следующий (или следующие…) импульсы.
Только зачем так часто? Глаз все равно этого не оценит. Чаще 25 раз в секунду - только мультики рисовать.
Взгляни на обычный код, который генерит CVAVR. Там инициализация камня, потом - цикл while.
Вот внутри цикла while у тебя будет вывод на дисплей ( ppm_display(); ). Выводиться будет то, что посчитано в прерывании останова. Так часто, как это позволит содержимое прочих строк внутри while. Так что, скорее всего, еще и замедление (внутри while) ставить придется, чтобы цифры на дисплее не мельтешили. Два - три раза в секунду, а то и за значением следить не сможешь. Будут цифры мелькать, как сотые секунды на электронном секундомере. Оно тебе надо?
А вот во время этих замедлений/простоев ты будешь замерять/обсчитывать свой импульс. “Обсчитывать” - скажем, можешь поставить осреднение медианным фильтром (сохраняешь некоторое число последних замеров (условно, 10); сортируешь по возрастанию, края (условно - по три значения) отбрасываешь; по оставшимся 4-м значениям считаешь среднее арифметическое и его показываешь).
Если я вынесу ppm_display(); из прерывания в main, как я буду обращаться к сабрутине void ppm_display(void)?
…Я хочу мереть … отвечает за угол поворота сервы.
Это скорее можно назвать канальным импульсом, но не важно. В основном цикле например делаете проверку lcd_1 и lcd_2, если не нуль, то вызываете вывод на дисплей, прерывание должно коротко отрабатывать событие и выплевывать ход программы обратно, при вашей куче вызываемых в прерывании функций что там творится отследить невозможно.
В процедуре дисплея есть задержка на 200 мс. Обычно компиляторы делают её на таймере, уведомляя об это программиста только в документации. Если ppm_display рабочая, я-бы вообще без прерываний сделал, совсем их запретив. Обнуляем таймер и циклом ждём единицу на PPM ноге. По её приходу пускаем таймер, ждём обнуления ноги, и по событию сохраняем значение таймера. Полученое число отправляем в ЖК и терпеливо ждём возврата управления. И так по кольцу.
Когда код заработает правильно, его уже можно “причесать” и усложнить по необходимости.
…
Чтобы импульс гарантированно измерить с нарастающего фронта, перед ожиданием нарастания надо ещё добавить пустое ожидание спада.
forum.modelka.com.ua/index.php?showtopic=19120&st=…
вот здесь что-то подобное обсуждают но там под компорт
а вобще можно и индикатор прикрутить