Передача порта avr как параметра в функцию
Перейти к содержимому

Передача порта avr как параметра в функцию

  • автор:

Ненастоящая работа с портом через указатель

Любое устройство на микроконтроллере AVR использует порты ввода вывода. Для работы с портами у AVR`ок есть три регистра: PORTx, PINx и DDRx, где x — буква порта, например A, B, C и т.д.
Регистр DDRx — определяет направление выводов микроконтроллера, PINx позволяет читать их состояние, «осязать» внешний мир, а PORTx в зависимости от направления вывода или задает его логический уровень, или подключает подтягивающий резистор.
Выводы микроконтроллера в проекте обычно задают с помощью макроопределений — define`ов. Мы получаем некую «отвязку» от железа и в дальнейшем это позволяет нам переназначать выводы на другие порты. Например, это может выглядеть так.


#define BUT_PIN 3
#define BUT_PORTX PORTB
#
define BUT_DDRX DDRB
#define BUT_PINX PINB

Неудобство такого подхода состоит в том, что для каждого вывода нужно определять три регистра. Бывает, что два (только PORTx и DDRx), но это тоже неудобно, если выводов много. Существует другой подход, позволяющий сократить число макроопределений. Разберемся в чем он заключается.

В микроконтроллер atmega16 регистр PORTB имеет адрес 0x18, а регистры DDRB и PINB — 0x17 и 0x16 соответственно. Тоже самое и с регистрами остальных портов, они тоже расположены друг за другом. Мы можем определить в проекте только один регистр, а к остальным обращаться вычисляя их адрес. За основу можно взять любой из них, главное ничего не напутать. Лучше всего для этих целей использовать макросы. Если отталкиваться от регистров PORTx, то макросы будут выглядеть так.


//это макросы для доступа к регистрам порта
#define PortReg(port) (*(port))
#define DirReg(port) (*((port) - 1))
#define PinReg(port) (*((port) - 2))

Макросы принимают в качестве параметра адрес регистра PORTx. Для взятия адреса регистра используется оператор &. Посмотрим, как можно использовать эти макросы.


//определили вывод мк
#define BUT_PIN 3
#define BUT_PORT PORTB
.
// конфигурируем вывод как вход
DirReg(&BUT_PORT) &= ~(1<//включаем подтягивающий резистор
PortReg(&BUT_PORT) |= (1<
.
//проверяем нажата ли кнопка
if(! (PinReg(&BUT_PORT)&(1 <.
>

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

Компилятор преобразует эти макросы в очень компактный код. Точно такой же, как если бы мы обращались к регистрам используя их имена. И дело в том, что с точки зрения ассемблера это так и есть. Если вы посмотрите ассемблерный код этих примеров, то увидите, что обращение к регистрам осуществляется методом прямой адресации с помощью команд IN, OUT. Поэтому я и озаглавил этот раздел «ненастоящая работа с портом через указатели». Указатели вроде как используются, но на самом деле нет.
Такой подход можно использовать не со всеми микроконтроллерами AVR, потому что в некоторых моделях регистры порта располагаются не по соседним адресам. Как, например, регистры порта F в микроконтроллере ATmega128.

Настоящая работа с портом через указатель

Иногда приходится прибегать к работе с портом, используя настоящий указатель. Для этого создается переменная указатель, которая инициализируется адресом какого-нибудь регистра порта. Делается это следующим образом.


//объявляем указатель на регистр
//обязательно должно присутствовать volatile
volatile uint8_t *portReg;

//инициализация
//передаем адрес регистра PORTB
portReg = &PORTB;

//вывод в порт через указатель
//перед указателем ставиться оператор *
(*portReg) = 0xff;

Также этот указатель можно передавать в функцию.


void OutPort(volatile uint8_t *pReg, uint8_t data)
*pReg = data;
>

.
//записываем в PORTB число 0xff
OutPort(&PORTB, 0xff);

//а здесь уже не нужен оператор &
//так как мы передаем переменную с адресом порта
OutPort(portReg, 0xff);

Работа с портом через указатель открывает большие возможности. Например, мы можем определить структуру, которая будет хранить все настройки пина микроконтроллера и обращаться к выводу, используя эту структуру. Или можем определить структуру виртуального порта содержащую выводы микроконтроллера из разных физических портов.
Все это так, но есть ложка дегтя. Работа с регистрами порта через указатель «тяжеловесна» с точки зрения размера кода и его быстродействия. Чтобы в этом убедиться, достаточно взглянуть на получаемый ассемблерный код. Если эти два фактора не критичны, то такой подход можно использовать, если нет, то придется работать по старинке.
Также п ри работе с портом через указатели, даже операция установки/сброса разряда будет неатомарна. Атомарность операций в этом случае нужно обеспечивать самостоятельно.

Вот небольшой пример, как можно использовать указатели при работе с портом.


//струтура для хранения настроек вывода - номера и порта
typedef struct outputs uint8_t pin;
volatile uint8_t *portReg;
>outputs_t;


//функция инициализации
void OUT_Init(outputs_t *out, uint8_t pin, volatile uint8_t *port, uint8_t level)
//сохраняем настройки в структуру
out->pin = pin;
out->portReg = port;

//конфигурируем вывод на выход
(*(port-1)) |= (1<
//задаем логический уровень
if (level) (*port) |= (1 <>
else (*port) &= ~(1 <>
>

//установить на выходе 1
void OUT_Set(outputs_t *out)
(*(out->portReg)) |= (1 pin);
>

//установить на выходе 0
void OUT_Clear(outputs_t *out)
(*(out->portReg)) &= ~(1 pin);
>

//определили вывод мк
#define OUT1_PIN 4
#define OUT1_PORT PORTB
.
//объявляем переменную для хранения
//настроек пина
outputs_t out1;

//инициализируем ее
OUT_Init(&out1, OUT1_PIN, OUT1_PORT, 0);

//устанавливаем 1 на выводе OUT1_PIN
OUT_Set(&out1);

Еще один пример работы с портом через указатели есть в коде к статье » сенсорная кнопка на микроконтроллере «.

Устройство и работа портов ввода-вывода микроконтроллеров AVR. Часть 1

Изучив данный материал, в котором все очень детально и подробно описано с большим количеством примеров, вы сможете легко овладеть и программировать порты ввода/вывода микроконтроллеров AVR.

  • Часть 1. Работа портов ввода/вывода
  • Часть 2. Подключение светодиода к линии порта ввода/вывода
  • Часть 3. Подключение транзистора к линии порта ввода/вывода
  • Часть 4. Подключение кнопки к линии порта ввода/вывода

Пример будем рассматривать на микроконтроллере ATMega8.

Программу писать будем в Atmel Studio 6.0.

Эмулировать схему будем в Proteus 7 Professional.

С внешним миром микроконтроллер общается через порты ввода вывода. Схема порта ввода вывода указана в даташите:

image

Но новичку разобраться довольно со схемой довольно сложно. Поэтому схему упростим:

image

Pxn – имя ножки порта микроконтроллера, где x буква порта (A, B, C или D), n номер разряда порта (7… 0).
Cpin — паразитная емкость порта.
VCC — напряжение питания.
Rpu — отключаемый нагрузочный верхний резистор (pull-up).
PORTxn — бит n регистра PORTx.
PINxn — бит n регистра PINx.
DDRxn — бит n регистра DDRx.

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

Конденсатор (см.2) — это паразитная емкость вывода. Хоть она и крошечная, но присутствует. Обычно ее не учитывают, но она есть. Не забивай голову, просто знай это.

Дальше идут ключи управления (см.3,4). Каждый ключ подчинен логическому условию, которые нарисованы на рисунке. Когда условие выполняется — ключ замыкается.

Каждый порт микроконтроллера AVR (обычно имеют имена A, B и иногда C или даже D) имеет 8 разрядов, каждый из которых привязан к определенной ножке корпуса. Каждый порт имеет три специальных регистра DDRx, PORTx и PINx (где x соответствует букве порта A, B, C или D). Назначение регистров:

DDRx – Настройка разрядов порта x на вход или выход.

PORTx – Управление состоянием выходов порта x (если соответствующий разряд настроен как выход), или подключением внутреннего pull-up резистора (если соответствующий разряд настроен как вход).

PINx –Чтение логических уровней разрядов порта x.

PINхn – это регистр чтения. Из него можно только читать. В регистре PINxn содержится информация о реальном текущем логическом уровне на выводах порта. Вне зависимости от настроек порта. Так что если хотим узнать что у нас на входе — читаем соответствующий бит регистра PINxn. Причем существует две границы: граница гарантированного нуля и граница гарантированной единицы — пороги за которыми мы можем однозначно четко определить текущий логический уровень. Для пятивольтового питания это 1.4 и 1.8 вольт соответственно. То есть при снижении напряжения от максимума до минимума бит в регистре PINx переключится с 1 на 0 только при снижении напряжение ниже 1.4 вольт, а вот когда напряжение нарастает от минимума до максимума переключение бита с 0 на 1 будет только по достижении напряжения в 1.8 вольта. То есть возникает гистерезис переключения с 0 на 1, что исключает хаотичные переключения под действием помех и наводок, а также исключает ошибочное считывание логического уровня между порогами переключения.

При снижении напряжения питания разумеется эти пороги также снижаются.

DDRxn – это регистр направления порта. Порт в конкретный момент времени может быть либо входом либо выходом (но для состояния битов PINxn это значения не имеет. Читать из PINxn реальное значение можно всегда).

DDRxy = 0 – вывод работает как ВХОД.

DDRxy = 1 – вывод работает на ВЫХОД.

PORTxn – режим управления состоянием вывода. Когда мы настраиваем вывод на вход, то от PORTх зависит тип входа (Hi-Z или PullUp, об этом чуть ниже).

Когда ножка настроена на выход, то значение соответствующего бита в регистре PORTx определяет состояние вывода. Если PORTxn=1 то на выводе лог.1, если PORTxn=0 то на выводе лог.0.

Когда ножка настроена на вход, то если PORTxn=0, то вывод в режиме Hi-Z. Если PORTxn=1 то вывод в режиме PullUpс подтяжкой резистором в 100к до питания.

Таблица. Конфигурация выводов портов.

DDRxn PORTxn I/O Comment
0 0 I (Input) Вход Высокоимпендансный вход. (Не рекомендую использовать, так как могут наводится наводки от питания)
0 1 I (Input) Вход Подтянуто внутренне сопротивление.
1 0 O (Output) Выход На выходе низкий уровень.
1 1 O (Output) Выход На выходе высокий уровень.

Общая картина работы порта показана на рисунках:

image

Рис. DDRxn=0 PORTxn=0 – Режим: HI-Z – высоко импендансный вход.

image

Рис. DDRxn=0 PORTxn=1 – Режим: PullUp – вход с подтяжкой до лог.1.

image

Рис. DDRxn=1 PORTxn=0 – Режим: Выход – на выходе лог.0. (почти GND)

image

Рис. DDRxn=1 PORTxn=1 – Режим: Выход – на выходе лог.1. (почти VCC)

Вход Hi-Z — режим высокоимпендансного входа.
Этот режим включен по умолчанию. Все ключи разомкнуты, а сопротивление порта очень велико. В принципе, по сравнению с другими режимами, можно его считать бесконечностью. То есть электрически вывод как бы вообще никуда не подключен и ни на что не влияет. Но! При этом он постоянно считывает свое состояние в регистр PINn и мы всегда можем узнать что у нас на входе — единица или ноль. Этот режим хорош для прослушивания какой либо шины данных, т.к. он не оказывает на шину никакого влияния. А что будет если вход висит в воздухе? А в этом случае напряжение будет на нем скакать в зависимости от внешних наводок, электромагнитных помех и вообще от фазы луны и погоды на Марсе (идеальный способ нарубить случайных чисел!). Очень часто на порту в этом случае нестабильный синус 50Гц — наводка от сети 220В, а в регистре PINn будет меняться 0 и 1 с частотой около 50Гц

Вход PullUp — вход с подтяжкой.
При DDRxn=0 и PORTxn=1 замыкается ключ подтяжки и к линии подключается резистор в 100кОм, что моментально приводит не подключенную никуда линию в состояние лог.1. Цель подтяжки очевидна — не допустить хаотичного изменения состояния на входе под действием наводок. Но если на входе появится логический ноль (замыкание линии на землю кнопкой или другим микроконтроллером/микросхемой), то слабый 100кОмный резистор не сможет удерживать напряжение на линии на уровне лог.1 и на входе будет лог.0.

Режим выхода.
Тут, думаю, все понятно — если нам надо выдать в порт лог.1, мы включаем порт на выход (DDRxn=1) и выдаем лог.1 (PORTxn=1) — при этом замыкается верхний ключ и на выводе появляется напряжение, близкое к питанию. А если надо лог.0, то включаем порт на выход (DDRxn=1) и выдаем лог.0 (PORTxn=1) — при этом открывается уже нижний вентиль, что дает на выводе около нуля вольт.

  • программирование
  • программирование микроконтроллеров
  • программирование avr
  • микроконтроллеры avr
  • подключение кнопки к avr
  • подключение светодиода к avr

Передача порта avr как параметра в функцию

В этой статье описан принцип работы порта как GPIO и совмещения на нем дополнительных (альтернативных) функций, привязанных к различным периферийным устройствам (UART, TWI, SPI и т. д.) на примере популярного микроконтроллера ATmega328P (перевод из даташита). Многое, о чем говорится в этой статье, может быть практически без изменений перенесено на другие модели микроконтроллеров семейства Atmel AVR.

Все внешние выводы портов AVR имеют функционал Read-Modify-Write (т. е. совмещенная в одной инструкции операция чтение-модификация-запись), когда вывод работает как обычный цифровой порт ввода/вывода (I/O, или GPIO). Это означает, что направление работы одной ножки порта (вход это или выход) можно поменять (с помощью инструкций SBI и CBI) без нежелательных изменений направления работы других выводов. То же самое справедливо, когда программно меняется значение лог. уровня ножки выхода порта (если она сконфигурирована как выход), или когда разрешается или запрещается наличие на ножке порта pull-up резистора (если ножка порта сконфигурирована как вход). Каждый выходной силовой буфер ножки порта обладает одинаковыми нагрузочными характеристиками по входному току (ток лог. 0) и по выходному току (ток лог. 1). Драйвер выхода достаточно мощный, чтобы напрямую управлять током светодиода (LED). На всех ножках портов можно индивидуально настроить наличие подключенного внутреннего верхнего нагрузочного (pull-up) резистора; будучи включенным, этот резистор подтягивает лог. уровень входа к напряжению питания (лог. 1). Также на всех ножках портов I/O имеются внутренние защитные диоды, подключенные на шины питания VCC и GND, см. рис. 14-1.

AVR-input-simply-sch

AVR-output-simply-sch

Рис. 14-1. Эквивалентная схема внутренней схемотехники ножки порта ввода/вывода AVR.

Вот краткая сводка по параметра ножки порта AVR (в диапазоне температур TA = -40°C .. 85°C):

Параметр
Значение Ед.
Максимальный выходной ток ножки порта 40 мА
Напряжение на ножке порта -0.5..VCC+0.5 V
Сопротивление pull-up резистора 20..50
Ток утечки по входу ≤1 μA

Подробнее про электрические характеристики порта см. даташит на микроконтроллер.

[Работа выводов в качестве GPIO]

Выводы портов микроконтроллера могут работать как двунаправленные цифровые сигналы ввода/вывода общего назначения (GPIO). Это означает, что логическое состояние ножки порта как выхода можно программно устанавливать либо в лог. 0, либо в лог. 1, и также можно читать состояние ножки порта, настроенного либо на вход, либо на выход. В режиме работы как вход на ножке порта можно разрешить подключение внутреннего верхнего нагрузочного резистора (pull-up). На рис. 14-2 показано функциональное описание одного вывода порта I/O, где вывод обозначен для общего случая как Pxn (вместо x подставляется буква порта A, B, C, D и т. д., а вместо n подставляется номер разряда порта 7..0).

AVR General Digital IO sch

Рис. 14-2. Внутреннее устройство цифрового порта ввода/вывода.

Примечание: WRx, WPx, WDx, RRx, RPx и RDx являются общими для всех выводов в пределах одного и того же порта (A, B, C, D и т. д.). Сигналы clkI/O, SLEEP и PUD являются общими для всех портов.

PUD: PULLUP DISABLE
SLEEP: SLEEP CONTROL
clkI/O: I/O CLOCK
WDx: WRITE DDRx
RDx: READ DDRx
WRx: WRITE PORTx
RRx: READ PORTx REGISTER
RPx: READ PORTx PIN
WPx: WRITE PINx REGISTER

Конфигурирование ножки порта. К каждому выводу порта привязана три бита регистров: DDxn, PORTxn и PINxn (здесь указаны обобщенные имена, где x соответствует имени порта A, B, C и т. д., а n соответствует номеру разряда порта от 7 до 0). Как показано в описании регистров (DDRx, PORTx, PINx) ниже, к битам DDxn осуществляется доступ через адрес ввода/вывода DDRx, к битам PORTxn осуществляется доступ через адрес ввода/вывода PORTx, и к выводам PINxn осуществляется доступ через адрес ввода/вывода PINx.

Бит DDxn в регистре DDRx выбирает направление работы этого вывода. По умолчанию (после сброса или включения питания) в бит DDRx записан 0, и вывод порта работает как вход с отключенным pull-up резистором (находится в третьем состоянии). Если в DDxn записать лог. 1, то ножка порта Pxn конфигурируется как выход. Если в DDxn записать лог. 0, то вывод порта Pxn конфигурируется как вход.

Если в PORTxn записать лог. 1 когда вывод сконфигурирован как вход (DDxn=0), то на этом выводе активируется верхний нагрузочный резистор, встроенный в кристалл микроконтроллера (pull-up). Чтобы отключить резистор pull-up, в бит PORTxn записывается лог. 0. Либо можно сконфигурировать вывод как выход. Все выводы портов находятся в третьем (отключенном) состоянии с высоким сопротивлением, когда произошел сброса, даже если нет тактирования ядра микроконтроллера.

Если в PORTxn записать лог. 1, когда вывод сконфигурирован как выход (DDxn=0), то драйверы порта переведут его в состояние лог. 1 (при этом на выводе микросхемы микроконтроллера появится уровень, близкий к напряжению питания). Если в PORTxn записать лог. 0, когда вывод сконфигурирован как выход (DDxn=0), то драйверы порта переведут его в состояние лог. 0 (при этом на выводе микросхемы микроконтроллера будет очень малое напряжение, близкое к уровню GND).

Переключение вывода в противоположное состояние (toggling). Запись лог. 1 PINxn переключает значение PORTxn, независимо от значения DDRxn. Обратите внимание, что можно использовать инструкцию SBI для переключения какого-то одного бита в 8-разрядном порте.

Переключение между входом и выходом. Когда происходит переключение между третьим состоянием ( = 0b00) и выходом в лог. 1 ( = 0b11), должно произойти промежуточное состояние либо с разрешенным pull-up ( = 0b01), либо с выходным уровнем в лог. 0 ( = 0b10). Обычно полностью приемлемым будет разрешить состояние с разрешенным pull-up, поскольку нагрузка с высоким сопротивлением не увидит разницы между мощным лог. 1 от выходного драйвера и уровнем от pull-up. Если здесь другой случай, то бит PUD в регистре MCUCR можно установить с целью запретить все резисторы pull-ups на всех портах.

Переключение между входом с разрешенным pull-up и выходом с лог. 0 дает ту же самую проблему. Пользователь должен либо использовать промежуточное состояние с высоким сопротивлением вывода ( = 0b00), либо промежуточное состояние с высоким выходным уровнем ( = 0b11).

В таблице 14-1 сделан общий обзор управляющих сигналов, которые влияют на состояние вывода.

Таблица 14-1. Возможные варианты конфигурации вывода.

DDxn
PORTxn PUD I/O pull-up Примечание
0 0 X вход нет Третье состояние, отключено (Hi-Z).
0 1 0 вход да Через вывод ножки порта Pxn будет течь выходной ток, когда ножка притянута снаружи к земле.
0 1 1 вход нет Третье состояние, отключено (Hi-Z).
1 0 X выход нет Порт настроен как выход, и на нем уровень лог. 0 (втекающий снаружи ток).
1 1 X выход нет Порт настроен как выход, и на нем уровень лог. 1 (вытекающий наружу ток).

Чтение значения на выводе. Независимо от состояния бита настройки направления работы порта DDxn, состояние вывода порта может быть прочитано через чтение состояния бит регистра PINxn. Как показано на рис. 14-2, бит регистра PINxn и предшествующая защелка составляют синхронизатор. Такая схема нужна для того, чтобы избежать неопределенных изменений состояния физического вывода поблизости от перепадов внутренней тактовой частоты, однако это также вводит задержку. На рис. 14-3 показана диаграмма времени синхронизации при чтении внешне приложенного уровня напряжения. Максимальное и минимальное значение задержки распространения обозначены как tpd,max и tpd,min соответственно.

AVR Synchronization Reading Externally Applied Pin value

Рис. 14-3. Синхронизация при чтении уровня напряжения, приложенного к выводу снаружи.

Рассмотрим случай, когда период тактов начинается почти сразу после первого спада системной частоты. Защелка закрыта, когда такт в состоянии лог. 0, и становится прозрачной, когда такт в лог. 1, как показано на затененной области сигнала «SYNC LATCH». Значение сигнала защелкивается, когда системная тактовая частота переходит в лог. 0. Значение попадает в регистр PINxn на последующем положительном перепаде тактов. Как показано двумя стрелками tpd,max и tpd,min, одиночное изменение сигнала на выводе будет задержано на время от 0.5 до 1.5 периода тактовой частоты, в зависимости от момента, когда произошло изменение.

Когда происходит обратное чтение программно установленной величины цифрового сигнала на выходе, должна быть вставлена инструкция nop, как это показано на рис. 14-4. Инструкция out устанавливает сигнал «SYNC LATCH» на положительном перепаде тактов. В этом случае задержка tpd через прохождение синхронизатора составит 1 период системной тактовой частоты.

AVR Synchronization Reading Software Assigned Pin Value

Рис. 14-4. Синхронизация при чтении программно установленного значения на выводе.

Ниже приведены примеры кода, где показано как установить выводы разрядов 0 и 1 порта B в состояние лог. 1, и выводы разрядов 2 и 3 в лог. 0, и определить выводы порта с разрядами 7..4 как входы, где разрешены резисторы pull-up на выводах разрядов порта 6 и 7. Полученные значения на выводах порта вычитываются обратно, однако как обсуждалось ранее, добавлена инструкция nop, чтобы была возможность правильно прочитать только что установленные значения на выводах порта.

Пример кода на ассемблере:

; Настройка резисторов pull-up и установка выходов в лог. 1.
; Задается направление работы для выводов порта. ldi r16,(1  PB7)|(1  PB6)|(1  PB1)|(1  PB0) ldi r17,(1  DDB3)|(1  DDB2)|(1  DDB1)|(1  DDB0) out PORTB,r16 out DDRB,r17
; Добавление инструкции nop для синхронизации. nop
; Чтение выводов порта. in r16,PINB . 

Примечание: Для программы на ассемблере используются два временных регистра, чтобы минимизировать время между установкой pull-up выводов 0, 1, 6 и 7 и правильной установкой битов направления, определяя биты 2 и 3 как находящиеся в лог. 0 и переназначая биты 0 и 1 и как мощный выход в состоянии лог. 1.

Пример кода на языке C:

/* Настройка резисторов pull-up и установка выходов в лог. 1.
 Задается направление работы для выводов порта. */ PORTB = (1  PB7)|(1  PB6)|(1  PB1)|(1  PB0); DDRB = (1  DDB3)|(1  DDB2)|(1  DDB1)|(1  DDB0);
/* Добавление инструкции nop для синхронизации. */ __no_operation();
/* Чтение выводов порта. */ i = PINB; .

Разрешение цифровых входов и режимы сна (Sleep Modes). Как показано на рис. 14-2, цифровой входной сигнал может быть зафиксирован на уровне земли по входу триггера Шмитта. Сигнал, обозначенный на рисунке как SLEEP, устанавливается контроллером сна (MCU Sleep Controller) в режиме пониженного энергопотребления (режимы сна Power-down mode, Power-save mode и Standby mode), чтобы избежать потерь энергии, если некоторые входные сигналы остались неподключенными, или у них аналоговый уровень сигнала близок к VCC/2.

Сигнал SLEEP отменяется для выводов порта, которые разрешены как выводы внешних прерываний (external interrupt pins). Если запрос на внешнее прерывание не разрешен, то SLEEP для этих выводов все еще активен. SLEEP также отменяется различными альтернативными функциями портов, привязанных к периферийным устройствам.

Если на асинхронном внешнем выводе прерывания, который настроен как «прерывание по спаду» или как «прерывание по фронту» или как «прерывание по обоим изменениям сигнала», присутствует лог. 1, и внешнее прерывание не разрешено, то установится соответствующий флаг внешнего прерывания (External Interrupt Flag), когда происходит выход из вышеупомянутых режимов сна, так как изменение уровня выводит микроконтроллер из режима сна.

Не подключенные выводы. Если некоторые выводы остались неподключенными, то рекомендуется убедиться, что на этих выводах задан определенный уровень (либо лог. 0, либо лог. 1). Даже при том, что большинство цифровых входов запрещено в режиме глубокого сна, как это описано выше, нужно избегать наличия плавающих входов, чтобы уменьшить энергопотребление во всех других режимах, где цифровые входы разрешены (Reset, Active mode и Idle mode).

Самый простой метод гарантировать наличие определенного уровня на неиспользуемых выводах портов — разрешить внутренний резистор pull-up. В этом случае pull-up будет запрещен во время сброса. Если важно малое потребление энергии во время сброса, то рекомендуется использовать внешние подтягивающие резисторы pull-up или pull-down. Подключение неиспользуемых выводов напрямую к VCC или GND не рекомендуется, потому что это может привести к чрезмерному току короткого замыкания выхода, если эти выводы случайно были сконфигурированы как выходы.

[Альтернативные функции выводов порта]

У большинства выводов портов есть альтернативные функции, которые могут быть привязаны к ножке порта (под альтернативностью имеется в виду аппаратные функции, отличающиеся от работы ножки порта в режиме GPIO). На рис. 14-5 показаны управляющие сигналы из рис. 14-2 (см. врезку «Работа выводов в качестве GPIO»), которые могут переназначить альтернативные функции. Переназначающие сигналы могут не присутствовать на всех выводах порта, но рисунок показывает общее описание, которое может относиться ко всем выводам портов микроконтроллера семейства AVR.

AVR Alternate Port Functions

Рис. 14-5. Альтернативные функции вывода порта (1) .

PUOExn: Pxn PULL-UP OVERRIDE ENABLE
PUOVxn: Pxn PULL-UP OVERRIDE VALUE
DDOExn: Pxn DATA DIRECTION OVERRIDE ENABLE
DDOVxn: Pxn DATA DIRECTION OVERRIDE VALUE
PVOExn: Pxn PORT VALUE OVERRIDE ENABLE
PVOVxn: Pxn PORT VALUE OVERRIDE VALUE
DIEOExn: Pxn DIGITAL INPUT-ENABLE OVERRIDE ENABLE
DIEOVxn: Pxn DIGITAL INPUT-ENABLE OVERRIDE VALUE
SLEEP: SLEEP CONTROL
PTOExn: Pxn, PORT TOGGLE OVERRIDE ENABLE
PUD: PULLUP DISABLE
WDx: WRITE DDRx
RDx: READ DDRx
RRx: READ PORTx REGISTER
WRx: WRITE PORTx
RPx: READ PORTx PIN
WPx: WRITE PINx
clkI/O: I/O CLOCK
DIxn: DIGITAL INPUT PIN n ON PORTx
AIOxn: ANALOG INPUT/OUTPUT PIN n ON PORTx

Примечание (1): WRx, WPx, WDx, RRx, RPx и RDx являются общими для всех выводов в пределах одного порта (A, B, C, D и т. д.). Сигналы clkI/O, SLEEP и PUD общие для всех портов. Все другие сигналы уникальны для каждого вывода порта.

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

Таблица 14-2. Общее описание сигналов переназначения для альтернативных функций.

Имя
Полное имя Описание
PUOE Pull-up Override Enable Если этот сигнал в лог. 1, то разрешение резисторов pull-up управляется сигналом PUOV. Если сигнал PUOE в лог. 0, то резистор pull-up разрешен, когда = 0b010.
PUOV Pull-up Override Value Если PUOE в лог. 1, то pull-up разрешен/запрещен, когда PUOV в лог. 1/лог. 0, независимо от установки битов регистров DDxn, PORTxn и PUD.
DDOE Data Direction Override Enable Если этот сигнал в лог. 1, то сигнал Output Driver Enable управляется сигналом DDOV. Если сигнал DDOE очищен, то выходной драйвер разрешается битом DDxn регистра DDRx.
DDOV Data Direction Override Value Если DDOE в лог. 1, то выходной драйвер разрешен/запрещен, когда DDOV в лог. 1/лог. 0, независимо от установки бита DDxn в регистре DDRx.
PVOE Port Value Override Enable Если этот сигнал в лог. 1, и выходной драйвер разрешен, то значение порта управляется сигналом PVOV. Если PVOE в лог. 0, и выходной драйвер разрешен, то значение на ножке порта управляется битом PORTxn регистра PORTx.
PVOV Port Value Override Value Если PVOE в лог. 1, значение порта установится в значение PVOV, независимо от установки бита PORTxn регистра PORTx.
PTOE Port Toggle Override Enable Если бит PTOE в лог. 1, то бит PORTxn регистра PORTx инвертируется.
DIEOE Digital Input Enable Override Enable Если этот бит в лог. 1, то Digital Input Enable управляется сигналом DIEOV. Если DIEOE в лог. 0, то Digital Input Enable определяется состоянием микроконтроллера (Normal mode, sleep mode).
DIEOV Digital Input Enable Override Value Если DIEOE в лог. 1, то Digital Input разрешается/запрещается, когда DIEOV в лог. 1/лог. 0, независимо от состояния микроконтроллера (Normal mode, sleep mode).
DI Digital Input Это цифровой вход для альтернативных функций. На рисунке этот сигнал подключен к выходу триггера Шмитта, но перед синхронизатором. За исключением ситуации использования цифрового входа как источника тактов, модуль с альтернативной функцией имеет свой собственный синхронизатор.
AIO Analog Input/Output Это аналоговый вход/выход для альтернативных функций. Этот сигнал подключен напрямую к выводу кристалла, и его можно использовать в двух направлениях.

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

Таблица 14-3. Альтернативные функции порта B.

Порт Альтернативная функция
PB7 XTAL2 (Chip Clock Oscillator pin 2)
TOSC2 (Timer Oscillator pin 2)
PCINT7 (Pin Change Interrupt 7)
PB6 XTAL1 (Chip Clock Oscillator pin 1 or External clock input)
TOSC1 (Timer Oscillator pin 1)
PCINT6 (Pin Change Interrupt 6)
PB5 SCK (SPI Bus Master clock Input)
PCINT5 (Pin Change Interrupt 5)
PB4 MISO (SPI Bus Master Input/Slave Output)
PCINT4 (Pin Change Interrupt 4)
PB3 MOSI (SPI Bus Master Output/Slave Input)
OC2A (Timer/Counter2 Output Compare Match A Output)
PCINT3 (Pin Change Interrupt 3)
PB2 ~SS (SPI Bus Master Slave select)
OC1B (Timer/Counter1 Output Compare Match B Output)
PCINT2 (Pin Change Interrupt 2)
PB1 OC1A (Timer/Counter1 Output Compare Match A Output)
PCINT1 (Pin Change Interrupt 1)
PB0 ICP1 (Timer/Counter1 Input Capture Input)
CLKO (Divided System Clock Output)
PCINT0 (Pin Change Interrupt 0)

XTAL2/TOSC2/PCINT7 – Port B, Bit 7

XTAL2: вывод 2 внутреннего системного генератора тактов (Oscillator), выход. Обычно (по умолчанию) используется как вывод для подключения кварцевого резонатора или низкочастотного резонатора, как вариант пьезокерамического. Когда этот вывод используется как вывод тактового генератора, его нельзя использовать как порт ввода/вывода (GPIO).

TOSC2: вывод 2 для генератора таймера (Timer Oscillator). Используется только если в качестве источника системной тактовой частоты используется внутренний калиброванный RC-генератор, и разрешен асинхронный таймер правильной настройкой в регистре ASSR. Когда бит AS2 в регистре ASSR установлен в лог. 1, и бит EXCLK сброшен в лог. 0 для разрешения асинхронного тактирования Timer/Counter2 с помощью кварцевого резонатора, вывод PB7 отключается от порта, и становится инвертирующим выходом усилителя генератора. В таком режиме к этому выводу подключен кварцевый резонатор (обычно на частоту 32768 Гц), и вывод не может использоваться как порт ввода/вывода (GPIO).

PCINT7: источник прерывания 7 по изменению уровня (Pin Change Interrupt source 7). Вывод порта PB7 может работать как вход внешнего источника прерывания.

Если PB7 используется как вывод тактов, то разряды DDB7, PORTB7 и PINB7 все читаются как 0.

XTAL1/TOSC1/PCINT6 – Port B, Bit 6

XTAL1: вывод 1 внутреннего системного генератора тактов (Oscillator), вход. Используется для всех источников тактовой частоты за исключением внутреннего калиброванного RC-генератора. Когда используется как вывод тактов, этот вывод не может быть использован как порт ввода/вывода (GPIO).

TOSC1: вывод 1 для генератора таймера (Timer Oscillator). Используется только если в качестве источника системной тактовой частоты используется внутренний калиброванный RC-генератор, и разрешен асинхронный таймер правильной настройкой в регистре ASSR. Когда бит AS2 в регистре ASSR установлен в лог. 1, и бит EXCLK сброшен в лог. 0 для разрешения асинхронного тактирования Timer/Counter2 с помощью кварцевого резонатора, вывод PB6 отключается от порта, и становится входом инвертирующего усилителя генератора. В таком режиме к этому выводу подключен кварцевый резонатор (обычно на частоту 32768 Гц), и вывод не может использоваться как порт ввода/вывода (GPIO).

PCINT6: источник прерывания 6 по изменению уровня (Pin Change Interrupt source 6). Вывод порта PB6 может работать как вход внешнего источника прерывания.

Если PB6 используется как вывод тактов, то разряды DDB6, PORTB6 и PINB6 все читаются как 0.

SCK/PCINT5 – Port B, Bit 5

SCK: Master Clock output, Slave Clock input для канала SPI. Когда SPI разрешен в режиме Slave, этот вывод конфигурируется как вход независимо от установки бита DDB5. Когда SPI сконфигурирован в режиме Master, направление данных на этом выводе управляется битом DDB5. Когда этот вывод принудительно назначен блоком SPI как вход, то подключение pull-up резистора все еще можно задавать битом PORTB5.

PCINT5: источник прерывания 5 по изменению уровня (Pin Change Interrupt source 5). Вывод порта PB5 может работать как вход внешнего источника прерывания.

MISO/PCINT4 – Port B, Bit 4

MISO: Master Data input, Slave Data output для канала SPI. Когда SPI разрешен в режиме Master, этот вывод конфигурируется как вход независимо от установки бита DDB4. Когда SPI разрешен в режиме Slave, направление данных на этом выводе управляется битом DDB4. Когда вывод принудительно сконфигурирован блоком SPI как вход, то подключение pull-up резистора все еще можно задавать битом PORTB4.

PCINT4: источник прерывания 4 по изменению уровня (Pin Change Interrupt source 4). Вывод порта PB4 может работать как вход внешнего источника прерывания.

MOSI/OC2/PCINT3 – Port B, Bit 3

MOSI: SPI Master Data output, Slave Data input для канала SPI. Когда SPI разрешен в режиме Slave, этот вывод конфигурируется как вход независимо от установки бита DDB3. Когда SPI разрешен в режиме Master, направление данных на этом выводе управляется состоянием бита DDB3. Когда этот вывод принудительно сконфигурирован блоком SPI как вход, то pull-up все еще может управляться состоянием бита PORTB3.

OC2, Output Compare Match Output: вывод PB3 может работать как аппаратный выходной сигнал, сообщающий о событии совпадения таймера/счетчика 2 (Timer/Counter2 Compare Match). Для обслуживания этой функции PB3 должен быть сконфигурирован как выход (DDB3=1). Вывод OC2 также работает как выходной сигнал работы таймера в режиме аппаратного ШИМ (PWM mode).

PCINT3: источник прерывания 3 по изменению уровня (Pin Change Interrupt source 3). Вывод порта PB3 может работать как вход внешнего источника прерывания.

~SS/OC1B/PCINT2 – Port B, Bit 2

~SS: Slave Select input. Когда SPI разрешен в режиме Slave, этот вывод конфигурируется как вход независимо от установки бита DDB2. Как устройство Slave, блок SPI активируется, когда этот вывод подтягивается к лог. 0. Когда SPI разрешен в режиме Master, направление данных на этом выводе управляется состоянием бита DDB2. Когда этот вывод принудительно настроен блоком SPI как вход, то подключение pull-up все еще управляется состоянием бита PORTB2.

OC1B, Output Compare Match output: вывод PB2 может работать аппаратный выходной сигнал, сообщающий о событии совпадения B таймера/счетчика 1 (Timer/Counter1 Compare Match B). Для обслуживания этой функции PB2 должен быть сконфигурирован как выход (DDB2=1). Вывод OC1B также работает как выходной сигнал работы таймера в режиме аппаратного ШИМ (PWM mode).

PCINT2: источник прерывания 2 по изменению уровня (Pin Change Interrupt source 2). Вывод порта PB2 может работать как вход внешнего источника прерывания.

OC1A/PCINT1 – Port B, Bit 1

OC1A, Output Compare Match output: вывод PB1 может работать аппаратный выходной сигнал, сообщающий о событии совпадения A таймера/счетчика 1 (Timer/Counter1 Compare Match A). Для обслуживания этой функции PB1 должен быть сконфигурирован как выход (DDB1=1). Вывод OC1A также работает как выходной сигнал работы таймера в режиме аппаратного ШИМ (PWM mode).

PCINT1: источник прерывания 1 по изменению уровня (Pin Change Interrupt source 1). Вывод порта PB1 может работать как вход внешнего источника прерывания.

ICP1/CLKO/PCINT0 – Port B, Bit 0

ICP1, Input Capture Pin: вывод PB0 может работать как вход захвата событий таймера/счетчика 1 (Input Capture Pin for Timer/Counter1).

CLKO, Divided System Clock: на вывод PB0 может быть выведена поделенная системная тактовая частота. Это произойдет, если запрограммирован фьюз CKOUT, независимо от установок бит PORTB0 и DDB0. Также вывод будет выходом во время сброса.

PCINT0: источник прерывания 0 по изменению уровня (Pin Change Interrupt source 0). Вывод порта PB0 может работать как вход внешнего источника прерывания.

В таблицах 14-4 и 14-5 показаны переназначающие сигналы, связанные с альтернативными функциями порта B (эти сигналы показаны на рисунке 14-5). SPI MSTR INPUT и SPI SLAVE OUTPUT составляют сигнал MISO, в то время как MOSI делится на SPI MSTR OUTPUT и SPI SLAVE INPUT.

Таблица 14-4. Переназначающие сигналы для альтернативных функций PB7. PB4.

Имя
сигнала
PB7/XTAL2/
TOSC2/PCINT7 (2)
PB6/XTAL1/
TOSC1/PCINT6 (2)
PB5/SCK/
PCINT5
PB4/MISO/
PCINT4
PUOE ~INTRC • ~EXTCK + AS2 ~INTRC + AS2 SPE • ~MSTR SPE • MSTR
PUOV 0 0 PORTB5 • ~PUD PORTB4 • ~PUD
DDOE ~INTRC • ~EXTCK + AS2 ~INTRC + AS2 SPE • ~MSTR SPE • MSTR
DDOV 0 0 0 0
PVOE 0 0 SPE • MSTR SPE • ~MSTR
PVOV 0 0 SCK OUTPUT SPI SLAVE OUTPUT
DIEOE ~INTRC • ~EXTCK + AS2 +
PCINT7 • PCIE0
~INTRC + AS2 +
PCINT6 • PCIE0
PCINT5 • PCIE0 PCINT4 • PCIE0
DIEOV (INTRC + EXTCK) • ~AS2 ~INTRC • AS2 1 1
DI PCINT7 INPUT PCINT6 INPUT PCINT5 INPUT
SCK INPUT
PCINT4 INPUT
SPI MSTR INPUT
AIO Oscillator Output Oscillator/Clock Input

Примечание (2): INTRC означает, что выбран один из внутренних RC-генераторов (выбирается фьюзами CKSEL), EXTCK означает, что выбрано внешнее тактирование (также выбирается фьюзами CKSEL).

Таблица 14-5. Переназначающие сигналы для альтернативных функций PB3. PB0.

Имя
сигнала
PB3/MOSI/
OC2/PCINT3
PB2/~SS/
OC1B/PCINT2
PB1/OC1A/
PCINT1
PB0/ICP1/
PCINT0
PUOE SPE • ~MSTR SPE • ~MSTR 0 0
PUOV PORTB3 • ~PUD PORTB2 • ~PUD 0 0
DDOE SPE • ~MSTR SPE • ~MSTR 0 0
DDOV 0 0 0 0
PVOE SPE • MSTR + OC2A ENABLE OC1B ENABLE OC1A ENABLE 0
PVOV SPI MSTR OUTPUT + OC2A OC1B OC1A 0
DIEOE PCINT3 • PCIE0 PCINT2 • PCIE0 PCINT1 • PCIE0 PCINT0 • PCIE0
DIEOV 1 1 1 1
DI PCINT3 INPUT
SPI SLAVE INPUT
PCINT2 INPUT
SPI ~SS
PCINT1 INPUT PCINT0 INPUT
ICP1 INPUT
AIO

Управление портами микроконтроллеров AVR на языке С (Си)

В этой статье будет рассмотрено управление портами микроконтроллеров AVR на языке программирования С (Си): установка выводов порта на вход или выход, считывание значений на входах портов, программа для управления миганием светодиода.

Общие сведения о портах микроконтроллеров AVR

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

Kонфигурирование каждой линии порта (задание направления передачи данных) может быть произведено программно в любой момент времени. Входные буферы портов построены по схеме триггера Шмитта. Для линий, сконфигурированных как входные, также имеется возможность подключения внутреннего подтягивающего резистора сопротивлением 35…120 кОм между входом и проводом питания. Kроме того, если вывод (вход) с подключенным внутренним подтягивающим резистором подключить к общему проводу, он может служить источником тока.

Обращение к портам производится через регистры ввода/вывода, причем под каждый порт в адресном пространстве ввода/вывода за-резервировано по 3 адреса. По этим адресам размещаются три регистра: регистр данных порта PORTx, регистр направления данных DDRx и регистр выводов порта PINx. Разряды этих регистров имеют названия: Px7…Px0 — для регистров PORTx, DDx7…DDx0 — для регистров DDRx и PINx7…PINx0 — для регистров PINx.

Действительные названия регистров (и их разрядов) получаются подстановкой названия порта вместо символа «x», соответственно для порта A ре¬гистры называются PORTA, DDRA, PINA, для порта B — PORTB, DDRB, PINB и т.д.

Следует заметить, что «регистры» PINx на самом деле регистрами не являются, по этим адресам осуществляется доступ к физическим значениям сигналов на выводах порта. Поэтому они доступны только для чтения, тогда как регистры PORTx и DDRx доступны и для чтения, и для записи.

Таким образом, запись в порт означает запись требуемого состояния для каждого вывода порта в соответствующий регистр данных порта PORTx. А чтение состояния порта выполняется чтением либо регистра данных порта PORTx, либо регистра выводов порта PINx. При чтении регистра выводов порта PINx происходит считывание логических уровней сигналов, присутствующих на выводах порта. А при чтении регистра данных порта PORTx происходит считывание данных, находящихся в регистре-защелке порта – это справедливо как для входных, так и для выходных контактов.

Любой порт микроконтроллера AVR можно сконфигурировать как вход или как выход. Для этой цели используется регистр DDRx. На вход или выход можно сконфигурировать сразу весь порт или только отдельный его вывод (контакт, пин).

Регистр DDRx определяет, является тот или иной вывод порта входом или выходом. Если некоторый разряд регистра DDRx содержит логическую единицу, то соответствующий вывод порта сконфигурирован как выход, в противном случае — как вход. Буква x в данном случае должна обозначать имя порта, с которым вы работаете. Таким образом, для порта A это будет регистр DDRA, для порта B — регистр DDRB и т. д.

Задание направления данных для всего порта

В программе для программирования микроконтроллеров AVR Atmel Studio на языке С можно задать направление передачи данных сразу для всего порта.

С помощью этой команды все выводы (контакты) порта B будут сконфигурированы как выходы.

0xff представляет собой шестнадцатиричное представление числа ff, а 0x является префиксом, указывающим на то, что число записано в шестнадцатиричное форме. В десятичном представлении число 0xff будет равно 255, а в двоичном – 11111111. То есть с помощью представленной команды во все биты регистра DDRB будут записаны логические единицы.

В языке Си для микроконтроллеров AVR для представления двоичных чисел применяется префикс 0b. Соответственно, представленную выше команду записи логических единиц во все биты регистра DDRB можно записать и с помощью двоичного вида числа 255:

Эта запись команды является более наглядной, но все таки правилом «хорошего тона» в программировании для микроконтроллеров считается использование шестнадцатиричного представления чисел.

Для того чтобы сконфигурировать все выводы (контакты) порта B как входы необходимо записать во все биты регистра DDRB логические нули. Это можно сделать с помощью следующей команды.

Но кроме рассмотренных «крайних» случаев (все единицы или все нули) в регистр DDRB можно записать и другие числа. Например:

0xb4 — шестнадцатиричное представление числа 180. В двоичном виде его можно записать как 10110100. То есть часть выводов (контактов) порта B будет сконфигурирована как выходы, а часть — как входы.

PB0 — 0 (вход)
PB1 — 0 (вход)
PB2 — 1 (выход)
PB3 — 0 (вход)
PB4 — 1 (выход)
PB5 — 1 (выход)
PB6 — 0 (вход)
PB7 — 1 (выход)

Установка 1 в произвольном бите регистра порта

Каждый бит регистров DDRx может быть установлен или сброшен отдельно. К примеру, если мы хотим сконфигурировать отдельно вывод PD2 как выход, то нам следует в соответствующий бит регистра DDRD записать 1. Для этой цели можно использовать следующую команду:

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

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

Установка 0 в произвольном бите регистра порта

Если мы хотим сконфигурировать отдельно вывод (контакт) PD2 как вход, то необходимо в соответствующий бит регистра DDRD записать 0. Для этого можно использовать следующую команду.

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

В результате операции инверсии мы получаем вместо нулей единицы, а вместо единиц — нули. Данная логическая операция также называется операцией НЕ (английское название NOT).

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

Логическое умножение, по другому называемое операцией И (английское название AND), выполняется по следующим правилам:
0*0=0
0*1=0
1*0=0
1*1=1

То есть если хотя бы один из операндов операции равен 0, то и результат операции равен 0.

Установка значений на выводах порта

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

PORTx — регистр порта, где x обозначает имя порта.

Если вывод (контакт) сконфигурирован как выход, то единица в соответствующем бите регистра PORTx формирует на выводе сигнал высокого уровня, а ноль — сигнал низкого уровня.

Если вывод (контакт) сконфигурирован как вход, то единица в бите регистра PORTx подключает к выводу внутренний подтягивающий pull-up резистор, который обеспечивает высокий уровень на входе при отсутствии внешнего сигнала.

Установить «1» на всех выводах (контактах) порта B можно c помощью следующей команды:

Аналогично установка «0» на всех выводах порта B выполняется следующим образом:

К каждому биту регистров PORTx можно обращаться и по отдельности так же, как и в рассмотренном выше случае с регистрами DDRx. К примеру, команда

установит «1» (сигнал высокого уровня) на контакте PB3.

установит «0» (сигнал низкого уровня) на контакте PB4.

В программе Atmel Studio сдвиг можно выполнять и с помощью функции _BV(), которая производит поразрядный сдвиг и помещает результат в компилируемый код.

В этом случае две предыдущие команды можно записать следующим образом:

PORTB |= _BV(PB3); // установить «1» на линии 3 порта B

PORTB &= ~_BV(PB4); // установить «0» на линии 4 порта B

Пример программы зажигания светодиода, подключенного к выводу микроконтроллера AVR

Для лучшего понимания работы с портами микроконтроллеров AVR рассмотрим примеры простейших программ, осуществляющих включение и выключение светодиода, подключенного к выводу порта.

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

В первом случае (рисунок слева) светодиод будет загораться от сигнала высокого уровня на выводе PD1, а во втором случае (рисунок справа) — от сигнала низкого уровня на этом же контакте.

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

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

#include // заголовок чтобы задействовать функции контроля данных на выводах микроконтроллера
int main(void) < // начало основной программы
DDRD = 0xff; // все выводы порта D сконфигурировать как выходы
PORTD |= 1 > // закрывающая скобка основной программы

#include // заголовок чтобы задействовать функции контроля данных на выводах микроконтроллера
int main(void) < // начало основной программы
DDRD = 0xff; // все выводы порта D сконфигурировать как выходы
PORTD &= ~_BV(PD1); // установить «0» (низкий уровень) на выводе PD1
> // закрывающая скобка основной программы

Теперь для случая, представленного на рисунке 1, попробуем мигнуть светодиодом. Для этой цели воспользуемся функцией задержки _delay_ms() .

Функция _delay_ms() формирует задержку в зависимости от передаваемого ей аргумента, выраженного в миллисекундах (в одной секунде 1000 миллисекунд). Максимальная задержка составляет 262.14 миллисекунд. Если передать функции значение более 262.14, то осуществится автоматическое уменьшение разрешения до 1/10 миллисекунды, что обеспечивает задержки до 6.5535 секунд. Более длительные задержки можно реализовать с помощью циклов в программе.

Функция _delay_ms() содержится в файле delay.h, поэтому его нужно будет подключить к основной программе. Также для работы этой функции необходимо задать значение тактовой частоты микроконтроллера в Герцах (Гц).

#define F_CPU 1000000UL // указываем тактовую частоту микроконтроллера в герцах
#include // заголовок чтобы задействовать функции контроля данных на выводах микроконтроллера
#include // заголовок чтобы задействовать функции задержки в программе

int main(void) < // начало основной программы
DDRD = 0xff; // все выводы порта D сконфигурировать как выходы

PORTD |= _BV(PD1); // установить «1» (высокий уровень) на выводе PD1,
//зажечь светодиод

_delay_ms(500); // ждем 0.5 сек.

PORTD &= ~_BV(PD1); // установить «0» (низкий уровень) на выводе PD1,
//погасить светодиод

_delay_ms(500); // ждем 0.5 сек.

PORTD |= _BV(PD1); // установить «1» (высокий уровень) на выводе PD1,
//зажечь светодиод

_delay_ms(500); // ждем 0.5 сек.

PORTD &= ~_BV(PD1); // установить «0» (низкий уровень) на выводе PD1,
//погасить светодиод

> // закрывающая скобка основной программы

В представленной программе светодиод мигнет всего 2 раза. Чтобы он мигал непрерывно можно организовать бесконечный цикл с помощью оператора безусловного перехода » goto «. Данный оператор выполняет переход к месту программы, обозначенному меткой. Но лучше это реализовать с помощью бесконечного цикла на основе оператора while – пример работающей схемы для этого случая и программы для нее можно посмотреть в этой статье на нашем сайте.

Комментарии

Управление портами микроконтроллеров AVR на языке С (Си) — 22 комментария

Gor говорит 28.08.2022 в 15:53 :

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

Gor говорит 28.08.2022 в 18:52 :

Наверное, нужно было сразу пояснить: я знаком с битовыми операциями, и знаю, как поменять состояние пина как вверх, так и вниз. Но перед этим потребуется определить текущее состояние пина, и только затем можно будет выполнить либо PORTB |= 1
admin-new говорит 31.08.2022 в 21:40 :

А почему для проверки текущего состояния пина нужно несколько операций? А команда по типу if (!bit_is_clear(PINA,5)) вам не подойдет. Примеры использования этой команды есть, к примеру, в этой статье.

Konter говорит 27.06.2022 в 17:34 :

Помогите пожалуйста.
У меня МК attiny13
Как мне задать направление пинам PB0 — 4
PB0 — вход
PB1 — вход
PB2 — выход
PB3 — выход
PB4 — выход Как я понимаю DDRB = 0x38 или DDRB = 0b00111000

admin-new говорит 29.06.2022 в 22:02 :

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

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

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