roboforum.ru

Технический форум по робототехнике.

Проблема с быстродействием ATmega168

Проблема с быстродействием ATmega168

HarryStar » 22 июл 2011, 11:56

Есть задача. Анализировать длительность сервоимпульсов.
Как обычно импульсы поступают с частотой 50 Гц, длиной от ну скажем 1000 мкс до 2000 мкс.
Делал эту задачу на АТмега32-16 Мгц на внешних прерываниях - все работает как часы, проблем нет. Точность измерения очень высокая (256 положений отрабатывает без проблем)

Возникла необходимость перенести все на АТмегу168 (там можно делать внешнее прерывание сразу по всему порту)
И тут возникли проблемы с недостаточностью быстродействия. Кварц тоже 16 Мгц, делитель 1(плюс еще во фьюзах отключил).

CVAVR Программа примерно такая:
Код: Выделить всёРазвернуть
void main(void)
{
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

PORTD=0xFF; DDRD=0x00; // In/P

// Timer/Counter 1 initialization
// Clock value: 2000,000 kHz
TCCR1A=0x00; TCCR1B=0x02;
TCNT1H=0x00; TCNT1L=0x00;
ICR1H=0x00;  ICR1L=0x00;
OCR1AH=0x00; OCR1AL=0x00;
OCR1BH=0x00; OCR1BL=0x00;

// External Interrupt(s) initialization
// Interrupt on any change on pins PCINT16-23: On
EICRA=0x00; EIMSK=0x00;
PCICR=0x04; PCMSK2=0xFF; PCIFR=0x04;

TIMSK0=0x00; TIMSK1=0x00; TIMSK2=0x00;

#asm("sei")

while (1) continue;
}

interrupt [PC_INT2] void pin_change_isr2(void)
{
    unsigned int t;
    t = (((unsigned int)TCNT1H)<<8) + (unsigned int)TCNT1L;

    if(PIND & 0b00100000)
    {
        channel_3_start = t; // Старт импульса на канале 3
        return;
    }
    if(PIND & 0b00010000)
    {
        channel_3_end = t; // Старт импульса на канале 4 и одновременно Стоп импульса на канале 3
        return;
    }
}


Проблема в том, что точность при таком измерении упала ниже плинтуса. Показания скачут в пределах 100/256, Т.е. можно более менее отследить короткий импульс 1000-1500 мкс, средний 1250-1750 и длинный 1500-2000. Т.е. вместо 256 градаций получается 3-5.

Есть большое подозрение, что хоть кварц выставлен на 16Мгц, Проц тактируется от 2Мгц или того меньше, хотя все вроде выставил как надо. Фьюзы сейчас у меня ВСЕ в 1 выставлены.

Длительность определяется как (channel_3_end - channel_3_start)/2 = длительность в мкс

В чем может быть проблема?

Re: Проблема с быстродействием ATmega168

avr123.nm.ru » 22 июл 2011, 12:00

Наверно во фьюзах и дело. Выведите тестовой прогой меандр на ножку замерьте звуковой картой его частоту и узнайте реальную частоту вашего МК.

Re: Проблема с быстродействием ATmega168

HarryStar » 22 июл 2011, 12:10

Попробовал только что не с прерыванием на весь порт, а с обычным INT1.
Тоже самое
Код: Выделить всёРазвернуть
interrupt [EXT_INT1] void ext_int1_isr(void)
{
    unsigned int t;
    t = (((unsigned int)TCNT1H)<<8) + (unsigned int)TCNT1L;

    if(PIND & 0b00001000)
    {
        channel_start[3] = t; // Старт импульса
        return;
    }
    else
    {
        channel_end[3] = t; // Стоп импульса
        return;
    }
}


Вот такой обработчик INT1 дает теже неточные данные. В точности такой же на АТмега32 на INT1 работал как часы.

Re: Проблема с быстродействием ATmega168

dccharacter » 22 июл 2011, 12:16

А зачем на ИНТ-ах это делать? CCP ворде самое оно???

Re: Проблема с быстродействием ATmega168

HarryStar » 22 июл 2011, 12:39

К сожалению не знаком с данным термином и в поиске сразу не нашлось. Это что?
Всегда думал, что для анализа длительности внешних сигналов подходят только внешние прерывания.

Если вы про внешнее прерывание на портах
PCINT0-7
PCINT8-14
PCINT16-23
То я так и делаю в первом посте. Мне как раз и надо 8 каналов анализировать. Но для начала надо разобраться с одним. Реализацию через INT1 я привел просто для примера.

Re: Проблема с быстродействием ATmega168

dccharacter » 22 июл 2011, 12:57

Capture Compare PWM

Re: Проблема с быстродействием ATmega168

HarryStar » 22 июл 2011, 15:22

Ясно. Но это мне к сожалению не подходит, т.к. каналов очень много, а таймеров у МК мало.

Добавлено спустя 57 минут 52 секунды:
Частично разобрался. Дело было в порядке чтения байтов из таймера. Я читал сначала старший, потом младший, надо было наоборот. Точнее у меня стояло просто (TCNT1H<<8) + TCNT1L и такая запись давала неправильный результат. Т.е. получалось, что старший байт от одного измерения, младший от следующего(или вообще мусор).

Но это ладно, это мне понятно, сам дурак.

Дальше вообще чудеса.

Есть у меня такой кусок кода для преобразования времени задержки из 1000 - 2000 мкс в 0-255:

исходные: в channel длительность импульса примерно от 975 до 2000 мкс

unsigned int t1;
unsigned char c;

t1 = (channel-975)/4;
c = (unsigned char)t1;

Все работает как часы. Измерение очень точные.
Но я решил ввести граничные условия для страховки.
t1 = channel;
if(t1<975) t1=975;
if(t1>1995) t1=1995;
t1 = (t1-975)/4;
c = (unsigned char)t1;

Чтоб не дай бог не вылезло за один байт на границах.

В результате полный хаос. Значения прыгают плюс минус 50 примерно. Жирную строчку если закомментировать - все ОК.

Добавлено спустя 5 минут 56 секунд:
Даже не +- 50, они скачут от текущего нормального до 255. Т.е. выделенное жирным условие иногда срабатывает когда t1 меньше 1995. И получаются скачки.

Re: Проблема с быстродействием ATmega168

=DeaD= » 22 июл 2011, 15:27

Чудеса какие-то, давай полную программу :)

Re: Проблема с быстродействием ATmega168

dccharacter » 22 июл 2011, 16:59

У тебя, похоже, channel - это знаковая переменная

Добавлено спустя 6 минут 56 секунд:
кстати, сузь границы, сделай 976 и 1994

Re: Проблема с быстродействием ATmega168

HarryStar » 22 июл 2011, 20:12

Да нет, не знаковая. Похоже глюк компилятора, другого не могу предположить.
Нижнюю границу изменить не проблема, а вот верхняя неуправляемая

Вот полный рабочий код на данный момент. Если раскоменнтировать строку, выделенную жирным то все становится плохо.
Код: Выделить всёРазвернуть
#include <mega168.h>
#include <delay.h>

#include "..\HS-LIB\TWI_DIHALT.c"

void TWI_Error();       // Реакция на ошибки передачи. Обычно ничего делать не надо.
void SlaveOutControl(); // Реакция на запрос байта. Передается значение текущего радиоканала.
void SlaveInControl();  // Реакция на прием байтов. Установка номера радиоканала.

unsigned char current_channel;
unsigned int channel_start[9];
unsigned int channel_end[9];
unsigned int channel[9];

void main(void)
{
    unsigned int t1;
    PORTB=0x00; DDRB=0x03; // 0-1 Out/0  2-7 In/T
    PORTC=0x00; DDRC=0x00; // In/T
    PORTD=0xFF; DDRD=0x00; // In/P

    // Timer/Counter 1 initialization
    // Clock source: System Clock
    // TCCR1A=0x00; TCCR1B=0x02; //  2000,000 kHz
    TCCR1A=0x00; TCCR1B=0x01; // 16000,000 kHz
    TCNT1H=0x00; TCNT1L=0x00;
    ICR1H=0x00;  ICR1L=0x00;
    OCR1AH=0x00; OCR1AL=0x00;
    OCR1BH=0x00; OCR1BL=0x00;

    // External Interrupt(s) initialization
    // Interrupt on any change on pins PCINT16-23: On
    EICRA=0x00; EIMSK=0x00; PCICR=0x04; PCMSK2=0xFF; PCIFR=0x04;

    TIMSK0=0x00; TIMSK1=0x00; TIMSK2=0x00;

    #asm("sei")

    Init_i2c(); // Запускаем и конфигурируем i2c
    Init_Slave_i2c(0b11111001, &SlaveOutControl, &SlaveInControl);
    ErrorOutFunc = &TWI_Error;

    while(1)
    {
        t1 = channel[current_channel];
        if(t1<975) t1=975;
        [b]//if(t1>1995) t1=1995;[/b]
        t1 = (t1-975)/4;
        i2c_OutBuff [0] = (unsigned char)t1;
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Отправка байта-статуса. Действий не требуется, только освобождение шины.
void SlaveOutControl()
{
    i2c_Do &= i2c_Free; // Осовобождаем шину
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Обработка ошибок. Действий не требуется.
void TWI_Error()
{
    i2c_Do &= i2c_Free; // Осовобождаем шину
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Получение команд. Установка текущего номера радио-канала.
void SlaveInControl()
{
    unsigned int t1;

    i2c_Do &= i2c_Free; // Осовобождаем шину
   
    if(i2c_InBuff[0]<10)
    {
        current_channel = i2c_InBuff[0]; // Текущий канал
        t1 = channel[current_channel];
        if(t1<975) t1=975;
        //if(t1>1995) t1=1995;
        t1 = (t1-975)/4;
        i2c_OutBuff [0] = (unsigned char)t1;
    }
}

// Pin change 16-23 interrupt service routine
interrupt [PC_INT2] void pin_change_isr2(void)
{
    unsigned int t, tH, tL;
    tL = TCNT1L;
    tH = TCNT1H;
    t = (tH<<4) + (tL>>4);

    if(PIND & 0b10000000)
    {
        channel_start[1] = t; // Старт импульса
        return;
    }
    if(PIND & 0b01000000)
    {
        channel_start[2] = t; // Старт импульса
        channel_end[1] = t; // Стоп импульса
        channel[1] = channel_end[1] - channel_start[1];
        return;
    }
    if(PIND & 0b00100000)
    {
        channel_start[3] = t; // Старт импульса
        channel_end[2] = t; // Стоп импульса
        channel[2] = channel_end[2] - channel_start[2];
        return;
    }
    if(PIND & 0b00010000)
    {
        channel_start[4] = t; // Старт импульса
        channel_end[3] = t; // Стоп импульса
        channel[3] = channel_end[3] - channel_start[3];
        return;
    }
    if(PIND & 0b00001000)
    {
        channel_start[5] = t; // Старт импульса
        channel_end[4] = t; // Стоп импульса
        channel[4] = channel_end[4] - channel_start[4];
        return;
    }
    if(PIND & 0b00000100)
    {
        channel_start[6] = t; // Старт импульса
        channel_end[5] = t; // Стоп импульса
        channel[5] = channel_end[5] - channel_start[5];
        return;
    }
    if(PIND & 0b00000010)
    {
        channel_start[7] = t; // Старт импульса
        channel_end[6] = t; // Стоп импульса
        channel[6] = channel_end[6] - channel_start[6];
        return;
    }
    if(PIND & 0b00000001)
    {
        channel_start[8] = t; // Старт импульса
        channel_end[7] = t; // Стоп импульса
        channel[7] = channel_end[7] - channel_start[7];
        return;
    }
    if(PIND == 0)
    {
        channel_end[8] = t; // Стоп импульса
        channel[8] = channel_end[8] - channel_start[8];
    }
}



Добавлено спустя 2 минуты 9 секунд:
Жирным не выделяется внутри code - Нужная строка, обрамленная тэгами [b] почти в конце mainа

Re: Проблема с быстродействием ATmega168

=DeaD= » 22 июл 2011, 20:18

А если t1 сделать знаковой?

Re: Проблема с быстродействием ATmega168

Michael_K » 22 июл 2011, 21:48

Прога написана в стиле школьника.
Заложено много потенциальных глюков.
Я не знаю, что за компилятор, но...
1. выучите слово volatile (это нужно здесь)
2. научитесь пользоваться критическими секциями (это нужно здесь)
3. я бы поточнее ставил условия внутри прерывания (это вам виднее - зависит от задачи)

Общая рекомендация:
Отлаживаться нужно с простого... Или выделить минимальный неработающий кусок. Но в случае как у вас - наличия множества неявных связей это бессмысленно. Нужно сначала избавиться от них.

З.Ы.
Я фигею с советов. Dead, вы серьезно подталкиваете форумчан к методу тыка? Не удивляйтесь потом, что электростанции разваливаются.

Re: Проблема с быстродействием ATmega168

=DeaD= » 22 июл 2011, 21:57

Для софта электростанций есть специальные подходы к верификации кода :) и они заключаются совсем не в запоминании ключевых слов типа volatile :)

Добавлено спустя 3 минуты 32 секунды:
Michael_K писал(а):1. выучите слово volatile (это нужно здесь)
2. научитесь пользоваться критическими секциями (это нужно здесь)
3. я бы поточнее ставил условия внутри прерывания (это вам виднее - зависит от задачи)

Сдаётся мне это никак не повлияет на проблему с исключительно локальной переменной t1, на которой странным образом обрабатывается условие. Но конечно попробовать можно.

Re: Проблема с быстродействием ATmega168

Michael_K » 22 июл 2011, 22:00

Может быть и не повлияет, но отлаживать программу с заложенными неоднозначностями нельзя. Вообще никак. Она сегодня работает, а завтра нет.

Re: Проблема с быстродействием ATmega168

HarryStar » 22 июл 2011, 22:43

Michael_K писал(а):1. выучите слово volatile (это нужно здесь)
2. научитесь пользоваться критическими секциями (это нужно здесь)
3. я бы поточнее ставил условия внутри прерывания (это вам виднее - зависит от задачи)

Программистом я работал очень давно (более 10 лет назад), с тех пор это для меня только хобби. Поэтому стиля придерживаюсь того, который мне нравится. Не думаю, что он похож на стиль школьника, но возможно с ваших высот они и не отличаются.

По сути: 1 знаю прекрасно, не ставил, потому что оптимизация отключена и проблем с глобальными переменными, изменяющимися в нескольких местах никогда не было.
2 - не понял, что вы имеете в виду
3 - в прерывании условия сделаны четко, основаны на осциллограммах работы приемника, с которого обрабатываются сигналы.

Не вижу в программе ни одного неоднозначного места, если подскажете, с удовольствием прокомментирую или приму к сведению.

Единственное, что я нашел - while в майне можно вообще убрать, поставив на его место while(1);
Хотя смысл в нем есть, и он ничему не мешает.


Rambler\'s Top100 Mail.ru counter