реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> старанности в работе timer0, mega16
covsh
сообщение Jul 1 2008, 16:08
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 26
Регистрация: 7-04-05
Пользователь №: 3 947



здравствуйте.

столкнулся со следующим непонятным поведением timer0 на atmega16.
первоначально была программа, в которой, помимо всего прочего, таймер отсчитывал 5 мс по кругу.
однако после добавления куска инициализации uart, а точнее только разрешения работы передатчика, приводит к тому что таймер срабатывает раз в 10 мс.
все это я прекрасно вижу на осциллографе. стоит только убрать бит TXEN, как все становиться нормально.

я с МК работаю недавно, и на ум ничего не приходит самому.
в чем может быть дело???

далее предлагаю исходник. компилятор IAR 5.11 (все оптимизации выключены)

Код
#define ENABLE_BIT_DEFINITIONS

#include <stdio.h>
#include <inavr.h>
#include <iom16.h>


#define USART_PC


volatile int intFront;    //содержит значения фронта сигнала по которому происходит прерывание (0 - спадающий, 1 - возрастающий)


//================================================================
//На вход подается сигнал с частотой 100 Гц, длительность импульса 160 мкс
//
//
//================================================================

#pragma vector=INT0_vect  
__interrupt void int0_gen(void)
{
  if (intFront==0)
  {
    //============================================================
    //спадающий фронт сигнала
    TIMSK |= (1<<OCIE0);  //разрешения прерывания таймера по совпадению
    TCCR0  = 0x0D;        //start timer
  }
  else
  {
     //установка генерации прерывания по спадающему фронту фронту
     intFront = 0;
      
     GICR  &= ~(1<<INT0);                //запрещение прерывания на INT0
     MCUCR |=  (1<<ISC01);               //генерация прерывания по спадающему фронту сигнала на ножже 11 (INT0)
     MCUCR &= ~(1<<ISC00);
     GICR  |=  (1<<INT0);                //разрешение прерывания на INT0
  
  }
}

//================================================================
//Обработка прерывания по совпадению таймера
//
//
//================================================================
#pragma vector=TIMER0_COMP_vect      
__interrupt void timer0_5ms(void)  
{
  
  PORTA ^= 0x01;
  TIMSK &= ~(1<<OCIE0);                //запрещение прерывания таймера по совпадению
  TCCR0  =  0x08;                      //останавливаем таймер
  //============================================================
  //установка генерации прерывания по возрастающему фронту
  intFront = 1;
  GICR  &= ~(1<<INT0);                //запрещение прерывания на INT0
  MCUCR |= (1<<ISC01) | (1<<ISC00);   //генерация прерывания по нарастающему фронту сигнала на ножке 11 (INT0)
  GICR  |= (1<<INT0);                 //разрешение прерывания на INT0
  
}


void timer0_init(void)               //таймер на 5 мс
{
TCCR0 = 0x00;  //stop
TCNT0 = 0x00;  //set count
OCR0  = 0x24;  //set compare
TCCR0 = 0x08;  //пока остановлен
//TCCR0 = 0x0D; //start timer
}



void uart_pc_init(void)
{
   UCSRB = 0x00; //disable while setting baud rate
   UCSRA = 0x00;
   UCSRC = (1<<URSEL) | 0x06;
   UBRRL = 0x03; //set baud rate lo
   UBRRH = 0x00; //set baud rate hi
   UCSRB = 0x18; // !!!! после включения передатчика и начинаются все казусы
}


int main( void )
{
  __disable_interrupt();                //disable all interrupts
  DDRA |= (1<<PA0);                     //PortA0 as output
  timer0_init();
  void uart_pc_init(void)

  intFront = 0;                         //сигнал того что первоначально прерывание по спадающему фронту
  MCUCR |= (1<<ISC01);                  //генерация прерывания по спадающему фронту на ножке 11 (INT0)
  GICR  |= (1<<INT0);                   //разрешение внешнего прерывания
  __enable_interrupt();                 //разрешения всех прерываний
  
  while(1)
  {
  
  }

}
Go to the top of the page
 
+Quote Post
Александр Куличо...
сообщение Jul 1 2008, 16:54
Сообщение #2


Местный
***

Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017



А что размещено по вектору передатчика UART?

IMHO, участок кода
Код
     GICR  &= ~(1<<INT0);                //запрещение прерывания на INT0
     MCUCR |=  (1<<ISC01);               //генерация прерывания по спадающему фронту сигнала на ножже 11 (INT0)
     MCUCR &= ~(1<<ISC00);
     GICR  |=  (1<<INT0);                //разрешение прерывания на INT0

следовало бы заменить на
Код
     MCUCR |=  (1<<ISC01);               //генерация прерывания по спадающему фронту сигнала на ножже 11 (INT0)
     MCUCR &= ~(1<<ISC00);
     GIFR  |=  (1<<INTF0);                // сброс флага, который мог установиться при смене активного фронта
     GICR  |=  (1<<INT0);                //разрешение прерывания на INT0 (при необходимости)

Т.е. в прерываниях (когда флаг I очищен) не обязательно временно запрещать разрешение прерывания от какого-либо устройства (в данном случае от INT0). А вот при смене режима его работы (смены активного фронта) может выставиться флаг прерывания(INTF0), который надо очищать.
Аналогично следует поступить и с куском кода по вектору TIMER0_COMP_vect

Добавлено:
Проглядел. Снимаю вопрос про вектор прерывния передатчика smile.gif
Почему бы UCSRB = 0x18; не заменить на UCSRB = 1<<RXEN|1<<TXEN ??? И Вам понятней будет (особенно когда вернетесь к данному куску кода через недельку), и ошибок меньше, и нам в даташит лишний раз заглядывать не нужно.
Go to the top of the page
 
+Quote Post
covsh
сообщение Jul 1 2008, 17:39
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 26
Регистрация: 7-04-05
Пользователь №: 3 947



Цитата(Александр Куличок @ Jul 1 2008, 20:54) *
Т.е. в прерываниях (когда флаг I очищен) не обязательно временно запрещать разрешение прерывания от какого-либо устройства (в данном случае от INT0). А вот при смене режима его работы (смены активного фронта) может выставиться флаг прерывания(INTF0), который надо очищать.
Аналогично следует поступить и с куском кода по вектору TIMER0_COMP_vect

спасибо за рекомендации, исправлю.
но все таки, разве это могло служить причиной того что таймер начал взводится через раз (раз в 10 мс), при добавление просто куска инициализации UART?
Цитата
Добавлено:
Проглядел. Снимаю вопрос про вектор прерывния передатчика smile.gif
Почему бы UCSRB = 0x18; не заменить на UCSRB = 1<<RXEN|1<<TXEN ??? И Вам понятней будет (особенно когда вернетесь к данному куску кода через недельку), и ошибок меньше, и нам в даташит лишний раз заглядывать не нужно.

это кусок оставший от генератора начального кода из CodeVisionAVR smile.gif сам предпочитаю, естественно, вышеизложенную запись
Go to the top of the page
 
+Quote Post
Александр Куличо...
сообщение Jul 1 2008, 17:53
Сообщение #4


Местный
***

Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017



Цитата
но все таки, разве это могло служить причиной того что таймер начал взводится через раз (раз в 10 мс), при добавление просто куска инициализации UART?


Нет, это не причина. Но для выяснения причины Вам не помешало бы описать, что вы хотите получить в конечном результате. И еще мне непонятно, что означает "таймер начал взводится через раз (раз в 10 мс)"? Увеличился период от запуска до останова таймера или же он стал запускаться в 2 раза реже?
Бегло проанализировав код и комментарии, могу сказать, что при отрицательной полярности импульса на INT0 таймер будет запускаться после каждого второго спада на INT0 (каждых 20мс) и через 5мс инвертировать порт РА0.
Если полярность импульса положительная, то таймер будет запускаться при каждом спаде на INT0 (т.е. каждых 10мс/100Гц). Но тогда не совсем понятно, зачем настраивать прерывание INT0 по фронту только для того, чтобы после поступления этого самого фронта перенастроить INT0 на спад сигнала?
Go to the top of the page
 
+Quote Post
covsh
сообщение Jul 1 2008, 19:32
Сообщение #5


Участник
*

Группа: Участник
Сообщений: 26
Регистрация: 7-04-05
Пользователь №: 3 947



Цитата(Александр Куличок @ Jul 1 2008, 21:53) *
Нет, это не причина. Но для выяснения причины Вам не помешало бы описать, что вы хотите получить в конечном результате.

на прикрепленном файле я нарисовал что должно быть. на вход контроллера подается ШИМ с периодом 10 мс. мне важны два момента: 5 мс после спадающего фронта и 160 мкс после возрастающего. в этим промежутки времени необходимо делать некоторые вычисления.
Цитата
И еще мне непонятно, что означает "таймер начал взводится через раз (раз в 10 мс)"? Увеличился период от запуска до останова таймера или же он стал запускаться в 2 раза реже?

у меня создалось такое ощущение, т.к. раньше каждый раз когда срабатывал таймер он инвертировал ножку порта, т.е. через каждые 5 мс. а после включения USART по осциллографу видно что ножда стола инвертироваться каждые 10 мс.
Цитата
Бегло проанализировав код и комментарии, могу сказать, что при отрицательной полярности импульса на INT0 таймер будет запускаться после каждого второго спада на INT0 (каждых 20мс) и через 5мс инвертировать порт РА0.

что-то я не могу понять, в моей задумке таймер должен срабатывать только при спадающем фронте
Цитата
Если полярность импульса положительная, то таймер будет запускаться при каждом спаде на INT0 (т.е. каждых 10мс/100Гц). Но тогда не совсем понятно, зачем настраивать прерывание INT0 по фронту только для того, чтобы после поступления этого самого фронта перенастроить INT0 на спад сигнала?

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

вообщем надеюсь по картинке все сразу станет понятно smile.gif
Эскизы прикрепленных изображений
Прикрепленное изображение
 
Go to the top of the page
 
+Quote Post
Александр Куличо...
сообщение Jul 1 2008, 19:43
Сообщение #6


Местный
***

Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017



Цитата
что-то я не могу понять, в моей задумке таймер должен срабатывать только при спадающем фронте

Это справедливо для *отрацательной* полярности импульса. (для tL < tH). Для положительной, как я и писал, срабатывать должно при каждом спаде.
Цитата
у меня создалось такое ощущение, т.к. раньше каждый раз когда срабатывал таймер он инвертировал ножку порта, т.е. через каждые 5 мс. а после включения USART по осциллографу видно что ножда стола инвертироваться каждые 10 мс

Так она и должна инвертироваться каждых 10 мс (через 5 мс после спада, период спадов = 10мс). Мне непонятно почему у Вас при выключенном UART'е ножка инвертируется каждых 5мс.
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Jul 1 2008, 20:01
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



На мой взгляд.
1) TCNT тоже надо обнулять, раз ты его в режиме одновибратора пускаешь. Иначе не точно будет как минимум.
2) Я бы не делал пинг-понг с прерываниями. То есть - в прерывании INT0 обработал бы сразу оба фронта и переключение там же. А в прерывании от таймера - только таймерную часть. Кто знает - может дребезг какой.
Go to the top of the page
 
+Quote Post
covsh
сообщение Jul 1 2008, 20:34
Сообщение #8


Участник
*

Группа: Участник
Сообщений: 26
Регистрация: 7-04-05
Пользователь №: 3 947



Цитата(Александр Куличок @ Jul 1 2008, 23:43) *
Это справедливо для *отрацательной* полярности импульса. (для tL < tH). Для положительной, как я и писал, срабатывать должно при каждом спаде.

может и дурацкий вопрос, но все же:что такое полярность импульсов в данном случае и какая она в моем примере?
Цитата
Так она и должна инвертироваться каждых 10 мс (через 5 мс после спада, период спадов = 10мс). Мне непонятно почему у Вас при выключенном UART'е ножка инвертируется каждых 5мс.

ошибся, действительно ножка должна инвертироваться каждые 10 мс. так и происходит без включенного USART.
со включенным период инвертирования 20 мс - вот в чем и загвоздка
Go to the top of the page
 
+Quote Post
Александр Куличо...
сообщение Jul 1 2008, 21:37
Сообщение #9


Местный
***

Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017



Цитата
может и дурацкий вопрос, но все же:что такое полярность импульсов в данном случае и какая она в моем примере?

Так как Вы пишете, что "длительность импульса 160 мкс", то по рисунку, приведенному выше, полярность - положительная

Приведенный Вами листинг полный? Если это так, то по поводу 20 мс уж и не знаю, что сказать.
Если нет, то пока единственное, что приходит на ум - это то, что программа не успевает переключиться на фронт/спад сигнала ДО его прихода. Опровергнуть/подтвердить это помогут 2хлучевой осциллограф и инверсия какого-либо пина при каждом фронте INT0.

Кстати, а что за void uart_pc_init(void) в теле main?
Go to the top of the page
 
+Quote Post
covsh
сообщение Jul 2 2008, 09:53
Сообщение #10


Участник
*

Группа: Участник
Сообщений: 26
Регистрация: 7-04-05
Пользователь №: 3 947



Цитата(Александр Куличок @ Jul 2 2008, 01:37) *
Так как Вы пишете, что "длительность импульса 160 мкс", то по рисунку, приведенному выше, полярность - положительная

Приведенный Вами листинг полный? Если это так, то по поводу 20 мс уж и не знаю, что сказать.
Если нет, то пока единственное, что приходит на ум - это то, что программа не успевает переключиться на фронт/спад сигнала ДО его прихода. Опровергнуть/подтвердить это помогут 2хлучевой осциллограф и инверсия какого-либо пина при каждом фронте INT0.

да, приведенный текст программыполный.
спасибо за советы, проблема решилась добавлением строчки при перенастройки прерываний
Код
GIFR  |=  (1<<INTF0);     // сброс флага, который мог установиться при смене активного фронта

по двухлучевому осциллографу видно что прерывание по спадающему фронту без этой строки и включенном USART срабатывало через раз.
теперь же все нормально.
однако в чем причины я не совсем понимаю.
Цитата
Кстати, а что за void uart_pc_init(void) в теле main?

это случайно попала когда постил текст программы smile.gif

вообщем большое спасибо!
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Jul 2 2008, 10:35
Сообщение #11


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(covsh @ Jul 2 2008, 12:53) *
да, приведенный текст программыполный.
спасибо за советы, проблема решилась добавлением строчки при перенастройки прерываний
Код
GIFR  |=  (1<<INTF0);     // сброс флага, который мог установиться при смене активного фронта


Неправильно!
Эдак Вы все выпавшие флаги прерывания очистите при перенастройке. И будут очередные поиски глюков.

Код
GIFR  =  (1<<INTF0);     // сброс флага, который мог установиться при смене активного фронта


Так что мне тоже спасибо smile.gif
Go to the top of the page
 
+Quote Post
defunct
сообщение Jul 2 2008, 15:45
Сообщение #12


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Что-то я не понял зачем намудрено с переключением фронтов?

Нельзя просто запускать таймер по конкретному одному фронту, в том случае если таймер не запущен.

e.g.

Код
INT0_ISR()
{
   if (!TimerRunning)
       StartTimer();
}


Timer_ISR()
{
     PORTA = ...;
     StopTimer();
}


хотя для антидребезга IMHO будет лучше просто перезапускать таймер по INT0 всегда (закоментировать if (!TimerRunning).
Go to the top of the page
 
+Quote Post
Александр Куличо...
сообщение Jul 2 2008, 22:41
Сообщение #13


Местный
***

Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017



Цитата
Эдак Вы все выпавшие флаги прерывания очистите при перенастройке

Согласен, с моей стороны глупая и грубая ошибка. Вам и от меня спасибо smile.gif

Кстати, я вполне согласен с SasaVitebsk насчет пинг-понга с прерываниями. Лучше, действительно, настроить INT0 на оба фронта, и в самом прерывании по состоянию пина INT0(PD2) определять, какой фронт пришел. Правда, это реализуемо только в случае, если время от-фронта-до-фронта сигнала меньше промежутка времени от фронта сигнала до считывания состояния пина INT0(PD2):
Код
__interrupt void int0_gen(void)
{
  if (PIND_Bit2 == 0)
  {
    // сигнал "упал" в 0
    TIMSK |= (1<<OCIE0);  //таймер по совпадению
    TCCR0  = 0x0D;        //start timer
  }
  else  // сигнал "вырос" в 1
  {
    ....
  }
}

void main(void)
{
......
// прерывание по обеим фронтам
  MCUCR |= 1<<ISC00;
  MCUCR &= ~(1<<ISC01);
  GICR  |= (1<<INT0);                   //разрешение внешнего прерывания
  __enable_interrupt();                 //разрешения всех прерываний
.......
}


p.s. intFront типа int - многовато для 8-битного МС smile.gif
Go to the top of the page
 
+Quote Post
covsh
сообщение Jul 3 2008, 17:08
Сообщение #14


Участник
*

Группа: Участник
Сообщений: 26
Регистрация: 7-04-05
Пользователь №: 3 947



Цитата(_Pasha @ Jul 2 2008, 14:35) *
Неправильно!
Эдак Вы все выпавшие флаги прерывания очистите при перенастройке. И будут очередные поиски глюков.

Код
GIFR  =  (1<<INTF0);     // сброс флага, который мог установиться при смене активного фронта


Так что мне тоже спасибо smile.gif

а объясните мне, начинающему почему надо писать так, а не GIFR |= (1<<INTF0)?
почему именно единицу это понятно, так в даташите написано.
ведь насколько я понимаю, именно такая команда включает бит INTFO, а предложенная вами запишет 01000000 (Bit 6 – INTF0: External Interrupt Flag 0 )

или я что-то путаю?
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Jul 3 2008, 17:36
Сообщение #15


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(covsh @ Jul 3 2008, 21:08) *
или я что-то путаю?


Путаете GIFR с GICR smile.gif
Для того чтобы очистить флаг прерывания надо записать туда 1. Если же туда записать че-нить другое smile.gif, флаг останется в "1".
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th July 2025 - 14:19
Рейтинг@Mail.ru


Страница сгенерированна за 0.01531 секунд с 7
ELECTRONIX ©2004-2016