AVR Урок 14. USART. Связь МК с ПК. Часть 5



 

Урок 14

Часть 5

 

USART. Связь МК с ПК

 

Сегодня мы продолжим изучение программирования интерфейса  USART.

В предыдущей части занятия мы научились передавать данные в ПК по шине USART. Сегодня мы попробуем их оттуда принять.

Что для этого нам потребуется? Для этого нам потребуется обработать прерывание, которое возникает при окончании заполнения буфера чтения, то есть когда пакет с данными был окончательно принят.

Давайте создадим новый проект, чтобы проект, который мы написали для передачи данных, у нас остался нетронутым. Проект назовём Test12 и полностью со всем кодом и файлами туда всё перенесём из предыдущего Test11, благо, делать мы это уже умеем неплохо. Поэтому процесс я этот здесь описывать повторно не буду, чтобы, как говорят, не засорять эфир.

Как мы видели в одной из прошлых частей в таблице, данное прерывание имеет обозначение RXC и вектор 12 или 0x0B.

По идее, нам даже не надо заботиться о векторах и их номерах, всё это за нас сделает библиотека interrupt.h, которой мы также пользовались, когда использовали таймер. Но ради любопытства мы всё же найдём описание в библиотеке, заодно и точное имя функции. Откроем файл interrupt.h и… ничего такого там не увидим. Там совсем другие вещи, а именно способы представления строки векторов и прочие мудрёные прибамбасы. А уже зайдя в io.h, также подключенному к interrupt.h, найдём вот такую строку

 

#elif defined (__AVR_ATmega8A__)

# include <avr/iom8a.h>

 

которая говорит нам о том, что если мы используем контроллер, который указан в скобках, то у нас подключится файл iom8a.h, в котором мы всё и найдём. Зайдём в него и найдём там такие строки

 

/* USART, Rx Complete */

#define USART_RXC_vect _VECTOR(11)

#define USART_RXC_vect_num 11

 

Вот это нам и нужно. Правда вектор тут не 12, как в таблице, а 11. Ну, видимо, или ошибка где-то, или эти векторы можно как-то переопределять. Не будем заморачиваться по этому поводу, а возьмём в буфер обмена имя функции-обработчика и вернёмся в наш главный файл Test12.c и напишем функцию-обработчик перед функцией main(), пока, соответственно, с пустым телом

 

//—————————————-

ISR(USART_RXC_vect)

{

}

//—————————————-

int main(void)

 

Но это ещё не всё. Чтобы нам увидеть и оценить приём данных из ПК, нам как-то это надо ощутить. И поможет нам в этом символьный дисплей, который мы подключали в уроке 12. Скопируем в проект из проекта урока 12 файлы lcd.c и lcd.h, подключим их в дерево проекта и файл lcd,h подключим в файл main.h

 

#include «lcd.h»

#include «usart.h»

 

Из того же проекта скопируем инициализацию порта D и напишем её до функции-обработчика в файле Test12.c

 

//—————————————-

void port_ini(void)

{

PORTD=0x00;

DDRD=0xFF;

}

//—————————————-

ISR(USART_RXC_vect)

 

Код вывода слова «Ok!» на ПК вернём из бесконечного цикла в основное тело функции main().

В результате со всем сборным ходом функция main() у нас будет вот такая

 

//—————————————-

int main(void)

{

  port_ini(); //Инициализируем порты

  LCD_ini(); //Инициализируем дисплей

  USART_Init (8); //115200

  USART_Transmit('O');//Передаем при включении

  USART_Transmit('k');//сообщение «Ok!», что свидетельствует

  USART_Transmit('!');//о правильно работе программы

  USART_Transmit(0x0d);//переход в начало строки

  USART_Transmit(0x0a);//переход на новую строку

  while(1)

  {

  }

}

 

 

Ну и также, я думаю, что все мы помним, что кроме чем описать и применить функцию-обработчик прерывания, для того, чтобы прерывания работали, нам необходимо разрешить ещё и глобальные прерывания. Вызовем соответствующую функцию в main()

 

  USART_Transmit('O');//Передаем при включении

  sei();

 

Теперь посмотрим. как всё подключено. Дисплей я подключил точтно таким же способом, как и в уроке по нему и теперь у нас к плате подключен и дисплей и переходник USB-USART

 

image39

 

Вот такая картина получилась, соответственно, после того, как проект, который мы создали уже был собран и прошит. Как мы видим, на дисплее красуются 32 белых ярких прямоугольника. Чтобы такое безобразие исключить, нам желательно дисплей очистить. Только мы функцию очистки в 12 уроке написали, а прототип на неё в файл lcd.h не добавили. Давайте это сделаем

 

void LCD_ini(void);

void clearlcd(void);

 

Теперь мы данную функцию свободно можем вызвать из файла main.c

 

USART_Transmit(0x0a);//переход на новую строку

clearlcd();

while(1)

 

Также в силу того, что на дисплей мы информацию будем выводить по одному символу, то прототип для функции sendchar мы пропишем

 

void clearlcd(void);

void sendchar(unsigned char c);

 

Теперь соберём код и прошьём контроллер. Дисплей должен будет очиститься.

Ну и, в принципе у нас всё готово теперь для того, чтобы писать исходный код функции-обработчика прерываний.

Создадим переменную и запишем в неё значение из буфера USART

 

ISR(USART_RXC_vect)

{

  int b;

  b = UDR;

 

 

И чтобы по пришествии очередного байта с кодом символа мы могли его отображать не в ту же позицию дисплея, а в следующую, мы создадим две переменные горизонтальных и вертикальных координат символа на дисплее. Создадим мы их в начале нашего файла Test12.c

 

#include «main.h»

//—————————————-

unsigned char x=0,y=0;

//—————————————-

 

Теперь напишем в обработчике вывод символа и расчёт координат для следующего символа, учитывая количество символов по вертикали и горизонтали. Для нас это уже простой код, так как мы уже много чего изучили, поэтому объяснять я его не буду

 

  b = UDR;

  setpos(x,y);

  sendchar(b);

  if(x<15) x++;

  else

  {

    x=0;

    if(y==0) y=1;

    else if(y==1) {y=0;clearlcd();}

  }

}

 

Ну вот, в принципе и весь код.

Давайте его соберём и прошьём контроллер и попробуем ввести из терминала какой-нибудь символ, например '1'. Делается это легко. В терминале для этого есть специальная строка внизу

 

image40

 

Но просто ввести недостаточно, чтобы данные отправились в порт. Нужно либо нажать кнопку «-> Send«, либо на клавиатуре клавишу «Enter«

После полного ввода нашего символа мы видим, что символ нормально пришел и отобразился на дисплее

 

image41

 

Введём теперь по очереди ещё два символа. Всё работает

 

image42

 

Но, как оказалось, успокаиваться на этом рано. Давайте попробуем ввести в терминальную программу длинную строку и передать её в контроллер. Чтобы видеть, что всё дошло, давайте передадим вот такую вот упорядоченную строку из 31 символа «1234567890123456789012345678901». Отправляем строку и видим, что символы дошли не все, и не просто не все, а мало какие символы вообще дошли

 

image43

 

Я думаю, здесь не сложно догадаться, почему такое происходит. А происходит это потому, что время вывода на экран символа намного больше, чем время от прихода по USART одного символа до прихода другого. То есть для работы с дисплеем по приёму непрерывных строк из USART скорость 115200 будет очень велика. Попробуем её убавить пока до 57600 бит в секунду. Для этого мы нажмем Disconneсt в терминальной программе и переключимся в ней на такую скорость

 

image44

 

Здесь переключили, теперь откроем таблицу в технической документации на контроллер и узнаем число, которое мы должны передать в функцию инициализации USART

 

image45

 

Исправим аргумент в вызове функции

 

USART_Init (16); //28800

 

Соберем код, нажмём «Connect» в терминале, прошьём контроллер, и отправим ещё раз ту же строку. Очень хорошо, что после отправки строка в терминале не стирается. Достаточно поставить в неё курсор и нажать Enter или кноку Send. Мы видим, что символов пришло больше, но не все

 

image46

 

Таким же образом убавим скорость до 28800. Узнаем число

 

image47

 

Исправим в коде

 

USART_Init (34); //28800

 

Пересоберём код, соединимся в терминале с портом и отправим строку, дописав в неё даже ещё цифру 2

 

image48

 

Мы видим, что теперь у нас на дисплее почти всё отображается. Когда я писал видеоурок, я не заметил, что всё-таки одна цифра у нас пропущена. В нижнем ряду не хватает тройки. Таким образом. оптимальная и максимальная скорость для приёма непрерывных строк и отображения их на дисплее будет всё-таки 19200 bps. При такой скорости будет всё нормально. Можно конечно было ещё поиграть с таймингами в выводе символов в файле lcd.h и добиться вывода всех символов при скорости 28800, но для строк, я думаю достаточно и 19200. Соответственно, вы можете попробовать свои скорости либо даже поиграть с добавлением дополнительного буфера в оперативной памяти контроллера, и отправкой строк сначала туда, а затем уже в другой какой-то функции на дисплей и написать в комментариях, что у вас получилось. Писать можно либо здесь, либо под видеоуроком на Youtube.

На этом работа с USART у нас закончена. Но использовать его мы на этом не прекращаем. Будет ещё много уроков, в котором мы им ещё воспользуемся.

 

 

Предыдущая часть Программирование МК AVR Следующий урок

 

Исходный код

 

Терминальная программа

 

Программатор и переходник USB-TTL можно приобрести здесь:

Программатор (продавец надёжный) USBASP USBISP 2.0

Переходник USB-TTL лучше купить такой (сейчас у меня именно такой и он мне больше нравится)

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

AVR USART. Связь МК с ПК

Один комментарий на “AVR Урок 14. USART. Связь МК с ПК. Часть 5
  1. Владимир:

    Подскажи пожалуйста как отправить по UARTу данные АЦП
    для контроля АКБ по SMS.

    • admin:

      Обычно, преобразовать в строку и отправить функцией USART_TX, которую мы написали в 39 уроке.

      • admin:

        По кусочку кода непонятно. И как именно не работает. Вообще дисплей не работает, или счётчик не считает, т.е. показывает одну только цифру, какой именно у Вас дисплей, какой в нём контроллер.

        • Владимир:

            Совсем запутался,наверно серое вещество высохло,

          сейчас программу чищу.

            Мне не на дисплей выводить даннные надо,

          а на SIM-модуль для отправки по SMS.

          MK — ATmega32

          дисплей — WG12864B

          контроллер — KS0107

           

           

           

           

           

        • Алексей:

          Текст на дисплей приходит весь, но не важно 1 символ и слово отправляю в конце приходит например
          1==

          Hello==

          Hello My Dear Fr
          iend==

          Прошу прощения это были два символа 0x0d и 0x0a

  2. Dmitriy:

    Спасибо за данный урок. Замучился уже с одним проектом. Или ресурсов Attiny 2313A не хватает, или компилятор своеобразно себя ведёт. Решил разбить на две задачи и установить две Attiny 2313A в устройство, связав их через UART.

  3. Алексей:

    Мы не изучали функцию очистки дисплея, но я ее сам написал
    void clearLCD(void)
    {
    sendbyte(0b00000001, 0);//Очистка дисплея
    _delay_ms(2);
    }
    В даташите нашел, кстати спасибо за такие подробные описание, всегда хотел научится понимать даташит. Сейчас с вами научился немного. Спасибо!!!

  4. Дмитрий:

    Добрый день! Как не менял скорость выводит три первых символа и последний. В чём может быть дело?
    Спасибо!

    • Даже не знаю, что и сказать. Пробуйте другой переходник, другой контроллер, другой порт. У меня нет конкретного ответа. Это надо находиться на месте и смотреть, но у меня, к сожалению, такой возможности нет.

  5. Семен:

    Доброго времени суток!

    Правильно я понял что у Atmega8 1 общий регистр на передачу и прием?или я ошибаюсь (оба UDR называются)
    В PIC например есть 2 отдельных регистра

  6. Fedor:

    Доброго времени суток .

    Прежде всего спасибо за уроки (просмотрел весь курс по AVR и многому научился) ! Теперь собственно главное о чем бы хотел вас спросить: есть необходимость чтобы MK в частности Atmega8 принимала не один, а 2 или 3 байта данных . Как все устроено с одним байтом предельно понятно (байт пришёл и МК вошел в прерывание где я его и считал в глобальную переменную) . Но как быть если я посылаю два или три байта . Каким образом можно реализовать данную возможность ?
    Я могу предположить что припрееме первого из 2 или трех байтов нужно выходить из вектора прерываний передавать присланные данные и затем по пришествии второго байта опять уходить в прерывания но реализовать грамотна у меня не получилось данную задумку .

    • Прерывание настраивается только на 1 байт, поэтому когда идёт серия, надо применять какие-нибудь концевые байты, для этого и существуют протоколы. А в прерывании уже следить, а не концевой ли байт к нам пришёл.

      • Fedor:

        Спасибо за пояснение по поводу протокола переда данных, но я спрашивал немного не то (видимо надо было подробнее пояснять) . Мне непонятно как реализовать саму возможность приема 2-х или 3-х байт одного за другим разом с ПК в одну переменную (скажем формата uint32_t) . Насколько я понимаю регистр UDR может принять 8 бит или один байт ( при UCSRB|=(0<<UCSZ2) , UCSRC|=((1<<UCSZ1)|(1<<UCSZ0)) ) а значит при активации вектора прерываний ISR(USART_RXC_vect) я получаю за раз только один байт в объявленную мной ранее глобальную переменную переменную . Можно ли как то так исхитрится чтобы я смог записать в одну переменную 2 — 3 байта отправленных разом один за другим с ПК используя специальное ПО (к примеру Terminal1_9_b или еще что) . Сам голову сломал но нечего дельно придумать не получилось.

  7. Trackout:

    На скоростях 115200 и 57600 выводит более-менее нормально символы. Не каждый раз, но выводит. А вот на меньших скоростях — сплошная абракадабра))

    • Trackout:

      А, нет, это я не отключал программатор от отладочной платы, из-за него, должно быть. Хотя, 31 символ приходит нормально, вот 32 — абракадабра))

  8. danylee:

    А если у меня 6 разрядный семисегментный индикатор ? как тогда прописать код ? подскажите пожалуйста !!!!! заранее спасибо !

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*