1i7 (1i7) wrote,
1i7
1i7

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

Продолжение, начало Лабораторная работа 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 <p32xxxx.h>
####################################################################
# Зажечь лампочку на ножке 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, подсветка синтаксиса.
Tags: chipkit, mips, pic32, лаба 6, цифровая электроника для программистов
Subscribe

  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 1 comment