Таблица данных применяется для задания большого количества констант (чисел) в памяти программ. Например, требуется получить сигнал заданной формы с помощью ШИМ или резистивного ЦАП (R-2R), для этого необходимо загнать в таблицу заранее рассчитанные коэффициенты, и считывать их через определенные промежутки времени, как это сделано в статье про регулировку яркости светодиода. Также таблица данных используется для реализации знакогенератора при выводе символов на жидкокристаллический индикатор (например, дисплей мобильного телефона).
Таблица данных задается с помощью директивы dt (определение таблицы) в памяти программ, при этом табличное чтение организуется в виде отдельной подпрограммы с применением вычисляемого перехода. В простейшем варианте подпрограмма выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 | ;предварительно перед вызовом подпрограммы table в аккумулятор W записываем ;порядковый номер элемента таблицы ;при возврате из подпрограммы значение требуемого элемента таблицы ;будет лежать в аккумуляторе W table addwf PCL,F ;прибавляем к счетчику команд PCL порядковый ;номер элемента таблицы из аккумулятора W dt 0x0B, 0xDC, 0x5D, 0x3D, 0x85; ;таблица данных с числами, равнозначна записи: dt 0x9E, 0x58, 0xAE, 0x9F, 0xBA; ;retlw 0x0B dt 0xC2, 0xF7, 0xC9, 0xBF, 0xCF; ;retlw 0xDC dt 0xD3, 0x9C, 0xD7, 0x4F, 0xDA; ;retlw 0x5D ;и так далее |
После компиляции кода, записи вида dt 0x0B, 0xDC, 0x5D, заменяются на команды возврата, с записью числа в аккумулятор: retlw 0x0B, retlw 0xDC, retlw 0x5D, расположенные последовательно (по адресам) в памяти программ. Чтобы считать из таблицы определенную константу (коэффициент, число), необходимо перед вызовом подпрограммы table записать в аккумулятор W порядковый номер элемента таблицы, содержащий искомую константу. Например, если в аккумуляторе лежит число 0, то после прибавления к регистру PCL (младший байт счетчика команд), мы попадем на первый элемент таблицы, точнее на команду retlw 0x0B, после исполнения которой произойдет выход из подпрограммы с записью числа 0x0B в аккумулятор. Предварительно записав в аккумулятор число 1, мы попадем на второй элемент таблицы, после выхода из подпрограммы в аккумуляторе будет лежать число 0xDC. Всего в одной таблице можно задать 256 констант, так как младший байт счетчика команд PCL однобайтный, и может принимать значения в диапазоне от 0 до 255.
Как я уже писал в первой статье, здесь надо соблюдать некоторые правила, при использовании вычисляемого перехода не следует допускать переполнения регистра PCL, так как при его переполнении не происходит приращения старшего байта счетчика команд PCH.
Рассмотрим вариант, где подпрограмма table расположена в памяти программ начиная с адреса 253 (00FDh) (PCH=xxx00000, PCL=11111101). Значение регистра PCLATH равно 0. При чтении первых двух элементов таблицы никаких проблем не возникает, прибавляя 0 к PCL, попадаем на первый элемент таблицы расположенный по адресу 254, прибавляя 1, попадаем на второй элемент по адресу 255. А вот при чтении третьего элемента произойдет переполнение PCL, инкремента PCH не произойдет, в PCH передается значение из регистра PCLATH, в итоге мы попадем на адрес 0 (0000h) (PCH=xxx00000, PCL=00000000) в памяти программ. Чтобы такого не происходило, подпрограмма table не должна пересекать границы блока памяти программ (размер блока 256 слов), она должна располагаться в пределах одного блока, если нет пересечения, то и переполнения PCL не произойдет. Другими словами подпрограмма не должна пересекать следующие адреса в памяти программ: 256 (0100h), 512 (0200h), 768 (0300h), 1024 (0400h), и так далее. Для исключения пересечения можно воспользоваться директивой org для размещения подпрограммы на удобном участке памяти программ.
Данное ограничение можно обойти, если следить за переполнением регистра PCL, и своевременно записать правильное значение в регистр PCLATH. Код подпрограммы в этом случае выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ;перед вызовом подпрограммы table, необходимо записать порядковый номер требуемого элемента ;таблицы в регистр nomer table movlw high tab ;запись старшего байта адреса метки tab в movwf PCLATH ;регистр PCLATH movf nomer,W ;суммирование порядкового номера элемента таблицы с addlw low tab ;младшим байтом адреса метки tab в аккумуляторе W btfsc STATUS,C ;проверка переполнения W в результате суммирования incf PCLATH,F ;произошло переполнение W: инкремент PCLATH movf nomer,W ;не было переполнения W: прибавляем порядковый addwf PCL,F ;номер элемента таблицы к счетчику PCL tab dt 0x0B, 0xDC, 0x5D, 0x3D, 0x85; ;таблица данных с числами dt 0x9E, 0x58, 0xAE, 0x9F, 0xBA; ; dt 0xC2, 0xF7, 0xC9, 0xBF, 0xCF; ; dt 0xD3, 0x9C, 0xD7, 0x4F, 0xDA; ; ; |
Перед вызовом подпрограммы table, необходимо записать порядковый номер требуемого элемента таблицы в регистр nomer. Здесь функцией ассемблера high, извлекается значение старшего байта адреса метки tab в памяти программ, напомню что, значение старшего байта адреса памяти программ считает регистр PCH, а значение младшего байта соответственно регистр PCL. Адресу метки tab в памяти программ соответствует первый элемент таблицы, а точнее команда retlw 0x0B. Старший байт адреса метки tab заносится в регистр PCLATH для дальнейшего инкремента, если это будет необходимо. Чтобы проверить возможное переполнение регистра PCL в результате операции сложения (вычисляемый переход), извлекается младший байт адреса метки tab и складывается с порядковым номером элемента таблицы. В случае переполнения, значение регистра PCLATH инкрементируется. Теперь при возникновении переполнения PCL в результате сложения, в регистр PCH передается правильное значение из регистра PCLATH, и мы попадаем на правильный адрес в памяти программ.
Используя данную подпрограмму табличного чтения, можно не заботиться о переполнении PCL и пересечении границ блока памяти программ, подпрограмма может находиться на любом участке памяти программ.