Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /home3/mrwolfeu/public_html/zps-electronics.com/index.php on line 14

Deprecated: Function ereg() is deprecated in /home3/mrwolfeu/public_html/zps-electronics.com/index.php on line 27
Световой эффект на pic16f84 <- ZPS-electronics
 
   

  Звук
  Свет
  Радио
  Питание
  PC & GSM
  PIC и AVR
  Разное
  Справка
  Верстак
  Техника


  ФОРУМ

 

 

 

схемы электронных устройств

 

 

все темы форума одной страницей

 

 

DIY Projects

Световой эффект на pic16f84  
Автор: нет   Добавлено: 30.06.2004


Предисловие:

В начале года я узнал про pic’и, мне стало интересно и я решил ими позаниматься. Заказал нахаляву sample пики, перечитал pic16f84.narod.ru и микрочиповскую документацию о среднем семействе (pic16) и о конкретных пиках (16f84a и 16f628), сваял макетку как в статье о световых эффектах (решил собрать все на 1 плате) и стал искать туториалы, гайды, книги, сайты, вобщем по чему можно учиться писать для пиков. Ничего особенного я не нашел и начал учиться сам. И вот первые плоды моего творчества: я написал две работающие программы на MASMе (хоть я и не работал на С, но мне кажется с ограниченным объемом памяти ASM будет выгоднее, так как он компактнее и красивее чем скомпилированный С), одна без управления (это проще, да и на момент её написания у меня ещё не было кнопок), вторая попроще эффект но зато можно регулировать скорость (в этой проге эффект не главное =)). Теперь я хочу поделиться своей работой с теми кто ещё только начинает (начинающие начинающим =)), имхо кому-то это будет интересно. Профам прошу не пинать особенно за неграмотность, а по возможности исправлять.

Итак, поехали.

Начнём с того, что всё (или почти всё), с чем мы будем работать в программе – это регистры. По архитектуре пиков, память программ и память оперативная (данных) разделены. Память программы имеет разрядность 14 бит и энергонезависимая, там хранится собственно исполняемая программа, эта память недоступна для чтения из программы. Память данных (RAM) имеет разрядность 8 бит (1 байт) и используется во время работы пика для хранения регистров (1 регистр – 1 байт), специальных и общих (я говорю здесь конкретно о 16f84). Специальные регистры контролируют работу микроконтроллера, общие предназначены для хранения данных пользователем. Память данных разнесена по банкам, в каждом банке может быть до 128 регистров (байтов), в 16f84 используется два банка, причем не полностью (в первом доступны адреса 00h-4Fh, во втором 80h-CFh). В каждом банке в начале хранятся специальные регистры – в первом по адресам 00h-0Bh включительно, остальное в первом – 68 регистров общего назначения (0Ch-4Fh), некоторые из специальных повторяются в обоих банках, их содержимое естественно и там и там одинаковое (самые необходимые и часто используемые регистры). Про регистры наверное всё.

Теперь команды. Есть несколько типов команд: байт-ориентированные – команды, в которых операндами (то, над чем совершается действие) являются любой регистр из памяти данных и в большинстве случаев W – рабочий (work) регистр. В остальных просто только один операнд (регистр из памяти) или его вообще нет (даже так). Байт-ориентированными они называются потому-что действия происходят с целым байтом данных. Важно: регистр из памяти может быть только одним операндом, второй обязательно W!

Бит-ориентированные команды: ну тут вообще просто. Операнды – регистр из RAM и собственно нужный из 8 битов из этого регистра (0h-7h).

Команды управления и операции с константами.. тут либо действие происходит между W и константой k, либо команда выполняет действие, слабо связанное с данными (CLRWDT – очистить WDT, предотвратить сброс по переполнению WDT (watch-dog timer), SLEEP – переход в режим пониженного энергопотребления, RETURN – возврат из подпрограммы (данные тут не зависят от пользователя))

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

Первая часть программы (ещё не сама программа, а вводная часть, некоторые данные для ассемблера)


        STATUS       EQU        03h
        PORTA        EQU        05h
        PORTB        EQU        06h
        TIME1        EQU        0Ch         ;
        TIME2        EQU        0Dh         ;
        TIME3        EQU        0Eh         ;Здесь я назначаю имена моих общих
        VARS         EQU        0Fh         ;регистров и указываю ассемблеру
        TEMP1        EQU        10h         ;их адреса в памяти
        TW           EQU        11h         ;

        C            EQU         0h
        DC           EQU         1h
        Z            EQU         2h         ;Это названия битов регистра
        PD           EQU         3h         ;STATUS
        TO           EQU         4h
        RP           EQU         5h
        TRISA        EQU         85h
        TRISB        EQU         86h

EQU (equal) – знак для ассемблера что вместо какогото символа (то, что слева от EQU) подставлять числовое значение (справа от EQU). Сколько бы мы в этой части не писали (можем хоть все 68 общих расписать и все биты специальных регистров), это не отразится на объеме программы в микроконтроллере. А теперь о каждом специальном регистре по-подробнее.

STATUS – очень важный регистр, содержит 6 битов, управляющих некоторыми аспектами работы мк, в частности:

Бит 5: RP: выбор активного банка памяти. 0 – банк 0 (00h-7Fh), 1 – банк 1 (80h-FFh)
Ничего сложного, просто надо помнить, что если нужный регистр в банке 1, то перед операции с ним над переключиться в банк памяти 1

Бит 0: C: бит переноса, если был перенос из старшего бита (7h), флаг устанавливается в 1

Остальные бита регистра STATUS в программе не используются

PORTA – используются только биты 4h-0h, отображают состояние pin’ов ввода/вывода порта А: RA4-RA0

PORTB – тоже самое, только порт B: RB7-RB0

TRISA – отвечает за состояние каждого pin’а (ввод или вывод), 0 – вывод, 1 – ввод
TRISB – тоже самое только порт B

Названия общих регистров произвольные, лишь бы самому было понятно.

Часть вторая (короткая) – самое начало программы


        ORG        0
GOTO BEGIN


ORG – это тоже команда для ассемблера, она обозначает адрес в памяти программ, где размещаются нижеследующие команды.

Впринципе, этой части может и не быть. Дело в том, что она должна быть в программе, использующей прерывания. Если происходит разрешенное прерывание, адрес, на котором сейчас выполняется программа аппаратно записывается в стек (без участия пользователя), мк переходит на адрес 0004h (вектор прерывания) и выполняет команды, находящиеся там. Возврат из прерывания производится пользователем (программой). При этом выполнение команд возвращается на записанный в стеке адрес. Если бы мы использовали прерывание, мы бы дополнили эту части следующими строками:


        ORG        4h
        Траляля
        Траляля
        Траляля
        (программа обработки прерывания)

В последствии часто придется использовать прерывания, поэтому этот пример полезен для запоминания.

Часть третья – инициализация.


        ORG         100h           ;1
BEGIN
        CLRF        PORTA          ;2
        CLRF        PORTB          ;3
        BSF         STATUS,RP      ;4
        CLRF        TRISA          ;5
        CLRF        TRISB          ;6
        BCF         STATUS,RP      ;7
        BSF         PORTA,3        ;8

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

BEGIN – это метка, она не занимает памяти, служит для как операнд в командах CALL и GOTO (пока мы пишем программу), ассемблер заменяет её на адрес команды, находящейся после этой метки (в данном случае CLRF PORTA)

Итак, первая команда:

CLRF f – очистить регистр f, короче говоря затереть его нулями

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

BCF f,b – сбросить бит b в регистре f
BSF f,b – установить бит b в регистре f

В четвертой строчке мы выбираем банк1 (т.к. регистры TRISA и TRISB находятся в банке1)
Обнуляем их (ставим все pin’ы как выходы)
Выбираем банк0

Восьмой строчкой мы гасим светодиод на выходе RA3 (он горит когда на RA3 – его катоде – низкий уровень сигнала)

Теперь надо рассказать про сам эффект:

Всегда зажжены 2 светодиода, двигаются они по такой схеме (4 фазы движения)

Начало
7 6 5 4 3 2 1 0
X X - - - - - -

Фаза 1

Светодиод 6 двигается вправо до конца:

7 6 5 4 3 2 1 0
X - - - - - - X

Фаза2

Светодиод 7 перемещается в положение 1

7 6 5 4 3 2 1 0
- - - - - - X X

Фаза3

Светодиод 1 (тот же) двигается обратно

7 6 5 4 3 2 1 0
X - - - - - - X

Фаза4

Светодиод 0 перемещается в положение 6

7 6 5 4 3 2 1 0
X X - - - - - -

Потом все начинается сначала. При «столкновении» светодиодов, когда зажжены два соседних (крайних) светодиода, мигает сигнальный LED (RA3).

Часть четвертая - начало программы. Эта часть выполняется один раз.

MOVLW – загрузить константу в регистр W
MOVWF – переслать содержимое W в регистр f


        MOVLW        7h
        MOVWF        TIME1
        BCF          STATUS,C
        CLRF         TEMP1
        BSF          TEMP1,06h
        BSF          PORTB,07h
        BSF          PORTB,06h


В первых двух строках мы загружаем 7h в регистр TIME1. Зачем мы это делаем я расскажу позже.

Состояние общих регистров после включения питания непредсказуемо, поэтому мы обнуляем TEMP1

Устанавливаем бит 6 в TEMP1

Делаем начальное состояние светодиодов (до первой фазы) – горят 7 и 6
Если бы нам надо было установить более 2 битов в PORTB, экономичнее было бы использовать команды:


        MOVLW B’11100000’
        MOVWF PORTB

,так как BSF бы занимали три строки.


Часть пятая – фаза 1

RRF f,d - сдвиг вправо регистра f, сохранить результат в f, если d=1 и в W, если d=0. Этот сдвиг осуществляется с использованием бита STATUS,C. Если бит 0 установлен в 1, C при сдвиге установится в 1. Другими словами, бит 0 переносится в STATUS,C.

RLF f,d - тоже самое, только сдвиг влево, соответственно в C переносится бит 7.

IORWF f,d - логическое побитное ИЛИ между W и f. При выполнении ИЛИ (OR) между двумя битами в результат записывается 1, если хоть 1 бит был равен 1. Если оба = 0, результат 0.

CALL <метка> - вызов подпрограммы. GOTO отличается от CALL тем, что из последнего можно вернутся командой RETURN (RETLW, RETFIE).

BTFSS f,b - проверить бит b в регистре f, пропустить следующую команду, если b=1

BTFSC f,b - пропустить следующую команду, если b=0


LOOP1
        MOVLW        B'10000000'        ;1
        RRF          TEMP1,1            ;2
        IORWF        TEMP1,0            ;3
        MOVWF        PORTB              ;4
        CALL         PAUSE              ;5
        BTFSS        TEMP1,0h           ;6
        GOTO         LOOP1              ;7
        RRF          TEMP1,1            ;8


1. Загружаем в W постоянную состовляющую фазы 1.
2. Двигаем вправо TEMP1, оставляем результат в TEMP1
3. командой ИЛИ мы «накладываем» переменную состовляющую на постоянную, результат в W
4. Переносим наложение в PORTB
5. Вызываем паузу (расскажу скоро)
6. Проверяем, не достиг ли двигающийся светодиод места назначения (см. описание фаз)
7. Нет, повторяем движение первой фазы
8. Да, переносим в STATUS,C двигающийся сд (нужно для следующей фазы)

Когда выполнение дойдет до строки 8, будут гореть два крайних светодиода (как на схеме).

Пауза – единственная подпрограмма

DECFSZ f,d - уменьшить на 1 f, сохранить в d (W если 0 и f если 1), пропустить следующую команду, если результат = 0

DECF f,d - уменьшить f на 1

Сначала надо сказать, что TIME1 определяет время задержки (1 = примерно 10мс при ~4MHz)


PAUSE
        MOVWF        TW
        MOVF         TIME1,W
        MOVWF        VARS
        MOVLW        0FFh
        MOVWF        TIME2
        MOVLW        0Dh
        MOVWF        TIME3
PAUSELOOP
        DECFSZ        TIME2,1          ;1
        GOTO          PAUSELOOP        ;2
        DECF          TIME2            ;3
        DECFSZ        TIME3,1          ;4
        GOTO          PAUSELOOP        ;5
        DECF          TIME2            ;6
        MOVLW         0Dh              ;8
        MOVWF         TIME3            ;9
        DECFSZ        TIME1,1          ;10
        GOTO          PAUSELOOP        ;11
        MOVF          VARS,W           ;12
        MOVWF         TIME1            ;13
        MOVF          TW,W             ;14
        RETURN

До метки PAUSELOOP мы сохраняем значения W и TIME1, пишем некоторые константы в TIME2 и TIME3 (они обеспечивают нужное время паузы)

В первых двух строчках уменьшаем TIME2 до 0, потом восстанавливаем значение TIME2 (00h-1 = FFh)

Ходим через GOTO на 5 строке пока TIME3 не станет 0

Восстанавливаем значения TIME2 и TIME3.

Если TIME1 больше 1, проходим все заново количество раз, равное TIME1 – 1 (один раз мы уже прошли)

Если TIME1 = 0, восстанавливаем сохранённые W и TIME1, выходим из подпрограммы паузы.

TIME1 мы сохраняем и восстанавливаем для того чтобы не писать его по много раз если он всегда один и тот же (процедура паузы же универсальная и может использоваться в другой программе).

Подпрограммы удобно держать в самом конце программы.

Часть шестая – фаза 2


LOOP2
        MOVLW        B'00000001'
        RRF          TEMP1,1
        IORWF        TEMP1,0
        MOVWF        PORTB
        CALL         PAUSE10
        BTFSS        TEMP1,1h
        GOTO         LOOP2
        BCF          PORTA,03h
        CALL         PAUSE5
        BSF          PORTA,03h

Здесь мы делаем очень похожие действие, что в части пятой. Различие в том, что в конце этой фазы светодиоды у нас «сталкиваются», поэтому надо зажечь RA3 на момент. Для этого используется процедура PAUSE5 (см листинг в конце), имеющая фиксированное время паузы (около 5мс).

Часть шестая и седьмая – фазы 3 и 4


LOOP3
        MOVLW        B'00000001'
        RLF          TEMP1,1
        IORWF        TEMP1,0
        MOVWF        PORTB
        CALL         PAUSE10
        BTFSS        TEMP1,7h
        GOTO         LOOP3
        RLF          TEMP1,1
LOOP4
        MOVLW        B'10000000'
        RLF          TEMP1,1
        IORWF        TEMP1,0
        MOVWF        PORTB
        CALL         PAUSE10
        BTFSS        TEMP1,6h
        GOTO         LOOP4
        BCF          PORTA,03h
        CALL         PAUSE5
        BSF          PORTA,03h
        GOTO         LOOP1

Опять же, всё очень похоже, отличие лишь в том, что TEMP1 мы двигаем влево. В конце фазы 4 мы переходим в начало, имея позицию светодиодов, как в начале.

Сложив все части программы и добавив кое-что ещё, можно получить следующий листинг:


        STATUS       EQU        03h
        PORTA        EQU        05h
        PORTB        EQU        06h

        TIME1        EQU        0Ch
        TIME2        EQU        0Dh
        TIME3        EQU        0Eh
        VARS         EQU        0Fh
        TEMP1        EQU        10h
        TW           EQU        11h

        C            EQU        0h
        DC           EQU        1h
        Z            EQU        2h
        PD           EQU        3h
        TO           EQU        4h
        RP           EQU        5h

        TRISA        EQU        85h
        TRISB        EQU        86h

        ORG 0
        GOTO         BEGIN

        ORG 100h
BEGIN
        CLRF         PORTA
        CLRF         PORTB
        BSF          STATUS,RP
        CLRF         TRISA
        CLRF         TRISB
        BCF          STATUS,RP
        BSF          PORTA,3

        MOVLW        7h
        MOVWF        TIME1
        BCF          STATUS,C
        CLRF         TEMP1
        BSF          TEMP1,06h
        BSF          PORTB,07h
        BSF          PORTB,06h

LOOP1
        MOVLW        B'10000000'
        RRF          TEMP1,1
        IORWF        TEMP1,0
        MOVWF        PORTB
        CALL         PAUSE
        BTFSS        TEMP1,0h
        GOTO         LOOP1
        RRF          TEMP1,1
LOOP2
        MOVLW        B'00000001'
        RRF          TEMP1,1
        IORWF        TEMP1,0
        MOVWF        PORTB
        CALL         PAUSE
        BTFSS        TEMP1,1h
        GOTO         LOOP2
        BCF          PORTA,03h
        CALL         PAUSE5
        BSF          PORTA,03h
LOOP3
        MOVLW        B'00000001'
        RLF          TEMP1,1
        IORWF        TEMP1,0
        MOVWF        PORTB
        CALL         PAUSE
        BTFSS        TEMP1,7h
        GOTO         LOOP3
        RLF          TEMP1,1
LOOP4
        MOVLW        B'10000000'
        RLF          TEMP1,1
        IORWF        TEMP1,0
        MOVWF        PORTB
        CALL         PAUSE
        BTFSS        TEMP1,6h
        GOTO         LOOP4
        BCF          PORTA,03h
        CALL         PAUSE5
        BSF          PORTA,03h
        GOTO         LOOP1

PAUSE5
        MOVWF        TW
        MOVF         TIME1,W
        MOVWF        VARS
        MOVLW        01h
        MOVWF        TIME1
        MOVLW        0FFh
        MOVWF        TIME2
        MOVLW        15h
        MOVWF        TIME3
        GOTO         PAUSELOOP
PAUSE
        MOVWF        TW
        MOVF         TIME1,W
        MOVWF        VARS
        MOVLW        0FFh
        MOVWF        TIME2
        MOVLW        0Dh
        MOVWF        TIME3
PAUSELOOP
        DECFSZ       TIME2,1
        GOTO         PAUSELOOP
        DECF         TIME2
        DECFSZ       TIME3,1
        GOTO         PAUSELOOP
        DECF         TIME2
        MOVLW        0Dh
        MOVWF        TIME3
        DECFSZ       TIME1,1
        GOTO         PAUSELOOP
        MOVF         VARS,W
        MOVWF        TIME1
        MOVF         TW,W
        RETURN

        END

Остаётся сказать, что всё, что находится после знака ; ассемблер игнорирует, а команда END нужна ассемблеру, чтобы понять, что дальше кода (хода =)) нет.


Статью написал Froxy.







Комментарии посетителей:
drv
Froxy
спасибо
буду пробовать потихоньку читать.. 8)
SuperXAM
На каком ассемблере делал и ссылку на него плиз.
ZPS
MASM
качается на сайте microchip.com
Froxy
Кстати, мой вариант платы:
front
back
забыл прорисовать дорожку smile.gif
ZPS
Froxy
эээ... а не проще ли было одним проводком соединить? smile.gif
Froxy
всмысле?
гость_26.143.67
забыл рассказать про опрос клавиатуры, раз уж она у тебя на плате есть. Или я не внимателен (smile.gif)???
Добавить комментарий Перейти к обсуждению на форуме


DIY Projects