LINUX.ORG.RU

Прерывание по переполнению таймера atmega328p

 


0

4

Я пытаюсь забыдлокодить такое поведение на прерываниях:

  • жду данных по uart, получаю, отключаю RX_vect.
  • включаю таймер прерывания через 1024мс, включаю UDR0_vect.
  • передаю данные. отключаю UDR0_vect, Включаю RX_vect.
  • ждём данные по uart…

При срабатывании исключение таймера он не ждёт 1024мс, а сразу же выплёвывает то что я ему послал. Если послать данные 2 раза сразу же друг за другом, то таймер после 2го прилёта данных начинает отсчитывать время. Но если их отправлять с интервалом раз в 2 секунды то данные получаю обратно моментально.

int main(void) {

  init_uart();
  init_interrupt();
  xfunc_output = my_putchar;

  while (1) {
  }

  return 0;
}

#define BUFF_SIZE 32
#define BUFF_MASK (BUFF_SIZE - 1)

volatile char buff[BUFF_SIZE];
volatile uint8_t head = 0, tail = 0;

void init_uart(void) {
  UBRR0H  = UBRRH_VALUE;                   // Defined in setbaud.h
  UBRR0L  = UBRRL_VALUE;
  UCSR0A |= (1 << U2X0);
  UCSR0B  = (1 << TXEN0)  | (1 << RXEN0);  // Enable USART transmitter|reciever
  UCSR0C  = (1 << UCSZ01) | (1 << UCSZ00); // 8 data bits, 1 stop bit
}

void init_interrupt (void) {
  TCCR1B |= (1<<CS12);      // 256
  UCSR0B |= (1<<RXCIE0);
  sei();
}

ISR(TIMER1_OVF_vect){
  TIMSK1 &= ~(1<<TOIE0);    // disable timer
  UDR0    = buff[head];
  UCSR0B |= (1<<UDRIE0);    // enable UDRE_vect
}


ISR(USART_RX_vect){
  buff[tail] = UDR0;
  if (buff[tail] == ';') {
    buff[tail] = '\0';
    UCSR0B &= ~(1<<RXCIE0);       // disable RX_vect
    enable_timer_tx_data();
  }
  tail = (tail + 1) & BUFF_MASK;
}

ISR(USART_UDRE_vect){
  head = (head + 1) & BUFF_MASK;
  UDR0 = buff[head];
  if(!buff[head]){
    UCSR0B &= ~(1<<UDRIE0);         // disable UDRE_vect
    head = (head + 1) & BUFF_MASK;
    UCSR0B |=  (1<<RXCIE0);         // enable RX_vect
  }
}

void enable_timer_tx_data(void){
  /* TCNT1= 34287;        // 500 ms */
  TCNT1= 0;               // 1024 ms
  TIMSK1|=(1<<TOIE0);
}

Я плохо разбираюсь в поведении avr и у меня нет понимания что просходит в этой ситуации. Подскажите пожалуйста чяднт?



Последнее исправление: SmilePlz (всего исправлений: 1)

Лет десять не программировал AVR и поэтому могу и ошибаться, но записывать в TCNT1 ноль – плохая идея.

И вот это

tail = (tail + 1) & BUFF_MASK

костыль ужасный, который работает только если BUFF_SIZE равно двойке в степени.

u-235
()
Ответ на: комментарий от SmilePlz

Вроде бы это связано со способом формирования прерывания по переполнению:

In normal operation the Timer/Counter Overflow Flag (TOV1) will be set in the same timer clock cycle as the TCNT1 becomes zero.

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

u-235
()

Может, достаточно будет принудительно сбросить флаг прерывания? Что-то в этом роде

void enable_timer_tx_data(void){
  /* TCNT1= 34287;        // 500 ms */
  TCNT1= 0;               // 1024 ms
  TIFR1 |= (1<<TOV1);
  TIMSK1|=(1<<TOIE1); //TOIE1, а не TOIE0, наверное?
}
COKPOWEHEU
()
Последнее исправление: COKPOWEHEU (всего исправлений: 1)
Ответ на: комментарий от COKPOWEHEU

спасибо это работает. Не понимаю почему бит TOV1 в самом начале он стоит в 1, если я не включал прерывания до этого?

SmilePlz
() автор топика
Последнее исправление: SmilePlz (всего исправлений: 1)

Я плохо разбираюсь в поведении avr и у меня нет понимания что просходит в этой ситуации.

Вы нам подходите! Сразу видно - человек с головой!

anonymous
()