Интернет-магазин

Просмотр корзины
В корзине:

товаров - 0 шт.



§ 51. Первая программа на ассемблере для PIC16F877

Ирков Алексей Николаевич, Июнь 2007
Статья обновлена 26 Мая 2014

Файлы к статье скачать

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


В свое время, я довольно много времени потратил на то, чтобы разобраться, как же все-таки работает эта «страшная» вещь, под грозным названием МИКРОКОНТРОЛЛЕР!!! Меня удивило то, что литературы, ориентированной на людей без опыта, практически нет:'(Потратив кучу времени и сил мне пришлось перекопать тонну книг, чтобы наконец-то сообразить, что к чему. Благо, назад пути у меня не было, т.к. моя дипломная работа напрямую была связана с программирование МК. Самое интересное, что у меня никак не получалось сложить в одну картинку все прочитанное в своей голове. И вдруг однажды у меня просто что-то перемкнуло в голове, и все стало на свои места. Так вот, к чему весь этот треп…

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

Ух, с введением закончили, перейдем к делу. Но в начале хотелось бы сказать пару слов о том, почему же мы будем программировать именно на ассемблере, а не Си… Во-первых, цикл статей по программированию PIC МК на Си начал Дмитрий, и я не хочу ему в этом мешать. Я полностью согласен с тем, что разрабатывать программы на Си получается гораздо быстрее и проще, чем на ассемблере. Однако именно знание ассемблера, поможет разобраться во внутренней работе МК, а также, я надеюсь, позволит Вам быстрее понять структуру программ, написанных на Си. Еще одним плюсом в пользу ассемблера является компактность получающегося кода. И в этом плане именно ассемблер занимает лидирующее место, по сравнению с языком Си. В вашей программе не будет ничего «лишнего», а это не может не радовать.

Еще не надоел своей болтовней? Нет? Ну, тогда приступим. На первое время, мною было принято решение дублировать программы, которые предлагал Дмитрий на Си. Только писать их на ассемблере. Во-первых, Вам не придется перепаивать схему, а во-вторых, можно будет провести аналогию, между Си и ассемблер проектами.

Процесс установки среды разработки MPLAB я рассказывать не буду, так как это уже за меня сделал Дмитрий, однако замечу, что у меня стоит версия 7.50, хотя я не думаю, что она сильно отличается от 7.40. Кстати, на сегодняшний день уже вышла версия 7.60, но в любом случае принципиальной разницы между ними нет, так что каждый ставит себе ту, которая ему больше нравится. Еще один момент, моя версия глючит с длинными именами папок, так что желательно размещать свои проекты как можно ближе к корневому каталогу, и использовать англ. названия папок.

Приступаем. Для начала создадим наш первый проект. Открываем MPLAB. Щелкаем Project -> Project Wizard… откроется вот такое окошко:

Не читая, щелкаем по кнопке Далее...

Выбираем из списка МК PIC16F877 и смело жмем Далее

Здесь из списка Active Toolsuite выбираем компилятор MPASM и опять Далее

Указываем имя проекта и директорию. Ну и по традиции, щелкаем Далее

Это окошко пока пропускаем… Далее

Щелкаем Готово. Теперь, жмем File -> New. Появляется окно с именем Untitled. Это основной файл нашего проекта, нужно его сохранить. Выбираем File -> Save as… Указываем имя файла main.asm, и сохраняем его в папке с проектом. Не забудьте про галочку Add file to project.

Ура!!! Все готово, проект создан, и главное ничего сложного то не было!!! Щелкаем Готово. Процесс практически не отличается от создания проекта на Си, который описывал Дмитрий, единственное, что мы сделали – это поменяли компилятор. Слева, в окне программы, должно появиться окошко First.mcw с кучей папок. Это наш менеджер проекта. Видите, в нем появился наш файл main.asm? Если у вас нет этого окошка, то поставьте галочку в меню View -> Project.

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

;======================== «ШАПКА» ПРОГРАММЫ ======================
;=========================================
; Имя файла: main.asm
; Дата: 28.05.07
; Автор: Ирков Алексей Николаевич
; E-mail: nsuirkov@ngs.ru
;==================================================================
; Используется микроконтроллер PIC16F877. Частота кварца 20 МГц.
;==================================================================
; Описание программы
; Данная программа заставляет все светодиоды загореться
;==================================================================


;================================================
; Настройка и конфигурация микроконтроллера
;================================================
	LIST  p=16F877
	__CONFIG H'3F72'

;================================================
; Инициализация регистров специального назначения
;================================================

INTCON			equ 0x0B
STATUS			equ 0x03
PORTB			equ 0x06
TRISB			equ 0x86

;================================================
; Инициализация констант
;================================================

RP0			equ 0x05

;================================================
; Инициализация переменных в памяти данных
;================================================

;================================================
; Начало программы
;================================================
	ORG 0x00
	goto Start


	ORG 0x05

Start:
	clrf INTCON			;запрещаем все прерывания

;===============================================
; Настраиваем линии порта PORTB на выход
;===============================================

	bsf STATUS, RP0 			;переходим в банк 1
	
	movlw b'00000000'		;помещаем в аккумулятор число 0 
	movwf TRISB			;устанавливаем линии порта 
					;PortB на выход

	bcf STATUS, RP0			;переходив в банк 0

;===============================================
; Закончили настройку
;===============================================


	movlw .255 			;помещаем в аккумулятор число 255
	movwf PORTB			;перемещаем число из аккумулятора 
					;в регистр DelL

Loop:	
	nop				;ничего не делаем
	goto Loop			;переходим на метку Loop

	End

Страшно? Ну это дело поправимое! Давайте разбираться, что здесь написано.

Ну во-первых, все что стоит после знака ; является комментарием и помечается зеленым цветом. Очень советую Вам применять комментарии в вашей программе, так как в будущем они сократят Вам кучу времени при отладке программы. К тому же, читать текст с комментариями гораздо удобнее. И даже если Вы не собираетесь выкладывать ваши исходники на всеобщее обозрение, попробуйте открыть и разобраться в какой-нибудь собственноручно написанной старой программе (конечно если она у вас есть:)... Ох и намучаетесь, это я по своему горькому опыту говорю, к тому же программы, написанные на ассемблере, читать нетренированным глазом - жуткое дело. Надеюсь, я Вас убедил… И еще один маленький момент. Пишите текст программы самостоятельно, не надо его копировать, это ведь не ускоренный курс обучения секретарей. К тому же так вы гораздо быстрее запомните команды.

С комментариями разобрались, смотрим дальше:

	LIST  p=16F877

Данная команда указывает компилятору, что мы будем использовать микроконтроллер PIC16F877. Дальше идет команда:

	__CONFIG H'3F72'

Пока вдаваться в подробности не буду, Дмитрий уже объяснял, зачем она нужна, впоследствии мы превратим ее в более читабельный вид, а пока оставляем как есть. Идем дальше:

	INTCON				equ 0x0B
	STATUS				equ 0x03
	PORTB				equ 0x06
	TRISB				equ 0x86

В принципе это тоже самое что и #define в Си, если кто знает... Мы просто ставим в соответствие регистру данных с адресом 0x0B слово INTCON. Это придает нашей программе более понятный вид. Согласитесь, фраза «Заносим в регистр INTCON число 20» звучит гораздо приятнее, чем «Заносим в регистр 0x0B число 20». Другой вопрос – что такое регистр... Вот тут уже двумя словами не отделаться... Придется вкратце описать архитектуру микроконтроллера.

Никогда не задумывались, как же работает программа, записанная в микроконтроллер и где она в нем храниться? Каждый микроконтроллер имеет у себя «на борту» какой-то объем памяти. Эту память разделяют на память программ и память данных: в одну память записывается программа, а в другую – данные. Такая архитектура называется гарвардской и позволяет одновременно считывать команду и обращаться к данным. Тем самым повышается скорость работы МК, а это всегда хорошо:) Еще один нюанс. Ячейка памяти программ и памяти данных различаются по длине. Как это ни странно, но ячейка памяти программ состоит из 14 бит!!! Давайте дальше будем называть эту ячейку словом. Так вот, каждое слово памяти программ занимает у нас 14 бит. Зачем так много спросите вы? Пока не будем углубляться в подробности, об этом чуть позже. В свою очередь каждая ячейка памяти данных у нас так и осталось 8 битной, а называют ее регистром. Единственное, что память данных поделена на 4 банка, первые адреса которых содержат регистры специального назначения. Вот засада, скажите вы. Что за банки, какие еще регистры??? Непонятно!!!

На самом деле нет ничего страшного, представьте, что Вы взяли и разбили всю память данных на 4 одинаковые части, и каждую из них назвали банком. Объем каждого в нашем случае будет равен 128 байт. Малова-то, скажете Вы! А вот и нет!!! Программирование микроконтроллеров очень сильно отличается от написания программ для винды. Наши программы будут куда компактнее, так что не смотрите на размер памяти пессимистическим взглядом, нам ее хватит за глаза. К тому же, если вы еще не забыли у нашего микроконтроллера целых 4 банка. Посмотрите на табличку в приложении 1. Четыре столбца это четыре банка, справа написаны адреса регистров в памяти данных. Посмотрите внимательно. В начале каждого банка идут так называемые регистры специального назначения, их довольно много и все они предназначены для управления микроконтроллером. Плюс к этому, часто используемые регистры дублируются в каждом банке. После регистров специального назначения (РСН) идут регистры общего назначения, где можно хранить переменные. Вернемся к исходному коду. Теперь, я думаю, стало понятнее, почему регистру INTCON я присвоил значение 0x0B.

Дальше мы инициализируем константы:

	RP0				equ 0x05

Константе RP0 мы присвоили значение 5. Идем дальше:

	ORG 0x00
		goto Start

	ORG 0x05

Значение этих строк давайте для начала пропустим. Слишком много не хочу на вас сваливать с самого начала, смотрим дальше:

	Start:
		clrf INTCON			;запрещаем все прерывания

Start – это так называемая метка. С нее (а если быть точнее, то со следующей за меткой команды) и начинается исполнение нашей программы. Метки не занимают памяти программы, они лишь хранят адрес следующей за ней команды. Еще момент, метка должна начинаться с самого начала строчки, а вот все остальные команды лучше писать через табуляцию, если конечно вы не хотите, чтобы компилятор на вас «сругался». Дальше идет непонятная команда clrf INTCON. Что такое INTCON Вам уже должно быть немножко понятно. Это регистр специального назначения, которому мы определили адрес 0x0B. А команда clrf просто очищает этот регистр, устанавливая все его биты в 0. Встает резонный вопрос, зачем это нужно. Мне пока придется ограничится словами ТАК НАДО, потому что на разъяснение этого вопроса уйдет еще по меньшей мере страниц 5 печатного текста, а в вашей голове будет сплошная каша, чего мне ужасно не хочется. Скажу лишь, что таким образом мы запрещаем все прерывания у нашего микроконтроллера. Идем дальше:

bsf STATUS, RP0 			;переходим в банк 1
	
	movlw b'00000000'		;помещаем в аккумулятор число 0 
	movwf TRISB			;устанавливаем линии порта 
					;PortB на выход

	bcf STATUS, RP0			;переходив в банк 0

Ох не хочется мне этого говорить, но эти строчки для первого раза придется тоже пропустить. Но мы вернемся к ним очень скоро. Дальше:

	movlw .255 			;помещаем в аккумулятор число 255
	movwf PORTB 			;перемещаем число из аккумулятора 
					;в регистр PORTB

Первой идет команда movlw .255. Этой командой мы помещаем в аккумулятор число 255. Вы заметили, что у меня на каждую команду уходит по небольшому параграфу объяснений, ничего не поделаешь, вначале придется потерпеть, зато потом будет легче. Так вот, что такое аккумулятор и зачем он нужен? Как бы это странно не звучало, но в МК PIC нет возможности напрямую записать в регистр данных число. Для этого нужно сначала записать это число в аккумулятор, а потом переместить это число из аккумулятора в регистр. Аккумулятор в данном случае получается вроде временного регистра. Кстати именно с аккумулятором в той или иной степени связанно большинство команд МК. Посмотрите внимательно на карту памяти данных выше, что удивительно, Вы там не найдете регистра аккумулятора. Это потому, что он хранится непосредственно «внутри» МК. Но это не должно Вас смущать. Так вот, как я сказал раньше, команда movlw .255 помещает в аккумулятор число 255. Почему именно 255 спросите Вы, а не 256 например? Да просто у нас регистр всего 8ми битный (вы ведь помните) и может хранить число от 0 до 255. А если Вы запишите в него число 256, то он «превратит» его обратно в 0. Кстати, точка перед числом означает, что число у нас представлено в десятичной системе счисления. А если Вы переведете число 255 в двоичную систему счисления, то увидите, что ему соответствует число B’11111111’. В принципе, мы могли написать movlw B’11111111’, от этого работа программы никак не изменилась.

Дальше идет команда movwf PORTB. Она копирует в регистр PORTB содержимое аккумулятора. То есть, по сути, мы за 2 команды записали в регистр PORTB число 255. Зачем это надо? Дело в том, что основной целью регистра PORTB является общение с внешним миром. И это не удивительно, так как по сути, если бы мы не могли ничего записывать в МК и ничего из него считывать, то он бы оказался абсолютно бесполезной железячкой. Посмотрите внимательно, у нашего микроконтроллера целых 5 портов ввода/вывода!!! И все они предназначены для приема или отправки данных. Порт PORTB это 8-ми битный регистр, об этом мы уже говорили, и каждый бит этого регистра, это отдельная ножка микроконтроллера! Посмотрите на рисунок:

Видите справа напротив 33 ножки написано RB0, напротив 34 написано RB1, и так далее до 40 ножки (RB7). Это как раз и есть наш порт POTRB, а цифра в конце указывает на номер бита в регистре, т.е. нулевому биту регистра PORTB соответствует ножка RB0, первому биту регистра PORTB соответствует RB1 и т.д. вплоть до седьмого. Кстати, двунаправленная стрелочка напротив каждой ножки обозначает, что мы с помощью этой ножки можем как принимать, так и отправлять данные. Следует лишь предварительно их настроить. В нашем случае все 8 линий порта PORTB настроены на выход, то есть мы с помощью них будем отправлять либо 0 либо 1 во внешний мир. А чтобы это сделать, нужно лишь записать в регистр PORTB на нужные линии порта значения либо 0 или 1. В нашем случае мы командой movwf PORTB установили все биты регистра в единицу, то есть, на все ножки, начиная с 33 и кончая 40, подали напряжение. Теперь, если Вы посмотрите на схему, то увидите, что у нас к порту PORTB подключены светодиоды, которые загорятся.

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

	Loop:
		nop				;ничего не делаем
		goto Loop			;переходим на метку Loop

Этими тремя сточками мы реализовали простейший цикл, который заставит бесконечно крутиться микроконтроллер. Первой строкой идет метка Loop, ее микроконтроллер пропускает, но запоминает куда ему нужно будет вернуться. Затем идет команда nop. Она говорит МК ничего не делать. А потом идет команда goto Loop, эта команда безусловного перехода, если перевести на человеческий язык принцип ее работы, то получится примерно следующее: «А теперь возьми и вернись на метку Loop». То есть мы просто перескочим на метку, которую сами и создали и МК снова приступит к выполнению команды nop.

Зачем же надо создавать этот цикл? В принципе, его можно и убрать, но в таком случае я за действия программы уже не отвечаю. Помните, я рассказывал вам про память программы? Так вот, после того, как мы скомпилируем текст нашей программы, у нас получится .hex файл, который нам нужно будет прошить в микроконтроллер. Во время прошивки наша программа как раз и заносится в память программ. Начиная с адреса 0x0000. Так вот, при запуске МК у нас автоматически начинается исполнение команды с этого адтеса. Если бы мы не зациклили нашу программу, то МК дошел бы до конца нашей программы, а потом просто продолжил бы последовательно «выдергивать» команды из памяти программ и выполнять их. Дойдя до последнего адреса памяти, он бы снова перескочил на адрес 0x0000, и все пошло по кругу. То есть заново бы стал выполнять преобразование регистров. Нам такого «счастья» не надо. Мы просто зациклили МК на выполнение одной команды nop, и сидим любуемся на горящие светодиоды.

Ну все, думаю для первого раза достаточно. Компилируем программу, нажав кнопку Ctrl + F10, затем прошиваем полученный .hex файл в МК, и собираем схему, которую предложил Дмитрий. Включаем и видим, что все светодиоды горят!!! УРА!!!

Давайте еще раз повторим основные моменты. Итак, МК в своем составе имеет память программ и память данных. Программа записывается в память программ, а при запуске МК начинается последовательное выполнение команд, начиная с адреса 0x0000. Память данных в свою очередь поделена на 4 банка и состоит из регистров специального назначения, предназначенные для управления работой МК, и регистров общего назначения, предназначенные для хранения данных. Так же мы познакомились с регистром специального назначения PORTB, который может включать и выключать подключенные к его выводам светодиоды, для этого нужно лишь записать 0 или 1 в нужный бит данного регистра. Ну и закончили мы на том, что любую написанную программу нужно «зацикливать» либо полностью, либо вставлять холостой цикл в конце программы, чтобы МК не выходил за пределы программы.



© Ирков Алексей Николаевич
Июнь 2007
http://www.kernelchip.ru



© KERNELCHIP 2006 - 2023