1i7

Лабораторная работа 6: знакомство с промышленной реализацией архитектуры MIPS на примере pic32 (4)

Sep 29, 2013 01:19

Продолжение, начало Лабораторная работа 6: знакомство с промышленной реализацией архитектуры MIPS на примере pic32 (3) <<

Примеры

Задание 1 - зажечь лампочку ассемблером MIPS
Например мы хотим зажечь светодиодную лампочку, подключенную к нашей знакомой ножке pic32 #44 (chipKIT #8), которая одновременно с этим является 10й ножкой внутри порта PORTD, т. е. к ножке RD10.

Программа на ассемблере MIPS32 выглядит следующим образом:

task1-light-asm/light.S

# Зажечь лампочку на ножке RD10 (chipKIT #8) ассемблером
main: .global main # Помечаем метку main как глобальную

# Установить ножку RD10 как вывод - установить бит TRISD[10] в 1 - отправить 0x400 в TRISDCLR
li $t1, 1 << 10
la $t2, 0xBF8860C4 #TRISDCLR
sw $t1, 0 ($t2)

# Установить значение 1 на ножке RD10 - установить бит LATD[10] в 1 - отправить 0x400 в LATDSET
li $t1, 1 << 10
la $t2, 0xBF8860E8 #LATDSET
#la $t2, 0xBF8860E4 #LATDCLR
sw $t1, 0 ($t2)


Разберем по строкам.

Отмечаем точку входа в программу - этот код будет выполняться сразу после загрузки контроллера:

main: .global main # Помечаем метку main как глобальную
Теперь устанавливаем режим работы ножки на вывод - записываем 0 в 10й бит регистра TRISD, т.е. нам нужно сделать так, чтобы ячейка памяти по адресу 0xBF88_60C0 выглядела следующим образом:

0xBF88_60C0: [0...0 0...0 xxxxx0xx xxxxxxxx]

Достигаем этот результат за 3 команды:

# Установить ножку RD10 как вывод - установить бит TRISD[10] в 0 - отправить 0x400 в TRISDCLR
li $t1, 1 << 10
la $t2, 0xBF8860C4 #TRISDCLR
sw $t1, 0 ($t2)

Загружаем двоичное значение 100'00000000 (оператор сдвига '<<' перемещает 1 на 10 бит влево) в промежуточный регистр $t1 при помощи псевдо-команды li (load immediate):

li $t1, 1 << 10
Загружам адрес целевой ячейки памяти 0xBF8860C4 (регистр TRISDCLR) в промежуточный регистр $t2 при помощи псевдо-команды la (load address).

la $t2, 0xBF8860C4 #TRISDCLR
Отправляем двоичное значение 100'00000000 в регистр TRISDCLR (ячейка памяти по адресу 0xBF8860C4) при помощи уже известной нам команды sw (store word) - таким образом сбрасываем 10й бит регистра TRISD в 0 и ножка RD10 настраивается на вывод:

sw $t1, 0 ($t2)
Или тоже самое словами - записываем в регистр TRISDCLR значение (1 отмечает сбрасываемый бит):
0xBF88_60C4: [0...0 0...0 00000100 00000000]
и в TRISD получаем нужное нам:
0xBF88_60C0: [0...0 0...0 xxxxx0xx xxxxxxxx] (биты, помеченные как 'x' останутся нетронутыми).

Замечание 1: В этом и заключается механизм работы вспомогательных регистров CLR, SET и INV - они позволяют манипулировать выбранными битами главного регистра не затрагивая при этом остальные биты. Изменяется только тот бит, который во вспомогательном регистре помечен 1, остальные биты (во вспомогательном регистре помеченные нулями) в главном регистре остаются неизменными. Вспомогательный регистр CLR сбрасывает выбранные биты главного регистра в 0, вспомогательный регистр SET устанавливает выбранные биты главного регистра в 1, вспомогательный регистр INV инвертирует текущие значения выбранных битов главного регистра (меняет 1 на 0, а 0 на 1).

Замечание 2: команды li и la называются псевдо-командами потому, что они позволяют загрузить в регистр любое 32хбитное значение одной строкой кода на ассемблере MIPS, хотя, как мы помним, в одну 32хбитную машинную команду уместить произвольное 32хбитное число в качестве аргумента никак не получится, т.к. должно остаться место как минимум для кода операции. Хитрость в том, что подобные псевдо-команды обрабатываются препроцессором компилятора MIPS - он проверяет, сколько бит занимает аргумент и в случае необходимости при генерации машинного кода разбивает загрузку числа в регистр на нескольких машинных команд, хотя в программе на ассемблере они все умещаются в одну строку.

Зажигаем лампочку - подаем плюс на ножку RD10, т. е. записываем значение 1 в 10й бит регистра LATD:
LATD10 = 1:
0xBF88_60E0: [0...0 0...0 xxxxx1xx xxxxxxxx]

Проворачиваем всю процедуру аналогичным образом - для установки выбранного 10го бита регистра LATD в 1 теперь используем вспомогательный регистр LATDSET:

# Установить значение 1 на ножке RD10 - установить бит LATD[10] в 1 - отправить 0x400 в LATDSET
li $t1, 1 << 10
la $t2, 0xBF8860E8 #LATDSET
sw $t1, 0 ($t2)

Подключаем плату, компилируем программу, загружаем код программы:

> make
> make upload

Лампочка загорается:



Теперь для чистоты эксперимента погасим лампочку аналогичным образом:

Подаем минус на ножку, т. е. 0 в 10й бит регистра LATD:
LATD10 = 0:
0xBF88_60E0: [0...0 0...0 xxxxx0xx xxxxxxxx]

Просто заменям LATDSET на LATDCR:

li $t1, 1 << 10
la $t2, 0xBF8860E4 #LATDCLR
sw $t1, 0 ($t2)

> make; make upload

Лампочка гаснет:



Кто не верит на слово, вот видео:


Работа с регистрами SFR pic32 на ассемблере: вывод значения на ножку from 1i7 on Vimeo.

Замечание: в примере выше в коде программы адреса регистров указаны как абсолютные численные значения для лучшего погружения в тему, т.к. таким образом можно наглядно убедиться, что адреса из таблиц документации действительно работают на реальном железе. В нормальной ситуации все эти числа конечно же прописаны в специальных именованных константах, которые подключаются к программе через заголовочный файл p32xxxx.h. В нормальном виде этот же код выглядит следующим образом:

task1_1-light-asm/light.S

#include

####################################################################
# Зажечь лампочку на ножке RD10 (chipKIT #8) ассемблером
main: .global main # Помечаем метку main как глобальную

# Установить ножку RD10 как вывод - установить бит TRISD[10] в 0 - отправить 0x400 в TRISDCLR
li t1, 1 << 10
la t2, TRISDCLR #0xBF8860C4
sw t1, 0 (t2)

# Установить значение 1 на ножке RD10 - установить бит LATD[10] в 1 - отправить 0x400 в LATDSET
li t1, 1 << 10
la t2, LATDSET #0xBF8860E8
#la t2, LATDCLR #0xBF8860E4
sw t1, 0 (t2)

Задание 2 - считать значение с ножки ассемблером MIPS

Программа считывает значение с ножки RF1 (chipKIT #4), к которой можно или подключить кнопку или напрямую подавать на провод + или -, и в зависимости от полученного значения зажигает или гасит лампочку RD10 (chipKIT #8).

В бесконечном цикле читаем значение RF1 и тут же устанавливаем значение лампочки RD10.

task2-input-asm/input.S

# Зажечь лампочку RD10 (chipKIT #8) по нажатию кнопки RF1 (chipKIT #4)
main: .global main # Помечаем метку main как глобальную

# Установить ножку RF1 как ввод - установить бит TRISF[1] в 1 -
# отправить 0x1 в регистр TRISFSET
li $t1, 1 << 1
la $t2, 0xBF886148 #TRISFSET
sw $t1, 0 ($t2)

# Установить ножку RD10 как вывод - установить бит TRISD[10] в 0 -
# отправить 0x400 в регистр TRISDCLR
li $t1, 1 << 10
la $t2, 0xBF8860C4 #TRISDCLR
sw $t1, 0 ($t2)

loop:
# Считать значение с ножки RF1 - получить значение бита PORTF[1]
la $t2, 0xBF886150 #PORTF
lw $t1, 0 ($t2)
ext $t3, $t1, 1, 1

addi $t4, $0, 1
beq $t3, $t4, button_on

# кнопка выключена
# Задать значение 0 на ножке RD10 - установить бит PORTD[10] в 0 -
# отправить 0x400 в регистр LATDCLR
li $t1, 1 << 10
la $t2, 0xBF8860E4 #LATDCLR
sw $t1, 0 ($t2)

j loop
button_on:
# кнопка включена
# Задать значение 1 на ножке RD10 - установить бит PORTD[10] в 1 -
#отправить 0x400 в регистр LATDSET
li $t1, 1 << 10
la $t2, 0xBF8860E8 #LATDSET
sw $t1, 0 ($t2)

j loop

Ножка RF1 в режим ввода:

# Установить ножку RF1 как ввод - установить бит TRISF[1] в 1 -
# отправить 0x1 в регистр TRISFSET
li $t1, 1 << 1
la $t2, 0xBF886148 #TRISFSET
sw $t1, 0 ($t2)

Ножка RD10 в режим вывода:

# Установить ножку RD10 как вывод - установить бит TRISD[10] в 0 -
# отправить 0x400 в регистр TRISDCLR
li $t1, 1 << 10
la $t2, 0xBF8860C4 #TRISDCLR
sw $t1, 0 ($t2)

Метим вход в бесконечный цикл:

loop:
Считываем значение с ножки RF1 - читаем значение региста PORTF и определяем значение 1го бита:

# Считать значение с ножки RF1 - получить значение бита PORTF[1]
la $t2, 0xBF886150 #PORTF
lw $t1, 0 ($t2)
ext $t3, $t1, 1, 1

Здесь из нового - команда ext (extract bit field) - извлечение битового поля.

Синтаксис:
ext rt, rs, pos, size

Принцип работы:
rt = ИзвлечьПоле(rs, pos, size)

В нашем случае:
rs=$t1 - регистр с текущим значением всего регистра PORTF, из которого будет извлекать нужный бит;
pos=1 - позиция нужного бита - для RF1 это 1;
size=1 - нам нужен один бит;
rt=$t3 - здесь будет результат: 1, если на входе RF1 1; 0, если на входе RF1 0.

Сравниваем результат $t3 с 1: если на входе 1, то прыгаем на метку button_on ("зажечь лампочку"):

addi $t4, $0, 1
beq $t3, $t4, button_on

а если 0, то просто двигаемся дальше, тушим лампочку и прыгаем на старт бесконечного цикла loop считать новое значение на входе:

# кнопка выключена
# Задать значение 0 на ножке RD10 - установить бит PORTD[10] в 0 -
# отправить 0x400 в регистр LATDCLR
li $t1, 1 << 10
la $t2, 0xBF8860E4 #LATDCLR
sw $t1, 0 ($t2)

j loop

Метка button_on ("зажечь лампочку") - зажигаем лампочку и прыгаем на старт бесконечно цикла loop считать новое значение на входе:

button_on:
# кнопка включена
# Задать значение 1 на ножке RD10 - установить бит PORTD[10] в 1 -
#отправить 0x400 в регистр LATDSET
li $t1, 1 << 10
la $t2, 0xBF8860E8 #LATDSET
sw $t1, 0 ($t2)

j loop

Компилируем, загружаем:

> make; make upload

Подключаем провод RF1 (chipKIT #4) к плюсу - лампочка горит:



Подключаем провод RF1 (chipKIT #4) к минусу - лампочка гаснет:



Все правильно, но можно еще раз проверить на видео:

Работа с регистрами SFR pic32 на ассемблере: ввод значения с ножки from 1i7 on Vimeo.

Еще пара аналогичных примеров на Си и для лабы достаточно.

Код лабы на github, подсветка синтаксиса.

цифровая электроника для программистов, лаба 6, chipkit, pic32, mips

Previous post Next post
Up