Text
                    
Оглавление Предисловие ................................................................................................................................................ 3 Глава 1. Мимолётное знакомство с микроконтроллером....................................................................... 5 Резюме ...................................................................................................................................................13 Глава 2. Мимолётное знакомство с программированием ....................................................................14 Резюме ...................................................................................................................................................24 Глава 3. Первая глава, начинающая рассказ о микроконтроллере ......................................................26 Резюме ...................................................................................................................................................36 Глава 4. Рабочее место .............................................................................................................................37 Глава 5. Установка программ gpsim и KTechlab в виртуальной ОС .......................................................59 Глава 6. Как заставить двигаться фигурку ...............................................................................................72 Резюме ...................................................................................................................................................89 Глава 7. Как ещё можно рисовать светодиодами? ................................................................................90 Резюме .................................................................................................................................................102 Глава 8. Как выводы порта работают на ввод информации................................................................103 Резюме .................................................................................................................................................112 Глава 9. Почему удобно использовать функции? .................................................................................113 Резюме .................................................................................................................................................121 Глава 10. Ветвление программы ............................................................................................................124 Подведём первые итоги .....................................................................................................................132 Глава 11. Начало разработки первого устройства ................................................................................133 Термостат вообще ...............................................................................................................................133 Современная элементная база ....................................................................................................137 Глава 12. Разрабатываем имитатор DS18B20 .......................................................................................144 Резюме .................................................................................................................................................158 Глава 13. Продолжаем рассмотрение термостата ...............................................................................159 Резюме .................................................................................................................................................170 Глава 14. Первые эксперименты на макетной плате ...........................................................................172 Глава 15. Измерение температуры и другое ........................................................................................186 Глава 16. Опыты с вентилятором и подогревателем ...........................................................................196 Глава 17. Опыты с термостатом .............................................................................................................206 Глава 18. Другой способ измерения температуры и другие задачи ..................................................217 2
В.Н. Гололобов Микроконтроллер глазами начинающего Предисловие Количество книг, рассказывающих о микроконтроллерах, огромно. Казалось бы, что за резон писать ещё одну? Но, как показывает практика, начинающие, просмотрев множество книг, попробовав себя в программировании, очень часто не столько обогащаются знанием и умением, сколько накапливают вопросы, на которые не находят ответа. Не думаю, что можно написать книгу для всех и каждого так, чтобы у него не возникло вопросов без ответа, однако чем больше книг, чем больше разных рассказов о предмете, тем легче выбрать то, что понятно именно тебе – все люди разные, все люди особенные, и сколько людей, столько подходов к себе они требуют. Микроконтроллеры широко применяются в современной электронике – это и автоматика, как большая, так и малая, то есть, от комплексных систем управления до стиральных машин; это и измерительные приборы, где значение микроконтроллеров, как основы измерения, трудно переоценить; это и современные устройства управления двигателями автомобилей, другими его системами и т.д. Для применения микроконтроллеров в любительской практике достаточно иметь программатор и готовый файл загружаемой программы. После загрузки программы в микросхему её можно впаять, как любой другой элемент электрической схемы. В этом смысле нет необходимости даже задумываться о том, как это всё работает. Работает, и ладно. А если не работает? Очень часто файл «прошивки» существует в «бумажном» виде или в текстовом файле на сайте. Достаточно небольшой опечатки, чтобы загруженная в микроконтроллер программа не работала. Любой микроконтроллер требует задания начальной настройки, то есть, задания конфигурации. Эта настройка осуществляется в специальных регистрах, куда записывают слово (или слова) конфигурации при «прошивке» микросхемы. В одних случаях это слово конфигурации находится в hex-файле для программатора, в других может отсутствовать или не считываться вашей программой для программатора. Без правильной для данного устройства конфигурации устройство работать не будет. А при ошибке в слове конфигурации для некоторых моделей контроллеров их придётся восстанавливать, чтобы «вернуть к жизни». Поэтому лучше потрать немного своего времени на освоение микроконтроллеров, чем впоследствии сожалеть о содеянном. Тем более, что работать с программированием очень интересно. На сегодняшний день, и это очень хорошо, разновидностей и подвидов микроконтроллеров существует не меньше, чем книг, рассказывающих о них. Чем это хорошо? 3
В.Н. Гололобов Микроконтроллер глазами начинающего При всей универсальности микроконтроллеров, они не могут обеспечить решение всех и каждой из задач, стоящих перед проектировщиками электронных устройств. Создавая какое-то новое устройство, разработчик использует существующую модель контроллера, но добавляет ряд микросхем, требуемых в данном, конкретном случае. Если это происходит часто, то производители микроконтроллеров создают новую модель, в состав которой включаются нужные модули. Такая модель стоит дороже, но новое устройство, не требующее дополнительных затрат на введение внешних компонентов, становится дешевле и надёжнее, что всегда важно для производителей электроники. Вместе с тем, если бы производитель микроконтроллеров перестал производить предыдущую модель, он стал бы единственной и безусловной причиной удорожания устройств, которые прекрасно обходились без нового модуля. Так появляются разные модели микроконтроллеров. Зачем знать об этом начинающим? Затем, что не следует рассчитывать, что, купив самый дорогой, самый современный, самый универсальный микроконтроллер от самого успешного производителя электронных компонентов, вы раз и навсегда избавитесь от необходимости осваивать что-то иное, чем выбранная вами модель контроллера. Решив одну задачу, вы перейдёте к другой, а выбирать контроллер, который следует использовать для решения задачи, стоит именно относительно задачи. Разобраться с особенностями сложного контроллера труднее, чем с более простым, тогда как вопросов, что и как, в любом случае появляется слишком много, и тем больше, чем сложнее модель микроконтроллера. Для своего рассказа я выбрал два микроконтроллера разных производителей, которые есть у меня в наличии. Оба варианта из тех, что сегодня принято называть «эконом класса». Оба контроллера позволяют узнать многое, многое освоить и понять, многое применить на практике. Было бы желание. Последнее, надеюсь, у вас есть. 4
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 1. Мимолётное знакомство с микроконтроллером Микроконтроллер – это специализированное устройство, предназначенное для выполнения программы, которая, в свою очередь, предназначена для управления другими устройствами, которые, как ни странно, могут тоже управлять другими устройствами и т.д. Сердцем или, если хотите, головой микроконтроллера служит процессор – цифровое устройство, предназначенное для выполнения операций, заданных при проектировании и изготовлении процессора. За годы развития цифровой техники было выработано понимание обязательных и расширенных операций, закладываемых в процессор в зависимости от его назначения. Цифровая техника использует сигналы (или напряжения) двух видов: логическая единица и логический ноль. Если говорить о напряжениях, то логической единице давно соответствует напряжение близкое к напряжению питания, а логическому нулю соответствует напряжение близкое к нулевому. Хотя может быть и наоборот. Логическая единица в качестве цифры – это просто единица, а логический ноль – ноль. С помощью двух цифр можно выразить любое число, каким бы большим оно ни было. Да, мы пользуемся более компактной десятичной системой счисления, потому что для больших чисел количество единиц и нолей достаточно велико, чтобы мы могли легко запутаться. Но процессору удобнее работать с нолями и единицами. Процессор может складывать и вычитать, умножать и делить эти числа, может сравнивать их. Но что же заставляет его делать это? Всё, что делает процессор, он делает под воздействием команд. А команды – это тоже (те же) двоичные числа, что и операнды, то есть числа, предметы операций. Как процессор их различает? Процессор не может отличить команду от числа (или данных). Как помочь ему в этом, позаботились создатели процессора. Первое число, которое «видит» процессор после включения питающего напряжения – это обязательно команда. Если за командой следует операнд, число для обработки, то в самой команде есть указание на этот факт. В этом случае процессор обращается за командой, пропуская следующее число. Все команды располагаются в памяти микроконтроллера. Эту часть памяти, встроенную в микроконтроллер, называют памятью программы. В неё записывают управляющую программу. Процесс записи управляющей программы в микроконтроллер, обязательный процесс для его работы, называют программированием микросхемы, а в быту «прошивкой» микросхемы. Память – это последовательно расположенные ячейки, куда вписываются двоичные числа. Каждая ячейка имеет свой номер или, как его называют, адрес. Таким образом, программа записывается в эти ячейки команда за командой. 5
В.Н. Гололобов Микроконтроллер глазами начинающего Чтобы процессор не ошибался при выполнении программы, у него есть счётчик команд, который после выполнения первой команды увеличивает адрес на единицу, и за следующей командой процессор обращается по этому адресу. Если в команде указано, что за командой следует операнд (число), то счётчик увеличивает адрес не на единицу, например, а на два. Кроме памяти программы (команд) у микроконтроллера есть память для переменных. Если память программы после выключения контроллера сохраняется, такой вид памяти называют энергонезависимым, то значения переменных в памяти не сохраняется. Переменные используются для «оперативного» хранения результатов операций или отображения внешних событий, за которыми следит микроконтроллер. И значения переменных важны и нужны только при выполнении программы. Когда значения, используемые в процессе работы, важны всё время, когда их нельзя терять при выключении питания, их, эти значения, записывают в ещё одну, тоже энергонезависимую, как и память программы, часть микроконтроллера. Её называют EEPROM – электрически перепрограммируемую память. С точки зрения программиста микроконтроллер – это регистры, в которые можно что-то записать, и команды, которые выполняет контроллер. На заре цифровой техники все команды рассматривались в двоичном виде, но затем их стали записывать в более компактном, но подходящем для совмещения с двоичными числами, шестнадцатеричном виде. Вы и сегодня, если взглянете на загружаемый в микроконтроллер hex-файл, а это обычный текстовый файл, увидите набор шестнадцатеричных чисел. Чтобы не быть голословным, я приведу пример загружаемого в микросхему файла для самой простой операции: установить один вывод контроллера в высокое состояние (состояние логической единицы). :02000000F72FD8 :100FEE008301F92F831603130510831203130514BF :020FFE000028C9 :08400000FF3FFF3FFF3FFF3FC0 :02400E00FFFFB2 :00000001FF В этом файле кроме команд процессору есть и команды для программатора. Я выделил два шестнадцатеричных числа в файле: 0514h (буква h указывает именно на то, что число шестнадцатеричное). Поменяем их местами – обычная практика в двухбайтовых командах первый байт команды записывать на втором месте: 1405h. Теперь запишем это число в двоичном виде: 1 0100 0000 0101. Есть правила перевода чисел из одной системы счисления в другую, знать которые полезно, но переводить удобнее, признаюсь, с помощью калькулятора, который есть на каждом компьютере. Заглянув в справочные данные микроконтроллера (это был контроллер PIC16F628A), среди команд можно отыскать такую: 01 01bb bfff ffff, здесь «b» это три бита для обозначения номера бита порта, адрес которого задан буквой 6
В.Н. Гололобов Микроконтроллер глазами начинающего «f». В нашем случае адрес порта 05h, это порт А. Бит (все биты, заданные буквой «b» равны нулю) порта нулевой. А сама команда означает установку, то есть, перевод в состояние логической единицы, нулевого бита порта А. Эта команда записана как BSF f,b. Все команды, которые вы найдёте в справочнике, выглядят не мене загадочно: XORWF, INCFSZ и т.д. Каждой из них соответствует двоичное число. Я уже говорил, что когда-то процесс программирования состоял из записи двоичных чисел в память цифрового процессора. Позже стали использовать шестнадцатеричные числа, а перевод их в двоичный вид возложили на разработанное для этой цели устройство ввода. Шестнадцатеричные числа гораздо легче воспринимать и запоминать. Но ещё проще запоминать команды в том виде, в котором мы привыкли их понимать как определённые действия, то есть, на языке близком к разговорному, чем ближе, тем лучше. Первым шагом стало применение аббревиатуры привычных слов. Так команда BSF происходит от слов bit set, установить бит. Запись программ на языке аббревиатур используется до настоящего времени. Язык этих записей называется ассемблером. Ассемблер ближе других языков программирования к машинному языку. Тем самым дальше других языков от нашего языка, на котором мы разговариваем. Обладая опытом работы с этим языком, можно написать очень компактный код программы, но не следует думать, что для этого достаточно запомнить все команды. Я всегда считал, что программирование ближе к искусству, чем к ремеслу, если говорить о хорошо написанных программах, а программирование на языке ассемблера ещё ближе к искусству, вдобавок требует упорных тренировок и знания всех приёмов и правил, выработанных за долгие годы использования этого языка. Позже по мере усовершенствования процессорной техники появилась возможность создавать специальные программы, которые помогают писать программы, то есть, переводят текст с языка понятного программисту, на язык, понятный процессору. Такие программы, трансляторы, часто можно встретить под именем компиляторов. Как правило, они переводят со своего языка программирования на язык ассемблера, а затем осуществляется перевод с языка ассемблера в машинные коды, и под руководством компоновщика программы (линковщика) получают код, который может работать с процессором. Языки высокого уровня легче понимать, чем даже ассемблер, не говоря о машинных кодах: скажем, write – записать, read – прочитать и т.д. Оставим на время программирование и обратимся к собственно микроконтроллеру. Не думаю, что в ваши намерения входит создание микросхем, но некоторое представление о физическом устройстве микроконтроллера следует иметь. Это не будет ни электрическая схема, ни функциональная схема устройства. Это простейшая модель некоторых составляющих элементов любого микроконтроллера. Она не потребует от вас глубоких знаний даже цифровых устройств. Я использую одно, которое 7
В.Н. Гололобов Микроконтроллер глазами начинающего называют триггером-защёлкой или D-триггером. На схеме его изображают с двумя основными входами: вход данных и вход записи. На вход данных подают нужный уровень, единицу или ноль, а вход записи подают импульс, которым данные переписываются на выход (прямой выход, поскольку есть и инверсный), где сохраняются до следующего обращения к триггеру. Любой триггер – это цифровой элемент с двумя устойчивыми состояниями, которые он способен сохранять до тех пор, пока мы не изменим его состояние, проделав вышеописанную запись. Возьмём вывод микроконтроллера, принадлежащий его порту. Да, он устроен иначе, но мы можем его представлять как D-триггер. Когда мы хотим установить на выводе контроллера логическую единицу или логический ноль, мы запишем это в D-триггер. Используя столько триггеров, сколько есть выводов порта, а обычно это восемь (восемь битов – это байт), мы получим некоторое представление об устройстве порта вывода. Мы говорили о памяти. Что мешает нам представить триггер-защёлку как устройство для запоминания одного бита. А для запоминания байта возьмём восемь триггеров. В итоге мы получим одну ячейку памяти. И вновь, конечно, память устроена иначе, но и такое построение памяти не противоречит функциональному назначению этой обязательной составляющей контроллера. Арифметико-логическое устройство слишком сложное, чтобы его можно было представить в виде простейшей модели. Поэтому его мы будем рассматривать как «чёрный ящик», осуществляющий эти операции, арифметические и логические. Но я упоминал о счётчике команд. Его тоже можно создать из триггера-защёлки: достаточно соединить его инверсный выход с входом данных, чтобы каждый сигнал записи был сосчитан. Соединив так устроенных четыре D-триггера последовательно: выход первого с входом записи следующего, - мы получим счётчик, который считает до шестнадцати. И это тоже примитивнейшая модель, но модель. А регистры? Читая справочную информацию по микроконтроллеру, мы часто встретим это понятие. Регистры имеют большое значение в работе микроконтроллера. Но мы можем представлять регистр как ячейку памяти, о которой говорили выше: восемь D-триггеров, входы записи которых объединены вместе; на входы данных подаём данные, на входы записи подаём импульс записи, регистр запоминает двоичное число. Ещё одно понятие, для которого легко создать модель, это адресация. И регистры, и память, и порты вывода – все они адресуются. Адреса регистров можно найти в описании микроконтроллера. Какую модель можно предложить для того, чтобы можно было различить, скажем, регистры по адресу, а это тоже двоичное число, задаваемому при разработке контроллера? 8
В.Н. Гололобов Микроконтроллер глазами начинающего Начнём с простого случая: у нас есть четыре (однобитовых) регистра, представленных в виде четырёх D-триггеров. Пусть вас не смущает, что наши регистры однобитовые. При необходимости можно увеличить количество элементов до необходимого значения, но схема станет сложнее, а это никак не поможет понять сущность процессов. Итак, для записи значения в регистр нам нужен импульс записи. Без этого импульса данные не будут записаны в регистр. Адреса (двоичные) регистров в нашем случае: 00, 01, 10, 11. Для формирования импульса записи по последнему адресу используем логический вентиль 2И. Единица на выходе этого вентиля появится только тогда, когда на входах будут две единицы (наш последний адрес), а её появление перепишет данные в триггер, то есть, переведёт его в состояние, задаваемое числом на входе данных. С первым адресом (вернее, последним) мы разобрались. Используем ещё вентили, которые называются инверторами. На выходе инвертора появляется ноль, когда на входе единица, и наоборот. Два инвертора на двух входах вентиля 2И вызовут появление единицы на его выходе, когда адрес будет 00. Осталось добавить по одному инвертору на входы оставшихся вентилей 2И, чтобы организовать адресацию оставшихся регистров. Я всегда считал, и считаю, что лучше один раз увидеть, чем сто раз услышать. Поэтому все простые модели физического устройства отдельных элементов микроконтроллера я проиллюстрирую с помощью программ idealCircuit (это свободно распространяемая и бесплатная программа симуляции электрических цепей) и KTechlab. Коль скоро мы говорим об электрических цепях (или элементах электрической схемы), удобнее проделать простые опыты в этих программах. 9
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 1.1. Работа триггера-защёлки в динамике Я использовал для управления D-триггером импульсные генераторы, что ближе к реальности, но мы могли бы провести эксперимент с помощью кнопок: нажимая кнопку записи, мы переписывали бы значение с входа данных на выход. Диаграмма выше не отображает один нюанс, представление о котором следует иметь. Я покажу это на следующей диаграмме. Рис. 1.2. Последовательность записи данных 10
В.Н. Гололобов Микроконтроллер глазами начинающего Всегда сначала устанавливаются данные на входе, а потом, пусть и с небольшой задержкой, подаётся импульс записи. Это не кажется важным при создании программы, но это следует помнить. Позже мы вернёмся к этому, если будет повод. Рис. 1.3. Запись числа в четырёхбитовый регистр из D-триггеров Число, которое мы записали можно прочитать на выходе: 1010. И, добавив ещё четыре элемента, мы получим байтовую (восьми битовую) модель регистра. Обычно входы данных соединены с шиной данных. Так называют совокупность соединений, по которым передают данные. Такую же модель можно представить себе и для одной ячейки памяти, хотя ячейки памяти объединяют вместе, образуя некую область памяти, где каждая ячейка имеет свой адрес. Все адресные входы объединяются в адресную шину – совокупность соединений со специальным регистром процессора, в котором записывается адрес внешнего (все устройства вне процессора для него внешние) устройства, к которому обращается процессор. Чаще всего адресация внутри микроконтроллера идёт к области программной памяти. Эта адресная шина соединена со счётчиком команд процессора. Прочитав первую команду, процессор увеличивает адрес в этом счётчике, формируя адрес для следующей команды. Но процессору нужна и адресная шина для обращения к оперативной памяти, где хранятся все переменные программы. И все регистры микроконтроллера тоже имеют адреса. Так что, адресная шина не менее важна, чем шина данных. Если говорить о микропроцессорах вообще, то у некоторых процессоров шина данных может быть объединена с адресной шиной. В этом случае процессор формирует специальный сигнал, давая понять внешним устройствам, что за число будет выставлено на шину, адрес или данные. 11
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 1.4. Модель адресного селектора И последняя модель, о которой я упоминал – это двоичный счётчик. Рис. 1.5. Простая модель двоичного счётчика 12
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 1.6. Диаграмма работы счётчика С каждым приходом импульса на выходе увеличивается значение (двоичное) на единицу. Счётчик из четырёх D-триггеров, где выход каждого из них представляет соответствующий бит, может считать до 15, после чего выходы сбрасываются в ноль и всё начинается сначала. Увеличивая количество триггеров можно получить счётчик, который считает до больших значений. Не так, но похожим образом, счётчик команд процессора считает команды и выводит адрес следующей команды. Резюме Мы бегло познакомились с микроконтроллером. Всё сказанное в этой главе нет нужды запоминать, но кое-что запомнить следует. Каждый микроконтроллер имеет в своём составе процессор – устройство, которое понимает программу и выполняет её. Каждый микроконтроллер имеет память программы, где программа размещается в процессе записи с помощью программатора. В этом процессе записывается и конфигурация микроконтроллера, указывающая, например, как будет реализован тактовый генератор, будет ли это внутренний тактовый генератор или для его работы используется внешний кварцевый резонатор. Эта конфигурация остаётся неизменной в дальнейшем. При задании конфигурации можно запретить дальнейшее считывание информации с микросхемы, то есть, запретить прочитывание записанной программы. Для некоторых моделей такой запрет означает, что вы не сможете без дополнительных усилий перепрограммировать контроллер. Значит, следует очень внимательно отнестись к слову (или словам) конфигурации. В остальном, с тем, как программировать микроконтроллер, мы познакомимся в следующих главах. 13
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 2. Мимолётное знакомство с программированием В предыдущей главе мы познакомились с понятием программы. Программа – это некая последовательность команд, которые понимает и может выполнить процессор микроконтроллера или компьютера, если относить это к компьютеру. Хотя мы вполне можем считать микроконтроллер очень маленьким и простеньким (далеко не всякий контроллер) компьютером. С другой стороны, программа – это набор двоичных чисел, часть из которых процессор воспринимает как команды, а остальные числа (включая и те, что составляют команды) как числа, с которыми процессор может оперировать: складывать, вычитать, сравнивать и т.д. Программа может быть линейной – за каждой командой следует команда, а процессор считывает их последовательно одну за другой. Программа может ветвиться, когда после выполнения команды процессор получает адрес следующей команды, расположенной где-то в другом месте программы. Вместе с тем программа – это отображение наших желаний по работе микроконтроллера. И любая программа, имеющая смысл, всегда начинается с того, что мы определяем, что должен делать микроконтроллер. Когда мы знаем это, мы можем записывать программу: можем записывать её в машинных кодах, можем записывать её на ассемблере, можем записывать на языках высокого уровня. Запись программы ещё называют процессом кодирования, написания кода программы. Для записи программы можно использовать простейший текстовый редактор, как блокнот в Windows. Если вы знаете формат hex-файла, если вы пишите программу в машинных кодах, то, записав программу, вы можете включить программатор и записать программу в микросхему, которую останется только впаять в плату, чтобы получить работающее устройство. Но я не уверен, что есть хотя бы один человек в мире, который этим занимается. Вы можете написать программу в блокноте на ассемблере, затем использовать транслятор для перевода программы в машинные коды. Но, если вы используете транслятор, то удобнее пользоваться какой-либо из сред разработки, в составе которой обязательно есть текстовый редактор. Таких сред разработки существует великое множество, как бесплатных, так и платных. Для работы на языке ассемблера, как правило, вам не понадобится покупать программу – есть бесплатные среды разработки с бесплатными трансляторами. Их обычно создают производители микроконтроллеров, чтобы их продукция пользовалась спросом. В настоящее время некоторые производители готовы бесплатно предоставить и трансляторы с языков высокого уровня. Писать программу на языках высокого уровня проще. Особенно, если среда разработки на языке высокого уровня имеет готовые библиотеки функций для работы со всеми типовыми устройствами, с которыми микроконтроллер обычно имеет дело: индикаторы, клавиатуры, другие контроллеры и т.п. 14
В.Н. Гололобов Микроконтроллер глазами начинающего Для всех моделей микроконтроллеров сегодня есть трансляторы с многих языков программирования, выбор языка программирования остаётся за вами, но следует иметь в виду, что на практике используют готовые тексты программ, готовые блоки обработки данных, которые чаще написаны на языке Си. В остальном – нет плохих языков программирования, есть плохое знание этих языков. Иногда опытные разработчики советуют сразу осваивать программирование на ассемблере. Мне кажется, что единственная причина в том, что можно создать очень компактный код, занимающий мало места в памяти программы. Но они забывают, что учились создавать такой компактный код довольно долго. Да и нужен ли компактный код начинающему, если его программа занимает 10% всей доступной памяти даже в самом некомпактном виде? Давайте посмотрим, как выглядит самая простая программа, установить единицу на выводе 0 порта А, в записи на языках разного уровня. Вот запись программы при использовании графического языка программирования. Рис. 2.1. Простейшая программа на графическом языке программирования Я знаю, к сожалению, только две программы, использующие графический язык программирования (а знаю я не всё), это Flowcode для Windows и KTechlab для Linux. Обе программы имеют сегодня на мой взгляд серьёзные недостатки: первая требует её покупки, и в соседнем магазине её не купишь; вторая на сегодняшних версиях тех дистрибутивов, что я проверил, не работает в части графического языка программирования. Оба недостатка огорчают, но… начиная изучать программирование, вы можете ориентироваться на простейшие программы, что позволит вам использовать демонстрационную версию программы Flowcode. А пользователи Linux могут вернуться к предыдущим версиям дистрибутивов, где можно очень много интересного выполнить в программе KTechlab. О том, как это сделать, вы можете прочитать в моей книге, которая называется «Неоконченный рассказ о программе KTechlab». Та же программа на язык Си для работы с компилятором XC8 выглядит так: #include "pic.h" int main(void) { TRISA0 = 0; RA0 = 1; 15
В.Н. Гололобов Микроконтроллер глазами начинающего return 0; } Для работы с недорогими PIC-контроллерами на языке Си удобно использовать программу MPLABX. Последняя на сегодня версия 1.30 позволяет использовать два бесплатных и полнофункциональных компилятора, SDCC и XC8. Запись программы для компилятора SDCC вы можете сравнить с приведённой выше: #include <pic16f628.h> void main(void) { TRISA = 0xFE; RA0 = 1; } Та же программа на ассемблере выглядит так: list p=16f628 radix dec include "p16f628.inc" _main MOVLW 0xfe BANKSEL _TRISA MOVWF _TRISA BANKSEL _PORTA_bits BSF _PORTA_bits,0 RETURN end Чем выше уровень языка программирования, тем больше забот он проявляет о вас. Обратите внимание, что при использовании графического языка вы пишите только то, что вам нужно: установить единицу (высокий уровень) на вывод порта. Вы указываете тот вывод, который выбрали для этого, об остальном заботится среда программирования. Когда вы используете язык Си, вам следует указать, что выбранный вами вывод порта А будет работать на выход. По умолчанию все выводы работают на ввод. Этот выбор осуществляется переводом в регистре TRISA соответствующего бита в ноль. При использовании ассемблера вам дополнительно следует указать, в каком банке памяти находится регистр TRISA. И это не всё, программа, приведённая выше, может не работать, если нет определений для всех имён в тексте. За ошибками такого рода следит транслятор с выбранного вами языка программирования. Каждый транслятор имеет строго определённый синтаксис – правила написания. Многие трансляторы (переводчики), да и компоновщики, с языков программирования требуют использования только командной строки. Все ошибки выводятся в этом режиме на экран. В средах программирования эти же сообщения выводятся в специальном окне вывода сообщений. Однако ошибки написания, как отсутствие двоеточия в конце каждого из операторов языка Си или фигурных скобок, окаймляющих тело функции, не исчерпывают всех возможных ошибок. Наиболее характерной ошибкой в этом смысле в языке Си будет ошибка условия. Операторы цикла, например, следят 16
В.Н. Гололобов Микроконтроллер глазами начинающего за выполнением условия выхода из цикла. Такое условие отслеживается по достижению переменной некоторого значения. Если записать условие, скажем, x = 10, то с точки зрения транслятора ошибки нет. Но знак равенства в языке Си служит для операции присваивания. В результате цикл будет работать неправильно, поскольку для записи условия служит другая конструкция: x == 10. А с точки зрения синтаксиса оба выражения правильны. Чтобы проверить правильность работы программы можно использовать специальные отладочные платы. Они позволяют определить точки остановки, проверить значения переменных и т.п. Труднее проверить программу на макетной плате готового устройства. Но среды разработки, как правило, имеют средства отладки программы, позволяющие проверить работу программы целиком или в режиме пошагового прохождения программы, отображая те же значения переменных, состояние регистров и т.д. Ещё больше удобства при отладке программы предоставляют современные отладочные модули сред разработки. Редко случается так, что устройство состоит только из одного микроконтроллера. Обычно добавляются, по меньшей мере, клавиатура (даже в виде одной кнопки, это клавиатура), индикаторы или исполняющие устройства: транзисторы, триаки или реле. И, конечно, хотелось бы проверить совместную работу программы и этих устройств. Особенно это важно для начинающих, которые склонны делать ошибки при монтаже, что ещё больше усугубляет положение, и может привести к выходу из строя элементов устройства. Ряд сред разработки, такие как Flowcode, ISIS, KTechlab, предоставляют возможность проверить почти всё до включения паяльника и первой пайки элементов электрической схемы. Особенно это относится к программам Proteus (ISIS) и KTechlab. Омрачают эту радостную картину только те недостатки, о которых я говорил выше: сложность с покупкой Proteus и отсутствие прогресса в проекте KTechlab. Есть ещё один аспект, который следует упомянуть, сред разработки устройств с использованием микроконтроллеров – наличие готовых библиотек. Если вы загляните в описание программы MicroC, то найдёте много полезного в этом плане: есть библиотечные функции для работы с дисплеями, клавиатурой, модулями обмена информацией и разными протоколами этого обмена. К чести разработчиков этой программы они предоставляют в пользование бесплатную версию, хотя и имеющую ряд ограничений. Почему я уделяю внимание этому вопросу, библиотеке функций? Потому что всё, что вам предстоит делать в отсутствии готовых библиотек, потребует от вас дополнительных усилий. Как правило, программы не бывают линейными и содержат ряд подпрограмм, которые называются макросами, процедурами или функциями. В языке Си сама программа называется главной функцией. Простейший пример необходимости в использовании функций даёт программа, которую называют «Помигать светодиодом». Мне больше нравится название для этой программы, которое я встретил где-то в тексте руководства: «Hello, World!» в мире микроконтроллеров. Эта фраза, если я не ошибаюсь, впервые 17
В.Н. Гололобов Микроконтроллер глазами начинающего встречается в книге, соавтором которой был один из создателей языка Си Деннис Ритчи, и относится к программе, которая выводит эту фразу на экран монитора. Вот, как может выглядеть эта программа для микроконтроллера при использовании транслятора SDCC: void main(void) { TRISA = 0xFE; for (;;) { RA0 = 1; delay(); RA0 = 0; delay(); } } Если есть библиотечная функция, то достаточно в функции delay() указать число. Функция имеет и указание на использование единиц: delay_ms(1000). При отсутствии библиотеки, вам потребуется самостоятельно написать эту функцию: delay(){ int count = 0; for (count = 0; count<1000; count++); } Задержка (пауза) delay зависит от частоты тактового генератора микроконтроллера, поэтому обобщение функции потребует получения данных о частоте тактового генератора, а сама функция приобретёт более сложный вид, чем приведённый выше. Создание собственных функций очень полезно для освоения языка программирования, хотя удобнее пользоваться библиотеками. Функцию для частного случая (впрочем, и общую) легко проверить в программе ISIS: 18
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 2.2. Проверка функции паузы в программе ISIS Длительность паузы, определяемой функцией delay(), 60 микросекунд. Эту же проверку можно осуществить после программирования микросхемы и переноса её на макетную плату с помощью осциллографа. Меняя слово конфигурации, что потребует изменений на макетной плате, если вы используете макетную плату, вы можете наблюдать, как именно работа функции зависит от частоты тактового генератора микроконтроллера. Существуют разные подходы к тому, как помочь начинающим освоиться в мире микроконтроллеров. Я считаю полезным начать с простых программ, а всё, что обычно делает микроконтроллер – это следит за входами и «мигает» выходами. Простые программы помогают освоить эти операции при использовании графических языков программирования. Программы Flowcode и KTechlab, единственные, с которыми я встречался, генерируют промежуточные коды: Flowcode на диалекте языка Си и ассемблере, KTechlab на диалекте языка Basic и ассемблере. Рассматривая эти коды можно быстрее понять, как следует писать программы на этих языках. Я часто придумываю (или вспоминаю) те ошибки, что подстерегают начинающих. Это затягивает начальный период, но ещё больше затягивает период создания сложной программы поиск ответа на вопрос, где ошибка? Особенно это относится к ошибкам не синтаксическим, хотя и они, подчас, могут поставить в тупик. Если вас не устраивает такой подход, что вполне справедливо для тех, кто знаком с программированием, не стоит тратить время на чтение этой книги. Вы не найдёте в ней оригинальных программных конструкций, я избегаю их по разным причинам. Одна из них в том, что на начинающего обрушивается такой шквал информации, что может унести всё желание продолжить знакомство со столь полезными компонентами, каковыми являются микроконтроллеры. Об использовании графических языков программирования я писал ранее, поэтому ограничусь только упоминанием о них, сделанном выше. В следующих главах я использую среду разработки MPLABX с одним из двух доступных компиляторов, XC8, и язык программирования Си в самом простом виде. Почему компилятор от Microchip? Он позволяет использовать (я не исключаю, что и SDCC можно как19
В.Н. Гололобов Микроконтроллер глазами начинающего то к этому приспособить) встроенный в MPLABX логический анализатор, чтобы увидеть динамику процессов. Но я использую SDCC, когда потребуется привести пример в программе KTechlab, поскольку этот компилятор даёт необходимые для моделирования вспомогательные файлы. Ещё один приём, который я часто использую, и который может вызывать неприятие у опытных программистов, я советую записывать программу на первом этапе обычным языком. Дело в том, что любая идея по созданию чеголибо возникает в виде образа готового устройства, скрывающего детали в ореоле нетерпения. Любая запись заставляет задуматься хотя бы о том, готовы ли вы сразу приступить к реализации? Это похоже на то, как создаются функции в языке Си – вначале вы декларируете функцию, а затем внедряете в программу. Профессиональные программисты используют функциональное описание программы с помощью алгоритма. Вы можете использовать и такую форму записи, если она больше привлекает вас, важно только, что любая форма записи помогает осознать предстоящую работу. Программу легче разбить на отдельные этапы, написать и отладить модули, из которых позже вы соберёте программу. С таким подходом легче отлаживать программу. А из готовых модулей можно собирать свою библиотеку, поскольку далеко не всегда есть всё нужное в готовых библиотеках, даже когда они есть. Я считаю, что ясное понимание того, что вы хотите сделать, как этого можно достичь, каковы ваши возможности в достижении цели, гораздо важнее, чем готовые решения, заученные приёмы или подсказки. А всё вместе поможет избежать разочарований и получить удовольствие от своей работы. Вот пример отладки схемы в программе KTechlab: 20
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 2.3. Совместная работа микроконтроллера и микросхемы памяти Приведённый пример относится к использованию внешней памяти, иной раз такая задача возникает при создании своих устройств. Чтобы отлаживать схему в программе KTechlab потребуется специальный файл с расширением .cod, который получается с компилятором SDCC в операционной системе Linux. В программе ISIS (Proteus) для подобного моделирования достаточно получить в результате трансляции исходного текста программы hex-файл. В этом беглом обзоре мне хочется упомянуть ещё одну программу. Она тоже имеет ряд недостатков – она требует, чтобы ваш компьютер был подключён к Интернету, все описания и примечания на английском языке. Вместе с тем, эта обучающая программа, а она не ограничивается электроникой, программированием или электрическими цепями, помогает в первом знакомстве с электроникой и микроконтроллерами. Вот пример одной из реализаций программы для микроконтроллера. 21
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 2.4. Микроконтроллер в программе Yenka Программа, загруженная в микроконтроллер, изображена в виде алгоритма, а работа программы начинается с кнопки «Ждать» на рисунке справа. Если нажать эту кнопку, то можно увидеть, как работает программа в «живом виде». И не только микроконтроллер, но и другие электрические схемы будут представлены и в графическом виде, и в трёхмерном «живом виде». Рис. 2.5. Исследование электрической схемы с таймером 555 22
В.Н. Гололобов Микроконтроллер глазами начинающего Раздел, посвящённый программированию, расскажет вам об основных элементах, как на рисунке ниже, используемых при создании программы. Рис. 2.6. Переменные и значения в программе Yenka Одно из примечаний, а их несколько для этого раздела, я перевёл в верхней части рисунка. Как правило, примечания достаточно лаконичны, чтобы их можно было понять хотя бы с помощью словаря. Микроконтроллер, как и компьютер, двуликая сущность – аппаратная часть без программы не более, чем железный лом, а программа без аппаратной части, в сущности, испорченная бумага (или лишний файл на компьютере). При том, конечно, владея программированием, следует иметь представление и об аппаратном оснащении микросхемы, иными словами об электрических цепях. Yenka даёт представление и о том, и о другом. В основном дальнейший рассказ будет строиться на использовании программы MPLABX, но при необходимости я буду обращаться и к другим программам. Программируя микросхемы, я использую программатор PicKit2, который у меня есть. Причина в том, что работа этого программатора поддерживается из среды MPLABX, но вы можете не спешить с выбором программатора. Впереди ещё много времени, когда вы сможете решить, нужно ли приобретать или делать 23
В.Н. Гололобов Микроконтроллер глазами начинающего такой программатор, не лучше ли использовать что-то другое, словом, до реализации любой из схем есть время подумать. Рис. 2.7. Свойства проекта в MPLABX Обратите внимание, что предпочтение отдаётся программатору PicKit3. Как и предыдущая модель программатора, он работает через порт USB, но поддерживает больше моделей микроконтроллеров. Как и предыдущая модель, он играет роль и аппаратного отладочного модуля: можно выполнять пошаговое прохождение программы, задавать точки останова, просматривать и менять состояние регистров. Резюме В отличие от других цифровых микросхем микроконтроллер требует выполнение такой специфической операции, как запись программы в память, встроенную в контроллер и предназначенную для этого. Однажды записанная программа сохраняется в памяти контроллера до следующей операции записи, которую осуществляют с помощью специального аппаратного устройства, программатора. Разновидностей программаторов достаточно много, но, как правило, программы для работы с программатором используют специальный файл, hex-файл, который получается в результате трансляции программы с языков высокого уровня в машинные коды. Трансляторов с языков высокого уровня тоже существует достаточно много. Далее мы используем два таких транслятора: компиляторы с языка Си XC8 и SDCC. Для удобства написания и отладки программы мы используем среду разработки устройств на базе микроконтроллеров MPLABX v.1.30. Дополнительно можно использовать отладочные возможности программатора PicKit2(3) и программ ISIS и KTechlab. 24
В.Н. Гололобов Микроконтроллер глазами начинающего Можно не осваивать программирование и языки программирования, для создания устройств можно взять готовые файлы, которые часто сопутствуют электрической схеме, однако слишком часто в эти готовые программы хочется внести изменения, а это невозможно без знания языков программирования. 25
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 3. Первая глава, начинающая рассказ о микроконтроллере В Москве, думаю и в других городах, в последние годы появились новые светофоры на регулируемых перекрёстках, оснащённые «светофорами» для пешеходов. На специальном табло высвечивается время, оставшееся до переключения светофора, на втором табло красным высвечивается пиктограмма стоящего пешехода; когда переход открывается для пешеходов, появляется зелёная пиктограмма движущегося пешехода и (тоже зелёным) время, оставшееся до закрытия перехода для пешеходов. К чему это? Я предлагаю начать освоение микроконтроллера с создания модели такого светофора. Если вы задумаете довести работу до конца, то подобную модель можно использовать в школе на уроках, посвящённых правилам дорожного движения. Если это вам не нужно, то: как среди чисел нет плохих и хороших, интересных и неинтересных, так среди программ нет смысла искать интересные программы. Для каждого из вас есть одна единственная, которая интересна вам (остальные неинтересны), но всё, о чём мы будем говорить, относится и к той единственной, что интересна именно вам. Разбирая «устройство программы», мы попутно коснёмся сопутствующих вопросов таких, как – зачем использовать транзисторные ключи, как работать с матричными конструкциями и т.д. Перед тем, как перейти собственно к программе, я хочу поделиться своими впечатлениями о создании рабочей среды: об установке программы MPLABX. Я установил эту программу и загрузил и установил оба компилятора в операционной системе Windows Vista (для SDCC, кажется, понадобится установка пакета gputils). Думаю, что установка без проблем осуществима и в Windows XP, и в Windows 7. Однако при установке программы в Fedora 17 я столкнулся с тем, что потребовалось удалить пакет java версии 7 (что удалило пакет LibreOffice ), установить пакет jdk версии 6, найденный на сайте Oracle. Удобно ввести в поисковую строку браузера запрос java se download, который приведёт на сайт oracle.com: Рис. 3.1. Сайт загрузки java 6 26
В.Н. Гололобов Микроконтроллер глазами начинающего К загрузке предлагается два пакета для Linux: jdk-6u35-linux-i586.bin и jdk-6u35-linux-i586-rpm.bin. Установка требует только согласия (поставить галочку в окне соглашения) с правилами использования программы. Первый пакет распаковывается в домашнюю папку как пакет java, второй даёт набор пакетов для установки rpm. Надеюсь, вы будете последовательны в своих действиях (в отличие от меня) и быстро выясните, что и как следует устанавливать. Для запуска установки следует, возможно, предварительно в свойствах этих файлов указать, что они исполняемые (щелчок правой кнопкой мышки по файлу и выбор из выпадающего меню свойств) использовать терминал. Вначале командой cd /путь_к_файлу, например, я устанавливал из домашней папки: cd /home/Vladimir, перейти в место, где находится пакет, а затем использовать команду ./jdk-6u35… и т.д., то есть имя файла. В Fedora 17 мне не потребовалось устанавливать пакет с компилятором SDCC, что пришлось сделать в ALTLinux и openSUSE. Компилятор устанавливается аналогично, а после распаковки файла с компилятором, можно обнаружить инструкцию по установке SDCC, которая, думаю, поможет вам без особенных хлопот установить и пакет java. Добавления могут коснуться переноса (с правами root) всех файлов в файловую систему (из домашней папки, например) и задания путей в переменных окружения. Советы, как установить java 6, можно поискать в Интернете. После установки компилятора SDCC и java 6 можно установить MPLABX. С правами root (опять проверьте, файл MPLABX должен быть исполняемым) в файловом менеджере достаточно запустить установку двойным щелчком, как в Windows. В репозитории ALTLinux (и для openSUSE я нашёл пакет установки) есть компилятор sdcc, но попытка использовать его привела в обоих дистрибутивах к ошибкам, что и заставило меня скачать последнюю версию компилятора с сайта производителя. После замены версии компилятора ошибки исчезли, появилась возможность использовать и этот компилятор вдобавок к другим. Но вернёмся к рассказу. Отчего-то я думаю, что проблемы использования MPLABX в Linux волнуют далеко не всех. Итак. Я предпочитаю начинать рассказ с простых решений. В смысле решений, которые понятны даже непосвящённому. Предположим, что мы выложили фигурку пешехода из светодиодов. Как это выглядит мне удобнее показать в программе KTechlab: Я не художник, что явно, но для наших целей это вполне подойдёт. Можно было бы добавить светодиодов, но в этом случае легко запутаться с выводами. Программу я, не сомневайтесь, предварительно написал и откомпилировал, иначе моделирование в KTechlab не получилось бы. Хотя вместо контроллера можно было бы использовать батарейку, но мы рассматриваем микроконтроллеры, и в таком виде всё выглядит… Рис. 3.2. Фигурка стоящего пешехода из светодиодов 27
В.Н. Гололобов Микроконтроллер глазами начинающего Формально для реализации программы достаточно выполнить команду RA0 = 1, записанную на языке Си. Но! Давайте разбираться с этими «но!»:    Каждый из светодиодов потребляет, если мы возьмём обычные индикаторные светодиоды, ток около 15 мА. Вся фигурка потребляет ток более 150 мА. Порты микроконтроллера могут использоваться и для ввода данных, и для вывода данных, но микроконтроллеру нужно сообщить об этом! Я привёл только несколько «но», с которыми начнём разбираться. Каждая модель микроконтроллера (это описано в datasheet, справке) может обеспечить только предельно допустимый ток на выходе, и это, как правило, не очень большой ток. Поэтому лучше между выводом микроконтроллера и фигуркой из светодиодов поставить промежуточный элемент – реле, а лучше транзистор с подходящим допустимым током коллектора. Если вы посмотрите в справочник по диодам, то можете найти параметры для светодиода, которые могут выглядеть так: падение напряжения при токе 15 мА порядка 1.5 В. На выходе микроконтроллера напряжение, очень часто, порядка 5 В. Избыток напряжения обычно «гасят» с помощью резистора, который устанавливается последовательно с диодом. Мы можем посчитать ток через все светодиоды (взяв напряжение на них 1.5 В) и по закону Ома и закону Кирхгофа определить величину добавочного резистора для всех светодиодов. Конечно, светодиоды элементы достаточно надёжные и работают без проблем долго, но, представим, что один из светодиодов вышел из строя. Ток через дополнительный резистор уменьшается, что увеличивает падение напряжения на светодиодах (за счёт уменьшения напряжения на резисторе). А это приводит к увеличению тока через светодиоды и, в свою очередь, может привести к выходу из строя другого (самого слабого в цепи) светодиода и т.д. Согласитесь, есть о чём задуматься. Вернёмся к микроконтроллеру. Чтобы сказать ему, будет ли вывод работать на ввод информации или на вывод, используют специальные регистры, которые называются TRIS, то есть в нашем случае это будет регистр TRISA (для порта А). Будет ли вывод микроконтроллера работать на вход или на выход, определяется записью единицы в соответствующий бит в первом случае и ноля во втором. То есть, нам надо записать единицу в младший бит регистра TRISA. Это можно сделать так: TRISA = 0xFE. Запись 0x (ноль и икс) говорит компилятору, что мы используем шестнадцатеричное число. В двоичном виде мы получим ноль только в младшем бите. Напишем нашу первую программу на языке Си. Загрузим MPLABX: 28
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 3.3. Загруженная программа MPLABX На рисунке я отметил раздел Create New Project. С этого раздела начинается создание любого нового проекта: щелчком по этому разделу левой клавишей мышки. Появляется диалоговое окно для выбора характера проекта: Рис. 3.4. Выбор характера проекта Мы ещё не готовы к созданию чего-либо иного, чем Standalone Project, то есть, отдельного проекта. Поэтому нажимаем на кнопку Next> и продолжаем настройки проекта. 29
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 3.5. Выбор модели микроконтроллера Я уже говорил, что использую микроконтроллер PIC16F628A. Есть модели проще, есть модели с более привлекательными параметрами, но это довольно доступная модель, имеющая описание (datasheet) на русском языке, с этой моделью в виде PIC16F628 работает (как может) программа KTechlab и эту модель (под тем же именем) поддерживает компилятор SDCC. В общих задачах выбор модели зависит от ряда параметров, присущих именно самой задаче. Для всех, или почти всех, задач, которые мы попробуем решить, эта модель вполне подходит. Если понадобится что-то исследовать, что не сможет обеспечить эта модель микроконтроллера, мы обратимся к другим моделям, но постараемся сделать это за компьютером. Нажимаем кнопку Next>. В следующем диалоге можно установить опцию использования заголовков для отладчика, но работать это будет в тех случаях, когда такая возможность обеспечена остальными параметрами проекта. Переход к следующему диалоговому окну позволяет выбрать программатор и отладочный аппаратный модуль: Рис. 3.6. Выбор отладчика и/или программатора И, наконец, мы можем выбрать компилятор, с которым предпочитаем работать: 30
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 3.7. Выбор транслятора с языка высокого уровня Остаётся добавить имя проекта и его расположение. По умолчанию место, где будет расположен проект, создаётся при установке программы. Но вы можете указать папку для проекта, которая удобна для вас, как я указываю место, где будут собраны все проекты, относящиеся к этой главе. Рис. 3.8. Ввод имени проекта и выбор места его расположения Остаётся нажать кнопку Finish, которой завершается создание проекта. Но завершается ещё не всё. Любая программа на языке Си должна иметь основной файл, где, собственно, и пишется программа. Чтобы сделать это в MPLABX, щёлкните правой клавишей мышки по разделу Source Files в окне навигации по проекту: 31
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 3.9. Создание основного файла проекта Выбрав New и Other, вы имеете возможность выбрать компилятор для этого файла и главный (или один из главных) файл проекта: Рис. 3.10. Выбор главного файла проекта После задания имени этого файла в следующем диалоговом окне, вы получите в редакторе текста заготовку: 32
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 3.11. Заготовка файла в окне редактора текста программы В этом текстовом редакторе мы проведём достаточно много времени. Работать с текстом программы можно так же, как с обычным текстом – записывайте нужные команды, требуемые функции и т.д. Мы выяснили, что нам следует записать две команды нашей программы, которые и запишем: Рис. 3.12. Первая программа Обратите внимание на знаки вопроса в тексте шаблона. Это свидетельство непонимания программой кириллицы: в тексте программы, понимающей кириллицу, системная дата появилась бы как 1 сентября 2012 г. Обращайте на это внимание. Конечно, вы можете поэкспериментировать, но проблемы могут возникнуть не сразу. Не тогда, когда вы размещаете программу в папке, скажем, с именем «Проект» или тогда, когда вы называете проект «Мой проект». Хуже, если проблемы возникнут позже, вызывая ошибки при трансляции проекта. Некоторые программы не понимают кириллицу. Для них следует использовать любые имена: в 33
В.Н. Гололобов Микроконтроллер глазами начинающего пути к проекту, в названиях файлов, - написанные латиницей. И ещё одно, на что я хочу обратить ваше внимание – программа с именем main, главная программа проекта, в языке Си оформлена в виде функции, что особенно подчёркнуто в шаблоне для компилятора XC8: перед функцией main есть тип возвращаемых данных, это int, а в теле функции main есть запись return 0. Проект можно было бы транслировать и проверить его работу. Но, если вы помните, я говорил о слове конфигурации, что важно для работы микроконтроллера. Хорошо сразу записать это слово конфигурации в исходном тексте программы. Для этого обратимся к пункту основного меню программы Window: Рис. 3.13. Раздел задания слова конфигурации Щёлкнув левой клавишей мышки по этому разделу вы попадёте в окно задания слова конфигурации: Рис. 3.14. Задание слова конфигурации 34
В.Н. Гололобов Микроконтроллер глазами начинающего Каждый из битов слова конфигурации имеет стрелку, направленную вниз, справа от своего имени, в выпадающем списке после нажатия на эту стрелку, вы можете выбрать доступные решения. Я отключил все ненужные сейчас функции и выбрал внутренний генератор в качестве тактового генератора. Реальное слово конфигурации это 0x3F18 (шестнадцатеричное число), но первая тройка относится к максимальному числу для данной модели. Остаётся нажать кнопку Generate Source Code to Output, чтобы получить вывод слова конфигурации в этом окне: Рис. 3.15. Запись слова конфигурации для компилятора XC8 Остаётся скопировать это в буфер обмена и перенести в основной текст программы. Этот текст теперь выглядит так: #include <xc.h> __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); & PWRTE_OFF & MCLRE_OFF & BOREN_OFF & int main(void) { TRISA = 0xFE; RA0 = 1; return 0; } Эту программу можно оттранслировать и загрузить в микросхему или включить режим отладки. Чтобы транслировать программу, выбираем в основном меню программы раздел Run и Build Main Project. Рис. 3.16. Компиляция первой программы 35
В.Н. Гололобов Микроконтроллер глазами начинающего Если в программе будут ошибки, то в окне вывода в нижней части экрана программы появится сообщение об ошибке. Если ошибок нет, то сообщение BUILD SUCCESSFUL завершает ваши усилия по созданию программы. Нет смысла отлаживать сейчас программу, она слишком проста. Но можно проверить, работает ли программа, используя доступные вам средства: другие программы, отладочную плату или макетную плату. Резюме Мы написали первую, самую простую программу, но прошли все шаги от создания нового проекта до трансляции программы. В итоге, вы можете проверить, в папке с проектом появились отладочный и загрузочный файлы. Рис. 3.17. Файлы для загрузки и отладки проекта 36
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 4. Рабочее место Прежде, чем начать эту главу, я долго сомневался, стоит ли её писать. Вдобавок, я не уверен, что уложусь в одну главу. В чём суть моих сомнений? Процесс, который я намерен описать, довольно долгий, требует терпения и, если быть до конца честным, определённых знаний. А результат может не заинтересовать никого, кроме немногочисленных приверженцев новшеств. Конечно, счастливые обладатели полных версий программы Proteus (ISIS) могут не понять меня – программа позволяет прекрасно симулировать работу многих моделей микроконтроллеров, поддерживает и другие компоненты, часто применяемые совместно с контроллерами. Мало того, для пользователей Linux я описал, как можно проверить работу контроллера в существующей версии бесплатной программы KTechlab… Я всё ещё сомневаюсь, следует ли писать эту главу. Вместе с тем, если вы намерены освоить работу с микроконтроллерами в такой мере, чтобы не чувствовать свою беспомощность, когда вы хотите что-то сделать, а готовой для загрузки в контроллер программы нет, то вы столкнётесь с проблемами, близкими к тем, что я предполагаю описать. Давайте так, я напишу эту главу, возможно, не одну, а вы, если вам не захочется её читать, пролистаете до того места, которое вам покажется интересным для чтения. Что я намерен описать? Во-первых, мне самому удобнее писать этот рассказ, проделав то, что я сейчас буду делать, обустраивая своё рабочее место. Я часто в своих рассказах пишу для пользователей и Windows, и Linux. Как правило, я перезагружаю компьютер, где у меня установлено несколько операционных систем. У меня не самый новый, не самый «крутой», не исключаю, не самый лучший компьютер. Но он меня устраивает и, почти уверен, устроит на ближайшее десятилетие. Однако перезагрузка – это не только потерянное время, но и потерянное настроение, забытые мысли и «удачные» фразы, как текстовые, так и схемные, дополнительные ошибки (и те, и другие), которые появляются совсем некстати. Я уже однажды рассказывал о VirtualBox. Есть такая программа. Или, если хотите, среда для работы виртуального компьютера. Я собираюсь установить эту программу, в ней установить дистрибутив Fedora 15, чтобы работать одновременно и в Windows Vista, и в Linux, и установить KTechlab из исходных текстов. Есть другие похожие решения, но это, как мне кажется, проще. Итак. Для дальнейшего полезно иметь безлимитный или дешёвый Интернет. Хотя можно купить Fedora 15, это не так дорого. Скачиваем VirtualBox для Windows и устанавливаем. Для скачивания достаточно ввести в поисковой строке (я чаще всего использую Yandex) virtualbox. У меня первая же ссылка указывает на сайт virtualbox.org, где в разделе Dowloads легко найти версию для Windows (и других операционных систем) от Oracle. В Windows программа устанавливается без проблем, как и в Linux 37
В.Н. Гололобов Микроконтроллер глазами начинающего (хотя я не проверял во всех дистрибутивах). Теперь скачиваем образ Fedora 15 Live KDE CD. Эта версия позволяет установку операционной системы на компьютер с графической оболочкой KDE, которая по своим свойствам очень похожа на Windows. Мне было удобнее скачивать этот образ диска с mirror.yandex.ru. Можно не записывать скачанный образ на диск, я, например, оставил его на рабочем столе. В Virtualbox запускаем создание виртуальной операционной системы (кнопкой создать). В мастере создания задаём размер памяти либо рекомендованный, либо желаемый (я выбрал 1.4 Гбайт). Задаём размер виртуального жёсткого диска – не скупитесь, дайте 15-20 Гбайт. Запускается LiveCD: Рис. 4.1. Работа виртуального CD диска в Virtualbox Когда виртуальный CD загрузится с виртуального оптического привода, можно использовать тот ярлык установки на жёсткий диск, что виден на рисунке выше. Запуск обычный – двойной щелчок мышки, но наберитесь терпения, поскольку CD работает медленнее жёсткого диска. Я предпочитаю оставить всё «по умолчанию», включая клавиатуру, поскольку лучше пароли и прочее задавать латиницей, впрочем, вы можете и поэкспериментировать. И жёсткий диск я оставляю по умолчанию, поскольку будет использован виртуальный жёсткий диск. Проделывать все дальнейшие операции я советую не спеша, загрузка с виртуального CD немного «тормозит», можно сделать лишние движения, которые приведут к лишним проблемам. Удобство использования Virtualbox в том, что для удаления всего ненужного, кроме самой программы, достаточно будет в программе удалить всё ненужное. Это может быть вся полностью установленная операционная система большим количеством программ, установленных в ней. Вы не потеряете ни места на диске. При установке я выбрал английский в качестве основного языка. Вы можете выбрать русский, а пароль ввести цифровой, скажем, 12345. Позже его можно будет изменить. 38
В.Н. Гололобов Микроконтроллер глазами начинающего Вот выбор жёсткого диска: Рис. 4.2. Выбор жёсткого диска для установки Fedora 15 И вы можете убедиться, что это виртуальный диск: Рис. 4.3. Сообщение указывает на то, что диск виртуальный 39
В.Н. Гололобов Микроконтроллер глазами начинающего Если вы нажмёте кнопку, соглашаясь с уничтожением всех данных (которых нет), то кнопка Next> начнёт процесс установки: Рис. 4.4. Начало установки Fedora 15 на виртуальный компьютер Проявив достаточно терпения, вы будете вознаграждены. Рис. 4.5. Завершение первого этапа установки Fedora 15 Нажав кнопку Close, перейдите в основное меню и выключите виртуальный компьютер. 40
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 4.6. Раздел основного меню для выключения виртуального компьютера После выключения отсоедините образ LiveCD. Выделите свою новую операционную систему и в основном меню, в разделе Файл выберите Менеджер виртуальных носителей Рис. 4.7. Раздел основного меню Virtualbox, управляющий носителями А в появившемся диалоге выберите закладку Оптические диски, выделите образ диска и нажмите кнопку Освободить и следом Удалить. При необходимости вы вновь сможете присоединить этот диск, но, надеюсь, это произойдёт не скоро. 41
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 4.8. Отключение виртуального CD-диска Запустив кнопкой Старт новую систему, вы окажетесь на завершающем этапе установки Fedora 15. Рис. 4.9. Завершающий этап установки Fedora 15 Вам предстоит ввести пользователя этой ОС и задать пароль, не забудьте поставить опцию включения его в административную группу. 42
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 4.10. Пользователь и его пароль в ОС Fedora 15 После нескольких нажатий кнопки Forward вы можете войти в новую операционную систему, как полноправный пользователь: Рис. 4.11. Вход в новую виртуальную операционную систему 43
В.Н. Гололобов Микроконтроллер глазами начинающего В этой части вам, если говорить об общих возможностях, будет доступен выбор графической оболочки (пока она единственная) в разделе Session Type, или возможность выключить компьютер с помощью кнопки Menu. Следующее, что я очень рекомендую сделать, это настроить систему. Я предпочитаю видеть её на родном языке. Для этого служит раздел Locale в системных настройках: Рис. 4.12. Раздел системных настроек в ОС Fedora 15 В основном меню есть два места, где можно найти настройки – это панель избранных программ, как на рисунке, и закладка Computer. В разделе настроек можно настроить почти всё по своему вкусу. Если вас заинтересовала ОС Linux, то вы можете, пока система в «чистом виде» и терять вам нечего, попробовать все настройки, посмотреть все программы, что установлены на компьютере по умолчанию. Я считаю, что такой безопасный способ знакомства с Linux самый удобный и привлекательный. Я рассказал об одном дистрибутиве и не последней версии. Вы можете, было бы место на жёстком диске, поставить разные дистрибутивы Linux. Они не столь разительно отличаются друг от друга, но различия есть. И поэтому кто-то больше любит Ubuntu, а кому-то нравится SUSE. У каждого из дистрибутивов есть свои привлекательные особенности. Я выбрал графический интерфейс KDE за близость его к интерфейсу Windows, так проще познакомиться с Linux. Но, если у вас нет проблем с Интернетом, вы можете загрузить оболочку Gnome. Последняя версия имеет необычный вид, но пользоваться этим интерфейсом не сложнее, чем KDE. Не могу одолеть искушение и приведу вид нового Gnome. 44
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 4.13. Вид нового графического интерфейса Gnome Но вернёмся к настройкам. Рис. 4.14. Настройка языка интерфейса в разделе системных настроек В диалоговом окне Locale выбираем свою страну: 45
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 4.15. Выбор страны в диалоговом окне Locale Но это ещё не изменит язык интерфейса. Далее следует выбрать в основном меню раздел администрирования, в котором есть выбор языка: Рис. 4.16. Подразделы раздела Администрирование основного меню 46
В.Н. Гололобов Микроконтроллер глазами начинающего Там-то и выбирается нужный язык интерфейса. Рис. 4.17. Диалоговое окно выбора языка интерфейса Я предпочитаю дальнейшие операции проводить через Интернет: Рис. 4.18. Загрузка из Интернета нужных пакетов для смены языка интерфейса 47
В.Н. Гололобов Микроконтроллер глазами начинающего Но, думаю, если выбрать в самом начале установки свою страну, то можно было бы избежать смены Locale: Рис. 4.19. Новый вид интерфейса после смены языка и проблемы с размерами окна На этом, как видно на рисунке, настройки не заканчиваются – окно у вас будет фиксированного размера, что не очень удобно. Поэтому продолжим настройки. Но… Рис. 4.20. Обновление системы 48
В.Н. Гололобов Микроконтроллер глазами начинающего Но прежде, не уверен, что это обязательно, обновим систему. Обновлений за годы накопилось много. Но я бы обновил. Загрузка займёт много времени. Потребуется много терпения. Но это стоит того. После перезагрузки системы можно продолжить настройку. В первую очередь я хочу отменить ввод пароля при каждом входе в систему. Рис. 4.21. Раздел настроек, управляющий входом в систему На закладке Дополнительно диалогового окна настройки входа можно найти, как сделать вход для себя без ввода пароля. Рис. 4.22. Закладка, где можно управлять входом в систему 49
В.Н. Гололобов Микроконтроллер глазами начинающего Нажав кнопку Применить, подтвердив свои администраторские права вводом пароля, не спешите покинуть раздел настроек. В верхней части окна есть стрелочка с надписью Назад. Нам туда. Нужно настроить переключение клавиатуры между латиницей и кириллицей. Рис. 4.23. Раздел настроек клавиатуры Рис. 4.24. Выбор дополнительной раскладки клавиатуры 50
В.Н. Гололобов Микроконтроллер глазами начинающего Нам нужна русская раскладка клавиатуры в дополнение к английской. Обратите внимание на рисунке выше на те галочки, что расставлены на закладке Раскладки. И здесь же можно выбрать, какими клавишами переключать раскладки (я привык к правым клавишам Ctrl и Shift). Не забудьте поставить галочки, как на рисунке! Рис. 4.25. Выбор клавиши переключения раскладок Для завершения настроек нам понадобиться загрузить из Интернета (если только у вас не коробочный вариант Fedora 15) две программы (или два пакета для установки): dkms и gcc. Они нужны Virtualbox для установки дополнений. Всё, что я описал выше, относилось к Fedora 15. Если дистрибутив Linux другой, то по умолчанию может быть установлена другая графическая оболочка. А может быть так, что разделы основного меню несколько отличаются. Поэтому при настройке что-то не найдётся в нужном месте. Но в другом дистрибутиве и установка KTechlab может потребоваться несколько иная. Я пробовал установить из тех исходных программ, о которых расскажу далее, программу в дистрибутиве, например, Fedora 17, но столкнулся с новыми трудностями, что заставило меня отказаться от установки программ из исходных текстов. Такой же результат получился и при аналогичной попытке установки в ALTLinux последней версии. Поэтому, вы можете установить в Virtualbox разные операционные системы, но установку gpsim и KTechlab (о них рассказ далее), лучше сделать в Fedora 15. Если эта книжка будет дописана, и я выложу её на своём сайте, то там же постараюсь выложить исходные тексты, в которых большая часть правок будет сделана. 51
В.Н. Гололобов Микроконтроллер глазами начинающего Для установки нужных пакетов понадобится менеджер пакетов. Рис. 4.26. Менеджер установки и удаления пакетов в Fedora 15 Есть разные менеджеры пакетов. Обычно я пользуюсь yumex, но по умолчанию он не установился, обойдёмся без него. В разделе администрирования есть другой менеджер пакетов. В строку поиска введём первое название пакета и запустим поиск. По завершении выберем найденный пакет и нажмём кнопку Применить. Рис. 4.27. Поиск пакета dkms в менеджере пакетов 52
В.Н. Гололобов Микроконтроллер глазами начинающего Аналогично поступим со вторым пакетом (кроме gcc установите пакет gcc+c++, он позже понадобится). А теперь… зачем они нужны? Я хочу, чтобы окно виртуального компьютера было такого размера, какой мне нравится – не люблю неосмысленных ограничений. Для этого есть дополнения к VirtualBox, которые устанавливаются с помощью этих программ. Рис. 4.28. Установка дополнений в Virtualbox Нажав этот раздел основного меню, выключим виртуальный компьютер (через основное меню). И проверим в менеджере виртуальных носителей на закладке оптических дисков (в Virtualbox): Рис. 4.29. Отображение виртуальных носителей Virtualbox 53
В.Н. Гололобов Микроконтроллер глазами начинающего Появился новый носитель, который нам и нужен. Включим наш виртуальный компьютер, где выберем подключаемые устройства: Рис. 4.30. Подключаемые носители в виртуальной ОС Если выбрать этот виртуальный носитель, то можно его открыть в диспетчере файлов. Рис. 4.31. Выбор диспетчера файлов для просмотра виртуального диска Содержание диска открывается полностью, но советую использовать отмеченный файл. 54
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 4.32. Файл запуска установки дополнений Запуск файла позволяет ввести пароль root (администратора) и установить дополнения: Рис. 4.33. Установка дополнений через терминал Fedora 15 55
В.Н. Гололобов Микроконтроллер глазами начинающего Подождать, опять нужно подождать, когда появится сообщение о том, что для выхода из установки нужно нажать клавишу Enter (Return). Зато теперь после перезагрузки виртуального компьютера… Рис. 4.34. Завершение установки дополнений Virtualbox Теперь можно увеличить (или уменьшить) монитор виртуального компьютера. Сделать его удобным для работы. На маленьком экране можно что-то увидеть, но для работы это неудобно, а ничто не должно мешать работе. Осталось сделать последний штрих перед тем, как начать то, ради чего было то, что было ранее. Когда вы перемещаете курсор на папку рабочего стола, вы можете заметить, как появляется дополнительное окно настроек – можно менять размер стола, можно… если нужно, а мне не нужно. Чтобы избежать этих предложений, щёлкните мышкой в верхнем правом углу экрана виртуального компьютера. На рабочем поле окна графической среды можно разместить различные дополнительные компоненты, виджеты. В Windows у меня добавлены часы, календарь, заметки. К виду экрана привыкаешь. Если вас беспокоит отсутствие знакомых вещей на рабочем столе, можно добавить их, используя доступные виджеты. Меняя тему графической среды, вы, скорее всего, измените и вид этих виджетов. Но это совсем не обязательно. 56
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 4.35. Меню блокирования и разблокирования виджетов Заблокировав изменение виджетов, щёлкните ещё раз в правом верхнем углу, чтобы убрать выпадающее меню работы с виджетами. Рис. 4.36. Добавление виджетов на монитор виртуального компьютера 57
В.Н. Гололобов Микроконтроллер глазами начинающего Щёлкнув ещё раз в этом же месте, вы можете разблокировать виджеты, добавить новые или изменить уже установленные. Далее наши пути могут разойтись. Вы, возможно, облегчите свою жизнь, установив KTechlab версии 0.3.6 от предыдущих версий Fedora, и есть надежда, что установится и подходящая версия программы gpsim. А я хочу проделать долгий путь, скомпилировав обе программы из исходных текстов. Разница небольшая – я получу возможность доступа к дополнительным регистрам микроконтроллера. Программа KTechlab поддерживает только три модели PIC-контроллеров, но «видны» не все регистры. О том, как исправить исходные тексты, я писал, когда сочинял историю под названием «Неоконченный рассказ о программе KTechlab». Однако прошло время, многое изменилось (поэтому я и установил Fedora 15), может быть не столь подробно, но постараюсь рассказать и о том, как это выглядит сейчас. 58
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 5. Установка программ gpsim и KTechlab в виртуальной ОС Для установки нужных программ из исходных текстов потребуются эти тексты, потребуется правка этих текстов, потребуется ряд вспомогательных программ. Но вначале установим программы, которые будут нужны впоследствии и безусловно: gputils и sdcc. Они не требуют выбора и устанавливаются с помощью менеджера пакетов. При компиляции программы KTechlab ей потребуется установленная программа gpsim – симулятор для работы с PIC-контроллерами. Если установить её из готового пакета для установки, то могут появиться проблемы: не все версии программы хорошо сочетаются с любыми версиями KTechlab; некоторые версии при компиляции KTechlab дают ошибку, связанную с ошибкой в gpsim. Таким образом, если устанавливать KTechlab из исходных кодов, то резонно установить gpsim подходящей версии из исходных кодов, предварительно исправив её. Хотя я описывал эти процедуры в книге о KTechlab, я повторю сейчас основное. В исходных кодах gpsim следует найти файл… но я немного поспешил. Чтобы найти нужный файл, предварительно следует иметь исходные файлы в виртуальной операционной системе. Думаю, можно связать обе операционные системы каким-то удобным образом, но я использую флэшку для переноса файлов – проще и спокойнее. Подключив флэшку, в Windows я записываю на неё нужные файлы. Затем, обратите внимание на значки в VirtualBox на самой нижней панели, щёлкнув правой клавишей мышки по значку USB… Рис. 5.1. Подключение флэшки к виртуальному компьютеру 59
В.Н. Гололобов Микроконтроллер глазами начинающего …можно увидеть подключённые к этому порту устройства, в частности я вижу свою флэшку. Поставив галочку рядом с флэшкой, мы подключаем её к виртуальной операционной системой (если галочку снять, то мы отключим устройство). Рис. 5.2. Появление меню выбора подключённого устройства В появившемся меню после щелчка по кнопке на панели (кнопка отмечена на рисунке внизу) можно выбрать этот накопитель, достаточно щёлкнуть левой клавишей мышки по его названию, а в следующем меню выбрать открывание флэшки в диспетчере файлов. Остаётся скопировать нужные файлы в домашнюю папку. После копирования, используя кнопку подключения в том же меню справа от ссылки на носитель, можно отключить флэшку, что соответствует безопасному извлечению устройств, подключенных по USB. Теперь о правке в исходных кодах gpsim. Открываем папку с исходными кодами, находим в папке gpsim текст main.cc, открываем его и находим (строка 192, если повезёт) такой текст: void exit_gpsim(int ret) { exit_cli(); exit(ret); } Его следует вырезать в этом месте, затем открыть файл input.cc, который расположен в папке cli, найти функцию extern int init_parser(); (возможно, строка 452), за которой и пристроить вырезанный фрагмент: extern int init_parser(); void exit_gpsim(int ret) 60
В.Н. Гололобов Микроконтроллер глазами начинающего { exit_cli(); exit(ret); } Можно устанавливать (компилировать) программу gpsim в Fedora обычными операциями: в терминале перейти командой cd /home/ваша_домашняя_ папка/gpsim в папку с текстом в домашней папке, у меня это выглядит так – cd /home/vladimir/gpsim – ввести команду ./configure. Рис. 5.3. Использование терминала для компиляции исходного текста Если вы получите сообщение, что в доступе отказано, откройте свойства (правым щелчком мышки по файлу) файла configure в папке gpsim и поставьте галочку рядом с «Является выполняемым» на закладке Права. Найти терминал для компиляции программ из исходных текстов не сложно. В основном меню, в разделе Система есть искомый Терминал. Перемещение внутри каждого меню – вращение колёсика мышки. Если нужно вернуться к предыдущему меню, а они могут быть вложенными – щелчок по кнопке (длинной, во всё меню) справа, меняющей цвет при наведении на неё курсора мышки. Конфигурация, запускаемая файлом configure, проверяет наличие всех (или почти всех) необходимых для компиляции программ. Если все программы для компиляции на компьютере есть, то вас ждёт удача, иначе придётся доустанавливать необходимое. Первым в списке оказывается пакет pot-devel. Запустите менеджер пакетов и введите это название в строку поиска. Установите этот пакет. Следующим в списке будет пакет gtk-extra: 61
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 5.4. Сообщение об отсутствии пакета gtk-extra Рис. 5.5. Поиск и установка нужного пакета в менеджере пакетов Попутно, думаю, это не будет лишним, я устанавливаю (как и предыдущий, если он уже не установлен) пакет gcc-c++, многие программы написаны на языке С++. Пакет понадобится в дальнейшем, если не сейчас. Повторяем конфигурирование в терминале. Для тех, кто не знает – 62
В.Н. Гололобов Микроконтроллер глазами начинающего нет необходимости вводить команды заново, достаточно в терминале использовать курсорные клавиши на клавиатуре – стрелка вверх повторяет последние команды. Если процесс проходит полностью, то осталось выполнить (на всякий случай) его повтор, но с дополнительным условием, которое выглядит так: ./configure –prefix=/usr Этим префиксом мы задаём место установки программы gpsim. Без префикса программа устанавливается в папку /usr/local. Следующая команда make выполнит трансляцию исходного кода. При условии, что установлены все нужные пакеты. Я обнаруживаю, например, что нет пакета readline, о чём компилятор сообщает, выводя очередную ошибку при компиляции. Рис. 5.6. Очередное сообщение об отсутствующем пакете Ищем в менеджере пакетов, в чём проблема, и устанавливаем readline-devel (readline). Рисунок выше я привёл, чтобы показать, где искать причину отказа в компиляции. В дальнейшем эта ситуация может повторяться неоднократно, пока не будут установлены все необходимые пакеты программ. Следующая ошибка связана с необходимостью установить пакет gcc-objc++, предварительно выполнив команду make clean. Следующая ошибка, неверная ссылка в функциях, как-то связанных с утилитой readline, не решается с установкой readline-static и сompatreadline5, и в ход я пускаю «тяжёлую артиллерию»: устанавливаю пакеты kdebase3, включая kdebase3-devel и kdebase3-libs пакеты. Увы, не помогла и тяжёлая артиллерия, увы-увы. 63
В.Н. Гололобов Микроконтроллер глазами начинающего Начнём всё сначала, запустив команду конфигурирования с должным префиксом, затем ещё раз команду make. И в этот раз… Рис. 5.7. Окончание трансляции программы gpsim Фу! Каждая из трансляций (до поры неудачных) занимает много времени. Да у меня компьютер не последней модели, но вполне приличный. И со всеми его несколькими ядрами, со всем моим терпением… Ну, да что там… Итак. Запускаем установку командой sudo make install. Почему sudo? Потому что запуск на установку должен осуществляться от имени администратора (главного, он же root). После ввода пароля администратора процесс установки проходит быстро, если сравнивать его, конечно, с предыдущими процедурами. Но теперь, зато, можно перейти к установке основной программы, которая нас и интересует. Предварительно, можете в этом убедиться, нужно поставить ещё несколько пакетов. В строке поиска менеджера пакетов вводим qt3, чтобы убедиться, что этот пакет уже установлен вместе с пакетом qt-devel, или чтобы установить эти пакеты, если их нет. Приключений с установкой программы KTechlab, надеюсь, это вас уже не удивит, предстоит ещё больше. Начинаются они, как и прошлые, с отказа в доступе. Как в прошлый раз это лечится на закладке Права в диалоговом окне свойств файла установкой флажка «Является выполняемым»: 64
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 5.8. Задание файла как выполняемый Кстати, программа-симулятор gpsim, можете убедиться после установки, запускается из терминала: вводим gpsim в терминале и видим программу. Рис. 5.9. Запуск программы gpsim (в терминале) 65
В.Н. Гололобов Микроконтроллер глазами начинающего Этой программой можно пользоваться отдельно от KTechlab, если загружать в неё файл с расширением .cod. Но об этом позже, а впереди… После выполнения команды ./configure, которой предшествует команда перехода в папку с исходным кодом, запускаем make (компиляцию). Рис. 5.10. Сообщение об ошибке при компиляции На рисунке выше я отметил возникающие ошибки, и рекомендации, как их исправить. Файл, где возникает ошибка, указывается в сообщении об ошибке с указанием номера строки текста. Место, где расположен файл, мешающий продолжить компиляцию программы, можно определить по информации о выходе из режима компиляции. На рисунке чуть ниже отметки, сделанной мной, можно прочесть, что выход осуществляется, в данном случае, по указанному пути: /home/Vladimir/ktechlab/src/gui. Там следует искать файл orientationwidget.cpp. Исправить ошибку не так трудно. Ниже на рисунке показано, как удобно это сделать. 66
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 5.11. Исправление исходного текста в редакторе KWrite В текстовом редакторе KWrite в разделе основного меню Вид можно включить опцию показа номеров строк, а номера строк выводится при сообщении об ошибке. Но лучше использовать в текстовом редакторе поиск. При следующей ошибке я покажу, почему это удобнее. Сохранив исправленный файл, можно продолжить компиляцию командой make. Уже готовые фрагменты программы пропускаются, а компиляция идёт до следующей ошибки. Ошибки аналогичные вышеприведённому примеру встрется в нескольких файлах. Собственно, это не ошибки – это разница в компиляторах. Один использовался при написании программы, а другой работает сейчас в установленной операционной системе Fedora 15. Многие составляющие дистрибутивов Linux быстро меняются. С одной стороны это хорошо, все изменения идут во благо. С другой стороны, вы видите, что полностью правильный текст программы приходится править из-за появляющихся изменений в библиотеках или компиляторах. И можете видеть, что ряд проектов его разработчики перестают поддерживать, слишком утомительно переделывать тексты программ каждый раз, как вносятся изменения в дистрибутив. Такое положение дел характерно не только для Linux. Недавно, пытаясь разобраться в вопросе, в котором я плохо разбираюсь, я поставил одну из программ в Windows Vista. Поставил, и обнаружил, что программа не работает в полной мере так, как должна работать. А несколько лет назад, когда я вознамерился установить на компьютере MS DOS, оказалось, что компьютер уже не поддерживает эту операционную систему. Следующая «ошибка» не заставила себя ждать. 67
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 5.12. Взаимодействие терминала и текстового редактора Как видно на рисунке, в терминале можно выделить место ошибки и скопировать. Переходя в текстовый редактор, открыв поиск в разделе основного меню Правка, вы можете вставить скопированное из буфера обмена. Когда ошибка одна, можно искать и по номеру строки, когда их много, удобнее исправлять с помощью поиска. При первом обнаружении искомой строки редактор сразу показывает вам её, а остальные можно найти, нажимая кнопку Следующее (проверяйте, нажав на эту кнопку, единственная ли это ошибка?). Тем более, что во многих следующих файлах ошибка та же, то есть, сразу в строку поиска можно вставить из буфера обмена нужную строку. Файлов, сразу предупреждаю, будет много: и start.cpp, и count.cpp, и pulse.cpp, и я перечислил только некоторые из них. Особенно много исправлений потребует файл multiinputgate.cpp. Однако все неприятности когда-нибудь заканчиваются. Так и в этом случае – наступает момент, когда вы можете ввести команду sudo make install. Все установки программ производятся с разрешения главного администратора операционной системы (root). И вот то, ради чего вы перенесли столько… собственно чего? Если вы хотите овладеть умением работать с микроконтроллером, то подобная работа и будет тем, чем вам предстоит заниматься. Не точно такая, но похожая работа осуществляется каждый раз при создании программы. Привыкайте. 68
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 5.13. Установленная и работающая программа KTechlab Последнее, что следует сделать, чтобы продолжить рассказ – это установить программу MPLABX с её компилятором на виртуальный компьютер. Предварительно следует установить программу jdk1.6… Её удобно устанавливать с помощью менеджера пакетов. MPLABX устанавливается обычным образом (для Linux): Рис. 5.14. Установка программы MPLABX После установки предлагается перезагрузить компьютер, что не характерно для Linux. 69
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 5.15. Завершение установки MPLABX Можно и перезагрузить, но перед этим установим компилятор xc8. Компилятор устанавливается аналогично MPLABX, но есть одно место: Рис. 5.16. Место ввода ключа при установке компилятора xc8 Здесь следует оставить поле пустым и нажать кнопку Next>. В этом случае устанавливается свободная версия компилятора. Если я правильно понимаю, разница только в режиме оптимизации кода, вернее в возможностях оптимизировать готовый код. После установки компилятора, чтобы не спорить с производителями программы, можно перезагрузить компьютер. И вот результат: 70
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 5.17. Установленная программа MPLABX в разделе Разработка основного меню Можно запустить программу и начать работать. С виртуальным компьютером следует поступать так, как принято поступать с реальным, то есть, выключать не выключением VirtualBox, а кнопкой выключения на закладке «Выход» основного меню. Полезно удалять всё лишнее, как оригиналы программ и исходные тексты (их лучше хранить на диске архивов в основной ОС), очищать корзину и т.п. Словом, всё «по-настоящему». 71
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 6. Как заставить двигаться фигурку Надеюсь, вас не испугали предыдущие главы, и вы сохранили твёрдое намерение продолжить освоение микроконтроллера, чтобы чувствовать себя достаточно уверенно в своих планах на будущее. Вот предмет дальнейшего разговора: Рис. 6.1. Модель движущейся фигурки Два чередующихся вида фигурки дают представление о движении. Вы можете придумать другие формы, но идея сохраняется. В дальнейшем мы не будем рисовать (у меня получается это плохо), а подойдём к работе формально: движение имитируется чередующимся переключением двух светодиодов. Именно их мы используем при проверке. И начнём эту главу мы не с кода программы на языке Си, а с создания нужной нам программы в KTechlab, где используется графический язык программирования. Как выглядит наша программа, если описать её русским языком? Первый светодиод включён, второй выключен; пауза 0.5 секунд; первый светодиод выключен, второй включён; пауза 0.5 секунд; возвращение к первому действию. Можно разными способами записать эту программу. Можно нарисовать её в виде блок-схемы. 72
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.2. Программа в виде ряда последовательных действий Программа KTechlab позволяет записать программу в таком же виде. Рис. 6.3. Запись программы в KTechlab в виде ряда последовательных действий Да, в первом случае речь шла о светодиодах, а здесь мы манипулируем выводами порта. Но мы уже выяснили, что к порту подключаются светодиоды. Когда вывод порта принимает состояние высокого уровня, светодиод загорается, и светодиод гаснет, когда вывод порта принимает состояние низкого уровня. К двум выводам порта подключаются два светодиода, о которых речь шла раньше. Но об этом потом. Сейчас нас интересует другое. Программа KTechlab позволяет получить, стартуя от нарисованной блок-схемы, работающую программу, которую можно загрузить в микросхему с помощью программатора, а, припаяв светодиоды (через токоограничивающие резисторы) к выводам микросхемы, проверить работу программы. Выглядит это, примерно, так: 73
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.4. Проверка работы программы в KTechlab И вновь это не то, что нас интересует. А что нас сейчас интересует? KTechlab переводит программу с графического языка в машинные коды, записывая их в формате для программатора. Происходит это в несколько явных этапов: На инструментальной панели есть кнопка, открывающая меню трансляции. Первый этап трансляции – это перевод с графического языка на диалект языка Бэйсик, microbe. Думаю, что это сокращение от microBasic. С языка microbe программа переводится на ассемблер. И, наконец, с ассемблера программа транслируется в машинные коды, записывая hex-файл. Рис. 6.5. Этапы трансляции программы Каждый из промежуточных этапов записывается. Вы можете видеть, как выглядит программа и на языке microbe, и на ассемблере, и в машинных кодах. А нас интересует код программы на языке microbe. Вот этот текст. 74
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.6. Текст программы на языке microbe Начинается программа с настройки порта А, эту часть можно назвать инициализацией порта. Настройка осуществляется записью в специальный регистр tris двоичного числа, в котором бит со значением единица настраивает соответствующий вывод порта для работы на вход, а бит с нулевым значением предписывает выводу работу на вывод информации. Поскольку выводов порта восемь, значение в регистре tris должно быть (нулевой и первый выводы выходные!) 252. Но мы видим число 124 (десятичное). Это происходит потому, что по умолчанию в программе KTechlab старший бит регистра TRISA нулевой. Не знаю, отчего так, но считаю, что это особенность программы, ведомая только её автору. Когда мы правили исходные коды, можно было бы исправить и это, но, если это не было сделано, следует помнить, что вывод RA7 не следует использовать как входной. Можно, конечно, попробовать это сделать, на иконке микросхемы он обозначен как входной, но RA7 имеет полное право не подчиняться нам. Вернёмся к программе. Общепринятое правило задавать начальные значения переменным в данном случае можно рассматривать перенесённым на значения, выводимые портами А и В. Поскольку порт В работает (хотя мы не используем это) на ввод информации, запись нуля в порт не должно сказываться на его работе. Нужно ли это обнуление портов? Не знаю. Я считаю, что в отличие от программных переменных порты настраиваются при включении питания аппаратно, если мы не устраняем этот процесс сознательно. Следующая запись называется метка: _label_setpin: Любое имя, кроме зарезервированных имён, с двоеточием в конце очень часто используется в разных языках как метка. Зачем нужна метка? Она отмечает то место в программе, куда программа возвращается по команде перехода. В приведённом примере команда возврата – это goto _label_setpin. То есть, перейти к месту, отмеченному в программе меткой. Обязана эта конструкция тому, что мы связали в графическом виде последнюю команду с первой. Мы образовали замкнутый путь, который называется циклом. В нашем случае бесконечным циклом. Программа будет всегда проходить этот замкнутый путь. Считается, что использование команды (или оператора) goto без жестокой необходимости – это плохой стиль программирования. Закончив разбирать программу, мы перепишем её, устранив эту стилистическую особенность. Внутри замкнутого пути запись, как мне кажется, понятна без дополнительных пояснений. PORTA.0 – порт А, вывод 0. Этот вывод мы устанавливаем в высокое состояние. Следующий вывод 75
В.Н. Гололобов Микроконтроллер глазами начинающего устанавливаем в низкое состояние. Затем следует пауза (задержка) delay длительностью, определённой нами, в 500 мсек. И, наконец, выводы меняют состояния на противоположные, а пауза повторяется. Заканчивается программа (после оператора перехода) ключевым словом end. Вот так выглядит программа на языке microbe. Как я и обещал, я сейчас уберу оператор goto, заменив этот безусловный переход циклом while с условием. Существуют разные конструкции циклов. По мере знакомства с языком мы используем их. Отличительной особенностью этих конструкции я называл бы условие выполнения цикла или, что точнее, условие выхода из цикла. Оно записывается в операторе цикла и проверяется при каждом выполнении этого оператора. В одних случаях условие проверяется до выполнения самого цикла, в других случаях условие проверяется после первого прохода цикла. Определяется это типом оператора цикла, и это причина существования разных видов циклов. Рис. 6.8. Использование оператора цикла while В графическом виде оператор while выглядит как отдельный блок. Подобные блоки программ можно называть подпрограммами, макросами, функциями, процедурами. Но в отношении цикла принято называние «тело цикла». После входа в цикл операторы в теле цикла выполняются до тех пор, пока не будет выполнено условие. В данном примере это условие записано сразу вслед за оператором. И это условие выглядит так: цикл будет выполняться, пока переменная x равна нулю. На языке microbe тело цикла выделяется фигурными скобками, в остальном всё выглядит почти одинаково и в графическом виде, и в записи на языке microbe. Присваивание переменной x до входа в цикл значения равного нулю делает цикл бесконечным. Если бы мы хотели избежать этого, то в теле цикла должны были бы изменять переменную x, чтобы она перестала равняться нулю. Завершив трансляцию программы, можно убедиться, что она работает так же, как и предыдущая. Их работу нельзя различить в готовом устройстве. Эту особенность – программы имеют разный вид, но работают одинаково – опытные программисты используют, например, тогда, когда нужно 76
В.Н. Гололобов Микроконтроллер глазами начинающего уменьшить размер машинного кода. Однако этому предшествуют годы изучения и работы. Мы не станем пока об этом говорить, но следует об этом помнить. А теперь перейдём к тому, что было целью написания этой главы. Перейдём к созданию программы на языке Си в среде MPLABX. Запускаем программу MPLABX. Я сделаю это в Windows. Правда при этом я не смогу проверить работу программы в KTechlab… Впрочем, позже мы разберёмся и с этим. Обычно я избегаю этого, но сейчас повторю ещё раз все этапы создания проекта в MPLABX. После запуска MPLABX используем раздел Create New Project (Создать новый проект). Если вы впервые запускаете программу MPLABX, то панель навигатора программы (окно слева) пуста. При повторном запуске вы обнаружите в навигаторе последний проект, над которым работали. В диалоговом окне, которое появляется, предстоит выполнить ряд действий по выбору типа программы, выбора модели контроллера, компилятора и т.д. Давайте последовательно рассмотрим эти действия. Рис. 6.10. Первое окно диалога создания проекта в MPLABX Среда программирования, как видно из рисунка, поможет вам создать отдельную программу (что мы собираемся делать), рассмотреть примеры (раздел Samples), открыть существующий проект, созданный ранее в MPLAB v.8, и т.д. Нажав кнопку Next>, мы попадаем в следующее окно: 77
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.11. Окно выбора модели микроконтроллера Выбор осуществляется из списка, который появляется, если нажать кнопку слева от окна. Выбор модели микроконтроллера важен для компилятора. Разные модели имеют разное количество портов ввода-вывода, имеют разные встроенные модули. Компилятор позволяет избежать ошибок, связанных с отсутствующими элементами контроллера, выводя соответствующие сообщения. В следующем окне диалога можно добавить, если компилятор это позволяет сделать, дополнительный файл для отладочных целей. Достаточно поставить галочку. Рис. 6.12. Выбор отладочного средства В этом диалоговом окне можно выбрать, например, программатор PICKit, который может работать в режиме отладки. Есть отладочные макетные платы, их тоже можно выбрать в этом диалоге. Но самый универсальный выбор – это симулятор. В следующем диалоге выбирается компилятор. 78
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.13. Выбор компилятора для работы с проектом Все доступные трансляторы отображены в этом окне. Я выбрал компилятор XC8. Хочу отметить, что на компьютере у меня установлен компилятор SDCC, но его нет в списке. Не знаю причины этого, но… вернёмся к выбору модели микроконтроллера. Выберем не PIC16F628A, а модель PIC16F628: Рис. 6.14. Доступные трансляторы при выборе другой модели контроллера Вот, что можно обнаружить, если проявить некоторую настойчивость. Но это «к слову». Вернём всё к прежним настройкам и перейдём в окно выбора имени и расположения проекта: 79
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.15. Диалоговое окно задания имени проекта и его расположения По умолчанию MPLABX при установке создаёт папку, в которой будут располагаться все проекты. Но при желании, как это сделал я, вы можете выбрать другое место. Я не советую это делать сейчас, но позже… отчего бы и нет? Чтобы изменить путь к проекту, воспользуйтесь кнопкой Browse. Она открывает встроенный в среду разработки проводник, где можно указать нужное место, что определит программе путь к проекту. При следующем использовании программы она предложит этот путь. Задав путь к проекту и его имя, вы завершаете предварительные настройки, а нажав кнопку Finish, получаете заготовку проекта. Но эта заготовка пока не имеет файлов исходного кода. Их предстоит создать. Хотя бы один файл. В навигаторе проекта выделим правой клавишей мышки нужную нам строку, открываем выпадающие меню: 80
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.16. Создание шаблона исходного кода В следующем окне мы выбираем компилятор и имя главного файла проекта: Рис. 6.17. Выбор файла проекта для исходного текста В следующем окне мы зададим имя файла (можно, кстати, хранить его в другом месте) и нажмём кнопку Finish. Мы получи заготовку для написания программы. Для разных компиляторов используются разные шаблоны. Отличия могут быть небольшими, но они могут быть. Это я к тому, что опытного программиста это не удивит, а начинающий, столкнувшись даже с небольшими различиями, может отказаться от использования готового исходного текста, который его вполне устраивает по выходным критериями, но написан для другого компилятора. Как выглядит подобная правка я описал в предыдущих главах, чтобы вы, если подобное случится, попытались поправить текст до того, как отказаться от него. 81
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.18. Заготовка главного файла проекта в MPLABX Пришла пора писать текст программы. Для этого я использую «шпаргалку», полученную в программе KTechlab. И станет ясно, зачем я привёл программу на языке microbe – для получения «шпаргалки». Языки программирования разные, но принципы схожи. Проверим это. Вот шаблон: #include "pic.h" int main(void) { return 0; } В начало программы я добавлю настройку порта А. #include "pic.h" int main(void) { TRISA = 252; return 0; } Следом добавлю цикл while, ориентируясь на текст в microbe. #include "pic.h" int main(void) { TRISA = 252; x = 0; 82
В.Н. Гололобов Микроконтроллер глазами начинающего while(x==0){ } return 0; } И в теле цикла while запишу то же, что записано на «шпаргалке». #include "pic.h" int main(void) { TRISA = 252; x = 0; while(x==0){ PORTA.0 = 1; PORTA.1 = 0; delay_ms(500); PORTA.0 = 0; PORTA.1 = 1; delay_ms(500); } return 0; } Я не рассчитываю на удачный исход трансляции, но рассчитываю на подсказки со стороны компилятора. Итак, для компиляции в основном меню выбираем раздел Run, где используем команду Build Project: Рис. 6.19. Сообщения об ошибках Первая ошибка, выделенная на рисунке, относится к тому, что язык Си (да и другие языки программирования) требует при создании переменной обязательно указать её тип. Если этого не 83
В.Н. Гололобов Микроконтроллер глазами начинающего сделать, то появляется сообщение о неопределённом идентификаторе. Компилятор не знает, что мы хотели создать переменную x, и просит нас уточнить, что мы от него хотели в этом месте программы. Есть разные типы данных. Мы, скорее всего, будем иметь дело не со всеми, а с переменными типа byte (или char) и int. Первый тип переменной занимает байт в оперативной памяти микроконтроллера, второй два байта. Нам достаточно одного. Однако тип byte данный компилятор не понимает, но понимает тип char. Исправление простое: char x = 0; Вторая ошибка (на следующей строке) вызвана тем, что компилятор не использует обращения к выводам порта в том виде, который использован в языке microbe. Попробуем записать это обращение иначе: RA0 = 1; Теперь ошибок меньше, но неприятие вызывает запись delay_ms(500). Я постарался обозначить единицы, миллисекунды, но это не помогло. Действительно, в языке Си нет такого оператора как delay. Скорее всего, это функция, которая может содержаться в библиотеке, если есть библиотека функций, или её придётся создавать нам. Сообщение об ошибке выглядит так: :0: error: undefined symbol: _delay_ms(dist/default/production\proj5.X.production.obj) Если заглянуть в примеры, полученные с программой MPLABX (на стартовой странице есть раздел Open Sample Project), то можно найти пример получения и использования функции delay. Рис. 6.20. Функция delay в примерах MPLABX 84
В.Н. Гололобов Микроконтроллер глазами начинающего Я обвёл и создание функции и её применение. Это не совсем то, что хотелось бы, но мы вернёмся к этой проблеме позже, когда у нас будет больше опыта, а сейчас я просто скопирую всё в свою программу. Программа приобретает вид: #include "pic.h" void delay() { int counter = 0; for (counter = 0; counter<10000; counter++) { ; } } int main(void) { TRISA = 252; char x = 0; while(x==0){ RA0 = 1; RA1 = 0; delay(); RA0 = 0; RA1 = 1; delay(); } return 0; } И теперь трансляция программы проходит полностью. Остаётся проверить её работу. Для счастливых обладателей полной версии программы Proteus проверка может выглядеть так: Выводы RA0 и RA1 попеременно становятся активными (переходят в высокий уровень). Можно к этим выводам добавить светодиоды с резисторами и наблюдать, как мигают светодиоды. Рис. 6.21. Проверка работы программы в Proteus Для тех, кто не может проверить работу в Proteus, можно проверить работу в симуляторе, не выходя из программы MPLABX. Позже мы вернёмся к отладочным операциям в MPLABX, а сейчас я только приведу вид регистров микроконтроллера в режиме отладки. Заглянув в справочные данные по шестнадцатеричный адрес порта А: 05h. контроллеру 85 PIC16F628A, можно убедиться, что
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.22. Отладка программы в MPLABX Изменение значения в регистре порта А – это и есть результат работы нашей программы. Не показательно, да? Поэтому я предлагал установить программу KTechlab, тем, у кого нет работающей программы Proteus. Для проверки работы нашей программы её следует переписать и оттранслировать в MPLABX, установленной в операционной системе виртуального компьютера, компилятором SDCC. Трансляция даёт необходимый симулятору gpsim файл с расширением cod. Этот файл можно загрузить либо в программе KTechlab, либо в самом симуляторе. Хотя компилятор sdcc был предварительно установлен, он не обнаруживается программой MPLABX. Чтобы исправить эту… это… не знаю, как называть этот инцидент, но для этого зайдём в раздел Options пункта основного меню Tools. Выберем закладку Embedded, и нажмём кнопку Add. Во вновь открывшемся диалоговом окне выбираем кнопку Browse, в файловом менеджере находим путь к папке компилятора и нажимаем кнопку Открыть, затем ОК, возвращаясь обратным путём. Следуя инструкции, мы должны были использовать кнопку сканирования, но она не даёт искомого результата. Впрочем, теперь это неважно. 86
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.23. Добавление компилятора SDCC в MPLABX Добавив компилятор SDCC, можно создать новый проект, скопировать оттранслировать её, и открыть в симуляторе gpsim (или программе KTechlab). Рис. 6.24. Проверка работы программы в симуляторе gpsim 87 программу,
В.Н. Гололобов Микроконтроллер глазами начинающего Программы на виртуальном компьютере ведут себя несколько странно, но можно отнести к версии Fedora, версии программ gpsim и т.д. С этими странностями можно разобраться позже, а сейчас отметим, что при создании паузы мы использовали ещё одну разновидность цикла, цикл for. Его называют счётным циклом. Количество проходов этого вида цикла определяется числом, заданным в условиях работы цикла. Я повторю то, что записано выше, удалив лишнее. for (counter = 0; counter<10000; counter++) { ; } Цикл for в данном случае работает с переменной counter, но переменная может быть любой. Важно только, как это сделано в функции delay, эту переменную добавить до начала работы с циклом. В условии работы цикла записано, что после присваивания этой переменной значения ноль, до тех пор, пока эта переменная меньше значения 10000, эту переменную следует увеличивать на единицу (это записано в конструкции counter++), затем выполнить подпрограмму, записанную в теле цикла. Это «тело цикла» в нашем случае представлено пустым оператором «;», заключённым в фигурные скобки (фигурные скобки нужны, как правило, в тех случаях, когда в теле цикла несколько операторов). В нашем случае это означает, что делать ничего не надо. Поскольку цикл при проходе подпрограммы, записанной в нём, сам считает количество проходов, его и называют счётным циклом. При использовании компилятора SDCC в шаблоне основного файла вы столкнётесь с ещё одной конструкцией цикла for. void main(void) { //Infinite loop for (;;) { } } Мы видим объявление цикла, фигурные скобки, задающие тело цикла, но не видим параметров выполнения цикла. Это тоже конструкция, характерная для языка Си. Из-за отсутствия параметров цикл будет выполняться бесконечное число раз. То есть, это одна из форм бесконечного цикла, которую в прошлый раз мы получили с помощью цикла while. В языке Си есть много конструкций, которые удобны программистам, но к которым нужно привыкнуть начинающим, если вам нравится этот язык программирования, если вы намерены освоить его. Я постараюсь не увлекаться этими конструкциями, есть прекрасные учебники, которые можно найти и в книжном магазине, и в Интернете, однако при случае буду рассказывать о тех конструкциях, которые не выглядят привычным образом. Кроме того, вы могли убедиться, что программы можно написать по-разному, но работать они будут одинаково. Начинающих это иной раз смущает, но, если вы знакомы с электрическими схемами, вы знаете, что одинаковые в своей работе устройства могут иметь разные схемы. Это же относится и к программам. И, завершая эту главу, я хочу показать, чем мне понравилась в своё время программа KTechlab: 88
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 6.25. Зависимость яркости светодиодов от величины добавочного резистора Резюме В предыдущих главах мы научились управлять выходами микроконтроллера, устанавливая их в состояние с высоким и низким уровнем напряжения. Как правило, это и есть основная работа выводов микроконтроллера по управлению устройствами внешнего (для контроллера) мира. В этой главе мы познакомились с тем, как управлять периодическим переключением выводов, что позволяет уже сейчас, хотя мы только начали знакомство с микроконтроллером, создать вполне полезное устройство. Вы можете использовать большее количество выводов, заставляя их переключаться разным образом, для создания других полезных устройств. Придумывать такие устройства очень интересно. Пожалуй, интереснее всего. Вы можете придумывать разные фигурки, которые можно заставить двигаться различным образом. Пробуйте! 89
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 7. Как ещё можно рисовать светодиодами? Конечно, конечно с помощью микроконтроллера! Если вы не против, я предварю рассказ тем, что установлю на свой виртуальный компьютер ещё один дистрибутив Linux. Я не коллекционирую разные дистрибутивы, но, рассказывая о программах для Linux, я, как правило, проверяю всё, о чём рассказываю, в разных операционных системах. В одной операционной системе всё выглядит хорошо, в другой не всегда хорошо, но достаточно что-то подправить, чтобы результат стал положительным. Современные компьютеры имеют жёсткие диски большого объема. Но многие на компьютере хранят кинофильмы, фотографии, музыку. Не самый лучший выбор, я бы переносил это всё на CD и DVD диски. Так надёжнее. Не всегда сам поступаю правильно, но это не отменяет того, что компьютер подвержен разного рода проблемам, как повреждения или вирусы. Записанные на оптические диски файлы хранятся гораздо надёжнее. Если у вас достаточно современный компьютер, если вы правильно храните данные, то свободного места на жёстком диске должно быть много. Я хочу поставить на виртуальный компьютер дистрибутив ALTLinux версии 5.0. Не так давно этот дистрибутив предлагалось использовать в школах. Сегодня предлагается уже шестая версия. Но эта версия ALTLinux у меня на компьютере есть, и в KTechlab не работает симуляция. Цель этого эксперимента – упростить использование программы KTechlab, поскольку в ALTLinux используется полнофункциональный вариант программы. Для установки дистрибутива, все процедуры аналогичны установке Fedora, я скачиваю установочный CD-диск ALTLinux 5 школьный, лёгкий. Позже при установке я столкнулся с необходимостью использовать второй, дополнительный установочный диск, который скачал, но не исключаю, что сняв «галочку» с дополнительной установкой OpenOffice, я избежал бы необходимости в добавочном установочном диске. При установке я соглашался со всеми предложенными вариантами, в частности с языком интерфейса: Рис. 7.1. Выбор языка интерфейса 90
В.Н. Гололобов Микроконтроллер глазами начинающего По умолчанию я использовал и диск, размер которого выбрал 15 Гбайт, как и доступную память в 1 Гбайт, в диалоге создания новой виртуальной операционной системы. Рис. 7.2. Выбор жёсткого диска для установки ОС Поскольку образы диска я после загрузки перенёс на рабочий стол, а не стал копировать на CDдиск, установка идёт довольно быстро (ещё быстрее она осуществлялась бы без офисного пакета, который мне и не нужен). Рис. 7.3. Установка ОС 91
В.Н. Гололобов Микроконтроллер глазами начинающего И вот то место, где от меня потребовалось поменять установочный диск: Рис. 7.4. Необходимость смены установочного диска Собственно, в этот момент я приостановил установку (перестал что-либо делать), вернулся к списку дистрибутивов для загрузки, загрузил второй диск. А сменить диски можно так: Рис. 7.5. Процедура смены установочного диска 92
В.Н. Гололобов Микроконтроллер глазами начинающего В нижней части панели VirtualBox есть иконка оптического привода. Если щёлкнуть правой клавишей диска, то можно отсоединить предыдущий диск (снять галочку рядом с его образом) и воспользоваться разделом «Выбрать образ оптического диска». Открывается проводник Windows, в нём можно указать образ второго диска, как видно на рисунке после закрытия проводника. Теперь достаточно нажать кнопку Далее, чтобы установка продолжилась. Ложка дёгтя подстерегает после установки операционной системы. Используя опыт предыдущей установки, я попытался добавить дополнения VirtualBox. Как написано в руководстве, как я это делал в прошлый раз, я поменял установочный диск на диск дополнений. Он подключился, но попытка запустить файл autoran.sh или установку дополнений для Linux потерпели фиаско. Я не нашёл в ALTLinux рекомендованного файла dkms, нашёл, что компилятор gcc уже установлен, но запустить установку дополнений не получалось. Выходом из этой ситуации стало копирование нужных файлов в домашнюю папку. Рис. 7.6. Скопированные файлы установки дополнений Используя терминал, можно запустить установку дополнений, предварительно перейдя в режим использования терминала с правами администратора (команда su и ввод пароля). 93
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 7.7. Запуск из терминала установки дополнений Вторая ложка дёгтя подстерегает в этом месте – особенность ALTLinux в отсутствии файла kerneldevel, который нужен VirtualBox для компиляции модулей видеокарты для ядра системы. Может быть, я не отрицаю этого, проблему можно обойти, но… После перезагрузки виртуальной ОС экран виртуального монитора стал больше, но не таким, каким его хотелось бы видеть. Установив программы gpsim, KTechlab и утилиту gputils можно убедиться, что KTechlab работает полностью. И первая проверка не выявила особых проблем (может быть, они появятся позже?). Мне хотелось облегчить жизнь тем, кто, следуя моим советам, решил использовать программу KTechlab в качестве симулятора. Я знал, что в предыдущих версиях ALTLinux KTechlab работала нормально. Я попробовал сделать это, но получилось, что получилось. Вместе с тем, с программой можно работать, хотя и не столь комфортно, а количество операций по достижению цели меньше, чем при установке KTechlab из исходных кодов. При этом некоторые регистры недоступны, но это пока неважно. Важно, что программа работает. Можно установить компилятор sdcc и программу MPLABX. 94
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 7.8. Работа программы KTechlab в ALTLinux При необходимости вы можете изменить графический интерфейс. В ALTLinux для установки программ используется программа управления пакетами: Рис. 7.9. Программа установки программ в ALTLinux В программе есть поиск, есть классификация всех пакетов программ и т.д. Мне жаль, что не получилось так же удачно, как с Fedora, но всякое бывает. 95
В.Н. Гололобов Микроконтроллер глазами начинающего Вернёмся к микроконтроллеру. То, о чём я хочу рассказать, я изображу (да и использую) в KTechlab, но в Fedora 15. Вы можете повторить это в ALTLinux или использовать Proteus совместно с MPLABX. Я использую весь порт В на вывод, записывая нули в TRISB. Почему порт В, а не порт А? У микроконтроллера PIC16F628A один из выводов порта А может работать только в качестве цифрового входа, а мне нужны все выводы порта. Рис. 7.10. Использование светодиодов для создания рисунков Конструкция из светодиодов, представленная на рисунке, называется матрицей из светодиодов. Матрицы в математике самостоятельные сущности, есть, например, алгебра матриц, а в других дисциплинах они активно используются при преобразованиях. Так hFE на мультиметре (если есть) – это режим измерения коэффициента усиления транзистора. А сама величина – это элемент матрицы преобразования входных величин в выходные для транзистора. Каждый элемент матрицы расположен на своей строке и в своём столбце. Выбором строки и столбца мы обращаемся к определённому элементу матрицы. Вот программа, которую я использовал: Не забудьте, что приведённое выше – это не электрическая схема. Думаю, вы понимаете, почему не следует использовать рисунок для реальной макетной платы. Программа пока проста и включает один из светодиодов. Рис. 7.11. Программа включения одного светодиода в матрице светодиодов 96
В.Н. Гололобов Микроконтроллер глазами начинающего Устанавливая один из выводов, соединённый с первой строкой матрицы, в высокое состояние, а вывод третьего столбцы в состояние с низким уровнем (остальные столбцы мы включим в высокое состояние), мы зажигаем один светодиод. Мы легко можем включить все светодиоды первой или другой строки. Достаточно включить все «столбцы» в низкое состояние. Но мы пока не можем включать любые светодиоды. Скажем, включив первую строку, мы попытаемся включить один светодиод второй строки. Рис. 7.12. Первый неудачный опыт создания рисунка Хотя на рисунке трудно разглядеть (программа показывает, что ток через светодиоды очень маленький), но включается вся вторая строка. Что же делать? Первое решение, которое напрашивается, это подключить, например, все катоды светодиодов к отдельным выводам микроконтроллера. Можно. Но нам не хватит выводов микросхемы. Попробуем поступить иначе. Будем поочерёдно включать нужные нам светодиоды. Рис. 7.13. Проверка новой версии программы На рисунке плохо видно горящие светодиоды, но такой режим работы называют динамической индикацией. Каждый из светодиодов включён на короткий промежуток времени. Быстрое 97
В.Н. Гололобов Микроконтроллер глазами начинающего переключение, а микроконтроллер делает это очень быстро, не даёт разглядеть отдельные моменты, всё сливается в одну картинку. Программа выглядит так: Рис. 7.14. Вид программы динамической индикации Такой вид программы, конечно, я привожу, чтобы стало понятно, как организовано динамическое использование светодиодов. Однако каждый из этих блоков программы не более, чем записанное в порт число. Можно так и записать программу: Рис. 7.15. Приведение программы к должному виду Проверим работу программы в симуляторе. Рис. 7.16. Проверка работы программы 98
В.Н. Гололобов Микроконтроллер глазами начинающего На экране монитора эта проверка выглядит лучше предыдущего варианта. Светодиоды горят ярче. Хотя две предыдущие программы выполняют одинаковую работу, но делают это несколько поразному. И это хороший пример того, что одинаково с точки зрения логики работающие, но написанные разным кодом программы могут отличаться и в работе. Сейчас каждый из светодиодов включён четверть времени, то есть, средний ток в четыре раза меньше. Это можно учесть при выборе добавочных сопротивлений. Особенно это будет заметно тогда, когда в процессе участвуют все светодиоды матрицы. В этом случае средний ток через каждый светодиод уменьшится более чем в 10 раз. Но это вопросы схемотехники, а нас сейчас интересует программирование. Вместе с тем, надеюсь, понятно, отчего я так много времени уделил симулятору. Матрицу светодиодов можно собрать на макетной плате. Но нужда в реализации схемы может возникнуть только в том случае, когда вы задумаете выполнить, скажем, что-то для лабораторных работ. Иначе паять такой макет едва ли кому-то захочется. А симулятор позволяет проверить разные варианты без опасений что-то испортить. Но программа не представляет для вас трудности – вы уже умеете управлять выходами микроконтроллера. Желательно, конечно, увеличить количество элементов матрицы. Но даже с этой матрицей можно нарисовать, например, цифры. Программа симуляции в динамическом режиме отображает реальное уменьшение яркости светодиодов, из-за чего почти не различимы горящие и погашенные элементы матрицы. Мы пока не собираемся реализовывать схему, поэтому можем использовать все выводы микроконтроллера, возвращаясь к первоначальной идее статического создания изображений. Первая же попытка, уменьшив количество элементов матрицы, зажечь светодиоды приводит к появлению «глюка». Затем программа перестаёт работать, исчезая с экрана. Попытка скопировать программу в ALTLinux приводит к тому же результату. Ничего не поделаешь. Неудачи – необходимый элемент любой работы. Но в чём причина? Количество проводов, соединяющих микроконтроллер со светодиодами (виртуальных проводов) стало непомерно большим, а в итоге вместо перекрещивания проводов некоторые соединились. Если бы это произошло с реальной макетной платой… Кроме этой ошибки есть ещё одна особенность программ KTechlab или gputils. Я не стану утверждать, что это ошибка, у меня нет datasheet PIC16F628, но выводы порта А, шестой и седьмой биты, не работают на выход, можете проверить: 99
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 7.17. Сообщение об ошибке компиляции при попытке вывести данные Как вы думаете, смириться с неудачей, остановиться на достигнутом? Или… Есть такие микросхемы, которые называются регистры. Подобные регистры есть внутри микроконтроллеров. В программе KTechlab нет регистра, но есть микросхема памяти. Оба устройства схожи. И вот, что я хочу сделать. Рис. 7.18. Идея организации матрицы с использованием внешнего элемента Чтобы повода не спутывались, можно применить шину (кстати, внутри микроконтроллера тоже есть, думаю, шина данных и адресная шина), которую часто применяют при черчении схем. Теперь, записывая в память нужное число через порт В, я вывожу второе число в порт, присоединённый к светодиодам (не забудьте, на рисунке не схема, а только идея схемы!). В моём распоряжении оказывается 16 выводов, которые позволят управлять матрицей 4x3 светодиодов в статическом режиме. Если использовать внешнее питание для светодиодов, то можно получить, как мне кажется, и нужную яркость. Но нас интересует программа. Она выглядит так: 100
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 7.19. Программа для проверки идеи организации матрицы Для записи в регистр (или память, как сделал я) мы выставляем нужное число на выходах порта В. Затем устанавливаем активный уровень на вход записи (WE). Снимаем этот уровень, устанавливая активный уровень на вход чтения из микросхемы. На выходах микросхемы нужные уровни сигнала, а мы можем использовать порт В для вывода второго нужно нам числа. Вывод CS используется для выбора микросхемы, если их несколько. Я пробовал использовать две микросхемы, но меня постигла неудача. Входы А0-А3 используются для адресации ячеек памяти в микросхеме, поскольку микросхемы памяти имеют много ячеек памяти, к которым нужно адресоваться. Кстати, когда мы используем переменные при работе с микроконтроллером, мы тоже адресуемся к ячейкам памяти. Вы можете посмотреть код на ассемблере, где адреса ячеек памяти для хранения переменных, указываются явным образом. В языке Си механизм адресации развит очень хорошо. Для этого используют специальный синтаксис, о котором мы поговорим, когда (и если) до этого дойдёт дело. И напоследок, как могут счастливые обладатели программы Proteus повторить то, что показано в программе KTechlab: 101
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 7.20. Матрица светодиодов в программе Proteus Резюме Мы научились писать на языке Си очень немногое. Мы научились управлять только выводами портов микроконтроллеров. Но, вы можете убедиться, используя матрицу светодиодов, можно создать много полезных устройств. Потренируйтесь в этом, придумывая разные статические и динамические картинки, и вы поймёте, почему интересно всем этим заниматься. 102
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 8. Как выводы порта работают на ввод информации Не хочу вас больше пугать рассказами об установке программ и прочими неурядицами. Безусловно, если неурядицы не возникнут. В этой главе мы рассмотрим работу выводов порта в качестве цифровых входов. Это основной режим работы на ввод информации. Исключение возникает у микроконтроллеров, имеющих аналоговые дополнительные модули, как АЦП или компараторы. Но это особый случай, который мы рассмотрим позже. Итак. В регистре TRIS мы задаём направление работы выводов порта, на вход или на выход. По умолчанию выводы порта, как правило, работают на ввод информации. Входы микроконтроллера устроены так же, как и другие цифровые входы, то есть, при напряжении, скажем, больше 2.5 В, считается, что на входе логическая единица, а при напряжении меньше 0.5В – логический ноль. Напряжения между этими двумя величинами никак не определены. Поэтому на входе всегда должно присутствовать одно из этих напряжений. Чтобы подать на вход уровень логической единицы, вход соединяют с плюсом питающего напряжения через резистор. Для нуля на входе используют соединение входа с общим проводом через резистор (если вы намерены менять состояние входа). Такие резисторы называются «подтягивающими», и многие контроллеры имеют встроенные подтягивающие резисторы. Обычно входы микроконтроллера соединяют с контактами: кнопками, контактами реле, контактами датчиков. Однако вход микроконтроллера может быть соединён с выходом транзистора. В этом случае подтягивающие резисторы лишние. По этой причине встроенные подтягивающие резисторы включаются установкой определённого бита в соответствующем регистре микроконтроллера. Программа, использующая входы микроконтроллера, должна быть построена так, чтобы состояние входов постоянно проверялось. Есть два механизма подобного выявления состояния входов. Один механизм – это опрос входов (или входа). С него мы и начнём. А о втором механизме, который называется прерыванием, мы поговорим позже. Механизм прерывания может использоваться не только при опросе входов. И в нём нет ничего загадочного. Но этот механизм сложнее, чем опрос. Как мы будем опрашивать входы? Не станем увлекаться количеством, остановившись на опросе одного входа. Операция опроса состояния входа проста – это операция присваивания. Создадим переменную. В программе присвоим этой переменной значение интересующего нас входа. Всё. Конечно, опрашивать вход имеет смысл только тогда, когда по результатам опроса мы что-либо предпримем, иначе нет смысла следить за состоянием входа. Я приведу простейший пример. Есть некий датчик. Когда датчик срабатывает, он замыкает контакты. Если датчик сработал, контроллер должен включить какое-то устройство. Выполнить включение проще всего с помощью реле. Таким образом, если датчик сработал, контроллер должен включить реле. Чтобы не перегружать выход микросхемы, мы включим реле через транзистор. Выход контроллера переходит в состояние высокого уровня, на базу транзистора через резистор подаётся напряжение, транзистор открывается и включает реле, обмотка которого в его коллекторной цепи. Я не буду рисовать всю схему, во всяком случае, пока, а сразу перейду к программе. 103
В.Н. Гололобов Микроконтроллер глазами начинающего Для PIC16F628A подтягивающие резисторы порта В включаются в регистре OPTION старшим битом, -RBPU. Если этот бит равен нулю, то подтягивающие резисторы включены. Используем эту возможность, подключив кнопку между выводом и общим проводом (землёй). Таким образом, если на входе, пусть это будет вход RB0, единица, кнопка не нажата; в противном случае включив выход RA0 (установим его в состояние логической единицы). Используем компилятор xc8 в программе MPLABX. Создадим новый проект, получим шаблон основного файла и запишем программу: #include "pic.h" #include <pic16f628a.h> #include <xc.h> __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); & PWRTE_OFF & MCLRE_OFF & BOREN_OFF & int main(void) { TRISA = 0xFC; TRISB = 0xFF; OPTION_REG = OPTION_REG & 0x7F; char input = 0; PORTA = 0; while(1) { input = RB0; if (input == 0) RA0 = 1; else RA0 = 0; } return 0; } Файлы, включаемые в проект, те, что следуют за ключевым словом #include, дают возможность обращаться к регистрам по их имени, использовать имена битов отдельных регистров и т.д. Не исключаю, что некоторые файлы не обязательны в данном случае, но пусть будут. Запись, следующая за ключевым словом __CONFIG, задаёт слово конфигурации, которое записывается по адресу 2007h при программировании микросхемы. В настройках портов я указал явно, что выводы порта В должны использоваться на ввод информации, хотя по умолчанию это будет сделано автоматически. У порта А два младших вывода работают на вывод информации. Запись OPTION_REG = OPTION_REG & 0x7F; устанавливает в ноль старший бит регистра OPTION, включая подтягивающие резисторы. Переменная input служит для хранения состояния входа RB0. Это переменная типа char (или byte). Явным образом я обнулил порт А. Что ещё? Присваивая переменной input значение вывода порта В, я тем самым проверяю состояние этого вывода. По результатам проверки я совершаю нужное мне действие. 104
В.Н. Гололобов Микроконтроллер глазами начинающего Типовая конструкция для языка Си if … then … else в данном случае превратилась в две фразы. Не все компиляторы имеют синтаксис классического языка Си, но всегда можно использовать работающую языковую конструкцию. Вот, собственно, всё, а с бесконечным циклом while мы уже знакомы. Вот работа программы: Рис. 8.1. Использование вывода микроконтроллера для работы на ввод информации И попутно, пока всё работает, я хочу показать ещё одну картинку: Рис. 8.2. Прохождение одного цикла программы Я немного поправил предыдущую программу, чтобы при каждом прохождении цикла вывод RA1 менял своё значение. То, что отображено на рисунке, это, в сущности, время одного прохода цикла while. Вы можете убедиться, что оно составляет 25 мкс. Правка свелась к добавлению в конце программы одной строки: RA1 = !RA1; Время играет важную роль в работе микроконтроллера. Всегда следует помнить, что одной операции, записанной на языке Си, может сопоставляться несколько машинных операций, каждая из которых выполняется за один или несколько тактов машинного цикла. Вместе с тем, не следует забывать и о быстродействии микроконтроллера. В предыдущей программе слово конфигурации выбрано так, что работает внутренний тактовый генератор. Работает он на частоте 4 МГц. А вся программа успевает завершиться за 25 мкс. Мы использовали кнопку, имеющую контакты, которым свойствен такой эффект, как 105
В.Н. Гололобов Микроконтроллер глазами начинающего «дребезг контактов»: контакты многократно замыкаются и размыкаются при нажатии на кнопку в течение некоторого времени. В нашем случае мы не замечаем этот дребезг. Но, если бы мы решили подсчитать количество нажатий на кнопку, то удивились бы результату. Насколько важно помнить о времени при создании программ для микроконтроллера, я хочу показать на примере, который приходит мне в голову. На базе микроконтроллера можно быстро собрать генератор импульсов. Для изменения частоты генерации можно использовать кнопку, которая будет менять частоту с некоторым шагом. Если не принять дополнительных мер, то программа будет работать очень плохо. Какие меры и почему, мы разберём, используя предыдущую программу. Мы видели, что один обход программы завершается за 25 мкс, а кнопка, если мы постараемся нажимать её очень быстро, скорее всего, будет замкнута не менее 100 мс. За это время программа успеет повториться около 4000 раз. То есть, изменения будут внесены столько же раз. Чтобы избежать этого, остановим программу до тех пор, пока кнопка не будет отпущена. Для этой цели используем почти пустой цикл while (я запишу только ту часть программы, о которой идёт речь): while(1) { input = RB0; if (input == 0) RA0 = 1; while (input == 0) { input = RB0; } else RA0 = 0; } Я намерено выделил тело второго цикла while, чтобы подчеркнуть, в теле цикла только опрос кнопки. Когда мы отпустим кнопку, значение на выводе RB0 станет равно единице и программа покинет этот цикл while, продолжая работу. Этот приём исправит ту часть проблемы, которая связана с многократным прохождением программы. Но не исправит проблему «дребезга контактов». То, как долго длится дребезг, зависит от конкретной пары контактов. Предположим, что это время порядка 10 мс. Тогда, внеся изменение в программу, мы можем избавиться от этого эффекта: while(1) { input = RB0; if (input == 0) { __delay_ms(10); input = RB0; } if (input == 0) RA0 = 1; while (input == 0) { input = RB0; } else RA0 = 0; } Или не можем? При использовании компилятора xc8, хотя в описании библиотеки есть такая функция, компиляция не проходит, выводя ошибку. Мы можем использовать, как это делали раньше, задержку с помощью цикла for. Но хотелось бы добавить не просто задержку, а задержку примерно на 10 мс. 106
В.Н. Гололобов Микроконтроллер глазами начинающего В этих экспериментах я использую микроконтроллер PIC16F628A. В качестве программатора я хочу использовать PICkit2, оставшийся у меня после написания предыдущей истории, где речь шла о конструкторе ROBOPICA. Приладить программатор несложно – панелька для установки микросхемы, гнездо типа RJ с 6 выводами и несколько паек. В настройках программы PICkit 2 v2.61 (настройки тоже остались с прошлого раза) я меняю только одно, без чего микросхема не определяется: Рис. 8.3. Изменение настроек программатора для определения нужного контроллера Ещё можно изменить настройки отображения слова конфигурации, чтобы оно выглядело привычнее для меня – в режиме с отключёнными дополнениями и работой от встроенного генератора тактовых импульсов я привык видеть число 3F18h. Но теперь можно записывать программу в микросхему. А программу я хочу записать такую: #include "pic.h" #include <xc.h> __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); & PWRTE_OFF int main(void) { TRISB = 0x3F; int i = 0; while(1) { RB7 = 1; for (i = 0; i <= 10; i++); RB7 = 0; for (i = 0; i <= 10; i++); } 107 & MCLRE_OFF & BOREN_OFF &
В.Н. Гололобов Микроконтроллер глазами начинающего return 0; } Почему я использую вывод RB7? При программировании этот вывод используется. Следовательно, для следующего эксперимента я могу оставить микросхему в панельке и ничего не менять. Программа понятна – цикл for как пауза при манипуляции с выводом RB7. Но, главное, зачем эта программа мне понадобилась? Программатор PICkit2 не только программатор, его можно использовать в качестве логического анализатора. Что я и намерен сделать. Записываю предыдущую программу, конечно, предварительно оттранслированную. В основном окне программы PICkit2 нужно установить галочку, отмеченную на рисунке ниже: Рис. 8.4. Начало работы с анализатором PICkit2 Выбираем инструмент: Рис. 8.5. Выбор логического анализатора 108
В.Н. Гололобов Микроконтроллер глазами начинающего В открывшемся окне вначале определяем, как будут работать выводы логического анализатора, они, кстати, могут имитировать нажатие кнопок. Рис. 8.6. Выбор направления работы выводов анализатора После нажатия на кнопку Enable IO сразу видно, что вывод Pin4, а это вывод RB7 микросхемы, меняет своё значение. Можно оставить всё, как есть, нажать кнопку Analyzer, слегка подстроить логический анализатор, нажать кнопку Run и увидеть сигнал на этом выводе. Рис. 8.7. Сигнал на выводе RB7 Мои настройки коснулись только выбора масштаба и выбора переключения триггера по возрастающему фронту сигнала первого канала (для остальных каналов безразличный уровень сигнала). Как видно из рисунка, длительность задержки составляет около 250 мкс. Это при 10 циклах, заданных в операторе for. Для получения паузы около 1 мс следует использовать 40 циклов. Проверим это: внесём изменение в программу, оттранслируем её, загрузим в микросхему и посмотрим: 109
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 8.8. Новый вид сигнала после исправления программы Это близко к 1 мс, но ещё не одна миллисекунда. Можно ещё немного изменить количество проходов, получив близкое к миллисекунде значение, а затем увеличить всё в десять раз, чтобы получить задержку в 10 мс. Рис. 8.9. Получение задержки 10 мс Такой точности вполне хватит для нашей цели. Если нужно точнее выбирать временные интервалы, то следует использовать встроенные таймеры, но пока нам это не нужно. Я хотел показать, что программатор PICkit2 может кроме программирования использоваться и в качестве осциллографа, если у вас нет осциллографа. Он может использоваться и для проверки последовательной передачи данных через UART, и в качестве аппаратного отладчика программы. Я согласен, что подгонять, как я это делал, временной интервал, многократно перепрограммируя микроконтроллер, это плохая идея. Но посмотреть, как меняется сигнал – это хорошая идея. А для подгонки вручную временных интервалов следовало использовать симулятор. Программа ISIS даёт хорошее совпадение с реальными параметрами. Вот как выглядит полученная нами задержка в Proteus: 110
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 8.10. Симуляция программы, использующей задержку, в программе ISIS С учётом того, что одно деление – это 1 мс, результаты аппаратной и виртуальной проверки достаточно хорошо совпадают. Программа ISIS позволяет не только видеть сигнал на экране виртуального осциллографа, но использовать и график для более точной оценки событий. Рис. 8.11. График предыдущего сигнала в Proteus Вернёмся, впрочем, к программированию. Созданный нами цикл for для задержки работы программы мы можем использовать при устранении дребезга контактов. Достаточно после первого опроса входа добавить эту задержку, после неё ещё раз опросить состояние контактов, чтобы дребезг контактов не повлиял на результат опроса. Эту же задержку можно добавить и после выхода из проверки внутри нашего блока, ожидающего отпускания кнопки. При отпускании кнопки тоже возможен дребезг контактов. Вот, как выглядит программа после изменений: while(1) { input = RB0; if (input == 0) { for (i = 0; i <= 500; i++); 111
В.Н. Гололобов Микроконтроллер глазами начинающего input = RB0; } if (input == 0) RA0 = 1; while (input == 0) { input = RB0; } for (i = 0; i <= 500; i++); if (input == 1) RA0 = 0; } Резюме Мы пока научились немногому: мы можем управлять выходами микроконтроллера, мы можем использовать выводы порта для ввода цифровой информации, мы можем организовать задержку в выполнении программы, когда нам это нужно. Вместе с тем, используя даже это немногое, можно создать ряд полезных устройств, в чём вы можете убедиться лично. Придумывать же такие устройства – это самое интересное. Я приведу пример, как полученные знания вы можете применить на практике. Одна из программ этой главы генерирует меандр на выводе RB7 с частотой примерно 1 кГц. Последняя программа позволяет опрашивать кнопку. Нажимая кнопку, вы можете менять частоту генератора, если вместо числа проходов цикла for в задержке используете переменную, которую будете менять при каждом нажатии на кнопку, определив удобный шаг изменения частоты (менять вы будете, конечно, время задержки или период). Используя вторую кнопку для уменьшения времени задержки, вы получите вполне реальное устройство – генератор прямоугольных импульсов. В следующей главе мы рассмотрим ряд удобств при создании своих программ, но вы уже сейчас, повторюсь, можете писать свои программы для реальных устройств. Не верите? 112
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 9. Почему удобно использовать функции? В предыдущей главе мы придумали, как создать в программе задержку, паузу при выполнении программы. Для этой цели прекрасно подошёл цикл, выполняющийся заданное число раз. Пока выполняется этот цикл, программа ничего другого не делает. Мы подогнали количество проходов цикла так, чтобы при тактовой частоте 4 мГц задержка составляла примерно 1 миллисекунду. Мы можем, конечно, если нам нужны задержки с другим временем, погонять этот цикл каждый раз. Занятие увлекательное, но со временем оно начинает раздражать. Хотелось бы использовать некий оператор, который позволяет выполнить нужную по времени задержку. Ряд компиляторов поддерживает библиотеки таких удобных средств. Если же нет, то приходится создавать их самостоятельно. Нужный нам оператор задержки выполняется в виде функции. Однажды создав эту функцию, мы можем многократно обращаться к ней в программе. Такое обращение называется вызовом функции. Функция по сути – это подпрограмма (или макрос в других случаях). Мы можем в эту подпрограмму отправить нужный нам параметр, влияя на характер работы этой функции. В языке Си понятие функции используется и для математической обработки данных, и для выполнения других операций. При математической обработке данных в функцию, как правило, передаются данные, которые нужно обработать. Это могут быть, например, два числа, которые функция должна сложить. Завершив работу, функция должна вернуть результат. Поэтому при описании функции мы должны определить тип данных, с которыми она будет работать, и тип результата, если результат будет возвращаться из функции. Давайте напишем и попробуем отладить функцию задержки на заданное значение миллисекунд. Вернёмся к предыдущей главе, где мы осуществляли задержку на 1 мс с помощью цикла for. И с помощью этого цикла напишем функцию, которую можно использовать для создания задержек на заданное число миллисекунд. Как обычно, создадим новый проект в программе MPLABX. Используем компилятор xc8. Назовём проект ms_func. #include "pic.h" #include <xc.h> __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); & PWRTE_OFF void delay_ms(int val) { int i = 0; val *= 48; for (i = 0; i <= val; i++); } int main(void) { TRISB = 0x3f; while (1) { RB7 = 1; delay_ms(10); RB7 = 0; 113 & MCLRE_OFF & BOREN_OFF &
В.Н. Гололобов Микроконтроллер глазами начинающего delay_ms(10); } return 0; } Функция delay_ms(int val) позволяет в качестве параметра использовать число, указывающее время в миллисекундах, на которое следует задержать выполнение программы. В основной программе для вывода RB7 эта задержка определена в 10 миллисекунд. Проверим с помощью программы ISIS, похожа ли задержка на то, что мы хотели получить. Рис. 9.1. Проверка работы программы ms_func Я уверен, что функция не будет давать точных времён, это так, но во многих случаях нам и не нужны точные времена задержек, а использовать функцию, как вы можете увидеть, полезно. Один раз описав функцию, мы в программе можем обращаться к ней по имени, задать нужный нам параметр, и это всё, что нам нужно. Кроме того, если функция нам полезна, мы можем вынести её в специальный файл с расширением «.h», файл заголовка, и добавлять его в свой проект, как мы это делаем с помощью директивы #include. Если файл в той же папке, что и проект, то его имя заключается в двойные кавычки, если файл в разделе включений, то в скобки – посмотрите выше, оба варианта есть в программе. Любая функция может возвращать некоторое значение, поэтому перед именем функции добавляется тип возвращаемого значения. Компилятор xc8 даёт шаблон для основной программы, которая тоже не более, чем функция. Поэтому имени основной программы, а её имя всегда main, предшествует тип возвращаемого значения int, а в теле программы есть оператор return 0. Каждый из операторов языка Си должен завершаться точкой с запятой. Наша функция ничего не возвращает в основную программу, ей предшествует запись void. Все операторы в функции заключены в фигурные скобки, чтобы компилятор знал, где начинается, а где заканчивается функция. В скобках после имени функции «шаблон» параметра, то есть, параметр обозначен в виде переменной, имеющей определённый тип. Вписывая число впоследствии, мы задаём значение этой переменной. Таким образом, мы передаём значение функции, с которым она будет работать. В приведённом выше примере есть запись: val *= 48;. Эта запись эквивалента такой: val = val*48;. В языке Си часто используют сокращения – когда-то всё написание программы делалось вручную. Чтобы облегчить работу программиста ввели подобные 114
В.Н. Гололобов Микроконтроллер глазами начинающего сокращения. Но можно применять и обычную запись операций, эквивалентны, например, записи вида a += 10 и a = a + 10. Я предпочитаю их не использовать, но вы должны знать, что они означают. Программа MPLABX имеет встроенный инструмент, который называется логический анализатор. Он позволяет в динамике посмотреть, что происходит с выводом, не выходя из программы MPLABX. Рис. 9.2. Инструмент логический анализатор в программе MPLABX Включив анализатор в окно вывода информации, можно с помощью его настроек задать вывод микроконтроллера для наблюдения. Рис. 9.3. Выбор вывода для наблюдения 115
В.Н. Гололобов Микроконтроллер глазами начинающего Теперь достаточно запустить отладку, подождать некоторое время, чтобы увидеть результат. Рис. 9.4. Вид сигнала на выводе микроконтроллера Я воспользовался кнопкой Reset Zoom, чтобы развернуть сигнал. В программе, как вы видите, функция задержки имеет параметр единица. Не знаю, можно ли изменить отсчёт времени не в циклах, а в тех же миллисекундах, но мне больше нравились предыдущие версии MPLABX, где это было возможно. Я согласен, что отсчёт в циклах более правилен, поскольку время цикла меняется при изменении тактовой частоты генератора микроконтроллера, а количество циклов остаётся неизменным, но мне было удобнее видеть этот сигнал в миллисекундах. Впрочем, программа стремительно меняется. Впервые она привлекла моё внимание в тестовом виде, начал я рассказ, установив версию 1.30, а сегодня скачал и установил версию 1.41. Если нам всё-таки захочется, чтобы наша функция задержки была точнее вышеприведённой. Есть два пути достижения цели. Об одном из них, об использовании таймера мы поговорим позже, а сейчас попробуем использовать другой. В программу мы можем сделать вставки на ассемблере. А в ассемблере есть оператор, который не делает ничего, но за один машинный цикл – это оператор nop. Посмотрим, сколько времени займёт пауза, если мы запишем функцию в виде: void delay() { #asm nop nop nop nop nop nop nop nop nop nop #endasm } 116
В.Н. Гололобов Микроконтроллер глазами начинающего В программе эту функцию мы используем без параметра. Проверим вначале, будет ли сигнал на выводе RB7 в самой программе MPLABX. Рис. 9.5. Сигнал на выводе RB7 при использовании ассемблерной вставки Чтобы точнее определить, как сказывается на длительности задержки использование вставки на ассемблере, я удалю всё написанное на языке Си, дополнив ассемблерную вставку управлением одного вывода. Каждая из пауз будет состоять из десяти пустых операций. Вот вид сигнала в окне логического анализатора: Рис. 9.6. Сигнал на выводе RA0 в программе на ассемблере На рисунке видно, что каждое деление оси времени – это два цикла (время измерено в циклах тактового генератора). В среднем, а импульсы различаются, пауза занимает 14 циклов. А, заглянув в настройки осциллографа (логического анализатора), можно легко перевести это в привычные для нас секунды (вернее, микросекунды). 117
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 9.7. Настройки осциллографа в программе MPLABX При частоте процессора микроконтроллера 1 МГц один цикл выполняется за 1 мкс. То есть, 14 циклов – это 14 мкс. Но пустых операторов было 10!? Но кроме пустых операторов есть операторы перехода для создания бесконечного цикла, есть операторы установки и сброса вывода RA0. Они и добавляют время к нашему запросу, и они делают полученные на осциллограмме времена высокого и низкого состояния вывода разными. Однако с увеличением количества пустых операторов nop влияние дополнительных операторов уменьшится, а пауза будет получаться более точной, чем при использовании нашей конструкции на языке Си. Мы можем это проверить, ведь вы не спешите? Современные среды разработки имеют встроенные редакторы текста, что существенно облегчает подобные операции – достаточно выделить фрагмент текста, скопировать его и вставить в нужное место. Исходный текст вставки (собственно всей программы) на ассемблере имел вид: #include "pic.h" #include <xc.h> __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); int main(void) { #asm movlw (0FEh) bsf status, 5 bcf status, 6 movwf (133)^080h & PWRTE_OFF ;RP0=1, select bank1 ;RP1=0, select bank1 l02: bcf bcf bsf status, 5 ;RP0=0, select bank0 status, 6 ;RP1=0, select bank0 (40/8),(40)&7 ;volatile nop nop nop nop nop nop 118 & MCLRE_OFF & BOREN_OFF &
В.Н. Гололобов Микроконтроллер глазами начинающего nop nop nop nop bcf bcf bcf status, 5 ;RP0=0, select bank0 status, 6 ;RP1=0, select bank0 (40/8),(40)&7 ;volatile nop nop nop nop nop nop nop nop nop nop goto l02 #endasm return 0; } Повторяя пустую операцию по 40 раз в каждом блоке, мы получим следующий результат: Рис. 9.8. Осциллограмма при увеличении пустых операторов Импульсы сблизили длину при высоком и низком состоянии, длительность импульсов стала близка к 44 циклам. Если в первом случае ошибка была 40%, то сейчас она 10%. Использование ассемблера, похоже, позволила нам улучшить положение. И, видимо, по этой причине так много поклонников ассемблера – точнее, компактнее и т.п. Но кодированию на ассемблере нужно учиться дольше, и пока мы оставим это. Если заглянуть в руководство к компилятору xc8, то можно обнаружить, что компилятор предлагает функцию задержки (и даже несколько), она есть в библиотеке функций этого компилятора, хотя будут ли они работать в свободной версии компилятора?.. Можно попробовать. #include "pic.h" #include <xc.h> 119
В.Н. Гололобов Микроконтроллер глазами начинающего __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); #define _XTAL_FREQ 4000000 & PWRTE_OFF & MCLRE_OFF & BOREN_OFF & int main(void) { TRISA = 0xFE; while(1) { RA0 = 1; __delay_ms(1); RA0 = 0; __delay_ms(1); } return 0; } Простая программа, но её достаточно для теста. Программа для микроконтроллера PIC16F628A. Рис. 9.9. Осциллограмма при применении встроенной функции задержки Каждое деление – это 200 циклов, а осциллограф настроен так, что каждый цикл совершается за 1 микросекунду. Не трудно подсчитать, что пауза завершается за 1000 циклов, что соответствует времени задержки 1 миллисекунда. Как мы и «заказывали». Обратите внимание на строку #define _XTAL_FREQ 4000000, которая указывает тактовую частоту внутреннего генератора, а слово конфигурации указывает на то, что будет использован внутренний тактовый генератор. Компилятору для создания точных временных интервалов обязательно нужно знать, какова тактовая частота. Именно она задаёт тон расчётам по вычислению нужного времени задержки. Все эти расчёты скрыты во встроенной функции, которую мы используем в готовом виде: __delay_ms(1). Для следующего эксперимента я использую другую паузу, это будет пауза в 100 мкс. Функция, работающая с микросекундными паузами, похожа на предыдущую, меняется только единица измерения: __delay_ms(100). Рис. 9.10. Пауза в 100 мкс в логическом анализаторе MPLABX 120
В.Н. Гололобов Микроконтроллер глазами начинающего Компилятор xc8 имеет достаточно много полезных функций, которые, вероятно не сейчас, но позже, понадобятся вам. В плане библиотечных функций очень выделяется среда разработки MicroC Pro – её библиотека хорошо ориентирована на дополнительные модули и часто используемые внешние устройства микроконтроллеров. Но это совсем другая среда разработки, требующая отдельного разговора. О том, какие функции доступны, можно прочитать в руководстве к компилятору. При этом не следует забывать, что разные среды разработки, разные компиляторы могут иметь не только разный синтаксис, но очень отличаться своими библиотеками. Рис. 9.11. Описание библиотеки в руководстве к xc8 Резюме В этой главе мы бегло познакомились с таким полезным понятием, как функция. Множество полезных функций собирают, как правило, в библиотеку, и после этого можно пользоваться функциями, вызывая их по имени. 121
В.Н. Гололобов Микроконтроллер глазами начинающего Можно и не знать, как устроена функция, достаточно знать, как вызвать функцию и какие параметры следует передать в функцию. Некоторые функции используются прямым указанием файла заголовка, если эти файлы есть в разделе включения компилятора. Так мы обращались к файлам pic.h и xc.h , если файл находится в той же папке, что и проект, то его имя берётся в кавычки, если он в другом месте, то имя заключается в ломаные скобки. В этой главе мы часто использовали логический анализатор для наблюдения за сигналами. Осциллограф логического анализатора использует в качестве единицы времени машинные циклы микроконтроллера. Каждая операция выполняется за определённое количество машинных циклов. Сколько циклов требует та или иная машинная операция, зависит от конкретной модели или модельного ряда микроконтроллеров. Не следует забывать, что операции языка Си, за редким исключением, выполняются за несколько машинных операций. То есть единственная команда на языке Си при переводе в машинные коды превращается в ряд команд, каждая из которых выполняется за один или несколько тактов генератора. От частоты тактового генератора, определяемой либо внешними цепями – это может быть кварцевый резонатор или RC цепь – либо внутренними цепями, от выбранной частоты генератора зависят такие временные конструкции, как паузы. При использовании библиотечных пауз в компиляторе xc8 требуется указать частоту тактового генератора явным образом. Без этого указания трансляции программы будет происходить с ошибками, не позволяя получить загрузочный hex-файл. Создавая свои собственные программы на языке Си, приходится часто создавать собственные функции. Если эти функции применимы многократно, то собирайте их в своей библиотеке. Самый простой способ – это выписать функцию в отдельный файл с расширением «h». Позже его можно добавить в проект с помощью директивы #include. Очень полезная директива (указание компилятору) #define. Вы можете определить с её помощью ряд постоянных, к которым будете обращаться по имени, например, если какие-то регистры не прописаны в файле микроконтроллера, можно их определить, указав явно их адрес, а в программе обращаться по имени регистра. Это не так часто требуется, но… Встречая готовые полезные функции, полностью описанные в чужих программах, вы можете сохранить их в своей коллекции функций. Создайте заголовочный файл, скопируйте в него нужную функцию, сохраните этот файл в своей библиотеке файлов. Примером такого полезного файла может служить файл для работы с жидкокристаллическим дисплеем или файл для обмена данными через порт последовательного ввода-вывода данных. Встроенный во многие микроконтроллеры модуль USART поддерживает программную часть 122
В.Н. Гололобов Микроконтроллер глазами начинающего протокола RS232. В некоторые модели встраивают модуль USB, имеющий более сложный протокол обмена. Если скорость работы контроллера позволяет обслужить работу по протоколу USB, а встроенный модуль отсутствует, то файл организации работы по программному протоколу USB сослужит хорошую службу. Напомню, что в языке Си функция может выполнять и математические операции, возвращая результат расчётов, и выполнять ряд полезных операций, принимая параметры и используя их, как, например, при работе с широтноимпульсной модуляцией. Но об этом, если мы и поговорим, то позже. А сейчас обратимся к более простым, но не менее важным вопросам. 123
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 10. Ветвление программы Мы уже сталкивались с ветвлением программы, когда использовали условный оператор if. Если условие, следующее за оператором, выполняется, то программа проходит одну ветвь, если нет, то другую. Повторим программу, использующую оператор if. #include "pic.h" int main(void) { char input; TRISA = 0xFE; TRISB = 0xFF; while (1) { input = RB0; if (input == 0) RA0 = 0; if (input == 1) RA0 = 1; } return 0; } Переменная input запоминает состояние вывода RB0, к которому мы подключим, скажем, кнопку, соединяющую вывод с общим проводом. В зависимости от того, нажата кнопка или нет, программа проходит по той или другой ветви, управляя состояние вывода RA0. Графический язык программирования позволяет лучше видеть то, как ветвится программа: Рис. 10.1. Ветвление программы И, надеюсь, вы помните, что использование кнопки в программе удобно для моделирования работы устройства за компьютером. В реальном устройстве кнопка – это, например, контакты датчика температуры. Когда контакты замкнуты, температура достигла заданного уровня, нагреватель выключается, иначе, температура ниже заданной, он остаётся включён. Если мы заговорили о реальном устройстве, то представьте ситуацию: вы автоматизировали обогреватель, чтобы он поддерживал тепло в доме. Он исправно включается и выключается. В доме тепло. Но вы уходите на работу, в доме никого нет, а обогреватель исправно включается и выключается, поддерживая заданную температуру! Вам это нужно? 124
В.Н. Гололобов Микроконтроллер глазами начинающего Добавьте датчик, который отслеживает, когда все покидают дом. Теперь есть ещё одна пара контактов, и вы можете добавить ещё одно условие: #include "pic.h" int main(void) { char input; char people; TRISA = 0xFE; TRISB = 0xFF; while (1) { input = RB0; people = RB1; if (input == 0) RA0 = 0; if (input == 1){ if (people == 1) RA0 = 1; } } return 0; } Такой условный оператор называется вложенным. При размыкании контактов термометра проверяется состояние датчика, показывающего присутствие людей в доме. Если никого нет дома, то обогреватель (команда RA0 = 1;) не будет включаться. Такого же результата можно достичь иначе записывая условие: if ((input == 1)&( people == 1)). Давайте проверим так ли это? Мы могли ошибиться и в первом варианте программы, и во втором. Создадим новый проект, как делаем это обычно. Как обычно я использую PIC16F628A и компилятор xc8, а вы можете использовать другую модель контроллера и другой компилятор, благо MPLABX поддерживает достаточно много компиляторов. Запишем и оттранслируем первый вариант программы. Осталось проверить её работу. Я уже рассказывал о программах KTechlab и ISIS (Proteus). Но проверить работу программы можно и не покидая MPLABX. Среди отладочных инструментов этой среды разработки, есть нужный нам: Рис. 10.2. Инструмент отладочных стимулов Выбрав этот инструмент, зайдём на вкладку Clock Stimulus: 125
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 10.3. Вводим необходимые данные для работы стимулов Записав метку для первого вывода RB0, зададим количество машинных циклов, когда вывод в низком состоянии, и количество машинных циклов высокого состояния вывода. Рис. 10.4. Настройки для выводов RB0 и RB1 После настройки вывода RB0 для настройки второго вывода используем кнопку (на панели слева) добавления строки, где во вновь появившейся строке запишем метку people и введём данные для неё. Дополнительно используем кнопку для задания синхронного режима (верхняя кнопка на панели слева, помечена на рисунке). Далее мы используем уже знакомый нам логический анализатор, где выведем сигналы RA0, RB0 и RB1. Запустим отладку (Debug основного меню, раздел Debug Project). Рассмотрим полученную осциллограмму: Рис. 10.5. Осциллограмма, полученная отладчиком после настройки стимулов Как видно из осциллограммы (я увеличил её после нажатия на кнопку Reset Zoom, выделив курсором нужный участок диаграммы), вывод RA0 переходит в высокое состояние, когда в высокое состояние переходят выводы RB0 и RB1. И вывод RA0 остаётся в высоком состоянии до тех пора, пока оба вывода RB0 и RB1 в высоком состоянии, иначе (посмотрите на вывод RB0), если хотя бы один из них в низком состоянии, переходит в низкое состояние. 126
В.Н. Гололобов Микроконтроллер глазами начинающего Изменим программу в соответствии со вторым предположением (или, если вам угодно, предложением). После трансляции программы и запуска отладки получим такую диаграмму: Рис. 10.6. Диаграмма выводов RA0, RB0 и RB1 после изменения программы Представленная на рисунке диаграмма показывает, что результат одинаков при обоих вариантах программы. Вывод RA0 принимает состояние высокого логического уровня, когда «И» вывод RB0, «И» вывод RB1 находятся в состоянии высокого логического уровня. Это и записано в выражении, где операция «&» объединяет два условия в одно: if ((input == 1)&( people == 1)). Вернёмся ненадолго к первому варианту программы, учитывающей два условия: if (input == 1){ if (people == 1) RA0 = 1; } Я уже говорил, что условных ветвлений программы может быть несколько. Они могут быть вложенными, а могут учитывать разные варианты состояния или значения какой-либо переменной. Чтобы не повторять многочисленные if, программисты давно придумали конструкцию, которая называется switch, переключение. Вновь, наиболее ясно это видно в среде разработки, использующей графический язык программирования. Я использую рисунок, полученный в программе Flowcode, где эта конструкция появилась, если я не путаю, начиная с версии 4: Рис. 10.7. Переключение по значению переменной 127
В.Н. Гололобов Микроконтроллер глазами начинающего Здесь переменная input, зависящая от кнопки, подключённой к выводу RB0, переключает работу программы в двух направления, которые зависят от значения переменной, ноль или единица. Если переменная результат, например, вычисления, если переменная может принимать несколько значений, полученных при разных параметрах функции, то программа может проходить по нескольким ветвям, переключаемым оператором switch, в зависимости от значения переменной. В нашем случае переменная может принимать только два значения, поэтому ветвей только две. На языке Си подобная конструкция выглядит так: switch(input) { case ’0’: RA0 = 0; break; case ’1’: RA0 = 1; break; } Не ветвление, но похоже – подпрограмма. Некоторые программные блоки мы можем использовать многократно по ходу выполнения программы. Конечно, мы можем их записывать раз за разом, благо встроенный текстовый редактор позволяет выделить блок с помощью курсора мышки, а затем вставить его в нужное место столько раз, сколько мы захотим. Но, согласитесь, программа растягивается, её труднее воспринимать как целое. Чтобы избежать повторения блоков программы, программисты используют такое понятие как подпрограмма. Записав блок программы один раз, его можно вызвать с помощью инструкции CALL, за которой следует имя (или название) подпрограммы. Вновь я начну с примера на языке графического программирования. Рис. 10.8. Использование подпрограммы Вам это ничего не напоминает? Вспомните вызов функции! 128
В.Н. Гололобов Микроконтроллер глазами начинающего Даже в тех случаях, когда подпрограмма не используется многократно, её можно создать отдельно от основной программы, проверить, отладить. Скажем, основная программа получает данные от датчиков температуры. В подпрограмме мы приводим эти данные к нужному формату и выводим значения температуры на индикатор. Согласитесь, и программа выглядит аккуратнее и понятнее, и подпрограмма может пригодиться позже. Некоторые компиляторы не используют даже ключевое слово CALL для вызова подпрограммы. Вот как выглядит подпрограмма и её вызов (программа повторяет выше приведённую) с компилятором xc8. #include "pic.h" #define _XTAL_FREQ 4000000 void my_sub() //Подпрограмма { RA0 = 1; __delay_ms(1000); RA0 = 0; } int main(void) { //Основная программа TRISA = 0xFE; int input; while (1) { input = RB0; if (input == 0) my_sub(); } return 0; } С подпрограммой поступают как с любой функцией: её описывают как функцию, её вызывают как функцию. Особое значение подпрограмма приобретает при использовании механизма прерывания. Сам механизм задуман для выполнения важных блоков программы, но достаточно редко требующих внимания, когда процессор занят очень важной работой. Рассмотрим пример с регулировкой температуры в помещении. Температура меняется очень медленно. Процессор микроконтроллера, даже тогда, когда он работает с внутренним генератором, как PIC16F628A при частоте 4 МГц, даже в этом случае он успевает не только отслеживать температуру, но и управлять обогревателем. Нет нужды использовать прерывание. Теперь усложним задачу – положим, что наш микроконтроллер опрашивает несколько десятков датчиков в теплице. Современные датчики температуры передают данные по протоколу I2C или 1wire. Что занимает не один-два машинных такта, а много больше. Предположим, что в теплице установлены противопожарные датчики. Если хотя бы один из датчиков сработал, следует немедленно отреагировать на это. В этом случае выручает механизм прерывания. Процессор прекращает текущую работу, переходит к подпрограмме обслуживания прерывания. В случае пожарной тревоги он может перепроверить, действительно ли датчик сработал или это был сбой, если датчик сработал, включить тревогу. 129
В.Н. Гололобов Микроконтроллер глазами начинающего Поскольку при использовании подпрограммы на стадии трансляции можно точно указать и адрес подпрограммы, и адрес продолжения работы, подпрограмма, как мы видели, мало чем отличается от любой функции. Другое дело прерывание, которое может возникнуть в любом месте программы. В этом случае для процессора организуется особая область памяти, которая называется стеком. В ней, прерываясь, процессор может сохранить адрес того места программы, где он прервался, сохранить промежуточные данные. Возвращаясь их подпрограммы обслуживания прерывания, процессор прочитывает эти данные из стека и может продолжать текущую работу. Прерывание может быть разрешено, а может быть запрещено программно. У разных моделей микроконтроллеров прерывания обустроены по-разному. У контроллеров типа PIC16F628A для прерываний используется аппаратный механизм со скрытым стеком. Пользователь только использует доступные прерывания, а всеми процедурами занимается аппаратная часть микроконтроллера. Я считаю, что для начинающего пользователя такой механизм удобнее. Не следует только увлекаться прерываниями – прерывания, вложенные в другие прерывания, могут переполнять стек, что приведёт к сбою в работе контроллера или его зависанию. Об этом следует помнить. Ошибки, связанные с такими сбоями, трудно выявить на этапе отладки программы и трудно обнаружить в реальном устройстве. Подпрограмма обслуживания прерывания может выглядеть так (пример из руководства): int tick_count; void interrupt tc_int(void) { if (TMR0IE && TMR0IF) { TMR0IF=0; ++tick_count; return; } // process other interrupt sources here, if required } Вид подпрограммы, если не считать ключевого слова interrupt, схож с видом функции. Для возвращения к программе служит ключевое слово return. Оно-то и инициирует обращение к стеку для получения данных о месте, в котором возникло прерывание программы. Поближе с прерываниями мы познакомимся тогда, когда нам понадобится зачем-либо прерывать программу. Микроконтроллеры часто используют для реакции на внешние события. При подключении к датчику движения микроконтроллер реагирует на срабатывание этого датчика. Если микроконтроллер входит в состав охранного устройства, он должен включить тревогу. Произошло событие – сработал датчик движения при проникновении постороннего в охраняемую зону – контроллер реагирует, подавая сигнал на пульт охраны. Если микроконтроллер входит в состав программируемого выключателя, то реагирует на срабатывание датчика движения, включая свет. Есть событие, есть ответная реакция. Во всех случаях программа имеет ответвления. При наличии события программа в месте, отведённом для этого, переходит на другую ветвь, как если бы это были две разные программы. Поэтому полезно, особенно для начинающего, подробно описать 130
В.Н. Гололобов Микроконтроллер глазами начинающего каждую из ветвей программы, возможно, написать две программы при её ветвлении и исследовать каждую из программ отдельно. Я уже говорил, что при работе кнопки возникает дребезг контактов. В одних случаях он никак не сказывается на работе, в других может полностью деморализовать программу. Выделяя ветви программы в отдельные программы, можно добавить в них программный счётчик событий (срабатывания кнопки), чтобы выяснить, сколько раз происходит замыкание контактов. В большой программе это сделать сложнее. А, используя осциллограмму, можно определить и время, за которое проходит этот процесс «дребезга», чтобы выбрать время задержки работы программы, как мы делали это ранее. Вновь, в большой программе это сделать сложнее. При отладке программы можно применять разные приёмы. Так, чтобы проверить, выполняется ли ветвление программы, то есть, выполняется ли условие, можно временно, на период отладки, добавить в ветви программы установку вывода в высокое состояние в одну ветвь, а установку этого вывода в низкое состояние в другую ветвь. Так легко выявить, скажем, ошибку ветвления такого рода: #include "pic.h" int main(void) { char input; TRISA = 0xFE; TRISB = 0xFF; while (1) { input = RB0; if (input = 0) RA0 = 0; else RA0 = 1; } return 0; } Мы хотели бы, чтобы при нажатии на кнопку вывод RA0 принимал значение низкого логического уровня. Вот как это выглядит в действительности: Рис. 10.9. Проверка работы программы в Proteus 131
В.Н. Гололобов Микроконтроллер глазами начинающего Кнопка нажата, а вывод RA0 остаётся в высоком состоянии. В большой программе, просматривая текст, можно не заметить ошибку. Она может остаться незамеченной и при отладке программы. Только в реальной работе, когда что-то идёт не так, можно определить, в каком месте программы возможна ошибка. Вместе с тем, выделив ветку программы в самостоятельную программу, мы быстро можем определить, что эта часть программы неверна, найти и исправить ошибку: if (input == 0) RA0 = 0; Кроме того, большая программа для всего устройства может обращаться ко многим внешним компонентам: кнопкам, датчикам, индикаторам. Рисовать всё это с целью проверки в той же программе ISIS долго. И смысл этого появляется тогда, когда есть уверенность, что программа написана верно. Подведём первые итоги Мы пока мало узнали и о контроллерах, и о языке Си (почти ничего), но даже сейчас мы можем создавать работающие и полезные устройства. Что мы узнали за это время? Что внутри микроконтроллера есть область памяти, где мы можем хранить переменные. Переменная - это необходимый элемент программы, который в сочетании с операторами языка программирования позволяет манипулировать входами и выходами микроконтроллера, что в свою очередь, и обуславливает полезную работу, выполняемую микроконтроллером. Мы выяснили, что выводы контроллера (почти все) могут служить входами или выходами, как нам удобнее. И это определяется в специальных регистрах микроконтроллера TRIS. Мы познакомились с некоторыми компьютерными инструментами, которые крайне полезны при создании программ для микроконтроллеров. Попутно узнали, что есть разные языки программирования, очень разные. И есть разные трансляторы программы с этих языков программирования в те коды, которые понятны процессору контроллера. В зависимости от ваших финансовых возможностей вы можете использовать разные трансляторы, как и разные инструменты для отладки программ. Некоторые инструменты, как программа KTechlab, не требуют финансовых затрат (почти не требуют), но требуют некоторых усилий, чтобы этот инструмент был полезен при работе. Мы познакомились с такими простыми операторами языка программирования, как присваивание – назначения, скажем, числа переменной или значения регистру микроконтроллера. Как оператор цикла, позволяющий многократно повторять одни и те же операции. Познакомились с такими полезными понятиями, как подпрограмма (или функция, макрос). И с такой полезной конструкцией, как матрица. Согласитесь, мы почти ничего ещё не узнали. Но, тем не менее, я предлагаю в следующей главе начать разработку устройства, которую мы осуществим от общих рассуждений, до реализации и проверки на макетной плате. Разработку не самого сложного устройства, это так, однако и знания предмета у нас пока не самые богатые. 132
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 11. Начало разработки первого устройства Во многих учебниках вы встречаете примеры, которые вам кажутся скучноватыми: из одной трубы вода вливается, из другой выливается… Или что-то похожее. Я сам далеко не всегда могу дочитать условие задачи до конца, мне становится скучно, а в таком состоянии решать задачу трудно, только необходимость может заставить сделать это. Увы. Я точно знаю, что у каждого начинающего есть своя заветная мечта – заветная схема или устройство. Но таких схем слишком много, чтобы описать каждую из них, слишком много устройств, чтобы рассказать о том, как разработать их на базе микроконтроллера (и их превеликое множество), создавая собственную программу. Как всякий сочинитель, я хочу придумать нечто, что было бы не скучнее задачки из учебника, но занятнее, чем переливание из пустого в порожнее. Хотя благоразумие требует начать с простых примеров, я часто это делал, подобно управлению ёлочными гирляндами, мне самому скучно писать об этом. А что вы скажете, например, о… протоколе 1-wire, на котором микроконтроллер общается с некоторыми датчиками? Достаточно солидно – протокол, вполне респектабельно и 1wire. И хочу сделать это не рассказом о том, как протокол обустроен, как это перевести на язык, скажем, Си, а иначе. Вначале мы создадим упрощённый имитатор датчика температуры DS18B20. Затем проверим его работу с микроконтроллером. А потом, проверим всё это на макетной плате с помощью реального датчика, создав простой термостат. Я не думаю, что при этом мы выйдем далеко за пределы тех знаний, что получены из предыдущих глав. Займёт это одну-две главы, может быть, больше, посмотрим. Н начнём мы с рассмотрения самого устройства, термостата. Термостат вообще Термоста́т — прибор для поддержания постоянной температуры. Поддержание температуры обеспечивается либо за счёт использования терморегуляторов, либо осуществлением фазового перехода (например, таяние льда). Для уменьшения потерь тепла или холода термостаты, как правило, теплоизолируют. Но не всегда. Широко известны автомобильные моторы, где летом нет никакой теплоизоляции и за счёт действия восковых термостатов поддерживается постоянная температура. Другим примером термостата, широко используемого в быту, является холодильник (Wiki). Самый простой термостат, какой я могу вспомнить, состоял из ртутного термометра с контактами, один из которых был подвижным, реле, обогревателя и источника питания. Когда температура повышалась, ртутный столбик поднимался до подвижного контакта и замыкал контакты. Реле включалось и своими контактами выключало обогреватель. Некоторое время температура продолжала расти, затем начинала падать. Когда ртутный столбик опускался при остывании так, что размыкал контакты, реле выключалось, включая обогреватель. И процесс повторялся. Схема такого термостата до гениальности проста и выглядит так: 133
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 11.1. Схема простейшего термостата SW-SPST1 на схеме – это контакты ртутного термометра или биметаллические; реле RL1 включает и выключает обогреватель (изображённый мною с помощью светодиода). Для симуляции этой схемы я использовал программу TINA-TI. Напряжение питания определяется параметрами используемого реле. А параметры подогревателя, который включается контактами реле, зависят от параметров закрытого пространства термостата. Как правило, через контакты термометра протекает достаточно большой ток. И первое изменение, которое вносят в эту схему, направлено на уменьшение этого тока. Проще всего устранить проблему – добавить в схему транзистор. Вот ток через контакты для выбранного реле без транзистора: Рис. 11.2. Измерение тока через контракты «термометра» А вот схема с транзистором: 134
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 11.3. Усовершенствование простейшей схемы термостата Цель достигнута, ток через контакты термометра существенно снижен. Если температура, которую должен поддерживать термостат, не слишком высокая, а обогреваемый объём достаточно маленький, то схему можно упростить, если найти подходящий нагреватель. Рис. 11.4. Схема с прямым подключением нагревателя Ток через контакты сохранился прежним, маленьким, а надёжность и долговечность такого термостата (при правильном выполнении) существенно выше. С появлением дешёвых термочувствительных элементов, таких, например, как терморезисторы, появилась возможность ещё больше повысить надёжность термостатов. При изменении температуры меняется сопротивление терморезистора. Этим изменением величины сопротивления можно заставить включать и выключать обогревательный элемент. Например, так: 135
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 11.5. Применение терморезистора в термостате В роли терморезистора выступает R4. Под влиянием температуры резистор меняет величину сопротивления, напряжение на инверсном входе операционного усилителя поддерживается постоянным, благодаря использованию стабилитрона Z1, в итоге операционный усилитель меняет состояние, управляя транзистором T1. Но так это выглядит при моделировании. В этом случае я изменяю величину сопротивления R4, чтобы показать конечный результат. А как меняется сопротивление терморезистора под воздействием температуры в реальной жизни? В справочнике можно найти такую величину: 2-8% от номинального значения при изменении температуры на один градус. Взяв в качестве примера один из терморезисторов, я готов привести значение коэффициента температурной чувствительности – его значение 4300. Этот коэффициент мне пока ни о чём не говорит. Но вот формула, определяющая этот коэффициент (взята из Wiki): Рис. 11.6. Формула определения коэффициента температурной чувствительности Взяв начальную температуру T1 = 250C в единицах по шкале Кельвина (добавим 273 градуса), а конечную температуру T2 = 260C (тоже в единицах по шкале Кельвина), можно рассчитать (в процентах), насколько изменится терморезистор с номинальным сопротивлением (при 25 градусах Цельсия) 10 кОм. Как показывает подсчёт, сопротивление измениться приблизительно на 5%. Фактически, мы измеряем падение напряжения на терморезисторе. Изменение на 5% при изменении температуры на один градус вполне соизмеримо с колебаниями опорного напряжения при неблагоприятных условиях. Хотелось бы получить более стабильный результат. Мы можем изменить схему включения терморезистора. Например, так: 136
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 11.7. Мостовая схема включения терморезистора На рисунке слева добавочное сопротивление R5 равно нулю (терморезистор R4 при температуре 25 градусов), на рисунке справа R5 = 50 Ом (терморезистор изменил сопротивление на 0.5%). В реальном устройстве лучше использовать не операционный усилитель, хотя и он справляется с работой, а специализированную микросхему, компаратор. Схема термостата теперь выглядит гораздо сложнее той самой первой. Но надёжность устройства при правильной разработке схемы может быть выше. Так реле, даже очень хорошее, может переключаться несколько сотен тысяч раз до первого отказа. А транзистор, работающий в режиме переключений на частоте 1 МГц, переключается миллион раз в секунду, работая в этом случае годами. Однако не бывает просто термостатов. Бывают конкретные термостаты. А конкретный термостат характеризуется конкретными параметрами, которые должны быть выдержаны. Приведённая выше схема вполне подойдёт для небольшого объёма и поддержания температуры с точностью 1-3 градуса. Современная элементная база А как быть с задачей, которая выглядит, примерно, так: поддерживать температуру в заданном диапазоне с точностью до одной десятой градуса? Можно использовать современную элементную базу, например, датчик температуры DS1624. Вот его характеристика: Рис. 11.8. График зависимости ошибки измерения от температуры 137
В.Н. Гололобов Микроконтроллер глазами начинающего Градация считывания температуры 0.03 градуса. Таким датчиком можно, по крайней мере, считывать температуру достаточно точно. Ошибка в широком диапазоне температур составляет 0.5%. Если приурочить эту точность к температуре 380С, то это составит 0.2 градуса. Но, похоже, что в интересующем нас диапазоне температур точность будет выше. Выход этого датчика цифровой. Что подразумевает его совместное использование с микроконтроллером. Интерфейс обмена данными I2C. Позже мы вернёмся к этому интерфейсу и к этому датчику. А сейчас воспользуемся другим датчиком температуры, DS18B20. Разрешение этого датчика меньше, но в данный момент он интересует меня больше. Этот датчик использует другой, но не менее интересный интерфейс для передачи данных. Называется интерфейс 1-wire. Датчик с микроконтроллером (или компьютером, что тоже возможно, если использовать дополнительные микросхемы-конверторы) можно соединить двумя проводами. К шине 1-wire можно подключать несколько датчиков и провода могут иметь длину до 300 метров. Каждый датчик имеет уникальный идентификатор, присваиваемый ему при изготовлении. Особенность интерфейса, отчего он называется однопроводным, в использовании одного провода и для обмена данными, и для подачи питающего напряжения на датчик. Идея такого соединения датчика и контроллера близка к тому, как работали раньше обычные телефонные аппараты – оба абонента соединялись по двум проводам, оба аппарата получали питающее напряжение по этим проводам, оба абонента говорили, формируя сигналы на этих проводах. Конечно, разговаривая по телефону, мы не заботимся о порядке беседы – не всегда выслушав собеседника, мы перебиваем его, он перебивает нас, и мы говорим, зачастую, вместе. Цифровая техника не терпит такой невежливости, поэтому программное обеспечение 1-wire построено таким образом, что на линии есть одно ведущее (или главное) устройство и несколько ведомых (или подчинённых) устройств. Беседой управляет ведущее устройство. Когда ведущее устройство «задаёт вопрос», подчинённое устройство отвечает, а ведущее устройство слушает ответ. В первую очередь нас интересуют сигналы интерфейса. Ведь мы умеем управлять выходом микроконтроллера, умеем принимать цифровой сигнал на входе. Можем ли мы, используя эти знания, формировать ответ ведомого устройства? Ответ можно поискать в книгах, журналах. Однако трудами многих людей давно создаётся Интернет-энциклопедия. Интернет сегодня доступен многим, если не дома, то в Интернет-кафе или других учреждениях, предоставляющих доступ в Интернет. Информацию о 1-wire можно найти и на многих радиолюбительских сайтах. Что можно сказать о сигналах на шине 1-wire? Я разбил бы их на две категории – сигналы инициализации обмена и сигналы обмена данными, включающие команды ведущего устройства. Сигналы инициализации – это сигналы, формируемые ведущим устройством, чтобы начать процесс работы с ведомым устройством; и это сигналы ведомых устройств, если их несколько, или ведомого устройства, если оно одно. Нас пока интересует последний вариант. Я напомню, что на этом этапе разработки мы собираемся создать имитатор датчика температуры. 138
В.Н. Гололобов Микроконтроллер глазами начинающего О необходимых сигналах ведущего устройства и отклике ведомого достаточно хорошо рассказано в описании датчика температуры DS18B20. Я приведу (переведу) выдержки из этого рассказа, поскольку, начиная любую разработку, в первую очередь следует обратиться к документации на все компоненты, которые вы предполагаете использовать. Для имитатора датчика температуры я намерен использовать микроконтроллер PIC16F628A; документацию (datasheet) можно найти на русском языке (одна из причин, по которой я почти всегда рассказываю об этой модели). Посмотрим, что написано в описании DS18B20 по поводу начала обмена между устройствами: ИНИЦИАЛИЗАЦИЯ Все транзакции на шине 1-wire начинаются с инициализационной последовательности. Эта последовательность состоит из импульса сброса, отправляемого ведущим, и ответного импульса от ведомого устройства. Ответный импульс даёт знать ведущему, что ведомое устройство (такое, как DS18B20) подключено к шине и готово к проведению операций. Временные характеристики этой последовательности следующие. Рис. 11.9. Временные параметры сигналов инициализации Таким образом, мы должны записать для себя, что: 1. Нашему имитатору следует «слушать» шину обмена. 2. Когда сигнал на шине 1-wire принимает значение логического нуля, дождаться завершения этого сигнала. 3. После завершения сигнала «Сброс» выдержать паузу длительностью 15-60 мкс, а затем установить на шине уровень логического нуля на время 60-240 мкс. Каждый сочинитель, сказочник он или реалист, придумывает своих героев, придумывает события, якобы с ними происходящие. В этом сочинители похожи на иллюзионистов, за чьей работой мы наблюдаем, открыв от удивления рот. Но и те, и другие в своём ремесле немного обманщики. Вот и я хочу немного сжульничать, чтобы показать в виртуальном пространстве, как выглядят сигналы инициализации, используя те средства, которые мне удобны. 139
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 11.10. Сигналы инициализации на экране виртуального осциллографа Первый сигнал, как мы говорили раньше, формирует ведущее устройство. В данном случае длительность этого сигнала около 500 мкс. Далее следует пауза в 30 мкс. А затем ответ датчика температуры длительностью 120 мкс. Как вы видите, эти сигналы соответствуют рекомендациям производителя датчика температуры. После процедуры «обмена любезностями» устройства могут приступить к работе. Данные, которыми обмениваются устройства, байтовые. Есть разные протоколы обмена данными в последовательной передаче-приёме: RS232, RS485, I2C, SPI, USB. У каждого из этих протоколов своя программная часть, к некоторым мы позже вернёмся, но объединяет их именно последовательная передача информации. До того, как мы начнём создание имитатора датчика, мы вернёмся к последовательности операций, производимых при работе с DS18B20. Как выше было написано, каждый датчик температуры имеет свой уникальный адрес, устанавливаемый при изготовлении. По этому адресу, хотя реально процесс несколько сложнее, ведущее устройство обращается к ведомым. ПОСЛЕДОВАТЕЛЬНОСТЬ ОПЕРАЦИЙ Последовательность операций для доступа к DS18S20 следующая: 1. Инициализация (мы её рассмотрели). 2. ROM команды (с последующим требуемым обменом данными). 3. Функциональные команды DS18S20 (с последующим требуемым обменом данными). Очень важно следовать этой последовательности каждый раз при обращении к DS18S20, поскольку датчик не будет отвечать, если какой-то из шагов пропущен или сделан не в той последовательности. Исключением из этого правила являются команды Search ROM [F0h] и Alarm Search [ECh]. После выполнения любой из этих 140
В.Н. Гололобов Микроконтроллер глазами начинающего команд ведущее устройство последовательности. должно вернуться к первому шагу в Я не буду перечислять все команды, они есть в документации, но мы рассмотрим те команды, которые нужны ведущему устройству при начале диалога и рассмотрим ответ датчика DS18S20. Без этого рассмотрения мы не сможем создать имитатор датчика. Итак, после инициализации ведущий может отправить команду SKIP ROM [CCh]. SKIP ROM [CCh] Ведущее устройство может использовать эту команду для адресации ко всем устройствам на шине одновременно без отправки какой-нибудь информации ROM кода. Например, ведущий может заставить все датчики на шине выполнить одновременное преобразование температуры, выполнив команду Skip ROM с последующей командой Convert T [44h]. Однако заметьте, что команда Skip ROM может предшествовать команде Read Scratchpad [BEh], если на шине есть только одно ведомое устройство. Эта последовательность сохраняет время, позволяя ведущему читать данные из ведомого устройства без отправки 64-битового ROM кода. Такая последовательность может стать причиной коллизий данных на шине, если есть более одного ведомого устройства, и все одновременно будут пытаться передать данные. Пока мы планируем использование только одного устройства, поэтому следующей командой ведущего будет Read Scratchpad [BEh]. READ SCRATCHPAD [BEh] Эта команда позволяет ведущему устройству прочитать содержание scratchpad. Передача данных начинается с последнего значащего бита или байта 0 и продолжает передавать scratchpad, пока не будет прочитан 9й байт (байт 8 – CRC). Ведущий может выполнить сброс, чтобы остановить чтение в любое время, если нужна только часть данных scratchpad. Осталось выяснить, что такое scratchpad? Рис. 11.11. Описание scratchpad 141
В.Н. Гололобов Микроконтроллер глазами начинающего Как видно из рисунка – это область памяти из 9 байт, содержащая информацию о работе датчика. Два байта, второй и третий, поддерживают доступ к регистрам TH и TL, где содержится информация о текущей температуре. Давайте посмотрим, как выглядят сигналы, если мы повторим описанные выше команды. Рис. 11.12. Осциллограмма сигналов при отправке описанных команд Хотя сигналы – это привычная форма информации для всех, кто занимается электроникой, понять, что изображено на осциллограмме без детального анализа трудно. Попробуем разобраться в увиденном, благо мы используем не просто обычный осциллограф, где изображение, мелькнув, исчезает, а вполне удобный для анализа виртуальный осциллограф программы ISIS (Proteus). Рис. 11.13. Начальная часть диаграммы (reset, presence) Теперь попробуем выделить две команды ведущего: CCh и BEh (11001100 и 10111110). 142
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 11.14. Команда ведущего устройства CCh Рис. 11.15. Команда ведущего устройства BEh Есть, по меньшей мере, два варианта реализации имитатора, которые мы рассмотрим в следующих главах. 143
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 12. Разрабатываем имитатор DS18B20 У микроконтроллера PIC16F628A есть возможность использовать вывод RA4 для подключения к шине 1-wire. При этом, такая возможность тоже есть, необходимо будет программно, используя описание протокола, организовать его работу. Это интересная задача, но сейчас мы попробуем упростить её. Фирма Atmel предлагает использовать модуль USART для работы с шиной 1-wire. Это проще, чем писать программу в полном объёме. Тем более, что нам имитатор DS18B20 в данный момент нужен, скорее, как учебная задача. Как следует включить выводы контроллера? Рис. 12.1. Соединение выводов контроллера с шиной 1-wire Два транзистора и несколько резисторов – это небольшая цена за возможность упростить программу. Этот вариант реализации обмена, как мне кажется, подходит даже для реальной конструкции. Начнем мы, как бы ни хотелось сделать всё побыстрее, с выяснения того, что за выводы TXD и RXD, и как с ними работать. Вот что можно найти в описании PIC16F628A (совсем кратко) для USART. USART – это модуль последовательного ввода/вывода, который может работать в полнодуплексном асинхронном режиме для связи с терминалами, персональными компьютерами или синхронном полудуплексном режиме для связи с микросхемами ЦАП, АЦП, последовательными EEPROM и т.д. USART может работать в трех режимах:    асинхронный, полный дуплекс; ведущий синхронный, полудуплекс; ведомый синхронный, полудуплекс. Биты SPEN (RCSTA<7>) и TRISB<2:1> должны быть установлены в ‘1’ для использования выводов RB2/TX/CK и RB1/RX/DT в качестве портов универсального синхронноасинхронного приемопередатчика. Пока нас интересует асинхронный режим, но для реализации других протоколов, возможно, заинтересует и синхронный режим. А выводы TXD и RXD – это выводы передатчика и приемника соответственно. Однако до начала работы встроенного модуля USART необходимо произвести несколько настроек. Об одной из настроек написано выше, но это не всё. Есть регистр TXSTA 144
В.Н. Гололобов Микроконтроллер глазами начинающего управления передатчиком. Есть регистр RXSTA управления приёмником. О назначении битов этих регистров можно прочитать в описании PIC16F628A, я же возьму необходимые с моей точки зрения настройки, когда дойдёт дело до кодов программы. Кроме регистров управления есть регистры буфера приёма и передачи данных – это RCREG и TXREG. Но до использования этих регистров следует настроить скорость обмена данными, определяемую генератором BRG. BRG используется для работы USART в синхронном ведущем и асинхронном режимах. BRG представляет собой отдельный 8-разрядный генератор скорости обмена в бодах, период которого определяется значением в регистре SPBRG. В асинхронном режиме бит BRGH (TXSTA<2>) тоже влияет на скорость обмена (в синхронном режиме бит BRGH игнорируется). Значения, подходящие для использования при работе внутреннего тактового генератора, можно взять из таблиц, представленных в документации. При установке бита BRGH в единицу, регистр TXSTA, для скорости 19.2 Кбит/сек десятичное значение регистра SPBRG должно быть 12, а для скорости 9.6 Кбит/сек 25. Я советую сейчас, не откладывая в долгий ящик, прочитать этот раздел документации. Вместе с тем, я встречал ошибки и в документации. Поэтому, пока мы не зашли в своих изысканиях слишком далеко, давайте проверим работу USART. Я использую простую программу (компилятор xc8) для проверки передачи латинского символа «A»: #include "pic.h" #include <xc.h> #define _XTAL_FREQ 4000000 __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); & PWRTE_OFF & MCLRE_OFF & BOREN_OFF & unsigned char snd = ‘A’; int main(void) { TRISB = 0xFF; STATUS = 0x20; TXSTA = 0xA4; SPBRG = 12; INTCON = 0; SPEN = 1; STATUS = 0; do { while (!TXIF) continue; TXREG = snd; __delay_ms(100); } while (1); return 0; } // // // // // // // установка порта В на ввод переход ко 2 банку памяти настройка USART задание скорости обмена запрет прерываний включение USART переход к 1 банку памяти // ожидание освобождения буфера передачи // отправка символа в буфер передачи Кроме уже упомянутых битов и регистров используется бит TXIF регистра PIR1. Бит устанавливается, когда регистр передачи пуст. Регистры микроконтроллера находятся в разных 145
В.Н. Гололобов Микроконтроллер глазами начинающего банках памяти. Одни компиляторы не требуют явного выбора банка памяти, другие могут этого потребовать. Рис. 12.2. Отправка символа «А» на скорости 19.2 Кбит/сек И такую же простую программу для проверки приёма: #include "pic.h" #include <xc.h> #define _XTAL_FREQ 4000000 __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); & PWRTE_OFF & MCLRE_OFF & BOREN_OFF unsigned char snd = 0; int main(void) { TRISB = 0xFF; // установка порта В на ввод STATUS = 0x20; // переход ко 2 банку памяти TXSTA = 0xA4; // настройка USART SPBRG = 12; // задание скорости обмена INTCON = 0; // запрет прерываний SPEN = 1; // включение USART CREN = 1; // разрешение приёма STATUS = 0; // переход к 1 банку памяти do { while(!RCIF) continue; // ожидание заполнения буфера приёма snd = RCREG; // чтение из буфера приёма while (!TXIF) continue; // ожидание освобождения буфера передачи TXREG = snd; // отправка символа в буфер передачи __delay_ms(100); 146 &
В.Н. Гололобов Микроконтроллер глазами начинающего } while (1); return 0; } Рис. 12.3. Приём и отправка принятого символа В программе добавлено разрешение приёма (CREN = 1;), ожидание установки флага RCIF, когда буфер приёма заполнен (принят отправленный «собеседником» байт), и присваивания этого символа переменной snd, которая затем отправляется в линию связи. Отправляемые символы через терминал и принятые затем контроллером отображаются на экране терминала. Не знаю, будет ли работать интерфейс 1-wire на скорости 19.2 кБод, но у нас есть возможность изменить эту скорость. Следующий шаг – обмен приветствиями между ведущим микроконтроллером и имитатором датчика температуры. Для этого ведущий отправит сигнал reset, а имитатор ответит отправкой байта 0xF0. Посмотрим, что из этого получится – мы можем сравнить осциллограммы, можем обратиться к диаграммам описания протокола 1-wire. 147
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 12.4. Обмен приветствиями имитатора и ведущего Сравнивая с диаграммой из предыдущей главы, мы можем сказать, что импульсы очень похожи. Программа в этот момент выглядит так: #include "pic.h" #include <xc.h> #define _XTAL_FREQ 4000000 __CONFIG(FOSC_INTOSCIO & WDTE_OFF LVP_OFF & CPD_OFF & CP_OFF); & PWRTE_OFF & MCLRE_OFF & BOREN_OFF unsigned char res = 0; int main(void) { TRISB = 0xFF; STATUS = 0x20; TXSTA = 0xA4; SPBRG = 12; INTCON = 0; SPEN = 1; CREN = 1; STATUS = 0; do { while(!RCIF) continue; res = RCREG; if (res == 0x00) { while (!TXIF) continue; TXREG = 0xF0; } __delay_ms(1); } while (1); return 0; } // Инициализация // Основной цикл // Ждём сигнала сброс // Если полученный сигнал сброс, то // посылаем presence (младшие биты уходят первыми) 148 &
В.Н. Гололобов Микроконтроллер глазами начинающего Но приём сигнала опроса и ответ на него ещё не гарантируют, что команды будут приниматься правильно. Давайте, с этого и следовало начинать разговор о данных, посмотрим, как формируются данные на шине 1-wire: Рис. 12.5. Формирование ноля и единицы в протоколе 1-wire Длительность импульса каждого переданного бита порядка 120 мкс. Ноль формируется длительным низким уровнем, а единица коротким. Такое формирование импульсов напоминает мне некоторые стандарты, используемые в ИК управляющих пультах. Но для формирования нужных импульсов нам потребуется (при использовании USART) скорость обмена, которая определится из того, что за 120 мкс нужно передать байт, то есть, время, определяющее скорость обмена, около 12 мкс на бит. А это приблизительно 80-90 Кбит/сек. Сразу можно сказать, что не получится использовать встроенный тактовый генератор имитатора, потребуется внешний кварц. Что ж, в нашей виртуальной лаборатории есть возможность быстро решить эту проблему: перенастроим регистры USART, зададим новую конфигурацию МК, выберем частоту тактового генератора 20 МГц. И попробуем отправить в буфер передачи USART байт 0x00: Рис. 12.6. Байт «0» при скорости 115.2 кБод 149
В.Н. Гололобов Микроконтроллер глазами начинающего Этот сигнал вполне вписывается в заданные протоколом временные параметры в качестве нуля. Вот программа отправки этого байта (компилятор xc8): #include "pic.h" #include <xc.h> // PIC16F628A Configuration Bit Settings __CONFIG(FOSC_HS & WDTE_OFF & PWRTE_OFF & MCLRE_OFF & BOREN_OFF & LVP_OFF & CPD_OFF & CP_OFF); #define _XTAL_FREQ 20000000 unsigned char snd = 0x00; int main(void) { TRISB = 0xFF; // Инициализация STATUS = 0x20; TXSTA = 0xA4; SPBRG = 10; INTCON = 0; SPEN = 1; STATUS = 0; do { while (!TXIF) continue; // Отправка символа TXREG = snd; __delay_ms(100); } while (1); return 0; } Повторим эту программу, отправив байт 0xFE (последний бит ноль, поскольку он будет первым при передаче): Рис. 12.7. Отправка единицы протокола 1-wire 150
В.Н. Гололобов Микроконтроллер глазами начинающего Импульс единицы (с нулевым уровнем) немного длинноват, но у нас ещё есть запас, который можно использовать при неудаче. Начнём с того, что попробуем с этими настройками USART прочитать байт команды, отправленной ведущим. Но не всё так просто. Ведущий отправит восемь байт для одной команды, скажем, BEh. И мы должны принять через USART восемь байт, каждый из которых будет нулем или единицей протокола 1-wire. Затем нам следует эти восемь байт собрать в один байт, который и определит эту команду. Здесь возможно несколько решений. Но в любом случае мы используем то, что процессор любого микроконтроллера поддерживает обычные арифметические, логические и битовые операции. В чём суть того, что я намерен сделать? В первом эксперименте мы примем восемь байт команды 0xBE, затем отправим их назад, чтобы убедиться (вернее, чтобы увидеть) в правильности принятой команды. Для приёма команды мы используем такую программную конструкцию как одномерный массив. Что это такое? В сущности, одномерный массив – это заданное количество последовательных ячеек памяти, в которых хранятся данные, доступ к которым осуществляется по имени массива и индексу. В языке Си принято начинать индекс с нуля. Таким образом, для восьми байт индексы будут от 0 до 7. Назовём массив cmd. Задаётся такой массив записью вида: unsigned char cmd[8]; А для доступа к любому из байтов массива используется индекс, например, cmd[5] – шестой элемент массива. Вот, как выглядит программа чтения команды ведущего: #include "pic.h" #include <xc.h> // PIC16F628A Configuration Bit Settings __CONFIG(FOSC_HS & WDTE_OFF & PWRTE_OFF & MCLRE_OFF & BOREN_OFF & LVP_OFF & CPD_OFF & CP_OFF); #define _XTAL_FREQ 20000000 unsigned char cmd[8]; unsigned char i = 0; int main(void) { TRISB = 0xFF; // Инициализация STATUS = 0x20; TXSTA = 0xA4; SPBRG = 10; INTCON = 0; SPEN = 1; CREN = 1; STATUS = 0; do { for(i=0;i<=7;i++){ // Принимаем восемь байт команды while(!RCIF)continue; cmd[i] = RCREG; 151
В.Н. Гололобов Микроконтроллер глазами начинающего }; CREN = 0; // Запрещаем приём данных for(i=0;i<=7;i++){ // Передаём принятые байты while (!TXIF)continue; TXREG = cmd[i]; }; CREN = 1; // Разрешаем приём данных __delay_ms(1); } while (1); return 0; } Обратите внимание, что после приёма команды мы выключаем приёмник перед передачей данных – это обусловлено тем, что приём и передача осуществляются по одной и той же линии, а мы не хотим принимать то, что передаём. Бит CREN регистра RCSTA управляет включением и выключением приёмника. Вы можете проверить, что без выключения приёмника программа может «подвисать» после приёма команды, а передача принятых восьми байтов не проходит. На осциллограмме ниже (и экране виртуального терминала) обратите внимание, что первым появляется младший бит команды, то есть, команда 10111110 принимается как 01111101: Рис. 12.8. Приём и передача команды 0xBEh На осциллограмме видны сигналы в линии 1-wire (верхняя осциллограмма) и сигналы на выходе микроконтроллера (нижняя осциллограмма). Убедившись, что команду мы принимаем, похоже, правильно, проведём следующий эксперимент. Мы преобразуем полученные восемь байт в один байт команды, который отправим следом за передачей восьми принятых байт. Цель этого эксперимента определить, сколько времени занимает операция преобразования. Если промежуток времени небольшой, то преобразование можно осуществлять между приёмом двух команд, если промежуток велик, а это 152
В.Н. Гололобов Микроконтроллер глазами начинающего может привести к ошибкам приёма, мы будем принимать две последовательные команды в два массива данных, а преобразование осуществлять после этого приёма. Что можно сказать о преобразовании восьми байт в один? Во-первых, зачем это нужно? Позже мы определим, какая это команда. Можно команду определить, сравнивая массив принятых данных с эталоном, но проще сравнить два байта. Во-вторых, как мы будем это делать? Создадим дополнительную переменную cmd_b типа char, байт, которую будем формировать. И переменную msk того же типа, присвоив ей значение 0x80. Если первый принятый байт единица (то есть, 0xFF, 0xFE не срабатывает, но об этом позже), то выполним операцию ИЛИ переменной cmd_b и переменной msk, затем сдвинем биты переменной msk вправо на одну позицию, если нет, то сдвинем без логического сложения, которое выглядит так: cmd_b |= msk; Сдвиг битов на одну позицию выглядит так: msk >>= 1; Я не буду приводить всю программу ниже, а приведу только то, что добавлю после отправки принятых от ведущего байтов и до разрешения приёма в предыдущую программу: unsigned char cmd_b = 0; unsigned char msk = 0x80; for(i=0;i<8;i++){ if (cmd[i] == 0xFF) cmd_b |= msk; msk >>= 1; }; while (!TXIF)continue; TXREG = cmd_b; cmd_b = 0; msk = 0x80; Посмотрим, что у нас получилось (или не получилось): 153
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 12.9. Передача полученного после форматирования байта Байт 0x7D – это в обратном порядке 0xBE. И, пока не забыл, в программе ISIS (Proteus) у виртуального терминала максимальная скорость из списка 57600, а скорость 115200, похоже, следует устанавливать каждый раз, когда запускаешь программу, иначе скорость установится в значение по умолчанию. Итак, попробуем принять две команды от ведущего, после каждой переформатируя их в байты. Какие это будут команды? Две команды CCh и BEh. Первая команда в двоичном виде: 11001100 . Мы должны увидеть её в обратном порядке, то есть: 00, 00, FF, FF, 00, 00, FF, FF. Для приёма команд используем два массива: cmd1 и cmd2. После приёма команд сравним их с эталонами, а при совпадении команд, выведем (пока) число AAh. Вот во что превратилась некогда лаконичная и простая программа: #include "pic.h" #include <xc.h> // PIC16F628A Configuration Bit Settings __CONFIG(FOSC_HS & WDTE_OFF & PWRTE_OFF & MCLRE_OFF & BOREN_OFF & LVP_OFF & CPD_OFF & CP_OFF); #define _XTAL_FREQ 20000000 unsigned unsigned unsigned unsigned unsigned unsigned char char char char char char cmd1[8]; // Массив для первой команды cmd2[8]; // Массив для второй команды i = 0; cmd_b1 = 0; // Переменная для первой команды cmd_b2 = 0; // Переменная для второй команды msk = 0x80; int main(void) { TRISB = 0xFF; STATUS = 0x20; // Инициализация 154
В.Н. Гололобов Микроконтроллер глазами начинающего TXSTA = 0xA4; SPBRG = 10; INTCON = 0; SPEN = 1; CREN = 1; STATUS = 0; do { CREN = 1; for(i=0;i<8;i++){ // Приём первой команды while(!RCIF)continue; cmd1[i] = RCREG; }; for(i=0;i<8;i++){ // Приём второй команды while(!RCIF)continue; cmd2[i] = RCREG; }; CREN = 0; for(i=0;i<8;i++){ // Форматирование первой команды if (cmd1[i] == 0xFF) cmd_b1 |= msk; msk >>= 1; }; msk = 0x80; for(i=0;i<8;i++){ // Форматирование второй команды if (cmd2[i] == 0xFF) cmd_b2 |= msk; msk >>= 1; }; if ((cmd_b1 = 0x7D)&(cmd_b2 = 0x33)){ // Проверка принятых команд while (!TXIF)continue; TXREG = 0xAA; }; cmd_b1 = 0; cmd_b2 = 0; msk = 0x80; __delay_ms(10); } while (1); return 0; } И вот как это выглядит в программе ISIS: 155
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 12.10. Проверка работы программы, принимающей две команды Если мы хотим завершить создание имитатора, то следует ввести в программу работы имитатора «обмен приветствиями» и завершить всё выводом данных. Однако предварительно не лишне будет проверить, а сработает ли этот механизм – «приветствия» плюс две команды – в среде ISIS (Proteus). Пока мы не перешли к макетированию ISIS единственная возможность получить какието результаты. Пробуем: Рис. 12.11. Предварительная проверка в среде Proteus 156
В.Н. Гололобов Микроконтроллер глазами начинающего Вот почему я предпочитаю всё предварительно проверить за компьютером: есть обмен «приветствиями», есть обе команды, отправленные ведущим устройством, а дальше… дальше ничего. Причин в данный момент для этого я вижу несколько: 1. Я мог неверно истолковать то, что прочитал в описании датчика температуры. 2. Документация у меня на датчик ds1820-ds18s20, а я использовал в Proteus датчик DS18B20. Отличий может и не быть, но могут и быть нюансы, хотя и не принципиального характера. 3. Модель датчика в программе ISIS может требовать «именного» обращения. 4. Программа ISIS «глючит». В подобных случаях я предпочитаю обратиться к макетной плате с реальным устройством. Но это позже, а сейчас я хочу рассказать о том, где и в чём я «сжульничал», а, главное, почему? Вот, как выглядит в программе ISIS, то, чем я пользовался на протяжении рассказа о 1-wire: Рис. 12.12. Работа с датчиком DS18B20 в программе ISIS И «сжульничал» я в том, что программу для ведущего устройства, микроконтроллера PIC16F628A, написал в MicroC Pro. Эта среда разработки имеет хорошую библиотеку для работы с разными внешними устройствами. Этой библиотекой я и воспользовался. Свободная версия программы имеет ограничение на размер используемой памяти в 2 Кбайта, но прекрасно подходит для учебных целей, хотя можно и с таким ограничением создавать много полезных устройств. Написание даже простой программы без использования удобных библиотек занятие достаточно хлопотное. Поэтому следует ценить всё то, что сделано для вас другими. Я оценил! Вернёмся к последней версии той программы, что мы успели написать. Очень полезным упражнением было бы создание двух функций: мы дважды читали команды, мы дважды 157
В.Н. Гололобов Микроконтроллер глазами начинающего переформатировали полученный результат. Даже в том случае, если всё вам показалось скучным и неинтересным, постарайтесь написать эти две функции и проверить их работу. Лекарство может быть неприятным на вкус, но полезным для восстановления здоровья. Резюме Зачем нужна была эта глава? Мне хотелось познакомить вас с модулем USART, что мы и сделали. В дальнейшем вы можете обратиться к руководству для вашей модели микроконтроллера и пополнить свои знания, чтобы использовать модуль всякий раз, как в нём есть необходимость. А подобная необходимость появляется при необходимости связать либо микроконтроллер с компьютером, либо два микроконтроллера, либо микроконтроллер, например, с датчиком. Это вопервых. Во-вторых, мне хотелось показать, что микроконтроллер может прекрасно справиться с ролью генератора сложных сигналов, генератора, который не только воспроизводит периодическую последовательность импульсов, но может принимать данные и отвечать сложными сигналами. Наконец, мы ближе познакомились с очень полезным интерфейсом 1-wire и его протоколом. Впоследствии вы можете больше прочитать о нём, когда возникнет необходимость. И совсем уж, наконец, пролистав назад несколько глав, вы можете убедиться, что мы мало узнали и о языке Си, и о микроконтроллере. Но можем не только создавать полезные устройства, но и связывать микроконтроллеры между собой, используя модуль USART, а зачем это нужно, мы узнаем из следующих глав. 158
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 13. Продолжаем рассмотрение термостата Мы познакомились с тем, как можно использовать подключение датчика температуры к микроконтроллеру. Таким образом, микроконтроллер может получать данные о текущей температуре. Но как управлять температурой? Самый простой способ подогреть рабочее пространство термостата – это включить обогреватель. Свойства обогревателя, его мощность, его конструкция, зависят от назначения термостата. Не будем увлекаться большими проектами, а выберем небольшой объём, о поддержании заданной температуры в котором и продолжим рассказ. Даже небольшой объём можно обогреть, включив, скажем нагревательный элемент. Для включения этого нагревателя нам достаточно выставить уровень логической единицы (или снять его при другом варианте) на одном из выводов порта микроконтроллера. Как это сделать, мы знаем. Если не требуется высокая точность поддерживаемой температуры, то в качестве «гистерезиса» мы можем использовать время. То есть, включим обогреватель, скажем, на несколько секунд, а потом будем измерять температуру, дожидаясь, когда она понизится до заданного уровня. Так можно поддерживать комфортную температуру в комнате. Время работы обогревателя, если вы, как и я, не владеете теплотехническими расчётами, можно определить опытным путём. Но представим, что мы хотим поддерживать температуру довольно точно. Слово довольно, согласен, это, скорее, шаблон-заглушка. Оно существует до тех пор, пока мы не определимся, с какой именно точностью, выраженной числом, следует поддерживать температуру. Позже мы, видимо, так и сделаем. А пока я хочу рассказать об эксперименте, который может дать хотя бы первое представление о пути поиска решения. Итак. Объём небольшой. Точность пока неизвестна. Однако конструкция, которую я хотел бы проверить выглядит так: к вентилятору, работающему на постоянном токе, перед его лопастями расположены один или два резистора большой допустимой мощности. Вентилятор и эти резисторы подключены к микроконтроллеру (через транзистор), который на одном из выводов генерирует импульсы. Скважность импульсов мы можем менять. В итоге должна меняться и скорость вращения вентилятора (скорость воздушного потока), и степень нагрева резисторов. Вот с такой конструкцией я предлагаю провести ряд экспериментов. Конечно, вначале за компьютером, потом на макетной плате. Позже мы объединим два проекта – чтение температуры и создание подогрева. Идея, импульсы разной скважности как питание для подогревателя и вентилятора, думаю, вам понятна. Но посмотрим на это ещё раз: 159
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 13.1. Импульсы с разной скважностью Среднее напряжение, средний ток за период в обоих случаях будут разными. Следовательно, разными будут и нагрев резисторов, и скорость (надеюсь) вращения вентилятора. Микроконтроллер будет управлять скважностью импульсов, а величину напряжения и, соответственно, тока мы можем регулировать, подключив вентилятор и обогреватель к другому источнику питания. Управление вентилятором и нагревателем можно разделить, но вначале посмотрим, как эта пара будет себя вести при параллельном включении. Если с нагревателем мне всё пока ясно (исключая, может быть, мощность резистора), то вентилятор требует прояснения нескольких моментов: во-первых, мне нужно проверить при каких напряжениях питания вентилятор вращается; во-вторых, следует проверить, при какой частоте импульсов вентилятор работает нормально. С этой проверки можно и начать. Я не ожидал, признаться, найти в закромах программы ISIS подходящих для проверки элементов, но был приятно удивлён, когда обнаружил вентилятор. При значении параметров, заданных по умолчанию, его показатели близки к тому, что можно прочитать о кулере в справке: питающее напряжение 12 В, потребляемый ток 70 мА, количество оборотов около 4000: Рис. 13.2. Вентилятор в программе ISIS Изменяя напряжение батарейки с 12 В до 2 В, я вижу, что скорость вращения уменьшается до 600700 оборотов в минуту. Что же, проверим, как будет работать вентилятор при импульсном питании. 160
В.Н. Гололобов Микроконтроллер глазами начинающего Частоту импульсов (меандр) я выбираю равной 2 кГц. Почему так? В прошлых экспериментах с двигателем постоянного тока я обнаружил, что он может работать при импульсном питании, но при очень низкой частоте импульсов. А как заставить PWM микроконтроллера работать на этих частотах, я не нашёл. То есть, конечно, используя дополнительные меры (или компоненты) я добился бы нужного результата, но как раз этого делать было нельзя. Поэтому вопрос частоты импульсов для меня не менее важен (пока, в данный момент), чем остальные вопросы. Итак, если верить программе ISIS, то вентилятор справляется с работой на этой частоте: Рис. 13.3. Вентилятор с импульсным питающим напряжением То, что меня интересует, это поведение вентилятора при разных значениях скважности импульсов. Например, при длительности импульса в 75% (от периода) и 25%: Рис. 13.4. Вентилятор при изменении скважности импульсов Частота вращения вентилятора меняется от 2500 об/мин в первом случае до 800 об/мин во втором случае. Мощность, рассеиваемая на резисторе, меняется от 4 Вт до 0.5 Вт. Для генерации импульсов удобно использовать модуль PWM, который есть в ряде моделей микроконтроллеров. В противном случае можно использовать любой из выводов порта на выход, а сигнал генерировать «вручную». 161
В.Н. Гололобов Микроконтроллер глазами начинающего К сожалению, использование библиотечной функции MicroC Pro не привело к успеху. Так бывает. Но можно, и это не сложно, написать несколько строк, задающих нужные параметры регистров микроконтроллера. Какие регистры должны принимать участие в процессе, можно выяснить из описания микроконтроллера. Вот последовательность операций из datasheet для PIC16F628A: Последовательность настройки модуля в режиме PWM:      Установить период PWM в регистре PR2. Установить длительность импульса в регистрах CCPR1L и CCP1CON <5:4>. Настроить вывод CCP1 как выход, сбросив бит TRISB<2>. Настроить предделитель и включить TMR2 в регистре T2CON. Включить CCP1 в режиме PWM. Режим PWM – это режим широтно-импульсной модуляции, когда частота повторения импульсов остаётся неизменной, а скважность импульсов меняется. Подробнее о работе этого модуля (он выполняет не только ШИМ) можно прочитать в описании микроконтроллера. Проверим, будет ли работать такая программа в ISIS: #include "pic.h" #include <xc.h> // PIC16F628A Configuration Bit Settings __CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_OFF LVP_OFF & CPD_OFF & CP_OFF); unsigned char duty = 192; int main(void) { TRISB = 0xF7; PR2 = 0xFF; CCPR1L = duty; CCP1CON = 0x3C; T2CON = 0x7B; TMR2ON = 1; return 0; } & MCLRE_OFF // Скважность импульсов // // // // Вывод RB3 на выход Максимальное значение периода Установка скважности Настройка ШИМ // Включение ШИМ 162 & BOREN_OFF &
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 13.5. Проверка программы генерации питающих импульсов Как ни странно, но последовательность операции мне пришлось изменить, иначе в программе ISIS симуляция не работает. Вот как выглядит программа, если следовать документации: #include "pic.h" #include <xc.h> // PIC16F628A Configuration Bit Settings __CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_OFF LVP_OFF & CPD_OFF & CP_OFF); unsigned char duty = 192; int main(void) { PR2 = 0xFF; CCPR1L = duty; CCP1CON = 0x3C; TRISB = 0xF7; T2CON = 0x7B; TMR2ON = 1; return 0; } & MCLRE_OFF & BOREN_OFF & // Скважность импульсов // // // // Максимальное значение периода Установка скважности Настройка ШИМ Вывод RB3 на выход // Включение ШИМ Но так симуляции не проходит. Интересно. Интересно, кто в этом виноват? Давайте, проверим этот казус на макетной плате. В чём суть проблемы? В том, где записать настройку TRISB. Если я вписываю её в самом начале, симуляция работает. Если вписываю, как следует из datasheet, ближе к концу настроек, симуляция не работает. Итак, загрузим в микросхему программу и посмотрим, что происходит на выводе RB3. 163
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 13.6. Сигнал на выводе RB3 на экране осциллографа, макетирование Сигнал имеет место при расположении настройки порта и в первом, и во втором случае. Вот так. Этого, впрочем, и следовало ожидать. Осциллограмма выполнена небрежно, но это не меняет существа – появление сигнала не зависит от места расположения настройки порта. Можно было бы пропустить этот эксперимент, но мне хотелось показать, что при появлении сомнений лучше прервать работу над проектом и постараться выяснить, есть ли проблема, в чём проблема, и, если она есть, то, как можно с этой проблемой разобраться. Если в среде разработки MicroC Pro настройка порта выполнена согласно документации, то моделирование в ISIS может не работать по этой причине (и может заработать после появления обновления ISIS). Но это не повод, чтобы отказываться от использования MicroC Pro! Используя программатор PICkit2, я могу выполнить и эту проверку: Рис. 13.7. Проверка программы, написанной в MicroC 164
В.Н. Гололобов Микроконтроллер глазами начинающего Я использовал логический анализатор PICkit2, слева предыдущая версия программы, как на рисунке выше, справа та, что загружена после компиляции в MicroC. Обе работают на макетной плате. Более того, обновив ISIS, я могу убедиться, что и она даёт тот же результат. Рис. 13.8. Работа симулятора ISIS после обновления 165
В.Н. Гололобов Микроконтроллер глазами начинающего Вернёмся к проекту. Я приведу два рисунка, которые пояснят, что я хотел бы продолжить в экспериментах. Рис. 13.9. Работа схемы при температуре близкой к температуре изменения режима До достижения заданной мною температуры вентилятор и нагреватель питаются импульсами, обеспечивающими большую скорость вращения кулера и большую мощность нагрева резистора. На рисунке отмечены скорость вращения вентилятора и температура близкая к температуре перехода в другой режим работы. И температура, и режим питания пары кулер-резистор (нагреватель) – всё это условно. Без уточнения данных, полученных на макете, эти значения не лучше и не хуже любых других. Программа в данный момент получилась такой, что показывать её не следовало бы, но я её всётаки приведу ниже. 166
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 13.10. Работа схемы, когда температура превысила заданный порог Едва пороговое значение было превышено, режим работы обогревателя изменился – вентилятор стал вращаться медленнее, а резистор меньше греться. Вот программа для схемы изображённой выше. // Lcd задание sbit LCD_RS at sbit LCD_EN at sbit LCD_D7 at sbit LCD_D6 at sbit LCD_D5 at sbit LCD_D4 at выводов RB1_bit; RB2_bit; RB7_bit; RB6_bit; RB5_bit; RB4_bit; // Направление выводов sbit LCD_RS_Direction at sbit LCD_EN_Direction at sbit LCD_D7_Direction at sbit LCD_D6_Direction at sbit LCD_D5_Direction at sbit LCD_D4_Direction at TRISB1_bit; TRISB2_bit; TRISB7_bit; TRISB6_bit; TRISB5_bit; TRISB4_bit; //Зададим TEMP_RESOLUTION как соответствующее разрешение для датчика DS18x20: // 18S20: 9 (default setting; может быть 9,10,11 или 12) // 18B20: 12 167
В.Н. Гололобов Микроконтроллер глазами начинающего const unsigned short TEMP_RESOLUTION = 12; char *text = "000.0000"; unsigned temp; unsigned char duty = 0; void set_duty(unsigned char duty) { CCPR1L = duty; T2CON.F0 = 1; T2CON.F0 = 0; } void Display_Temperature(unsigned int temp2write) { const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8; char temp_whole; unsigned int temp_fraction; // Проверим, не отрицательна ли температура if (temp2write & 0x8000) { text[0] = '-'; temp2write = ~temp2write + 1; } // Извлечём temp _whole temp_whole = temp2write >> RES_SHIFT ; // Преобразуем temp_whole в символы if (temp_whole/100) text[0] = temp_whole/100 + 48; else text[0] = '0'; text[1] = (temp_whole/10)%10 + 48; text[2] = temp_whole%10 + 48; // Выделим количество десятков // Выделим количество единиц // Извлечём temp_fraction и преобразуем в unsigned int temp_fraction = temp2write << (4-RES_SHIFT); temp_fraction &= 0x000F; temp_fraction *= 625; // Convert temp_fraction to characters text[4] = temp_fraction/1000 + 48; text[5] = (temp_fraction/100)%10 + 48; text[6] = (temp_fraction/10)%10 + 48; text[7] = temp_fraction%10 + 48; // // // // Выделим Выделим Выделим Выделим // Выведем температуру на LCD Lcd_Out(2, 5, text); } void main() TRISB PORTB T2CON { = 0; = 0; = 0x3F; // Инициализация модуля PWM 168 количество количество количество количество тысяч сотен десятков единиц
В.Н. Гололобов PR2 = 0xFF; CCP1CON = 0x0C; set_duty (127); T2CON.F0 = 1; T2CON.F0 = 0; Микроконтроллер глазами начинающего // Задание начального значения скважности Lcd_Init(); // Инициализация LCD Lcd_Cmd(_LCD_CLEAR); // Очистим LCD Lcd_Cmd(_LCD_CURSOR_OFF); // Выключим курсор Lcd_Out(1, 1, " Temperature: "); // Напечатаем символ градуса для шкалы 'C' Цельсия Lcd_Chr(2,13,223); // Разные LCD дисплеи имеют разные коды для символа //градуса Lcd_Chr(2,14,'C'); //--- Основной цикл do { //--- Выполним чтение температуры Ow_Reset(&PORTA, 4); Ow_Write(&PORTA, 4, 0xCC); Ow_Write(&PORTA, 4, 0x44); Delay_us(120); // Onewire сигнал сброса // Использование команды SKIP_ROM // Использование команды CONVERT_T Ow_Reset(&PORTA, Ow_Write(&PORTA, Ow_Write(&PORTA, Ow_Write(&PORTA, Ow_Write(&PORTA, Ow_Write(&PORTA, Ow_Write(&PORTA, Ow_Write(&PORTA, Ow_Write(&PORTA, Ow_Write(&PORTA, // Сигнал сброса Onewire // Обратимся к датчику по его адресу // Начало адреса датчика 4); 4, 0x55); 4, 0x28); 4, 0x30); 4, 0xC5); 4, 0xB8); 4, 0); 4, 0); 4, 0); 4, 0x8E); Ow_Write(&PORTA, 4, 0xBE); // Используем команду READ_SCRATCHPAD temp = Ow_Read(&PORTA, 4); temp = (Ow_Read(&PORTA, 4) << 8) + temp; //--- Format and display result on Lcd Display_Temperature(temp); if (temp > 600) set_duty(64); // Если температура выше, понизим напряжение if (temp < 600) set_duty(192);// Если температура ниже, повысим напряжение Delay_ms(700); } while (1); } Мы проверили идеи проекта в виртуальном пространстве, чтобы наметить пути реализации проекта. Теперь пора уточнить все параметры, чтобы составить некое, но конкретное техническое задание (для себя, но ТЗ необходимо). И здесь есть ещё один аспект проблемы. Что я хочу сказать? 169
В.Н. Гололобов Микроконтроллер глазами начинающего Чтобы выбрать, определить конкретные цифры параметров, их следует измерять. Для измерения мы используем приборы. Купив, скажем, мультиметр, мы полагаемся на те паспортные данные, что приведены изготовителем. В большинстве случаев производитель либо контролирует параметры приборов, либо осуществляет их поверку. Регулярную поверку приборов делает каждое предприятие, использующее приборы для измерений. У нас нет возможности (возможность, не исключаю, есть, но кто ею пользуется?) поверить приборы нашей любительской лаборатории. Вместе с тем, мой мультиметр может измерять температуру, но с точностью не более чем 0.75%. Этой точности не хватит для проверки работы датчика температуры. А приборов для измерения температуры с требуемой точностью (я не говорю даже с точностью в несколько раз большей, что было бы правильнее), если исключить медицинский термометр, я, например, не нашёл среди доступных и с точки зрения цены, и с точки зрения подходящих параметров. Можно решить эту проблему разными способами, например, использовать ещё одну модель датчика температуры. Вот как выглядит термометр, который можно собрать. Рис. 13.11. Цифровой термометр для проведения экспериментов Эта конструкция интересна с точки зрения знакомства с протоколом I2C, а датчик DS1621 имеет хорошее разрешение в 0,030С. Если возникнет необходимость, такой контрольный термометр можно собрать. Резюме В своих рассказах я использую компьютерные программы, поскольку мне так удобнее. Более того, я искренно считаю, что и радиолюбители не должны пренебрегать теми возможностями, что даёт использование компьютера. Да, бывает, что программы «подводят», это так, но есть и другие инструменты, позволяющие разобраться в происходящем. 170
В.Н. Гололобов Микроконтроллер глазами начинающего Обычно, рассказывая об инструментах, которые необходимы или полезны при работе, я старюсь ориентироваться на доступность этих инструментов для всех. Идёт ли речь о приборах, идёт ли речь о программах – есть многое из того, что при всей своей полезности оказывается слишком дорогим удовольствием. Если это так, то всегда можно поискать замену дорогостоящему решению. Внешне это выглядит, может быть, не столь убедительно: звуковая плата компьютера в качестве осциллографа позволяет работать до частоты в 20 кГц, а современный осциллограф работает на частотах выше 100 МГц. Однако для понимания существа вопроса, касающегося многих схемных решений, не обязательно использовать реальные параметры, как, например, рабочая частота, можно разобрать суть проблемы на частоте пригодной для звуковой карты компьютера, повторить схему в программе моделирования, попытаться увидеть в программе моделирования все «подводные камни», а затем перейти к макетной плате. В предыдущих главах мы сделали всё (или многое из возможного), что было доступно без перехода к макетированию. Теперь пришла пора проверить наши решения и предположения с реальными элементами и устройствами, используя все инструменты, которые есть в нашем распоряжении. 171
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 14. Первые эксперименты на макетной плате Итак, как я и намеревался, пора перейти к макетированию. Но есть одна небольшая проблема – я ещё не получил заказанные мной детали. Поэтому начну издалека. За время между двумя главами этого повествования я решил обновить операционную систему Windows до восьмой версии – любопытство один из моих недостатков. Ещё до появления этой версии в продаже, как в случае с новой версией Word, я прочитал много негативных высказываний. Дело вкуса. Лично мне эта версия Windows понравилась не менее предыдущих. И очень напомнила… Впрочем, сами посмотрите: Рис. 14.1. Основное рабочее окно Windows 8 Рис. 14.2. Последняя на сегодня версия графической оболочки Gnome в Fedora 17 172
В.Н. Гололобов Микроконтроллер глазами начинающего В ожидании деталей, спасибо Интернет-магазину «RoboCraft» за оперативность – бандероль уже в Москве, в ожидании, когда бандероль перевезут из одного места в Москве в другое, я начну с экспериментов с кулером. Не то, чтобы раньше мне не приходилось менять кулер, но я никогда не задумывался о том, какое это интересное устройство. Кулер, приобретённый для меня по моей просьбе, имеет три вывода. Если верить тому, что я нашёл в Интернете, чёрный провод – общий, красный провод – плюс питающего напряжения 12 В, а синий – это вывод датчика скорости вращения. У меня нет оснований не доверять этой информации, но и сам я никогда не проверял её. Поэтому первый опыт, который я намерен провести, это подключение к регулируемому источнику постоянного напряжения и наблюдение за тем, что происходит на синем проводе датчика, когда напряжение питания кулера меняется от 5 до 10 вольт. Рис. 14.3. Сигнал на выводе датчика скорости вращения кулера при напряжении 5 В Рис. 14.4. Сигнал на выводе датчика скорости вращения кулера при напряжении 12 В Как и следовало ожидать, при изменении напряжения питания изменяется период импульсов от датчика скорости вращения. Сигнал довольно слабый, амплитуда порядка 30 мВ, но я не использовал никаких дополнительных элементов, подключив щуп осциллографа непосредственно к выводу кулера. На сайте RoboCraft, где я находил много интересного, есть и статья об управлении кулером. При необходимости я могу использовать то решение, что предложено в этой статье. Но об этом позже. Сейчас у меня есть возможность контролировать скорость вращения 173
В.Н. Гололобов Микроконтроллер глазами начинающего кулера, поэтому разумно перейти к его питанию импульсами разной длительности, чтобы убедиться в изменении скорости вращения (или отсутствии этого). Программу для контроллера PIC16F628A я могу использовать из предыдущих глав. Но предварительно следует проверить, как вентилятор реагирует на импульсное питание. Однако в проведении этого эксперимента есть маленький «подводный камень». В чём проблема? Вот схема опыта: Рис. 14.5. Схема эксперимента с импульсным питанием вентилятора На рисунке вольтметр должен изображать осциллограф. Но у моего измерителя общие выводы осциллографа и генератора объединены. В итоге транзистор будет закорочен. Самое простое решение, которое я вижу – это применить двухкаскадный ключ: Рис. 14.6. Окончательная схема эксперимента Здесь VM1 – это осциллограф, а P1 (я как смог, так изобразил) – это вентилятор с датчиком. Транзисторы можно использовать любые подходящие, например, КТ502 и КТ503 с допустимым напряжением на коллекторе 20-30 вольт. Диод D1 следует включать, если нагрузка носит индуктивный характер, но у моего кулера этот диод уже есть в схеме кулера. Устранив эту маленькую неприятность, связанную с измерителем, можно приступить к сборке схемы на макетной плате и проведению испытаний. 174
В.Н. Гололобов Микроконтроллер глазами начинающего Следующий «подводный камешек» вполне ожидаемый: Рис. 14.7. Вид сигнала с датчика вращения кулера При импульсном питании кулера наводки на датчик затрудняют считывание периода повторения импульсов с датчика. Чтобы улучшить эту ситуацию, можно подключить датчик через резистор в 10-20 кОм к плюсу питания. То есть, схема опыта будет выглядеть так: Рис. 14.8. Скорректированная схема эксперимента Вид сигналов с датчика вращения кулера стал несколько лучше. Во всяком случае, можно сравнить период (или полупериод) импульсов с датчика при заполнении питающими импульсами на 50% (меандр) и 80%. 175
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 14.9. Сигнал с датчика вращения при заполнении питающими импульсами на 50% Рис. 14.10. Сигнал с датчика вращения при заполнении питающими импульсами на 80% Сравнивая эти осциллограммы с теми, что были получены при питании кулера постоянным напряжением, можно согласиться, как и ожидалось, меняя скважность импульсов, можно управлять скоростью вращения кулера. 176
В.Н. Гололобов Микроконтроллер глазами начинающего Проверив эти предположения на макете, можно приступить к проверке схемы считывания температуры с датчиков DS16B20 (датчики я получил, можно продолжить работу). В виртуальном пространстве предыдущих глав, выяснив, что протокол 1-wire при наличии нескольких датчиков требует знания адреса конкретного датчика, я использовал программу для считывания этого адреса. Есть, правда, специализированная программа от изготовителей датчиков, но не думаю, что она будет удобнее – потребуется, вероятно, интерфейс для связи датчика с компьютером. Однако прежде, вы помните, остался неясным вопрос с командами для одного датчика. Вкратце повторю суть проблемы: Я пытался отправить, моделируя в программе Proteus, приветствие и две команды от ведущего устройства: CCh и BEh. Я надеялся получить вразумительный ответ от датчика (виртуального), но не получил его (или ничего не понял). Разрешение сомнений я отложил до того момента, когда смогу это проверить на макетной плате. Этот момент настал. Я уже говорил, что временами жульничаю? Если нет, то сказал теперь. И в этот раз я хочу максимально облегчить себе работу. Во-первых, я использую микроконтроллер (и плату от робота ROBOPICA) PIC16F887. Контроллер подключён к дисплею, написав программу, я легко могу всё проверить. И с программой, у меня есть возможность воспользоваться программой Flowcode 5 версии, которая существенно ускорит процесс написания программы. Позже можно будет вернуться и к контроллеру PIC16F628A, и к программе MicroC Pro или MPLABX. Более того, позже можно будет попробовать всё повторить, используя модуль Arduino. Но сейчас, чем быстрее, тем лучше. Итак. Рис. 14.11. Программа проверки в Flowcode Вот результат виртуальной проверки: 177
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 14.12. Проверка программы в ISIS И наконец, собрав схему на реальной макетной плате, я могу сказать… Рис. 14.13. Проверка программы на реальной макетной плате …я могу сказать, что и на реальной макетной плате вижу тот же результат, что и на виртуальном её воплощении. Видимо что-то я не понял, понял не правильно, или сделал не так, как следовало. Я не исключаю, что позже вернусь к этому вопросу, а сейчас предпочту идти дальше. 178
В.Н. Гололобов Микроконтроллер глазами начинающего Каждый из датчиков DS18B20 имеет уникальный номер (или адрес), встроенный при изготовлении датчика. Чтобы выяснить адреса датчиков, можно использовать опрос всех датчиков. Но в данный момент, как мне кажется, предпочтительнее использовать программу, определяющую адрес датчика. Вот эта программа, которую можно оттранслировать в MicroC Pro: // Lcd задание sbit LCD_RS at sbit LCD_EN at sbit LCD_D7 at sbit LCD_D6 at sbit LCD_D5 at sbit LCD_D4 at выводов RD2_bit; RD3_bit; RD7_bit; RD6_bit; RD5_bit; RD4_bit; // Направление выводов sbit LCD_RS_Direction at sbit LCD_EN_Direction at sbit LCD_D7_Direction at sbit LCD_D6_Direction at sbit LCD_D5_Direction at sbit LCD_D4_Direction at unsigned short unsigned short unsigned short unsigned short unsigned short unsigned short unsigned short unsigned short unsigned short char txt[5]; void main() { ANSEL = 0; ANSELH = 0; C1ON_bit = 0; C2ON_bit = 0; t0 t1 t2 t3 t4 t5 t6 t7 t8 = = = = = = = = = TRISD2_bit; TRISD3_bit; TRISD7_bit; TRISD6_bit; TRISD5_bit; TRISD4_bit; 0; 0; 0; 0; 0; 0; 0; 0; 0; // Configure AN pins as digital I/O // Disable comparators Lcd_Init(); Lcd_Cmd(_LCD_CLEAR); Lcd_Cmd(_LCD_CURSOR_OFF); // Initialize LCD // Clear LCD // Turn cursor off //--- Основной цикл do { Ow_Reset(&PORTE, 2); Ow_Write(&PORTE, 2, 0x33); t0 = Ow_Read(&PORTE, 2); t1 = Ow_Read(&PORTE, 2); t2 = Ow_Read(&PORTE, 2); t3 = Ow_Read(&PORTE, 2); // Onewire сброс // Команда выдачи адреса // Считываем адрес в восемь байт 179
В.Н. Гололобов t4 t5 t6 t7 t8 = = = = = Ow_Read(&PORTE, Ow_Read(&PORTE, Ow_Read(&PORTE, Ow_Read(&PORTE, Ow_Read(&PORTE, ByteToStr(t0, txt); Lcd_Out(1,3,txt); Delay_ms(1000); Микроконтроллер глазами начинающего 2); 2); 2); 2); 2); // Преобразуем каждый байт в символы // которые выводим на дисплей ByteToStr(t1, txt); Lcd_Out(1,8,txt); Delay_ms(1000); ByteToStr(t2, txt); Lcd_Out(2,3,txt); Delay_ms(1000); ByteToStr(t3, txt); Lcd_Out(2,8,txt); Delay_ms(1000); ByteToStr(t4, txt); Lcd_Out(1,3,txt); Delay_ms(1000); ByteToStr(t5, txt); Lcd_Out(1,8,txt); Delay_ms(1000); ByteToStr(t6, txt); Lcd_Out(2,3,txt); Delay_ms(1000); ByteToStr(t7, txt); Lcd_Out(2,8,txt); Delay_ms(1000); } while (1); } После загрузки программы в микроконтроллер, повторюсь, что использую плату от ROBOPICA и программатор PICKit2, на экране дисплея можно прочитать числа, составляющие адрес датчика. 180
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 14.14. Считывание адреса на макетной плате Зная адрес датчика, можно вывести на дисплей температуру, измеряемую этим датчиком. Программу вывода температуры можно взять из примера, приведённого в библиотеке среды разработки MicroC Pro. Подключение датчика и дисплея также приведено в примере: Рис. 14.15. Схема подключения датчика температуры и дисплея из руководства MicroC Pro 181
В.Н. Гололобов Микроконтроллер глазами начинающего Программа для проведения эксперимента тоже взята из примеров. На макетной плате дисплей подключён к другим выводам, и это пришлось корректировать в программе. // Lcd задание sbit LCD_RS at sbit LCD_EN at sbit LCD_D7 at sbit LCD_D6 at sbit LCD_D5 at sbit LCD_D4 at выводов RD2_bit; RD3_bit; RD7_bit; RD6_bit; RD5_bit; RD4_bit; // Направление выводов sbit LCD_RS_Direction at sbit LCD_EN_Direction at sbit LCD_D7_Direction at sbit LCD_D6_Direction at sbit LCD_D5_Direction at sbit LCD_D4_Direction at TRISD2_bit; TRISD3_bit; TRISD7_bit; TRISD6_bit; TRISD5_bit; TRISD4_bit; // Задание TEMP_RESOLUTION для использования датчика DS18x20: // 18S20: 9 (default setting; can be 9,10,11,or 12) // 18B20: 12 const unsigned short TEMP_RESOLUTION = 12; char *text = "000.0000"; unsigned temp; void Display_Temperature(unsigned int temp2write) { const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8; char temp_whole; unsigned int temp_fraction; // Проверка, не отрицательная ли температура if (temp2write & 0x8000) { text[0] = '-'; temp2write = ~temp2write + 1; } // Образование temp_whole temp_whole = temp2write >> RES_SHIFT ; // Преобразование temp_whole в символьный вид if (temp_whole/100) text[0] = temp_whole/100 + 48; else text[0] = '0'; text[1] = (temp_whole/10)%10 + 48; text[2] = temp_whole%10 + 48; // Выделение цифры десятков // Выделение цифры единиц // Выделение temp_fraction и преобразование в unsigned int temp_fraction = temp2write << (4-RES_SHIFT); temp_fraction &= 0x000F; temp_fraction *= 625; 182
В.Н. Гололобов Микроконтроллер глазами начинающего // Convert temp_fraction to characters text[4] = temp_fraction/1000 + 48; text[5] = (temp_fraction/100)%10 + 48; text[6] = (temp_fraction/10)%10 + 48; text[7] = temp_fraction%10 + 48; // // // // Выделение Выделение Выделение Выделение цифры цифры цифры цифры тысяч сотен десятков единиц // Вывод температуры на LCD Lcd_Out(2, 5, text); } void main() { ANSEL = 0; ANSELH = 0; C1ON_bit = 0; C2ON_bit = 0; // Конфигурируем выводы AN как цифровой I/O // Компараторы выключаем Lcd_Init(); // Lcd_Cmd(_LCD_CLEAR); // Lcd_Cmd(_LCD_CURSOR_OFF); // Lcd_Out(1, 1, " Temperature: "); // Вывод символа градусов для шкалы Цельсия Lcd_Chr(2,13,223); // Разные LCD имеют разные Инициализация LCD Очистка LCD Выключение курсора коды для символов Lcd_Chr(2,14,'C'); //--- Основной цикл do { //--- Подготовка чтения температуры Ow_Reset(&PORTE, 2); // Onewire сигнал сброса Ow_Write(&PORTE, 2, 0xCC); // Использование команды SKIP_ROM Ow_Write(&PORTE, 2, 0x44); // Использование команды CONVERT_T Delay_us(120); Ow_Reset(&PORTE, 2); Ow_Write(&PORTE, 2, 0xCC); Ow_Write(&PORTE, 2, 0xBE); // Использование команды SKIP_ROM // Использование команды READ_SCRATCHPAD temp = Ow_Read(&PORTE, 2); temp = (Ow_Read(&PORTE, 2) << 8) + temp; //--- Форматирование и отображение результата на Display_Temperature(temp); Delay_ms(500); } while (1); } Пока мы не ушли слишком далеко от программы, я хочу обратить ваше внимание на использование команды SKIP_ROM , что не получалось ранее. В данном случае использована последовательность команд CCh и BEh (чуть выше в программе). Если сейчас программа заработает, то, возможно, будет найдена причина, по которой у нас этот эксперимент не получился. В этой программе предварительно используется команда конвертирования 183
В.Н. Гололобов Микроконтроллер глазами начинающего температуры, а следом полученное значение считывается командой чтения. Эти команды отсутствовали в эксперименте ранее. Однако результат не оправдал ожиданий: Рис. 14.16. Неудачное измерение температуры на макете В чём теперь проблема? Моделирование показывает, что программа должна работать!? Рис. 14.17. Моделирование той же программы Разгадка кроется в примечании из справки к датчику по команде 44h: (not applicable for parasite-powered DS18S20s) 184
В.Н. Гололобов Микроконтроллер глазами начинающего Команда НЕ применима при паразитном питании датчика. На рисунке выше использовано это самое паразитное питание датчика. Режим работы датчика допустимый, но команда 44h работать не будет. Если изменить питание датчика, то есть, подключить его к +5 вольт, то всё получается. Рис. 14.18. Удачное измерение температуры на макетной плате 185
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 15. Измерение температуры и другое Давайте начнём эту главу с резюме к предыдущей главе. То есть, обсудим подробнее то, что делали в предыдущей главе в плане программирования. Я использовал не только готовую библиотеку среды разработки MicroC Pro, но и готовую программу, приведённую в справочнике по библиотеке этой программы. Причина проста – если есть готовая программа, то удобнее использовать её, а не изобретать велосипед. Ещё одна причина – программа написана профессиональными программистами, а именно у них и следует учиться программированию. И, наконец, в данном случае мой интерес в первую очередь направлен на то, чтобы показать вам, что хорошая среда разработки – это половина успеха. Вместе с тем, в программе могут встретиться фразы языка Си, с которыми мы ещё не познакомились. Давайте с ними познакомимся. Я повторю программу чтения температуры, а затем мы фраза за фразой попробуем понять, знакомы ли мы с этим или нет? // Lcd задание sbit LCD_RS at sbit LCD_EN at sbit LCD_D7 at sbit LCD_D6 at sbit LCD_D5 at sbit LCD_D4 at выводов RD2_bit; RD3_bit; RD7_bit; RD6_bit; RD5_bit; RD4_bit; // Направление выводов sbit LCD_RS_Direction at sbit LCD_EN_Direction at sbit LCD_D7_Direction at sbit LCD_D6_Direction at sbit LCD_D5_Direction at sbit LCD_D4_Direction at TRISD2_bit; TRISD3_bit; TRISD7_bit; TRISD6_bit; TRISD5_bit; TRISD4_bit; // Задание TEMP_RESOLUTION для использования датчика DS18x20: // 18S20: 9 (default setting; can be 9,10,11,or 12) // 18B20: 12 const unsigned short TEMP_RESOLUTION = 12; char *text = "000.0000"; unsigned temp; void Display_Temperature(unsigned int temp2write) { const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8; char temp_whole; unsigned int temp_fraction; // Проверка, не отрицательная ли температура if (temp2write & 0x8000) { text[0] = '-'; temp2write = ~temp2write + 1; } // Образование temp_whole temp_whole = temp2write >> RES_SHIFT ; 186
В.Н. Гололобов Микроконтроллер глазами начинающего // Преобразование temp_whole в символьный вид if (temp_whole/100) text[0] = temp_whole/100 + 48; else text[0] = '0'; text[1] = (temp_whole/10)%10 + 48; text[2] = temp_whole%10 + 48; // Выделение цифры десятков // Выделение цифры единиц // Выделение temp_fraction и преобразование в unsigned int temp_fraction = temp2write << (4-RES_SHIFT); temp_fraction &= 0x000F; temp_fraction *= 625; // Convert temp_fraction to characters text[4] = temp_fraction/1000 + 48; text[5] = (temp_fraction/100)%10 + 48; text[6] = (temp_fraction/10)%10 + 48; text[7] = temp_fraction%10 + 48; // // // // Выделение Выделение Выделение Выделение цифры цифры цифры цифры тысяч сотен десятков единиц // Вывод температуры на LCD Lcd_Out(2, 5, text); } void main() { ANSEL = 0; ANSELH = 0; C1ON_bit = 0; C2ON_bit = 0; // Конфигурируем выводы AN как цифровые I/O // Компараторы выключаем Lcd_Init(); // Lcd_Cmd(_LCD_CLEAR); // Lcd_Cmd(_LCD_CURSOR_OFF); // Lcd_Out(1, 1, " Temperature: "); // Вывод символа градусов для шкалы Цельсия Lcd_Chr(2,13,223); // Разные LCD имеют разные Инициализация LCD Очистка LCD Выключение курсора коды для символов Lcd_Chr(2,14,'C'); //--- Основной цикл do { //--- Подготовка чтения температуры Ow_Reset(&PORTE, 2); // Onewire сигнал сброса Ow_Write(&PORTE, 2, 0xCC); // Использование команды SKIP_ROM Ow_Write(&PORTE, 2, 0x44); // Использование команды CONVERT_T Delay_us(120); Ow_Reset(&PORTE, 2); Ow_Write(&PORTE, 2, 0xCC); Ow_Write(&PORTE, 2, 0xBE); // Использование команды SKIP_ROM // Использование команды READ_SCRATCHPAD temp = Ow_Read(&PORTE, 2); temp = (Ow_Read(&PORTE, 2) << 8) + temp; 187
В.Н. Гололобов Микроконтроллер глазами начинающего //--- Форматирование и отображение результата на дисплее Display_Temperature(temp); Delay_ms(500); } while (1); } Начинается программа с примечания (комментария): // Lcd задание выводов Мы знаем, что двойная косая черта в строке указывает на строчный комментарий. Всё, что за этим знаком, компилятором игнорируется. Примечания служат только для того, чтобы тот, кто будет читать вашу программу (даже в том случае, когда это вы сами), быстрее понимал, что написано в программе. Чем яснее и подробнее написаны комментарии, тем легче, например, отлаживать программу или находить в ней ошибки; хорошо прокомментированную программу легче переделать под свои нужды. В данном случае примечание говорит о том, что ниже приводится задание выводов, к которым присоединяется дисплей. Вот эти выводы на моей макетной плате: sbit sbit sbit sbit sbit sbit LCD_RS LCD_EN LCD_D7 LCD_D6 LCD_D5 LCD_D4 at at at at at at RD2_bit; RD3_bit; RD7_bit; RD6_bit; RD5_bit; RD4_bit; То, как подключается дисплей, зависит от конкретной модели дисплея. Многие модели дисплеев очень схожи в этом смысле, но всё равно следует свериться с документацией, как подключить конкретную модель дисплея. Приведённый выше фрагмент текста обусловлен не столько языком Си, сколько конкретным транслятором (компилятором), входящим в состав среды разработки. Поскольку мы сейчас проверяем всё, о чем говорили ранее (и о чём говорим сейчас), давайте проверим и это. Всё просто: программа начинается с этого фрагмента; создадим проект в среде MPLABX с компилятором xc8, добавим этот фрагмент в текст программы: #include "pic.h" sbit LCD_RS at RD2_bit; sbit LCD_EN at RD3_bit; sbit LCD_D7 at RD7_bit; sbit LCD_D6 at RD6_bit; sbit LCD_D5 at RD5_bit; sbit LCD_D4 at RD4_bit; int main(void) { return 0; } …и попробуем оттранслировать этот текст. 188
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 15.1. Попытка трансляции текста программы другим компилятором Ошибка за ошибкой, трансляция не проходит. Этого и следовало ожидать. Поэтому, когда вы берёте готовый текст программы, убедитесь, что среда разработки та же, что и у создателя программы, или хотя бы в том, что используемый компилятор то же, включая версию компилятора, поскольку в разных версиях могут быть отличия, дающие ошибки при трансляции. Но вернёмся к тексту программы. Примечание далее говорит о том, что задаётся направление ранее определённых выводов подключения к дисплею. Мы знаем, что выводы микроконтроллера могут работать и для цифрового ввода, и для цифрового вывода. А у многих моделей кроме цифрового ввода выводы микроконтроллера могут осуществлять и аналоговый ввод. Поэтому без задания направления работы выводы могут не работать правильно. Следующая строка, которая выглядит так: const unsigned short TEMP_RESOLUTION = 12; …задаёт разрешение датчика, о чём есть указание в комментарии выше. Что здесь нас может интересовать? const – это определение константы. Во многих случаях мы многократно используем одну и ту же константу, как число «Пи» или корень квадратный из двух. Удобно задать эту константу, дав ей имя, и далее использовать это имя, а не вписывать каждый раз четыре пять цифр, рискуя допустить ошибку, которую позже придётся отыскивать, и сделать это будет трудно. Вдобавок, если вы захотите увеличить точность вычислений, достаточно будет изменить постоянную в одном месте, чтобы она правильно работала во всей программе. unsigned short – так задаётся тип данных в виде беззнакового значения. Типы данных и их обозначение лучше проверить в описании конкретного компилятора. Одни и те же типы данных могут иметь разное наименование, а могут и несколько отличаться по смыслу. В основном разные 189
В.Н. Гололобов Микроконтроллер глазами начинающего типы данных занимают разное количество ячеек в памяти, а данные со знаком и беззнаковые ещё и определяют то, как следует понимать число, записанное в ячейках памяти. Ведь в памяти процессора записаны только двоичные числа, команда это или отрицательное целое. Следом за именем, оно, напомню, произвольно, даётся программистом, но тоже должно учитывать требования конкретного компилятора. Современные компиляторы дают много свободы в написании имён переменных и констант, функций и процедур, но следует убедиться, что компилятор, например, различает регистры и допускает очень длинные имена. С операцией присваивания «=» мы уже знакомы. Далее есть запись: char *text = "000.0000"; Чем интересна эта строка текста? Во-первых, используется указатель. О нём мы раньше не говорили. Указатели настолько часто используются в языке Си, что взяв любой профессиональный текст программы, вы наверняка сразу столкнётесь с указателем, как здесь - *text. Об указателях и их использовании можно прочитать в Интернете: Указатели обеспечивают доступ к адресам переменных в памяти. Они позволяют работать с символическими адресами (именами переменных, содержащих адреса) и выполнение над адресами ряда операций. О них можно прочитать в книге, одним из соавторов которой был Деннис Ричи создатель языка Си, «Язык программирования Си» Б.В. Керниган и Д.М. Ричи. Можно сколько угодно спорить, писать программы для микроконтроллеров на ассемблере или Си, но любой из спорщиков согласится с тем, что язык Си почти самый популярный язык программирования. Если вы намерены заниматься программированием микроконтроллеров даже в любительских целях, я очень советую вам скачать в Интернете или купить в магазине книгу Кернигана и Ричи. Итак, указатель: Указатель - это переменная, значением которой является адрес другой переменной. Так как указатель может ссылаться на переменные разных типов. С указателем в языке Си связывается тип того объекта, на который он ссылается. Для описания указателей используется операция косвенной адресации *. Например, указатель целого типа uk описывается так : int *uk. Унарная операция &, примененная к некоторой переменной, показывает, что нам нужен адрес этой переменной, а не ее текущее значение. Если переменная uk объявлена как указатель, то оператор присваивания uk=&x означает: "взять адресс переменной x и присвоить его значение переменной-указателю uk". Это определение я взял из статьи в Интернете, где вы можете увидеть ещё одну операцию, связанную с указателем, и которую не следует путать с логической операцией «И». Возвращаясь к записи в нашей программе, отметим, что указатель связывает с переменной text строковую переменную, начальное значение которой задаётся как "000.0000", а все элементы этого, в сущности, массива – это символы (типа char). Двойные кавычки и указывают на строку. 190
В.Н. Гололобов Микроконтроллер глазами начинающего Переведя в программе полученные при измерении данные в десятичные значения, мы выводим их на дисплей в том виде, в каком нам удобно их понимать, то есть, как десятичное число. На дисплее мы отображаем символы. Таким образом, нам предстоит ещё преобразовать число в набор символов, представляющих это число. Этим займётся функция void Display_Temperature(unsigned int temp2write). Как вы видите, имя функции, спасибо программисту, написано так, что мы можем сразу понять, что делает эта функция – Отображение_Температуры. Некоторые компиляторы, возможно, допускают использование кириллицы, но я не советую это делать. Проблемы могут возникнуть позже, а отыскать ошибку будет сложно. Используйте латиницу и в тексте программы, и в названиях файлов, и в месте расположения файлов. Этим вы обезопасите себя от ошибок, связанных с тем, что программа может использовать, например, папку по умолчанию «Мои документы», но при работе спотыкаться на этом имени и выводить ошибку самым непредсказуемым образом. Не обязательно, если вы не в ладах с английским, писать имена на английском языке, достаточно использовать латиницу. В эту функцию отображения температуры передаётся беззнаковое целое (unsigned int). А ключевое слово void в данном случае не столько обозначает тип данных, сколько указывает на то, что функция не возвращает значения, поэтому в функции отсутствует ключевое слово return. Обратите внимание на условие оператора if, следующее за заданием переменных и констант: if (temp2write & 0x8000) { text[0] = '-'; В условии использована операция логического «И», выполняющая роль маски, чтобы проверить состояние одного бита. Если этот бит равен единице, то условие выполняется. И ещё, строковая переменная text – посмотрите, с ней мы работаем так же, как с элементом массива, задавая нулевой элемент (по его индексу) символом «минус», если измеренная температура отрицательна (если условие выполняется). Дело в том, что проверяемый нами бит несёт информацию о знаке температуры. Если 15й бит (младший бит нулевой) равен нулю, то температура положительная, если он равен единице, то температура отрицательная. Именно это и проверяет конструкция temp2write & 0x8000 в условии оператора if. Далее вы встретите языковую конструкцию ~temp2write – это побитовая операция инверсии каждого бита. Между логическим отрицанием и побитовой операцией NOT существует разница не только в написании. Логическое отрицание записывается как «!» (восклицательный знак). Вот пример из руководства MicroC Pro: ! 0x1234 равно нулю; ~ 0x1234 равно 0xEDCB. Обязательно загляните в раздел Help! Вы узнаете много интересного и программе MicroC Pro, и об операторах языка Си, с которыми работает компилятор этой среды разработки. Следующая конструкция: temp_whole = temp2write >> RES_SHIFT; 191
В.Н. Гололобов Микроконтроллер глазами начинающего Кроме присваивания, с которым мы знакомы, есть операция сдвига вправо «>>». Все биты переменной temp2write сдвигаются вправо на количество позиций, определяемое константой RES_SHIFT. Что записано дальше? Вот строка: text[1] = (temp_whole/10)%10 + 48; // Выделение цифры десятков Первый элемент массива, ранее мы видели нулевой, получит символ цифры десятков. Для этого двоичное число разделено на десять. К результату применяется операция %. Что это за операция? Вот примечание из справки: Modulus operator returns the remainder of integer division (cannot be used with floating points) – оператор модуля возвращает остаток от целочисленного деления (нельзя использовать с числами с плавающей точкой). Запись операций: temp_fraction &= 0x000F; …типична для языка Си. Если записать эту операцию в привычном виде, то она будет выглядеть так: temp_fraction = temp_fraction & 0x000F; Я считаю, что очень полезно для начинающего написать ряд простых программ, использующих по одной операции из ранее встреченных, чтобы в режиме отладки увидеть, как выглядят результаты работы разных операторов. Возвращаясь к экспериментам, я хочу использовать два датчика температуры, адреса которых я выяснил ранее, чтобы сравнить их показания. Я использую «тупое» копирование для превращения ранее приведённой программы в нужную мне версию. Вот результат: // Lcd задание sbit LCD_RS at sbit LCD_EN at sbit LCD_D7 at sbit LCD_D6 at sbit LCD_D5 at sbit LCD_D4 at выводов RD2_bit; RD3_bit; RD7_bit; RD6_bit; RD5_bit; RD4_bit; // Направление выводов sbit LCD_RS_Direction at sbit LCD_EN_Direction at sbit LCD_D7_Direction at sbit LCD_D6_Direction at sbit LCD_D5_Direction at sbit LCD_D4_Direction at TRISD2_bit; TRISD3_bit; TRISD7_bit; TRISD6_bit; TRISD5_bit; TRISD4_bit; // Set TEMP_RESOLUTION to the corresponding resolution sensor: // 18S20: 9 (default setting; can be 9,10,11,or 12) // 18B20: 12 192 of used DS18x20
В.Н. Гололобов Микроконтроллер глазами начинающего const unsigned short TEMP_RESOLUTION = 12; char *text = "000.0000"; unsigned temp; void Display_Temperature(unsigned int temp2write) { const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8; char temp_whole; unsigned int temp_fraction; // Check if temperature is negative if (temp2write & 0x8000) { text[0] = '-'; temp2write = ~temp2write + 1; } // Extract temp_whole temp_whole = temp2write >> RES_SHIFT ; // Convert temp_whole to characters if (temp_whole/100) text[0] = temp_whole/100 + 48; else text[0] = '0'; text[1] = (temp_whole/10)%10 + 48; text[2] = temp_whole%10 + 48; // Extract tens digit // Extract ones digit // Extract temp_fraction and convert it to unsigned int temp_fraction = temp2write << (4-RES_SHIFT); temp_fraction &= 0x000F; temp_fraction *= 625; // Convert temp_fraction to characters text[4] = temp_fraction/1000 + 48; text[5] = (temp_fraction/100)%10 + 48; text[6] = (temp_fraction/10)%10 + 48; text[7] = temp_fraction%10 + 48; // // // // Extract Extract Extract Extract thousands digit hundreds digit tens digit ones digit // Print temperature on LCD // Lcd_Out(2, 5, text); } void main() { ANSEL = 0; digital I/O ANSELH = 0; C1ON_bit = 0; C2ON_bit = 0; // Configure AN pins as // Disable comparators Lcd_Init(); Lcd_Cmd(_LCD_CLEAR); Lcd_Cmd(_LCD_CURSOR_OFF); Lcd_Out(1, 1, "T1: "); // Initialize LCD // Clear LCD // Turn cursor off 193
В.Н. Гололобов Микроконтроллер глазами начинающего Lcd_Out(2, 1, "T2: "); // Print degree character, 'C' for Centigrades Lcd_Chr(2,13,223); have different char code for degree // Different LCD displays // If you see greek alpha letter try typing 178 instead of 223 Lcd_Chr(2,14,'C'); //--- Main loop do { //--- Perform temperature reading Ow_Reset(&PORTE, 2); Ow_Write(&PORTE, 2, 0xCC); Ow_Write(&PORTE, 2, 0x44); Delay_us(120); Ow_Reset(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, // Onewire сигнал сброса // Использование команды SKIP_ROM // Использование команды CONVERT_T 2); 2, 0x55); 2, 0x28); 2, 0x99); 2, 0x90); 2, 0xFB); 2, 3); 2, 0); 2, 0); 2, 0x8F); // Onewire reset signal Ow_Write(&PORTE, 2, 0xBE); READ_SCRATCHPAD // Issue command temp = Ow_Read(&PORTE, 2); temp = (Ow_Read(&PORTE, 2) << 8) + temp; //--- Format and display result on Lcd Display_Temperature(temp); Lcd_Out(1, 5, text); Delay_ms(1000); Ow_Reset(&PORTE, 2); Ow_Write(&PORTE, 2, 0xCC); Ow_Write(&PORTE, 2, 0x44); Delay_us(120); Ow_Reset(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, Ow_Write(&PORTE, // Onewire сигнал сброса // Использование команды SKIP_ROM // Использование команды CONVERT_T 2); 2, 0x55); 2, 0x28); 2, 0x1E); 2, 0xB2); 2, 0xFB); 2, 3); 2, 0); 2, 0); // Onewire reset signal 194
В.Н. Гололобов Микроконтроллер глазами начинающего Ow_Write(&PORTE, 2, 0x9B); Ow_Write(&PORTE, 2, 0xBE); // Issue command READ_SCRATCHPAD temp = Ow_Read(&PORTE, 2); temp = (Ow_Read(&PORTE, 2) << 8) + temp; //--- Format and display result on Lcd Display_Temperature(temp); Lcd_Out(2, 5, text); Delay_ms(1000); } while (1); } И вот результат работы программы: Рис. 15.2. Работа программы на макетной плате Две фотографии рядом показывают то, что чаще всего я наблюдал – разница в показаниях двух датчиков не более чем шаг отсчёта, то есть, 0.0625. На одном из форуме Flowcode.info я встретил рекомендацию опрашивать датчики не чаще 2х раз в минуту. Внести изменения в программу особенных сложностей не представляет. И можно перейти к экспериментам с вентилятором и подогревателем. А вам я советую в качестве практики по использованию языка Си обратить внимание на механизм функций. Напишите функцию, в которую вы передадите адрес датчика, которая выполнит все операции по чтению и выводу температуры на экран дисплея. А в основной программе используйте двукратное обращение к функции, а не двойной повтор одних и тех же команд. 195
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 16. Опыты с вентилятором и подогревателем Теперь, когда мы научились считывать температуру с датчиков, можно проверить, что происходит с температурой, если использовать для подогрева вентилятор с подогревателем. Меня интересует, как велика, если она есть, разница температур в двух точках воздушного потока при разных расстояниях одной точки измерения от другой. Схема первого эксперимента проста: подготовленные на макетной плате датчики обдувает вентилятор с подогревателем; подогреватель и вентилятор включены параллельно и питаются постоянным напряжением от блока питания; напряжение питания вентилятора и подогревателя меняется от 5 В до 12 В. Первоначально расстояния между датчиками около 15 см. При подготовке к этому эксперименту я столкнулся с эффектом, который вызывает у меня некоторую насторожённость, следует ли продолжать работу. Проверяя показания двух датчиков при опросе дважды в минуту, я не сразу заметил сильное расхождение в их показаниях. Сомнения заставили меня провести ряд измерений, при которых я менял положение макетной платы с установленными на ней датчиками температуры. Вот какие результаты у меня получались: № опыта 1 2 3 4 Датчик Показания датчика Т1 Т2 Т1 Т2 Т1 Т2 Т1 Т2 023,3750 023,5000 023,2500 023,4375 023,4375 023,6875 023,9375 023,5625 Разность показаний Т1-Т2 -0,1250 Положение платы -0,1875 -0,2500 +0,3750 Я не стал бы обращать на это внимание, если бы не одно обстоятельство – я предположил, что точность измерения температуры будет порядка 0,10С. Это не более, чем предположение, основанное на том, что ранее я получил почти одинаковые (иногда одинаковые) показания двух датчиков. Если теперь это не так, то моё предположение может быть неверно. Впрочем, на показания датчиков может повлиять то, что батарейки, от которых питается и микроконтроллер, и датчики, могут сильно «постареть» за прошедшее время. Хотя питание МК стабилизировано на макете с помощью импульсного преобразователя, но его возможности тоже не безграничны. На разброс показаний может повлиять тот факт, что датчики смонтированы на беспаечной макетной плате. Потеря контакта может влиять на показания. В это мне, правда, верится мало – токи, потребляемые датчиками, не столь велики, чтобы сказывалось переходное сопротивление контактов, а в переходное сопротивление в несколько килоом или более, я не очень верю. И, последнее, на расхождение в показаниях датчиков температуры может сказываться естественная неоднородность температуры. Макетная плата лежит на рабочем столе рядом с монитором компьютера. 196
В.Н. Гололобов Микроконтроллер глазами начинающего Так что же происходит? Я хочу повторить эксперимент, уменьшив паузу между двумя измерениями до 3 секунд – ждать полминуты терпения, пожалуй, не хватит. В этот раз результаты получаются следующими: № опыта 1 2 3 4 Датчик Показания датчика Т1 Т2 Т1 Т2 Т1 Т2 Т1 Т2 023,7500 023,8125 023,7500 023,8125 023,8125 023,8750 023,9375 023,9375 Разность показаний Т1-Т2 -0,0625 Положение платы -0,0625 -0,0625 0,0000 Теперь я больше склонен считать, что разность измерений в первом эксперименте могла возникать из-за того, что температура в ту минуту, что проходила между двумя измерениями показаний датчика могла меняться на несколько десятых градуса: сквозняк, работа вентилятора компьютера или мои неосторожные движения. Благодаря большому количеству запасных деталей конструктора Robopica, я собрал вентилятор и нагреватель (резистор 20 Ом, 5 Вт) в виде удобном для работы. Первый эксперимент при питающем напряжении 12 В. Рис. 16.1. Проведение первого эксперимента с вентилятором и нагревателем 197
В.Н. Гололобов Микроконтроллер глазами начинающего Результаты эксперимента через 10 минут получились следующими: № опыта 1 Датчик Показания датчика Т1 Т2 024,3750 025,0625 Разность показаний Т2-Т1 0,6875 Температура нагревателя 530С Спустя 30 минут показания датчика можно прочитать на рисунке выше, температура нагревателя не изменилась. Температуру нагревателя я измерял мультиметром. Кстати, мощность, рассеиваемая нагревателем (ток от источника питания, потребляемый нагревателем и вентилятором 0,67 А) составила около 7 Вт. Это почти вдвое превышает допустимую мощность рассеивания резистора. Данные эксперимента по температуре нагревателя интересны только в данный момент, но сам эксперимент можно повторить, когда вам понадобится использовать вентилятор для охлаждения полупроводников: транзистора, диода, триака и т.д. И первые выводы. Если забирать воздух из комнаты, комнатная температура в эксперименте была около 24 0С, то поднять температуру с помощью использованного нагревателя получится не более чем на 1-2 градуса. Продувая воздух вентилятором, мы можем получить на расстоянии 15 см разность температур около 0,50С. Хорошо, теперь попробуем повторить этот эксперимент в замкнутом (относительно замкнутом) и изолированном объёме. В качестве этого объёма я использую картонную коробку объёмом около 0,016 м3. Внешняя температура пусть будет 23,80С (из полученных ранее таблиц). Через 15-20 минут после начала эксперимента температура в коробке поднялась до 27,80С. После выключения подогрева через 15 минут температура опустилась до 24,70С. Таким образом, в замкнутом объёме мой подогреватель способен увеличить температуру на 4 0С по сравнению с окружающей температурой. Следующий эксперимент я намереваюсь провести с меньшим объёмом – 0,0032 м3. Эксперименты я планирую провести с напряжением питания 6, 8 и 10 В. Начальная температура в этот раз 22,20С. № опыта 0 1 2 3 4 Датчик Показания датчика, град. Т1 Т2 Тсредн Тсредн Тсредн Т1 Т2 022,1875 022,2500 24,2 26 28 28,5625 28,7500 Напряжение питания Интервал времени, нагревателя, В минут 0 старт 6 8 10 10 198 10 10 10 10
В.Н. Гололобов Микроконтроллер глазами начинающего В следующем эксперименте я хочу снижать напряжение питания на вольт и через каждую минуту, не меняя напряжение, наблюдать за показаниями термометров до некоторой стабилизации температуры. Итак. № опыта Показания датчика Т1, град. Показания датчика Т2, град. 0 1 028,5625 028,4375 028,3750 028,3125 028,2500 028,2500 028,2500 028,2500 028,1250 028,0625 028,0000 027,9375 027,8750 027,8125 027,8125 027,7500 027,7500 027,7500 028,8125 028,6875 028,5625 028,5000 028,4375 028,4375 028,4375 028,4375 028,3750 028,3125 028,2500 028,1250 028,0625 028,0000 028,0000 028,0000 027,9375 027,9375 2 Напряжение питания нагревателя, В 10 9 9 9 9 9 9 9 8 8 8 8 8 8 8 8 8 8 Интервал времени, минут старт 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 Промежуточные выводы по результатам наблюдений: 1. Выбранный тип и сопротивление нагревателя позволяют поднять температуру небольшого объёма на 60С выше температуры окружающей среды. При лучшей герметизации и теплоизоляции этот показатель может измениться. 2. Разница в показаниях двух датчиков, разнесённых на расстояние 15 см, достигает 0,25 0С. Что может свидетельствовать как о неоднородности температур в заданном объёме, так и о погрешности измерений датчиков. Если принять ошибку каждого из датчиков ±0,0625 0С, то суммарная погрешность в показаниях может достигать 0,1250 0С. В этом случае разница температур в двух точках на расстоянии 15 см будет равна 0,12500С. 3. При снижении напряжения питания на 1 В температура достигает стабилизации через 3-5 минут и зависит от стартовой температуры. Можно предположить, что обратный процесс происходит в том же темпе. При появлении сомнений в этом предположении можно провести эксперимент, который подтвердит или опровергнет это предположение, эксперимент подобный вышеприведённому. 4. Если использовать один датчик температуры в системе регулировки температуры, то величину 0,0625-0,12500С можно взять за порог срабатывания. 5. Выбранной температуре соответствует питающее напряжение 8-10 В. Из ранее проведённых экспериментов с вентилятором я готов предположить, что скважность импульсов следует выбрать 70-80%. 199
В.Н. Гололобов Микроконтроллер глазами начинающего 6. При проведении последующих экспериментов следует использовать объём термостата, использованный в данном эксперименте, и выбрать температуру 270С в качестве температуры, которую следует поддерживать в термостате. Предварительные данные, которые необходимы для выбора схемы и создания программы термостата:      Ток, потребляемый схемой, не более 0,7 А. Скважность импульсов меняется от 70 до 80% при изменении температуры. Поддерживаемая температура 270С. Верхний порог переключения скважности к 70% 27, 06250С. Нижний порог переключения скважности к 80% 26,93750С. Для управления вентилятором с параллельно включённым нагревателем используем транзистор КТ815 или КТ817, какой обнаружится в запасе. Чтобы избежать перегрузки выхода МК и инвертирования сигнала, к выводу МК подключим транзистор КТ503 или аналогичный. В этом случае схема будет выглядеть: Рис. 16.2. Схема следующего эксперимента Пока схема не ожила даже на компьютере. Чтобы «оживить» её, необходимо написать программу, которая будет выполнять все необходимые операции. Программу считывания температуры и вывода этого значения на дисплей мы возьмём из ранее написанного фрагмента. 200
В.Н. Гололобов Микроконтроллер глазами начинающего Можно, конечно, сразу начать переделывать программу, которую мы использовали в предыдущем эксперименте, убирая лишнее, дописывая недостающее. Но я поступлю осторожнее: я разобью задачу на подзадачи, чтобы проверить всё по отдельности, а затем объединить полученные фрагменты общей задачи воедино. Первая подзадача, которую я хочу проверить за компьютером, а потом на макетной плате – это задача управления вентилятором (и обогревателем) при изменении скважности импульсов 70% и 80% с помощью модуля ШИМ (PWM) микроконтроллера PIC16F887. У этой модели два модуля, первый из которых имеет выход на вывод 17. Программу управления модулем я, не мудрствуя лукаво, возьму из примеров среды программирования MicroC Pro: unsigned short current_duty, current_duty1; void InitMain() { ANSEL = 0; ANSELH = 0; C1ON_bit = 0; C2ON_bit = 0; PORTA = 255; TRISA = 255; PORTB = 0; TRISB = 0; PORTC = 0; TRISC = 0; TRISD.F0 = 0; TRISD.F1 = 0; PORTD.F0 = 1; PORTD.F1 = 0; PWM1_Init(1500); // Configure AN pins as digital // Disable comparators // configure PORTA pins as input // set PORTB to 0 // designate PORTB pins as output // set PORTC to 0 // designate PORTC pins as output // Настройка для работы // микросхемы L293D // Initialize PWM1 module at 1500Hz } void main() { InitMain(); current_duty = 204; current_duty1 = 178; // initial value for current_duty // initial value for current_duty1 PWM1_Start(); PWM1_Set_Duty(current_duty); // start PWM1 // Set current duty for PWM1 while (1) { Delay_ms(2000); PWM1_Set_Duty(current_duty1); Delay_ms(2000); PWM1_Set_Duty(current_duty); } // endless loop // Change current duty for PWM1 // Change current duty for PWM1 } Этот пример пришлось править, но в сторону удаления лишнего. Я хочу, задав начальную скважность, менять её каждые 2 секунды. Я хочу увидеть это на экране монитора. Я хочу увидеть это на осциллографе. На экране монитора: 201
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 16.3. Проверка работы ШИМ в Proteus А с наблюдением сигнала на экране осциллографа возникают некоторые проблемы. Я много раз советовал всем, работая над проектами, не спешить. Когда вы меняете что-то в проекте, то создайте новую папку, где и сохраните новый проект. Если бы я сам следовал своим советам… Первые глюки возникли в работе среды программирования MicroC Pro, уж не знаю, что не понравилось ей. Но правильно написанный код стал работать «криво». Мало того, при проверке программы в Proteus к прежним глюкам добавились новые – вместо нужного мне проекта, похоже, работает совсем другой, хотя я старательно указываю нужный мне проект. Эта проблема решилась просто: создаём для проекта новую папку, создаём новый проект, и всё работает должным образом. Правда, в текст программы приходится внести некоторые коррективы, связанные с особенностью макетной платы, которую я использую. Плата от конструктора Robopica ориентирована на подключение двигателей к модулям PWM. А последние подключаются через управляющую микросхему L293D. Вдобавок параллельно двигателям включён конденсатор. И частоту повторения импульсов приходится увеличить до 1500 Гц. За компьютером (и, наверное, ранее) я проверял работу программы с частотой 500 Гц. Однако поиск источника проблем не бесконечен, всё когда-то заканчивается. На экране осциллографа: Рис. 16.4. Проверка работы ШИМ осциллографом 202
В.Н. Гололобов Микроконтроллер глазами начинающего Теперь, когда завершена проверка программы управления вентилятором, задача немного меняется. Я не проверял работу вентилятора при частоте повторения импульсов 1500 Гц, но только при 500 Гц. Вдобавок конденсатор… если я включу транзисторный ключ (посмотрите на сигнал последней картинки), то работать он будет довольно плохо (если будет работать, в чём я очень сомневаюсь). Есть два варианта: либо припаять провод к нужному выводу МК, либо собрать новый макет с другой моделью микроконтроллера. Я очень не люблю что-то переделывать в готовом изделии (а макетную плату я использую готовую, собранную для робота Robopica). Но припаять один провод для получения нужного мне сигнала управления, это мне кажется быстрее, чем собрать полностью макетную плату (хотя и этого, возможно, не избежать). И ещё, что меня не устраивает в том, что я увидел на экране осциллографа? Давайте, разберёмся. Я постараюсь промоделировать сигнал от микроконтроллера через микросхему управления двигателем: Рис. 16.5. Моделирование управления вентилятором через L293D Верхняя диаграмма показывает сигнал на входе управления вентилятором, который похож на осциллограмму рисунка 16.4. Нижняя диаграмма показывает мощность рассеивания на транзисторе Т2. 203
В.Н. Гололобов Микроконтроллер глазами начинающего Можно убедиться, что мощность, рассеиваемая на коллекторе транзистора Т2, превышает 1 Вт. В этом случае необходимо устанавливать транзистор на радиаторе. Кроме того, напряжение на вентиляторе (и, особенно, нагревателе) будет отличаться от тех значений, что мы получали в предыдущих экспериментах. Если использовать сигнал с выхода микроконтроллера, то ситуация, может быть, изменится к лучшему? Рис. 16.6. Моделирование управления вентилятором с выхода МК Моделирование показывает, что мощность, рассеиваемая на коллекторе транзистора Т2, существенно снизилась и не превышает 100 мВт. Транзистор КТ815 можно использовать без радиатора. И падение напряжения на нагрузке (средняя диаграмма) практически равно напряжению питания. Если бы не нагреватель, то вентилятор вполне можно было бы подключить к микросхеме L293D. Но с нагревателем вентилятор может потреблять ток более 600 мА, а этот ток является максимально допустимым для микросхемы. Для многих, кто увидел осциллограмму сигнала на выходе микросхемы L293D, всё, что я написал выше, было очевидно. И я не исключаю, что можно улучшить ситуацию иными методами, но я выбрал то, что выбрал. 204
В.Н. Гололобов Микроконтроллер глазами начинающего Можно убедиться, что сигнал на выводе МК – это прямоугольные импульсы хорошей формы. Рис. 16.7. Сигнал управления на выводе микроконтроллера Всю эту часть главы я не должен был оставлять. Её следовало выбросить, решить все проблемы, затем начисто написать эту часть главы. Всё было бы правильно. И я был бы «белый и пушистый». Но я оставлю всё как есть, потому что в работе очень часто приходится сталкиваться с похожими проблемами. Можно, конечно, придумывать проблемы, но зачем, если они появляются сами, непридуманные? 205
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 17. Опыты с термостатом Почти все подзадачи были решены в предыдущей главе. Осталось сократить текст программы для работы с одним датчиком температуры и дописать тот фрагмент, который будет отвечать за поддержание заданной температуры. Сам фрагмент задания порогов прост: используем if (условие)… Что-то… Интерес в данном фрагменте представляет условие. Мы получаем данные от датчика, преобразуем их, в основном, для вывода на дисплей. Преобразуем в символьный вид. Но для проверки условия нам нужно сравнить два числа: число, присвоенное переменной при чтении температуры, и число, которое мы задаём в качестве порога. В предыдущей главе я приводил пороговые значения, но в форме понятной мне, а не процессору: 27, 0625 0С и 26,93750С. Я, как и многие, думаю и соображаю в тех числах, к которым привык. Чтобы разобраться, как следует записывать условие, запишем фрагмент программы, где мы считываем значение температуры с единственного датчика и выводим его на дисплей. // Lcd задание sbit LCD_RS at sbit LCD_EN at sbit LCD_D7 at sbit LCD_D6 at sbit LCD_D5 at sbit LCD_D4 at выводов RD2_bit; RD3_bit; RD7_bit; RD6_bit; RD5_bit; RD4_bit; // Направление выводов sbit LCD_RS_Direction at sbit LCD_EN_Direction at sbit LCD_D7_Direction at sbit LCD_D6_Direction at sbit LCD_D5_Direction at sbit LCD_D4_Direction at TRISD2_bit; TRISD3_bit; TRISD7_bit; TRISD6_bit; TRISD5_bit; TRISD4_bit; // Задание TEMP_RESOLUTION для использования датчика DS18x20: // 18S20: 9 (default setting; can be 9,10,11,or 12) // 18B20: 12 const unsigned short TEMP_RESOLUTION = 12; char *text = "000.0000"; unsigned temp; void Display_Temperature(unsigned int temp2write) { const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8; char temp_whole; unsigned int temp_fraction; // Проверка, не отрицательная ли температура if (temp2write & 0x8000) { text[0] = '-'; temp2write = ~temp2write + 1; } 206
В.Н. Гололобов Микроконтроллер глазами начинающего // Образование temp_whole temp_whole = temp2write >> RES_SHIFT ; // Преобразование temp_whole в символьный вид if (temp_whole/100) text[0] = temp_whole/100 + 48; else text[0] = '0'; text[1] = (temp_whole/10)%10 + 48; text[2] = temp_whole%10 + 48; // Выделение цифры десятков // Выделение цифры единиц // Выделение temp_fraction и преобразование в unsigned int temp_fraction = temp2write << (4-RES_SHIFT); temp_fraction &= 0x000F; temp_fraction *= 625; // Convert temp_fraction to characters text[4] = temp_fraction/1000 + 48; text[5] = (temp_fraction/100)%10 + 48; text[6] = (temp_fraction/10)%10 + 48; text[7] = temp_fraction%10 + 48; // // // // Выделение Выделение Выделение Выделение цифры цифры цифры цифры тысяч сотен десятков единиц // Вывод температуры на LCD Lcd_Out(2, 5, text); } void main() { ANSEL = 0; ANSELH = 0; C1ON_bit = 0; C2ON_bit = 0; // Конфигурируем выводы AN как цифровые I/O // Компараторы выключаем Lcd_Init(); // Lcd_Cmd(_LCD_CLEAR); // Lcd_Cmd(_LCD_CURSOR_OFF); // Lcd_Out(1, 1, " Temperature: "); // Вывод символа градусов для шкалы Цельсия Lcd_Chr(2,13,223); // Разные LCD имеют разные Инициализация LCD Очистка LCD Выключение курсора коды для символов Lcd_Chr(2,14,'C'); //--- Основной цикл do { //--- Подготовка чтения температуры Ow_Reset(&PORTE, 2); // Onewire сигнал сброса Ow_Write(&PORTE, 2, 0xCC); // Использование команды SKIP_ROM Ow_Write(&PORTE, 2, 0x44); // Использование команды CONVERT_T Delay_us(120); Ow_Reset(&PORTE, 2); Ow_Write(&PORTE, 2, 0xCC); Ow_Write(&PORTE, 2, 0xBE); // Использование команды SKIP_ROM // Использование команды READ_SCRATCHPAD 207
В.Н. Гололобов Микроконтроллер глазами начинающего temp = Ow_Read(&PORTE, 2); temp = (Ow_Read(&PORTE, 2) << 8) + temp; //--- Форматирование и отображение результата на дисплее Display_Temperature(temp); Delay_ms(30000); } while (1); } В этом фрагменте не используется адрес датчика, поскольку он один. Формирование полученных данных происходит в двух строках текста программы: temp = Ow_Read(&PORTE, 2); temp = (Ow_Read(&PORTE, 2) << 8) + temp; Две строки, как я полагаю, нужны для того, чтобы прочитать два регистра датчика температуры и сформировать значение в одной переменной. Я почти уверен в этом, но лучше это проверить. Однако с этим есть определённые трудности. Что за трудности? Для получения hex-файла, загружаемого в микроконтроллер, я использовал программу MicroC Pro. Отладчик программы, как я понимаю, не имеет представления о датчиках температуры. Я много раз говорил об удобстве проверки в программе ISIS (Proteus). И я могу проверить работу в этой программе, получив при трансляции hex-файл. Но проверить переменные я, увы, не смогу. Для пошаговой отладки в Proteus мне нужен отладочный файл с расширением cof. Но этого файла при трансляции в MicroC я не получаю. Иначе отладка выглядела бы так: Рис. 17.1. Пошаговая отладка в ISIS 208
В.Н. Гололобов Микроконтроллер глазами начинающего Нужный мне файл я мог бы получить, используя другую среду разработки, например, Flowcode. Рис. 17.2. Программа считывания температуры в Flowcode 5 Эту среду разработки я считаю очень удачной в первую очередь для начинающих (хотя и опытным пользователем она будет и полезна, и удобна). Какие сомнения возникают у меня в этом случае? Программа Flowcode использует датчик температуры DS1820. В описании этого датчика температуры написано, что градация считывания температуры 0.50С. И нужных мне значений я могу не получить даже в том случае, если программу в Flowcode соберу правильно. Использование компиляторов в MPLABX потребует от меня усилий по написанию программы, которые сведут на нет идею получения результата легко и непринуждённо. Я не уверен, что обдумал все возможные варианты обхода проблемы, но, видимо, самый простой путь – это открыть описание датчика DS18B20 и прочитать, как форматируются считанные данные о температуре. В итоге я предполагаю, что нужные мне значения для определения порогов срабатывания образуются числом, определяющим количество шагов градации. Поскольку шаг градации у меня при разрешении 12 бит – это 0,06250С, то, разделив значения температуры на этот шаг, я должен получить нужное мне десятичное число: 27, 0625/0,0625 = 433 26,9375/0,0625 = 431 Видимо, прямой путь в очередной раз оказался самым коротким. Теперь я могу дописать условие изменения скважности импульсов, дописать управление модулем PWM и, оттранслировав программу, загрузить её в микросхему. 209
В.Н. Гололобов Микроконтроллер глазами начинающего Перед записью программы в микросхему я хочу проверить работу усилителя для вентилятора и подогревателя, поскольку это была последняя «зашитая» в контроллер программа. Вот осциллограмма результата проверки: Рис. 17.3. Осциллограммы двух режимов работы вентилятора Вентилятор «попискивает» на частоте 1500 Гц, но работает. Хотелось бы, конечно, чтобы падение напряжения на транзисторе Т2 в открытом состоянии было меньше. Для этого можно уменьшить сопротивление между коллектором первого и базой второго транзистора. Что можно ожидать от этого решения? Вот: Рис. 17.4. Изменение выходного импульса Падение напряжения на коллекторе транзистора Т2 в первом случае 1,3 В, во втором – 0,2 В. И я проверю это на макетной плате, поскольку выходное напряжение в первом случае меньше расчётного, вдобавок транзистор будет греться больше, чем этого хотелось бы. 210
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 17.5. Последняя проверка работы ключевого транзистора Теперь падение напряжения на открытом транзисторе не превышает 0,4 В, что приемлемо. Многие радиолюбители, повторяя готовую схему, довольствуются тем, что она заработала на макетной плате. Затем они старательно изготавливают печатную плату, переносят «работающую» схему на плату, не менее старательно делают корпус, а потом долго удивляются, отчего схема перестала работать. Всё, что я привёл выше, я сделал для того, чтобы объяснить, какие причины могут вызвать отказ работы схемы, которая заработала на макетной плате. Впрочем, я надеюсь, что вы не из числа тех, кто бездумно повторяет чужие разработки. Проведя предварительные эксперименты, можно приступить к основному опыту: собрать программу, запрограммировать микросхему и проверить работу термостата. Вот собранная программа: // Lcd задание sbit LCD_RS at sbit LCD_EN at sbit LCD_D7 at sbit LCD_D6 at sbit LCD_D5 at sbit LCD_D4 at выводов RD2_bit; RD3_bit; RD7_bit; RD6_bit; RD5_bit; RD4_bit; // Направление выводов sbit LCD_RS_Direction at sbit LCD_EN_Direction at sbit LCD_D7_Direction at sbit LCD_D6_Direction at sbit LCD_D5_Direction at sbit LCD_D4_Direction at TRISD2_bit; TRISD3_bit; TRISD7_bit; TRISD6_bit; TRISD5_bit; TRISD4_bit; // Задание TEMP_RESOLUTION для использования датчика DS18x20: // 18S20: 9 (default setting; can be 9,10,11,or 12) // 18B20: 12 const unsigned short TEMP_RESOLUTION = 12; char *text = "000.0000"; unsigned temp; 211
В.Н. Гололобов Микроконтроллер глазами начинающего unsigned short current_duty, current_duty1; void InitMain() { ANSEL = 0; ANSELH = 0; C1ON_bit = 0; C2ON_bit = 0; PORTA = 255; TRISA = 255; PORTB = 0; TRISB = 0; PORTC = 0; TRISC = 0; TRISD.F0 = 0; TRISD.F1 = 0; PORTD.F0 = 1; PORTD.F1 = 0; PWM1_Init(1500); // Configure AN pins as digital // Disable comparators // configure PORTA pins as input // set PORTB to 0 // designate PORTB pins as output // set PORTC to 0 // designate PORTC pins as output // Настройка для работы // микросхемы L293D // Initialize PWM1 module at 1500Hz } void Display_Temperature(unsigned int temp2write) { const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8; char temp_whole; unsigned int temp_fraction; // Проверка, не отрицательная ли температура if (temp2write & 0x8000) { text[0] = '-'; temp2write = ~temp2write + 1; } // Образование temp_whole temp_whole = temp2write >> RES_SHIFT ; // Преобразование temp_whole в символьный вид if (temp_whole/100) text[0] = temp_whole/100 + 48; else text[0] = '0'; text[1] = (temp_whole/10)%10 + 48; text[2] = temp_whole%10 + 48; // Выделение цифры десятков // Выделение цифры единиц // Выделение temp_fraction и преобразование в unsigned int temp_fraction = temp2write << (4-RES_SHIFT); temp_fraction &= 0x000F; temp_fraction *= 625; // Convert temp_fraction to characters text[4] = temp_fraction/1000 + 48; // Выделение цифры тысяч text[5] = (temp_fraction/100)%10 + 48; // Выделение цифры сотен text[6] = (temp_fraction/10)%10 + 48; // Выделение цифры десятков 212
В.Н. Гололобов text[7] = Микроконтроллер глазами начинающего temp_fraction%10 + 48; // Выделение цифры единиц // Вывод температуры на LCD Lcd_Out(2, 5, text); } void main() { InitMain(); current_duty = 204; current_duty1 = 178; // initial value for current_duty // initial value for current_duty1 PWM1_Start(); PWM1_Set_Duty(current_duty); // start PWM1 // Set current duty for PWM1 Lcd_Init(); // Lcd_Cmd(_LCD_CLEAR); // Lcd_Cmd(_LCD_CURSOR_OFF); // Lcd_Out(1, 1, " Temperature: "); // Вывод символа градусов для шкалы Цельсия Lcd_Chr(2,13,223); // Разные LCD имеют разные Инициализация LCD Очистка LCD Выключение курсора коды для символов Lcd_Chr(2,14,'C'); //--- Основной цикл do { //--- Подготовка чтения температуры Ow_Reset(&PORTE, 2); // Onewire сигнал сброса Ow_Write(&PORTE, 2, 0xCC); // Использование команды SKIP_ROM Ow_Write(&PORTE, 2, 0x44); // Использование команды CONVERT_T Delay_us(120); Ow_Reset(&PORTE, 2); Ow_Write(&PORTE, 2, 0xCC); Ow_Write(&PORTE, 2, 0xBE); // Использование команды SKIP_ROM // Использование команды READ_SCRATCHPAD temp = Ow_Read(&PORTE, 2); temp = (Ow_Read(&PORTE, 2) << 8) + temp; //--- Форматирование и отображение результата на дисплее Display_Temperature(temp); Delay_ms(5000); if (temp >= 433) if (temp <= 431) } while (1); PWM1_Set_Duty(current_duty1); PWM1_Set_Duty(current_duty); } Проверкой этой программы мы и займёмся. 213
В.Н. Гололобов Микроконтроллер глазами начинающего Если вы ещё не привыкли считать компьютер самой удобной макетной платой, то я давно этим пользуюсь и не намерен от этой привычки избавляться: меньше риск сжечь что-то на реальной макетной плате. Рис. 17.6. Работа схемы при разогреве термостата Рис. 17.7. Работа схемы при достижении температуры переключения Осциллограмма показана для подключения к выходу транзистора ключевого усилителя. Если у вас нет возможности использовать программу Proteus, то проверьте программу без подключения силовой части. Надеюсь, микроконтроллер на макетной плате вы смонтировали правильно, в этом случае нет причин опасаться, что вы его выведете из строя. Подключите вентилятор с нагревателем к источнику питания 12 В, проверьте, будет ли меняться скважность импульсов. Если у вас нет осциллографа (а можно в качестве осциллографа в нашем случае использовать звуковую карту, об этом я писал в книге «Самоучитель игры на паяльнике»), добавьте в две проверки температуры отладочные вставки, скажем, такие: 214
В.Н. Гололобов Микроконтроллер глазами начинающего if (temp >= 433) { PWM1_Set_Duty(current_duty1); PORTC.F0 = 0; } if (temp <= 431) { PWM1_Set_Duty(current_duty); PORTC.F0 = 1; } Так вы сможете контролировать состояние, хотя и косвенно, нагревателя и вентилятора. Конечно, следует проверить TRISC. Но в нашей программе весь порт работает на выход. Подключите к выводу RC0 светодиод с резистором, проверьте работу программы. А проверку ключевого усилителя можно осуществить либо в бесплатной программе Qucs, как сделал я, либо в бесплатной версии программы TINA-TI, либо в другой программе симуляции. Завершив все предварительные проверки, можно загрузить программу в микросхему контроллера, собрать полный макет и провести испытание термостата. Мне и самому интересно, чем закончатся наши изыскания. Да, программу я загрузил, макетную плату и вентилятор с нагревателем поместил в ту же коробку, с которой проделал предыдущие опыты, но… Первый эксперимент оказался неудачным. И я сразу хочу сказать о том, в чём могли бы быть причины этой неудачи. Прошло несколько дней с момента первого эксперимента. Температура окружающего воздуха могла измениться. Пусть не столь значительно, но измениться. Коробка (замкнутый объём для размещения в нём датчика и вентилятора с нагревателем) в прошлый раз могла закрываться хуже (или лучше), чем я это сделал сейчас. И, наконец, напряжение питания я выставляю по мультиметру, но не стремлюсь к абсолютной точности. Вот неполный перечень причин, по которым первый «блин получился комом». Ещё напомню, что я плохо разбираюсь в теплотехнике! Начальная температура в первом эксперименте была около 230С. Время от предыдущего измерения (минут) 3 3 1 1 1 1 1 3 15 Температура (град. Цельсия) 025.1250 026.9375 027.1875 027.3125 027.4375 027.6250 027.6875 027.8750 028.8125 Режим работы нагревателя и вентилятора (ШИМ, %) 80 80 70 70 70 70 70 70 70 Продолжать наблюдения я не посчитал нужным, поскольку явно результаты выходили за пределы заданной температуры. Второй эксперимент, признаюсь, тоже не стал образцом для подражания. Начальная температура была около 260С, а параметры ШИМ я изменил на 80% и 50%. Через 15 минут после завершения наблюдений с интервалом в 1 минуту температура достигла 027.93750С. Получив эти разочаровывающие результаты, я изменил ШИМ, задав 50% и 30% (127 и 76 в программе) соответственно. Ещё в предыдущий раз я, программу всё равно пришлось 215
В.Н. Гололобов Микроконтроллер глазами начинающего переделывать, добавил включение светодиода (как советовал вам) для индикации переключения вентилятора (и нагревателя, конечно). И в этот раз результаты получились следующие: Время от предыдущего измерения (минут) 1 1 1 1 10 1 10 10 Температура (град. Цельсия) 026.8750 026.8750 026.8750 027.0625 027.0000 026.9375 027.0000 027.0625 Режим работы нагревателя и вентилятора (ШИМ, %) 50 50 50 30 30 50 30 30 Признаться, я почти не верил в то, что с помощью того устройства, что я придумал, температуру можно поддерживать в интервале ±0,050С. Согласен, 30 минут – это не показатель для того, чтобы считать эксперимент удачным. Но и все проделанные ранее эксперименты, и последние эксперименты – это только первый этап разработки. И обычная упаковочная коробка – не корпус хорошего термостата. Однако работа проделана не зря, она оставляет надежду на осуществление удачной разработки, она даёт хотя бы первичные данные, с которыми можно работать дальше. Для реального термостата нужен реальный объём, в котором будет поддерживаться температура; нужна реальная температура, которую следует поддерживать; нужен нагреватель и вентилятор, которые обеспечат заданную температуру. Всё ещё предстоит сделать и проверить, как всегда. 216
В.Н. Гололобов Микроконтроллер глазами начинающего Глава 18. Другой способ измерения температуры и другие задачи В предыдущих главах я рассказывал о работе с датчиком температуры DS18B20. В первую очередь это связано с моим желанием показать, что такие таинственные атрибуты работы с микроконтроллером, как протокол 1-wire, загадочен не более, чем любая другая программа для микроконтроллера. Всё зависит от вас. Если вы хотите самостоятельно осуществить этот протокол, то придётся много поработать. Если хотите воспользоваться уже проделанной работой программистов, то вам достаточно проделать простые операции по написанию текста программы. Хотя, конечно, познакомиться с протоколом следует. Этот протокол используют разные внешние (по отношению к микроконтроллеру) устройства. И не важно, датчик ли это температуры или другое устройство, могут измениться команды, но суть останется той же. Но может статься, что вам не нужно измерять температуру с такой точностью. Например, для комнатного или уличного термометра достаточно отображать изменение температуры на градус. В этом случае можно использовать другой модуль, часто встраиваемый в микроконтроллер – АЦП. В качестве датчика хорошо подойдёт диод (или переход эмиттер-база транзистора), включённый в прямом направлении. Рис. 18.1. Симуляция эксперимента с диодом В таблице показаны ток через диод и падение напряжения на нём при изменении температуры от 20 до 400С. Ниже на графике показана зависимость падения напряжения на диоде при изменении температуры. График показателен в том отношении, что зависимость эта линейная. По таблице можно ориентироваться на то, как изменяется напряжение при изменении температуры на один 217
В.Н. Гололобов Микроконтроллер глазами начинающего градус (примерно 2мВ/град). И остаётся повторить эксперименты аналогичные проделанным в этой книге для получения реальных данных. Если вы приобрели датчик DS18B20, проделали все эксперименты с ним, то у вас есть хороший термометр, с помощью которого вы можете откалибровать новое устройство. Расчёт температуры по напряжению аналогичен тому, как мы это делали раньше. И вывод на дисплей не должен вас затруднить. При использовании среды разработки MicroC вы можете применить готовые библиотечные функции для создания кода программы считывания напряжения с помощью АЦП. Если у вас есть возможность использовать программу Flowcode, то задача решается ещё проще. Рис. 18.2. Сборка программы считывания напряжения в Flowcode В этой же программе вы может использовать дисплей для отображения полученного результата. Ещё один из удобных вариантов – это использовать модуль Arduino. Программ, поддерживающих этот проект, и все они бесплатные, несколько. Основная программа работает как в Windows, так и в Linux. Модуль соединяется по USB с компьютером и, что немаловажно, вам не понадобится программатор. Язык программирования основной программы создан на основе языка Си, хорошая возможность познакомиться и с языком, благо в руководстве программиста приведены примеры всех типовых использований конструкций языка с модулем Arduino. Прочитать об этом модуле, не буду повторяться, можно в моей книге «С чего начинаются роботы?». Её легко найти в Интернете или на моём сайте: http://vgololobov.narod.ru. Значительную часть работы вы можете проделать за компьютером, проверяя то, что вызывает сомнения, на макетной плате. Модуль построен на базе микроконтроллера Atmel AVR, и у вас будет возможность познакомиться с микроконтроллером другого производителя. Стоит модуль не так дорого, есть смысл подумать о его приобретении, если у вас есть желание познакомиться с микроконтроллерами. С момента написания рассказа об Arduino прошло не так много времени, но всё меняется достаточно быстро. Базовая программа, которая позволит вам работать с модулем Arduino, изменилась не столь сильно, хотя версии обновились основательно. Вместе с тем удобная виртуальная плата, которая раньше была бесплатной и работала с Arduino, изменилась. Прежние версии в Windows 8 работать не захотели, а новая версия выглядит так: 218
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 18. 3. Версия VBB 4.22 Можно использовать (загрузив, например, но можно вначале попробовать и без загрузки этого пакета) рекомендации бесплатного использования программы: Рис. 18.4. Примеры работы с Arduino Найти VBB (virtualbreadboard) можно на сайте: http://www.virtualbreadboard.com/ Для Windows (возможно) потребуется установка дополнительных программ, о чём можно прочитать в руководстве. Так мне для Win8 потребовалось загрузить с сайта Microsoft файл, управляющий работой программ на J# в реальном времени. Классический вариант мигающего светодиода будет выглядеть так: 219
В.Н. Гололобов Микроконтроллер глазами начинающего Рис. 18.5. Мигание светодиода на виртуальной макетной плате Насколько удобно использовать теперь виртуальную плату я сказать не могу, поскольку, видимо, программа для Arduino написана на Java. Но получить некоторый опыт без обращения к реальному устройству, думаю можно. В любом случае вы можете посмотреть, что из этого опыта получится, программа не очень большая, загрузить её можно даже в случае плохого доступа к Интернету. Запустите программу, выберите закладку с существующими проектами, где в диалоговом окне выберите рекомендуемый вариант (как на одном из предыдущих рисунков) Frappuccino. Вот что можно прочитать об этом в вопросах и ответах сайта VBB: Что такое Frappuccino? Frappuccino – это открытый проект на Java, порождённый проектом Arduino, который портирует структуру и библиотеки на язык Java вместе с библиотеками для разработки в Processing IDE. Arduino базируется на Processing и уже наследует Java. Frappuccino – это расширение, чтобы можно было работать с Arduino на Java. Frappuccino позволяет вам разрабатывать приложения в стиле Arduino в таких средствах Java, как Processing, Eclipse, Netbeans и также VirtualBreadboard. Могу ли я запустить примеры Frappuccino в VBB? Да, в стандартной версии VirtualBreadboard поддерживает Frappucino, так что всякий может запустить примеры Frappuccino. Поскольку Frappuccino – это Java, приложения могут эмулироваться виртуальной машиной Java компьютера. И это значит, что приложения Frappuccino эмулируются? Да, виртуальная Java машина компьютера выполняет java код, так что приложения будут эмулироваться компьютером. Что и согласуется с философией, 220
В.Н. Гололобов Микроконтроллер глазами начинающего что однажды написанное на Java будет запускаться где бы то ни было. Действительно, виртуальная Java машина сама по себе – это абстрактный процессор эмуляции, определённый в спецификации Java Virtual Machine. Во многом приведённые примеры соотносятся с теми, что есть в программе Arduino. И основные приёмы работы можно осваивать, используя наглядные примеры VBB, кодируя программу в Arduino и проводя испытания с реальным модулем. Например, вы можете использовать такую программу: Рис. 18.6. Программа «мигалки» с управляемым временем переключения Возможно, вам не понравилось всё, о чём я сказал, но я уверен, что вам во всём этом полезно будет разобраться, чтобы извлечь максимум пользы. Есть ещё одна из программ симулятора Arduino, но и она в бесплатном варианте выглядит не столь дружелюбно, как хотелось бы: Рис. 18.7. Запуск программы моделирования для Arduino 221
В.Н. Гололобов Микроконтроллер глазами начинающего Это сообщение вы будете получать при каждой загрузке программы или примера. Вдобавок, запустив программу после установки, я получаю предложение внести изменения в настройки Windows: Рис. 18.8. Требование изменения настроек операционной системы Не знаю как вам, а мне это не пришлось по душе. Я уже говорил, что не так важно, какую задачу решать, когда начинаешь учиться программировать микроконтроллеры. Так, проведя эксперименты по считыванию напряжения с диода в качестве датчика температуры, вы легко можете заменить его фоторезистором. С его помощью вы можете определять освещённость. Пределы измерения напряжения изменятся, но суть всего происходящего останется той же. Если сделать это устройство автономным, в качестве исполняющего устройства использовать светодиодный светильник, то можно сделать освещение в коридоре, задавая включение светильника тогда, когда становится темно. Используя потенциометр как датчик напряжения, вы можете регулировать яркость свечения такого светодиодного светильника (используйте ШИМ!). Используя вместо светильника низковольтный маломощный паяльник, вы можете регулировать его температуру нагрева. Можете добавить к проекту датчик температуры (можно использовать диод или терморезистор) и дисплей, тогда температура паяльника будет высвечиваться. Не так важно, с решения какой задачи вы начинаете своё знакомство, важно начать это знакомство, овладеть первыми навыками программирования, а остальное зависит от ваших желаний и вашей фантазии. Придумывать свои устройства и создавать придуманное – это самое интересное занятие. При этом трудно сказать, что интереснее: придумывать или создавать? Многое из сказанного мною относительно схем можно реализовать без микроконтроллера. Но микроконтроллер позволяет быстро изменить функции и работу уже готового устройства, что либо невозможно, либо затруднительно без микроконтроллера. И если вам интересно работать с электронными устройствами, познакомиться с микроконтроллером следует обязательно. Я не сторонник моды. Пришла мода, все работают с микроконтроллерами. Не так. Просто в своей практике я сталкивался со случаями, когда очень жалел, что разработанное устройство не использовало микроконтроллер. В те времена микроконтроллеры были в диковинку, но как бы удобно было их использовать! 222