Перейти к содержанию

Система команд 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—аккумулятор, в карте памяти данных его нет
INDF00hобращение по адресу из FSR; физически регистра нет
PCL02hмладшие 8 бит счётчика команд, доступен для чтения и записи
STATUS03hфлаги C, DC, Z, биты TO и PD, выбор банка IRP, RP1, RP0
FSR04hуказатель для косвенной адресации
PCLATH0Ahбуфер старших 5 бит счётчика команд
INTCON0Bhразрешение и флаги прерываний, бит GIE

Регистр STATUS

БитИмяЗначение
7IRPбанк при косвенной адресации: 0 — банки 0 и 1 (00h–FFh), 1 — банки 2 и 3 (100h–1FFh)
6–5RP1:RP0банк при прямой адресации: 00 — 00h–7Fh, 01 — 80h–FFh, 10 — 100h–17Fh, 11 — 180h–1FFh
4TO0 — сработал сторожевой таймер; 1 — после включения питания, CLRWDT и SLEEP
3PD0 — после команды SLEEP; 1 — после включения питания и CLRWDT
2Zрезультат арифметической или логической операции равен нулю
1DCперенос из бита 3 в бит 4; при вычитании — инверсный заём
0Cперенос из бита 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
k8-битная константа в командах с W и в RETLW, 11-битный адрес в CALL и GOTO
xбезразличный бит, ассемблер записывает 0

Как это выглядит в машинном коде:

КомандаДвоичный кодШестнадцатеричный
addwf 0x20, w00 0111 0010 00000x0720
addwf 0x20, f00 0111 1010 00000x07A0
bsf 0x06, 301 0101 1000 01100x1586
movlw 0x5511 0000 0101 01010x3055
goto 0x12310 1001 0010 00110x2923

Таблица команд PIC16

Все 35 команд — по таблице 15-2 даташита PIC16F87XA; в даташите PIC16F628A таблица та же. В колонке «Циклы» указаны машинные циклы по 4 периода генератора; «1 (2)» означает 2 цикла, если пропуск сработал. «Приёмник» — W при d = 0 или регистр f при d = 1. Колонка «Флаги» перечисляет биты STATUS, которые команда может изменить.

Байтовые команды

КомандаДействиеЦиклыКодФлаги
ADDWF f, dW + f → приёмник100 0111 dfff ffffC, DC, Z
ANDWF f, dW И f → приёмник100 0101 dfff ffffZ
CLRF f0 → f100 0001 1fff ffffZ
CLRW0 → W100 0001 0xxx xxxxZ
COMF f, dинверсия всех битов f → приёмник100 1001 dfff ffffZ
DECF f, df − 1 → приёмник100 0011 dfff ffffZ
DECFSZ f, df − 1 → приёмник, пропустить следующую команду, если получился 01 (2)00 1011 dfff ffff—
INCF f, df + 1 → приёмник100 1010 dfff ffffZ
INCFSZ f, df + 1 → приёмник, пропустить следующую команду, если получился 01 (2)00 1111 dfff ffff—
IORWF f, dW ИЛИ f → приёмник100 0100 dfff ffffZ
MOVF f, df → приёмник; при d = 1 — проверка f на ноль100 1000 dfff ffffZ
MOVWF fW → f100 0000 1fff ffff—
NOPнет операции100 0000 0xx0 0000—
RLF f, dсдвиг влево через C: бит 7 → C, C → бит 0100 1101 dfff ffffC
RRF f, dсдвиг вправо через C: бит 0 → C, C → бит 7100 1100 dfff ffffC
SUBWF f, df − W → приёмник100 0010 dfff ffffC, DC, Z
SWAPF f, dобмен старшей и младшей тетрад f → приёмник100 1110 dfff ffff—
XORWF f, dW исключающее ИЛИ f → приёмник100 0110 dfff ffffZ

Битовые команды

КомандаДействиеЦиклыКодФлаги
BCF f, b0 → бит b регистра f101 00bb bfff ffff—
BSF f, b1 → бит b регистра f101 01bb bfff ffff—
BTFSC f, bпропустить следующую команду, если бит b = 01 (2)01 10bb bfff ffff—
BTFSS f, bпропустить следующую команду, если бит b = 11 (2)01 11bb bfff ffff—

Команды с константой и управления

КомандаДействиеЦиклыКодФлаги
ADDLW kW + k → W111 111x kkkk kkkkC, DC, Z
ANDLW kW И k → W111 1001 kkkk kkkkZ
CALL kадрес возврата → стек, переход в подпрограмму; биты 12–11 адреса из PCLATH<4:3>210 0kkk kkkk kkkk—
CLRWDTсброс сторожевого таймера и его предделителя, TO = 1, PD = 1100 0000 0110 0100TO, PD
GOTO kбезусловный переход; биты 12–11 адреса из PCLATH<4:3>210 1kkk kkkk kkkk—
IORLW kW ИЛИ k → W111 1000 kkkk kkkkZ
MOVLW kk → W111 00xx kkkk kkkk—
RETFIEвозврат из прерывания, GIE = 1200 0000 0000 1001—
RETLW kk → W и возврат из подпрограммы211 01xx kkkk kkkk—
RETURNвозврат из подпрограммы200 0000 0000 1000—
SLEEPрежим сна с остановленным генератором, TO = 1, PD = 0100 0000 0110 0011TO, PD
SUBLW kk − W → W111 110x kkkk kkkkC, DC, Z
XORLW kW исключающее ИЛИ k → W111 1010 kkkk kkkkZ

Любая команда, которая меняет счётчик команд, например 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 = bZ = 1
a ≠ bZ = 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>
00000h–07FFh00
10800h–0FFFh01
21000h–17FFh10
31800h–1FFFh11

У 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 и возвратом:

T=2⏟CALL+1⏟MOVWF+1000N−1+2⏟RETURN=1000N+4 цикловT = \underbrace{2}_{\text{CALL}} + \underbrace{1}_{\text{MOVWF}} + 1000N - 1 + \underbrace{2}_{\text{RETURN}} = 1000N + 4\ \text{циклов}

При N = 10 это 10 004 мкс. Прерывания удлиняют задержку на время своих обработчиков; для точных интервалов лучше таймер.

Ядро enhanced mid-range: PIC16F1xxx

В семействах PIC12F1xxx и PIC16F1xxx — например, PIC16F1825, PIC16F1829 и PIC16F18877 — работает обновлённое ядро enhanced mid-range. Слово команды осталось 14-битным, и все 35 команд на месте, но их стало 49 — к ним добавились 14 новых, — а модель памяти заметно изменилась.

Mid-range: PIC16F877AEnhanced mid-range: PIC16F1825
Команды3549
Частота и циклдо 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, dW + f + C → приёмник111 1101 dfff ffffC, DC, Z
SUBWFB f, df − W − заём → приёмник111 1011 dfff ffffC, DC, Z
ASRF f, dарифметический сдвиг вправо: бит 7 сохраняется, бит 0 → C111 0111 dfff ffffC, Z
LSLF f, dлогический сдвиг влево: бит 7 → C, в бит 0 входит 0111 0101 dfff ffffC, Z
LSRF f, dлогический сдвиг вправо: бит 0 → C, в бит 7 входит 0111 0110 dfff ffffC, Z
MOVLB kk → BSR, выбор банка100 0000 001k kkkk—
MOVLP k7-битная константа → PCLATH111 0001 1kkk kkkk—
BRA kотносительный переход на PC + 1 + k, k от −256 до 255211 001k kkkk kkkk—
BRWпереход на PC + 1 + W, W без знака200 0000 0000 1011—
CALLWвызов по адресу PCLATH<6:0>:W200 0000 0000 1010—
RESETпрограммный сброс микроконтроллера100 0000 0000 0001—
ADDFSR FSRn, kFSRn + k → FSRn, k от −32 до 31111 0001 0nkk kkkk—
MOVIWINDFn → W: ++FSRn, --FSRn, FSRn++, FSRn-- или k[FSRn]100 0000 0001 0nmm, 11 1111 0nkk kkkkZ
MOVWIW → INDFn в тех же пяти режимах100 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 не читает.

Частые ошибки

  1. Забытый банк. Запись в TRISB (86h), OPTION_REG (81h) или ADCON1 (9Fh) без переключения на банк 1 попадает в регистр банка 0 с теми же младшими 7 битами адреса — в PORTB, TMR0 или ADCON0.
  2. Аналоговые выводы после сброса. У PIC16F877A выводы PORTA и PORTE после включения питания — аналоговые входы и читаются как 0, цифровыми их делает ADCON1 (PCFG3:PCFG0 = 011x). У PIC16F628A после сброса компараторы в режиме CM<2:0> = 000, и выводы RA0–RA3 работают как аналоговые входы — их освобождает запись 0x07 в CMCON. У PIC12F675 нужно настроить ANSEL и CMCON.
  3. PCLATH после дальнего вызова или таблицы. Подпрограмма в другой странице или табличный переход оставляют в PCLATH чужие биты, и следующий GOTO уходит не туда. Таблица, пересекающая границу 256 слов, возвращает мусор.
  4. Переполнение стека. Девятый уровень вложенности молча затирает самый старый адрес возврата. Рекурсия на PIC16 mid-range практически невозможна.
  5. Инверсный заём. После SUBWF и SUBLW C = 1 означает «результат не отрицательный». Перепутанная проверка BTFSC или BTFSS даёт ошибку только на части значений.
  6. Чтение — модификация — запись. Несколько 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 работают одинаково.

Источники

  1. 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, описания команд
  2. Microchip. PIC16F627A/628A/648A Data Sheet (DS40044G, 2009) — разд. 15 — та же система команд, табл. 15-2; описания OPTION, TRIS и примеры флага C у SUBLW; разд. 10 — компараторы и CMCON
  3. Microchip. PICmicro Mid-Range MCU Family Reference Manual (DS33023A, 1997) — разд. 5.2 — четыре формата команды и код операции длиной от 3 до 6 бит
  4. Microchip. PIC16(L)F1825/1829 Data Sheet (DS40001440E, 2015) — ядро enhanced mid-range: разд. 2–3 — стек, BSR, общая и линейная память, FSR; разд. 8.5 — автоматическое сохранение контекста; разд. 29 — 49 команд, табл. 29-3
  5. Microchip. PIC16(L)F18857/77 Data Sheet (DS40001825F, 2021) — 6-битный BSR и код MOVLB, табл. 36-4
  6. Microchip. MPASM to MPLAB XC8 PIC Assembler Migration Guide (DS50002973B, 2022) — разд. 3.1–3.4: основание чисел по умолчанию, метки, BANKSEL, PAGESEL, BANKMASK, fcall и ljmp

Нашли ошибку?