Свежие обсуждения
Микроконтроллеры

Ликбез по программированию PIC , vol. # 2

1 14 17

Получил. Интересно будет сравнить.

Рассказываю о своём первом варианте программы организации динамической индикации на mb.

Для смены отображающихся разрядов использованы прерывания по переполнению таймера TMR0. Немного о прерываниях вообще. Прерывания позволяют следить за какими-либо определенными событиями, не отрываясь от выполнения основной программы. Когда происходит прерывание, контроллер временно останавливает выполнение основной программы и запоминает точку, где это случилось (адрес заносится в стек). Затем, если необходимо, контроллер анализирует - какое именно прерывание произошло (если источников могло быть несколько). Определив источник прерывания, контроллер выполняет предписанные ему программистом для этого случая действия, а затем возвращается к выполнению основной программы - в точку, которую ранее запомнил.
Прерывание представляет собой по сути аппаратный вызов специально предусмотренной подпрограммы (обработчика прерываний), расположенной в памяти программ по определенному адресу. Для PIC16 это адрес 4, т.е. пятая по счёту ячейка от начала памяти программ. Поэтому при программировании на асме в одну из первых четырех ячеек памяти программ как правило помещают команду безусловного перехода (goto) к началу основной части программы - для обхода обработчика прерываний. Пример можем увидеть в первом "ликбезе". Обработчик прерываний там предусмотрен, хотя и не задействован.

PIC переходит по предусмотренному адресу (вектору прерываний) при выполнении следующих условий:
1. Установлен бит GIE регистра INTCON. Этот бит разрешает (1) или запрещает (0) вообще все прерывания одним махом. Устанавливается и сбрасывается программно, т.е. командой программиста.
2. Установлен бит, разрешающий какое-либо определенное прерывание. В нашем случае это бит T0IE регистра INTCON. Каждому прерыванию соответствует особый бит в регистрах управления прерываниями. Для PIC16F628 это регистры INTCON и PIE1. Регистр PIE1 находится в банке 1, его адрес 08Ch, и предназначен для управления прерываниями от периферийных модулей. Прерывания от периферии можем также разрешать/запрещать битом PEIE регистра INTCON - сразу одним битом семерых. Эти все биты также устанавливаются/сбрасываются программно. Сбросив соответствующий бит, мы можем "замаскировать" какое-либо прерывание.
3. Установлен флаг запроса на прерывание. Этот флаг - также бит определенного регистра (для PIC16F628 - INTCON и PIR1). Флаг запроса на прерывание (в нашем случае это бит T0IF регистра INTCON) устанавливается, как правило, АППАРАТНО - при возникновении какого-либо события. В нашем случае событие - это переполнение таймера TMR0. Т.е. в момент перехода значения в регистре TMR0 от FF к 00 флаг T0IF сам устанавливается в 1 - выпрыгивает как чёртик из коробочки. ВСЕГДА. Независимо от состояния битов GIE, T0IE и прочих. Флаги запроса на прерывание могут быть установлены/сброшены и программно. Никто не помешает, например, нам самим установить флаг T0IF:
bsf INTCON, T0IF ; на асме
INTCON.T0IF = 1 ' на mikrobasic
При этом, если установлены биты GIE и T0IE, точно так же произойдет переход на обработку прерывания. Т.е. контроллеру безразлично, кто установил флаг - таймер (аппаратно) или сам программист. Иногда эта хитрость бывает полезна.

Так вот, при выполнении сих трёх условий PIC сохранит следующее значение программного счетчика в стеке, т.е. запомнит - в какой точке произошло прерывание. Затем АППАРАТНО сбросит бит GIE и перейдет по адресу 04, где и располагается заботливо подготовленная нами подпрограмма - обработчик прерываний. После выполнения этой подпрограммы по команде retfie произойдет возврат в исходную точку, при этом будет АППАРАТНО установлен бит GIE. Манипуляции с битом GIE позволяют обрабатывать запросы на прерывания по очереди, чтоб они друг другу не мешали. Флаг запроса на прерывание аппаратно НЕ СБРАСЫВАЕТСЯ! Это - забота программиста. В обработчике прерываний следует предусмотреть сброс флага запроса на прерывание. Спрятать чёртика обратно в коробочку. Иначе получится "заколдованный круг" - контроллер обрабатывает запрос, возвращается к выполнению основной программы, а флаг-то установлен! И опять то же самое прерывание, и так бесконечно.

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

Теперь немного о самом обработчике. Прежде всего следует помнить, что регистры W, STATUS, а также при необходимости FSR и PCLATH (да и другие) могут использоваться программистом как в основной программе, так и в обработчике прерываний. Как сортир и ванная в коммуналке. Прерывание может возникнуть внезапно, в любой момент - как сосед, которому невтерпёж. Поэтому содержимое регистров W, STATUS, а при необходимости FSR, PCLATH (и др.) следует перед началом обработки запроса на прерывание тщательно сохранить. И затем перед возвратом из обработчика заботливо восстановить. В общем, сосед за собой убирать должен. Так, чтобы основная программа совершенно не почувствовала прерывания, и в счастливом неведении успешно продолжила свою работу. Это называется "сохранение контекста" и "восстановление контекста". Иначе неприятности будут.

mikrobasic предусматривает при компиляции сохранение и восстановление контекста сам, автоматически - регистры W, STATUS, FSR, PCLATH. Это облегчает жизнь программиста.

О прерываниях рассказано также в "Радио" № 12 за 2001г., стр.23-25 (очень рекомендую прочитать). И ещё вот:
http://www.radio.ru/ubb/Forum4/HTML/000769.html

 

ОК! Ждемс.

 

Отлично написано! Коротко, но вместе с тем очень понятно и информативно!
А я при обработке внешних прерываний еще дополнительно проверяю лог. уровень на входе прерывания. Если не тот, то выхожу нахрен. Не знаю, нужен ли такой "памперс"? :)

 

Zandy, это от задачи зависит. Однозначно сразу не могу сказать.

Splav56, беру на вооружение префиксы g и l для обозначения глобальных и локальных переменных. Отличная задумка, очень полезно.

Продолжаю. Для организации динамической индикации предстоит поочередно отображать четыре разряда на индикаторе. Т.е. поочередно выводить информацию о состоянии сегментов на выводы PORTB, информацию о состоянии анодов - на выводы младшей тетрады PORTA.
Подключив перед восьмиразрядным таймером TMR0 предделитель с коэффициентом деления 16, получим прерывания с периодом 256*16=4096 мкс при тактовой частоте 4 МГц. Для этого запишем в регистр OPTION_REG двоичное число 00000011 (0x03 в шестнадцатиричном представлении). Подробности - см. стр. 20 даташита на PIC16F628.
Для хранения информации о состоянии сегментов предусмотрен массив gLED_DIGITS, состоящий из четырех элементов типа byte. Биты элементов массива - переменные gLED_DIGITS[0], gLED_DIGITS[1], gLED_DIGITS[2], gLED_DIGITS[3] - соответствуют сегментам разрядов индикатора (76543210 hgfedcba). gLED_DIGITS[0] - крайний правый разряд (младший), gLED_DIGITS[3] - крайний левый разряд (старший). Единицы соответствуют горящим сегментам, нули - погашенным.
Для выбора знакоместа предусмотрен массив констант cLED_NUMBER. Этот массив содержит четыре восьмиразрядных константы. Они определяют состояние выводов порта, к которому подключены аноды индикатора. Аноды в нашем случае подключены к RA0, RA1, RA2, RA3, поэтому массив содержит константы 00000001, 00000010, 00000100, 00001000 в двоичном представлении. В десятичном представлении это 1, 2, 4, 8. При записи этих констант в регистр PORTA активизируется соответствующий разряд индикатора.
Номер отображаемого разряда (0, 1, 2, 3) хранится в переменной gLED_COUNTER.
Далее предстоит решить простую задачу - гонять номер разряда по кругу (gLED_COUNTER = 0->1->2->3->0->1->2->3->0...), и выдавать информацию из элементов массива gLED_DIGITS в PORTB с инверсией (т.к. индикатор с общими анодами), информацию из элементов массива cLED_NUMBER - в PORTA. Выводы портов, разумеется, при этом должны быть настроены на выход.

Чтобы убедиться в правильной работе программы, достаточно записать в массив gLED_DIGITS желаемые комбинации сегментов и разрешить прерывания от TMR0. На индикаторе мы увидим вожделенные символы. Например ПU8O.

В учебной программе символы отображаются на индикаторе по мере нажатия на кнопки, по порядку. Новый символ появляется в правом разряде, остальные сдвигаются влево. Предусмотрено подавление дребезга. При нажатии на * отображается символ "|_", при нажатии на # - символ "_|". Если нажаты одновременно две кнопки - дополнительно отображается точка. Одновременно три и более кнопки - отображается "П".
Вот и проект для mb. Еще раз замечу, что это первый, сырой вариант. Хотя вполне работоспособный. Hardware tested.
http://www.pic16.nm.ru/likbez2/Test02_1.zip

 

picmaniac: Предусмотрено подавление дребезга.
Кстати, в микроБейсике для этого есть специальная функция "Button". Но, насколько я понимаю, предназначена она не для матрицы, а для одиночной кнопки.

 

picmaniac: беру на вооружение префиксы g и l для обозначения глобальных и локальных переменных. Отличная задумка, очень полезно.

Так проще не запутаться когда переменных становится много. А в чем провинились префиксы s (symbol) и c (const)? :о)

 

Ни в чём, они тоже при деле. Префикс "с" использован, а символов мне пока не потребовалось.

 

Всем привет.
Я написал небольшую программу для PIC, клавиатуру 21*5 линий, и хачу услышать комментарии и советы.
Особенно в области инициализации пика, какие флаги нужны какие нет, правильно ли настроены порты, и т.д.
Если есть ошибки в области логики самой программы, то тоже не стесняйтесь, хотя они и так все патом вылезут.

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

91480.asm

 

+ блок схема

 

не захотел цеплять