пауков и прочей робототехники ! бесплатно:
Исходники и прошивки
С графической оболочкой для програаммирования движения
Как программировать управление 8-ю сервами на AVR на таймере
и прерывании -
И программа для управления 8 servo с ПК.
< добавил avr123.nm.ru >
================================================
Тема создана по мотивам переписки меня и denissyslo по электронной почте, которую принято решение перетащить сюда, чтобы осталось для следующих "поколений"
Добавлено спустя 3 минуты 40 секунд:
denissyslo писал(а):Разбираюсь второй день - не могу сообразить как рулить сервами от портов, но
которых нет аппаратного шима (наверное я тупой). Нашел только на 8 странице
курса в примерах для меги 128, но там подключают их на ноги с аппаратным
шимом...
Хотел просить тебя дать кусок кода от твоего касательно серв контроллера на
изучение.
Добавлено спустя 31 секунду:
=DeaD= писал(а):Попробую пояснить - я вешаю на прерывание по таймеру, которое срабатывает каждые 160 тактов (с частотой 100КГц), что даёт мне разрешение 10мкс, то есть учитывая, что я должен выдать на серву сигнал шириной 1000-2000мкс (1-2мс), я имею 100 позиций сервы. то есть шаг 1.8 градуса.
SIGNAL(SIG_OVERFLOW0){
TCNT0=0x5F; //0x5F=255-160 - to get this interrupt each 160/16'000'000=1/100'000 sec
//***************************************************************************************************
//Here is servo-controlling block
//***************************************************************************************************
if(tmr<200){
uint8_t tmp=tmr; //Тут надо вычитать соответствующий отступ, а тут это ноль
if(types[0]==2){ //Тут у меня лежит тип выхода, 2 - выход на серву
if(tmp==0){ outb(PORTA,inb(PORTA) | 0x01); }; //Set PA0 pin to 1
uint16_t xxx=value[0];
if(xxx>MAXSERVO){ xxx=MAXSERVO; };
if(xxx<MINSERVO){ xxx=MINSERVO; };
if(tmp==xxx){ outb(PORTA,inb(PORTA) & 0xFE); }; //Set PA0 pin to 0
};
if(types[1]==2){
if(tmp==0){ outb(PORTA,inb(PORTA) | 0x02); }; //Set PA1 pin to 1
uint8_t xxx=value[1];
if(xxx>MAXSERVO){ xxx=MAXSERVO; };
if(xxx<MINSERVO){ xxx=MINSERVO; };
if(tmp==xxx){ outb(PORTA,inb(PORTA) & 0xFD); }; //Set PA1 pin to 0
};
};
tmr++;
if(tmr==2150){
tmr=0;
};
};
ну и инициализация прерывания:
// Initiating Timer/Counter-0 Control Registers:
//
// TCCR0 = FOC0:WGM00:COM01:COM00:WGM01:CS02:CS01:CS00
// 0 0 0 0 0 0 0 1
//
// COM1x1:COM1x0 = 0:0 => OC1x disconnected from output pin
// FOC1A:FOC1B = 0:0 => Don't force output compare event
// WGM13:WGM12:WGM11:WGM10 = 0:0:0:0 => Normal timer/counter operation
// ICNC1 = 0 => Input Capture Noise Canceller disabled (because not needed)
// ICES1 = 0 => Input Capture Edge Select set to "falling" mode (no matter)
// CS12:CS11:CS10 = 0:0:1 => Timer "on", no prescaling (1 clock every cpu tact)
// TCCR1A = 00000000 = 0x00
TCCR0=0x01;
TCNT0=0x69;
// Initiating internal program subcounter with zero
tmr=0;
// Initiating Timer/Counter Interrupt Mask register:
//
// TIMSK = OCIE2:TOIE2:TICIE1:OCIE1A:OCIE1B:TOIE1:OCIE0:TOIE0
// 0 0 0 0 0 1 0 0
//
// OCIE2 = 0 => Timer/Counter2 Output Compare Match Interrupt disabled
// TOIE2 = 0 => Timer/Counter2 Timer Overflow disabled
// TICIE1 = 0 => Timer/Counter1 Input Capture Interrupt disabled
// OCIE1x = 0 => Timer/Counter1 Output Compare Match Interrupt disabled
// TOIE1 = 0 => Timer/Counter1 Timer Overflow enabled
// OCIE0 = 0 => Timer/Counter0 Output Compare Match Interrupt disabled
// TOIE0 = 1 => Timer/Counter0 Timer Overflow disabled
TIMSK=0x01;
И разрешение на работу прерываний:
sei();
Вроде всё.
Задавай вопросы
Добавлено спустя 1 минуту 5 секунд:
denissyslo писал(а):> Антон, привет! Начинаю раскидывать твой код:
> Еще посылаю мой исходник. В нем пытаюсь рулить одной сервой на ПОРТА.1.
> Посмотри пожалуйста. Компилятор коде визионАВР. Не могу там кое-что понять.
> Ниже то как я разбирал твой код.
>
>
> Попробую пояснить - я вешаю на прерывание по таймеру, которое срабатывает
> каждые 160 тактов (с частотой 100КГц), что даёт мне разрешение 10мкс, то
> есть учитывая, что я должен выдать на серву сигнал шириной 1000-2000мкс
> (1-2мс), я имею 100 позиций сервы. то есть шаг 1.8 градуса.
>
>
> SIGNAL(SIG_OVERFLOW0){
> TCNT0=0x5F; //0x5F=255-160 - to get this interrupt each
> 160/16'000'000=1/100'000 sec !!! это пишем в блоке объявления
> переменных!!!???
>
> !!!т.к. у меня кварц 10Мгц, то будет так:
>
> Попробую пояснить - я вешаю на прерывание по таймеру, которое срабатывает
> каждые 100 тактов (с частотой 100КГц), что даёт мне разрешение 10мкс, то
> есть учитывая, что я должен выдать на серву сигнал шириной 1000-2000мкс
> (1-2мс), я имею 100 позиций сервы. то есть шаг 1.8 градуса.
>
> TCNT0=0x9B; //0x9B=255-100 - to get this interrupt each
> 100/10'000'000=1/100'000 sec
>
> Правильно?
>
>
> //**************************************************************************
> *************************
> //Here is servo-controlling block
>
> //**************************************************************************
> *************************
> if(tmr<200){ !!!тут мы проверяем - накопилось ли 20мс. В начале
> надо объявить tmr integer'ом????
>
> uint8_t tmp=tmr; //Тут надо вычитать соответствующий отступ, а тут это
> ноль !!!немного не понял - это чисто ФИЧА ТВОЙ программы. Точнее вообще не
> понял. ?????
>
> if(types[0]==2){ //Тут у меня лежит тип выхода, 2 - выход на серву
> !!!тут надо понимать тоже ПРИМОЧКА твоего контроллера!!!?
> if(tmp==0){ outb(PORTA,inb(PORTA) | 0x01); }; //Set PA0 pin to 1
> !!!тут вроде ясно - если начало цикла то устанавливаем порт А.1 как выход!!!
>
> uint16_t xxx=value[0]; !!! вот тут немного не ясно ххх - это
> задание угла сервы???? Т.е. это какая-то переменная? Т.е. мы должна в начале
> программы ее объявить, например int? Если да, то в каком формате задавать
> угол - ххх это количество тактов по 10мкс?????
>
> if(xxx>MAXSERVO){ xxx=MAXSERVO; }; !!! Тут понял - проверяем - не
> уехали ли мы за предельное значение - что бы небыло зависания проги!!!
> if(xxx<MINSERVO){ xxx=MINSERVO; }; !!! Аналогично проверка
> минимального значения, все продумано Вначале программы необходимо
> объявить MAXSERVO и MINSERVO типами int и присвоить значения, только тогда
> КАКИЕ ЗНАЧЕНИЯ??? В тиках по 10мкс???? Т.е 21 и 9 соответственно??? !!!
>
> if(tmp==xxx){ outb(PORTA,inb(PORTA) & 0xFE); }; //Set PA0 pin to 0
> !!! тут вроде разобрался - все просто если текущее значение, того сколько
> времени прошло по 10мкс (переменная tmp) равно установленному углу, то
> поускаем на нашу ножку 0! Т.е. в начале надо объявить tmp int & tmp=0;???
> };
>
> !!!Ну ниже я так понимаю все АБСОЛЮТНО так же только для следующей ножки.
> Антон, такой вопрос: ххх естественно это другая переменная и вообще имя ххх
> для читабельности лучше заменить на что-то типа servo_2 (хотя это конечно не
> принципиально). А переменная tmp для всех ножек будет одна и таже???!!!
>
> if(types[1]==2){ !!! вот этот ИФ (и выше): if(types[1]==2){};Я могу
> смело убрать? Оставивто что в скобках???!!!
>
> if(tmp==0){ outb(PORTA,inb(PORTA) | 0x02); }; //Set PA1 pin to 1
> uint8_t xxx=value[1];
> if(xxx>MAXSERVO){ xxx=MAXSERVO; };
> if(xxx<MINSERVO){ xxx=MINSERVO; };
> if(tmp==xxx){ outb(PORTA,inb(PORTA) & 0xFD); }; //Set PA1 pin to 0
> };
>
> };
>
> tmr++; !!! тут тоже вроде понял - следующий шаг - инкримент
>
> if(tmr==2150){ !!! а вот тут я действительно не понял...по идее тут могла
> быть проверка на выполнения полного цикла, но цикл то у нас 200 раз по
> 10мкс???? И вообще что за цифра в 2150 и откуда она...как говорил ДЖАМШУТ
> ПонятнА....ннепонЯтА.
> tmr=0;
> };
>
> };
>
>
> Вот этот блок с сервами я пишу в основном цикле программы (после вайл(1)
> )????? А где обнуление переменной tmp ????
>
>
>
>
> ОГО...с этим будет трудно для меня...
> Это где мы пишем?????? Это сделано генератором кода КВАРА???? Пойдет ли мне
> этот прибамбас, если у меня кварц 10, а не 16?
>
> ну и инициализация прерывания:
>
> // Initiating Timer/Counter-0 Control Registers:
> //
> // TCCR0 = FOC0:WGM00:COM01:COM00:WGM01:CS02:CS01:CS00
> // 0 0 0 0 0 0 0 1
> //
> // COM1x1:COM1x0 = 0:0 => OC1x disconnected from output pin
> // FOC1A:FOC1B = 0:0 => Don't force output compare event
> // WGM13:WGM12:WGM11:WGM10 = 0:0:0:0 => Normal timer/counter operation
> // ICNC1 = 0 => Input Capture Noise Canceller disabled (because not
> needed)
> // ICES1 = 0 => Input Capture Edge Select set to "falling" mode (no
> matter)
> // CS12:CS11:CS10 = 0:0:1 => Timer "on", no prescaling (1 clock every cpu
> tact)
> // TCCR1A = 00000000 = 0x00
> TCCR0=0x01;
>
> TCNT0=0x69;
>
>
> К этому куску аналогичные вопросы .
>
> // Initiating internal program subcounter with zero
> tmr=0;
>
> // Initiating Timer/Counter Interrupt Mask register:
> //
> // TIMSK = OCIE2:TOIE2:TICIE1:OCIE1A:OCIE1B:TOIE1:OCIE0:TOIE0
> // 0 0 0 0 0 1 0 0
> //
> // OCIE2 = 0 => Timer/Counter2 Output Compare Match Interrupt disabled
> // TOIE2 = 0 => Timer/Counter2 Timer Overflow disabled
> // TICIE1 = 0 => Timer/Counter1 Input Capture Interrupt disabled
> // OCIE1x = 0 => Timer/Counter1 Output Compare Match Interrupt disabled
> // TOIE1 = 0 => Timer/Counter1 Timer Overflow enabled
> // OCIE0 = 0 => Timer/Counter0 Output Compare Match Interrupt disabled
> // TOIE0 = 1 => Timer/Counter0 Timer Overflow disabled
> TIMSK=0x01;
>
> И разрешение на работу прерываний:
>
> sei(); !!! В кваре, кажется пишется немного по-другому...
Добавлено спустя 33 секунды:
=DeaD= писал(а):Итак по порядку:
1. Со средой CVAVR не знаком, могу только про WinAVR говорить, но регистры и порты все те же, если ты разбираешься с CVAVR - легко переделаешь под себя;
2. Переделка TCNT0 под кварц 10МГц правильная;
3. Нулевой отступ при расчете переменной tmp - это если мне надо будет рулить скажем 20-ю сервами, в этом случае мне нельзя будет сразу их все обрабатывать, потому что я тогда не уложусь скорее всего в 100 тактов которые проходят между прерываниями, поэтому я буду первые 2мс из 20мс обрабатывать первые несколько серв, потом следующие несколько и т.д. Я сейчас по 2-3 сервы обрабатываю за раз, чтобы успевать оставшееся время работать с UART'ом и движками. То есть если надо будет рулить 4 сервами, я их буду обрабатывать не в if(tmr<200), а в if(tmr>=200 && tmr<400) и там уже напишу tmp=tmr-200;
4. xxx=value[0] - это должно быть число от 100 до 199 означающее количество тактов прерывания (по 10мкс) которое будет длиться импульс для управления сервой;
5. MIN/MAXSERVO объявлять лучше через #define, а не переменными - не перегружаем программу лишними переменными;
6. Обнуление переменной tmp не нужно, поскольку она в каждом цикле заново инициализируется из tmr;