Сегодня зачастую электронные устройства, включающие в свой состав датчики, создаются с применением микроконтроллеров. Микроконтроллер представляет собой электронную схему, которая может выполнять самые разные задачи: контроль температуры в помещении, управление автомобильным двигателем или промышленным роботом и многие другие. Весьма упрощенная структурная схема микроконтроллера показана на рисунке 2.1. Обратите внимание, так как микроконтроллер состоит из модулей общего назначения (микропроцессор, память, модули ввода - вывода и т.д.), то взглянув на схему, нельзя сказать какую задачу он сейчас выполняет: возможно, следит за состоянием автоматизированной линией, а, может быть, загружает интернет страницу в планшете. Функционирование микроконтроллера определяется программой, загруженной в него.
Программа представляет собой последовательность команд, которую выполняет микропроцессор. Команды определяются выбранным языком программирования, которых достаточно много (рисунки 2.2-2.3): низкоуровневые (ассемблеры), высокоуровневые общего назначения (Бейсик, Паскаль, Си и Си++), высокоуровневые специального назначения (Simatic Step 7, PHP, Arduino, Algorithm Builder).
В этой и последующих лабораторных работах использоваться будет Arduino, практически идентичный языку программирования Си++ с очень небольшими особенностями из-за ориентированности на обучение программированию микроконтроллеров. В наши дни многие программы написаны именно на Си++, язык включает в себя практически все конструкции, которые можно встретить в других языках программирования: владея Си++, освоить любой другой можно в самые кратчайшие сроки. Богатые возможности языка позволяют создавать программы различных сложностей: от управления светодиодами до операционных систем (Windows, Android и т.д.). Конечно, он является не самым простым для изучения. Поэтому мы рассмотрим поверхностно лишь те конструкции языка, которые позволят создавать простые программы для работы с датчиками и выполнить лабораторные работы. В случае затруднений Вы всегда можете воспользоваться литературой из списка, полученного на вводном занятии.
Рисунок 2.1 – Упрощенная структура микроконтроллера
Рисунок 2.2 – Классификация языков программирования
Рисунок 2.3 – Сравнение операций сложения на низкоуровневом и высокоуровневых языках программирования
На рисунке 2.4 изображен один из вариантов контроллера Arduino. Контроллер включает следующие основные узлы:
1. Микроконтроллер, часто Atmega1280 или Atmega32U2;
2. разъем питания 6-12 вольт;
3. интегральный стабилизатор напряжения LM7805 преобразующие напряжение с разъема питания от 6 до 12 вольт в 5 вольт, которое подается на выводы питания микроконтроллера;
4. USB – разъем (на разных контроллерах может быть нескольких типов: обычный, mini, micro);
5. Интерфейсная микросхема FT232 с необходимой обвязкой, организующая связь с персональным компьютером через USB. Стоит отметить, что эта микросхема составляет значительную долю в стоимости всего контроллера. В микроконтроллер Atmega32U2 встроена аппаратная поддержка USB и FT232 отсутствует.
6. Обвязка контроллера: фильтры питания аналого-цифрового преобразователя, кварцевый резонатор с конденсаторами, обеспечивающий тактовую частоту процессора, токоограничивающие резисторы и другие защитные элементы;
7. Разъемы для удобного подключения устройств, в том числе датчиков, к выводам микроконтроллера.
8. Индикаторный светодиод, соединенный с 13 выводом микроконтроллера. Если программа установит напряжение 5 вольт на 13 выводе, светодиод загорится, если ноль вольт, погаснет.
Рисунок 2.4 – Контроллер Arduino DFROBOT MEGA2560
Сердцем платы является микроконтроллер Atmega 1280, имеющий 100 выводов. Некоторые выводы имеют строго определенное назначение, например питание, назначение других определяется программой. Чтобы поместить программу во Flash-память кристалла микроконтроллера используется USB-интерфейс. На рисунке 2.5 представлен скриншот интегрированной среды разработки Arduino, с помощью которой программа может быть написана, проверена и откомпилирована, прошита в микроконтроллер и отлажена.
Рассмотрим очень простую программу, работу которой можно наблюдать визуально: мигающий светодиод, подключенный к 13 выводу. Для ее реализации Вам потребуется выполнить следующие шаги.
Получите один микроконтроллер с соединительным шнуром на бригаду у преподавателя. Во избежание повреждений контроллера в результате короткого замыкания ни в коем случае не размещайте плату на металлическом корпусе компьютера или металлических поверхностях лабораторного оборудования: нижняя сторона платы имеет выступающие паяные выводы элементов. Подключите контроллер к свободному USB разъему, загорится индикатор питания. Питание контроллера может осуществляться как через специальный разъем 6-12В, так и от компьютера через USB.
Проверьте, что контроллер обнаружен компьютером. Нажмите правой кнопкой на иконку «Компьютер», выберите «Свойства». Вызовите диспетчер оборудования. Откройте Порты (COM и LPT). В списке должно быть устройство, подключенное к COM-порту с номером больше 1 (Последовательный порт COM1 присутствует всегда). Например, это может быть что-то вроде Arduino Leonardo (COM3). Запомните номер порта и закройте диспетчер.
Рисунок 2.5 – Среда разработки Arduino
Если плата подключена впервые к компьютеру, то устройство не будет установлено без соответствующих драйверов. Драйверы расположены в папке с Arduino: для контроллеров на основе FT232 и без нее. В случае необходимости установите их и повторите предыдущий шаг.
Запустите среду разработки. Введите текст программы, представленный ниже. Количество пробелов, символов табуляций, пустых строк между отдельными элементами программы не важно для ее работы и влияет только на визуальное оформление текста. С другой стороны регистр букв («маленькие» или «большие») имеет значение, не ошибитесь. Обратите на то, что некоторые строчки заканчиваются символом «;».
void setup()
{
pinMode(13, OUTPUT);
}
void loop()
{
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(500);
}
Нажмите на кнопку «Проверить» на панели инструментов Arduino. Если ошибок не найдено, программа успешно скомпилируется, о чем будет выведено соответствующее сообщение в нижней черной части окна.
Теперь необходимо ее загрузить в контроллер. Выполните команду меню Сервис → Плата и выберите название используемой платы или ее аналог. Далее выполните команду меню Сервис → Последовательный порт и выберите номер используемого порта (который Вы посмотрели и запомнили предварительно в диспетчере устройств). Нажмите на кнопку «Загрузить» на панели инструментов. Через несколько секунд, если не возникло ошибок, программа будет загружена в микроконтроллер, а индикаторный светодиод начнет мигать.
Разберемся в работе программы. Перед рассмотрением текста программы, необходимо определиться с некоторыми используемыми далее терминами:
«пин» - вход или выход, подключённый к чему-либо: светодиоду на выходе или кнопка на входе.
«цифровой» - значение HIGH или LOW (как вкл/выкл или один/ноль). Пример: состояние выключателя.
Большинство пинов являются цифровыми и могут работать в режиме входа или выхода.
Все выходы в микроконтроллерах, используемых на лабораторных работах, являются цифровыми. То есть на выходе программа может установить единицу (5В) или ноль (ноль вольт), но установить, например, 2В не получится.
То же самое при чтении входов: на большинстве выводах нельзя определить промежуточные значения напряжений. Исключение составляют входа аналого-цифрового преобразователя (АЦП), их немного, вынесены на контроллере в отдельный разъем, обозначаются в программах как A0, A1 и т.д., и могут быть использованы для измерения напряжений в диапазоне от ноля до пяти вольт.
Блок-схема алгоритма загруженной программы приведена на рисунке 2.6. Программа состоит из двух частей: инициализация, выполняется один раз при включении контроллера, и бесконечное повторение процесса включения и выключения светодиода.
Рисунок 2.6 – Блок-схема алгоритма тестовой программы
Программы могут состоять из миллионов отдельных команд. Чтобы в них ориентироваться, программы разбиваются на отдельные части – подпрограммы. Примером подпрограммы может являться вывод символа на текстовый дисплей, подключенный к контроллеру. Чтобы вывести отдельный символ необходимо сформировать импульсы на разных входах дисплея. Для этого необходимо изучить документацию на дисплей, его интерфейс, команды и т.д. Не очень удобно обращаться к документации при необходимости вывода очередного символа. С другой стороны можно написать подпрограмму - последовательность команд, предназначенную для многократного выполнения. Подпрограмма будет формировать управляющие сигналы для любого символа, который будет передан ей. Затем вызывать ее из основной программы, передавая отдельные символы. При этом уже не будет необходимости даже помнить об интерфейсе дисплея, а подпрограмма и основная программа может быть написана разными людьми. Можно создать и другую подпрограмму – вывод текста, она будет вызывать подпрограмму вывода символа. В свою очередь работа с текстовым дисплеем из основной программы еще сильнее упроститься. Любую даже самую сложную программу можно свести к подпрограммам, имеющим разную степень вложенности как матрешка, образующие иерархическую структуру. Такой подход называется структурным программированием и является основным в языке Си.
В языках программирования высокого уровня используется два типа подпрограмм: процедуры и функции. В процедуры принимают данные в виде одного или нескольких аргументов (выводимый символ в примере выше) и выполняют определенный код. Функции в отличие от процедур дополнительно возвращают значение (например, результат вывода – успешно или произошла ошибка в примере выше). В языке Си используются только функции. Следующая функция принимает вещественное число и возвращает число в два раза больше.
float mult2(float x)
{
return x * 2;
}
Первая строчка – заголовок функции.
float – тип данных для хранения вещественных чисел, занимает 4 байта в оперативной памяти микроконтроллера.
mult2 – уникальное название (идентификатор) функции, выбираемое самостоятельно. Может состоять только из латинских букв, цифр и символа подчеркивания. Не может начинаться с цифры! А также не может совпадать с командой Си++ или с именем функции, объявленной ранее.
В скобках перечисляются аргументы через запятую: тип и имя переменной. В нашем случае аргументом будет переменная x типа float. Некоторые другие варианты функций:
float function2() ← аргументы могут отсутсвовать;
int function3(float x, int y) ← может быть несколько аргументов.
Обратите внимание на новый тип int, он предназначен для хранения только целых чисел. Разрядность int зависит от платформы и сегодня обычно варьируется от 16 до 64 бит. А вот тип long является аналогом int, но с фиксированной разрядностью в 32 бита. Операции с целыми числами намного быстрее, чем с вещественными.
А что делать, если нет необходимости возвращать результат работы подпрограммы, то есть необходима процедура? Для этого используется специальный тип void(пустота, ничего). То есть формально функция и что-то возвращает, но результат не занимает в памяти место, не существует:
void function4(int x) ← процедура по сути.
Далее в фигурных скобках следует тело функции – выполняемые ею команды. Кроме заголовков функции и скобок вероятно все конструкции, с которыми Вам придется столкнуться при выполнении работ, заканчиваются точкой с запятой. Наша функция mult2 состоит только из одной команды (return x * 2;). Команда return возвращает управление к точке, откуда функция была вызвана. При этом возвращает значение выражения x умноженное на 2. Команда return должна присутствовать в любой функции, возвращающее значение типа отличного от void. В случае, если возвращается void, можно записать так:
void function5()
{
return;
}
А впрочем, в этом случае return можно пропустить, что и было сделано в самой первой рассмотренной программе, управление вернется после выполнения всех команд функции:
void function6()
{
}
Теперь рассмотрим, как вызываются функции.
void main()
{
float y;
y = mult2(4);
y = mult2(y);
function4(10);
function5();
return;
}
Очевидно, что в любой программе, состоящей только из функций, должна существовать функция, которая вызывается «сама» (на самом деле операционной системой или микроконтроллером), с которой начнется работа программы – главная функция. В Си у нее имеется уникальное имя main (главная). Первая строчка программы «float y;» определяет новую переменную в программе с именем y и типа float. Любые переменные перед первым использованием должны быть определены, микроконтроллер должен выделить необходимое количество ячеек памяти для нее. Кроме варианта выше, приведем еще способы:
float x, y, pressure; ← определяются переменные x, y, pressure (давление), все типа float, начальные значения неизвестны;
int temperature = 20; ← определяется переменная temperature целочисленного типа и ей сразу же присваивается начальное значение;
float y = mult2(4); ← В Си++ можно и так.
Следующая строчка программы – вызов функции mult2. Запись и действие как в математике, переменной y будет присвоено значение 8. А после выполнения следующей строчки y станет равной 16. И даже такой вариант вызова функций является верным:
y = mult2(5) + mult2(2);
Вызов будет происходить слева направо, возвращаемые значения сложатся, и результат будет присвоен переменной y.
Переменные, определенные внутри функции, существуют только во время одного вызова этой функции.
void setup()
{
int variable1 = 5;
}
void loop()
{
variable1 = variable1 + 1;
}
Данная программа ошибочна: в функции loop() переменная не определена! Даже если ее заново определить в loop(), это будет уже совсем другая переменная variable1. Также стоит еще раз отметить, если функция завершается, то все переменные, определенные в ней, уничтожаются. Иногда требуются глобальные переменные, доступ к которым необходим из любой части программы. В этом случае их необходимо определить вне функций:
int variable1 = 5;
void setup()
{
}
void loop()
{
variable1 = variable1 + 1;
}
Данная программа каждый шаг будет увеличивать значение переменной на 1. Но в общем случае стоит избегать большого числа таких переменных.
Вернемся теперь первой программе. Первая функция выглядит так:
void setup()
{
pinMode(13, OUTPUT);
}
Как Вы можете теперь догадаться, функция состоит из единственной команды. И эта команда всего лишь вызов другой функции pinMode («режим пина»). А где описана эта функция? Ответ: в среде разработки Arduino. Можете попробовать отыскать ее код в текстовых файлах. На самом деле функция записывает соответствующие биты регистра направления микропроцессора, что в свою очередь изменяет состояние транзисторных ключей порта. Это приводит к появлению высокого сопротивления между выводом и землей в случае работы пина как вход, снижая потребляемый ток, и низкого в случае работы как выход, обеспечивая достаточный ток примерно в 20 мА для свечения светодиода.
Сейчас же достаточно запомнить, что у функции два аргумента: номер пина и его режим работы. Режим работы задается константами OUTPUT (выход), INPUT(вход). Если Вы подключаете источник напряжения к пину (например, датчик) используйте INPUT. Обратите внимание подключать к микроконтроллеру можно только датчики с диапазоном выходных напряжений от ноля до пяти вольт. Многие промышленные датчики не удовлетворяют этому условию, необходимо применять специальные согласующие схемы, например, на операционных усилителях.
Если Вы подключаете устройство, принимающее сигналы от микроконтроллера, используйте OUTPUT. Однако, максимальный ток одного вывода микроконтроллера типа Atmega только 40 мА – напрямую подключить десяток светодиодов к одному выводу не получится! Впрочем, к разным тоже - максимальный ток по линии питания VCC и GND 200мА.
Превышение максимальных токов в результате неверно выбранных режимов в программе приведет к повреждению контроллера.
Вторая функция loop состоит уже из 4 вызовов функций.
void loop()
{
digitalWrite(13, HIGH); ← установка высокого уровня на 13 пине, светодиод загорелся
delay(1000); ← задержка 1000 миллисекунд, т.е. 1 секунда
digitalWrite(13, LOW); ← установка низкого уровня на 13 пине, светодиод погас
delay(500); ← задержка 500 миллисекунд, т.е. полсекунды
}
Две новые функции также являются стандартными в Arduino. Функция выполнит лишь одно мигание, ее необходимо вызывать постоянно из главной функции main. Но где она? Она описана в среде Arduino примерно так (точный текст чуть более сложен его можно найти в файлах среды):
void main()
{
setup();
while (1)
loop();
}
где while оператор цикла следующего формата:
while(выражение)
команда;
Команда будет вызываться пока выражение не ноль, а в нашем случае всегда! В операторах цикла и условий (далее в работе №4) можно задавать составные команды:
while (выражение)
{
команда1;
команда2;
команда3;
}
Все в фигурных скобках считается одной командой.
Функции setup и loop не определены в Arduino. Их необходимо написать самостоятельно. Таким образом, программа на Си должна включать в себя минимум одну функцию main, а программа на Arduino минимум две функции setup и loop.
Варианты заданий
4.1 Составьте программу, циклически формирующую световые импульсы светодиодом в последовательности, указанной в таблице 2.1.
№ бригады
Последовательность световых импульсов
Короткий-длинный-длинный
Короткий-короткий-длинный
Короткий-длинный
Короткий-короткий-длинный-длинный
Длинный-длинный-длинный-короткий
Короткий-короткий-короткий-длинный
Длительность паузы всегда постоянна и равна половине секунды, длительность короткого импульса полсекунды, длительность длинного импульса полторы секунды.
Содержание отчета по лабораторной работе
Отчет должен содержать следующие обязательные пункты:
1. титульный лист;
2. цель работы, программу работы и задание для своего варианта;
3. текстовое описание выполняемых действий в процессе отладки программы из пункта «Краткие теоретические сведения»;
4. блок-схему алгоритма Вашей программы согласно заданию.
5. исходный текст программы.
6. выводы по работе.
Вопросы для защиты лабораторной работы
1. Поясните работу программ, приведенных в отчете.
2. Поясните основные конструкции языка Си, приведенные в кратких теоретических сведениях.
3. Перечислите основные методы измерений.
4. В чем заключаются компенсационный метод, метод отклонений? Приведите примеры.
5. В чем заключаются методы аналогий, повторений и перечисления? Приведите примеры.
6. В чем заключаются методы чередований и подстановки? Приведите примеры.
7. Что такое когерентная выборка? Как данная стратегия измерений используется в осциллографах?
8. Каковы различия между дискретными и аналоговыми датчиками?
Лабораторная работа №3
Подключение ультразвукового датчика расстояния к микроконтроллеру
Цель работы
Цель работы – научиться работать с ультразвуковым датчиком расстояния, формировать управляющие импульсы с помощью микроконтроллера, определять программным способом длительность импульсов.
Программа работы
2.1 Подготовить аппаратное и программное обеспечение (установить драйверы) лабораторного стенда к работе.
2.2 Изучить краткие теоретические сведения.
2.3 Ввести, скомпилировать, загрузить в микроконтроллер и отладить программу опроса датчика, представленную в кратких теоретических сведениях.
2.4 Модифицировать программу согласно заданию.
2.5 Оформить и защитить отчет по лабораторной работе.