STM Урок 204. Assembler. SysTick. Прерывания



Продолжим освоение ассемблера для архитектуры ARM.

В предыдущих уроках мы использовали задержки исполнения кода с помощью пустых циклов, тем самым нам тяжело было даже примерно подсчитать заранее, сколько циклов потребуется для организации задержки на определённое время. Так как в прошлом занятии мы смогли добиться тактирования контроллера строго определённой частотой импульсов, то теперь нам эту задачу решить будет намного легче. А поможет нам в этом системный таймер SysTick, который присутствует во всех контроллерах STM32 и представляет собой простейший программируемый счётчик с обратным отсчётом до нуля. Как только данный счётчик досчитывает до нуля, генерируется прерывание, если оно конечно разрешено, а также устанавливается определённый флаг, который также можно отследить, тогда уже прерывания будет использовать необязательно.

С системным таймером мы также работали уже в уроке 167 с использованием библиотеки CMSIS, поэтому задача наша ещё более упростится, так как практически мы знаем все регистры и биты данной периферии и также некий алгоритм действий работы с данным таймером у нас уже есть.

Как всегда, на данном уроке мы изучим ещё несколько ассемблерных команд.

Схема наша также не изменилась

 

 

А проект для урока мы сделаем из проекта прошлого урока с именем ASM_BLINK01_RCC и присвоим ему имя ASM_SYSTICK01.

Откроем наш проект в Keil и добавим в файл с константами ещё несколько

 

 

 

Также создадим и подключим в качестве нового модуля ещё один ассемблерный файл с именем utils.s, в котором для начала также подключим наш файл с константами

 

 

Ещё в данном файле мы объявим область, которая будет доступна не только для чтения, но и для записи. Там будут храниться переменные (правда в данном случае только одна)

 

 

С помощью данной переменной мы будем считать срабатывания нашего системного таймера, заранее занося в неё определённое значение в милисекундах и в обработчике прерывания Systick декрементируя данное значение, а уже в процедуре задержки смотреть, не досчитали ли мы до нуля.

Объявим эту переменную в нашей секции

 

 

Далее пойдёт код, поэтому объявим соответствующую область

 

 

Далее добавим процедуру инициализации системного таймера, в которой мы также сохраним в стеке значения регистров, которые будут в ней использоваться и по окончанию их вернём оттуда обратно в регистры

 

 

Объявим данную процедуру внешней

 

 

 

А в main.s её подключим

 

 

И затем вызовем

 

 

Вернёмся в utils.s и в нашей процедуре проинициализируем счётчик системного таймера с тем расчетом, чтобы он, досчитывал до данного значения ровно за 1 милисекунду

 

 

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

Сбросим счётчик

 

 

Прерываниям от системного таймера установим самый низкий приоритет (15-й)

 

 

Настроим управляющий регистр, настроив в качестве источника неразделённую на 8 (оригинальную) системную частоту, разрешив прерывания и включив собственно счётчик

 

 

Если мы теперь соберём код и прошьём контроллер, то наш светодиод мигать перестанет, так как мы включили прерывания, а обработчик не написали, мало того, у нас нет таблицы векторов прерываний (адресов их обработчиков), которая начинается в начале кода, вместо неё идёт код, и когда возникнет прерывание, то адрес обработчика будет браться прямо из кодового сегмента и приведёт непонятно куда. Мы с вами видели подобную таблицу в startup в наших проектах, написанных на C, подобную таблицу нам надо будет сгандобить и в нашем проекте.

Не обязательно её размещать всю, но надо разместить хотя бы ту часть которая идёт от её начала до вектора прерывания, которое мы хотим обрабатывать.

Поэтому расположим нашу таблицу перед секцией кода в main.s

 

 

В качестве ненужных нам пока адресов у нас будут нули, а в качестве адресов тех прерываний, попав в которые мы будем уходить в бесконечный цикл, будем использовать соответствующую метку. Самым последним в таблице у нас будет адрес метки, с которой будет располагаться подпрограмма обработчика прерываний от SYSTICK.

Добавим для начала метку для перезагрузки контроллера и соответственно сразу же и безусловный переход на неё, чтобы процессор крутился на одном месте, если случайно попадёт не в то прерывание

 

 

 

Теперь идём в utils.s, в котором добавим процедуру для обработки прерывания от системного таймера ниже процедуры SYSTICK_START

 

 

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

Экспорт и импорт данной процедуры производится аналогично предыдущим, поэтому данный процесс я уже показывать не буду.

Если мы теперь соберём код и прошьём контроллер, то у нас по-прежнему будет мигать светодиод, так как нам никакие флаги сбрасывать не нужно, главное, чтобы был обработчик и чтобы в таблице прерываний в нужном месте был его адрес.

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

Для начала сравним с нулём значение данного счётчика

 

 

Теперь, если значение не равно нулю, то мы должны декрементировать данное значение, а если равно — то просто выйти из обработчика.

Здесь мы можем применить условную конструкцию.

Условная конструкция выглядит следующим образом

 

 

Суффиксы x,y.z необязательны. Сейчас расскажу, зачем они нужны. Если мы не будем использовать суффикс, то мы в теле условия сможем использовать только одну ассемблерную команду, но только этого не всегда достаточно для условных конструкций.

Для начала давайте попробуем обойтись без суффиксов. Сначала идёт команда самого условия

 

 

Условие NE в команде IT означает то, что если означает что условие наше будет истинно тогда, когда флаг Z будет равен нулю, то есть результат предшествующей команды не будет нулевым.

В данном случае мы декрементируем значение нашего счётчика

 

 

Причём мы обязательно либо повторяем условный суффикс в команде, либо используем обратный (в нашем случае будет EQ). Таковы требования условных конструкций. Выглядит немного запутанно, но постепенно привыкнем, так как использовать такие конструкции приходится довольно-таки часто.

Но только данной команды нам будет недостаточно, так как мы продекрементировали только значение регистра, а нам ведь надо данное значение обратно в переменную записать.

Поэтому для того, чтобы использовать две команды, то нам будет нужно в команде условной конструкции добавить ещё один суффикс

 

ITT NE

 

Ещё одна T потребует теперь от нас ещё одной команды, пока мы её не добавим, у нас будет ошибка сборки. Здесь вместо T мы можем использовать E, которая будет означать то, что вторая команда будет выполняться в противном случае, то есть в случае, если условие не выполнится (если сравнить с бейсиком, то IF THEN ELSE).

Добавим вторую команду

 

 

Теперь данные две команды будут выполняться тогда, когда счётчик наш будет иметь ненулевое значение.

Вот и весь код обработчика.

Теперь нам нужно будет написать подпрограмму для задержки в милисекундах. Условимся, что значение задержки мы перед вызовом подпрограммы будем помещать в регистр R0.

Добавим процедуру задержки ниже процедуры SYSTICK_START

 

 

Сбросим в данной подпрограмме сначала счётчик системного таймера

 

 

Запишем значение из регистра R0 (значение милисекунд для задержки) в наш пользовательский счётчик

 

 

Добавим метку

 

 

Далее мы будем в цикле забирать значение из нашей переменной, которая будет постоянно декрементироваться в обработчике прерывания, и, пока она не будет в нуле, мы будем крутиться в данном цикле, а как только достигнет нуля — выйдем из цикла

 

 

Произведём экспорт нашей процедуры и импортируем её в main.s, в который затем перейдём и изменим код процедуры DELAY, которая примет после этого следующий вид

 

 

Теперь наша задержка будет ровно (ну или почти ровно) 500 милисекунд.

Соберём код, прошьём контроллер и проверим результат работы нашего кода.

Светодиод теперь мигает раз в секунду

 

 

 

Итак, на данном уроке мы научились использовать системный таймер SysTick, благодаря которому мы теперь можем отсчитывать строго определённые интервалы, а также научились обрабатывать прерывания и использовать условные конструкции.

Всем спасибо за внимание!

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

 

Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM SysTick. Прерывания

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*