Наступила очередь рассказать о довольно интересной теме — о том, как подключить AVR микроконтроллер к компьютеру. Для этого, у микроконтроллеров есть приемопередатчик. У некоторых их, даже два.
Update:10.02.17
Использовать будем UART наиболее простой и распространенный интерфейс. Чтобы понять как он работает, представьте себе что уарт это водопроводный кран, из которого течет вода — байты. Каждая новая капля, «затирает» старую, поэтому главная задача нашей программы успевать забирать данные до того, как придут новые.
Существует два основных способа, это все время в цикле проверять наличие новых данных по флагам или настроить прерывание, по приходу нового байта. Если программа не использует задержки и скорость передачи низкая, то можно использовать первый способ, в остальных случаях, лучше использовать прерывания.
Данные приходят последовательно один байт за другим, поэтому часто UART называют последовательным портом. Если ваша программа достаточно простая, то можно принять/передать один байт и на основании этого выполнить какое то действие. Например если приняли байт 100, то включили светодиод.
if(byte == 100) { PORTB.1 = 1; } |
Только не стоит путать ASCII символ и байт, например i = ‘1’ i = 1 не одно и тоже. В чем разница можно почитать тут
Если ваша программа подразумевает, нечто более сложное, т.е. прием нескольких байт, то в прерывании по приходу данных складываем их в массив, далее в основном цикле разбираем массив. Стоит понимать, что поток не имеет ни начала, ни конца, он просто льется постоянно, поэтому нужно самому придумать какие то условности которые бы говорили о том, что сейчас пришел первый байт, а сейчас последний. Типовое решение, это использование спецсимволов, либо таймаутов. Пример можно посмотреть тут
Теперь перейдем к железу. У Atmega8 всего один приемопередатчик, PD0 — Rx, receiver (приемник) и PD1 — Tx, transmitter (передатчик).
Аналогичные ножки есть на нашем переходнике FT232. Соединяем микроконтроллер и переходник между собой. В протеусе это будет выглядеть так:
Напишем программу, которая по сигналу от компьютера будет включать светодиод и выключать. В CodeVision создаем новый проект, микроконтроллер atmega8, частота 8МГц (тактирование от кварца), на закладке USART включаем приемник и передатчик.
Обратите внимание, при текущей частоте микроконтроллера и скорости передачи, ошибка составляет 0,2%, т.е. данные могут теряться, но вероятность этого крайне низкая. Порт B настроим как выход — к нему подключим светодиод.
#include <mega8.h> #include <stdio.h> void main(void) { char data; PORTB=0x00; DDRB=0xFF; PORTD=0x00; DDRD=0x00; // USART initialization // Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: On // USART Transmitter: Off // USART Mode: Asynchronous // USART Baud Rate: 9600 UCSRA=0x00; UCSRB=0x10; UCSRC=0x86; UBRRH=0x00; UBRRL=0x33; while (1) { data=getchar(); if(data=='1') { PORTB=0xFF; } if(data=='0') { PORTB=0x00; } }; } |
В текущем алгоритме используется простой способ функция getchar() постоянно проверяет не появились ли новые данные. После того как данные пришли — записываем их в переменную data; если пришла единичка, зажигаем светодиод; если 0, то выключаем его. Еще раз обратите внимание ‘1’ означает ASCII символ 1, реально байт будет равен 0x31. Сделано так, потому что большинство терминалов при нажатии кнопки на клавиатуре, отправляет именно символы.
Собираем схемку и прошиваем. Подключаем к компьютеру. Запускаем программу KeTerm или другой терминал. Подсоединяемся к нужному com порту. Шлем единичку — светодиод включается, шлем 0 светодиод выключается.
Урок был бы неполным, если не применить наши знания по C#.
Создадим проект, нарисуем 2 кнопки и последовательный порт
В свойствах последовательного порта не забудьте настроить PortName, он должен соответствовать номеру порта переходника FT232. Добавим события: по клику на 1 кнопку — отсылаем 1, по клику на 2 кнопку — отсылаем 0, при загрузке формы — открытие порта, при выходе из формы — закрытие порта.
private void button1_Click(object sender, EventArgs e) { serialPort1.WriteLine("1"); } private void button2_Click(object sender, EventArgs e) { serialPort1.WriteLine("0"); } private void Form1_Load(object sender, EventArgs e) { serialPort1.Open(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { serialPort1.Close(); } |
И напоследок, видео работы всего этого безобразия)
Update: По просьбе Евгения программа немного изменена, теперь при запуске программа автоматически проверяет все ком порты, если есть активные, то они заносятся в comboBox1.
1 2 3 4 5 6 | private void Form1_Load(object sender, EventArgs e) { string[] myPort; //создаем массив строк myPort = System.IO.Ports.SerialPort.GetPortNames(); // в массив помещаем доступные порты comboBox1.Items.AddRange(myPort); //теперь этот массив заносим в список(comboBox) } |
Исходники программы
суть уловил, но почему нельзя передать сразу число 67? а надо его передавать по разрядам и в виде символа?
вот так например передадим: UDR=67;
и примем вот так: temperarure=UDR;
и получится в переменной temperarure десятичное число 67, и его спокойно на экранчик выводим на число, а не как символ
Правильнее делать именно так как вы и говорите. Только для не забывайте для дисплея ваше число 67 это 1 байт = 1 символ, если у дисплея шрифт встроенный то что под номером 67 записано, то он и выведет. Поэтому придется 67 переводить в 2 байта = 2 символа.
Зачем по разрядам? 67 влезает в байт, вот и передавайте его как число. А в приемнике разбиваете на разряды и переведя в символы выводите.
Добрый день! Подскажите, пожалуйста, если использовать для подключения к USB порту стандартный преобразователь уровней COM-USB UPort 1110 фирмы MOXA, а для подключения к МК микросхему MAX232, получится ли реализовать Ваш проект? Просто нет микросхемы FT232R, и COM порта тоже нет.
получится
Спасибо за статью http://avr-start.ru/?p=4557, теперь стало понятно как из буфера считать нужные байты, но не могу никак понять следующее:
Когда мы с терминальной программы отправляем символ «1» (единицу) по UART, то по ASCII в буфер записывается число 0x31 (или 39 в десятичной). А чтобы 0x31 записать в переменную в виде той же единицы, что нужно сделать? То есть отправил с терминальной программы 1 и в переменную temp тоже записалось 1 (а не 39).
точнее 0x31 = 49 в десятичной.
+0x30
Спасибо большое, за ответы. Сделал вот так data=UDR-0x30; и теперь в буфер записываются числа в нужном виде. )
сделал тупой до безобразия код(крутиться в бесконечном цикле)
chislo=UDR;
if (chislo==0x30) PORTC.0=0; else PORTC.0=1;
и приём байта осуществляется с задержкой в один байт, то есть отправил один байт, ничего не происходит, отправляю второй байт, получай первый, отправляю третий байт, получаю второй, и тд
а вот если через флаг принимать байты, то всё пучком
if (UCSRA>>7==1) {
chislo=UDR;
if (chislo==0x30) PORTC.0=0; else PORTC.0=1; }
что я делаю не так, и откуда в первом случает задержка образуется?
если посмотреть даташит атмела, то в нем рекомендуют именно сначала читать бит статуса, а потом забирать данные. почему именно так, а не иначе — возможно, что только после того как прочитан бит статуса данные из сдвигового регистра попадают в UDR
понял, спасибо, а я думал что фик с ним с флагом, мне нужен был код кое что проверить, вот и не заморачиваясь с флагами и всякими фишками тупо в лоб постоянно опрашивал UDR
по пробовал не много редактировать и кол-во светодиодов увеличить, получилось, интререстно стало , а как цифру 10 послать например и что бы зажёгся светодиод номер 10?
{
data=getchar();
if(data==’1′)
{
PORTB=0b00000001;
}
if(data==’2′)
{
PORTB=0b00000010;
}
if(data==’3′)
{
PORTB=0b00000100;
}
if(data==’4′)
{
PORTB=0b00001000;
}
if(data==’5′)
{
PORTB=0b00010000;
}
if(data==’6′)
{
PORTB=0b00100000;
}
if(data==’0′)
{
PORTB=0x00000000; //все выкл //
слать можно только побайтово, если нужно 2 символа, то каждый нужно отдельно отправлять. если же нужно отправлять просто число, то отправляйте 10 без кавычек
можно ли через UART осуществить радиоуправление роботом? например я в компьютер забиваю план комнаты, а компьютер посылает сигнал на радиопередатчик, где через юарт управляется микроконтроллер робота — пылесоса, т.е компьютер просто посылает роботу маршрут движения, а робот основываясь так же на собственные чувства(на ультразвуковые датчики) корректирует маршрут(мало ли что ждет его в пути следования), то есть обходит препятствия, ножки стульев и столов и тд. Как думаете, можно ли вообще такую систему вообще осуществить, такому профану как я? 😥
сделать можно, было бы желание
какие бы вы посоветовали микросхемы передатчики для данного дела?
Берите bluetooth, им можно управлять с телефона или планшета. Если планируются много данных передавать, то можно взять HC-05, если мало то можно взять low energy HC-08, или что то подобное.
Спасибо Админ за хороший сайт !
Если можно приведите пример работы USART с прерываниями RX TX
и отправкой и принятием чисел к примеру от 0 до 400.
Спасибо вам огромное
Подскажите пожалуйста,как на стороне С# принять и отобразить информацию с мк
там есть событие data received, создаете обработчик и забираете инфу
Спасибо огромное,попробую! 🙂
admin on 16.10.2016 в 02:41
«слать можно только побайтово, если нужно 2 символа, то каждый нужно отдельно отправлять. если же нужно отправлять просто число, то отправляйте 10 без кавычек»
Делаю так:
data=UDR;
if(data==’1′)PORTB.0=1;
if(data==10)PORTB.0=0;
Ввожу в протеусе в терминале 1 зажигаю светодиод, ввожу 10 он не тухнет(. Что не так?
почитайте еще раз про переменные, каждая цифра это байт в ASCII, вы шлете 2 байта, а сравниваете 1 байт.
Спасибо админу за ценную статью и сайт тоже! Решил собрать систему управления освещением. 8 каналов. Каждый канал Включить светодиод + Выключить светодиод.
Посылаю 1 в uart -> включаю portb.0
0 -> выключаю portb.0
2-> включаю portb.1
3-> выключаю portb.1
и так далее пока не придет portb.5 (в C# посылаю цывфру 10..) и дальше тоже ничего не работает. Судя по коментариям в статье предпологаю что скорее всего посылать надо поодиночке цыфру 10 (тоесть 1 и 0 ) но КАК посылать это мой вопрос ? )
добавил немного
Тоесть я посылаю символ ASCII в C# serialPort1.WriteLine(«10»); и принимаю на стороне микроконтроллера как цыфру?
if(data==’10’) { PORTB.5=1;}
Как было бы правильно посылать и принимать 10-ку ?
или я сравниваю 2 байтную информацию с однобайной или как вообше ?
Прочитайте еще раз, байт хранит в себе значение от 0 до 255, символ может быть закодирован одним или несколькими байтами. Теперь определитесь для себя, что вы будете использовать терминал или C#. Большинство терминалов выводят только символы. В статье я передавал именно символы, чтобы те люди которые, не хотят ставить C# могли с помощью терминала проверить работу устройства. Если вы будете в терминал слать 10 то он выведет какую то кракозябу которая закодирована под номером 10, а не ‘1’ и ‘0’. В C# вы можете передавать байты хоть в hex значениях, хоть в виде ASCII кодов, в примере отправляются также символы, чтобы можно было использовать прошивку и для терминала и для c#
Думаю что я начинаю понемать… 😀 При посылке символа 10 этот символ соответствует определенному значению в таблице ASCII на стороне мк.. Следовательно сравнивать мне надо не 10 а именно этот символ ASCII который соответствует определнноми значению из таблицы символов ASCII? Пробовал разные варианты но пока светодиодик не зажег )
В целом так, но если вам достаточно проверки байта, то отправляйте и проверяйте 1 байт,
на стороне мк
if(data==10)…
на стороне c#
byte[] buf;
buf[0] = 10;
serialPort.Write(buf, 0, 1);
на стороне c#
byte[] buf;
buf[0] = 10;
serialPort.Write(buf, 0, 1);
тут я уже совсем запутался. могли ли вы описать что делает каждая строка ?
создаете массив и толкаете туда 1 байт, который потом, отправляете. вообще читайте маны по c#, примеров и объяснений тонны.
Дайте пожалуйста пояснения и примерчик.
Тут я понемаю так…
byte[] buf; // в масив непонятного размера byte[] заносим buf…
buf[0] = 10; // на первое место (0) масива buf[0] заносим 10
serialPort.Write(buf, 0, 1); // посылаем buf через серийный порт. 0 и 1 чтоза параметры?
В обшем у меня в полная каша в голове )
Введите в гугл serialPort.write с# и откройте первую ссылку, все станет понятно.
Ели бы я знал бы чтотам к чему я бы не стал бы тут спрашивать. Спосибо конечно за совет.
Учитесь пользоваться гуглем! В первой же ссылке: Write(byte[] buffer, int offset, int count) — Записывает указанное число байтов в последовательный порт, используя данные из буфера. Далее пояснения: buffer — Массив байтов, данные из которого записываются в порт. offset-отсчитываемое от нуля смещение в массиве buffer (в байтах), с которого начинается копирование байтов в порт. count — Количество записываемых байтов. По c# очень много документации и вся в доступном изложении. Я правда не вижу смысла копировать информацию, которой все завалено.
Спасибо Админ! Буду разбираться. С гоолем знаком конечно но для меня это как тонуть в маленькой луже.. )
Добрый вечер! а переменную в обработчике событий можно объявлять ?
Делаю так:
private void button12_Click(object sender, EventArgs e)
{
byte[] buf;
buf[0] = 10;
serialPort1.Write(buf, 0, 1);
}
Выводит ошибку: Error 1 Use of unassigned local variable 'buf'
можно, но под любой массив нужно выделять память.