Программирование в машинных кодах микроконтроллера MSP430

#ffff00;">
Программирование в машинных кодах микроконтроллера MSP430
Как вы уже знаете:
Задача любого микроконтроллера:
- читать числа из регистров и памяти МК,
- делать что-то с числами, данными и
- записывать числа в регистры и память.
Только так программа может общаться с МК !!!
Поскольку из названия статьи видно, что мы будем рассматривать программирование только в машинных кодах, нашей целью будет научить пользователя писать как простые, так и сложные программы в машинном коде, не используя высокоуровневые языки, такие как Си++ или машинно-ориентированные, как ассемблер. Мы с вами постараемся создать набор базовых подпрограмм, необходимых для написания стартового Монитора ( или как сейчас принято говорить BIOS ) под конкретный микроконтроллер. При этом мы будем использовать опыт написания таких программ, который был накоплен инженерным сообществом со времен появления на свет первых 8-ми разрядных процессоров.
Введение
CPU (Central Processing Unit) — центральный процессор, ЦПУ
INT (N/2) (Integer portion of N/2) — целая часть от N/2
Регистры МК - это ячейки-байты в памяти МК MSP430 ,которые имеют свое название, согласно Дата шиту ( инструкции пользователя на микроконтроллер на сайте производителя ) и так как числа в большинстве из них можно менять - для программы регистры являются по сути переменными.
Переменная- это набор ячеек в памяти в которых можно хранить число или числа и менять их. Переменная имеет адрес.
Константа- это как переменная но менять содержимое нельзя.
Мы изначально предполагаем , что читатель знаком с система исчисления – двоичной , восьмеричной , десятеричной и шестнадцатеричной.
Итак, поскольку мы с вами уже определились, что микроконтроллер должен читать числа из регистров и памяти, изменять их различными вычислениями и записывать обратно в регистры или память, нам необходимо, при знакомстве с любым микроконтроллером, изучить его особенности, карту памяти, регистры, виды адресации и команды. Мы не сможем детально изложить все пункты в одной статье, поэтому основной акцент мы сделаем на системе команд, а остальные пункты изложим поверхностно. Если же кому будет нужно углубиться в детали вопроса,для этого нужно открыть руководство пользователя на конкретный микроконтроллер и изучить этот вопрос. Вообще, хороший программист должен держать всегда под рукой руководство пользователя на необходимый микроконтроллер.
Остановимся на рассмотрении модели микроконтроллера MSP430 и обратимся к его руководству пользователя.
Центральный процессор может адресовать память
во всём диапазоне адресов без разбиения её на страницы.
ЦПУ MSP430 имеет RISC архитектуру набор команд состоит из 27 команд ядра и 24 эмулированных команд, а также поддерживает 7 режимов адресации;
Особенности:
- - ортогональная архитектура — с каждой из команд может использоваться
любой режим адресации;
- - полная доступность регистров, включая счётчик команд, регистры состояния и указатель стека;
- - однотактные регистровые операции;
- - большой 16 битный регистровый файл, уменьшающий количество обращений к памяти;
- - 16 битная шина адреса, обеспечивающая прямой доступ и ветвление во всём диапазоне адресов;
- - 16 битная шина данных, позволяющая напрямую оперировать 2 байтными значениями;
- - генератор констант формирует шесть наиболее часто используемых значений, уменьшая размер кода;
- - прямой обмен данными между ячейками памяти без промежуточной записи в регистр;
- - одно и двухбайтные адресация и форматы команд.
Команды ядра - это команды, имеющие уникальный код операции, декодируемый ЦПУ.
Эмулированные команды - представляют собой инструкции, облегчающие чтение и написание кода, но не имеющие собственного кода операции,эти команды используются, как правило, языком программирования ассемблер и нами будут рассматриваться их эквивалентные команды ядра. Использование эмулированных команд не приводит к увеличению объема кода или снижению производительности.
Существует три формата команд ядра:
- С двойным операндом
- С одиночным операндом
- Команды перехода
Все команды с одним и двумя операндами могут быть командами для работы с байтами или командами для работы со словами, используя, соответственно, расширения «.B» или «.W». Байтовые команды используются для доступа к данным байта или к байту периферийного устройства. Команды-слова используются для доступа к данным слова или к слову периферийного устройства. Если никакое расширение не используется, команда является командой-словом.
Источник и получатель в команде определяются следующими полями:
src | Операнд источника определяется As и S-reg |
dst | Операнд получателя определяется Ad D-reg |
As | Адресные биты, задающие режим адресации, используемые для источника (src) |
S-reg | Рабочий регистр, используемый в качестве источника (src) |
Ad | Адресные биты, задающие режим адресации, используемые для получателя (dst) |
D-reg | Рабочий регистр, используемый в качестве получателя (dst) |
B/W | Операция с байтом или словом: 0: операция со словом 1: операция с байтом |
Примечание: Адрес получателя действителен в любом месте карты распределения памяти. Однако, при использовании команды, изменяющей содержимое получателя, пользователь должен быть уверен, что по адресу назначения можно производить запись. К примеру, маскированное ПЗУ имеет правильный адрес назначения, но его содержимое не может модифицироваться, поэтому команда изменения его содержимого не будет правильно выполнена. |
Примечание: использование меток EDE и TONI Везде в документации по семейству MSP430 используются универсальные метки EDE и TONI. Они являются только метками, не имеющими никакого специального назначения. |
Код команды | HEX | 000 | 040 | 080 | 0C0 | 100 | 140 | 180 | 1C0 | 200 | 240 | 280 | 2C0 | 300 | 340 | 380 | 3C0 |
HEX | |||||||||||||||||
0xxx | |||||||||||||||||
4xxx | |||||||||||||||||
8xxx | |||||||||||||||||
Cxxx | |||||||||||||||||
1xxx | RRC | RRC.B | SWPB | RRA | RRA.B | SXT | PUSH | PUSH.B | CALL | RETI | |||||||
14xx | |||||||||||||||||
18xx | |||||||||||||||||
1Cxx | |||||||||||||||||
20xx | JNE/JNZ | ||||||||||||||||
24xx | JEQ/JZ | ||||||||||||||||
28xx | JNC | ||||||||||||||||
2Cxx | JC | ||||||||||||||||
30xx | JN | ||||||||||||||||
34xx | JGE | ||||||||||||||||
38xx | JL | ||||||||||||||||
3Cxx | JMP | ||||||||||||||||
4xxx | MOV, MOV.B | ||||||||||||||||
5xxx | ADD, ADD.B | ||||||||||||||||
6xxx | ADDC, ADDC.B | ||||||||||||||||
7xxx | SUBC, SUBC.B | ||||||||||||||||
8xxx | SUB, SUB.B | ||||||||||||||||
9xxx | CMP, CMP.B | ||||||||||||||||
Axxx | DADD, DADD.B | ||||||||||||||||
Bxxx | BIT, BIT.B | ||||||||||||||||
Cxxx | BIC, BIC.B | ||||||||||||||||
Dxxx | BIS, BIS.B | ||||||||||||||||
Exxx | XOR, XOR.B | ||||||||||||||||
Fxxx | AND, AND.B |
Рис.1.1. Карта команд ядра
Набор команд MSP430
Мнемоника | Описание | V | N | Z | ||
ADC(.B)* | dst | Сложение бита С с получателем | dst + C -> dst | * | * | * |
ADD(.B) | src,dst | Сложение источника с получателем | src + dst -> dst | * | * | * |
ADDC(.B) | src,dst | Сложение источника и бита С с получателем | src + dst + C -> dst | * | * | * |
AND(.B) | src,dst | Операция «И» источника и получателя | src .and. dst -> dst | 0 | * | * |
BIC(.B) | src,dst | Очистка битов в получателе | .not.src .and. dst -> dst | - | - | - |
BIS(.B) | src,dst | Установка битов в получателе | src .or. dst -> dst | - | - | - |
BIT(.B) | src,dst | Проверка битов в получателе | src .and. dst | 0 | * | * |
BR* | dst | Переход по назначению | dst -> PC | - | - | - |
CALL | dst | Вызов получателя | PC + 2 -> stack, dst -> PC | - | - | - |
CLR(.B)* | dst | Очистка получателя | 0 -> dst | - | - | - |
CLRC* | Очистка бита С | 0 -> C | - | - | - | |
CLRN* | Очистка бита N | 0 -> N | - | 0 | - | |
CLRZ* | Очистка бита Z | 0 -> Z | - | - | 0 | |
CMP(.B) | src,dst | Сравнение источника и получателя | dst – src | * | * | * |
DADC(.B)* | dst | Десятичное сложение бита С с получателем | dst + c -> dst (десятичное) | * | * | * |
DADD(.B) | src,dst | Десятичное сложение источника и бита С с получателем | src + dst + C -> dst (десятичное) | * | * | * |
DEC(.B)* | dst | Декремент получателя | dst - 1 -> dst | * | * | * |
DECD(.B)* | dst | Двойной декремент получателя | dst - 2 -> dst | * | * | * |
DINT* | Запрещение прерываний | 0 -> GIE | - | - | - | |
EINT* | Разрешение прерываний | 1 -> GIE | - | - | - | |
INC(.B)* | dst | Инкремент получателя | dst + 1 -> dst | * | * | * |
INCD(.B)* | dst | Двойной инкремент получателя | dst + 2 -> dst | * | * | * |
INV(.B)* | dst | Инвертирование получателя | .not.dst -> dst | * | * | * |
JC/JHS | label | Переход, если С установлен / переход если наивысший или такой же | - | - | - | |
JEQ/JZ | label | Переход, если равно / переход если Z установлен | - | - | - | |
JGE | label | Переход, если больше или равно | - | - | - | |
JL | label | Переход, если меньше | - | - | - | |
JMP | label | Переход | PC + 2 * смещение -> PC | - | - | - |
JN | label | Переход, если N установлен | - | - | - | |
JNC/JLO | label | Переход, если С не установлен / переод если низший | - | - | - | |
JNE/JNZ | label | Переход, если не равно, переход если Z не установлен | - | - | - | |
MOV(.B) | src,dst | Пересылка источника в получатель | src -> dst | - | - | - |
NOP* | Нет операции | - | - | - | ||
POP(.B)* | dst | Снятие элемента со стека в получатель | @SP -> dst, SP + 2 -> SP | - | - | - |
PUSH(.B) | src | Помещение источника в стек | SP - 2 -> SP, src -> @SP | - | - | - |
RET* | Возврат из подпрограммы | @SP -> PC, SP + 2 -> SP | - | - | - | |
RETI | Возврат из прерывания | * | * | * | ||
RLA(.B)* | dst | Арифметическая ротация влево | * | * | * | |
RLC(.B)* | dst | Ротация влево через С | * | * | * | |
RRA(.B) | dst | Арифметическая ротация вправо | 0 | * | * | |
RRC(.B) | dst | Ротация вправо через С | * | * | * | |
SBC(.B)* | dst | Вычитание not(C) из получателя | dst + 0FFFFh + C -> dst | * | * | * |
SETC* | Установка С | 1 -> C | - | - | - | |
SETN* | Установка N | 1 -> N | - | 1 | - | |
SETZ* | Установка Z | 1 -> Z | - | - | 1 | |
SUB(.B) | src,dst | Вычитание источника из получателя | dst + .not.src + 1 -> dst | * | * | * |
SUBC(.B) | src,dst | Вычитание источника и not(C) из получателя | dst + .not.src + C -> dst | * | * | * |
SWPB | dst | Обмен байтов | - | - | - | |
SXT | dst | Распространение знака | 0 | * | * | |
TST(.B)* | dst | Проверка получателя | dst + 0FFFFh + 1 | 0 | * | * |
XOR(.B) | src,dst | Исключающее «ИЛИ» источника и получателя | src .xor. dst -> dst | * | * | * |
* Эмулируемые команды
. Влияет на бит состояния
- Не влияет на бит состояния
0 - бит состояния сбрасывается
1- бит состояния устанавливается
Режим адресации операндов
As/Ad | Режим адресации | Синтаксис | Описание |
00 / 0 | Регистровый режим | Rn | Содержимое регистра является операндом |
01 / 1 | Индексный режим | X(Rn) | Значение (Rn+X) указывает на операнд. X сохранен в следующем слове |
01 / 1 | Символьный режим | ADDR | Значение (PC+X) указывает на операнд. X сохранен в следующем слове. Использован индексный режим X(PC) |
01 / 1 | Абсолютный (безусловный) режим | &ADDR | Слово, следующее за командой, содержит абсолютный адрес. X сохранен в следующем слове. Использован индексный режим X(SR) |
10 / - | Косвенный регистровый режим | @Rn | Содержимое Rn использовано как указатель на операнд |
11 / - | Косвенный автоинкремент | @Rn+ | Содержимое Rn использовано как указатель на операнд. Содержимое Rn впоследствии увеличивается на 1 для байтовых команд и на 2 для команд-слов. |
11 / - | Прямой (непосредственный) режим | #N | Слово, следующее за командой, содержит непосредственную константу N. Использован косвенный автоинкрементный режим @PC+ |
Флэш/ПЗУ
ОЗУ
Указатель стека (SP)
Периферийные устройства
Регистры периферийных модулей (устройств) располагаются в общем адресном пространстве. Область адресов от 0100h до 01FFh зарезервирована для 16 битных периферийных модулей. Для обращения к таким устройствам необходимо использовать команды, оперирующие двухбайтными значениями. При использовании команд, работающих с однобайтными значениями, допускаются обращения только к чётным адресам памяти, при этом старший байт результата всегда будет равен нулю. Область адресов от 010h до 0FFh зарезервирована для 8%битных периферийных модулей. Для обращения к этим устройствам необходимо использовать команды, оперирующие байтами. Если для чтения из такого модуля использовать команду, оперирующую словами, то содержимое старшего байта результата будет неопределённым. При записи в 8 битный модуль двухбайтного значения, в регистр устройства будет записан только младший байт.
Регистры ЦПУ
Некоторые функции периферийных устройств конфигурируются посредством регистров специальных функций. Эти
8 битные регистры располагаются в младших 16 байт адресного пространства. Для обращения к указанным регистрам можно использовать только команды, оперирующие байтами. Назначение отдельных битов регистров специальных функций описано в документации на конкретные модели.
Счётчик команд (PC)
MOV #LABEL,PC ; | Переход к адресу LABEL |
MOV LABEL,PC ; | Переход к адресу, содержащемуся в LABEL |
MOV @R14,PC ; | Косвенный переход по адресу, содержащемуся в R14 |
Прерывания
В MSP430 имеется три типа прерываний:
- сброс системы;
- немаскируемые (NMI);
- маскируемые.
Приоритеты прерываний фиксированы и зависят от местонахождения конкретного модуля( см. руководство)
Организация памяти
Однобайтные значения располагаются по чётным или нечётным адресам.
1. Команды микроконтроллера
1.1. Команды с двойным операндом ( Формат I )
На рис.1.1 показана структура формата команды с двойным операндом.
Формат команды с двумя операндами.
Здесь показано распределение 16 битного двоичного поля команды от 0 до 15-го бита
- В поле "Код операции" подставляем 4-х битный двоичный код, соответствующий 16-ти разрядной команде в
табл. на рис.1.1 Карта команд ядра
- В поле "Регистр источник" и "Регистр-получатель" подставляем 4-х битный двоичный код, который
соответствует десятичному номеру регистра- приемника или регистра-передатчика ЦПУ.
Например: R0 будет иметь код 0000, R3 будет иметь код 0011 и т.д.
- В поле Ad и As подставляем соответственно двоичный код из таблицы "Режим адресации операндов",
приведенной выше.
- В поле B/W подставляем : 0 если это операция со словом или 1 если это операция с байтом
Например:
команда
MOV R10,R11 будет иметь код 0100 1010 0000 1011 или 4A0Dh
команда
MOV 2(R5),6(R6) будет иметь код 0100 0101 1001 0110 или 4595h и два операнда 0002h и 0006h
полная команда = 4596 0002 0006 и располагается в 3-х четных 16-ти разрядных ячейках памяти подряд, начиная с младшей слева.
В таблице 3.11 приведено описание и перечень команд с двойным операндом.
Формат команды с одним операндом.
Здесь показано распределение 16 битного двоичного поля команды от 0 до 15-го бита
- В поле "Код операции" подставляем 4-х битный двоичный код, соответствующий 16-ти разрядной команде в
табл. на рис.1.1 Карта команд ядра
- В поле "Регистр источник" и "Регистр-получатель" подставляем 4-х битный двоичный код, который
соответствует десятичному номеру регистра- приемника или регистра-передатчика ЦПУ.
Например: R0 будет иметь код 0000, R3 будет иметь код 0011 и т.д.
- В поле Ad и As подставляем соответственно двоичный код из таблицы "Режим адресации операндов",
приведенной выше.
- В поле B/W подставляем : 0 если это операция со словом или 1 если это операция с байтом
Например:
MOV(.B) R10,R11 будет иметь код 0100 1010 0100 1011 или 4A4Dh
Таблица 3.11. Команды с двумя операндами
Таблица 3.12. Команды с одним операндом
2. Первое включение микроконтроллера.
Схема сброса микроконтроллера формирует сигналы сброса (POR) и очистки (PUC) по включению питания. Генерация данных сигналов вызывается различными событиями, при этом исходное состояние устройства зависит от того, какой из сигналов был сгенерирован.
Сигнал POR является сигналом сброса устройства.
Этот сигнал генерируется при наступлении любого из следующих событий:
- включение устройства;
- сигнал НИЗКОГО уровня на выводе RST/NMI, если последний сконфигурирован как вход сброса;
- низкий уровень напряжения питания .
Сигнал PUC всегда генерируется при появлении сигнала POR, однако генерация последнего никак не связана с сигналом PUC.
Сигнал PUC генерируется принаступлении любого из следующих событий:
- генерация сигнала POR;
- таймаут сторожевого таймера (только в соответствующем режиме);
- обращение к сторожевому таймеру с неверным ключом;
- обращение к контроллеру флэшпамяти с неверным ключом;
- попытка выборки команды из памяти в диапазоне адресов 0h…01FFh.
Состояние устройства после сброса
После снятия сигнала POR микроконтроллер переходит в следующее состояние:
- вывод RST/NMI конфигурируется как вход сброса;
- все линии портов ввода/вывода конфигурируются как входы в соответствиис описанием, приведенным в главе 8
«Цифровые порты ввода/ вывода» руководства пользователя от Texas Instruments;
- прочие периферийные модули и регистры инициализируются так, как описано в соответствующих главах руководства
пользователя от Texas Instruments;
- регистр состояния (SR) сбрасывается;
- сторожевой таймер включается в сторожевом режиме;
- в счётчик команд (PS) загружается значение, находящееся по адресу вектора сброса (0FFFEh). Если по этому адресу
записано 0FFFFh, то устройство отключается для минимизации потребления. В этом векторе должен быть записан адрес начала вашей программы !
Инициализация программы
После сброса устройства пользовательская программа должна инициализировать его в соответствии с требованиями конкретного приложения. Процесс инициализации должен включать следующие этапы:
- инициализация указателя стека SP (как правило, указатель устанавливается на вершину ОЗУ);
- инициализация сторожевого таймера в соответствии с требованиями приложения;
- конфигурирование периферийных модулей в соответствии с требованиями приложения. Дополнительно можно
проверить состояние флагов сторожевого таймера,сбоя тактового генератора и флэш памяти для определения
причины сброса.
2. Рассмотрим разработку программ в машинных кодах, разобрав некоторые примеры и устройство программ, которые могут стать базовыми при разработке вашего собственного программного обеспечения.