Урок ОК04

Начальная страница курса

Урок построен на основе ОК03. Рассматривается как использовать таймер для мигания ОК (ACT) светодиодом с точным интервалом. Предполагается, что у Вас есть код урока ОК03.

1. Новое устройство

До сих пор мы рассмотрели только одно устройство Raspberry Pi — GPIO контроллер. Я просто рассказал Вам что делать, и это сработало. Сейчас мы срассмотрим таймер и я собираюсь помочь Вам понять как он работает.

Так же как и у контроллера GPIO у таймера есть адрес в памяти — 0x20003000. В документации на процессор мы можем найти такую таблицу:

Таблица 1 — Регистры GPIO контроллера

Адрес Размер (Байт) Имя Описание Чтение/Запись
20003000    4 Control / Status    Регистр управление таймером. RW
20003004 8 Counter Счетчик инкрементирующийся с частотой 1МГц. R
2000300C 4 Compare 0 0 регистр совпадения. RW
20003010 4 Compare 1 1 регистр совпадения. RW
20003014 4 Compare 2 2 регистр совпадения. RW
20003018 4 Compare 3 3 регистр совпадения. RW

Эта таблица многое нам рассказывает, но самое ценное содержит описание различных полей. Документация объясняет, что таймер в принципе просто инкрементирует значение счетчика (counter) на 1 каждую микросекунду. Каждый раз значение в счетчике (его младшие 32 бита) сравнивается со значениями 4ех регистров сравнения. Если значение совпало, то обновляется значение поля Control/Status сигнализирую какое из значений совпало.

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

Я вижу два варианта:

1. Считать значение счетчика, а затем возвращаться назад к тому же коду, пока счетчик не достигнет нужного значения.
2. Считать значение счетчика, добавить к этому значению время ожидания, записать это значение в регистр сравнения, а зтем ожидать пока необходимый бит в регистре Control/Status не установится.

Оба эти варианта сработают на отлично. Но в данном уроке мы рассмотрим только первый. Причина этого в том, что использование регистра сравнения скорее приведет к ошибке, т.к. проходит время при вычислении на вычисления. Счетчик уже может превысить необходимое значение и совпадения не произойдет. Могут возникнуть очень длинные не предусмотренные задержки, если будет задана задержка в 1мкс (или еще хуже при 0).

2. Реализация

Во многом я оставлю задачу написания идеальной функции задержки заданием для Вас. Я предлагаю расположить весь код связанный с таймером в файле systemTimer.s (надеюсь по понятным причинам). Сложность состоит в том, что счетчик имеет 8 байтное значение, но каждый регистр может содержать только 4 байта. Поэтому, значение счетчика должно быть помещено в два регистра.

Последующие блоки кода — примеры.

ldrd r0,r1,[r2,#4] 

Вам понадобиться инструкция ldrd. Она загружает 8 байт из памяти в два регистра. В данном случае 8 байт из памяти начиная с адреса в r2 будут скопированы в r0 и r1. Некоторые сложности вызывает то, что r1 на самом деле содержит старшие 4 байта. Другими словами, если счетчик имеет значение 999,999,999,999 = 0b1110100011010100101001010000111111111111, то r1 будет содержать 0b11101000, а r0 будет содержать 0b11010100101001010000111111111111.

Наиболее разумным способом реализации будет вычисление разности между текущим значением счетчика и тем, которое было при запуске метода. Затем необходимо сравнить полученное значение с временем, которое необходимо ожидать. Удобно, если только вы не хотите поддерживать время ожидание длиной 8 байт. Значение r1 в приведенном примере можно отбросить и сравнивать только младшие 4 байта.

Вы не должны использовать сравнения на равенство т.к. вы можете пропустить нужное значение таймера и задержка будет длиться бесконечно.

Если вы не можете понять как реализовать такую функцию, смотрите объяснение ниже.


Заимствуем идею из GPIO контроллера. Первая функция, которую мы должны написать — функция для получения адреса системного таймера.

.globl GetSystemTimerBase
GetSystemTimerBase:
ldr r0,=0x20003000
mov pc,lr

Другой полезной функцией будет функция, которая возвращает текущее значение счетчика в регистрах r0 и r1.

.globl GetTimeStamp
GetTimeStamp:
push {lr}
bl GetSystemTimerBase
ldrd r0,r1,[r0,#4]
pop {pc}

Эта функция ипользует функцию GetSystemTimerBase для получения адреса таймера, после чего загружает значение счетчика при помощи комманды ldrd.
Теперь мы хотим запрограммировать наш метод ожидания. Прежде всего нужно использовать функцию GetTimeStamp для получения текущего значения счетчика.

delay .req r2
mov delay,r0
push {lr}
bl GetTimeStamp
start .req r3
mov start,r0

Этот метод копирует входной параметр (время ожидания) в r2 и вызывает функцию GetTimeStamp, которая вернет значение счетчика в r0 и r1. После этого младшая часть копируется в r3.
Далее необходимо вычислить разность между текущим значением счетчика и начальным. Необходимо продолжать делать это пока значение не превысит заданное время ожидания.

loop$:
    bl GetTimeStamp
    elapsed .req r1
    sub elapsed,r0,start
    cmp elapsed,delay
    .unreq elapsed
    bls loop$

Этот код будет выполняться до тех пор пока не пройдет заданное время. Тут происходит чтение текущего значения счетчика, вычитание из него начального значения и сравнение с заданным промежутком. Если необходимое время еще не вышло происходит переход к метке loop$;

.unreq delay
.unreq start
pop {pc}

Этот код завершает метод задержки возвратом.


3. Другая моргалка светодиодом

Когда вы создали рабочую функцию задержки измените main.s чтобы использовать её. Задайте в качестве времени ожидания большое число (помните это время в микросекундах) и проверьте все на Raspberry Pi. Если что-то не так посетите страницу устранения неисправностей.

Если все заработало, поздравляю! Вы только что освоили еще одно устройство процессора Raspberry Pi и управление временем вместе с ним.
В следующем и последнем уроке этой части мы рассмотрим как моргать светодиодом в определенной последовательности.

 

Похожий код:

Фото аватара
Алексей Петров

Программист, разработчик с 5 летним опытом работы. Учусь на разработчика игр на Unity и разработчика VR&AR реальности (виртуальной реальности). Основные языки программирования: C#, C++.

Оцените автора
Бла, бла код
Добавить комментарий