Шаг 3. Разговорчики через UART

В этот раз мы попробуем связать компьютер и нашу микросхемку) Чуточку модернизируем предыдущую схему, перепишем программу и получим, что при нажатии кнопок на клавиатуре у нас будут загораться восьмибитный код символа (если единичка - соответствующий светодиод горит, если нолик - не горит).
Что потребуется нам в этот раз и подручных средств: из старой схемы остаются 8 светодиодиков и 9 резистров, из новенького - пара резисторов этак на 10 кОм да переходник Usb - Usart - можно сделать самому, если вдруг есть старые дата-кабели от телефонов, или же попросту заказать где-нибудь на алиэкспрессе. А также нам нужен конденсатор - у нас на 100 мкФ при максимальном напряжении 25В.
Чуть-чуть о том, что такое USART и с чем его едят:
UART - это последовательный асинхронный интерфейс для передачи данных.
А теперь понятно:
Интерфейс - совокупность средств, методов и правил взаимодействия между элементами системы.
Последовательный - значит, одна линия для передачи данных, и биты информации передаются один за одним.
Асинхронный - значит, и у приёмника, и у передатчика есть свой генератор импульсов - он задаёт, с какой частотой будут передаваться биты.
Можно сказать, чем-то похоже на морзянку) Сидит по разные концы провода такая парочка: один готов передавать, другой - получать.
При передаче по интерфейсу USART каждому блоку данных (обычно байту - 8 бит) предшествует СТАРТ-бит, сигнализируюший приёмнику о начале передачи, и завершает его СТОП-бит, обеспечивающий паузу между передачами.
Ну что ж, с теорией покончили, ближе к делу! Вот схемка:

Рисунок 1. Схема
Рисунок 1. Схема.

В принципе, тут добавлено не так уж много, и вроде всё более-менее должно быть понятно… Новый элемент - конденсатор, он используется как стабилизатор при возможных скачках напряжения. Возможно, возник вопрос «а зачем нужны резисторы при соединении переходника и микросхемки?» - но об этом дальше.
Пока же разберёмся, что вообще нам нужно:

  1. Включаем наш передатчик, настраиваем его на нужную частоту
  2. Ждём, когда придут данные - код символа
  3. Дождались - включаем наши светодиодики… Только вот проблема - мы-то привыкли, что единичка - включено, а нолик - выключено. А вот если мы подадим на вывод, к которому подключён светодиод, логическую 1 - ничего и не загорится((((Поэтому проделываем над нашим считанным символом инверсию - где были нолики, там станут единицы…
  4. Отвечаем компьютеру тем же символом)

Ну вот, в принципе и всё… Будем смотреть конкретнее:
Чтобы настроить наш передатчик… Ура, за нас всё сделано - достаточно только задать скорость: библиотечная процедура UART1_Init(9600)
Отлично, теперь смотрим, что нужно делать в цикле:
Сначала проверяем, пришли ли нам какие-нибудь данные. Тут опять за нас уже подготовили функцию UART1_Data_Ready(). Она возвращает «1», если что-то новенькое до нас дошло, и «0» - если нет.
Теперь, соответственно, нам нужно прочитать символ. Так как в дальнейшем мы его будем ещё использовать, то вначале заведём переменную типа char, и назовём её, например,cSymbol. Ну а чтобы читать данные, есть готовая функция - UART1_Read().
С этим разобрались, едем дальше: зажигаем наши светодиодики, не забыв проделать инверсию: portB = ~cSymbol;
И напоследок, отсылаем компьютеру «эхо» - тот же символ: UART1_Write(cSymbol)
Вот и всё
Соответственно, получаем программу:
Си:

//передача символа от компьютера к микросхеме по UART. Отсылка символа обратно и отображение символа на светодиодной дорожке
typedef unsigned char byte;
//определяем ссылки на регистры ввода-вывода
#define ddrLED_Road DDRB
#define portLED_Road PORTB
#define ddrUART DDRD
#define portUART PORTD

//глобальные переменные и константы
const byte DDR_LED_ROAD_INIT = 0xFF;                                              //настройка порта со светодиодами - на выход и
const byte PORT_LED_ROAD_INIT = 0xFF;                                             //светодиоды не горят
const byte UART_RX_MASK = 0x01;                                                   //маски для выводов UART
const byte UART_TX_MASK = 0x02;
  
void main()
{
  byte cRecievedSymbol;                                                         //переменная для хранения символа, принимаемого через UART

  //инициализируем все порты ввода-вывода на вход без подтяжки
  DDRA = 0;
  PORTA = 0;
  DDRB = 0;
  PORTB = 0;
  DDRD = 0;
  PORTD = 0;

  //инициализация занятых портов
  ddrLED_Road = DDR_LED_ROAD_INIT;
  portLED_Road = PORT_LED_ROAD_INIT;
  ddrUART = (ddrUART | UART_TX_MASK) & (~UART_RX_MASK);
  portUART |= UART_RX_MASK | UART_TX_MASK;
  //инициализация периферии - в нашем случае, UART-а
  UART1_Init(9600);

  while(1)
  {
    if(UART1_Data_Ready() == 1)                                                 //если получили данные
    {
      cRecievedSymbol = UART1_Read();                                           //считали символ
      portLED_Road = ~cRecievedSymbol;                                          //отобразили принятый символ на светодиодной дорожке
      UART1_Write(cRecievedSymbol);                                             //и отправили его обратно
    }
  }
}

Паскаль:

//передача символа от компьютера к микросхеме по UART. Отсылка символа обратно и отображение символа на светодиодной дорожке
program UART;
const
   DDR_LED_ROAD_INIT = 0xFF;                                                      //настройка порта со светодиодами - на выход и
   PORT_LED_ROAD_INIT = 0xFF;                                                     //светодиоды не горят
   UART_RX_MASK = 0x01;                                                           //маски для выводов UART
   UART_TX_MASK = 0x02;
var
   //указатели и константы для портов - где будет UART и где будут светодиоды
   ddrUART : byte at DDRD;
   portUART : byte at PORTD;
   ddrLED_Road : byte at DDRB;
   portLED_Road : byte at PORTB;

   cRecievedSymbol: byte;                                                       //переменная для хранения символа, принимаемого через UART
begin
   //инициализируем все порты ввода-вывода на вход без подтяжки
  DDRA := 0;
  PORTA := 0;
  DDRB := 0;
  PORTB := 0;
  DDRD := 0;
  PORTD := 0;

  //инициализация занятых портов
  ddrLED_Road := DDR_LED_ROAD_INIT;
  portLED_Road := PORT_LED_ROAD_INIT;
  ddrUART := (ddrUART or UART_TX_MASK) and (not UART_RX_MASK);
  portUART := portUART or UART_RX_MASK or UART_TX_MASK;
  //инициализация периферии - в нашем случае, UART-а
  UART1_Init(9600);

  while(1) do
  begin
    if(UART1_Data_Ready() = 1) then                                             //если получили данные
    begin
      cRecievedSymbol := UART1_Read();                                          //считали символ
      portLED_Road := not cRecievedSymbol;                                      //отобразили принятый символ на светодиодной дорожке
      UART1_Write(cRecievedSymbol);                                             //и отправили его обратно
    end;
  end;
end.

Готовая программка - тут.

А вот как это выглядит:

Рисунок 2. Результат
Рис. 2. Результат

Ну, естественно, нужна программа для «общения» с микросхемой. Так вот, в наших средах есть свой личной терминальчик (Ctrl + T). Наша задача - поставить правильно частоту, выбрать порт, к которому подключён передатчик, определить, что у нас один стоп-бит, отключить контроль чётности (parity) и управление потоком передачи данных (flow control), а также выбрать размер "слова" (то, сколько бит мы передаём за раз) 8 бит - именно так настраивается UART с помощью библиотечной функции. Нажимаем кнопку "Connect&, и вуаля - все работает! Вводим в строку "1", и на светодиодной дорожке появляется... 0x31 - код ANSII-символа "1". Чтобы увидеть действительную единицу, нужно использовать терминальчик с отправкой шестнадцатиричных чисел - например, терминал COM Port Toolkit.

Теперь пару слов о волшебных резисторах, или как жадность может погубить микросхемку:
В этот раз мы столкнулись с такой коварной вещью, как паразитное питание… После выключения из сети - о, боги! - у нас всё равно продолжали гореть светодиоды… Дело в том, наша микросхемка искала какой-нибудь другой источник питания - и находила его в лице нашего переходника; иными словами, она работала от usb. Однако питания для нормальной её работы, естественно, не хватало, вот и начались страшные и таинственные глюки через раз.
А наши резисторы легко и просто решают проблему - они просто не пускают ток, который пытается вытребовать себе микросхемка) Вот как-то так)

Автор - Moriam
Обсудить на форуме