Мультироторные платформы в МАИ

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Мультироторные платформы в МАИ » Программирование и Настройка » Микроконтроллеры для ленивых, или краткое введение в программирование


Микроконтроллеры для ленивых, или краткое введение в программирование

Сообщений 1 страница 2 из 2

1

Если у вас вдруг возникло желание приобщиться к великому таинству программирования микроконтроллеров или даже создать свою собственную так называемую «Embedded system», но:
вы не знаете, с чего начать
у вас нет паяльника, программатора, коробки с радиодеталями и умения все это совместить
вам лень отрываться от кресла и компьютера

то эта статья точно для вас.

Ни для кого не секрет, что современное программное обеспечение очень облегчило нелегкую жизнь инженера. Имеются тысячи программ для автоматизированного проектирования электронных схем, для моделирования их работы, в том числе и для микроконтроллерных систем. Одна из таких САПР — Proteus VSM, разработнанная компанией Labcenter Electronics(требуйте бесплатную ознакомительную версию).

Итак, для изучения микроконтроллеров нам потребуются:
желание
компьютер с установленным Proteus
свободное время

Для того, чтобы постичь все секреты микроконтроллера, начинать изучение лучше с прочтения документации (для микроконтроллеров PIC документацию можно скачать на сайте производителя ), а программировать начать не на С, Pascal или даже Basic, а на старом добром ассемблере, что позволит понять, как же эта железяка на самом деле работает.
Что можно в первую очередь отметить в микроконтроллере семейства PIC16 с точки зрения программиста:
раздельные память программ (14 бит) и память данных (8 бит)
всего 35 инструкций
большинство инструкций (кроме инструкций перехода и проверки условий) выполняется за 1 машинный цикл (4 такта тактового генератора)
один явно выраженный регистр общего назначения – аккумулятор
обращение к любой ячейке оперативной памяти как к регистру (так называемые регистровые файлы)
порты ввода-вывода
наличие прерываний
аппаратные таймеры

Создадим простое устройство на простом микроконтроллере PIC16F84A. Для этого в Proteus найдем этот микроконтроллер в библиотеке элементов, добавим в проект и разместим его на рабочем листе.

http://habreffect.ru/files/fd9/b376c00a8/pic1.gif

Далее создадим файл с исходным кодом и откроем его во встроенном редакторе.

http://habreffect.ru/files/87d/d44d5e6cf/pic2.gif

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

http://habreffect.ru/files/a5d/c528ba95c/pic3.gif

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

LIST p=16F84a
include "P16F84A.INC"
;---------------------------------
CBLOCK 0x0C
W_TEMP ;0x0C
STATUS_TEMP ;0x0D
FLAGS ;0x0E
COUNTER ;0x0F
ENDC
;---------------------------------
TF EQU 0x00 ;Task flag
ORG 0x00
goto START
ORG 0x04
;---------------------------------
INT movwf W_TEMP ;save W
swapf STATUS, W
movwf STATUS_TEMP ;save STATUS
btfss INTCON, T0IF
goto ENDISR
bcf INTCON, T0IF ;clear flag
bsf FLAGS, TF
ENDISR swapf STATUS_TEMP, W
movwf STATUS ;recover STATUS
swapf W_TEMP, F
swapf W_TEMP, W ;recover W
retfie
;---------------------------------
START clrf PORTA
clrf PORTB
bsf STATUS, RP0 ;Bank1
clrf TRISB ;PortB Output
movlw b'11010111'
movwf OPTION_REG ;Set Option_REG
bcf STATUS, RP0 ;Bank0
clrf TMR0
bsf INTCON, T0IE ;Enable Timer0 interrupt
bsf INTCON, GIE ;Enable interrupts
movlw 0x3D
movwf COUNTER
;---------------------------------
MAIN btfss FLAGS, TF
goto MAIN ;If TF = 0
call OUTPUT
bcf FLAGS, TF ;Clear TF
goto MAIN
;---------------------------------
OUTPUT decfsz COUNTER, F
goto NOT0
movlw 0x3C
movwf COUNTER
NOT0 movf COUNTER, W
call TAB
movwf PORTB
return
;---------------------------------
TAB addwf PCL, F
nop
retlw b'00100001' ;1
retlw b'00000001' ;2
retlw b'00100001' ;3
retlw b'00000001' ;4
retlw b'00100001' ;5
retlw b'00000001' ;6
retlw b'00100001' ;7
retlw b'00000001' ;8
retlw b'00100001' ;9
retlw b'00000001' ;10
retlw b'00100001' ;11
retlw b'00100001' ;12
retlw b'00100001' ;13
retlw b'00100001' ;14
retlw b'00100001' ;15
retlw b'00100001' ;16
retlw b'00100001' ;17
retlw b'00100001' ;18
retlw b'00100001' ;19
retlw b'00100001' ;20
retlw b'00010010' ;21
retlw b'00010010' ;22
retlw b'00010010' ;23
retlw b'00010010' ;24
retlw b'00010010' ;25
retlw b'00010010' ;26
retlw b'00010010' ;27
retlw b'00010010' ;28
retlw b'00010010' ;29
retlw b'00010010' ;30
retlw b'00010010' ;31
retlw b'00010010' ;32
retlw b'00010010' ;33
retlw b'00010010' ;34
retlw b'00010010' ;35
retlw b'00010010' ;36
retlw b'00010010' ;37
retlw b'00010010' ;38
retlw b'00010010' ;39
retlw b'00010010' ;40
retlw b'00001100' ;41
retlw b'00001000' ;42
retlw b'00001100' ;43
retlw b'00001000' ;44
retlw b'00001100' ;45
retlw b'00001000' ;46
retlw b'00001100' ;47
retlw b'00001000' ;48
retlw b'00001100' ;49
retlw b'00001000' ;50
retlw b'00001100' ;51
retlw b'00001100' ;52
retlw b'00001100' ;53
retlw b'00001100' ;54
retlw b'00001100' ;55
retlw b'00001100' ;56
retlw b'00001100' ;57
retlw b'00001100' ;58
retlw b'00001100' ;59
retlw b'00001100' ;60
;---------------------------------
END

Данная программа при всей своей простоте содержит почти все элементы, присущие и более сложным программам.
Рассмотрим структуру подробнее.

Комментарием считается строка или ее часть начинающаяся с символа «;».

Строка «LIST p=16F84a» представляет собой директиву ассемблера, предназначенную для изменения параметров компиляции и определяет тип используемого микроконтроллера (16F84a).

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

С помощью директивы:
CBLOCK 0x0C
W_TEMP
STATUS_TEMP
FLAGS
COUNTER
ENDC
мы размещаем в памяти данных несколько констант, начиная с адреса 0x0С, так что обратившись, например, к FLAGS, мы получим доступ к ячейке оперативной памяти с адресом 0x0E. Эти константы понадобятся нам в дальнейшем.

Директива EQU в строке «TF EQU 0x00» определяет константу TF равную числу 0x00.

Директива ORG предназначена для размещения программы по определенным адресам в памяти данных.

Исторически сложилось и закрепилось в архитектуре микроконтроллеров PIC, что по адресу 0x0000 расположен вектор сброса, а по адресу 0x0004 – вектор прерывания (то есть при сбросе, который происходит при включении контроллера с помощью специальной схемы, выполнение программы начнется с адреса 0x0000, а при возникновении прерывания произойдет переход на адрес 0x0004).
Таким образом, по адресу 0x0000 мы располагаем команду безусловного перехода на начало основной программы «goto START», а по адресу 0x0004 у нас будет подпрограмма обработки прерываний (метка INT).
Часть программы, начинающаяся с метки START, предназначена для инициализации начальных параметров, и выполняется только один раз при старте микроконтроллера. Часть программы, начинающаяся с метки MAIN, организует бесконечный цикл выполнения программы с помощью команды безусловного перехода «goto MAIN».
Программа должна заканчиваться директивой END.

Инициализация

Команда «clrf» предназначена для обнуления содержимого регистра. Командами

clrf PORTA
clrf PORTB

мы обнулим содержимое регистров, связанных с портами ввода-вывода PortA и PortB. Это необходимо, поскольку при сбросе микроконтроллера они могут получить случайные значения.
Команда «bsf» предназначена для установки определенного бита в байте регистра. Таким образом при выполнении:

bsf STATUS, RP0

в регистре STATUS бит RP0 примет значение 1. Регистр STATUS кроме того, что содержит флаги результатов арифметических операций (ноль, возникновение переноса), также отвечает за выбор банка памяти при прямой адресации. Микроконтроллер PIC16F84A содержит 2 банка памяти (начинающихся с адресов 0x00 и 0x80), специальные регистры расположены как в первом, так и во втором банке. Установив бит RP0, мы сможем обращаться ко второму банку памяти.

Во втором банке памяти нас интересует регистр TRISB, определяющий, какие выводы порта PortB являются входами, а какие выходами. Установкой с помощью команды «clrf TRISB» всех битов регистра TRISB в 0, определяем все выводы PortB как выходы.

Команда «bcf» сбрасывает определенный бит в регистре, таким образом с помощью команды

bcf STATUS, RP0

мы опять можем обращаться к банку первому банку памяти.

Теперь необходимо изменить значение регистров:
OPTION_REG, с помощью которого можно сконфигурировать таймер Timer0
INTCON, определяющий, какие прерывания разрешены в системе.

Команда «movlw b'11010111'» загружает в аккумулятор W число, с помощью которого мы устанавливаем источник для тактирования таймера от внутреннего генератора, подключаем предделитель к таймеру а также устанавливаем коэффициент предделителя (подробности смотрите в документации). Командой «movwf OPTION_REG» мы отправляем число из аккумулятора в специальный регистр OPTION_REG.

Команда «clrf TMR0» сбрасывает значение таймера Timer0, далее его значение будет инкрементироваться с частотой равной частоте цикла микропроцессора, деленной на выбранный нами предделитель. При переполнении регистра TMR0 будет происходить соответствующее прерывание.

Для разрешения прерывания от таймера в специальном регистре INTCON необходимо установить бит T0IE, а для глобального разрешения прерываний, необходимо установить бит GIE, что и делают команды:

bsf INTCON, T0IE
bsf INTCON, GIE

Также на этапе инициализации устанавливается счетчик циклов для основной программы:

movlw 0x3D
movwf COUNTER

В результате выполнения этих двух инструкций в ячейку памяти, обозначенную как регистр COUNTER (адрес 0x0F) будет помещено число 0x3D.

На этом инициализация заканчивается и начинается основной цикл программы.

Основной цикл

В основном цикле проверяется состояние флага задания TF (бит 0x00) в регистре FLAGS (по адресу 0x0E). Если флаг установлен (значение 1) выполняется подпрограмма OUTPUT. после выполнения подпрограммы флаг задания сбрасывается (значение 0).

Проверка значения бита в регистре осуществляется с помощью команды «btfss FLAGS, TF». Команда тестирует бит в регистре, если бит установлен в «1», то следующая инструкция не выполняется, а следующие 2 цикла выполняется команда «nop» («нет операции»). Если бит имеет значение «0» то выполняется следующая по порядку инстукция.

Поскольку следующая инструкция является командой безусловного перехода на метку MAIN, то при сброшенном флаге задания TF продолжается бесконечный основной цикл программы. При установленном флаге задания выполняется команда вызова подпрограммы «call OUTPUT», таким образом текущее значение счетчика команд (PC – «Program counter») помещается в стек, и происходит переход на подпрограмму с меткой OUTPUT.

Возврат из подпрограммы происходит при выполнении инструкции «return», при этом значение адреса из вершины стека помещается в счетчик команд. Стек имеет 8 уровней, таким образом, возможна вложенность (то есть вызов одной из другой) до 8 подпрограмм (в том числе и подпрограмма обработки прерываний).
После возврата из подпрограммы флаг задания сбрасывается уже знакомой нам командой «bcf».

Подпрограмма обработки прерываний

При переполнении регистра таймера происходит переход на вектор прерывания по адресу 0x0004 и запрещаются все прерывания (автоматически сбрасывается бит GIE в регистре INTCON). Подпрограмма обработки прерывания прежде всего должна обеспечивать сохранение содержимого аккумулятора W и регистра STATUS при перед началом обработки прерывания, и восстанавливать значения W и STATUS по окончании обработки. Это необходимо для предотвращения их случайной модификации в подпрограмме обработки прерывания.

Содержимое аккумулятора просто помещается в заранее выделенную ячейку памяти командой «movwf W_TEMP».
Регистр STATUS может изменять свое значение при выполнении ряда инструкций, поэтому применяется маленькая хитрость. Содержимое регистра помещается в аккумулятор с перестановкой нибблов (полубайтов) командой «swapf STATUS, W», а оттуда помещается в заранее выделенную ячейку памяти командой «movwf STATUS_TEMP».

Восстановление содержимого W и STATUS в конце подпрограммы происходит в обратном порядке. Содержимое ячейки памяти, хранящей содержимое регистра STATUS помещается в аккумулятор с перестановкой нибблов командой «swapf STATUS_TEMP, W», и из аккумулятора переносится непосредственно в регистр STATUS командой «movwf STATUS». Для предотвращения случайной модификации регистра STATUS при помещении неизвестного заранее значения из временной ячейки памяти в аккумулятор, восстановление аккумулятора осуществляется двумя командами с перестановкой нибблов:

swapf W_TEMP, F
swapf W_TEMP, W

Такое применение команды «swapf» объясняется тем, что она не оказывает влияния на флаги в регистре STATUS.

Сам обработчик прерывания проверяет что вызвало прерывание (т.к. возможны несколько источников прерывания, если они будут разрешены) путем проверки соответствующих битов регистра INTCON. Поскольку у нас разрешено только одно прерывание, можно было бы не делать такой проверки, но тем не менее проверим бит T0IF (устанавливается при возникновении прерывания от таймера):

btfss INTCON, T0IF
goto ENDISR
bcf INTCON, T0IF
bsf FLAGS, TF

Если бит не установлен, то произойдет переход к инструкциям восстановления содержимого W и STATUS на метку ENDISR. Если бит установлен то мы его сбросим вручную командой «bcf INTCON, T0IF» и установим флаг задания командой «bsf FLAGS, TF». Далее выполнятся инструкции восстановления содержимого W и STATUS.
Выход из подпрограммы обработки прерываний происходит при выполнении инструкции «retfie». Содержимое счетчика команд восстанавливается из стека, а прерывания автоматически разрешаются путем установки бита GIE регистра INTCON в «1».

Подпрограмма OUTPUT

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

http://habreffect.ru/files/1f9/d94576074/pic4.gif

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

Подпрограмма содержит инструкции:

OUTPUT decfsz COUNTER, F
goto NOT0
movlw 0x3C
movwf COUNTER
NOT0 movf COUNTER, W
call TAB
movwf PORTB
return

Инструкция «decfsz» предназначена для организации циклов. Она декрементирует содержимое регистра COUNTER, в случае если результат не равен нулю, выполняется следующая по порядку инструкция (переходим на метку NOT0), иначе в течение 2 циклов выполняется пустая инструкция «nop», а затем в регистр COUNTER помещается начальное значение 0x3C (заметьте, т.к. декремент регистра осуществляется до использования значения этого регистра в программе, при инициализации мы задали значение на 1 большее).

Значение регистра COUNTER помещается в аккумулятор W, после чего осуществляется чтение из таблицы путем вызова подпрограммы инструкцией «call TAB». При возврате из таблицы в W содержится необходимое нам значение состояния выходов. Это значение записывается в регистр PORTB, чем осуществляется вывод сигналов из микроконтроллера. Далее происходит возврат из подпрограммы командой «return».

Таблица значений

Таблица содержит несколько сервисных инструкций и, собственно, сами 60 требующихся нам значений:
TAB addwf PCL, F
nop
retlw b'00100001' ;1
retlw b'00000001' ;2
………
retlw b'00001100' ;60

Принцип чтения из таблицы в следующем. При вызове таблицы мы поместили в W значение – номер «ячейки» таблицы. В подпрограмме происходит сложение младшего байта счетчика команд PCL и значения из W инструкцией «addwf PCL, F». Таким образом в памяти программ произойдет переход к инструкции с адресом PCL+W. В диапазоне адресов, которые могут получиться в результате, находятся непосредственно «ячейки» таблицы в виде инструкций «retlw» (например «retlw b'00100001'»), которые вызывают возврат из подпрограммы с занесением своего аргумента в W.

Таким образом, в результате возврата из таблицы, мы имеем в W требуемое значение.

Инструкция «nop» применена для выравнивания значений в таблице, т.к. таблица не вызывается при W=0.
Следует отметить необходимость контроля содержимого аккумулятора W при вызове таблиц, чтобы не произошел переход за границы программы.

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

Продолжение следует

Информация взята с сайта: http://habrahabr.ru

0

2

я лучше пойду пивка выпью чем все это читать)хД

0


Вы здесь » Мультироторные платформы в МАИ » Программирование и Настройка » Микроконтроллеры для ленивых, или краткое введение в программирование