Страница 1 из 1

AVR - проблема прерываний и точного времени

Добавлено: Пт авг 12, 2011 12:45:21
tabr
исходник:

Код: Выделить всё

#include <mega128.h>
#include <delay.h>

#define BUTTON_READY    PORTA.0
#define BEEP      PORTA.1

int unsigned WaitCounter = 0;
int unsigned iHzCounter   = 0;
int unsigned beepCounter   = 0;
bit ReadyLightStatus        = 0;
bit beepUBeep             =0;

interrupt [TIM0_COMP] void timer0_comp_isr(void)
{
iHzCounter++;
beepCounter++;
//счетчик милисекунд
 {
 if (beepCounter>50)
  {
  beepCounter=0;
  if (beepUBeep==0)
   {
   beepUBeep=1;
   BEEP=1;
   }
  else
   {
   beepUBeep=0;
   BEEP=0;
   }
  }
 if (iHzCounter >= 125)//т.е. 1 мсек
  {
   iHzCounter =0;
   WaitCounter++;
   if (WaitCounter>=500)//т.е. каждые 500 мсек (ДОЛЖНО БЫТЬ) меняем состояние
    {
    WaitCounter=0;
     if (ReadyLightStatus == 1)
      {
       BUTTON_READY=1;
       ReadyLightStatus=0;
      }
     else
      {
       BUTTON_READY=0;
       ReadyLightStatus=1;
       }
    }
  }
 }
TCNT0=0;
}
void main(void)
{
PORTA=0x00;
DDRA=0xFF;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: Normal top=FFh
// OC0 output: Disconnected
ASSR=0x00;
TCCR0=0x05;
TCNT0=0x00;
OCR0=1;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x02;
ETIMSK=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;// Global enable interrupts

#asm("sei")
while (1)
 {
 };
}
чип: atmega128
кварц: 16mHz
цель: получить внутренний ТОЧНЫЙ таймер

Он должен каждые 500мсек менять состояние, т.е. период включение(начало)-выключение-включение(начало) должен проходить за 1сек, а в реальности проходит около 2 сек.
Как заставить его давать мне точное время?
Второе прерывание? А если оно сгенерируется во время работы первого? Прервет ли оно первое?
Или отдельные часы на какой-нибудь тиньке?
ЗЫ: весь код несколько больше и в прерывании реализован ШИМ(программно) и 2 пищалки.
заранее спасибо.
UPD: обновил код, чтобы не возникало вопросов по поводу возможного упрощения.

Re: AVR - прерывания и точное время

Добавлено: Пт авг 12, 2011 12:53:59
jordan
зачем усложнять? выставите пред делитель и число сравнение так что б прерывание генерировалось каждые пол секунды

Re: AVR - прерывания и точное время

Добавлено: Пт авг 12, 2011 13:18:47
tabr
если бы все было так просто, так бы и сделал, однако я же уточнил, что реальный код несколько сложнее - там и ШИМ, и звук.(обновил первый пост).

Re: AVR - проблема прерываний и точного времени

Добавлено: Пт авг 12, 2011 13:36:34
Мастер Ломастер
не увидел обнуления WaitCounter

Re: AVR - проблема прерываний и точного времени

Добавлено: Пт авг 12, 2011 14:03:35
tabr
оЧеПяТкА. Исправил. Но проблема все еще актуальна. Неужто второй контроллер?
ЗЫ: там еще и обнуления счетчика не было(хотя тут может он и не нужен, т.к. все равно КАЖДЫЙ раз входит?).

Re: AVR - проблема прерываний и точного времени

Добавлено: Пт авг 12, 2011 15:26:39
avreal
Что-то и в исправленном исходнике ничего не понятно.
Где разрешаются прерывания? Какие?
Какая частота тактироваия микроконтроллера?
OCR0 заносится а потом, зачем-то, в самом прерывании перезаноситс другой _константой_
Поскольку и режим CTC не включен, прерывание будет происходить в установленной по OCR0 фазе, но раз в полный оборот таймера TCNT0, т.е. в 256(период таймера)*128 (прескалер) тиков тактовой частоты процессора.

Если Вы хотели по CTC установить при кварце 16МГц и прескалере 128 получить 125 кГц прерываний, то, действительно, в OCR0 нужно занести 0.

И у меня в плате это работает. Только с поправкой на кварц 7.3728 -- меняет состояние раз в около 1.1 секунды.
Если при кварце 16 МГц выходит не 0.5 сек, а 1.0, то даже и не знаю...
Может, низкий уровень оптимизации и прерывание не успевает отработать за 128 тактов кварца. Хотя должно бы, прошивка от avr-gcc отрабатывает в худшем случае раза в два с половиной быстрее, чем период прерываний. Обычно -- почти в четыре. А если заменить тип iHzCounter и beepCounter (считают до 125 и 50) на unsigned char, то обычно в 6 раз быстрее, в худшем случае почти в 4.

Re: AVR - проблема прерываний и точного времени

Добавлено: Пт авг 12, 2011 16:01:16
tabr
Пршу прощения за неточности :( . Поправил.
avreal писал(а):OCR0 заносится а потом, зачем-то, в самом прерывании перезаноситс другой _константой_
опечатка.
avreal писал(а):Если Вы хотели по CTC установить при кварце 16МГц и прескалере 128 получить 125 кГц прерываний, то, действительно, в OCR0 нужно занести 0.
хмммм....
avreal писал(а):А если заменить тип iHzCounter и beepCounter (считают до 125 и 50) на unsigned char, то обычно в 6 раз быстрее, в худшем случае почти в 4.
Спасибо, что-то я про них совсем забыл, вот только там еще куча кода.
Значит он должен успевать отрабатываться за 128 тактов... Спасибо, пойду ковырять.
avreal писал(а): Если Вы хотели по CTC установить при кварце 16МГц и прескалере 128 получить 125 кГц прерываний, то, действительно, в OCR0 нужно занести 0.
вот только при OCR0=0 - он вообще не хочет входить в прерывание :(
АГА, тоесть таймер надо выставить на 250 мГц а OCR0 = 1?
UPD: тоесть если программа не успеет выполнится за отведенное время, то мы получим эти провалы.
И в таком случае ТОЧНОЕ время реализуется на отдельном контроллере?

Re: AVR - проблема прерываний и точного времени

Добавлено: Пт авг 12, 2011 17:35:47
avreal
Всё работает при OCR0 = 0.
Полный текст смотреть тут:
try128.c
(954 байт) 481 скачивание

Код: Выделить всё

int main(void)
{
    DDRB = 0x8F;
    ASSR = 0x00;
    TCCR0 = 0x05 | (1 << WGM01);
    OCR0 = 0;
    TIMSK = (1 << OCIE0);
    sei();
    for (;;);
}
При

Код: Выделить всё

    TCCR0 = 0x05 | (1 << WGM01);
    OCR0 = 0;
и при

Код: Выделить всё

    TCCR0 = 0x04 | (1 << WGM01);
    OCR0 = 1;
получаем одинаковый период прерываний. При

Код: Выделить всё

    TCCR0 = 0x04 | (1 << WGM01);
    OCR0 = 0;
в два раза чаще, но сильно дрожат фронты от

Код: Выделить всё

    PORTB ^= 0x01;
За 64 такта уже не всегда успевает отработать.

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

Код: Выделить всё

uint8_t div_25 = 25; // Наибольший общий делитель от 50 и 125
uint8_t div_beep_2 = 2; // 2*25 = 50
uint8_t div_Hz_5 = 5; // 5*25 = 125
unsigned wait_counter = 500;

ISR(TIMER0_COMP_vect)
{
    // Тут делаем то, что нужно делать с частотой 125 кГц и точностью "получше"
    // ...
    // После того, как сделали, считаем тики
    if ( --div_25 == 0) {
        div_25 = 25;
        sei();
        // Тело этого if() выполняется раз в 25 прерываний. Это уже можно прервать и главное,
        // чтобы оно успело отработать до следующего обнуления div_25, а это масса времени!!!
        // При следующем входе в прерывание
        // Если это прерывание не успеет отработать до следующего входа, то div_25 уменьшится
        // и запишется назад, но в if() входа не будет. А мы тут ниже к div_25 уже не обращаемся.
        if (--div_beep_2 == 0) {
            div_beep_2 = 2;
            // if (beepUbeep == 0) и так далее
        }
        if (--div_Hz_5 == 0) {
            div_Hz_5 = 5;
            if (--wait_counter == 0) {  //т.е. каждые 500 мсек
                wait_counter = 500;
                // ...
                // Собственно, делитель на 5 тут уже не очень и нужен, можно сразу на 2500 делить...
            }
        }
    }
}
Но можно сделать ещё немного лучше, используя два прерывания от одного таймера.
Об этом немного позже, убегаю.

Re: AVR - проблема прерываний и точного времени

Добавлено: Пт авг 12, 2011 19:25:09
avreal
------ Делаем два прерывания от одного таймера.

Прескалера нет.
Переполнение TIMER0_OVF_vect
16000000 / 256 = 62500 Гц.

62500 / 25 = 2500 Гц (как и было при 16000000/128/50) - каждое 25-ое переполнение -- обработка бипера.

62500 / 62.5 -- секунда. Деление на 62.5 означает деление то на 62, то на 63.
Т.е. по младшему биту счётчика секунд меняем загрузку делителя,

Код: Выделить всё

if (--seconds_div == 0) {
        seconds_div = 62 + (seconds & 1);
        ++seconds;
}
Накопления погрешности нет, интервал 2 секунды точно, но промежуточные на 0.8% отличаются.
Долговременная погрешность (на уровне минут) отсутствует (определяется кварцем), если "дрожание" секунды на 8 милисекунд (можно считать, что +-4 милисекунды) устроит, то все нормально.

Инициализация
OCR0 = 64;

Прерывание TIMER0_COMP_vect
OCR0 += 128;
Возникновение прерываний COMPARE и OVERFLOW разносится во времени на 64 такта.
Переполнение -- каждый переход с 255 на 0, COMPARE -- на 64-тых и на 192-ых тактах.
Тут только быстрая работа с частотой 125 кГц, другого кода в обработчике нет, так что даже лишние регистры не сохраняются и не восстанавливаются.
Всё очень быстро.

Прерывание TIMER0_OVF_vect
За первые тактов эдак сорок сделать то, что должно поменьше дрожать (но это уже касающееся медленных вещей, даже бипер -- это период прерываний 400 микросекунд, так что двже это "поменьше" не так критично, как для 125-килогерцового прерывания), а потом разрешить прерывания, чтобы от TIMER0_COMP_vect не задерживалось.

Если других прерываний в системе нет, то 125-килогерцовое будет с дрожанием только в длинные команды call/jmp/mul.
Ну у avr-gcc ещё на время коррекции указателя стека, но это тоже на уровнее дсятка тактов.


Однако вопрос -- а зачем так вот в 125-килогерцовом прерывании делать ШИМ программно?
Ведь таймер может ШИМ-ть аппаратно, а по переполнению таймера в прерывании всё равно можно делать более медленные вещи. Подобрать прескалер такой, чтобы и ШИМ хороший, и для программных биперов переполнение подходило.

Re: AVR - проблема прерываний и точного времени

Добавлено: Сб авг 13, 2011 00:14:48
tabr
:shock: Спасибо, перевариваю....

Re: AVR - проблема прерываний и точного времени

Добавлено: Чт авг 18, 2011 11:14:30
tabr
перековырял прерывание:
1) int на char где только возможно
2) использовал делители
3) повыкидывал лишний код из прерывания
и о чудо! с задержки в 4с (оринетировочно должно было быть 1с) упала до ~1.1-1.2 сек! Оказывается, контроллер жутко не любит int!
думаю, если еще хакнуть код - выйду на 1с.
avreal писал(а):Однако вопрос -- а зачем так вот в 125-килогерцовом прерывании делать ШИМ программно?
т.к. он выглядит так:

Код: Выделить всё

if (++HzDiv10==10)
{
HzDiv10=0;
        if (++Counter>ThresholdCounter)//[10-100]
            {
            if (++LightSinCounter==50)
                LightSinCounter=0;
            Counter=0;
            }
        if (LightOnOff == 0)
            {
            if ( ++LightOffDelay >= LightSinCounter)
                {
                LightOffDelay    = 0;
                LightOnOff       = 1;
                LIGHT           = 1;
                }
            }
        else
            {
            LightOnOff=0;
            LIGHT=0;
            }
}
т.е. интенсивность моргания меняется в зависимости от ThresholdCounter, и если организовать аппаратно, на том же прерывании, беда будет. Пробовал на другом прерывании - светодиод моргал в совершенном беспорядке(хотя вполне может быть что код корявый был).

хммм....а может ли ШИМ пищать спикером??
avreal писал(а):чтобы от TIMER0_COMP_vect не задерживалось
т.е. одно прерывание может прервать другое?

Re: AVR - проблема прерываний и точного времени

Добавлено: Сб авг 20, 2011 13:02:55
YS
Оказывается, контроллер жутко не любит int!
И правда, кто бы мог подумать, что на восьмибитной системе многобайтный тип будет тормозить? :)))

На будущее - все, что только можно, считайте как unsigned char или, что то же самое, uint8_t. А, желательно, и то, что нельзя, тоже. :)

Re: AVR - проблема прерываний и точного времени

Добавлено: Сб авг 20, 2011 17:44:40
Мастер Ломастер
YS писал(а):
Оказывается, контроллер жутко не любит int!
И правда, кто бы мог подумать, что на восьмибитной системе многобайтный тип будет тормозить? :)))

На будущее - все, что только можно, считайте как unsigned char или, что то же самое, uint8_t. А, желательно, и то, что нельзя, тоже. :)
что ни фраза - перл!
1. контроллер вполне лоялен к int. собственно, ему по барабану вообще все, это компилятор мог бы как-то не так с ним работать, но он работает вполне адекватно. что действительно WinAVR очень не любит, так это long long или uint64_t - вот тут полный крах.
2. unsigned char не совсем то же самое, что uint8_t - это только для 8-битных систем так, а в общем и целом - нет.
3. рекомендация делать то, что нельзя - вообще выше всяких похвал! :)))

думаю, если автор топика включит оптимизацию -Os, все его тормоза исчезнут, как туман. а если еще немножко подумает над алгоритмом работы и исключит из обработчика прерывания лишнее - будет вообще песня.

Re: AVR - проблема прерываний и точного времени

Добавлено: Сб авг 20, 2011 19:16:27
YS
что ни фраза - перл!
Я старался. :)))
контроллер вполне лоялен к int.
Ессно. Он вообще про него не знает. Он восьмибитник, и умеет работать только с байтом за раз. А что это за байт (bool, char, bitfield), ему, ессно, пофиг. :) Только вот в int их больше одного. :)
а в общем и целом - нет
Ну, во-первых, тут восьмибитная система. А во-вторых, не поясните разницу?
рекомендация делать то, что нельзя - вообще выше всяких похвал
Да-а-а! :))) :beer:
включит оптимизацию -Os
Она изначально включена.
а если еще немножко подумает над алгоритмом работы
Ну-у-у, Вы хотите слишком многого! :)))

Re: AVR - проблема прерываний и точного времени

Добавлено: Сб авг 20, 2011 20:22:14
Мастер Ломастер
YS писал(а): А во-вторых, не поясните разницу?
char и int - это платформо-зависимые типы, т.е. стандарт не содержит четких и однозначных указаний на их размерность в битах. так как все прочие типы в Си являются производными этих типов, то вся система становится платформо-зависимой. то есть далеко не во всех системах sizeof(char) == 1 и тому подобное... разумеется, далеко не всегда sizeof(int) == 2 * sizeof(char)

а вот uint8_t - это тип стандарта С99, и при этом по этому стандарту ВСЕГДА И ВЕЗДЕ sizeof(uint8_t) == 1, sizeof(int16_t) == 2 и так далее. поэтому я и говорю, что uint8_t вовсе не совсем то же самое, что unsigned char. собственно, это говорю не я, а стандарт С99

собственно, если применять Си для написания программ, по-настоящему близких к идеалу платформонезависимости, надо использовать только эти типы строгой размерности. кстати, стандарт С99 вводит еще и ряд других "странных" типов, например uint_fast8_t или uint_fast16_t (есть и другие), применение которых по идее должно сильно помочь в написании по-настоящему оптимальных по быстроджействию программ на РАЗНЫХ платформах.

Re: AVR - проблема прерываний и точного времени

Добавлено: Пн авг 22, 2011 14:51:00
tabr
Кот уже оптимизировал. Вот только осталась погрешность - 0.5%, т.е. примерно каждые 200 секунд опаздывает:(

Re: AVR - проблема прерываний и точного времени

Добавлено: Пн авг 22, 2011 15:46:07
ploop
Кот уже оптимизировал.
:shock: фига се у тя помощники!!

Re: AVR - проблема прерываний и точного времени

Добавлено: Пн авг 22, 2011 15:48:58
YS
Кот уже оптимизировал.
Теперь Вы сами попробуйте оптимизировать, после него. :)))