AVR274: однопроводный, программный UART на tinyAVR и megaAVR
Добавил(а) microsin
Перевод апноута AVR274 [1], который описывает софтовую реализацию однопроводного последовательного порта (UART). Протокол поддерживает полудуплексный обмен данными между 2 устройствами (передача в разных направлениях может происходить поочередно). Для реализации требуется только поддержка внешнего прерывания (external interrupt) на порте ввода/вывода I/O, и прерывание сравнения таймера (timer compare interrupt).
[1. Основные возможности и особенности программного UART]
• UART реализован программно • Поддерживается полудуплексный обмен данными по одному проводу • Используются прерывания • Поддерживаются скорости передачи до 9600 на тактовой частоте ядра 1 МГц • Описанный программный UART совместим с любым микроконтроллером AVR®, который поддерживает внешнее прерывание (external interrupt, прерывание по изменению уровня на ножке GPIO) и имеет 8-битный таймер с поддержкой прерывания сравнения (timer compare interrupt).
Обмен данными с помощью физического протокола UART обычно осуществляется с использованием отдельных сигналов данных для приема и передачи (сигналы RX и TX). Однопроводный UART использует для обмена только одну сигнальную линию. Поэтому такой UART является идеальным для случаев, когда нужно реализовать недорогое решения для обмена данными, и не требуется высокая скорость и полнодуплексная передача. Этот апноут описывает софтовую (программную) реализацию однопроводного UART. Протокол поддерживает полудуплексный обмен между двумя устройствами AVR. Для реализации требуется только один вывод порта I/O, на котором поддерживается внешнее прерывание (external interrupt), и требуется прерывание сравнения таймера (timer compare interrupt).
[2. Теория: как это работает]
2.1. Фрейм (битовый кадр) UART
Протокол UART использует стандарт асинхронного последовательного обмена данными. Данные передаются последовательно, бит за битом, каждому биту отводится определенный отрезок времени. Эта реализация использует фрейм из 8 бит данных, 1 стартового бита и 2 стоповых бит, как это показано на рис. 2-1 (таким образом, каждый битовый кадр состоит из 11 элементарных битовых интервалов). Такой формат выбран потому, что на практике именно он чаще всего используется для передачи данных. Другие реализации могут использовать другие форматы фрейма, к примеру состоящие из 5..9 бит данных, 1 бита четности (parity, бит для определения наличия ошибки), и 1 стоповый бит. Конечно же, обе передающие стороны в обмене точка-точка должны быть настроены на один и тот же формат данных. Когда передачи нет, то сигнальная линия UART находится в состоянии лог. 1.
Рис. 2-1. Формат фрейма UART.
Рис. 2.2. Фрейм передачи последовательных данных для символа ASCII 'a' (код 0x61).
2.2. Передача данных
Передача инициализируется отправкой стартового бита, который всегда равен 0 (сигнальная линия притягивается к нижнему уровню). Приемник на другой стороне линии детектирует спад уровня на линии, и по этому событию может засинхронизироваться с передатчиком (синхронизация по времени требуется для корректного приема всех бит данных). Самый младший бит данных передается первым (см. рис. 2-2).
Выходы с открытым коллектором часто используются для такого управления линией данных, однако если сразу оба устройства на линии будут передавать в одно и то же время, то передатчик, передающий лог. 0, все равно будет притягивать линию к лог. 0, когда второй передатчик пытается передать бит 1. Чтобы обработать эту ситуацию, UART никогда не запускает передачу, когда он находится в режиме приема. Передатчик также проверяет сигнальную линию перед отправкой нового бита, чтобы убедиться, что её состояние не изменилось, когда передавался последний бит. Флаг ошибки устанавливается, если принят бит низкого уровня, когда последним передавался бит высокого уровня.
2.3. Прием данных
Прием запускается, когда был детектирован стартовый бит на линии. Затем состояние линии анализируется посередине каждого битового интервала фрейма, чтобы принять значение каждого бита информации. Таким образом, к примеру, первый бит данных выбирается после 1.5 периода бита после начала стартового бита. Эта реализация (для упрощения, и уменьшения загрузки ядра AVR) не использует тройную мажоритарную выборку для каждого бита (этот способ уточнения значения бита используется в большинстве аппаратно реализованных UART).
2.4. Битовая скорость передачи (Baud Rate)
Скорость обмена данными UART задается параметром baud rate. Его значение в этом случае эквивалентно количеству битовых интервалов, которые проходят за единицу времени. Так как во фрейме применяются стартовый и стоповые биты, то реальная скорость передачи данных получается несколько меньше (для нашего примера она равна 8/11 от значения baud rate). Приемник и передатчик должны быть настроены на использование одного и того же значения baud rate, иначе они не смогут засинхронизироваться по битам, и обмен данными не получится. Имеется стандартный ряд скоростей baud rate: 4800, 9600, 19200, 28800, 38400 и т. д.
2.5. События ошибки (Error Conditions)
В процессе передачи данных могут произойти ошибки разного вида. Если скорости baud rate слишком отличаются между двумя устройствами, то они будут рассинхронизированы. Поскольку baud rate зависит от таковой частоты ядра AVR, эта проблема может возникнуть, если реальная тактовая частота отличается от той, которая нужна. К примеру, если используется внутренний тактовый генератор (internal RC oscillator), то рекомендуется его откалибровать перед началом использования UART. Пожалуйста обратитесь к соответствующим апноутам, чтобы узнать, как калибровать внутренний RC-генератор.
Принятый байт данных сохраняется в однобайтовом буфере данных. Если принятые данные не были обработаны вовремя, до того как начался прием следующего байта, то буфер будет переполнен, и старые данные будут перезаписаны. Флаг переполнения будет установлен, если произойдет такая ошибка. В устранении такой ошибки может помочь снижение используемой скорости UART, либо увеличение размера буфера, тогда у основной программы будет больше времени, чтобы обработать принятые данные. Либо нужно вызывать процедуру проверки и обработки буфера чаще, с максимально возможной частотой.
Если шумовой импульс помех принудительно переведет линию сигнала в лог. 0, то AVR определит спад сигнала и начнет прием. Если детектировано начало стартового бита, но он оказался ненулевым, то AVR остановит прием и не сохранит никаких данных, но если импульс шума оказался длиннее нескольких циклов тактовой частоты, то будет принят и сохранен неправильный байт (который на самом деле никто не передавал).
Шум может также исказить фрейм и во время передачи. Так как состояние бита выбирается только один раз, то фрейм может быть принят с ошибкой, если импульс шума пришелся на момент считывания приемником состояния сигнальной линии. Для определения наличия таких ошибок может помочь добавление к фрейму UART бита четности (parity bit), при этом гарантированно могут быть зарегистрированы только одиночные ошибки фрейма.
Поскольку для работы UART используются прерывания, то основной код программы (код приложения, который выполняется в главном цикле main) может выполняться "параллельно", вместе с обменом UART. Имейте в виду, что поскольку AVR не поддерживает приоритетов прерываний, то другие активные источники прерываний могут повлиять на UART (к примеру, не будут точно вычисляться интервалы битов), и это может привести к ошибкам в обмене данными.
Рекомендуется использовать UART в обмене точка-точка, когда одно устройство является главным, другое подчиненным (так называемая конфигурация master/slave). Тогда slave будет передавать данные только тогда, когда их запросил master. Это предотвратит ситуации, когда оба устройства передают в одно и то же время. Если slave находится в состоянии ошибки, то он может сигнализировать об этом для master, если притянет сигнальную линию UART к лог. 0 на заданное время. Тогда будет установлен флаг ошибки master, и обмен будет продолжен только тогда, когда slave прекратит удерживать линию с состоянии лог. 0.
Прим. переводчика: схема обмена master/slave часто используется даже в физически полнодуплексных системах, когда для передачи и приема используются отдельные сигналы TX и RX. Это делается с целью упрощения логики протокола обмена, и позволяет проще синхронизировать высокоуровневое состояние обменивающихся данными устройств. В частности, принцип master/slave используется в последней версии протокола STK500, а также это основной принцип работы шины USB.
[3. Реализация]
Код, описываемый в этом апноуте, написан как драйвер, предназначенный для обмена данными через UART.
3.1. Установка скорости Baud Rate
Для генерации интервалов времени, используемых при выборке бит приема и при формировании интервалов передаваемых бит, используется прерывание сравнения содержимого таймера с предустановленным значением (timer compare interrupt). Таймер настраивается в режим очистки при совпадении сравнения, режим Clear Timer on Compare (CTC), и на это событие настраивается прерывание. Это прерывание срабатывает, когда содержимое счетчика таймера уравнивается с содержимым регистра сравнения (output compare register). Время между каждым таким прерыванием зависит от системной тактовой частоты ядра, настройки предделителя тактовой частоты таймера (коэффициента деления прескалера), и от значения, записанного в регистр сравнения (compare register), как это показано в Формуле 3-1. Установка compare value в значение 10 приведет к генерации длительности между прерываниями в 11 тиков. Настройки скорости UART (Baud rate) устанавливаются в хедере UART, файле single_wire_UART.h.
System Clock
Baud Rate = --------------------------------------------------
(One Period Compare Setting + 1) * Timer Prescaler
Формула 3-1. Вычисление скорости передачи (Baud Rate).
System Clock
One Period Compare Setting = --------------------------- - 1
Baud Rate * Timer Prescaler
Формула 3-2. Вычисление значения для регистра совпадения.
Пояснения к формулам: "System Clock" означает тактовую частоту системы в Гц. "One Period Compare Setting" - значение, записанное в регистр сравнения таймера (output compare register). "Timer Prescaler" - коэффициент деления прескалера. "One Period Compare Setting" - значение для регистра сравнения, которое определяет период срабатывания обработчика таймера, что задает длительность бита UART.
Таблица 3-1. Установка периода бита UART для частоты тактового генератора 1, 2, 4 и 8 МГц.
Baud Rate (bps)
1 МГц
2 МГц
OPS
PS
Error
OPS
PS
Error
4800
207
1
-0.16%
51
8
-0.16%
9600
103
1
-0.16%
207
1
-0.16%
19200
не реализовано
103
1
-0.16%
4 МГц
8 МГц
4800
103
8
-0.16%
207
8
-0.16%
9600
51
8
-0.16%
103
8
-0.16%
19200
207
1
-0.16%
51
8
-0.16%
28800
138
1
0.08%
34
8
0.82%
38400
103
1
-0.16%
207
1
-0.16%
Пояснения к таблице: OPS означает One Period Settings (One Period Compare Settings, значение регистра сравнения таймера, от которого зависит длительность между срабатываниями прерываний таймера и вызовами обработчика Timer_Compare_SW_UART_ISR), PS означает Prescaler Setting (коэффициент деления прескалера), Error означает погрешность полученной реальной скорости UART в процентах (по отношению к требуемой стандартной скорости).
В таблице 3-1 показаны установки таймера для некоторых стандартных скоростей передачи, а также получающаяся погрешность установленной скорости. Погрешность установки скорости скорости (Error) вычисляется по формуле 3-3.
Формула 3-3. Вычисление ошибки установки скорости UART.
Пояснения к формуле: Error[%] - погрешность установки скорости, насколько скорость отличается от стандартной. Baud RateClosest Match - скорость UART по стандарту. Baud Rate - полученная по формуле 3-1 реальная скорость.
Имейте в виду, что максимальная скорость UART, которую реально можно получить, зависит от тактовой частоты ядра AVR и от его загруженности другими вычислениями (кодом, который должен выполняться в других прерываниях и в главном цикле main). Если превысить эту скорость, то UART перестанет работать, и основная программа также практически перестанет выполнять свои функции. Так происходит потому, что выполнение обработчика прерывания таймера не успеет завершиться, как таймер снова досчитает до значения регистра сравнения, и произойдет новое прерывание. Максимально допустимое количество циклов, которое может быть выполнено в прерывании compare interrupt, составляет около 100..110, и зависит от настроек компилятора. На практике использование тактовой частоты ядра 1 МГц дает максимально возможную скорость UART около 10000 бит/сек (бод). При этой скорости все время выполнения CPU будет израсходовано только на обработку UART, и на остальные задачи практически циклов не останется. Основной код приложения (действия, которые прокручиваются в основном цикле main) должен иметь некоторое время на обработку принятых данных до того, как поступит новый битовый фрейм, иначе произойдет ошибка переполнения, и некоторые данные на приеме будут потеряны. Поэтому рекомендуется установить скорость обмена UART несколько ниже предельно допустимой для заданной частоты ядра, в зависимости от требований к коду основного приложения.
System Clock
Baud Rate < --------------------------------------------------
Maximum Cycles in Compare Interrupt
Формула 3-4. Максимально допустимая скорость обмена программного UART.
Пояснения к формуле: Baud Rate - полученная по формуле 3-1 реальная скорость. System Clock - тактовая частота, на которой работает AVR. Maximum Cycles in Compare Interrupt - максимальная длительность выполнения обработчика прерывания сравнения таймера в циклах ядра.
3.2. Аппаратура, на которой реализован UART (Hardware)
В этой реализации шина данных UART разработана в расчете на использование внешнего верхнего нагрузочного резистора (pull-up). Таким образом, должны использоваться ножки ввода вывода (I/O, GPIO) с открытым коллектором (открытым стоком). Дополнительно нужно использовать вешнее прерывания для детектирования начала приходящего битового фрейма.
Типичное значение нагрузочного резистора pull-up микроконтроллера AVR составляет 15..40k?. Когда отправляется бит с высоким логическим уровнем, или когда происходит прием, то порт AVR для данных UART находится в третьем состоянии (настроен как вход с отключенным внутренним нагрузочным резистором). Бит с низким логическим уровнем отправляется путем конфигурирования порта как выхода с выведенным в порт лог. 0.
Если требуется обмен данными по физическому каналу стандарта RS-232, то необходимо приведение уровня сигнала UART к значениям -12..-15V (соответствует лог. 1) и +12..+15V (соответствует лог. 0). Для этого нужна специальная схема, которая делает инверсию, и приводит уровни сигнала к нужным значениям. Например, можно использовать специальный чип интерфейса MAX232 компании Maxim. Он работает от одного напряжения питания 5V, и имеет встроенный преобразователь напряжения DC/DC, чтобы генерировать стандартные уровни RS-232.
Прим. переводчика: можно также использовать преобразователь с гальванической изоляцией на основе оптронов [2].
3.3. Регистр состояния (Status register)
Регистр состояния однопроводного UART содержит следующие 4 флага: SW_UART_TX_BUFFER_FULL, SW_UART_RX_BUFFER_FULL, SW_UART_RX_BUFFER_OVERFLOW, SW_UART_FRAME_ERROR.
SW_UART_TX_BUFFER_FULL устанавливается, если данные TX готовы к передаче. Этот флаг должен быть обнулен, когда вызывается функция SW_UART_transmit.
SW_UART_RX_BUFFER_FULL устанавливается, если в приемном буфере есть необработанные принятые данные. Этот флаг должен установиться в лог. 1, когда вызывается функция SW_UART_Receive.
SW_UART_RX_BUFFER_OVERFLOW устанавливается, когда приходящие данные потеряны из-за переполнения буфера приема (новый прием начался, но ранее принятые данные не были обработаны).
SW_UART_FRAME_ERROR устанавливается, если неожиданно обнаружена лог. 1 в стартовом бите (на самом деле стартовый бит должен быть всегда нулевым), или если обнаружен лог. 0 в стоповом бите (стоповый бит должен быть всегда в лог. 1). Также устанавливается, когда при передаче считанный бит отличается от того, что был передан.
Чтобы уменьшить размер кода, регистр состояния может быть помещен в регистр GPIO, если это доступно (это нельзя сделать для ATmega32).
3.4. Счетчик UART
Переменная счетчика используется в коде драйвера UART, чтобы управлять состоянием, и какой бит принимается/передается через UART. UART переходит в состояние ожидания, когда значение счетчика равно 0. Значение счетчика является четным при передаче и нечетным при приеме, как показано на рис. 3-1.
Рис. 3-1. Значения счетчика UART.
3.5. Функции UART
Драйвер состоит из трех глобальных функций (описание функций см. далее):
SW_UART_status является глобальной переменной, которая удерживает флаги состояния UART (см. раздел 3.3). Макросы SET_FLAG, CLEAR_FLAG и READ_FLAG, определенные в хедере single_wire_UART.h, можно использовать для получения доступа к флагам.
В реализации UART используются также следующие обработчики прерывания:
Перед тем, как данные могут быть приняты или переданы через UART, он должен быть разрешен вызовом функции SW_UART_Enable. Он переводит ножку UART в состояние ожидания (при этом на выходе получается высокий уровень благодаря внешнему pull-up резистору). Регистр состояния и счетчик очищаются, так что исходящие передачи, если они работали, будут остановлены. Запрет внешнего прерывания и прерывания таймера остановят работу UART.
Рис. 3-2. Алгоритм работы функции SW_UART_Enable().
Функция SW_UART_Transmit() получает 1 байт в качестве параметра и добавляет этот байт в буфер передачи. Когда вызывается эта функция, то флаг SW_UART_TX_BUFFER_FULL должен быть очищен (его должен очищать внешний код передачи байт), иначе зарегистрируется потеря данных. Если передача данных не происходит, когда вызывается эта функция, то запустится новая передача путем отправки стартового нулевого бита и разрешения работы прерывания таймера.
Рис. 3-3. Алгоритм работы функции SW_UART_Transmit().
Эта функция возвратит принятый байт в переменной Rx_data. Перед вызовом этой функции должен быть проверен флаг UART_RX_BUFFER_FULL, чтобы убедиться, что в буфере Rx_data есть верные данные. При приеме нескольких байт, следующих друг за другом, очень важно вызвать эту функцию до приема следующего байта, иначе буфер приема будет переполнен и данные потеряются.
Рис. 3-4. Алгоритм работы функции SW_UART_Receive().
Обработчик внешнего прерывания External_SW_UART_ISR() будет вызван при детектировании спада уровня сигнала данных UART, когда нет выполняющихся операций передачи или приема. Обработчик проверяет, что ножка вывода данных UART находится в лог. 0. Если это не так, то прием не запускается. После начала приема фрейма запрещаются дальнейшие внешние прерывания, так что последующие спады уровня не вызовут повторного срабатывания внешнего прерывания с новым вызовом обработчика External_SW_UART_ISR. Таким образом, External_SW_UART_ISR используется только для детектирования стартового бита фрейма.
Рис. 3-5. Алгоритм работы обработчика прерывания External_SW_UART_ISR().
Обработчик прерывания по совпадению таймера Timer_Compare_SW_UART_ISR() (моменты срабатывания таймера показаны стрелочками на рис. 3-1) управляет обработкой передачи и приема битового фрейма. Он вызывается автоматически, когда значение регистра сравнения (output compare) уравнивается со значением аппаратного счетчика таймера, и когда разрешено прерывание таймера. На передаче счетчик UART начинает считать с четного значения (2, 4, ...), а на приеме с нечетного значения (1, 3, ... Не путайте счетчик UART со аппаратным счетчиком таймера). Обработчики приема (рис. 3-8) и передачи (рис. 3-7) реализованы напрямую в обработчике прерывания таймера. Пожалуйста обратитесь к рис. 3-1 для подробностей по поводу разных значений счетчика.
Рис. 3-6. Алгоритм работы обработчика прерывания Timer_Compare_SW_UART_ISR().
Рис. 3-7. Обработчик передачи UART_Transmit_Handler().
Рис. 3-8. Обработчик приема UART Receive Handler().
3.6. Программа примера
Модуль main.c содержит код программы примера для тестирования софтового однопроводного UART. Он принимает данные в массив байт, и передает эти данные обратно, когда массив заполнится, или когда будет принят символ возврата каретки '\n'.
3.7. Размер кода
При компиляции с использованием IAR® EWAVR 4.21A, с включенной оптимизацией по скорости по максимуму, размер кода драйвера UART занимает около 500 байт.
[4. Указания по быстрому старту]
Исходный код может быть загружен в архиве по ссылке [3] или с сайта компании Atmel. Код написан для микроконтроллера ATmega32 и скомпилирован под управлением среды разработки IAR EWAVR 4.20A. Для компилирования исходного кода примера, когда не сделано никаких изменений, требуется компилятор IAR EWAVR. В файле readme.html имеется документация Doxygen.