Как составить программу для контроллера

Итак, вы решили научиться программировать pic-контроллеры. Для начала поговорим о том, что вам для работы с этими контроллерами понадобится.

Контроллер работает по определённой программе, которая должна как-то в него попасть. Обычно программу в машинных кодах, готовую для записи в контроллер, называют прошивкой. Следовательно нужно какое-то устройство, которое будет записывать (на сленге обычно говорят заливать или прошивать) программу в контроллер. Такое устройство называется программатор. Подробнее о программаторах и заливке программы мы поговорим позднее, в последней части нашей эпопеи (когда уже будет что заливать), а пока давайте по-порядку — как нам эту программу написать.

Программа для контроллера — это, как я уже сказал, набор машинных кодов, записанный в файле с расширением «hex» (здесь можно почитать про формат *.hex), который и нужно заливать в контроллер с помощью программатора. Никакого другого языка контроллер не понимает. Следовательно, нужна специальная программа, которая будет переводить текст программы, написанный на каком-либо языке программирования, в машинные коды. Наиболее удобными в этом плане являются интегрированные среды разработки (IDE — integrated development environment), поскольку они могут не только осуществлять перевод текста программы в машинный код, но и производить симуляцию её работы. Причём симуляцию можно проводить пошагово, при этом можно наблюдать состояние регистров или даже менять их состояние по своему желанию. Короче, интегрированные среды помимо, собственно, компиляции (перевода в машинные коды) предоставляют отличные возможности для отладки программы.

IDE, как и программаторов, существует много. Лично я пользуюсь MPLAB и вам рекомендую, по той простой причине, что MPLAB — это IDE от самого производителя PIC-контроллеров — фирмы Microchip, поэтому имеет отличную поддержку (в том числе на русском языке, что особенно приятно). С официального сайта Microchip можно скачать и сам этот пакет, и подробное описание по работе с ним. Если не нашли или ломает искать — ссылки для скачивания здесь, правда это уже не самая свежая версия.

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

MPLAB позволяет писать программы на двух языках: СИ и Ассемблер. Интернет просто ломится от разборок СИ-шников и ассемблерщиков, которые с пеной у рта доказывают друг другу, какой язык лучше. Я отношу себя к ассемблерщикам, поэтому, естественно, расскажу почему лучше именно Ассемблер.

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

Программа на языке СИ (да и вообще на любом языке высокого уровня) — это уже набор команд не контроллера, а соответствующего языка. При компиляции каждая такая команда заменяется набором команд для контроллера, но каким именно набором команд она заменяется, — этого вы уже не знаете, это знает только разработчик языка программирования. Соответственно, невозможно понять, каким именно образом контроллер выполняет желаемое действие.

Короче говоря, в случае с языком высокого уровня вы изучаете как какой-то дядя обозвал свои способы реализации необходимых вам функций и по каким правилам их надо записывать. В данном случае можно провести следующую аналогию: вы хотите поговорить с китайцем, но вам говорят: “Китайский слишком сложный язык, но есть один дядя в Болгарии, который 20 лет жил в Китае и отлично его выучил. А болгарский язык с русским очень похожи и русскому человеку он интуитивно понятен, так что выучите болгарский, а уж дядя переведёт.”

В случае с ассемблером, вы изучаете сам контроллер и правила, по которым надо разговаривать с контроллером. При этом контроллер имеет всего-то несколько десятков команд, которые умещаются на одном листке и их легко можно окинуть одним взглядом.

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

Что нужно сделать в MPLAB, чтобы получить желанную прошивку? Как я уже сказал — подробности читайте в руководстве к IDE MPLAB, оно на русском и там всё понятно (если не понятно — идём на форум), я же только кратко перечислю самое основное и дам некоторые рекомендации.

Итак, мы установили MPLAB, хотим написать в нём программу для контроллера и получить готовую прошивку.

Сначала нужно создать проект. Для каждого проекта рекомендую заводить отдельную папку, потому что, во-первых, в проект может входить несколько файлов, а, во-вторых, сам MPLAB создаст ещё несколько вспомогательных файлов (*.lst, *.err, *.cod, *.bkx). Если несколько проектов будут в одной папке, то легко можно запутаться какие файлы к какому проекту относятся. Короче, создаём для проекта новую папку, потом запускаем MPLAB и выбираем меню Project -> New Project…

В появившемся окошке, в проводнике справа, выбираем нашу папку, в левой части (в поле под надписью File Name) пишем название будущего проекта, например my1.pjt (не забываем указать расширение), и жмём ОК.

Появляется окно с названием Edit Project. Это менеджер проекта, в котором указываются параметры проекта (какие файлы и библиотеки нужно подключить к проекту, какой будет использоваться камень, будет ли использоваться симуляция и многое другое). Находим поле ввода с названием Development Mode. Справа от этого поля есть кнопочка Change… Нажимаем.

Открывается окошко с названием Development Mode, в котором мы видим кучу вкладок. На вкладке Tools ставим галочку рядом с MPLAB SIM Simulator (грех для отладки симулятором не пользоваться), в поле ввода Processor выбираем контроллер, с которым мы будем работать. На вкладке Clock указываем какая у нас будет частота генератора. Жмём ОК. На ошибку и предупреждение не обращаем внимания, это просто нам говорят, что пока не могут создать .hex (ну правильно, у нас пока и программы нет) и что при изменении настроек надо заново перекомпилировать проект (так мы ещё вообще ни разу не компилировали).

В поле ввода Language Tool Suite выбираем Microchip.

Нажимаем кнопку с названием Add Node… В появившемся окне, в проводнике справа выбираем папку проекта, в поле ввода слева пишем как будет называться файл с текстом программы на ассемблере, например my1.asm (не забываем указывать расширение), и жмём ОК. Всё, теперь мы подключили к проекту файл my1.asm (указали, что текст программы будет в этом файле).

На этом с Edit project заканчиваем, — нажимаем ОК.

Теперь нужно, собственно, создать файл с текстом программы (в менеджере проекта мы просто указали, что текст будет в таком-то файле, но фактически этот файл ещё не создан). Для этого идём в меню File и выбираем пункт New. Откроется окошко редактора с названием Untitled1. Выбираем меню File -> Save As…, в проводнике справа указываем папку проекта, в поле ввода File Name пишем название файла, которое мы указали в менеджере проекта, то есть в нашем примере это будет my1.asm. Если всё сделано правильно, то название окошка редактора поменяется с Untitled1 на путьmy1.asm.

Вот и всё! Теперь осталось только набрать в окошке редактора текст программы, скомпилировать проект (меню Project->Build All) и, если в программе нет ошибок (что с первого раза бывает очень редко), то в папке проекта появится готовая прошивка (файл с расширением hex), которую можно заливать в контроллер.

  1. Часть 1. Необходимые инструменты и программы. Основы MPLAB
  2. Часть 2. Что такое микроконтроллер и как с ним работать
  3. Часть 3. Структура программы на ассемблере
  4. Часть 4. Разработка рабочей части программы. Алгоритмы
  5. Часть 5. Ассемблер. Организация циклов и ветвлений
  6. Часть 6. Как перевести контроллер в режим программирования и залить в него прошивку
Уроки по программированию микроконтроллеров для начинающих. – YouTube

В нашей предыдущей статье про микроконтроллеры PIC мы рассмотрели архитектуру микроконтроллеров PIC, установили необходимое для работы с ними программное обеспечение и купили программатор PicKit 3. В данной же статье мы напишем нашу первую программу мигания светодиодом для микроконтроллера PIC16F877A и рассмотрим установку в нем битов конфигурации (фьюзов).

Внешний схемы нашего первого проекта для микроконтроллера PIC16F877A

Поскольку мы используем микроконтроллер PIC16F877A вместе с компилятором XC8, то первым делом желательно изучить даташиты на них. Всегда, когда вы начинаете знакомство с новым микроконтроллером, желательно полностью прочитать даташит на него.

Программу для нашего микроконтроллера PIC16F877A мы будем писать в среде MPLAB-X

Создание нового проекта в MPLAB-X

Шаг 1. Запустите IDE MPLAB-X, в результате чего вы на экране должны увидеть следующее окно.

Главное окно программы MPLAB-X

Шаг 2. Откройте пункт меню Files -> New Project (для этой цели можно также использовать комбинацию клавиш Ctrl+Shift+N). После этого вы на экране увидите следующее всплывающее окно, в котором вам необходимо выбрать Standalone Project и нажать Next.

Создание нового проекта в MPLAB-X

Шаг 3. Теперь необходимо выбрать устройство для нашего проекта – выберем из выпадающего списка PIC16F877A. После этого нажмем на Next.

Выбор типа микроконтроллера в MPLAB-X

Шаг 4. На следующей странице нам необходимо выбрать инструменты (программатор) для нашего проекта. Выберем PicKit 3 и нажмем на Next.

Выбор типа программатора в MPLAB-X

Шаг 5. Затем необходимо выбрать компилятор – выберем XC8 и нажмем на Next.

Выбор типа компилятора в MPLAB-X

Шаг 6. На следующей странице необходимо задать имя нашего проекта и местоположение куда он будет сохраняться. Мы назвали проект Blink. Файл проекта будет сохраняться с расширением .X, что позволяет запускать его непосредственным образом из MAPLB-X. После этого нажмем на Finish.

Задание пути для сохранения проекта в MPLAB-X

Шаг 7. Поздравляем. Теперь наш проект успешно создан. Слева в окне мы увидим имя нашего проекта (у нас оно Blink), нажмите на него чтобы посмотреть все файлы, которые входят в проект.

Чтобы начать написание программы в наш проект необходимо добавить основной файл (C Main file) в каталог с нашим проектом. Для этого сделайте правый клик мыши на source file и в открывшемся меню выберете New -> C Main File как показано на следующем рисунке.

Создание основного файла программы в MPLAB-X

Шаг 8. Откроется диалоговое окно с именем нашего C-файла. Мы назвали его снова Blink, но вы можете выбрать для него любое другое имя по своему усмотрению. После ввода имени файла нажмите на finish.

Задание имени основного файла программы в MPLAB-X

Шаг 9. После того как файл C будет создан, откроется IDE с некоторым кодом в ней по умолчанию, как показано на следующем рисунке.

Код программы по умолчанию в MPLAB-X

Шаг 10. После этого можно начать написание программы в основном C файле (C-main File). Код по умолчанию, формируемый IDE, не будет нами использоваться в этом проекте, поэтому удалим его полностью.

Микроконтроллеры PIC имеют несколько мест, в которых расположены биты конфигурации, которые также называют фьюзами (fuses). Эти биты определяют глобальную логику функционирования устройства (в нашем случае микроконтроллера). С их помощью задается режим работы генератора тактовой частоты, сторожевого таймера (watchdog timer), режима программирования и защиты кода. Очень важно установить эти биты правильно иначе наш микроконтроллер не сможет нормально функционировать.

Узнать об имеющихся регистрах конфигурации можно из даташита на микроконтроллер. Важно понять, какие типы битов конфигурации присутствуют в рассматриваемом микроконтроллере и каковы их функции. Эти биты можно установить или сбросить использую инструкцию pragma.

Данная инструкция имеет следующую форму:

#pragma config setting = state|value

#pragma config register = value

где setting – описание настройки (установки), например, WDT, а state – текстовое описание необходимого состояния, например, OFF. Можно привести следующие примеры установки конфигурационных битов:

#pragma config WDT = ON // turn on watchdog timer (включить сторожевой таймер)

#pragma config WDTPS = 0x1A // specify the timer postscale value (записать значение в таймер)

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

Установка битов конфигурации (фьюзов) в MPLAB-X

Компания Microchip значительно упростила процесс установки битов конфигурации (фьюзов) в микроконтроллерах PIC, разработав для этого специальные графические инструменты. Для того, чтобы установить биты конфигурации (фьюзы) в микроконтроллерах PIC с помощью программы MPLAB-X, выполните следующую последовательность шагов.

Шаг 1. В программе MPLAB-X выберите пункт меню Window -> PIC Memory View -> Configuration Bits как показано на следующем рисунке.

Открытие окна для настройки битов конфигурации в MPLAB-X

Шаг 2. После этого внизу программы MPLAB-X откроется окно настройки битов конфигурации (Configuration Bits window) как показано на рисунке ниже. В нем вы можете установить значение каждого бита конфигурации по своему желанию. Далее мы рассмотрим установку некоторых из этих бит.

Внешний вид окна для настройки битов конфигурации в MPLAB-X

Шаг 3. Первыми битами конфигурации являются биты выбора генератора. Микроконтроллер PIC16F87XA может работать в одном из 4-х режимов генератора, которые можно установить с помощью битов FOSC1 и FOSC0:

  • LP Low-Power Crystal (маломощный генератор);
  • XT Crystal/Resonator (внешний кварцевый резонатор);
  • HS High-Speed Crystal/Resonator (высокоскоростной кварцевый резонатор);
  • RC Resistor/Capacitor (RC генератор).

Возможные частоты работы микроконтроллера в зависимости от выбранного типа генератора приведены в следующей таблице.

Возможные частоты работы микроконтроллера PIC в зависимости от выбранного типа генератора

Для нашего проекта мы будем использовать внешний кварцевый генератор на 20Mhz, поэтому нам необходимо выбрать HS из выпадающего меню.

Шаг 4. Следующим битом конфигурации является бит включения/выключения сторожевого таймера.

Сторожевой таймер (Watchdog Timer, WDT) представляет собой непрерывно работающий встроенный RC генератор, для работы которого не требуется никаких внешних компонентов. Данный RC генератор отделен от RC генератора на контакте OSC1/CLKI. Это означает что сторожевой таймер будет продолжать работать даже если на контакты OSC1/CLKI и OSC2/CLKO не подается никакой тактовой частоты. В режиме нормального функционирования сторожевой таймер формирует сигнал аппаратного сброса (Watchdog Timer Reset) при своем переполнении. И если таймер не отключен в нашей программе, то микроконтроллер будет сбрасываться каждый раз при переполнении таймера. Сторожевой таймер можно отключить при помощи очистки его бита конфигурации.

В нашей программе мы не будем использовать сторожевой таймер, поэтому в выпадающем списке напротив его бита конфигурации выберем OFF.

Шаг 5. Следующим битом является таймер по питанию (Power-up timer Bit, PWRT), который обеспечивает фиксированную задержку в 72 мс в работе микроконтроллера при подаче на него питания, что позволяет напряжению питания за это время подняться до приемлемого уровня. Когда данный бит активирован, таймер по питанию будет сбрасывать микроконтроллер до тех пор, пока питание не поднимется до необходимого уровня.

В нашей программе мы не будем использовать данную задержку, поэтому данный бит конфигурации мы также отключим (OFF).

Шаг 6. Следующим битом конфигурации является бит программирования при низком напряжении (Low-Voltage Programming, LVP). Бит LVP позволяет производить программирование микроконтроллера пониженным напряжением через разъем ICSP. Мы не будем в нашем проекте использовать данный вид программирования, поэтому отключим данный бит (OFF).

Шаг 7. Следующими битами конфигурации у нас является бит EEPROM и биты защиты программы. Если бит EEPROM включен, то после программирования микроконтроллера никто не сможет перезаписать в нем программу. В нашем случае мы оставим все эти три бита в выключенном состоянии.

После того как все необходимые настройки битов конфигурации сделаны наше диалоговое окно должно выглядеть следующим образом.

Настроенные биты конфигурации микроконтроллера PIC для нашего проекта

Шаг 8. После этого выберите пункт меню Generate Source Code to Output – в результате этого код нашей программы будет сгенерирован, нам необходимо всего лишь скопировать его вместе с заголовочным файлом и вставить его в файл Blink.c как показано на следующем рисунке.

Вставка кода настройки битов конфигурации в основной код нашей программыНа этом настройка битов конфигурации (фьюзов) для нашего проекта будет закончена. Мы будем использовать подобную настройку битов конфигурации для всех проектов на основе микроконтроллеров PIC, которые мы далее рассмотрим на нашем сайте.

Написание программы мигания светодиодом для микроконтроллера PIC

В нашем проекте мы будем подключать светодиод к одному из контактов ввода/вывода микроконтроллера PIC16F877A, с помощью которого мы будем управлять миганием светодиода. Распиновка микроконтроллера PIC16F877A представлена на следующем рисунке.

Распиновка микроконтроллера PIC16F877A

Как видно из представленного рисунка, микроконтроллер PIC16F877 имеет 5 основных портов ввода/вывода. Обычно они обозначаются как PORT A (RA), PORT B (RB), PORT C (RC), PORT D (RD) и PORT E (RE). В данном случае PORT A содержит 6 контактов (с RA-0 по RA-5), ”PORT B” , “PORT C” и ”PORT D” содержат по 8 контактов (с RB-0 по RB-7, с RC-0 по RC-7, с RD-0 по RD-7), ”PORT E” содержит всего 3 контакта (с RE-0 по RE-2).

Характеристики портов в микроконтроллере PIC16F877A

Все эти порты являются двунаправленными. Направление работы порта управляется с помощью регистров TRIS(X) (TRIS A используется для установки направления работы PORT-A, TRIS B используется для установки направления работы PORT-B и т.д.). Установка бита регистра TRIS(X) в ‘1’ будет означать, что соответствующий ему контакт порта PORT(X) сконфигурирован для работы на ввод данных (input). Установка бита регистра TRIS(X) в ‘0’ будет означать, что соответствующий ему контакт порта PORT(X) сконфигурирован для работы на вывод данных (output).

В нашем проекте мы установим контакт RB3 порта PORT B для работы на вывод данных – к нему подключен светодиод. Код программы для микроконтроллера PIC для мигания светодиодом будет выглядеть следующим образом:

#include <xc.h>

#define _XTAL_FREQ 20000000 //Specify the XTAL crystal FREQ

void main() //The main function

{

TRISB=0X00; //Instruct the MCU that the PORTB pins are used as Output.

PORTB=0X00; //Make all output of RB3 LOW

while(1)    //Get into the Infinite While loop

    {

    RB3=1; //LED ON

    __delay_ms(500);   //Wait

    RB3=0; //LED OFF

    __delay_ms(500);   //Wait

    //Repeat.

    }

}

В данном коде программы мы первым делом указываем частоту внешнего кварцевого генератора с помощью инструкции #define _XTAL_FREQ 20000000. Затем в функции void main() мы указываем что контакт RB3 будет работать на вывод данных (TRISB=0X00). И затем идет бесконечный цикл в котором производится мигание светодиодом.

После того как написание кода программы будет закончено выберем пункт меню Run -> Build Main Project. В результате его выполнения ваша программа будет скомпилирована. Если все нормально, то в нижнем окне программы вы увидите сообщение BUILD SUCCESSFUL как показано на следующем рисунке.

Сообщение об успешной компиляции проекта в MPLAB-X

Разработка схемы проекта в Proteus

Если компоновка (Build) нашего проекта была успешной, то нашей IDE в фоновом режиме будет сгенерирован HEX файл, который можно найти внутри каталога DesktopBlinkBlink.Xdistdefaultproduction (каталог может отличаться, если вы в настройках выбрали другой путь сохранения файлов).

Теперь откроем программу Proteus, установленную нами ранее и создадим в ней схему для нашего проекта. Процесс создания схемы проекта в Proteus объяснен в видео, приведенном в конце статьи. В результате в окне программы Proteus вы должны увидеть примерно следующую картину:

Схема нашего проекта в симуляторе Proteus

Для тестирования работы схемы в Proteus нажмите кнопку play в нижнем левом углу экрана после загрузки Hex файла. Светодиод, подключенный к микроконтроллеру, должен начать мигать.

Таким образом, в данной статье мы написали нашу первую программу для микроконтроллера PIC и протестировали ее работу в симуляторе Proteus.

Исходный код программы

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

#include <xc.h>

#define _XTAL_FREQ 20000000 //Specify the XTAL crystal FREQ

void main() //The main function

{

TRISB=0X00; //Instruct the MCU that the PORTB pins are used as Output.

PORTB=0X00; //Make all output of RB3 LOW

while(1)    //Get into the Infinite While loop

    {

    RB3=1; //LED ON

    __delay_ms(500);   //Wait

    RB3=0; //LED OFF

    __delay_ms(500);   //Wait

    //Repeat.

    }

}

Видео, демонстрирующее работу проекта

Загрузка…

4 753 просмотров

Введение

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

Любая программа начинается с инициализации: настройка кода и всей аппаратной составляющей контроллера в тот режим, который требуется для работы программы. А дальше идёт бесконечный цикл и реагирование на события.

Но не будем погружаться в дебри, как реализовываются отдельные действия, и посмотрим на простых примерах, как же устроены могут быть программы для приборов в целом. Напряжения и названия условны. От контроллера к контроллеру они могут различаться.

Пример 1. Моргание светодиодом.

Цель: светодиод на плате моргает с заданной частотой и видом сигнализации. Например, секунду потушен и 500 миллисекунд светится.

Ресурсы контроллера: вывод A, который подключён к светодиоду, ядро.

Примерная схема подключения светодиода к выводу микроконтроллера.
Примерная схема подключения светодиода к выводу микроконтроллера.

Примерный вид программы:

1. Включаем блок управления цифровыми выводами (подаём питание, тактирование — согласно документации).

InitGPIO();

2. Задаём режим работы вывода А на выход. То есть управляем его напряжением напрямую. В состоянии 0 там будет напряжение 0 и светодиод будет светиться, в состоянии 1 там будет порядка +5 В и свечения не будет.

PinA.Output();

3. Сразу же ставим его в состояние, когда светодиод светиться не будет, то есть в высокий уровень.

PinA.High();

Инициализация завершена, можно и начать работать!

4. Выставляем состояние вывода в высокий уровень. Светодиод гаснет. Сюда будем возвращаться после окончания цикла и начинать всё по-новой.

PinA. High();

5. Ждём 1 секунду

WaitMilliseconds(1000);

6. Выставляем состояние вывода в низкий уровень, светодиод включается!

PinA.Low();

7. Ждём 500 миллисекунд.

WaitMilliseconds(500);

8. Возвращаемся к пункту 4 и продолжаем оттуда.

В данном коде выкинута лишняя инструкция 3, так как она дублируется инструкцией 4. Но так совпало чисто случайно =)

InitGPIO();
PinA.Mode(OUTPUT);

while(true) {
PinA. High();
WaitMilliseconds(1000);
PinA.Low();
WaitMilliseconds(500);
}

Задача решена. Ничего сложного же?..

Пример 2. Включение светодиода на время по кнопке.

Цель: светодиод на плате включается на одну секунду после того, как нажали на кнопку.

Например, для игры-викторины, чтоб было видно, кто хочет отвечать.

Ресурсы контроллера: вывод A, который подключён к светодиоду, вывод Б, который подключён к кнопке, ядро.

Схема подключения светодиода, как и в программе А, и отдельная схема подключения кнопки. Пока не нажата — на входе напряжение высокое, когда нажата — низкое.
Схема подключения светодиода, как и в программе А, и отдельная схема подключения кнопки. Пока не нажата — на входе напряжение высокое, когда нажата — низкое.

Примерный вид программы:1. Включаем блок управления цифровыми выводами (подаём питание, тактирование — согласно документации) и задаём режим работы вывода А на выход, как в программе 1 и его начальное состояние.

InitGPIO();
PinA.Output();
PinA.High();

2. Задаём режим работы вывода Б на вход. Мы не будем влиять на состояние вывода, но будем знать, какое у него состояние: высокий там уровень или низкий.

PinB.Input();

Инициализация завершена, можно и начать работать!

3. Проверяем состояние вывода Б, если там низкий уровень, значит, кнопку нажали и идём на пункт 4! Если не нажали, то снова идём сюда же проверять. Всё равно больше делать ничего не надо.

if (PinB.Get() == false) {

4. Включаем светодиод, ждём 1000 миллисекунд и выключаем.

PinA.Low();
WaitMilliseconds(1000);

PinA. High();

5. Возвращаемся к пункту 3, чтобы снова проверить, не нажали ли на кнопку.

Итого:

InitGPIO();
PinA.Output();
PinA.High();

PinB.Input();

while(true) {
if (PinB.Get() == false) {
PinA.Low();
WaitMilliseconds(1000);
PinA.
High();
}
}

Просто же?.. Да, пусть код не лучший, но задачу выполняет. Если кнопка не нажата, условие не будет выполняться и светодиод не включится. Как только нажмём, напряжение на выводе Б станет нулевым, и проверка сработает: включится светодиод на секунду.

Заключение.

Простые программы просты и начать их писать может каждый! А дальнейший прогресс уже дело опыта и желания.

Попробуйте найти в интернете аналогичный пример для своего контроллера, сопоставить его с этими и поиграться, меняя тайминги, рисунки морганий или ещё что-нибудь. Можно так морзянкой написать сообщение, правильно подобрав тайминги и написав портянкой все включения и выключения или сделав функцию для этого… Имея в арсенале всего лишь смену уровня на выводе и проверку состояния вывода можно уже сделать целую кучу устройств, комбинируя их в правильном порядке!

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

Ссылки.

Приведу примеры статей, где это делается для самых разных контроллеров:

И подобных статей даже на русском языке не одна сотня под разные контроллеры, среды разработки, языки. Непонятна одна — можно открыть другую и продолжить!

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

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

Микроконтроллеры бывают разных фирм, которые делают одно и тоже дело, но разными методами. Сравнить это можно с человеческими расами: европейцы, китайцы и африканцы например. Я лично работаю с микроконтроллерами фирмы Атмел, про них и буду говорить. Ну уж пошло сравнение с расами, пускай это будут европейцы.) Программы для микроконтроллеров пишут на языках программирования. Я рекомендую начать с языка Си. Это древний и простой язык. Для написания текста програмы используют программы компиляторы. Они позволяют создавать, редактировать и переваривать написанный программистом текст программы в код (прошивку), который можно загрузить (прошить) в микроконтроллер. Таких программ есть множество. Пример для Атмел: Code VisionAVR, родная от Атмел AVR Studio, Bascom-avr и ещё.
Эти программы делают одно и тоже дело, но своими методами, особенностями достоинствами и недостатками. При это текст Си в тих программах компиляторах немного отличается, но в общем похож. Можно сравнить с различием украинского, русского и белорусского языка. Я использую Code VisionAVR, что и советую начинающим.

Далее я приведу простой текст программы, написанный на языке Си в компиляторе Code VisionAVR для микроконтроллера ATTiny13A. В конце темы есть проект, прошивка и проект для эмулятора протеуса. Микроконтроллер в этой программе умеет делать простую вещь: при помощи кнопки менять логическое состояние на двух выходах, при этом короткое нажатие меняет состояние первого выхода а длинное — второго. В автомобиле например эту схему можно применить для управления одной кнопкой обогревом заднего стекла (которая есть у многих штатно) и добавленным обогревом зеркал. Нажал коротко на кнопку — сработал обогрев стекла, нажал ещё — обогрев стекла выключился. Если нажать и удерживать кнопку, то через какое-то время включиться обогрев зеркал. Если нажать и удерживать кнопку повторно — обогрев зеркал отключится.

Для понятия текста нужно знать грамматику, правила писанины языка Си, этого материала в интернете предостаточно. Так же желательно ознакомиться хотя бы с материалом, по использованию мастера создания проектов в CodeVisionAVR.

Текст программы:

/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.0 Professional
Automatic Program Generator
© Copyright 1998-2010 Pavel Haiduc, HP InfoTech s.r.l.
www.hpinfotech.com

Project :
Version :
Date : 28.01.2012
Author :
Company :
Comments:

Chip type : ATtiny13A
AVR Core Clock frequency: 9,600000 MHz
Memory model : Tiny
External RAM size : 0
Data Stack size : 16
*****************************************************/

#include <tiny13a.h>
#include <delay.h>

unsigned char b, trig;

void main(void)
{
// Declare your local variables here

// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

PORTB=0x01;
DDRB=0x06;

TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;

GIMSK=0x00;
MCUCR=0x00;

TIMSK0=0x00;

ACSR=0x80;
ADCSRB=0x00;
DIDR0=0x00;

ADCSRA=0x00;

while (1)
{
if (PINB.0==0)
{
if (trig==0) b++;
if (b>100)
{
if (PINB.2==0)PORTB.2=1;
else PORTB.2=0;
trig=1;
b=0;
}
}
else
{
if (b>4)
{
if (PINB.1==0)PORTB.1=1;
else PORTB.1=0;
b=0;
}
b=0;
trig=0;
}

delay_ms(10);

}
}

А теперь поподробнее.

/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.0 Professional
Automatic Program Generator
© Copyright 1998-2010 Pavel Haiduc, HP InfoTech s.r.l.
www.hpinfotech.com

Project :
Version :
Date : 28.01.2012
Author :
Company :
Comments:

Chip type : ATtiny13A
AVR Core Clock frequency: 9,600000 MHz
Memory model : Tiny
External RAM size : 0
Data Stack size : 16
*****************************************************/

Это шапка, в которой содержится описание проекта, необходимые данные. Текст закомментирован знаками комментария /* в начале и */ в конце. Все, что находится между этими знаками программой не выполняется. Самое полезное здесь это указание типа микроконтроллера и его частота.

#include <tiny13a.h>
#include <delay.h>

Это ссылка на библиотеку. Если какая либо библиотека необходима, то она должна быть здесь указана. У нас есть библиотека самого микроконтроллера tiny13a.h, и библиотека задержек времени.

unsigned char a, b, trig;

Объявление трех переменных. unsigned char . Что это такое можно посмотреть здесь Вообще всё непонятное копируем в буфер и ищем в поисковике.

void main(void)
{
// Declare your local variables here

void main(void) — это оператор, говорящий что началась основная часть программы на Cи и микроконтроллер будет её с этого места выполнять. Все что начинается с // — это комментарий. Старайтесь чаще ими пользоваться. Вообще конкретный комментарий генерирует сам компилятор, как и во многих других местах. Большинство комментариев я удалил, что уменьшить текст.

// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

В комментарии по английски написано, что это такое. Это первая команда микроконтроллеру, одна из команд, которая настраивает нужные функции, порты и необходимые части микроконтроллера, необходимые для его работы и запуска.
Конкретно это настройка частоты делителя тактовой частоты микроконтроллера. Теперь подробнее:

Микроконтроллер имеет тактовый генератор, который задается в мастере и который потом можно изменить в свойствах проекта если что. У нас эта частота 9.6 мегагерца, как видно в шапке.

При прошивке микроконтроллера эту же частоту нужно указать во фьюзах.
CLKPR=0x80; и CLKPR=0x00; это команды настройки регистра внутреннего делителя этой частоты. Задается оно в мастере в первом окне “CHIP”. Если у нас выбран делитель 1, то тактовая частота делиться на 1, то есть остается без изменений. Если указать например делитель 128, то соответственно тактовая частота делиться на это число. 9.6Мгц / 128 = 75кГц. и значения регистра делителя будет выглядеть:
CLKPR=0x80;
CLKPR=0x07;

Особо внимательные заметили, в регистр делителя CLKPR сначала пишется число 0x80 а затем сразу 0x00. Нафига пишется сначала одно значение а потом сразу другое? Если у вас возникают какие либо вопросы по регистрам и не только, приучайтесь сразу читать даташиты. Там все подробные ответы на чистом английском языке. Открываете даташит, вставляете в поисковик текста название регистра (CLKPR ) и ищете его описание, за что какие биты данного регистра отвечают. Конкретно у этого регистра для изменения делителя необходимо записать единичку в седьмой бит, после чего микроконтроллер даст изменить и выбрать необходимый делитель в первых четырех битах этого регистра. После того, как пройдет четыре такта выполнения команд процессора, изменить регистр будет уже нельзя. Нужно снова сначала изменить седьмой бит CLKPR=0x80 а затем указать делитель CLKPR=нужный делитель

PORTB=0x01;
DDRB=0x06;

Команды управления и настройкой портов микроконтроллеров — ножек чипа. Задается тоже в мастере. В этих регистрах задается работа на вход порта PB0 и подключается к нему внутренний Pull-up резистор. Порты PB1 и PB2 настраиваются “на выход” с логическим нулем на выходе в их состоянии.

В колонке DataDitection мы указывает тип порта: вход или выход (in или out)
В колонке PullUp/Output Value указываем подключение подтягивающего резистора pullup если порт настроен на вход (P — подключен, Т — неподключен) Если порт настроен на выход, то можно указать его логическое состояние 0 или 1. У нас нули. Строчки Bit0 — Bit5 это порты микроконтроллера PORTB0 — PORTB5

Если посмотреть сгенерированный компилятором комментарий, то можно увидеть соответствие пинов и их настройку:
// Input/Output Ports initialization
// Port B initialization
// Func5=In Func4=In Func3=In Func2=Out Func1=Out Func0=In
// State5=T State4=T State3=T State2=0 State1=0 State0=P

Если перевести из 16-тиричного в двоичный значение регистров, то можно понять даже без даташита назначение битов в регистре:

PORTB=0x01 PORTB=0b00000001
DDRB=0x06 DDRB=0b00000110

Напоминаю, что при разложении в двоичный код младшие значения справа а старшие слева.
Незначащие нули слева можно не писать:

PORTB=0b1;
DDRB=0b110;

А можно вообще написать в десятичной системе:
PORTB=1;
DDRB=6;

Далее по тексту кода идет:

TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
GIMSK=0x00;
MCUCR=0x00;
TIMSK0=0x00;
ACSR=0x80;
ADCSRB=0x00;
DIDR0=0x00;
ADCSRA=0x00;

Настройка таймера микроконтроллера, прерываний, АЦП, компаратора и всего такого пока сложного. Пока его не используем — рановато. У всех этих регистров стоят в значениях нули, это значит, что они отключены. А особо внимательные заметили, что какой-то регистр ACSR имеет значение =0x80; Лезем в даташит и читаем:

Analog Comparator Control and Status Register – ACSR

Вообще, как правило название всех регистров это сокращение от первых букв или части их полного названия.

Если стоит значение данного регистра 0x80, значит в двоичной системе это число 10000000, значит стоит единичка в 7 бите этого регистра, значит читаем в даташите, что он означает:

• Bit 7 – ACD: Analog Comparator Disable
When this bit is written logic one, the power to the Analog Comparator is switched off.
This bit can be set at any time to turn off the Analog Comparator. This will reduce power
consumption in Active and Idle mode. When changing the ACD bit, the Analog Comparator
Interrupt must be disabled by clearing the ACIE bit in ACSR. Otherwise an interrupt
can occur when the bit is changed.

По-русски это означает, что установка единицы в этом бите отключает аналоговый компаратор, что его можно выключить в любой момент, что это приводит к снижению потребления электричества, и в конце текста написано про зависимости и что нужно соблюдать, если нужно изменять этот бит.

while (1)

После того, как микроконтроллер настроен запущен и выполнена инициализация необходимых частей и выполнены необходимые первоначальные команды в void main(void) в дело вступает его величество главный цикл. Все что находиться внутри этого главного цикла while (1) и заключено в скобки начала { и конца } будет крутиться, команды выполняться по кругу от начал до конца. А у нас в нашем коде будет крутиться алгоритм опроса кнопки, подключенной к порту PB0, от состояния которой (нажата кнопка или нет) будет меняться состояние выходных портов PB1 и PB2

На картинке видна схема собранную в эмуляторе Протеус схему, которая позволяет видеть работу кода программы.

Теперь про сами основные команды, которые находятся внутри цикла. Все команды используют один оператор if Это условие ЕСЛИ.

if (PINB.0==0)
{

кучка кода;
}
else
{

ещё кучка кода;
}

Подробнее:

if (PINB.0==0)

Если в регистре PINB в бите, отвечающем за порт PB0 микроконтроллера.0 содержится значение равное нулю ==0, то выполняем кучку кода, которая находится далее в границах скобок { и }
Короче, если нажата кнопка то выполняется следующий код в границах последующих скобок { и }
Далее после кучки кода в скобках видим оператор else и ещё кучку кода за ним в скобках { и }

Оператор else переводится не как ещё а как иначе

Оператор if и else всегда работают в паре, сначала идет if затем else. Оператор else можно не использовать совсем, если он не нужен.

В нашей ситуации алгоритм можно описать так:

если (нажата кнопка подключенная к порту PB0)
{

то выполняем кучку кода;
}
иначе
(кнопка не нажата)
{
выполняем эту кучку кода;
}

Так как это все находится внутри главного цикла, то этот код будет выполняться по кругу, будет постоянно опрашиваться кнопка и будет выполняться нужная кучка кода

Теперь рассмотрим кучку кода, которая выполняется, если кнопка нажата:

if (trig==0) b++;
if (b>100)
{
if (PINB.2==0)PORTB.2=1;
else PORTB.2=0;
trig=1;
b=0;
}

Операторы можно вкладывать друг в друга, как матрешку. то есть выполняется одно условие, потом если условие сработало, то другое внутри первого условия и т.д.

if (trig==0) b++;

Если переменное значение trig равняется нулю, то выполняем инкремент переменной b Инкремент — операция увеличения значения, хранящегося в переменной, на 1. То есть при проходе выполнения кода, если процессор натыкается на команду инкремента b++, то процессор прибавляет единичку в число, которое находится в переменной b
Так же здесь применяется упрощенная “орфография” написания условия и команды, без скобок { и }:

if (trig==0) b++;
это же самое что:
if (trig==0)
{
b++;
}

Такое представление используют, если после условия всего одна команда.

Немного отвлеклись, возвращаемся:

if (trig==0) b++; — если значение переменной равно нулю (а оно у нас равно нулю) то выполняем инкремент переменной b — переменная в была равна нулю, теперь стало единице.

Следующая операция:

if (b>100)
{

кучка кода;
}

Если переменная b больше ста, то выполняем кучку кода внутри скобок.

Переменная b за каждый круг цикла прибавляется на единичку и в итоге через сто “кругов” главного цикла выполниться условие, которая находится далее внутри скобок { и }

Теперь рассмотрим что же там делается, если нажата кнопка, если прошло сто кругов цикла:

if (PINB.2==0)PORTB.2=1;
else PORTB.2=0;
trig=1;
b=0;

Здесь мы видим ещё одно условие (жирная такая матрешка получилась))

if (PINB.2==0)PORTB.2=1;
Если регистр состояния выходного порта PB, а точнее PB2 равен нулю, то меняем его состояние на единичку PORTB.2=1.
else PORTB.2=0;
Иначе пишем в регистр нолик. Или если по-другому: если регистр состояния выходного порта PB2 равен единице, то меняем его на ноль.

Короче если происходит выполнение этих условий и команд, то меняется логическое состояние выхода 2 (PB2) на схеме.

Если полностью описать: если нажата кнопка, если прошло сто кругов главного цикла, то меняем логическое состояние выхода 2 — PORTB.2 в коде он же порт PB2 на схеме.

Как уже стало понятно этот кусок кода отрабатывает длительное нажатие кнопки.
Но этого мало, дальше ещё есть две выполняемые команды присвоения:

trig=1;
b=0;

trig=1; присвоение единице этой переменной необходимо, что бы описанное выше условие работы инкремента b++ перестало работать
b=0; обнуляем переменную b.

В итоге при длительном нажатии кнопки, условие при котором меняется состояние порта PB2 выполняется единожды, до тех пор, пока кнопка не будет отжата кнопка, ибо инкремент не будет работать и условие if (b>100) больше не сработает, если тупо нажать кнопку и не отпускать совсем.

Теперь вторая часть кучки кода, которая следует за первым условием:
else
{
if (b>4)
{
if (PINB.1==0)PORTB.1=1;
else PORTB.1=0;
b=0;
}
b=0;
trig=0;
}

Если кнопка отжата:
Опишем её с конца:

trig=0; присваиваем переменной trig значение ноль. Необходимо, что бы после длительного нажатия, когда наступит последующее отжатие кнопки микроконтроллер снова был готов к нажатиям кнопки ( срабатывало условие инкремента if (trig==0) b++;)

b=0; При не нажатой кнопке значение переменной b равняется нулю.

if (b>4)
{
if (PINB.1==0)PORTB.1=1;
else PORTB.1=0;
b=0;
}

Подробнее:
if (b>4)
Если значение переменной b больше четырех, то выполняем следующий код:
if (PINB.1==0)PORTB.1=1;
else PORTB.1=0;

Если состояние порта BP1 равно нулю, то делаем единицу, если нет, то ноль.

Это условие и команда отрабатывает кроткое нажатие кнопки. Если нажата кнопка, то начинает работать инкремент b++; значение которого начинает увеличиваться. Если отжать кнопку и при этом значение переменной b будет больше четырех ( но меньше ста — а то сработает длинное нажатие) то состояние выходного порта PB1 (он же выход 1 на схеме, он же PORTB.1 в коде) поменяется, сработает алгоритм короткого нажатия кнопки.

Если значение переменной b при отжатии меньше четырех, то условие не срабатывает и ничего не происходит. необходимо для работы “дребезга контактов” и ложных срабатываний.

И последнее это присвоение переменной b нулевого значения, что бы обработка алгоритма короткого нажатия происходило единожды.

В оконцовке главного цикла виднеется команда:

delay_ms(10);

Это задержка в главном цикле. То есть, выполняется пошагово команды, затем процессор натыкается на команду delay_ms(10); и начинает её выполнять. В итоге процессор будет 10 миллисекунд ждать и ничего не делать в этой строчке, затем опять приступит к выполнению команд.
Находясь в одном общем цикле, скорость нарастания значения инкремента b++ зависит от времени задержки, указанной в delay_ms.

Команда delay_ms находится в библиотеке задержек #include <delay.h>, которую мы для этого и включили в начале кода.

Как видно из описания, длинное нажатие срабатывает от фронта сигнала нажатия кнопки ( начинает работать инкремент) а короткое нажатие кнопки — по спаду, то есть срабатывает по отжатию кнопки.

Вообще выполняемая здесь последовательность: условие + инкремент достаточно часто используемая команда и в языке Си присутствует отдельный оператор для этого for

Архив с прошивкой, исходником и моделью Протеуса:
umat.ru/files/Button_13.zip
ВНИМАНИЕ!
Архив перезалил 22 сентября 2014 года, обнаружил косяк в выставленной частоте в проекте. Теперь тактовая частота 1.2 Мегагерца, при этом фьюзы стоят по дефолту и их при прошивке трогать вообще не надо

Пример реализации схемы rusgg
www.drive2.ru/cars/gaz/ga…usgg/journal/570874/#post

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