[Home] [< Prev: Минимальное приложение C++ для ARM (Cortex-M3)] [Next: Исключения и прерывания >]

GPIO

Цель
Средства
GPIO: обзор
GPIO: устройство
GPIO: характеристики
GPIO: управление
Пример программы
Неиспользуемые выводы
Защита выводов от перегрузки
Краткое содержание
// (!) VECT_TAB_SRAM - определить символ для отладки в RAM (!)

#include <stm32f10x.h>

int main()
{
    // Включаем тактовый сигнал порта GPIOC.
    RCC->APB2ENR|=RCC_APB2ENR_IOPCEN;

    // Конфигурируем PC9 как двухтактный выход,
    // основная функция, макс. частота 2МГц
    // (остальные выводы - как аналоговые входы).
    // CNF9: 00; MODE9: 10
    GPIOC->CRH=GPIO_CRH_MODE9_1;
    
    // Включить LED.
    // Используется регистр установки/сброса битов.
    GPIOC->BSRR=GPIO_BSRR_BS9;
    // Если бы не CMSIS, пришлось бы писать что-то вроде:
    // *(uint32_t*)(0x40011000+0x10)=1<<9;

    // Выключить LED.
    // Сброс бита с помощь регистра установки/сброса битов.
    GPIOC->BSRR=GPIO_BSRR_BR9;
    
    // Включить LED.
    // Чтение/модификация/запись регистра выходных данных;
    // неатомарно, небезопасно из-за прерываний.
    GPIOC->ODR|=GPIO_ODR_ODR9;

    // Стоп.
    while(true) {}
}
// Подробнее...


Цель

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

Средства

В качестве основы для данного проекта, а также для всех последующих будем использовать минимальное приложение (если не будет указано иное). Соответственно, используются те же средства разработки: оценочная плата STM32VLDISCOVERY и Atollic TrueSTUDIO for ARM, Lite-версия.

На оценочной плате установлены два светодиода, подключённые через токозадающие резисторы  к микроконтроллеру. Зелёный подключён к порту C9, синий - к C8. Зажигаются высоким уровнем на выводе микроконтроллера (логическая 1).

GPIO: обзор

GPIO (general-purpose I/O port) - порты ввода/вывода общего назначения, позволяют осуществлять низкоуровневый обмен цифровыми сигналами со внешними по отношению к микроконтроллеру устройствами. Проще говоря, на ножках микроконтроллера, которые сконфигурированы как выходы GPIO, мы можем программно вывести логический 0 или 1. А также можем прочитать логический уровень сигнала на ножке.

Микроконтроллер STM32F100xx имеет несколько портов, обозначаемых буквами A, B, и т.д.: (GPIOA, GPIOB, ...). Каждый содержит 16 разрядов (или битов), связанных с соответствующими 16 выводами микроконтроллера. Нумеруются от 0 до 15. Вывод порта кратко обозначаются PXn (X - буква порта, n - номер разряда, например PC8). Каждый разряд может быть индивидуально программно сконфигурирован как вход или выход, соответственно вывод микроконтроллера будет работать на ввод или вывод (тут главное не запутаться, где вывод - это ножка микросхемы, а где - процесс передачи данных). Причём предусмотрены 4 варианта режима как для входа, так и для выхода.

Вход может быть floating ("плавающий", т.е. без подтягивающих резисторов, с высоким входным сопротивлением); pull-up (с внутренним, подтягивающим к высокому уровню резистором); pull-down (с подтягивающим к низкому уровню резистором); analog (аналоговый вход).

Выход может быть push-pull (двухтактный); open-drain (с открытым стоком); alternate function push-pull (двухтактный для альтернативной функции); alternate function open-drain (с открытым стоком для альтернативной функции).

Во время и после сброса все выводы портов сконфигурированы как плавающие входы.

Так как количество встроенных в микроконтроллер периферийных устройств велико, а количество выводов ограничено, то приходится на один вывод распределять несколько функций - основную и альтернативные. Основная функция для большинства выводов - работа в качестве GPIO. Альтернативная функция - использование вывода как входа или выхода какого-то периферийного устройства. Если вывод доолжен работать как выход периферийного устройства, то мы выбираем режим альтернативной функции выхода. При использовании вывода как входа, специальной конфигурации для выполнения альтернативной функции не требуется - достаточно лишь активировать альтернативное устройство (чтение, в отличие от записи, может бесконфликтно осуществляться одновременно несколькими устройствами). Например, у микроконтроллеров STM32F100xx ножка с основной функцией PA0 имеет альтернативные функции: WKUP (сигнал для пробуждения из режима ожидания standby), USART2_CTS (одна из линий второго интерфейса USART), ADC1_IN0 (вход 0 ADC), TIM2_CH1_ETR (внешний тактовый сигнал для второго таймера). Разумеется, во избежание конфликтов, только одно из периферийных устройств, использующих общий выход, может быть включено.

Среди микроконтроллеров STM32F100xx есть варианты в корпусах LQFP100, LQFP64, TFBGA64, LQFP48 (LQFP - ножки с каждой стороны квадратного корпуса, TFBGA - матрица жёстких шариковых выводов снизу). В зависимости от исполнения доступно разное количество портов ввода/вывода. В 100-выводном корпусе доступны A, B, C, D, E порты (16*5 - всего 80 бит); в 64-выводном - A, B, C (вывод PC3 доступен в LQFP64, но недоступен в TFBGA64) и частично D (PD0, PD1, PD2); в 48-выводном доступны только A, B, частично C (PC13, PC14, PC15), частично D (PD0, PD1).

Есть некоторые ограничения на использование портов ввода/вывода. Выводы PA13, PA14, PA15, PB3, PB4 после сброса конфигурируются как используемые для отладки, что существенно ограничивает возможность их применения как портов ввода/вывода. PD0, PD1 используются для подключения кварцевого резонатора (это основная функция). К PC14, PC15 подключается кварц часов реального времени на 32 кГц (альтернативная, но часто используемая функция). PB2 является по совместительству входом BOOT1, совместно с BOOT0 определяющим способ начальной загрузки (правда, если на BOOT0 подан 0, задающий режим загрузки из Flash, а это самый часто используемый вариант, то сигнал на входе BOOT1 безразличен и PB2 можно использовать свободно). Кроме того, PC13, PC14 и PC15 получают питание через ключ с малым допустимым током (около 3 мА), так что использование PC13..PC15 в режиме выхода весьма ограничено: частота переключения не должна превышать 2 МГц, максимальная ёмкость нагрузки выхода - 30 пФ, недопустимо использовать в качестве источника тока (например для подключения светодиодов). Если учесть, что многие выводы в готовом устройстве будут задействованы используемыми периферийными устройствами и интерфейсами, то с учётом всех ограничений, остаётся не так много свободных портов ввода/вывода, как может показаться на первый взгляд.

GPIO: устройство

Структура GPIO

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

Оба защитных диода закрыты, пока напряжение на входе остаётся в пределах -ΔV..VDD+ΔV (где ΔV - напряжение, при котором открывается диод). При выходе напряжения за указанные пределы один из диодов открывается. Протекающий через диод ток вызывает падение напряжения на внутреннем сопротивлении источника сигнала и происходит ограничение сигнала. Или же, в случае статического электричества, статический заряд разрядится через один из диодов, в зависимости от знака. Ток через защитные диоды (инжектированный ток), а также суммарный инжектированный ток не должны превышать указанных в Datasheet пределов (для STM32F100xx: ±5 мА, за положительный инжектированный ток принимается втекающий в вывод микроконтроллера ток; кроме PA5, PA6, PA7, для которых установлено +5 мА/-0 мА, т.е. не допускается вытекающий инжектированный ток, что связано с влиянием на точность ADC; суммарный максимальный инжектированный ток: ±25 мА).

Наличие защиты вызывает известные проблемы согласования уровней сигналов при работе микроконтроллера совместно с логикой, питаемой напряжением 5 В, которая всё ещё очень распространена. Так как напряжение питания микроконтроллера VDD=2..3.6 В, то если подать на вход 5 В, откроется верхний по схеме диод защиты. Чтобы решить проблему, разработчики микроконтроллера предусмотрели так называемые выводы с допустимым напряжением 5 В (FT - five-volt tolerant, 5-вольт "толерантный"). Верхний по схеме защитный диод этих выводов подключён для обратного смещения не к источнику питания с напряжением VDD, а к точке схемы с потенциалом величиной VDD_FT=5В. Хитрость в том, что источника 5 В, естественно, в микроконтроллере нет. Этот потенциал формируется за счёт питания от самих FT-выводов и ограничения уровнем 5 В (и если ни на одном FT-выводе нет пяти вольт, то и смещающее напряжение не достигнет пяти вольт). Потребляемый от вывода ток очень мал, но он подвержен трудно предсказуемым изменениям в зависимости от соотношения величин напряжения на других FT-выводах. Это приводит к появлению шума, уровень которого не существенен для цифровых компонентов, но недопустим для аналоговых сигналов. Поэтому выводы, которые используются для работы с аналоговыми сигналами не сделаны FT-выводами. При работе FT-вывода в режиме выхода с открытым стоком совместно с внешним источником на 5 В, также можем получить сигнал на выходе с уровнем логической единицы до 5 В.

В datasheet на устройство указывается, какие выводы имеют допустимое напряжение 5 В. У микроконтроллеров STM32F100xx все сигнальные выводы являются FT-выводами, кроме: NRST, BOOT0, PA0..PA7, PB0, PB1, PB5, PC0..PC5, PC13, PC14, PC15 (из них PA0..PA7, PB0, PB1, PC0..PC5 не являются FT по той причине, что они в качестве альтернативной функции являются аналоговыми входами ADC: входы ADC1_IN0..ADC1_IN15). Уточняйте в datasheet.

Драйвер выхода выполнен на двух транзисторах: одном p-канальном МОП и одном n-канальном МОП-транзисторе, переключаемых схемой управления. В зависимости от режима оба они могут быть закрыты (в этом случае драйвер выхода отключён, вывод микросхемы работает как вход); может быть закрыт верхний на схеме P-MOS, а нижний задействован в работе по схеме с открытым стоком или могут быть задействованы оба, образуя двухтактный выход (один открыт, другой закрыт; если открыт верхний по схеме - на выходе получаем высокий уровень - логическую 1, если открыт нижний - на выходе будет низкий уровень, логический 0).

Источником сигнала для драйвера выхода является либо регистр выходных данных, либо сигнал с какого-то периферийного устройства микроконтроллера (если выбран режим альтернативной функции). Регистр выходных данных GPIOx_ODR (x = A, B, ... - буквенное обозначение регистра) программно доступен как для записи, так и для чтения. На выводе микроконтроллера в режиме выхода будет логический уровень, записанный в соответствующий бит этого регистра. GPIOx_ODR может быть также модифицирован с помощью регистров установки/сброса битов: регистры GPIOx_BSRR, GPIOx_BRR. Эти регистры доступны только для записи. Если при записи в младшую половину регистра установки/сброса битов GPIOx_BSRR, в какие-то разряды происходит запись единичных битов, то соответствующие разряды в регистре выходных данных GPIOx_ODR устанавливаются в 1, остальные разряды остаются без изменения. Аналогично, при записи значения в регистр сброса битов GPIOx_BRR (или в старшую половину GPIOx_BSRR, которая тоже отвечает за сброс), для тех разрядов, в которые записывается 1, соответствующие разряды в регистре выходных данных GPIOx_ODR сбрасываются в 0, остальные не изменяются.

Регистры установки/сброса битов упрощают атомарный доступ к отдельным разрядам портов ввода/вывода. Допустим, если требуется изменить логический уровень всего на одном выходе микроконтроллера, то, не пользуясь регистрами установки/сброса битов, пришлось бы выполнить несколько действий: считать значение соответствующего регистра GPIOx_ODR (требуется, так как мы хотим изменить один бит, а остальные оставить в прежнем состоянии); модифицировать изменяемый бит в считанном значении; записать результат обратно в GPIOx_ODR. Проблема в том, что такое решение не только требует большего объёма кода. Ещё хуже то, что в любой момент между чтением GPIOx_ODR и записью в него нового значения может произойти прерывание, и, не исключено, что внутри прерывания в GPIOx_ODR будет произведена запись. После возврата из прерывания, ничего не подозревающая программа перезапишет туда же своё значение, а изменения, сделанные внутри прерывания будут потеряны, работа устройства нарушится. Чтобы избежать такой ситуации, придётся запрещать прерывания на время чтения/модификации регистра. Это ещё больше увеличит размер кода и отрицательно скажется на быстродействии. В то же время регистры GPIOx_BSRR, GPIOx_BRR дают возможность очень просто и быстро выполнить атомарную модификацию выхода. Под атомарностью имеется ввиду неделимость, непрерываемость операции, выполнение операции как одного действия. Так что польза от регистров установки/сброса битов просто неоценима.

Драйвер входа содержит в себе схему управления подтягивающими резисторами и триггер Шмитта. Если вывод используется периферийными устройствами с аналоговым входом (например такими как ADC - аналогово-цифровой преобразователь), то оно подключается непосредственно ко входу. Периферийные устройства с цифровым входом и регистр входных данных подключаются ко входу через триггер Шмитта. Триггер Шмитта на входе - типичное решение для микроконтроллеров, он определяет входные уровни переключения, формирует на своём выходе стандартные уровни цифрового сигнала с хорошими фронтами при переключении независимо от качества входного сигнала. Защищает от шума на входе и позволяет осуществить согласование цифровой части схемы с аналоговой без использования дополнительных элементов. Триггер Шмитта включён во всех режимах, кроме режима аналогового входа. В режиме аналогового входа триггер Шмитта отключается и на его выходе формируется логический 0 независимо от сигнала на входе. Регистр входных данных, подключённый к выходу триггера Шмитта, доступен только для чтения. Он может быть прочитан независимо от режима работы соответствующего вывода и содержит значение, соответствующее реальному сигналу на выводе микроконтроллера. Таким образом, можно считывать данные с вывода микроконтроллера даже тогда, когда он работает как выход. Это имеет смысл, так как не всегда это значение будет совпадать с тем, что мы выводим, записывая значение в регистр вывода. Например, если используется выход с открытым стоком, то сигнал на выходе будет определяться также и другими, подключёнными к этой линии выходами (которые образуют "монтажное" И или ИЛИ). Если вывод сконфигурирован как аналоговый вход, то при чтении из регистра в соответствующем разряде будем получать 0 (триггер Шмитта отключён).

Выбором соответствующего режима входа может быть подключен один из подтягивающих резисторов. Наличие встроенных подтягивающих резисторов позволяет сократить количество компонентов на плате и упростить устройство в целом. Они используются для того, чтобы задать уровень сигнала на входе, который может в какие-то моменты работы устройства оставаться неподключённым, "висящим". Пример: вывод подключён через кнопку к общему проводу. Когда кнопка нажата, на выводе формируется логический 0. При разомкнутой кнопке подтягивающий резистор pull-up задаст логическую 1. Другие возможные варианты использования: подтягивание неиспользуемых выводов микроконтроллера (чтобы избежать наведения помех в этих выводах); при подключении к выходу с Z-состоянием или к выходу с открытым стоком. Стоит иметь в виду, что сопротивление внутренних подтягивающих резисторов довольно велико (для STM32F100xx по datasheet находится в пределах 30..50 кОм, типичное значение 40 кОм). С другой стороны, например для механических кнопок, может быть ограничен минимальный ток. При токе ниже минимального не гарантируется низкое сопротивление замкнутых контактов нажатой кнопки, кроме того это сопротивление может быть нестабильным и являться источником повышенного шума. Не знаю, по этой причине или из других соображений, но в STM32 value line Discovery evaluation board, например, используются внешние подтягивающие резисторы для подключения кнопок.

Кроме того, вход с подтягивающим резистором можно использовать как выход с большим выходным сопротивлением! Такой вариант тоже иногда бывает полезен!

GPIO: характеристики

Здесь будут рассмотрены только некоторые характеристики портов ввода/вывода микроконтроллеров STM32F100xx. За подробной информацией обращайтесь к datasheet.

Примечание. В datasheet указываются минимальные, максимальные и типовые значения величин. Если не указано иное, то гарантируется, что значение не выходит за пределы минимума и максимума в наиболее неблагоприятных условиях температуры окружающей среды, напряжения питания, тактовой частоты во всех испытуемых устройствах. Тестирование производится при температуре окружающей среды 25º C и при максимальной температуре (85/105º C при максимальной рассеиваемой мощности или 105/125º C при малой, в зависимости от варианта исполнения - соответственно для устройств с суффиксами 6 и 7).

Типовые значения обычно даются для температуры окружающей среды 25º C, напряжения питания 3.3 В.

Максимальные значения тока
Обозначение Характеристика Max Единица
IVDD Общий ток питания по линиям VDDи VDDA 150 мА
IVSS Общий ток по линиям земли VSS 150
IIO Ток выхода, втекающий в любой порт I/O или вывод управления (вывод - потребитель) +25
То же для вытекающего тока (вывод - источник) -25
IINJ(PIN) Инжектированный ток (через защитный диод) для выводов PA5, PA6, PA7 +5/-0
Инжектированный ток для всех остальных выводов (за положительный принят втекающий ток, который возникает при VIN>VDD) ±5
ΣIINJ(PIN) Общий инжектированный ток (сумма IINJ(PIN) для всех выводов) ±25

Максимально допустимый выходной ток одного вывода составляет 25 мА; если нагружены несколько выводов, то нужно учитывать ограничение по току линий питания микроконтроллера и линий земли величиной 150 мА. Например, если 6 выводов отдают ток по 25 мА каждый, потребуется 150 мА от источника по линиям VDD. Та как сам микроконтроллер тоже потребляет ток, то уже получаем перегрузку.

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

Уровни напряжений на выходе
Обозначение Параметр Условия измерения Min Max Единица
VOL Напряжение низкого уровня на выходе IIO=6 мА
2 В<VDD<2.7 В
  0.4 В
VOH Напряжение высокого уровня на выходе VDD-0.4  
VOL Напряжение низкого уровня на выходе IIO=8 мА
2.7 В<VDD<3.6 В
  0.4
VOH Напряжение высокого уровня на выходе VDD-0.4  
VOL Напряжение низкого уровня на выходе IIO=20 мА
2.7 В<VDD<3.6 В
  1.3
VOH Напряжение высокого уровня на выходе VDD-1.3  

Как видим, выходы микроконтроллера совместимы с TLL и CMOS уровнями сигналов. Напряжение сигнала низкого уровня на выходе TTL должно составлять не более 0.4 В, высокого - не менее 2.4 В. При этом TTL вход должен воспринимать напряжение до 0.8 В как низкий уровень, а выше 2 В - как высокий (разница между уровнями для входа и выхода обеспечивает помехозащищённость). В отличие от TTL, для CMOS уровней нет фиксированных значений, они зависят от напряжения питания. Для входа ориентировочно принимают 0.35*VDD (низкий уровень) и 0.65*VDD (высокий уровень), при напряжении питания 3.3 В соответственно 0.66 В и 2.64 В. Выход CMOS должен обеспечить указанный уровень с запасом для обеспечения желаемой помехозащищённости.

Однако совместимость наблюдается только при малых токах через вывод: до 6 мА при низких напряжениях питания (до 2.7 В) и до 8 мА при напряжении питания свыше 2.7 В. Большие токи допустимы только если нет строгих требований к уровням выходного напряжения (например, управление ключом на биполярном транзисторе, управление оптоэлектронными приборами).

Характеристики входа
Обозначение Параметр Условия измерения Min Typ Max Единица
VIL Напряжение низкого уровня стандартного входа   -0.5   0.28*(VVDD-2)+0.8 В
Напряжение низкого уровня FT-входа -0.5   0.32*(VVDD-2)+0.75
VIH Напряжение высокого уровня стандартного входа 0.41*(VDD–2)+1.3   VDD+0.5
Напряжение высокого уровня FT-входа 0.42*(VDD–2)+1   5.5
Vhys Гистерезис триггера Шмитта на стандартном входе   200     мВ
Гистерезис триггера Шмитта на FT-входе 5% VDD      
Ilkg Ток утечки входа VSS≤VIN≤VDD
Стандартный вход
    ±1 мкА
VIN=5 В
FT-вход
    3
RPU Сопротивление подтягивающего к высокому уровню резистора VIN=VSS 30 40 50 кОм
RPD Сопротивление подтягивающего к низкому уровню резистора VIN=VDD
CIO Ёмкость входа     5   пФ

Входы также являются TTL и CMOS-совместимыми. Микроконтроллер без проблем может работать в смешанных схемах, содержащих CMOS и TTL-логику с напряжением питания 5 В. Неплохо, особенно с учётом того, что сами микросхемы TTL и CMOS не всегда совместимы между собой без преобразования уровней!

GPIO: управление

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

Очень важно не забывать включать тактовый сигнал перед использованием устройства! Тактовый сигнал любого периферийного устройства включается и выключается индивидуально. Это позволяет оптимизировать энергопотребление микроконтроллера в целом, так как есть возможность включить тактовый сигнал только для используемых устройств. В STM32F100xx есть две APB шины для подключения периферии. К APB1 подключены низкоскоростные периферийные устройства, а к APB2 - высокоскоростные (по умолчанию стартовый код для обеих шин устанавливает тактовую частоту 24 МГц). Порты ввода/вывода подключены к APB2, для включения/выключения их тактового сигнала используется регистр APB2ENR устройства RCC (Reset and clock control). Например, чтобы включить порт GPIOC, нужно установить бит IOPCEN регистра. С использованием CMSIS код для включения тактирования GPIOC будет иметь вид:

RCC->APB2ENR|=RCC_APB2ENR_IOPCEN;
Регистры GPIO
GPIOx_CRL Регистры конфигурации порта. Для конфигурирования каждого разряда порта используется 4 бита. Порт содержит 16 разрядов, поэтому всего нужно 64 бита для конфигурирования порта, т.е. два слова: для младшей половины регистра (разряды 0..7) и для старшей (8..15).
GPIOx_CRH
GPIOx_IDR Регистр входных данных порта, только для чтения. 16 младших разрядов регистра содержат логический уровень сигнала на соответствующем выводе порта.
GPIOx_ODR Регистр выходных данных. Определяет уровень на соответствующих выводах порта, работающих в режиме выхода.
GPIOx_BSRR Регистр установки/сброса битов порта, только для записи. Младшие 16 разрядов служат для установки битов в регистре выходных данных, а старшие - для сброса, установка имеет приоритет над сбросом. Запись 1 в младшие 16 разрядов регистра приводит к установке соответствующих разрядов в регистре GPIOx_ODR, запись нулей не изменяет соответствующие разряды. Запись 1 в разряды 16..31 регистра GPIOx_BSRR приводит к сбросу соответствующих им разрядов 0..15 в GPIOx_BSRR.
GPIOx_BRR Регистр сброса битов порта, только для записи. Запись 1 в разряды регистра сбрасывает соответствующие разряды в регистре GPIOx_ODR, запись 0 не изменяет соответствующий разряд.
GPIOx_LCKR Регистр блокировки порта, запись определённой последовательности в порт, защищает выбранные биты от дальнейшей модификации. Чтение остаётся доступным.
AFIO_MAPR Порт для управления переназначением альтернативных функций: некоторые альтернативные функции, закреплённые по умолчанию за одним выводом микроконтроллера, могут быть переназначены на другой вывод. Возможность используется, если требуется освободить какой-то вывод, занятый альтернативной функцией для оптимизации схемы.
AFIO_MAPR2
AFIO_EXTICR1 Выбор входа - источника сигнала для генерации внешнего прерывания.
AFIO_EXTICR2
AFIO_EXTICR3
AFIO_EXTICR4

Регистры конфигурации порта определяют режим работы каждого разряда. Каждому разряду соответствуют 4 бита, для разрядов 0..7 - в GPIOx_CRL (биты 0..3 конфигурируют разряд 0, биты 4..7 - разряд 1 и т.д.), для разрядов 8..15 - в GPIOx_CRH (биты 0..3 - конфигурируют разряд 8 порта, биты 4..7 - разряд 9 и т.д.).

Порядок следования битов в битовом поле конфигурации разряда
Смещение бита +3 +2 +1 +0
Наименование CNF1 CNF0 MODE1 MODE0

Биты MODE задают режим работы вывода: вход или выход.

Значение битов MODE
MODE Режим
00 Вход
01 Выход, максимальная частота 10МГц
10 Выход, максимальная частота 2МГц
11 Выход, максимальная частота 50МГц

Такой загадочный параметр, как максимальная частота выходного сигнала всего-лишь навсего определяет время нарастания и спада сигнала на выходе при переключении. Величина 50МГц указанная в руководстве (Reference manual) проникла туда, очевидно из документации на более высокоскоростные устройства, тогда как в спецификации (Datasheet) честно говорится о максимальных частотах 10, 2 и 24МГц. Время спада и нарастания при переключении составляет 1/4 часть от периода максимальной частоты при ёмкости нагрузки 50пФ (t=1/(4*Fmax)). Составляет соответственно 125нс, 25нс и 8..12нс. Так как увеличение скорости переключения приводит к увеличению потребляемой мощности, не стоит без необходимости выбирать высокоскоростные режимы.

Биты CNF определяют детали конфигурации для бита порта в каждом из режимов.

Значение битов CNF в режиме входа
CNF Значение
00 Аналоговый вход
01 Плавающий вход
10 Вход с подтягивающим резистором
11 Зарезервировано

Если вход сконфигурирован для работы с подтягивающим резистором, то какой из двух подтягивающих резисторов будет использоваться, определяется регистром выходных данных GPIOx_ODR. Если соответствующий выводу бит регистра содержит 0, включается резистор, подтягивающий к низкому уровню, если 1 - к высокому.

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

Значение битов CNF в режиме выхода
CNF Значение
00 Двухтактный выход
01 Выход с открытым стоком
10 Альтернативная функция, двухтактный выход
11 Альтернативная функция, выход с открытым стоком

Доступ к регистрам ARM процессоров осуществляется точно так же как и к памяти. Например, в руководстве на микроконтроллеры STM32F100xx указано, что регистры порта GPIOC расположены в области, начинающейся с адреса 0x40011000. Смещение регистра GPIOx_BSSR относительно начала своей области регистров равно 0x10. Тогда, чтобы, например зажечь зелёный светодиод на оценочной плате (подключён к PC9), можем использовать следующий код:

*(uint32_t*)(0x40011000+0x10)=1<<9;

К счастью, CMSIS даёт возможность писать более наглядный код и избавляет от необходимости помнить адреса всех регистров. В CMSIS устройство описывается как структура, регистры - члены этой структуры. Доступ к регистрам осуществляется как доступ к членам структуры по указателю на неё. Например, для GPIOC файл stm32f10x.h содержит примерно следующий код (это не фрагмент файла, а эквивалентный код, иллюстрирующий концепцию):

// Регистры описываются как volatile,
// для предотвращения нежелательной оптимизации.
#define __IO volatile

// Тип GPIO_TypeDef описывает тип и
// относительное расположение регистров
// устройства GPIO.
typedef struct
{
    __IO uint32_t CRL;
    __IO uint32_t CRH;
    __IO uint32_t IDR;
    __IO uint32_t ODR;
    __IO uint32_t BSRR;
    __IO uint32_t BRR;
    __IO uint32_t LCKR;
} GPIO_TypeDef;

// Адрес области, отведённой под периферию.
#define PERIPH_BASE           ((uint32_t)0x40000000)
// Адрес области памяти для периферийных
// устройств, подключённых к шине APB2.
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
// Адрес области памяти, выделенной для устройства GPIOC.
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
// Указатель на структуру для доступа к устройству GPIOC.
#define GPIOC                 ((GPIO_TypeDef *) GPIOC_BASE)

Такой же подход используется для определения всех периферийных устройств микроконтроллера. Кроме того, файл содержит определения констант. Например, константа для установки бита 9 с использованием регистра BSRR определена как

#define GPIO_BSRR_BS9    ((uint32_t)0x00000200)

Имена имеют вполне закономерную форму: название устройства, название регистра и название бита (или группы битов), разделённые символом подчёркивания. Так что, читая datasheet, можно самостоятельно составлять нужные имена. Наличие в IDE функции автодополнения ещё более упрощает работу.

// Итак, добавив в код строчку
#include <stm32f10x.h>
// получаем возможность вместо кода
// *(uint32_t*)(0x40011000+0x10)=1<<9;
// использовать более читаемый и переносимый код
GPIOC->BSRR=GPIO_BSRR_BS9;

Пример программы

Итак, обещанная программа для зажигания светодиода, подключенного анодом через резистор к PC9.
// (!) VECT_TAB_SRAM - определить символ для отладки в RAM (!)

#include <stm32f10x.h>

int main()
{
    // Включаем тактовый сигнал порта GPIOC.
    RCC->APB2ENR|=RCC_APB2ENR_IOPCEN;

    // Конфигурируем PC9 как двухтактный выход,
    // основная функция, макс. частота 2МГц.
    GPIOC->CRH=
            GPIO_CRH_CNF9_1&0|    //CNF: 00
            GPIO_CRH_CNF9_0&0|    //(push-pull)
            GPIO_CRH_MODE9_1|     //MODE: 10
            GPIO_CRH_MODE9_0&0; //(output, 2MHz)
    // Это то же самое, что
    // GPIOC->CRH=GPIO_CRH_MODE9_1;
    // просто я хотел упомянуть все биты для конфигурирования PC9.
    // Остальные выводы порта GPIOC при этом конфигурируются как аналоговые
    // входы (CNFx: 00, MODEx: 00 - так как все биты в инициализирующем
    // значении для GPIOC->CRH, кроме GPIO_CRH_MODE9_1 - нулевые).

    // Включить LED.
    // Так осуществляется доступ к регистрам без использования CMSIS.
    // С использованием CMSIS строка будет иметь вид:
    // GPIOC->BSRR=GPIO_BSRR_BS9;
    *(uint32_t*)(0x40011000+0x10)=1<<9;

    // Выключить LED.
    // Сброс бита с помощь регистра установки/сброса битов.
    GPIOC->BSRR=GPIO_BSRR_BR9;
    
    // Включить LED.
    // Установка бита с помощью регистра установки/сброса битов.
    GPIOC->BSRR=GPIO_BSRR_BS9;
    
    // Выключить - включить LED.
    // Чтение/модификация/запись регистра выходных данных;
    // неатомарно, небезопасно из-за прерываний.
    GPIOC->ODR&=~GPIO_ODR_ODR9;    //Led off.
    GPIOC->ODR|=GPIO_ODR_ODR9;         //Led on.
    
    // Выключить тактирование GPIOC. Порт остаётся в прежнем состоянии,
    // светодиод горит.
    RCC->APB2ENR&=~RCC_APB2ENR_IOPCEN;

    // Когда тактовый сигнал выключен,
    // запись в регистр устройства не даёт эффекта.
    // Пытаемся сбросить бит с помощью регистра сброса битов, но безуспешно.
    GPIOC->BSRR=GPIO_BSRR_BR9;

    // Стоп.
    while(true) {}
}

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

Отметим два момента, которые нужно учитывать в работе с микроконтроллерами. Во-первых, для работы устройств микроконтроллера требуется тактовый сигнал. Для каждого из периферийных устройств по отдельности его можно включить или выключить. Это сделано для экономии потребляемой энергии - мы можем включить только то, что используем. После сброса тактовый сигнал для всех периферийных устройств отключён, так что важно не забыть его включить.

Во-вторых, следует учитывать, что не для всех регистров устанавливаемое после сброса значение - нулевое. Например, для регистров конфигурирования портов GPIOx->CRL и GPIOx->CRH после сброса устанавливается значение 0x44444444 (все выводы - плавающие входы). Это значит, что требуется сбросить битовое поле перед установкой нового значения оператором |=

// Выполнение следующего кода после сброса переведёт PC9 в режим
// выхода с открытым стоком, а не двухтактного выхода:
// было: CNF9: 01, MODE9: 00 - плавающий вход;
// устанавливаем бит MODE9_1 и получаем: CNF9: 01, MODE9: 10 -
// выход с открытым стоком, 2 МГц.
GPIOC->CRH|=GPIO_CRH_MODE9_1;

// Если хотим изменить конфигурацию только вывода PC9, оставив
// остальные выводы в прежнем состоянии, следует использовать общий
// подход в работе с битовыми полями: сбросить старое значение поля,
// затем установить новое.
GPIOC->CRH=
        GPIOC->CRH&~(GPIO_CRH_CNF9|GPIO_CRH_MODE9)| // Сброс по маске.
        GPIO_CRH_MODE9_1;                           // Установка битов.

Неиспользуемые выводы

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

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

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

Наиболее часто используемое решение проблемы - конфигурирование вывода как входа с подтягивающим резистором. Резистор значительно снижает сопротивление входа, делая его невосприимчивым к помехам. Теоретически можно сконфигурировать вывод как выход. Правда это создаст угрозу возникновения конфликтов в будущем, если устройство будет модифицировано, незадействованные выводы задействованы, а код конфигурирования выводов по ошибке не будет исправлен. В одном из примеров от STM говорилось о возможности конфигурирования неиспользуемых выводов как аналоговых входов - в этом случае триггер Шмитта отключается и на его выходе устанавливается логический 0, т.е. независимо от сигналов на входе, связанные с ним цифровые элементы переключаться не будут.

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

Защита выводов от перегрузки

Для того, чтобы ограничить ток через выводы микроконтроллера при ошибочном подключении к внешним элементам схемы, можно использовать включение через ограничивающие ток резисторы. Максимальный ток для большинства выводов микроконтроллеров STM32F100xx составляет 25 мА. Так что, резистор сопротивлением 200 Ом защитит вывод от перегрузки как в случае закорачивания линии на землю, так и при случайном подключении линии к источнику питания до 5 В (для FT-выводов) или к источнику VDD (для не FT-выводов). Если принять ёмкость нагрузки равной 50 пФ, то получившаяся RC-цепь будет иметь постоянную времени 10 нс. Поэтому описанная защита не годится для выводов, которые должны работать с высокочастотными сигналами. Если есть угроза случайного подключения не FT-вывода к источнику +5 В, сопротивление резистора должно быть выбрано с учётом максимально допустимого инжекционного тока. Например, при напряжении питания 3.3 В, максимальном инжекционном токе 5 мА, принимая падение напряжение на защитном диоде имеющим порядок 0.5 В, получаем, что требуется резистор с сопротивлением, как минимум 240 Ом.

Защита выводов микроконтроллера от перегрузки

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

author: hamper; date: 2015-12-23
  @Mail.ru