
UART (Universal Asynchronous Receiver/Transmitter) – один из самых распространённых интерфейсов для обмена данными между микроконтроллерами и периферийными устройствами. В Keil C51 работа с UART реализуется через регистры SBUF, SCON и PCON, а также прерывания ES и TI/RI. Для 8051-совместимых контроллеров стандартная скорость передачи задаётся таймером Timer 1 в режиме автоперезагрузки (режим 2), где формула расчёта значения регистра TH1 выглядит так:
TH1 = 256 — (Fosc / (12 × 32 × BaudRate))
Например, при тактовой частоте 11.0592 МГц и скорости 9600 бод значение TH1 составит 0xFD. Неправильный подбор этого параметра приводит к искажению данных или полному отсутствию связи. Для отладки рекомендуется использовать логический анализатор или терминал (например, PuTTY или Tera Term) с настройками 8N1 (8 бит данных, без контроля чётности, 1 стоп-бит).
В Keil C51 инициализация UART включает настройку таймера, разрешение прерываний и установку режима работы порта. Пример базовой конфигурации:
SCON = 0x50; // Режим 1 (8-битный UART), разрешение приёма
TMOD |= 0x20; // Timer 1 в режиме 2 (8-битный автоперезагруз)
TH1 = 0xFD; // Значение для 9600 бод при 11.0592 МГц
TR1 = 1; // Запуск таймера
ES = 1; // Разрешение прерывания UART
EA = 1; // Глобальное разрешение прерываний
Для передачи данных используется регистр SBUF, запись в который автоматически запускает отправку байта. Приём осуществляется через прерывание RI, где принятый байт считывается из SBUF. Важно учитывать, что флаг TI (передача завершена) и RI (приём завершён) необходимо сбрасывать программно в обработчике прерывания. Игнорирование этого правила приводит к блокировке дальнейшего обмена.
При работе с большими объёмами данных рекомендуется использовать кольцевые буферы для приёма/передачи. Это позволяет избежать потерь данных при высокой скорости обмена. Пример реализации кольцевого буфера на 16 байт:
unsigned char rx_buffer[16];
unsigned char rx_head = 0, rx_tail = 0;
В обработчике прерывания RI данные помещаются в буфер, а основная программа периодически проверяет наличие новых данных. Такой подход минимизирует задержки и повышает надёжность системы.
Передача данных по UART в Keil C51: пример кода
Для инициализации UART на микроконтроллерах семейства 8051 в Keil C51 используйте регистры SCON и TMOD. Установите режим работы UART (например, режим 1 с 8-битной передачей и переменной скоростью) через SCON: SCON = 0x50;. Для задания скорости обмена данными настройте таймер 1 в режим автоперезагрузки (TMOD = 0x20) и загрузите в TH1 значение, соответствующее требуемой скорости. Например, для 9600 бод при частоте кварца 11.0592 МГц используйте TH1 = 0xFD;. Не забудьте разрешить прерывания UART, если планируете асинхронный прием: ES = 1; EA = 1;.
Пример функции передачи байта по UART в синхронном режиме без прерываний:
void UART_SendByte(unsigned char dat) {
SBUF = dat;
while (!TI);
TI = 0;
}
Здесь TI – флаг завершения передачи, который необходимо сбрасывать вручную. Для передачи строки используйте цикл, вызывающий эту функцию для каждого символа. Учтите, что при работе с высокими скоростями (например, 115200 бод) может потребоваться оптимизация задержек или переход на прерывания.
Для приема данных в режиме прерываний реализуйте обработчик прерывания UART:
void UART_ISR(void) interrupt 4 {
if (RI) {
unsigned char received = SBUF;
RI = 0;
// Обработка принятого байта
}
}
RI – флаг приема, который также требует ручного сброса. Храните принятые данные в кольцевом буфере, чтобы избежать потерь при высокой скорости обмена. Размер буфера выбирайте исходя из максимальной длины ожидаемого сообщения и частоты опроса.
При отладке UART в Keil C51 используйте встроенный симулятор или аппаратный анализатор логических уровней. Проверяйте соответствие скоростей на обоих концах линии, корректность подключения (TX-RX, RX-TX) и наличие согласующих резисторов (обычно 1–10 кОм) при длинных линиях. Для тестирования передачи отправляйте фиксированные последовательности (например, 0x55 или 0xAA) и анализируйте сигнал на осциллографе – это позволит выявить искажения фронтов или неверную скважность.
Настройка регистров UART в микроконтроллере 8051 для работы на заданной скорости

Основные шаги настройки:
- Задайте режим таймера 1:
TMOD = (TMOD & 0x0F) | 0x20(режим 2, таймер 1). - Установите скорость:
TH1 = 0xFD(для 9600 бод при 11.0592 МГц). - Настройте UART:
SCON = 0x50(режим 1,REN=1). - Запустите таймер:
TR1 = 1. - Для других частот используйте формулу:
TH1 = 256 - (Fosc / (12 * 32 * Baud)), гдеFosc– тактовая частота,Baud– требуемая скорость.
Инициализация прерываний UART для приёма и передачи данных в Keil C51
Для работы с UART в режиме прерываний в Keil C51 необходимо настроить регистры SCON, TMOD и IE. Установите SCON в режим 1 (8-битный UART с переменной скоростью) и активируйте приёмник (REN=1). Таймер 1 (TMOD=0x20) используйте в режиме автоперезагрузки для генерации тактовой частоты UART. Скорость задаётся значением TH1: для 9600 бод при 11.0592 МГц установите TH1=0xFD. Разрешите прерывания UART через IE (EA=1, ES=1). Обработчик прерывания должен проверять флаг RI (приём) или TI (передача) в SCON и сбрасывать его вручную после обработки.
Вектор прерывания UART в Keil C51 располагается по адресу 0x0023. Пример обработчика: void UART_ISR(void) interrupt 4 { if (RI) { buffer = SBUF; RI = 0; } if (TI) { TI = 0; } }. Для корректной работы избегайте длительных операций в обработчике – используйте буферы FIFO или флаги для передачи данных в основной цикл программы. При передаче данных через прерывания инициализируйте TI=1 перед первой отправкой байта, чтобы запустить механизм прерываний.
Реализация функции отправки одного байта по UART с проверкой готовности передатчика

Для микроконтроллеров семейства 8051 проверка готовности передатчика UART реализуется через флаг TI (Transmit Interrupt) в регистре SCON. Перед отправкой байта необходимо убедиться, что предыдущая передача завершена: флаг TI устанавливается аппаратно после освобождения сдвигового регистра передатчика. Пример функции на C51:
void UART_SendByte(unsigned char data) {
while (!TI); // Ожидание установки флага TI (готовность передатчика)
TI = 0; // Сброс флага перед новой передачей
SBUF = data; // Запись байта в буфер передачи
}
Критические моменты: сброс флага TI должен выполняться программно, так как аппаратный сброс не предусмотрен. Для повышения надежности добавьте тайм-аут ожидания флага, например, через счетчик циклов или таймер, чтобы избежать зависания при аппаратных сбоях. На частоте 11.0592 МГц стандартная скорость 9600 бод обеспечивается значением TH1 = 0xFD в режиме 2 таймера 1.
Организация буфера FIFO для последовательной передачи строки через UART

Буфер FIFO (First-In-First-Out) для UART в микроконтроллерах семейства 8051 решает проблему асинхронной передачи данных без потерь при высокой загрузке шины. Типичный размер буфера – 16–256 байт, выбирается исходя из частоты тактирования и скорости передачи. Например, при скорости 115200 бод и тактовой частоте 11.0592 МГц, задержка между байтами составляет ~86 мкс, что требует буфера не менее 32 байт для компенсации задержек обработки прерываний.
Реализация кольцевого FIFO требует двух указателей: head (запись) и tail (чтение). Указатели инкрементируются по модулю размера буфера, например, для 64-байтного буфера: head = (head + 1) & 0x3F. Критическая секция защищается запретом прерываний на время модификации указателей, чтобы избежать состояния гонки при одновременном доступе из основного цикла и обработчика прерывания.
Пример структуры буфера для Keil C51:
| Поле | Тип | Назначение |
|---|---|---|
buffer[64] |
unsigned char |
Хранилище данных |
head |
volatile unsigned char |
Указатель записи |
tail |
volatile unsigned char |
Указатель чтения |
count |
volatile unsigned char |
Текущее количество байт в буфере |
Функция записи в буфер должна проверять переполнение: if (count < BUFFER_SIZE). При успешной записи инкрементируется head и count. Чтение из буфера выполняется в обработчике прерывания TI (Transmit Interrupt), где байт извлекается по указателю tail, после чего tail и count обновляются. Если буфер пуст, флаг TI сбрасывается вручную.
Для передачи строки через FIFO используется функция-обёртка, например:
void UART_SendString(const char *str) {
while (*str) {
while (fifo.count >= BUFFER_SIZE); // Ожидание освобождения места
EA = 0; // Запрет прерываний
fifo.buffer[fifo.head] = *str++;
fifo.head = (fifo.head + 1) & (BUFFER_SIZE - 1);
fifo.count++;
EA = 1; // Разрешение прерываний
if (!TI) TI = 1; // Запуск передачи, если не активна
}
}
Оптимизация производительности достигается за счёт использования аппаратного буфера UART (1 байт в 8051) как промежуточного звена. При этом FIFO заполняется только при опустошении аппаратного буфера, что снижает нагрузку на процессор. В обработчике прерывания TI проверяется наличие данных в FIFO: если fifo.count > 0, следующий байт передаётся в SBUF, иначе флаг TI остаётся сброшенным.
Тестирование буфера проводится с помощью генерации потока данных с переменной скоростью. Например, при скорости 9600 бод и буфере 32 байта, максимальная задержка между байтами не должна превышать 3.3 мс. Для проверки используются осциллограф или логический анализатор, подключённый к линии TX. При переполнении буфера рекомендуется реализовать механизм уведомления (например, установка флага ошибки) или динамическое расширение буфера до 128 байт при необходимости.
Обработка прерывания приёма данных по UART с сохранением в кольцевой буфер

Кольцевой буфер (circular buffer) – оптимальное решение для асинхронного приёма данных по UART в микроконтроллерах 8051, где критически важна скорость и предсказуемость обработки. Типичный размер буфера – 32 или 64 байта, что достаточно для большинства задач при скорости передачи до 115200 бод. Структура буфера включает два указателя: head (позиция записи) и tail (позиция чтения), а также массив данных фиксированной длины. При переполнении буфера старые данные перезаписываются, что требует контроля со стороны основной программы.
Пример реализации прерывания для UART в Keil C51:
- Инициализируйте буфер и указатели в глобальной области:
#define BUFFER_SIZE 64 volatile unsigned char rx_buffer[BUFFER_SIZE]; volatile unsigned char rx_head = 0, rx_tail = 0; - В обработчике прерывания
void UART_ISR(void) interrupt 4используйте SBUF для чтения байта и записывайте его в буфер:if (RI) { RI = 0; rx_buffer[rx_head] = SBUF; rx_head = (rx_head + 1) % BUFFER_SIZE; if (rx_head == rx_tail) { rx_tail = (rx_tail + 1) % BUFFER_SIZE; // Переполнение } } - Для проверки переполнения сравнивайте
rx_headиrx_tailперед записью. Если они равны – буфер полон.
Основная программа должна периодически извлекать данные из буфера, например, в цикле while(1). Для этого реализуйте функцию чтения, которая возвращает байт и сдвигает rx_tail:
unsigned char uart_read(void) {
if (rx_head == rx_tail) return 0xFF; // Буфер пуст
unsigned char data = rx_buffer[rx_tail];
rx_tail = (rx_tail + 1) % BUFFER_SIZE;
return data;
}
Приоритет обработки прерывания должен быть выше, чем у других задач, чтобы избежать потери данных. В Keil C51 установите приоритет прерывания UART через регистр IP (например, PS = 1;).
