Как задать скорость работы UART

Сижу, пишу проект. В проекте используется UART. На этот раз в обычном асинхронном режиме…

Думаю, не так много людей, кто любит читать документацию. Я тоже читаю pdf-ки только тогда, когда «прижимает». А вот так просто взять в руки и почитать — не-е! (Я уверен — не только один я такой ленивый!) Поэтому не удивительно, что многие разработчики про микроконтроллеры, с которыми работают, хотя и знают очень много чего, но ведь не всё. Всегда остаются «белые пятна», которые так или иначе закрываются другими способами и приёмами в работе.

Вот и у меня примерно так же обстояли дела с вопросом определения коэффициентов для задания скорости работы UART.

Собственно, что там сложного-то! — Взял исходные значения тактовой частоты МК и частоты, на которой должен работать UART, подставил в формулу:

          Fтакт
K = ----------------- - 1
       16 * Fuart

и получил коэффициент K. Затем этот коэффициент нужно выразить 16-ричном виде, разбить его на старший и младший байты, и записать эти байты в регистры UBRRH и UBRRL.

Тактовую частоту микроконтроллера я обычно определяю в Makefile примерно так:

PROJECT = NiCd

MCU = atmega48
F_CPU = 7372800UL
...
CFLAGS += -DF_CPU=$(F_CPU)
...
%.o:%.c
        $(CC) $(CFLAGS) -c $<
...

Потом в программном модуле, где происходит инициализация UART, я делаю примерно следующее:

...
#define BAUD (115200)
...
void uart_init(void)
{
  // Настроим скорость
  // UBRR0 = F_CPU / (16 * BAUD) - 1 = 7372800 / (16 * 115200) - 1 = 3
  UBRR0H = 0;
  UBRR0L = F_CPU / 16UL / BAUD - 1;
  ...

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

При расчётах нужно быть очень внимательны, особенно в ситуациях с кварцевыми резонаторами, у которых частота не кратна частоте UART. Проблема затаилась вот где.

Допустим, у нас кварцевый резонатор имеет частоту 20 МГц, а UART мы хотим запустить на скорости 115200 Бод. Вычисляем коэффициент K:

          Fтакт               20000000
K = ----------------- - 1 = ------------- = 9.85069444
       16 * Fuart            16 * 115200

Округляем до 10 и находим реальную частоту, с которой будет работать UART:

                  Fтакт         20000000
Fuart_real = -------------- = ------------- = 113636.36
               16 * K + 1      16 * 10 + 1

Определяем отклонение:

     113636.36 - 115200
d = -------------------- * 100% = -1.3573%
          115200

Грубо говоря, скорость работы UART будет на полтора процента ниже. Ну и пусть! Мы все равно укладываемся в допуск ±2%. Так что всё будет работать.

Однако, если мы воспользуемся командой:

UBRR0 = F_CPU / (16 * BAUD) — 1;

и не проконтролируем её, то вместо К= 10, мы получим K = 9. Препроцессор, который вычисляет это константное выражение не округляет дробное число до ближайшего целого, а тупо откидывает у него дробную часть. То есть в регистр вместо числа 9.85 будет записано целое число 9.

Проверим, велика ли ошибка. Ещё раз повторяем расчёт:

                  Fтакт         20000000
Fuart_real = -------------- = ------------- = 125000.00
               16 * K + 1      16 * 9 + 1

Определяем отклонение:

     125000 - 115200
d = ----------------- * 100% = 8.50694%
         115200

Отклонение от заданной скорости составляет плюс 8.5% — ого! Это примерно в четыре раза больше допустимого отклонения. Понятно, что работать ничего не будет.

В общем, при таких расчётах, как никогда нужна внимательность. Хотя сложного в них ничего нет, методика работает не один год.

Но оказывается, что в мире уже давно существует одно средство-помогалка для определения этих коэффициентов. Помощь средства состоит в том, что оно само вычисляет коэффициенты и само определяет их допустимую погрешность. Собственно, средство — это всего-лишь хэдерный файл util/setbaud.h, который нужно подключить в файл исходника, где происходит инициализация UART.

Есть, правда, одна тонкость — нужно определить два макроса до точки подключения этого файла. Примерно вот так:

...
#define F_CPU  (7372800UL)
#define BAUD   (115200)
#include <util/setbaud.h>
...

А потом в функции инициализации UART мы должны написать следующее:

...
void uart_init(void)
{
  // Настроим скорость
  UBRR0H = UBRRH_VALUE;  // Загружаем сначала старший регистр
  UBRR0L = UBRRL_VALUE;  // а затем младший
  ...

И это всё!

Если F_CPU, определена в Makefile (как у меня), то повторно определять её в исходных текстах не надо.

Можно по аналогии вынести в Makefile также и определение для BAUD, но я в этом не вижу особого смысла. Пусть лучше это определение остаётся там, где оно используется.

А вот с загрузкой регистров UBRRH и UBRRL нужно помнить, что у одной части микроконтроллеров они расположены на в смежных адресах, а у другой части — они разнесены по далеко отстоящим друг от друга адресам.

Так вот, у тех МК, у которых адреса расположены рядом, можно поступить даже так:

...
void uart_init(void)
{
  // Настроим скорость
  UBRR0 = UBRR_VALUE;  // Загружаем сразу оба регистра
  ...

 

Следует заметить, что подключение файла util/setbaud.h не приведёт к увеличению кода программы. Там всё организовано на макросах, которые «работают» во время препроцессинга.

Вообще, в жизни не всегда всё бывает гладко. Иногда приходится брать кварцевые резонаторы с некратными для UART частотами. Ну, например, ровно 10 МГц, ровно 8 МГц или ровно 16 МГц. При таких кварцах коэффициенты для UBRRx получаются не целыми числами, а дробными. Но ведь регистры-то принимают целые числа. Поэтому приходится округлять. А всякое округление приводит к неточности.

Поэтому вполне может оказаться так, что заданная частота (на которой, вы хотите, чтобы заработал UART) будет установлена с очень большой погрешностью. Вообще, считается, что если вы установили частоту в пределах ±2%, то UART будет работать вполне надёжно.

Проблема, однако в том, что не всегда этого можно достичь. Например, при частоте кварцевого резонатора ровно 8 МГц, как бы вы не «крутили» коэффициенты, вам никак не удастся вписаться в 2%-ые границы допустимой погрешности, чтобы работать на скорости 115200 Бод. Посчитайте сами и прослезитесь!

Так вот, если вы не попадёте в границы допустимых отклонений, во время компиляции вы получите предупреждение:

# warning "Baud rate achived is lower than allowed"

Будьте внимательны! Не пропустите этот момент.

Но это ещё не всё!

Дело в том, что в семействах микроконтроллеров Tiny и Mega в регистре UCSRA имеется бит U2X, который позволяет удвоить скорость работы UART. (В старом семействе classic, которое имеет обозначение AT90Sxxx, этот бит отсутствует.) И если вдруг при расчете коэффициентов получится так, что достижение заданной скорости (при заданной 2%-ой погрешности) возможно только при установленном бите U2X, то его тоже нужно будет «поднять».

А для того, чтобы разработчик знал — нужно или нет устанавливать этот бит, существует макрос USE_2X. Он тоже «взводится» и обнуляется в процессе расчета автоматически. Нам только нужно не забыть его учесть при инициализации UART.

Таким образом, полная инициализация скорости UART выглядит следующим образом:

...
void uart_init(void)
{
  // Настроим скорость
  UBRR0H = UBRRH_VALUE;  // Загружаем сначала старший регистр
  UBRR0L = UBRRL_VALUE;  // а затем младший
  #if USE_2X
    UCSR0A |= _BV(U2X);
  #else
    UCSR0A &= ~_BV(U2X);
  #endif
  ...

 

В этой статье я упомянул только самые существенные моменты. Остальное вы и сами найдёте в документации.

Я вообще не ставил себе задачу научить вас правильно задавать частоту UART. Я хотел только напомнить вам (да и себе положить в заметки), что в природе существует файл, который может немножечко облегчить нашу программерскую участь.

Ну вот, пожалуй, это всё, что я хотел сказать.

Реклама

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

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s