Система команд PIC16
Система команд PIC16 — 35 команд 8-битного ядра mid-range, на котором работают PIC16F877A, PIC16F628A и PIC12F675; каждая занимает одно 14-битное слово. Ниже — все команды с кодами, циклами и флагами, банки памяти данных и страницы памяти программ, примеры на ассемблере с подсчётом циклов и 14 команд, которые добавило ядро enhanced mid-range в PIC16F1xxx.
Обновлено Редакция GAW
Система команд PIC16 — набор машинных инструкций 8-битного ядра Microchip, которое в документации называется mid-range. На этом ядре построены, например, PIC16F877A, PIC16F628A и PIC12F675: у всех 35 команд, каждая — одно 14-битное слово памяти программ. Справочник составлен по даташитам Microchip PIC16F87XA (DS39582C) и PIC16F627A/628A/648A (DS40044G), раздел о новом ядре enhanced mid-range — по даташитам PIC16(L)F1825/1829 (DS40001440E) и PIC16(L)F18857/77 (DS40001825F). Обзор семейств PIC — на странице «Микроконтроллеры PIC».
Коротко
- 35 команд, каждая — одно 14-битное слово. У команд с операндами код операции занимает от 3 до 6 бит, остальное — операнды.
- Машинный цикл — 4 периода генератора: при 4 МГц команда длится 1 мкс, при 20 МГц — 200 нс. Переходы, вызовы, возвраты и сработавший пропуск занимают 2 цикла.
- Аккумулятор W: в байтовых командах бит d выбирает, куда записать результат — в W (d = 0) или обратно в регистр (d = 1).
- Банки по 128 байтов: в команде только 7 бит адреса, банк выбирают биты RP1:RP0 регистра STATUS.
- Страницы по 2K слов: CALL и GOTO несут 11 бит адреса, два старших бита 13-битного счётчика команд берутся из
PCLATH<4:3>. - Аппаратный стек на 8 уровней только для адресов возврата: команд PUSH и POP нет, переполнение ничем не отмечается.
Ядро и регистры
Ядро mid-range построено по гарвардской схеме: память программ состоит из 14-битных слов, память данных — из байтов, и шины у них раздельные. Арифметика и логика работают с аккумулятором W и одним байтом памяти данных. Регистры периферии и ОЗУ общего назначения в PIC адресуются одинаково и называются регистрами (file registers), отсюда буква f в операндах. Отдельного набора рабочих регистров, как r0–r31 у AVR, нет: в сложении участвует любой байт ОЗУ, но вторым операндом всегда служит W.
Регистры ядра, которые нужны для понимания команд, у PIC16F877A доступны из любого банка (таблица 2-1 даташита):
| Регистр | Адрес в банке 0 | Назначение |
|---|---|---|
| W | — | аккумулятор, в карте памяти данных его нет |
| INDF | 00h | обращение по адресу из FSR; физически регистра нет |
| PCL | 02h | младшие 8 бит счётчика команд, доступен для чтения и записи |
| STATUS | 03h | флаги C, DC, Z, биты TO и PD, выбор банка IRP, RP1, RP0 |
| FSR | 04h | указатель для косвенной адресации |
| PCLATH | 0Ah | буфер старших 5 бит счётчика команд |
| INTCON | 0Bh | разрешение и флаги прерываний, бит GIE |
Регистр STATUS
| Бит | Имя | Значение |
|---|---|---|
| 7 | IRP | банк при косвенной адресации: 0 — банки 0 и 1 (00h–FFh), 1 — банки 2 и 3 (100h–1FFh) |
| 6–5 | RP1:RP0 | банк при прямой адресации: 00 — 00h–7Fh, 01 — 80h–FFh, 10 — 100h–17Fh, 11 — 180h–1FFh |
| 4 | TO | 0 — сработал сторожевой таймер; 1 — после включения питания, CLRWDT и SLEEP |
| 3 | PD | 0 — после команды SLEEP; 1 — после включения питания и CLRWDT |
| 2 | Z | результат арифметической или логической операции равен нулю |
| 1 | DC | перенос из бита 3 в бит 4; при вычитании — инверсный заём |
| 0 | C | перенос из бита 7; при вычитании — инверсный заём; в RLF и RRF — выдвинутый бит |
После включения питания STATUS = 0001 1xxx, то есть выбран банк 0. Если STATUS сам служит приёмником команды, которая меняет флаги, запись в Z, DC и C не выполняется — их задаёт результат операции. Например, CLRF STATUS сбросит старшие биты, но установит Z. Поэтому даташит советует менять STATUS только командами BCF, BSF, SWAPF и MOVWF, которые флагов не трогают.
Форматы команд и операнды
Команды делятся на три группы: байтовые операции с регистром, битовые операции с регистром и операции с константой и управления. Поля 14-битного слова (рисунок 15-1 даташита DS39582C, номера битов сверху):
13 8 7 6 0
Байтовые | код (6) | d | f — адрес (7) |
13 10 9 7 6 0
Битовые | код (4) | b — бит (3) | f — адрес (7) |
13 8 7 0
С константой | код (6) | k — константа (8) |
13 11 10 0
CALL и GOTO | код (3) | k — адрес (11) |
Короткий код операции у CALL и GOTO оставляет место для 11-битного адреса, длинный у байтовых команд — для двух операндов. Именно переменная длина кода у команд с операндами, от 3 до 6 бит, позволяет уложить 35 команд в 14 бит (руководство DS33023A, раздел 5.2). У команд с одним операндом или без операндов код длиннее: у CLRF и MOVWF он занимает 7 бит, у CLRW — тоже 7 (остальные биты безразличны), а у RETURN, RETFIE, SLEEP и CLRWDT кодом служит всё 14-битное слово.
| Операнд | Значение |
|---|---|
| f | адрес регистра внутри банка, 7 бит: 0x00–0x7F |
| d | куда записать результат: 0 — в W, 1 — обратно в регистр f; если d не указан, ассемблер подставляет 1 |
| b | номер бита, 0–7 |
| k | 8-битная константа в командах с W и в RETLW, 11-битный адрес в CALL и GOTO |
| x | безразличный бит, ассемблер записывает 0 |
Как это выглядит в машинном коде:
| Команда | Двоичный код | Шестнадцатеричный |
|---|---|---|
addwf 0x20, w | 00 0111 0010 0000 | 0x0720 |
addwf 0x20, f | 00 0111 1010 0000 | 0x07A0 |
bsf 0x06, 3 | 01 0101 1000 0110 | 0x1586 |
movlw 0x55 | 11 0000 0101 0101 | 0x3055 |
goto 0x123 | 10 1001 0010 0011 | 0x2923 |
Таблица команд PIC16
Все 35 команд — по таблице 15-2 даташита PIC16F87XA; в даташите PIC16F628A таблица та же. В колонке «Циклы» указаны машинные циклы по 4 периода генератора; «1 (2)» означает 2 цикла, если пропуск сработал. «Приёмник» — W при d = 0 или регистр f при d = 1. Колонка «Флаги» перечисляет биты STATUS, которые команда может изменить.
Байтовые команды
| Команда | Действие | Циклы | Код | Флаги |
|---|---|---|---|---|
ADDWF f, d | W + f → приёмник | 1 | 00 0111 dfff ffff | C, DC, Z |
ANDWF f, d | W И f → приёмник | 1 | 00 0101 dfff ffff | Z |
CLRF f | 0 → f | 1 | 00 0001 1fff ffff | Z |
CLRW | 0 → W | 1 | 00 0001 0xxx xxxx | Z |
COMF f, d | инверсия всех битов f → приёмник | 1 | 00 1001 dfff ffff | Z |
DECF f, d | f − 1 → приёмник | 1 | 00 0011 dfff ffff | Z |
DECFSZ f, d | f − 1 → приёмник, пропустить следующую команду, если получился 0 | 1 (2) | 00 1011 dfff ffff | — |
INCF f, d | f + 1 → приёмник | 1 | 00 1010 dfff ffff | Z |
INCFSZ f, d | f + 1 → приёмник, пропустить следующую команду, если получился 0 | 1 (2) | 00 1111 dfff ffff | — |
IORWF f, d | W ИЛИ f → приёмник | 1 | 00 0100 dfff ffff | Z |
MOVF f, d | f → приёмник; при d = 1 — проверка f на ноль | 1 | 00 1000 dfff ffff | Z |
MOVWF f | W → f | 1 | 00 0000 1fff ffff | — |
NOP | нет операции | 1 | 00 0000 0xx0 0000 | — |
RLF f, d | сдвиг влево через C: бит 7 → C, C → бит 0 | 1 | 00 1101 dfff ffff | C |
RRF f, d | сдвиг вправо через C: бит 0 → C, C → бит 7 | 1 | 00 1100 dfff ffff | C |
SUBWF f, d | f − W → приёмник | 1 | 00 0010 dfff ffff | C, DC, Z |
SWAPF f, d | обмен старшей и младшей тетрад f → приёмник | 1 | 00 1110 dfff ffff | — |
XORWF f, d | W исключающее ИЛИ f → приёмник | 1 | 00 0110 dfff ffff | Z |
Битовые команды
| Команда | Действие | Циклы | Код | Флаги |
|---|---|---|---|---|
BCF f, b | 0 → бит b регистра f | 1 | 01 00bb bfff ffff | — |
BSF f, b | 1 → бит b регистра f | 1 | 01 01bb bfff ffff | — |
BTFSC f, b | пропустить следующую команду, если бит b = 0 | 1 (2) | 01 10bb bfff ffff | — |
BTFSS f, b | пропустить следующую команду, если бит b = 1 | 1 (2) | 01 11bb bfff ffff | — |
Команды с константой и управления
| Команда | Действие | Циклы | Код | Флаги |
|---|---|---|---|---|
ADDLW k | W + k → W | 1 | 11 111x kkkk kkkk | C, DC, Z |
ANDLW k | W И k → W | 1 | 11 1001 kkkk kkkk | Z |
CALL k | адрес возврата → стек, переход в подпрограмму; биты 12–11 адреса из PCLATH<4:3> | 2 | 10 0kkk kkkk kkkk | — |
CLRWDT | сброс сторожевого таймера и его предделителя, TO = 1, PD = 1 | 1 | 00 0000 0110 0100 | TO, PD |
GOTO k | безусловный переход; биты 12–11 адреса из PCLATH<4:3> | 2 | 10 1kkk kkkk kkkk | — |
IORLW k | W ИЛИ k → W | 1 | 11 1000 kkkk kkkk | Z |
MOVLW k | k → W | 1 | 11 00xx kkkk kkkk | — |
RETFIE | возврат из прерывания, GIE = 1 | 2 | 00 0000 0000 1001 | — |
RETLW k | k → W и возврат из подпрограммы | 2 | 11 01xx kkkk kkkk | — |
RETURN | возврат из подпрограммы | 2 | 00 0000 0000 1000 | — |
SLEEP | режим сна с остановленным генератором, TO = 1, PD = 0 | 1 | 00 0000 0110 0011 | TO, PD |
SUBLW k | k − W → W | 1 | 11 110x kkkk kkkk | C, DC, Z |
XORLW k | W исключающее ИЛИ k → W | 1 | 11 1010 kkkk kkkk | Z |
Любая команда, которая меняет счётчик команд, например addwf PCL, f, тоже выполняется за 2 цикла: второй цикл уходит на NOP (примечание 3 к таблице 15-2). Ещё два кода, OPTION (00 0000 0110 0010) и TRIS f (00 0000 0110 0fff), описаны в даташите PIC16F628A для совместимости со старыми PIC16C5X, но в таблицу 15-2 не входят: Microchip рекомендует их не использовать, а регистры OPTION_REG и TRISx записывать обычной MOVWF.
Флаги и сравнения
ADDWF, ADDLW, SUBWF и SUBLW меняют C, DC и Z; ANDWF, IORWF, XORWF, их варианты с константой, MOVF, INCF, DECF, COMF, CLRF и CLRW — только Z; RLF и RRF — только C. Команды пропуска DECFSZ и INCFSZ, пересылка MOVWF, SWAPF и битовые команды флагов не меняют.
Вычитание выполняется сложением с дополнительным кодом, поэтому флаг C после SUBWF и SUBLW означает «заёма не было»: C = 1, если результат положителен или равен нулю, и C = 0, если отрицателен (примеры к SUBLW в даташите PIC16F628A). Отдельных команд сравнения и условного перехода у PIC16 нет — сравнивают вычитанием, а ветвятся пропуском по флагу:
После movf val_b, w и subwf val_a, w (W = a − b, где a и b — значения val_a и val_b) | Признак |
|---|---|
a = b | Z = 1 |
a ≠ b | Z = 0 |
a ≥ b, числа без знака | C = 1 |
a < b, числа без знака | C = 0 |
a > b, числа без знака | C = 1 и Z = 0 |
; if (val_a >= val_b) goto a_ge_b -- unsigned 8-bit compare
movf val_b, w ; W = val_b
subwf val_a, w ; W = val_a - val_b, C = 1 when there was no borrow
btfsc STATUS, 0 ; C = 0 means val_a < val_b: skip the jump
goto a_ge_b
INCF и DECF флаг C не трогают, поэтому перенос в старший байт 16-битного счётчика ловят по Z:
; cnt_h:cnt_l += 1
incf cnt_l, f ; low byte + 1; Z = 1 when it wraps to 0x00
btfsc STATUS, 2 ; Z = 0: no carry, skip
incf cnt_h, f
Сложения с переносом у mid-range нет, и 16-битная сумма собирается из шести команд — всегда за 6 циклов, сработает пропуск или нет:
; sum_h:sum_l += add_h:add_l
movf add_l, w
addwf sum_l, f ; low bytes, C = carry out
btfsc STATUS, 0 ; no carry: skip the increment
incf sum_h, f ; propagate the carry
movf add_h, w
addwf sum_h, f ; high bytes
Сумма получается верной по модулю 65 536, но флаг C в конце не всегда показывает перенос из старшего байта: если INCF превратил 0xFF в 0x00, перенос потерялся, потому что INCF флаг C не меняет.
RLF и RRF сдвигают через C, так что для умножения или деления на 2 флаг C сначала сбрасывают: bcf STATUS, 0, затем rlf x, f — и в младший бит войдёт 0.
Банки памяти данных
Память данных разбита на банки по 128 байтов: в каждом сначала идут регистры специальных функций, затем ОЗУ общего назначения. В коде команды есть только 7 младших бит адреса, а банк задают биты RP1:RP0. У PIC16F877A четыре банка и 368 байтов ОЗУ; верхние 16 байтов каждого банка, 70h–7Fh, общие для всех банков, и обращение к ним не требует переключения. Часто используемые регистры — STATUS, PCLATH, INTCON, FSR — продублированы во всех банках.
; PIC16F877A: make RB0 an output and drive it high
bsf STATUS, 5 ; RP0 = 1: bank 1 (RP1 is 0 after reset)
bcf TRISB, 0 ; TRISB = 0x86, only 0x06 goes into the opcode
bcf STATUS, 5 ; back to bank 0
bsf PORTB, 0 ; PORTB = 0x06, RB0 = 1
Ассемблер помещает в команду младшие 7 бит адреса TRISB, а то, что это банк 1, программист обеспечивает сам. Если забыть переключить банк, bcf TRISB, 0 изменит регистр с адресом 06h в банке 0 — то есть PORTB. Директива BANKSEL подставляет нужные команды выбора банка автоматически и одинаково работает в MPASM и PIC Assembler.
Для косвенной адресации адрес кладут в FSR и обращаются к INDF. Полный 9-битный адрес составляют 8 бит FSR и бит IRP регистра STATUS:
; Clear 16 bytes at 0x20-0x2F (bank 0) through FSR/INDF
bcf STATUS, 7 ; IRP = 0: FSR points into banks 0 and 1
movlw 0x20
movwf FSR ; first byte
clear_next:
clrf INDF ; clears the byte FSR points to
incf FSR, f
movlw 0x30
xorwf FSR, w ; Z = 1 when FSR reaches 0x30
btfss STATUS, 2
goto clear_next
Страницы памяти программ и PCLATH
Счётчик команд PIC16F877A 13-битный и адресует 8K слов: адрес сброса 0000h, вектор прерывания 0004h. В CALL и GOTO помещается только 11 бит адреса, поэтому память программ делится на страницы по 2K слов, а два старших бита адреса берутся из PCLATH<4:3>:
| Страница | Адреса | PCLATH<4:3> |
|---|---|---|
| 0 | 0000h–07FFh | 00 |
| 1 | 0800h–0FFFh | 01 |
| 2 | 1000h–17FFh | 10 |
| 3 | 1800h–1FFFh | 11 |
У PIC16F628A с памятью 2048 слов страница одна, и страничные биты не нужны. При записи в PCL, как в табличном переходе, старшие биты счётчика берутся из PCLATH<4:0> целиком. RETURN, RETLW и RETFIE снимают со стека полный 13-битный адрес, но PCLATH не меняют: после вызова подпрограммы в другой странице следующий GOTO уйдёт не туда, если не вернуть PCLATH.
; Call a routine located in page 1 (0x0800-0x0FFF) from page 0
bsf PCLATH, 3 ; PCLATH<4:3> = 01: page 1
bcf PCLATH, 4
call far_routine ; low 11 address bits come from the opcode
bcf PCLATH, 3 ; page 0 again before the next GOTO or CALL here
Директива PAGESEL записывает страницу метки в PCLATH сама, а в PIC Assembler есть ещё псевдокоманды fcall и ljmp, которые совмещают выбор страницы и переход (руководство DS50002973B, раздел 3.4).
Табличный переход и RETLW
Самый простой способ хранить константы в памяти программ PIC16 — таблица из команд RETLW: подпрограмма прибавляет к PCL смещение из W и попадает на нужную RETLW, которая возвращается с константой в W. Пример — коды цифр для семисегментного индикатора с общим катодом, сегменты a–g на битах 0–6:
; In: W = digit 0...3
; Out: W = segment pattern; the table must not cross a 256-word boundary
movlw high(seg_table) ; PCLATH = upper bits of the table address
movwf PCLATH
movf digit, w
call seg_table ; 2 cycles
movwf PORTB
; ...
seg_table:
addwf PCL, f ; PC = PCLATH:(PCL + W), 2 cycles
retlw 0x3F ; 0: a b c d e f
retlw 0x06 ; 1: b c
retlw 0x5B ; 2: a b d e g
retlw 0x4F ; 3: a b c d g
PCL складывается с W как 8-битное число, перенос в PCLATH не идёт. Если таблица пересекает границу блока в 256 слов, переход с большим индексом окажется в начале того же блока — даташит отдельно предупреждает об этом в разделе о вычисляемом переходе.
Стек и прерывания
Стек PIC16F877A — 8 уровней по 13 бит. Он не входит ни в память программ, ни в память данных, указатель стека недоступен. В стек попадает адрес возврата при CALL и при входе в прерывание, снимается он командами RETURN, RETLW и RETFIE. Стек кольцевой: девятый вызов затрёт адрес первого, и никакого флага переполнения нет. Прерывание тоже занимает уровень, поэтому основная программа может вложить вызовы не глубже семи уровней, если обработчик сам никого не вызывает.
При входе в прерывание процессор сбрасывает GIE, кладёт адрес возврата в стек и переходит на 0004h, а RETFIE снова устанавливает GIE. W, STATUS и PCLATH аппаратно не сохраняются — это делает обработчик. Переменные для них удобно держать в общей области 70h–7Fh, доступной из любого банка:
; Interrupt entry at 0x0004 (PIC16F876A/877A)
w_save equ 0x70 ; common RAM: same byte in every bank
status_save equ 0x71
pclath_save equ 0x72
isr:
movwf w_save ; W first: the bank is still unknown
swapf STATUS, w ; SWAPF copies STATUS without touching Z
clrf STATUS ; bank 0, IRP = 0
movwf status_save ; stored with nibbles swapped
movf PCLATH, w
movwf pclath_save
clrf PCLATH ; page 0 for GOTO and CALL inside the handler
; ... handle and clear the interrupt flags ...
movf pclath_save, w
movwf PCLATH
swapf status_save, w ; swap back: original STATUS, bank bits included
movwf STATUS
swapf w_save, f ; two SWAPFs restore W
swapf w_save, w ; without changing Z
retfie ; pops the return address, sets GIE
Последовательность повторяет пример 14-1 даташита DS39582C (стр. 154), комментарии наши. SWAPF здесь нужна по двум разным причинам. STATUS сохраняется командой SWAPF и потому лежит в памяти с переставленными тетрадами: при восстановлении вторая SWAPF возвращает исходный вид, а MOVWF STATUS записывает флаги и биты банка. Для W важно другое: STATUS к этому моменту уже восстановлен, и movf w_save, w испортила бы флаг Z, а две SWAPF подряд возвращают W, не меняя флагов. Флаг источника прерывания обработчик должен сбросить сам до выхода, иначе прерывание повторится сразу после RETFIE.
Общая область 70h–7Fh есть у PIC16F876A/877A и у PIC16F628A. У PIC16F873A/874A её нет: там переменную для W резервируют с одинаковым смещением в банках 0 и 1 (раздел 14.12 даташита). У PIC12F675 ОЗУ — это 20h–5Fh, и вся эта область одинаково видна из обоих банков (даташит DS41190G, раздел 2.2), так что переменные кладут туда.
Пример: задержка с подсчётом циклов
При кварце 4 МГц машинный цикл длится 1 мкс, и задержку удобно считать прямо в циклах:
; PIC16F877A, PIC16F628A (common RAM at 0x70-0x7F; on PIC12F675 use 0x20-0x5F)
; Delay of W milliseconds at Fosc = 4 MHz (1 cycle = 1 us); W = 0 gives 256 ms
ms_count equ 0x73
loop_count equ 0x74
delay_ms: ; "call delay_ms": 2 cycles
movwf ms_count ; 1
ms_loop:
movlw 0xF9 ; 1 249 inner passes
movwf loop_count ; 1
inner:
nop ; 1
decfsz loop_count, f ; 1, or 2 on the last pass
goto inner ; 2
decfsz ms_count, f ; 1, or 2 on the last millisecond
goto ms_loop ; 2
return ; 2
Проход внутреннего цикла занимает 4 цикла, последний — 3, поэтому 249 проходов дают 4 · 249 − 1 = 995 циклов. Вместе с MOVLW, MOVWF, DECFSZ и GOTO одна миллисекунда — ровно 1000 циклов, а последняя на цикл короче. С вызовом, первой MOVWF и возвратом:
При N = 10 это 10 004 мкс. Прерывания удлиняют задержку на время своих обработчиков; для точных интервалов лучше таймер.
Ядро enhanced mid-range: PIC16F1xxx
В семействах PIC12F1xxx и PIC16F1xxx — например, PIC16F1825, PIC16F1829 и PIC16F18877 — работает обновлённое ядро enhanced mid-range. Слово команды осталось 14-битным, и все 35 команд на месте, но их стало 49 — к ним добавились 14 новых, — а модель памяти заметно изменилась.
| Mid-range: PIC16F877A | Enhanced mid-range: PIC16F1825 | |
|---|---|---|
| Команды | 35 | 49 |
| Частота и цикл | до 20 МГц, 200 нс | до 32 МГц, 125 нс |
| Счётчик команд | 13 бит, до 8K слов | 15 бит, до 32K слов; CALL и GOTO берут из PCLATH<6:3> четыре бита |
| Стек | 8 уровней × 13 бит, без признаков переполнения | 16 уровней × 15 бит, флаги STKOVF и STKUNF, сброс при переполнении (настраивается битом STVREN), доступ через TOSH, TOSL и STKPTR |
| Выбор банка | RP1:RP0 и IRP в STATUS | регистр BSR и команда MOVLB; у PIC16F1825 32 банка по 128 байтов |
| Банк изнутри | регистры периферии и ОЗУ | 12 регистров ядра, 20 регистров периферии, до 80 байтов ОЗУ и 16 байтов общей памяти |
| Косвенная адресация | 8-битный FSR, INDF и бит IRP | два 16-битных указателя FSR0 и FSR1, видят всю память данных и память программ |
| Контекст при прерывании | сохраняет программа | W, STATUS (кроме TO и PD), BSR, FSR и PCLATH сохраняются в теневых регистрах банка 31 и восстанавливаются при RETFIE |
Регистры ядра — INDF0, INDF1, PCL, STATUS, FSR0L, FSR0H, FSR1L, FSR1H, BSR, WREG, PCLATH и INTCON — занимают первые 12 адресов каждого банка, так что переключать банк ради них не нужно. Через FSR память ОЗУ видна и как непрерывная область: адреса 0x2000–0x29AF указывают на 80-байтные блоки ОЗУ всех банков подряд, и буфер может быть больше одного банка. Если установлен старший бит FSRnH, младшие 15 бит FSR адресуют память программ, и через INDF читается младший байт слова — с одним дополнительным циклом.
14 новых команд
| Команда | Действие | Циклы | Код | Флаги |
|---|---|---|---|---|
ADDWFC f, d | W + f + C → приёмник | 1 | 11 1101 dfff ffff | C, DC, Z |
SUBWFB f, d | f − W − заём → приёмник | 1 | 11 1011 dfff ffff | C, DC, Z |
ASRF f, d | арифметический сдвиг вправо: бит 7 сохраняется, бит 0 → C | 1 | 11 0111 dfff ffff | C, Z |
LSLF f, d | логический сдвиг влево: бит 7 → C, в бит 0 входит 0 | 1 | 11 0101 dfff ffff | C, Z |
LSRF f, d | логический сдвиг вправо: бит 0 → C, в бит 7 входит 0 | 1 | 11 0110 dfff ffff | C, Z |
MOVLB k | k → BSR, выбор банка | 1 | 00 0000 001k kkkk | — |
MOVLP k | 7-битная константа → PCLATH | 1 | 11 0001 1kkk kkkk | — |
BRA k | относительный переход на PC + 1 + k, k от −256 до 255 | 2 | 11 001k kkkk kkkk | — |
BRW | переход на PC + 1 + W, W без знака | 2 | 00 0000 0000 1011 | — |
CALLW | вызов по адресу PCLATH<6:0>:W | 2 | 00 0000 0000 1010 | — |
RESET | программный сброс микроконтроллера | 1 | 00 0000 0000 0001 | — |
ADDFSR FSRn, k | FSRn + k → FSRn, k от −32 до 31 | 1 | 11 0001 0nkk kkkk | — |
MOVIW | INDFn → W: ++FSRn, --FSRn, FSRn++, FSRn-- или k[FSRn] | 1 | 00 0000 0001 0nmm, 11 1111 0nkk kkkk | Z |
MOVWI | W → INDFn в тех же пяти режимах | 1 | 00 0000 0001 1nmm, 11 1111 1nkk kkkk | — |
Таблица — по таблице 29-3 даташита PIC16(L)F1825/1829. Если пересчитать мнемоники в самой таблице 29-3, их окажется 51: кроме 49 команд ядра там перечислены унаследованные OPTION и TRIS, которые Microchip в число команд не включает, — 49 = 35 + 14. Поле mm в MOVIW и MOVWI задаёт режим: 00 — преинкремент, 01 — предекремент, 10 — постинкремент, 11 — постдекремент; n выбирает FSR0 или FSR1. Изменение FSR флагов не трогает. Если команда обращается к INDFn, а соответствующий FSR указывает в память программ, она занимает на один цикл больше.
Ширина MOVLB зависит от кристалла. У PIC16F1825 и PIC16F1829 в BSR 5 бит и код 00 0000 001k kkkk, у PIC16F18877 BSR 6-битный (BSR<5:0>, банки 0–63), и MOVLB кодируется как 00 0001 01kk kkkk (таблица 36-4 даташита DS40001825F). При переносе программы между кристаллами это надо учитывать.
Часть новых команд заняла коды, в которых у mid-range стояли безразличные биты x: ADDWFC — в области SUBLW (11 110x), ASRF, LSLF и LSRF — в области RETLW (11 01xx), MOVLP, ADDFSR и BRA — в области MOVLW (11 00xx), MOVIW и MOVWI с индексом k[FSRn] — в области ADDLW (11 111x). Поэтому у PIC16F1825 коды этих команд сужены: например, MOVLW — 11 0000 kkkk kkkk вместо 11 00xx kkkk kkkk; уже стали и коды CLRW и NOP. Остальные новые команды — SUBWFB, BRW, CALLW, RESET, короткие формы MOVIW и MOVWI и 5-битная MOVLB — получили коды, которые у mid-range не использовались вовсе (DS40044G: неиспользуемые коды зарезервированы); только MOVLB 0 (00 0000 0010 0000) совпадает с одним из вариантов NOP mid-range (00 0000 0xx0 0000). Ассемблер записывает безразличные биты нулями, поэтому коды MOVLW, RETLW, ADDLW, SUBLW, CLRW и NOP, собранные для mid-range, совпадают с кодами enhanced mid-range — но совместимости программ это не гарантирует: адреса регистров периферии и схема банков у двух ядер разные.
Те же задачи на новом ядре решаются короче:
; PIC16F1825
; 16-bit addition sum_h:sum_l += add_h:add_l, the final C is the 16-bit carry
movf add_l, w
addwf sum_l, f ; low bytes, C = carry out
movf add_h, w
addwfc sum_h, f ; high bytes plus C
; RA2 as output, set through the latch: no read-modify-write on the pin
movlb 1 ; BSR = 1: bank 1
bcf TRISA, 2 ; TRISA = 0x08C
movlb 2 ; bank 2
bsf LATA, 2 ; LATA = 0x10C
; Copy 16 bytes through the linear window: 0x2000 -> 0x2050
movlw 0x20
movwf FSR0H
clrf FSR0L ; FSR0 = 0x2000
movwf FSR1H
movlw 0x50
movwf FSR1L ; FSR1 = 0x2050
movlw 0x10
movwf count ; count lives in common RAM 0x70-0x7F
copy:
moviw FSR0++ ; W = *FSR0, then FSR0 + 1
movwi FSR1++ ; *FSR1 = W, then FSR1 + 1
decfsz count, f
bra copy ; 5 cycles per byte, 4 for the last one
С ADDWFC 16-битное сложение занимает 4 команды вместо 6 у mid-range, и флаг C в конце правильно показывает перенос. Цикл копирования занимает 5 циклов на байт, последний байт — 4, всего 16 · 5 − 1 = 79 циклов. Табличный переход на новом ядре делают командой BRW: она прибавляет W ко всему 15-битному счётчику, поэтому граница блока в 256 слов ей не мешает, а PCLATH для самого перехода загружать не нужно (разд. 3.3.4 даташита DS40001440E).
Чем ассемблировать: MPASM и PIC Assembler
Набор команд от ассемблера не зависит, но синтаксис — да. Для новых проектов Microchip поставляет PIC Assembler в составе компилятора MPLAB XC8 и выпустила руководство по переносу кода с прежнего ассемблера MPASM (DS50002973B). Главные отличия:
- Основание чисел. Число без префикса в MPASM шестнадцатеричное, а в PIC Assembler — десятичное:
movlw 10загрузит 16 или 10 в зависимости от ассемблера. В перенесённом коде можно поставить директивуRADIX hex. Константы в примерах на этой странице записаны с префиксом 0x, который оба ассемблера понимают одинаково. - Метки. В PIC Assembler после метки обязательно двоеточие, в MPASM оно необязательно.
- Банки и страницы. BANKSEL и PAGESEL работают одинаково. Если опция
-Wl,--fixupoverflowне задана, лишние биты банка в адресе линкер PIC Assembler обрезает и отмечает предупреждением в листинге; маскировать адрес явно можно макросом BANKMASK() из<xc.inc>, адрес перехода — макросом PAGEMASK(). - Файлы. Исходник —
.sили.S(с препроцессором) вместо.asm; объектные файлы и библиотеки MPASM PIC Assembler не читает.
Частые ошибки
- Забытый банк. Запись в TRISB (86h), OPTION_REG (81h) или ADCON1 (9Fh) без переключения на банк 1 попадает в регистр банка 0 с теми же младшими 7 битами адреса — в PORTB, TMR0 или ADCON0.
- Аналоговые выводы после сброса. У PIC16F877A выводы PORTA и PORTE после включения питания — аналоговые входы и читаются как 0, цифровыми их делает ADCON1 (PCFG3:PCFG0 = 011x). У PIC16F628A после сброса компараторы в режиме
CM<2:0>= 000, и выводы RA0–RA3 работают как аналоговые входы — их освобождает запись 0x07 в CMCON. У PIC12F675 нужно настроить ANSEL и CMCON. - PCLATH после дальнего вызова или таблицы. Подпрограмма в другой странице или табличный переход оставляют в PCLATH чужие биты, и следующий GOTO уходит не туда. Таблица, пересекающая границу 256 слов, возвращает мусор.
- Переполнение стека. Девятый уровень вложенности молча затирает самый старый адрес возврата. Рекурсия на PIC16 mid-range практически невозможна.
- Инверсный заём. После SUBWF и SUBLW C = 1 означает «результат не отрицательный». Перепутанная проверка BTFSC или BTFSS даёт ошибку только на части значений.
- Чтение — модификация — запись. Несколько BSF и BCF подряд на одном порту с нагруженными выводами меняют соседние биты; на mid-range выручает теневой байт в ОЗУ, который целиком копируют в порт, на PIC16F1xxx — регистры LATx.
Частые вопросы
Сколько команд у PIC16?
У ядра mid-range — PIC16F877A, PIC16F628A, PIC12F675 — 35 команд, каждая занимает одно 14-битное слово. У ядра enhanced mid-range в PIC16F1xxx, например PIC16F1825 и PIC16F18877, — 49 команд с тем же 14-битным словом. Для сравнения: у PIC10F200 с ядром baseline 33 команды по 12 бит, у PIC18F4550 — 75 команд по 16 бит и ещё 8 в расширенном наборе.
Сколько длится команда PIC16F877A?
Машинный цикл равен четырём периодам генератора. При кварце 20 МГц это 200 нс, при 4 МГц — 1 мкс. Большинство команд выполняется за один цикл, а CALL, GOTO, RETURN, RETLW, RETFIE, любая запись в PCL и пропуск в BTFSS, BTFSC, DECFSZ и INCFSZ — за два.
Как переключить банк памяти в PIC16F877A?
Битами RP1 и RP0 регистра STATUS: 00 — банк 0 (адреса 00h–7Fh), 01 — банк 1 (80h–FFh), 10 — банк 2, 11 — банк 3. Например, TRISB лежит по адресу 86h в банке 1, поэтому перед записью в него нужно выполнить BSF STATUS, 5, а потом вернуть банк 0 командой BCF STATUS, 5. При косвенной адресации через FSR банк выбирает бит IRP.
Зачем нужен PCLATH?
Счётчик команд PIC16F877A 13-битный, а в коде CALL и GOTO помещается только 11 бит адреса — это страница в 2K слов. Два недостающих бита берутся из PCLATH<4:3>. При записи в PCL, например в табличном переходе ADDWF PCL, F, старшие 5 бит счётчика берутся из PCLATH<4:0>. RETURN восстанавливает полный адрес из стека, но PCLATH не меняет — его надо вернуть самому.
Чем MPASM отличается от PIC Assembler из XC8?
Набор команд тот же, отличаются директивы и синтаксис. Главная ловушка — основание чисел: в MPASM число без префикса считается шестнадцатеричным, а в PIC Assembler — десятичным. В PIC Assembler метка обязательно заканчивается двоеточием, а файлы исходников имеют расширение .s. Директивы BANKSEL и PAGESEL работают одинаково.
Источники
- Microchip. PIC16F87XA Data Sheet: 28/40/44-Pin Enhanced Flash Microcontrollers (DS39582C, 2013) — разд. 2 — карта памяти, STATUS, PCL и PCLATH, стек, страницы, INDF и FSR; разд. 14.11–14.12 — прерывания и сохранение контекста; разд. 15 — формат команд, табл. 15-1 и 15-2, описания команд
- Microchip. PIC16F627A/628A/648A Data Sheet (DS40044G, 2009) — разд. 15 — та же система команд, табл. 15-2; описания OPTION, TRIS и примеры флага C у SUBLW; разд. 10 — компараторы и CMCON
- Microchip. PICmicro Mid-Range MCU Family Reference Manual (DS33023A, 1997) — разд. 5.2 — четыре формата команды и код операции длиной от 3 до 6 бит
- Microchip. PIC16(L)F1825/1829 Data Sheet (DS40001440E, 2015) — ядро enhanced mid-range: разд. 2–3 — стек, BSR, общая и линейная память, FSR; разд. 8.5 — автоматическое сохранение контекста; разд. 29 — 49 команд, табл. 29-3
- Microchip. PIC16(L)F18857/77 Data Sheet (DS40001825F, 2021) — 6-битный BSR и код MOVLB, табл. 36-4
- Microchip. MPASM to MPLAB XC8 PIC Assembler Migration Guide (DS50002973B, 2022) — разд. 3.1–3.4: основание чисел по умолчанию, метки, BANKSEL, PAGESEL, BANKMASK, fcall и ljmp
Нашли ошибку?
Смотрите также
- PIC (Microchip)PIC10/12/16/18, dsPIC33, PIC32: ядра, классические кристаллы, MPLAB
- Система команд AVRВсе инструкции с флагами и тактами, адресация, примеры кода
- Система команд 8051111 команд с байтами, циклами и флагами, карта кодов, примеры
- Система команд MSP43027 основных и 24 эмулируемые команды, адресация, коды, флаги, такты
- Семисегментные индикаторыОбщий анод и катод, коды 0–F, резисторы, мультиплексирование разрядов