|
старанности в работе timer0, mega16 |
|
|
|
Jul 1 2008, 16:08
|
Участник

Группа: Участник
Сообщений: 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) { }
}
|
|
|
|
|
Jul 1 2008, 16:54
|
Местный
  
Группа: Свой
Сообщений: 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 Добавлено: Проглядел. Снимаю вопрос про вектор прерывния передатчика  Почему бы UCSRB = 0x18; не заменить на UCSRB = 1<<RXEN|1<<TXEN ??? И Вам понятней будет (особенно когда вернетесь к данному куску кода через недельку), и ошибок меньше, и нам в даташит лишний раз заглядывать не нужно.
|
|
|
|
|
Jul 1 2008, 17:39
|
Участник

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

|
Цитата(Александр Куличок @ Jul 1 2008, 20:54)  Т.е. в прерываниях (когда флаг I очищен) не обязательно временно запрещать разрешение прерывания от какого-либо устройства (в данном случае от INT0). А вот при смене режима его работы (смены активного фронта) может выставиться флаг прерывания(INTF0), который надо очищать. Аналогично следует поступить и с куском кода по вектору TIMER0_COMP_vect спасибо за рекомендации, исправлю. но все таки, разве это могло служить причиной того что таймер начал взводится через раз (раз в 10 мс), при добавление просто куска инициализации UART? Цитата Добавлено: Проглядел. Снимаю вопрос про вектор прерывния передатчика  Почему бы UCSRB = 0x18; не заменить на UCSRB = 1<<RXEN|1<<TXEN ??? И Вам понятней будет (особенно когда вернетесь к данному куску кода через недельку), и ошибок меньше, и нам в даташит лишний раз заглядывать не нужно. это кусок оставший от генератора начального кода из CodeVisionAVR  сам предпочитаю, естественно, вышеизложенную запись
|
|
|
|
|
Jul 1 2008, 17:53
|
Местный
  
Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017

|
Цитата но все таки, разве это могло служить причиной того что таймер начал взводится через раз (раз в 10 мс), при добавление просто куска инициализации UART? Нет, это не причина. Но для выяснения причины Вам не помешало бы описать, что вы хотите получить в конечном результате. И еще мне непонятно, что означает "таймер начал взводится через раз (раз в 10 мс)"? Увеличился период от запуска до останова таймера или же он стал запускаться в 2 раза реже? Бегло проанализировав код и комментарии, могу сказать, что при отрицательной полярности импульса на INT0 таймер будет запускаться после каждого второго спада на INT0 (каждых 20мс) и через 5мс инвертировать порт РА0. Если полярность импульса положительная, то таймер будет запускаться при каждом спаде на INT0 (т.е. каждых 10мс/100Гц). Но тогда не совсем понятно, зачем настраивать прерывание INT0 по фронту только для того, чтобы после поступления этого самого фронта перенастроить INT0 на спад сигнала?
|
|
|
|
|
Jul 1 2008, 19:32
|
Участник

Группа: Участник
Сообщений: 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 на спад сигнала? мне нужно знать где какой фронт у сигнала что бы запустить таймер (спадабщий фронт) и что бы проводить измерения (возрастающий фронт) вообщем надеюсь по картинке все сразу станет понятно
Эскизы прикрепленных изображений
|
|
|
|
|
Jul 1 2008, 19:43
|
Местный
  
Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017

|
Цитата что-то я не могу понять, в моей задумке таймер должен срабатывать только при спадающем фронте Это справедливо для *отрацательной* полярности импульса. (для tL < tH). Для положительной, как я и писал, срабатывать должно при каждом спаде. Цитата у меня создалось такое ощущение, т.к. раньше каждый раз когда срабатывал таймер он инвертировал ножку порта, т.е. через каждые 5 мс. а после включения USART по осциллографу видно что ножда стола инвертироваться каждые 10 мс Так она и должна инвертироваться каждых 10 мс (через 5 мс после спада, период спадов = 10мс). Мне непонятно почему у Вас при выключенном UART'е ножка инвертируется каждых 5мс.
|
|
|
|
|
Jul 1 2008, 20:34
|
Участник

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

|
Цитата(Александр Куличок @ Jul 1 2008, 23:43)  Это справедливо для *отрацательной* полярности импульса. (для tL < tH). Для положительной, как я и писал, срабатывать должно при каждом спаде. может и дурацкий вопрос, но все же:что такое полярность импульсов в данном случае и какая она в моем примере? Цитата Так она и должна инвертироваться каждых 10 мс (через 5 мс после спада, период спадов = 10мс). Мне непонятно почему у Вас при выключенном UART'е ножка инвертируется каждых 5мс. ошибся, действительно ножка должна инвертироваться каждые 10 мс. так и происходит без включенного USART. со включенным период инвертирования 20 мс - вот в чем и загвоздка
|
|
|
|
|
Jul 1 2008, 21:37
|
Местный
  
Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017

|
Цитата может и дурацкий вопрос, но все же:что такое полярность импульсов в данном случае и какая она в моем примере? Так как Вы пишете, что "длительность импульса 160 мкс", то по рисунку, приведенному выше, полярность - положительная Приведенный Вами листинг полный? Если это так, то по поводу 20 мс уж и не знаю, что сказать. Если нет, то пока единственное, что приходит на ум - это то, что программа не успевает переключиться на фронт/спад сигнала ДО его прихода. Опровергнуть/подтвердить это помогут 2хлучевой осциллограф и инверсия какого-либо пина при каждом фронте INT0. Кстати, а что за void uart_pc_init(void) в теле main?
|
|
|
|
|
Jul 2 2008, 09:53
|
Участник

Группа: Участник
Сообщений: 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? это случайно попала когда постил текст программы  вообщем большое спасибо!
|
|
|
|
|
Jul 2 2008, 10:35
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

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

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

|
Что-то я не понял зачем намудрено с переключением фронтов? Нельзя просто запускать таймер по конкретному одному фронту, в том случае если таймер не запущен. e.g. Код INT0_ISR() { if (!TimerRunning) StartTimer(); }
Timer_ISR() { PORTA = ...; StopTimer(); } хотя для антидребезга IMHO будет лучше просто перезапускать таймер по INT0 всегда (закоментировать if (!TimerRunning).
|
|
|
|
|
Jul 2 2008, 22:41
|
Местный
  
Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017

|
Цитата Эдак Вы все выпавшие флаги прерывания очистите при перенастройке Согласен, с моей стороны глупая и грубая ошибка. Вам и от меня спасибо  Кстати, я вполне согласен с 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-битного МС
|
|
|
|
|
Jul 3 2008, 17:08
|
Участник

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

|
Цитата(_Pasha @ Jul 2 2008, 14:35)  Неправильно! Эдак Вы все выпавшие флаги прерывания очистите при перенастройке. И будут очередные поиски глюков. Код GIFR = (1<<INTF0); // сброс флага, который мог установиться при смене активного фронта Так что мне тоже спасибо  а объясните мне, начинающему почему надо писать так, а не GIFR |= (1<<INTF0)? почему именно единицу это понятно, так в даташите написано. ведь насколько я понимаю, именно такая команда включает бит INTFO, а предложенная вами запишет 01000000 (Bit 6 – INTF0: External Interrupt Flag 0 ) или я что-то путаю?
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|