Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Тема закрыта

Как определить, что UART (ATmega64) передал все данные и ожи

Пн июн 25, 2012 17:04:01

Программа сделана в CodeVision.

Вот мои переменные:
Код:
unsigned char tmp=0; // возможное значение: 0..255 (8 бит)
const unsigned char string_in[20] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14}; //гамма функция
unsigned char index_str=0; //индекс массива гамма функции

А вот рабочая часть шифратора:
Код:
while (1)
      {
      // Place your code here
        if(rx_counter1 != 0) //если еще не все символы, из входного буфера, прочитанные функцией getchar
        {
            tmp = getchar1(); // tmp = входной символ (уменьшаем на 1 rx_counter1)

            putchar1(tmp^string_in[index_str]);  // Xor и отправляем на выход

            if (index_str == 19) index_str=0;
            else index_str++;
        };
      };

Устройство подключено через СОМ порт к ПК.
С ПК отправляю массив символов (нажатием кнопки) на UART нашего МК.
Программа берет по одному символу из массива с ПК и делает операцию Xor с каким-то элементом массива string_in (который записан в памяти МК). Если входной массив с ПК больше чем массив string_in, то index_str обнуляется. И данные для операции Xor берутся сначала массива string_in.
После чего возвращает на ПК (по UART) измененный массив символов.

Что нужно добавить в код, чтобы, когда с ПК отправляется новый массив символов, МК сбрасывал в 0 значение моей переменной index_str?

P.S. Попробовал добавить прерывание:
Код:
//Регистр данных USART1 свободен
interrupt [USART1_DRE] void usart1_dr_isr(void)
{
    index_str=0;
}

Но, это не помоголо.

Пожалуйста, помогите!!!
Вложения
Mytest_m64c.c
Полная версия прошивки для ATmega64
(10.71 KiB) Скачиваний: 493

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 03:10:53

Наколько я понял, максимальная длина массива 16. Допустим, вы передали 10 элементов. Как МК узнает, что передан один массив из 10 элементов или 2 массива по 5? В данном коде просто никак - он поместит все 10 в один массив и будет ожидать 11-го элемента. Как-бы Вы сами узнали?

Вам нужно, например, передавать фактическую длину массива, или делать временную задержку между посылкой разных массивов. Во втором случае можно после приема каждого символа включать таймер, который будет считать, скажем, до 1000. Если таймер не досчитал до 1000 и принят новый символ, то считается, что он принадлежит к тому-же массиву. Таймер следует каждый раз обнулять по приему символа. Как только таймер досчитает до 1000, значит массив принят полностью и следует обнулять index_str и готовиться принимать другой массив. Если разрешить прерывание по переполнению таймера, то обнулять переменную можно в подпрограмме обработки прерывания таймера. В ней-же следует останавливать таймер. Он включися опять после приема след. символа.

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 11:02:18

Спасибо за идею! Но, Ваша помощь все еще нужна.

Код:
Chip type           : ATmega64
Clock frequency     : 8,000000 MHz

interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
// Reinitialize Timer 1 value
//Начальное значение Таймера 1 при запуске: ??? (почему эти значения)
TCNT1H=0x0B;
TCNT1L=0xDC;
// Place your code here
index_str=0;  // должно выполнятся при переполнении таймера?
}

void main(void)
{
//…
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// OC1C output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
// Compare C Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x03; // Clock value: 125,000 kHz
//Начальное значение Таймера 1 при запуске: ??? (почему эти значения)
//в прерывании interrupt [TIM1_OVF] void timer1_ovf_isr(void) такие же значения
TCNT1H=0x0B;
TCNT1L=0xDC;
//..
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x04; //при переполнении таймера 1, запуск прерывания interrupt [TIM1_OVF] void timer1_ovf_isr(void)
ETIMSK=0x00; //что это???
//..
while (1)
      {
      // Place your code here
        if(rx_counter1 != 0) //если еще не все символы, из входного буфера, прочитанны функцией getchar
        {
            tmp = getchar1(); // tmp = входной символ (уменьшаем на 1 rx_counter1)

            putchar1(tmp^string_in[index_str]); 

            if (index_str == 19) index_str=0; //это же должно присходить, если дольше 1сек не поступают входные символы.
            else index_str++;
      };
}

Почитал Учимся использовать таймер. :)
Не смог разобраться ((( Первый раз с таймерами дело имею.

Подскажите, пожалуйста:
1. Данные TCNT1H=0x0B; TCNT1L=0xDC; взял с «потолка», какие у меня должны быть значения таймера? Что бы таймер переполнялся через 1 секунду (если данные не поступают по UART).
2. Как обнулять таймер при каждом входе в if(rx_counter1 != 0)…?

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 12:14:27

Правильнее буде оформлять данные в пакеты с разделителями.
Один из простых вариантов -- SLIP, на нём основан Wake от Леонида Ивановича.
Ещё тут -- viewtopic.php?f=20&t=36709
С некоторым отклоненем, зато с готовым софтом под Win для работы и отладки.

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 14:25:06

Cпасибо за комментарий.
Т.к. пишу на C# под ПК, то создал свой интерфейс используя SerialPort – класс

А вот с задачей написания прошивки для МК (ATmega64) столкнулся впервые.
Мне нужно, что бы по окончанию приема или передачи данных через UART происходило установление в ноль переменной (которую сам объявил).

Сейчас приходится выключать/включать устройство, что бы установились первоначальные параметры.

Пожалуйста, подскажите, как сделать на ATmega64 включение/отключение таймера?

Таймер будет считать до 1 секунды, если за это время не будет передана/принята никакая инфрмация через UART, то переменная index_str должна стать равной нулю.

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 15:19:33

по окончанию приема или передачи данных через UART происходило установление в ноль переменной

так в Меге есть регистр состояния передатчика и приемника - опрашивай нужный бит, и будет ясно что творится с байтами на линии

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 16:38:09

Вы это имели ввиду:
Код:
while (1)
      {
      // Place your code here
        if(rx_counter1 != 0) //если еще не все символы, из входного буфера, прочитанные функцией getchar
        {
            tmp = getchar1(); // tmp = входной символ (уменьшаем на 1 rx_counter1)

            putchar1(tmp ^ string_in[index_str]);  // Xor 1 символ и отправляем на выход
            if (index_str == 19) index_str=0;
            else index_str++;
        };
        //else
          //  index_str=0;
        //if (UDR1 == 0) index_str=0;
        if (tx_counter1 == 0)
         if (rx_counter1 == 0)
            index_str=0;
      };
}

При такой программе всегда index_str=0.

Ser60 предложил очень разумное решение.
Нужно на каждой итерации
Код:
while (1)
      {
        if(rx_counter1 != 0)
        {
             Запускать таймер (обнуляя его)
        };
      }

Таймер будет обнуляться чаще чем 1 раз в секунду. Но, если условие перестанет выполнятся длительное время (1 секунду или больше), прерывание переполнения таймера сбросит index_str = 0.

Подскажите, пожалуйста, код для запуска (обнуления) таймера?

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 17:12:03

Как всегда меня опередили пока спал... Ну раз уж написал, отправлю.

1. Если МК тактируется от 8 мгц и требуется переполмение таимера Timer1 через секунду, то коэффициент деления должен быть 8000000. Используя прескейлер 1:1024 получаем, что таймер должен считать до 8000000 / 1024 = 7812 = 0x1E84. Именно это число надо поместить в регистр ICR1 до входа в основной цикл программы.
ICR1 = 0x1E84

2. Используем таймер в режиме CTC (режим 1100). Это достигается так:
TCCR1A = 0
TCCR1B = 0x18

3. Разрешаем прерывания от таймера и обнуляем его флаг:
TIMSK1 = 1
TIFR1 = 0

4. Разрешаем прерывания глобально:
#asm("sei")

5. В программе обработки прерываний таймера:
а. Останавливаем таймер, отключая от него тактирующую частоту
TCCR1B = 0x18
б. обнуляем Вашу переменную
index_str=0;

Все предыдущие операции надо сделать до входа в основной цикл программы.
В основном цикле по приему символа из UART:

1. Обнуляем таймер
TCNT1 = 0
2. Запускаем таймер, подав на него частоту от прескейлера 1:1024
TCCR1B = 0x1D
Строго говоря, запуск необходим только 1 раз по приему первого элемента массива, но повторный запуск не повредит (он просто будет проигнорирован). Но если хотите, можно запускать только 1 раз:
if (index_str==0)
TCCR1B = 0x1D

Я-бы в основном цикле код в блоке
if (rx_counter1 != 0)
оформил-бы как прерывание от UART по приему символа. В этом случае процессор можно поместить в сон, нечего ему работать высунув язык. Пробуждение будет по приему символа или переполнению таймера.

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 18:13:51

Спасибо!
Правда, к этому моменту, решил таким «дубовым» способом задачу:
Код:
// При переполнении Таймера 1, запуск прерывания (установка начальных значений таймера)
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
     // Reinitialize Timer 1 value (где-то 0,5 секунды)
     TCNT1H=0x0B;
     TCNT1L=0xDC;
     index_str = 0; //обнуляю переменную
}

Идея в том, что прерывание от UART, похоже, приоритетней чем прерывание от Timer 1.
Поэтому, банально обнуляю нужную переменную в таймере.
Согласен, это метод некрасивый. Т.к. если буду обрабатывать массив данных долго (внутри МК), то переменная index_str может не вовремя обнулиться.
Поэтому начинаю медитировать, на Ваше решение.

Ser60 писал(а):Как всегда меня опередили пока спал... Ну раз уж написал, отправлю.
...
Я-бы в основном цикле код в блоке
if (rx_counter1 != 0)
оформил-бы как прерывание от UART по приему символа. В этом случае процессор можно поместить в сон, нечего ему работать высунув язык. Пробуждение будет по приему символа или переполнению таймера.

Вот весь цикл:
Код:
while (1)
      {
        if(rx_counter1 != 0)
        {
            tmp = getchar1();
            putchar1(tmp ^ string_in[index_str]); 
            if (index_str == 19) index_str=0;
            else index_str++;
        };
      };
В какое прерывание его переместить?
Как помещать в сон МК?

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 18:27:34

Код:
interrupt  <- оформите сами интеррaпт от UART в С
{
            tmp = getchar1();
            putchar1(tmp ^ string_in[index_str]);

            TCNT1 = 0;
            TCCR1B = 0x1D;
            if (index_str==0)
                TCCR1B = 0x1D;
 
            if (index_str == 19) index_str=0;
            else index_str++;
}


Как помещать в сон МК?

Основной цикл:
Код:
while(1)
{
           #asm("sleep");
}

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 20:14:26

Еще раз, спасибо!
Но, наверно, что-то сделал не так. Делал по Вашему образцу )))

Код:
//Регистр данных USART1 свободен
interrupt [USART1_DRE] void usart1_dr_isr(void)
{
   tmp = getchar1();
   putchar1(tmp ^ string_in[index_str]);

   TCNT1 = 0;
   TCCR1B = 0x1D;
   if (index_str==0)
     TCCR1B = 0x1D;
 
   if (index_str == 19) index_str=0;
     else index_str++;
}

А главный цикл поменял на:
Код:
// Global enable interrupts
#asm("sei")

while (1)
      {
      // Place your code here
        #asm("sleep");
      };

Собственно ничего не заработало. Т.е. отправляю с ПК данные, но ответа не вижу.
Подскажите, пожалуйста, где Ваш код не правильно понял?

P.S. С ПК отпраляю данные строками. Попробовал через стандартный терминал. Посимвольно то же не пересылаются данные.
Вложения
Mytest_m64d.c
Возможная версия прошивки для ATmega64
(10.87 KiB) Скачиваний: 308

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 20:41:40

Прерывание от UART по приему символа разрешить не забыли? Если нет - поставьте точку останова в отладчике (внутрисхемном) в нагале программы приема символа с UART и посмотрите что принимается.

... Добавлено позже.
Посмотрел код - конечно забыли.
А где ISR от Таймера, что я писал, и где его инициализация?

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 22:15:22

Ser60 писал(а):...поставьте точку останова в отладчике (внутрисхемном) в нагале программы приема символа с UART и посмотрите что принимается.

Пользуюсь только CV. Недавно начал читать про VMLAB.
Установил AVRStudio5.1, но она с CV (2008 года) не хочет работать.
Поэтому, пожалуйста, уточните: что значит внутрисхемный отладчик?
Ser60 писал(а):...А где ISR от Таймера, что я писал, и где его инициализация?

Извините, недопонял сразу.
Вот исправленный вариант (он то же не рабочий). :?
Вложения
Mytest_m64d1.c
новая версия
(11.57 KiB) Скачиваний: 294

Re: Как определить, что UART (ATmega64) передал все данные и

Вт июн 26, 2012 23:11:23

Я-же Вам написал, что для инициализации таймера нужно 5 строчек:

Код:
ICR1 = 0x1E84;
TCCR1A = 0;
TCCR1B = 0x18;
TIMSK1 = 1;
TIFR1 = 0;


А у Вас вместо TIMSK1 стоит TIMSK. Поставьте эти строчки вместе и выкиньте весь остальной код связанный с таймерами: помимо Timer1 Вы других не используете, ну и не трогайте их регистры.

В ISR таймера оставьте только это:

Код:
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
   TCCR1B = 0x18;[i][/i]
   index_str=0;
}


Прерывание UART надо по окончании приема. У Вас код в обработчике не того прерывания.

Еще выкиньте весь код запрета/разрешения прерываний иz getchar1 и putchar1. Сейчас он не нужен.

Чем Вы программируете МК? Внутрисхемный отладчик/программатор (например AVR Dragon) к IDE не имеет отношения. Но если Вы не знаете, что это такое, скорее всего им и не пользуетесь. Тогда забудьте про него.

Re: Как определить, что UART (ATmega64) передал все данные и

Ср июн 27, 2012 10:01:22

Код:
TIMSK1 = 1;
TIFR1 = 0;
Выдает ошибку:
Код:
Error: G:\MK\Mytest_m64d\Mytest_m64d.c(274): undefined symbol 'TIMSK1'
Error: G:\MK\Mytest_m64d\Mytest_m64d.c(275): undefined symbol 'TIFR1'
Переписал код для прерывания:
Код:
// Завершение передачи USART1 Transmitter interrupt service routine
interrupt [USART1_TXC] void usart1_tx_isr(void)
{
   if (tx_counter1)
   {
   --tx_counter1;
   UDR1=tx_buffer1[tx_rd_index1]; //Регистр ввода/вывода UART – UDR
   if (++tx_rd_index1 == TX_BUFFER_SIZE1) tx_rd_index1=0;
   };
   
   tmp = getchar1();
   putchar1(tmp ^ string_in[index_str]);

   TCNT1 = 0;
   TCCR1B = 0x1D;
   if (index_str==0)
     TCCR1B = 0x1D;
 
   if (index_str == 19) index_str=0;
     else index_str++;
}
Правильно?
Вложения
Mytest_m64d2.c
Исправил код для прерывания, но остались ошибки для TIMSK1 = 1; TIFR1 = 0;
(11.83 KiB) Скачиваний: 299

Re: Как определить, что UART (ATmega64) передал все данные и

Ср июн 27, 2012 16:32:45

Нет, не так. Вам нужно запускать таймер по окончании приему символа, как я писал, а не по окончании передачи, как у Вас сейчас. Давайте начнем с малого. Вернитесь к первоначальному коду, где обработка производится в основном цикле. Я написал внизу как он должен выглядеть. Весь этот код надо вставить в самый конец main(). Вы правы, регистры в Mega64 действительно называются TIMSK и TIFR. В TIMSK надо загружать 4, чтобы разрешить прерывания по переполнению таймера.

Код:
ICR1 = 0x1E84;       // настройки таймера до входа в основной цикл
TCCR1A = 0;
TCCR1B = 0x18;
TIMSK = 4;
TIFR = 0;

while (1)
{
        if (rx_counter1 != 0) //если еще не все символы из входного буфера прочитанны
        {
            tmp = getchar1(); // tmp = входной символ (уменьшаем на 1 rx_counter1)

            putchar1(tmp^string_in[index_str]);  // Xor и отправляем на выход

            TCNT1 = 0;         // запускаем таймер
            if (index_str==0)
               TCCR1B = 0x1D;

            if (index_str == 19) index_str=0;
            else index_str++;
        }
 }   


А код ISR таймера как и выше:

Код:
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
   TCCR1B = 0x18;
   index_str=0;
}

Re: Как определить, что UART (ATmega64) передал все данные и

Чт июн 28, 2012 01:46:46

Спасибо!
Вернусь через пару дней. Сейчас ПК недоступен.
Рассчитываю на Вашу помощь в дальнейшем.

Re: Как определить, что UART (ATmega64) передал все данные и

Пт июн 29, 2012 14:53:20

Сделал, как Вы написали.
Проверьте, пожалуйста.
Т.к. значение переменной (index_str=0;) не обнуляется.
Вложения
Mytest_m64d3.c
значение переменной (index_str=0;) не обнуляется
(10.83 KiB) Скачиваний: 284

Re: Как определить, что UART (ATmega64) передал все данные и

Пт июн 29, 2012 17:54:47

Мой косяк: измените строчку
TCCR1A = 0;
на
TCCR1A = 2;

Re: Как определить, что UART (ATmega64) передал все данные и

Пт июн 29, 2012 18:05:20

Поменял, ничего не изменилось.

//////////////////////////////////////////////////
Пока вернулся к самому первому варианту и добавил пару строк:

Код:
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
// Reinitialize Timer 1 value
TCNT1H=0x0B; //задержка таймера
TCNT1L=0xDC; //около 0,5 сек
// Place your code here
index_str = 0;
TIMSK=0x00;
}

void main(void)
{
//…
while (1)
      {
      // Place your code here
        if(rx_counter1 != 0) //если еще не все символы, из входного буфера, прочитанные функцией getchar
        {
            TIMSK=0x00;
            // Reinitialize Timer 1 value
            TCNT1H=0x0B;
            TCNT1L=0xDC;
           
            tmp = getchar1(); // tmp = входной символ (уменьшаем на 1 rx_counter1)

            putchar1(tmp ^ string_in[index_str]);  // Xor 1 символ и отправляем на выход
            if (index_str == 19) index_str=0;
            else index_str++;
        }
        else
        TIMSK=0x04; //при переполнении таймера 1, запуск прерывания interrupt [TIM1_OVF] void timer1_ovf_isr(void)
      };
}


Теперь около 2Мб входного потока шифрует, потом глючит (иногда).
Как думаете, это из-за программа прошивки или из-за интерфейса на ПК (может моя программа на ПК слишком быстро передает данные)?
Вложения
Mytest_m64c1.c
Файл из ТС с мелкими доработками
(11.25 KiB) Скачиваний: 301
Тема закрыта