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

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

Программа сделана в 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 КБ) 538 скачиваний
Реклама
Аватара пользователя
Ser60
Друг Кота
Сообщения: 3784
Зарегистрирован: Ср дек 24, 2008 09:58:58

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

Сообщение Ser60 »

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

Вам нужно, например, передавать фактическую длину массива, или делать временную задержку между посылкой разных массивов. Во втором случае можно после приема каждого символа включать таймер, который будет считать, скажем, до 1000. Если таймер не досчитал до 1000 и принят новый символ, то считается, что он принадлежит к тому-же массиву. Таймер следует каждый раз обнулять по приему символа. Как только таймер досчитает до 1000, значит массив принят полностью и следует обнулять index_str и готовиться принимать другой массив. Если разрешить прерывание по переполнению таймера, то обнулять переменную можно в подпрограмме обработки прерывания таймера. В ней-же следует останавливать таймер. Он включися опять после приема след. символа.
Реклама
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

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

Код: Выделить всё

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)…?
Аватара пользователя
avreal
Опытный кот
Сообщения: 842
Зарегистрирован: Чт дек 31, 2009 19:27:45
Откуда: Бровари, Україна
Контактная информация:

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

Сообщение avreal »

Правильнее буде оформлять данные в пакеты с разделителями.
Один из простых вариантов -- SLIP, на нём основан Wake от Леонида Ивановича.
Ещё тут -- http://radiokot.ru/forum/viewtopic.php?f=20&t=36709
С некоторым отклоненем, зато с готовым софтом под Win для работы и отладки.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Реклама
Эиком - электронные компоненты и радиодетали
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

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

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

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

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

Таймер будет считать до 1 секунды, если за это время не будет передана/принята никакая инфрмация через UART, то переменная index_str должна стать равной нулю.
Реклама
Аватара пользователя
igor-x
Мудрый кот
Сообщения: 1817
Зарегистрирован: Пн ноя 29, 2010 15:58:43

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

Сообщение igor-x »

по окончанию приема или передачи данных через UART происходило установление в ноль переменной
так в Меге есть регистр состояния передатчика и приемника - опрашивай нужный бит, и будет ясно что творится с байтами на линии
Реклама
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

Вы это имели ввиду:

Код: Выделить всё

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.

Подскажите, пожалуйста, код для запуска (обнуления) таймера?
Аватара пользователя
Ser60
Друг Кота
Сообщения: 3784
Зарегистрирован: Ср дек 24, 2008 09:58:58

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

Сообщение Ser60 »

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

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 по приему символа. В этом случае процессор можно поместить в сон, нечего ему работать высунув язык. Пробуждение будет по приему символа или переполнению таймера.
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

Спасибо!
Правда, к этому моменту, решил таким «дубовым» способом задачу:

Код: Выделить всё

// При переполнении Таймера 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++;
        };
      };
В какое прерывание его переместить?
Как помещать в сон МК?
Аватара пользователя
Ser60
Друг Кота
Сообщения: 3784
Зарегистрирован: Ср дек 24, 2008 09:58:58

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

Сообщение Ser60 »

Код: Выделить всё

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");
}
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

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

Код: Выделить всё

//Регистр данных 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 КБ) 368 скачиваний
Аватара пользователя
Ser60
Друг Кота
Сообщения: 3784
Зарегистрирован: Ср дек 24, 2008 09:58:58

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

Сообщение Ser60 »

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

... Добавлено позже.
Посмотрел код - конечно забыли.
А где ISR от Таймера, что я писал, и где его инициализация?
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

Ser60 писал(а):...поставьте точку останова в отладчике (внутрисхемном) в нагале программы приема символа с UART и посмотрите что принимается.
Пользуюсь только CV. Недавно начал читать про VMLAB.
Установил AVRStudio5.1, но она с CV (2008 года) не хочет работать.
Поэтому, пожалуйста, уточните: что значит внутрисхемный отладчик?
Ser60 писал(а):...А где ISR от Таймера, что я писал, и где его инициализация?
Извините, недопонял сразу.
Вот исправленный вариант (он то же не рабочий). :?
Вложения
Mytest_m64d1.c
новая версия
(11.57 КБ) 358 скачиваний
Аватара пользователя
Ser60
Друг Кота
Сообщения: 3784
Зарегистрирован: Ср дек 24, 2008 09:58:58

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

Сообщение Ser60 »

Я-же Вам написал, что для инициализации таймера нужно 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 не имеет отношения. Но если Вы не знаете, что это такое, скорее всего им и не пользуетесь. Тогда забудьте про него.
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

Код: Выделить всё

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 КБ) 373 скачивания
Аватара пользователя
Ser60
Друг Кота
Сообщения: 3784
Зарегистрирован: Ср дек 24, 2008 09:58:58

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

Сообщение Ser60 »

Нет, не так. Вам нужно запускать таймер по окончании приему символа, как я писал, а не по окончании передачи, как у Вас сейчас. Давайте начнем с малого. Вернитесь к первоначальному коду, где обработка производится в основном цикле. Я написал внизу как он должен выглядеть. Весь этот код надо вставить в самый конец 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;
}
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

Спасибо!
Вернусь через пару дней. Сейчас ПК недоступен.
Рассчитываю на Вашу помощь в дальнейшем.
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

Сделал, как Вы написали.
Проверьте, пожалуйста.
Т.к. значение переменной (index_str=0;) не обнуляется.
Вложения
Mytest_m64d3.c
значение переменной (index_str=0;) не обнуляется
(10.83 КБ) 341 скачивание
Аватара пользователя
Ser60
Друг Кота
Сообщения: 3784
Зарегистрирован: Ср дек 24, 2008 09:58:58

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

Сообщение Ser60 »

Мой косяк: измените строчку
TCCR1A = 0;
на
TCCR1A = 2;
sg6336
Первый раз сказал Мяу!
Сообщения: 29
Зарегистрирован: Вт фев 21, 2012 09:46:19

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

Сообщение sg6336 »

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

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

Код: Выделить всё

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 КБ) 371 скачивание
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»