Программирование ARM-контроллеров STM32 на ядре Cortex-M3. Часть 7.

Работа с портами ввода-вывода.

Часть 1. Установка MDK, создание проекта, основы Keil uVision

Часть 2. Команды и директивы ассемблера, структура и синтаксис программы. Первая программа для STM32.

Часть 3. Карта памяти контроллеров STM32, методы работы с памятью.

Часть 4. Регистры, старт и режимы работы контроллеров STM32.

Часть 5. Как залить прошивку в контроллер.

Часть 6. Настройка системы тактирования.

Часть 7. Работа с портами ввода-вывода.

Начнём с того, что вообще такое порт ввода-вывода. Для настройки режима работы или изменения состояния линии ввода-вывода обычно достаточно одного-двух битов, а регистры в контроллерах, как правило, восьми, шестнадцати или даже 32-х битные (как в stm32). Выделять для каждой линии ввода-вывода свой отдельный регистр - довольно расточительно, вот и придумали группировать в одном регистре биты управления сразу несколькими линиями, а группы линий, имеющие общие регистры управления, называть портами.

В контроллерах stm32 в порт может быть сгруппировано до 16-ти линий. Именуются порты обычно буквами латинского алфавита: PORTA, PORTB, PORTC ...

Теперь про сами линии ввода-вывода. Линии ввода-вывода могут быть общего назначения (GPIO - general purpose input output) или иметь какое-то специальное назначение (например, специальные линии модулей встроенной периферии: MOSI/MISO для модуля SPI, D+/D- для модуля USB ...). Как правило, линии делают конфигурируемыми, то есть такими, которые, в зависимости от настроек, могут использоваться или как GPIO, или как специальные. Типовую внутреннюю структуру канала ввода-вывода контроллера stm32 можно видеть на рисунке ниже.

Структурная схема канала ввода-вывода контроллера stm32

Всего возможно 8 конфигураций линий ввода-вывода:

Выход GPIO:

push-pull (работают оба выходных транзистора)

open-drain (работает только N-FET, а P-FET всегда разомкнут)

Альтернативный выход (выход периферийного модуля):

push-pull

open-drain

Вход:

Analog, высокоимпедансный вход (подтягивающие резисторы и триггер Шмитта отключены, используется для АЦП)

Floating, высокоимпедансный вход (подтягивающие резисторы отключены, триггер Шмидта включен)

Pull-up, вход с подтяжкой к питанию (через специальный встроенный резистор)

Pull-down, вход с подтяжкой к "земле" (через специальный встроенный резистор)

Конфигурация определяется регистрами GPIOx_CRL (первые 8 линий порта x) и GPIOx_CRH (вторые 8 линий порта x). В этих регистрах на каждую линию порта выделено по 4 бита, из которых 2 называются MODEy[1:0], а другие 2 - CONFy[1:0], где y - номер линии порта.

Кроме самих режимов, при конфигурировании линии в качестве выхода, сочетанием битов MODE[1:0] задаётся максимальная скорость переключения.

При конфигурировании линии в качестве входа с подтяжкой, в настройке принимает участие также выходной регистр порта (GPIOx_ODR). Соответствующий конфигурируемой линии бит регистра GPIOx_ODR в этом случае определяет "направление" подтяжки (к питанию или к "земле").

По умолчанию (то есть после ресета) все линии настроены как "floating input".

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

Режим CNF1 CNF0 MODE1 MODE0 GPIOx_ODR
General purpose
output
push-pull 0 0 01 - max speed 10 MHz
10 - max speed 2 MHz
11 - max speed 50 MHz
0 или 1
open-drain 1 0 или 1
Alternate function
output
push-pull 1 0 не важно
open-drain 1 не важно
Input analog 0 0 00 не важно
floating 1 не важно
pull-down 1 0 0
pull-up 1

Итак, порт мы настроили, что дальше? Дальше надо как-то линиями этого порта рулить.

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

Считать состояние линий портов можно через Port Input Data Registers, которые коротко именуются GPIOx_IDR (x - буква порта, A, B, C и так далее). В младшие 16 бит каждого из этих регистров читаются состояния линий соответствующего порта, а старшие 16 бит - не используются.

Когда линии порта настроены как выходы GPIO, то управлять состоянием этих выходов можно через Port Output Data Registers, которые кратко именуются GPIOx_ODR (x - буква порта, A, B, C и так далее). То есть, те значения, которые записываются в эти регистры, появляются и на соответствующих выходных линиях. В этих регистрах также используются только младшие 16 бит.

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

Помимо описанных выше регистров, для каждого порта выделены ещё два регистра, которые позволяют обойтись одной операцией для установки или сброса битов в выходных регистрах порта. Эти регистры называются GPIOx_BSRR (Port Bit Set/Reset Register) и GPIOx_BRR (Port Bit Reset Register), где x - буква порта.

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

Вместо этого как раз и придумали регистры GPIOx_BSRR и GPIOx_BRR. Эти регистры позволяют без всякого выключения прерываний осуществлять "атомарные" (то есть происходящие в одно действие) операции с отдельными битами регистров GPIOx_ODR.

Запись единицы в какой-либо из младших 16-ти бит регистра GPIOx_BSRR приводит к "атомарной" установке соответствующего бита регистра GPIOx_ODR в единицу, а запись единицы в какой-либо из старших 16-ти битов регистра GPIOx_BSRR приводит к "атомарному" сбросу соотвветствующего бита регистра GPIOx_ODR в ноль. Установка бита имеет приоритет над сбросом, то есть если установить в единицу одновременно два бита регистра GPIOx_BSRR, отвечающие за сброс и установку одного и того же бита регистра GPIOx_ODR, то в результате выполнения операции произойдёт установка этого бита в единицу.

Регистр GPIOx_BRR работает аналогично, за исключением того, что через этот регистр возможен только сброс битов регистра GPIOx_ODR и невозможна их установка.

Наверняка у некоторых читателей возникнет вопрос: "А в чём разница между использованием регистров GPIOx_BSRR + GPIOx_BRR и методом bit-banding?" Разница тут в том, что с помощью метода bit-banding за одну операцию можно изменить только один бит регистра GPIOx_ODR, а с помощью регистров GPIOx_BSRR, GPIOx_BRR можно изменить сразу несколько битов. В регистры GPIOx_BSRR, GPIOx_BRR мы записываем сразу целое слово и соответствующая реакция на эту запись появится сразу у всех битов регистра GPIOx_ODR для которых в этом слове установлены единицы.

Надеюсь с этим всё понятно (если нет, то спрашивайте на форуме), поэтому пойдём дальше.

Ещё одной интересной особенностью при работе с портами контроллеров STM32 является наличие механизма защиты отконфигурации порта от изменения. Для этого тоже существуют специальные регистры - GPIOx_LCKR (Port Configuration Lock Registers), где x - буква порта. Работает этот механизм следующим образом. Младшие 16 бит регистра GPIOx_LCKR используются для выбора линий порта, конфигурацию которых необходимо залочить (выбор осуществляется установкой соответствующего бита в единицу), а потом специальной последовательностью действий над 17-м битом осуществляется залочка этих выбранных конфигураций. Последовательность такая: записать 1, записать 0, записать 1, считать 0, считать 1. Правильный результат двух последних операций (чтение) говорит о том, что защита успешно включена. После включения защиты отменить её уже нельзя и выбранные конфигурации останутся залоченными до перезагрузки контроллера.

Главный документ, который стоит почитать чтобы узнать подробности (например, где какие биты расположены в описанных регистрах, адреса регистров и тому подобное) - это как обычно Reference Manual RM0008 (CD00171190.pdf). В данном случае копаем раздел 9 этого мануала (General-purpose and alternate function).

Пример работы писать не буду, так что с портами на этом всё.

Часть 1. Установка MDK, создание проекта, основы Keil uVision.

Часть 2. Команды и директивы ассемблера, структура и синтаксис программы. Первая программа для STM32.

Часть 3. Карта памяти контроллеров STM32, методы работы с памятью.

Часть 4. Регистры, старт и режимы работы контроллеров STM32.

Часть 5. Как залить прошивку в контроллер.

Часть 6. Настройка системы тактирования.

Часть 7. Работа с портами ввода-вывода.

radiohlam.ruтеорияконтроллеры и программирование

Понравилась статья? Поделись с друзьями!

Обсудить эту статью на форуме

 
Rambler's Top100 © 2009 - Материалы сайта охраняются законом об авторском праве