roboforum.ru

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

Помогите новичку решить простенькую задачку для ATMEGA16

Помогите новичку решить простенькую задачку для ATMEGA16

maxim25 » 12 окт 2010, 15:46

Уж который день парюсь, чувствую, что истина где-то рядом, но пока ничего не получается :cry:

В общем нужно:
1 Прерывание по получению данных из UDR

2 Включение таймера на 100мС(Каждый раз когда приходит байт, таймер перезапускается)

3 Формирование буфера из принятых по UDR байт (buf(n)+ = UDR) и подсчёт количества n

4 Прерывание по срабатыванию таймера(по истечении 100мС) т.е. последовательный приём n байт окончен.

5 Отправка n байт одной посылкой через UDR (цикл)

6 Обнуление n


Текст программы на С
Код: Выделить всёРазвернуть
[color=#008040]#include <mega16.h>
#include <m8_128.h>
// Standard Input/Output functions
#include <stdio.h>


#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7


#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)

int BUF[100]; //Объявляем количество буфера

int n;   

// USART Receiver buffer
/*#define RX_BUFFER_SIZE 100
char rx_buffer[RX_BUFFER_SIZE];

#if RX_BUFFER_SIZE<256
unsigned char rx_wr_index,rx_rd_index,rx_counter;
#else
unsigned int rx_wr_index,rx_rd_index,rx_counter;
#endif

// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow;


// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
   rx_buffer[rx_wr_index]=data;
   if (++rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
   if (++rx_counter == RX_BUFFER_SIZE)
      {
      rx_counter=0;
      rx_buffer_overflow=1;
      };
   };
}

#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter==0);
data=rx_buffer[rx_rd_index];
if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#pragma used-
#endif

// USART Transmitter buffer
#define TX_BUFFER_SIZE 100
char tx_buffer[TX_BUFFER_SIZE];

#if TX_BUFFER_SIZE<256
unsigned char tx_wr_index,tx_rd_index,tx_counter;
#else
unsigned int tx_wr_index,tx_rd_index,tx_counter;
#endif

// USART Transmitter interrupt service routine
interrupt [USART_TXC] void usart_tx_isr(void)
{
if (tx_counter)
   {
   --tx_counter;
   UDR=tx_buffer[tx_rd_index];
   if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0;
   };
}

#ifndef _DEBUG_TERMINAL_IO_
// Write a character to the USART Transmitter buffer
#define _ALTERNATE_PUTCHAR_
#pragma used+
void putchar(char c)
{
while (tx_counter == TX_BUFFER_SIZE);
#asm("cli")
if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))
   {
   tx_buffer[tx_wr_index]=c;
   if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0;
   ++tx_counter;
   }
else
   UDR=c;
#asm("sei")
}
#pragma used-
#endif
*/
// Timer 1 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
UDR=BUF[n];

}

void main(void)
{
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0xB8;
OCR0=0x00;

// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 9600
UCSRA=0x00;
UCSRB=0xD8;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x67;

// 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 (!(UCSRA & (1<<RXC)))
{
if (BUF[n]=UDR)
{
n++;
TCCR0 = 0x00;
TCNT0=0xB8;
TCCR0 = 0x01; //start timer  - clock/1024   
TIMSK=0x01;
}
else(!(UCSRA & (1<<TXC)));
{
TIMSK |= (1<<TOIE0);       
TIFR|=(1<<TOV0); 
}
}



программировать только начинаю, по этому не обессутьте, и пожалста помогите, чем сможите :)
Последний раз редактировалось maxim25 13 окт 2010, 12:06, всего редактировалось 1 раз.

Re: Помогите новичку решить простенькую задачку для ATMEGA16

avr123.nm.ru » 12 окт 2010, 17:46

Код заключите в одноименный тег.


Советую сделать модель в PROTEUS и цеплять к посту архив папки с прожектами.

Re: Помогите новичку решить простенькую задачку для ATMEGA16

North » 12 окт 2010, 18:30

Что-то я не вижу никакого кода, связанного с запуском таймера. Переменная rx_counter не особо нужна, т. к. rx_wr_index считает то же самое. Если складывать в буфер передачи отсылаемые байты не от нуля к tx_counter, а от tx_counter к нулю, то tx_counter тоже не нужна, её роль выполняет tx_rd_index. Я бы сделал так (объявления большинства переменных, и функции, не являющиеся обработчиками прерываний опущены, ибо лень писать :P ):
Код: Выделить всёРазвернуть
#define 100MS_TIMEOUT 100 // В случае если прерывание по таймеру происходит 1 раз в миллисекунду

int uart_timer;
unsigned char global_buffer[RX_BUFFER_SIZE];

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
  char status,data;
  status=UCSRA;
  data=UDR;
  uart_timer = 100MS_TIMEOUT;
  if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
  {
    rx_buffer[rx_wr_index]=data;
    if (++rx_wr_index == RX_BUFFER_SIZE)
    {
      rx_wr_index=0;
      place_event(event_uart_buffer_overflow);
    }
  };
}

// Timer 1 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
  if(uart_timer)
  {
    uart_timer--;
    if(!uart_timer) // 100 мС только что истекли
    {
      for(i=0; i<rx_wr_index; i++) // сохраняем всё что успели принять в некий глобальный буфер
      {
        global_uart_buffer[i] = rx_buffer[i];
      }
      rx_wr_index = 0;     
      place_event(event_uart_message_received); // сигнализируем в main loop о том что мы приняли пакет данных по УАРТУ
    }
  }
}

// USART Transmitter interrupt service routine
interrupt [USART_TXC] void usart_tx_isr(void)
{
  // Если класть данные в буфер задом наперёд, то переменная tx_counter не нужна
  whlie (tx_rd_index)
  {
    --tx_rd_index;
    while(UARTStatusRegister&UART_DATA_REGISTER_IS_EMPTY); // не помню названия регистров AVR и битов в них,
                                                           // но по именам переменных должно быть понятно что
                                                           // я имею ввиду.
    UDR=tx_buffer[tx_rd_index];
  };
}

Re: Помогите новичку решить простенькую задачку для ATMEGA16

maxim25 » 13 окт 2010, 09:24

North писал(а):Что-то я не вижу никакого кода, связанного с запуском таймера. Переменная rx_counter не особо нужна, т. к. rx_wr_index считает то же самое. Если складывать в буфер передачи отсылаемые байты не от нуля к tx_counter, а от tx_counter к нулю, то tx_counter тоже не нужна, её роль выполняет tx_rd_index. Я бы сделал так (объявления большинства переменных, и функции, не являющиеся обработчиками прерываний опущены, ибо лень писать :P ):


Связности с таймером нет, потому, что я черновик выкинул сюда, просто не могу понять строение блок схемы.Как тот псевдокод реализолвать в виде блоксхемы?
Должно получиться на деле -
Посылаю с клавиатуры символ, второй, третий.ПРи этом таймер перезагружается при посылке символа.
Потом перестаю посылать, проходит некоторое время, происходит прирывание по переполнению и те 3 символа одной посылкой отправляются через UDR.
что-то я туплю(

Re: Помогите новичку решить простенькую задачку для ATMEGA16

North » 13 окт 2010, 10:47

Я правильно понимаю, после прерывания по таймеру нужно просто вывалить всё что приняли по УАРТУ обратно в УАРТ?

Re: Помогите новичку решить простенькую задачку для ATMEGA16

maxim25 » 13 окт 2010, 11:45

North писал(а):Я правильно понимаю, после прерывания по таймеру нужно просто вывалить всё что приняли по УАРТУ обратно в УАРТ?


Да, т.е. я посылаю с клавы b, потом a, потом с.И он мне должен вывести после прирывания по таймеру " bac ".Сколько я символов послал, столько и должен вывести.
Вроде задачка-то лёгкая, но видно чего не допонимаю.

Re: Помогите новичку решить простенькую задачку для ATMEGA16

blindman » 13 окт 2010, 11:59

Модератор :maxim25, оформите код тэгом [code]

Re: Помогите новичку решить простенькую задачку для ATMEGA16

North » 13 окт 2010, 13:45

Тогда так (выкинул из программы лишние переменные, инициализацию периферии и тому подобную фигню):
Код: Выделить всёРазвернуть
#define 100MS_TIMEOUT 100 // В случае если прерывание по таймеру происходит 1 раз в миллисекунду
#define EVENT_RECEIVE_COMPLETE 0x01
#define EVENT_BUFFER_OVERFLOW  0x02

int uart_timer;
unsigned char rx_buffer[RX_BUFFER_SIZE];
unsigned char tx_buffer[RX_BUFFER_SIZE];
unsigned char event_flags = 0;
unsigned char rx_index = 0;
unsigned char tx_index = 0;

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
  char status,data;
  status=UCSRA;
  data=UDR;
  uart_timer = 100MS_TIMEOUT;
  if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
  {
    rx_buffer[rx_index]=data;
    if (++rx_index == RX_BUFFER_SIZE)
      event_flags |= EVENT_BUFFER_OVERFLOW;
  }
}

// Timer 1 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
  if(uart_timer)
  {
    uart_timer--;
    if(!uart_timer) // 100 мС только что истекли
    {
      for(i=0; i<rx_index; i++) // сохраняем всё что успели принять в буфер передачи
        tx_buffer[(rx_index-1)-i] = rx_buffer[i];
      tx_index = rx_index;
      rx_index = 0;
      event_flags |= EVENT_RECEIVE_COMPLETE; // сигнализируем в main loop о том что мы приняли пакет данных по УАРТУ
    }
  }
}

void main(void)
{
  // Инициализация таймеров, уарта и прочего хардвара...
  HardwareInit();
  while(1)
  {
    if(event_flags & EVENT_RECEIVE_COMPLETE)
    {
      whlie (tx_index)
      {
        --tx_index;
        while(UARTStatusRegister & UART_DATA_REGISTER_IS_EMPTY); // не помню названия регистров AVR и битов в них,
                                                                 // но по именам переменных должно быть понятно что
                                                                 // я имею ввиду.
        UDR=tx_buffer[tx_rd_index];
      }
      event_flags &= (~EVENT_RECEIVE_COMPLETE);     
    }
    if(event_flags & EVENT_BUFFER_OVERFLOW)
    {
      rx_index = 0;
      event_flags &= (~EVENT_BUFFER_OVERFLOW);
    }
  }
}

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

Re: Помогите новичку решить простенькую задачку для ATMEGA16

maxim25 » 13 окт 2010, 18:33

North писал(а):Скорее всего в этом коде есть мелкие ошибки, т. к. писал его чисто умозрительно. Но идея должна быть понятна, осталось её творчески переработать :pardon:


Большое спасибо!)Попробую довести до ума)

Re: Помогите новичку решить простенькую задачку для ATMEGA16

maxim25 » 14 окт 2010, 11:21

Вот.Написал программу работающую на билдере.
Теперь попытаюсь в си тоже реализовать.
Вложения
in.rar
Готовая прога на билдере
(5.57 КиБ) Скачиваний: 0


Rambler\'s Top100 Mail.ru counter