Руководство · Начальный · 28 min
8051 GPIO и подключение к портам: сток, а не источник
Вывод 8051 пропускает до десяти миллиампер, но почти не подаёт ток. Разбираем обратное подключение светодиода, запись 1 перед чтением и нагрузку на порт.
Введение
Первая программа для 8051, которую пишет почти каждый, мигает светодиодом (LED), и удивительно много таких программ не мигают. Схема подключения выглядит правильной, код выглядит правильным, а на выводе уверенно присутствует 5 V, не делая ничего полезного.
Причина почти всегда одна и та же, и она архитектурная, а не связана с ошибкой: вывод 8051 хорошо работает как сток и плохо как источник тока. Всё остальное в этом руководстве следует из этой асимметрии: почему светодиод подключается противоположно Arduino, почему перед чтением переключателя нужна запись и почему порт не может управлять восемью нагрузками одновременно.
Порт — это просто байт
API для GPIO нет. Четыре порта являются специальными функциональными регистрами с фиксированными адресами, и запись в них изменяет состояние выводов:
#include <reg52.h>
void main(void) {
P1 = 0x00; // all eight P1 pins low
P1 = 0xFF; // all eight high
P1 = 0b10101010; // alternating
}
Отдельные биты получают имена с помощью sbit; именно такую форму вы увидите почти во всех учебных материалах:
sbit LED = P1^0; // Keil C51 syntax
sbit BUTTON = P1^1;
LED = 0; // one instruction — the 8051 has real bit operations
Символ ^ здесь означает не исключающее ИЛИ. Это обозначение Keil для «бита 0 регистра P1». В SDCC эквивалент из файла <8051.h> имеет имя P1_0, а остальной код остаётся без изменений.
Из чего состоит вывод
Каждый вывод порта имеет слабую внутреннюю подтяжку к источнику питания и мощный нижний транзистор, стягивающий ток на землю, причём защёлка порта управляет затвором транзистора. Эти два элемента совершенно различаются по силе.
Запишите 0, и транзистор включится, удерживая вывод около земли и уверенно поглощая ток извне. Запишите 1, и транзистор выключится, оставляя лишь слабую подтяжку — десятки микроампер, — чтобы удержать линию на высоком уровне. Этого достаточно, чтобы передать логическую 1 на вход CMOS, но совершенно недостаточно, чтобы что-либо зажечь.
Такое решение называют квазидвунаправленным портом: один и тот же вывод является и выходом, и входом, причём регистр направления настраивать не нужно, потому что вывод, удерживающий 1, уже отпущен и может управляться внешним источником.
Поэтому светодиод подключается наоборот
В Arduino светодиод подключают от вывода через резистор к земле и подают на вывод высокий уровень. Сделайте то же самое с 8051 — светодиод будет светиться в лучшем случае тускло, потому что 60 µA не являются источником света.
Подключение 8051 обратное: анод через резистор к +5 V, катод к выводу. Вывод пропускает ток, и 0 включает светодиод включённым.
sbit LED = P1^0;
LED = 0; // ON — the pin sinks current from the supply
LED = 1; // OFF — the pin releases the line
Выберите сопротивление резистора для нужного тока, учитывая, что сам вывод при стоке падает примерно на полвольта:
R = (Vsupply − Vf(LED) − Vpin) / I
red LED at 5 mA : (5 − 2.0 − 0.5) / 0.005 = 500 Ω → use 470 Ω
red LED at 10 mA: (5 − 2.0 − 0.5) / 0.010 = 250 Ω → use 270 Ω
Бюджет тока порта, о котором не упоминают
Ограничения для каждого вывода — только половина истории. В техническом описании AT89S52 указаны оба, и второе ограничение значительно строже:
| Предел | Значение |
|---|---|
| Максимальный стоковый ток на один вывод | 10 mA |
| Максимальный суммарный ток для всего порта 1, 2 или 3 | 15 mA |
| Максимальный суммарный ток для всего порта 0 | 26 mA |
| Максимальный суммарный ток для всего кристалла | 71 mA |
Прочитайте среднюю строку ещё раз. Один вывод может пропускать 10 mA, но весь порт 1 при всех восьми выводах может пропускать только 15 mA. Классическое упражнение — подключить восемь светодиодов к порту и включить бегущую последовательность — работает, если горит только один светодиод. Если включить все восемь по 10 mA, вы запрашиваете у порта, рассчитанного на 15, ток 80 mA.
Микросхемы с превышением пределов редко взрываются — обычно у них проседает напряжение. Логические уровни смещаются, кристалл нагревается, а поведение становится прерывистым, как будто причина в программном обеспечении. Если нужны восемь одновременно и ярко горящих светодиодов, нужен драйвер: массив Дарлингтона ULN2803 пропускает до половины ампера на канал, а 8051 только сообщает ему, что делать.
При собственном пределе порта восемь одновременно включённых светодиодов получают примерно по 1.9 mA. Современный высокоэффективный светодиод при этом вполне виден, поэтому бегущая последовательность или шкала подходят; просто выполните расчёт, прежде чем считать яркость достаточной.
Чтение входа: сначала запишите 1
Поскольку один и тот же транзистор работает в обоих направлениях, вывод 8051 можно читать как вход только тогда, когда его защёлка удерживает 1. Если защёлка удерживает 0, транзистор стягивает вывод к земле, и любое чтение возвращает 0 независимо от того, что подключено.
sbit BUTTON = P1^1;
void main(void) {
BUTTON = 1; // release the pin — REQUIRED before reading
while (1) {
if (BUTTON == 0) { // switch wired to ground: pressed reads 0
// handle press
}
}
}
Пропуск этой первой строки — самая распространённая ошибка при работе с входами 8051. На микросхеме с регистром направления допустить такую ошибку нельзя, поэтому она особенно часто встречается у тех, кто приходит из AVR или PIC.
Переключатели подключают к земле, а не к источнику питания, по той же причине, по которой подключение светодиодов обратное: слабая подтяжка задаёт состояние покоя, а задача переключателя — пересилить её, замыкая вывод на землю.
Порт 0 — исключение
У порта 0 вообще нет внутренней подтяжки. Он работает с открытым стоком, потому что одновременно служит мультиплексированной шиной адреса и данных для внешней памяти. В режиме обычного ввода-вывода он может стянуть вывод к земле, а в остальных случаях вывод только плавает.
Добавьте внешнюю подтяжку к каждому используемому выводу P0 — 10 kΩ к +5 V или матрицу резисторов, если нужны все восемь. Без них выходы P0 никогда не достигнут высокого уровня, а входы P0 будут считывать шум. Порты 1, 2 и 3 ничего не требуют.
Устранение дребезга, потому что переключатель — не идеальный ключ
Механический контакт дребезжит в течение нескольких миллисекунд, а цикл опроса 8051 достаточно быстр, чтобы увидеть каждый дребезг как отдельное нажатие. Простое решение — подтвердить показание после задержки:
sbit BUTTON = P1^1;
bit pressed(void) {
if (BUTTON != 0) return 0; // not pressed
delay_ms(20); // let the contact settle
return (BUTTON == 0); // still down? real press
}
Двадцать миллисекунд с большим запасом перекрывают дребезг тактильного переключателя и значительно меньше времени, которое заметил бы человек. На этапе обучения допустима блокирующая задержка; когда запущено прерывание таймера, опрашивайте переключатель из него, чтобы задержка не останавливала всё остальное.
Когда всё идёт не так
- Светодиод светится очень слабо. Он подключён по схеме Arduino, от вывода к земле. Переверните подключение: анод через резистор к +5 V, катод к выводу и подайте на вывод низкий уровень.
- Вход всегда читает 0. Защёлка вывода удерживает 0. Запишите в неё 1 перед чтением.
- Порт 0 ничего не делает. Нет внешних подтягивающих резисторов. Добавьте 10 kΩ к +5 V на каждом используемом выводе P0.
- Одно нажатие засчитывается трижды. Это дребезг контакта. Подтвердите показание через ~20 ms.
- Всё работает, пока одновременно не включено несколько выходов. Вы превысили бюджет тока порта 15 mA. Перенесите нагрузку на ULN2803 или уменьшите ток каждого светодиода.
- Выводы ведут себя странно только при работе последовательного порта. Вы используете P3.0 или P3.1, то есть RXD и TXD. Альтернативные функции порта 3 имеют приоритет над вашими планами.
Исследуйте граф
Входит в эти сборки
Проекты и учебные пути, в которые входит это руководство.
Дополнительные материалы