Что такое двойная буферизация

Что такое двойная буферизация

UART – отличный протокол передачи как для любительских, так и профессиональных проектов, но в критически важных системах UART может быть коварен.

UART (Universal Asynchronous Reception Transmission, универсальная асинхронная приемопередача) – популярный протокол для микроконтроллеров для взаимодействия с другими микроконтроллерами и компьютерами. Проекты с низкими скоростями передачи данных, использующие высокоскоростные микроконтроллеры, обычно не имеют проблем с UART. Однако на более высоких скоростях, или если микроконтроллер выполняет множество задач, могут возникнуть серьезные проблемы, включая пропущенные байты и порядок этих байтов. Даже в системе, управляемой прерываниями, бывает очень трудно сохранить порядок байтов. В данной статье будет рассмотрен метод, разработанный для решения этого типа проблем, называемый двойной буферизацией UART.

Примечание: технический жаргон (push (проталкивание) и pop (выталкивание))

Для тех, кто не знаком со стеками, проталкивание (push) данных означает, что данные помещаются в буфер, а выталкивание (pop) – удаление данных из буфера.

Объяснение проблемы

Представьте себе обработчик прерывания, который выполняет одну задачу: после получения байта по UART он сохраняет этот байт в массив буфера и увеличивает счетчик количества байтов totalBytes .

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

Пока основная программа берет байты из буфера быстрее, чем они отправляются, порядок байтов будет сохраняться. Однако, если программа не может быстро вывести байты, и байты добавляются в середине этого цикла (помните, что прерывание имеет больший приоритет над основным циклом), тогда порядок байтов будет потерян. Но что такое «порядок байтов»?

Порядок байтов

Порядок байтов можно рассматривать как временную шкалу, на которой байты упорядочены хронологически. Первый полученный байт должен быть первой частью данных, подлежащих обработке, а последний полученный байт должен быть последним в обработке. Таким образом, в этом примере, если устройство отправляет через UART « Hello », и наша основная программа достаточно быстра, вывод cout (при условии, что у нас есть дисплей) также должен быть « Hello », а не « elHlo » или какую-либо другую комбинацию.

Порядок байтов

Итак, рассмотрев, что такое порядок байтов, давайте теперь посмотрим, как этот «порядок» теряется, если основная программа не может выводить данные из буфера быстрее, чем ISR (обработчик прерывания) помещает их туда. Для примера предположим, что за время, затрачиваемое нашей программой на получение одного байта из буфера, обработчик прерывания поместит туда два байта, переданные через UART. Как будет выглядеть вывод cout ? На выходе будет выведено « elolH ». Как это произошло?

  • UART быстро отправляет первые два байта, « He »;
  • основная программа берет один байт, расположенный в конце, « e »;
  • к этому времени UART отправил еще два байта, « ll »;
  • основная программа снова берет последний байт, « l »;
  • UART отправляет последний байт, « o »;
  • основная программа выводит данные из массива, начиная с конца к началу, « olH »;
  • в результате получаем « elolH ».

Мало того, что данные потеряли свой порядок, но он даже не стал обратным! Чтение байтов в обратном порядке НЕ решает проблему. Даже если вы читаете от первого элемента до последнего, вы не можете установить значение totalBytes , потому что ISR может остановить программу непосредственно перед изменением значения, поместить байт в конец массива, и затем, по возвращении, основная программа может сбросить значение totalBytes (тем самым потеряв этот отправленный байт). Если программа не изменяет значение totalBytes из-за потенциальной проблемы с вмешательством обработчика прерывания, буфер может переполниться.

Читайте также:  Что значит смотрите по посылкам

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

Двойной буфер

Двойной буфер можно рассматривать как два полностью отдельных блока (Unit), где обработчик прерывания работает с одним блоком, а программа – с другим. Для моделирования обработчик прерывания будет называться «Kernel» (ядро), а функции и программы, которые не выполняют обработку прерывания, будут называться «User» (пользователь) (они используют данные, а ядро Kernel обрабатывает аппаратное обеспечение).

Двойной буфер Структура блока Unit

Каждый блок Unit имеет две переменные: массив buffer[] и счетчик bufferCounter. Буфер сохраняет данные UART по мере их поступления, а bufferCount содержит количество переданных данных. Этот счетчик можно использовать двумя способами:

  1. определять количество данных в буфере;
  2. определять куда вставлять / откуда извлекать данные буфера.

Примечание: самый простой способ программирования блоков Unit и мультиплексора – использование многомерного буфера и многомерного буфера счетчика.

Селекторы блоков Unit для User и Kernel выполняются с помощью двух переменных uartKernel и uartUser . Каждое из этих значений всегда противоположно другому. Ниже приведена таблица истинности для этих значений:

uartUser uartKernel
1
1

Мультиплексор решает, какой блок Unit будет направлен на ядро Kernel, а какой – на пользователя User (мультиплексор может находиться только в двух состояниях).

  • состояние A приводит к тому, что Kernel использует Unit A, а User использует Unit B;
  • состояние B приводит к тому, что Kernel использует Unit B, а User использует Unit A.

Мультиплексор можно переключить, вызвав функцию switchBuffers() . Она вызывается не при каждом завершении чтения байта из буфера, а только после того, как все данные в этом буфере обработаны, и программа готова получить дополнительную информацию от UART.

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

Для помещения данных в буфер Kernel использует следующий код.

Поскольку значения uartUser и uartKernel всегда разные (1 и 0), это означает, что User и Kernel всегда будут обращаться к разным буферам и счетчикам. Итак, как мы передаем информацию от ядра Kernel к пользователю User? Всё, что нам нужно сделать, – это переключить значения uartUser и uartKernel , чтобы они указывали на буферы и счетчики другого. Поэтому, когда User читает новые данные, Kernel может продолжать запись в неиспользуемый буфер. Чтобы выполнить это переключение, всё, что должен сделать User (прежде чем обрабатывать новые данные), – это вызвать switchBuffers() .

Итак, давайте посмотрим на эту технологию двойной буферизации в сценарии, когда микроконтроллер находится под большой нагрузкой, и UART передает потоковые данные со скоростью, вдвое больше той, с которой программа могла бы справиться. Как и раньше, UART будет передавать « Hello », а программа будет печатать символы.

  • UART выдает « He » в Kernel – байты помещаются в Unit A;
  • программа вызывает switchBuffers . Программа печатает « H » из Unit A;
  • UART выдает « ll » в Kernel – байты помещаются в Unit B;
  • программа всё еще обрабатывает массив и печатает « e » из Unit A;
  • UART выдает « o » в Kernel – байт помещается в Unit B;
  • программа обработала Unit A и переключает буферы – программа печатает « l »;
  • программа всё еще обрабатывает массив и печатает « l » из Unit B;
  • программа всё еще обрабатывает массив и печатает « o » из Unit B.
Читайте также:  Как попасть в одноклассники без регистрации

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

Способ вывода изображения на экран, рассмотренный выше, называется двойной буферизацией (double buffering). Двойная буферизация позволяет убрать мерцание экрана в программах, моделирующих движение (анимацию) графических объектов.

Напомним, что в современных видеоадаптерах информация об изображении храниться в виде двухмерных массивов пикселей в области памяти видеоадаптера, в видеопамяти. Такая область называется кадровым буфером (frame buffer), или буфером кадра.

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

Сформированное на экране изображение остается видимым в течение очень короткого времени. Это время определяется качеством люминофоров, покрывающих экран, и не превышает нескольких миллисекунд. Поэтому изображение должно обновляться на экране с частотой не меньшей 50 Гц. Такое обновление называется регенерацией изображения.

В программе glopen03.cpp буферу кадра соответствует контекст окна hdcWin.

//получаем контекст окна

HDC hdcWin = GetDC(hwnd);

Когда программа рисует графический объект в контексте окна hdcWin, то одновременно информация о нем заносится в буфер кадра, и изображение объекта тут же появляется на экране.

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

Для удаления эффекта мерцания в программе используется два буфера кадра. Первый буфер кадра находится в видеопамяти видеоадаптера, и называется передним буфером (front buffer). С этим буфером кадра связан контекст окна hdcWin.

Роль второго буфера кадра выполняет битовая карта hBitmap, которая располагается в операционной памяти. Этот буфер кадра называется задним буфером (back buffer).

//создаем битовую карту совместимую с контекстом окна

HBITMAP hBitmap = CreateCompatibleBitmap(hdcWin, . ..);

С задним буфером кадра связан контекст памяти hdcMem.

//создаем контекст памяти связанный с контекстом окна

HDC hdcMem = CreateCompatibleDC(hdcWin);

//помещаем битовую карту в контекст памяти

HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMem, hBitmap);

Метод двойной буферизации сводится к следующему. Предыдущее изображение находится в переднем буфере кадра и с частотой 50 Гц обновляется на экране. С точки зрения программирования старое изображение находится в контексте окна hdcWin.

Пока старое изображение находится в переднем буфере кадра, новое изображение рисуется в заднем буфере кадра. С точки зрения программирования новое изображение рисуется на битовой плоскости в контексте памяти hdcMem.

Когда новое изображение будет полностью сформировано в заднем буфере кадра, оно практически мгновенно будет скопировано в передний буфер кадра, и тут же появится на экране. С точки зрения программирования копирование заднего буфера кадра в передний буфер кадра, означает копирование контекста памяти hdcMem в контекст окна hdcWin. Копирование осуществляется с помощью функции BitBlt().

Читайте также:  Sudo mount t vboxsf

//копируем контекст памяти в контекст окна

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

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

К сожалению, стандартные функции API не могут напрямую обращаться к заднему буферу кадра видеоадаптера. Единственное что они могут, это обращаться к переднему буферу кадра через контекст окна. Поэтому при программировании графики с помощью API-функций приходится в качестве заднего буфера кадра использовать область оперативной памяти в виде битовой карты контекста памяти.

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

двойная буферизация — Использование двух буферов для хранения передаваемых данные. Двойная буферизация используется с целью ускорения пересылки данных. Здесь, в то время, когда один буфер принимает данные от их источника, другой буфер передает ранее принятые данные… … Справочник технического переводчика

двойная буферизация — dvigubasis tarpinis kaupimas statusas T sritis radioelektronika atitikmenys: angl. double buffering vok. doppelte Pufferung, f rus. двойная буферизация, f pranc. tamponnage double, m … Radioelektronikos terminų žodynas

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

Буферизация (информатика) — Буферизация (от англ. buffer) метод организации обмена, в частности, ввода и вывода данных в компьютерах и других вычислительных устройствах, который подразумевает использование буфера для временного хранения данных. При вводе данных одни… … Википедия

Буферизация — (от англ. buffer) метод организации обмена, в частности, ввода и вывода данных в компьютерах и других вычислительных устройствах, который подразумевает использование буфера для временного хранения данных. При вводе данных одни устройства или… … Википедия

Буфер (информатика) — В этой статье не хватает ссылок на источники информации. Информация должна быть проверяема, иначе она может быть поставлена под сомнение и удалена. Вы можете … Википедия

Буфер (вычислительная техника) — В информатике буфер (англ. buffer) это область памяти, используемая для временного хранения данных при вводе или выводе. Обмен данными (ввод и вывод) может происходить как с внешними устройствами, так и с процессами в пределах компьютера. Буферы… … Википедия

Основной протокол X Window System — В этой статье не хватает ссылок на источники информации. Информация должна быть проверяема, иначе она может быть поставлена под сомнение и удалена. Вы можете … Википедия

Sinclair QL — Тип персональный компьютер Выпущен 12 января 1984 … Википедия

Кадровый буфер — Аппаратный видеобуфер Sun cgsix Кадровый буфер (англ. framebuffer) (другие названия: буфер кадр … Википедия

Ссылка на основную публикацию
Что случилось с facebook
На форумах и в поисковых запросах часто встречается вопрос, почему не работает Фейсбук сегодня, и что делать в такой ситуации....
Читы для вар тандер на орлы
Данный чит носит название Орлы чит для War Thunder 3.0. Это обновление для игры вышло совсем недавно, но для него...
Что больше мегабит или килобит
В эпоху оптоволокна и накопителей объемом в десятки терабайт считать в битах не принято. Мы бы совсем забыли, чем отличается...
Что смотрят в интернете больше всего
Наверное, многим интересно, что чаще всего запрашивают люди в поисковиках, какие поисковые запросы самые популярные и востребованные. Ошибки и опечатки...
Adblock detector