IPhone X: отключаем мерцание OLED дисплея. Управление яркостью подсветки дисплея Nextion в FLProg Что нам понадобится

В настоящее время я занимаюсь проектом на Arduino с использованием TFT дисплея. Недавно мне захотелось добавить в него, казалось бы, простую функцию - функцию регулировки яркости. Нашёл в документации к библиотеке для работы с TFT дисплеем (UTFT Library) нужный метод: setBrightness(br);

Что нам понадобится?

  • В качестве основы, я использовал Frearduino ADK v.2.2 на базе процессора ATmega2560
  • TFT LCD Mega Shield v.2.2
  • Сам дисплей - 7" TFT LCD SSD1963 ()
  • UTFT Library - универсальная библиотека для работы с TFT дисплеями (Найти саму библиотеку, а так же документацию можно )
  • Паяльник

Разберёмся с железом

Открыв схему дисплея, можно увидеть, что на конвертер mp3032 идет три входа: LED-A, PWM, 5V. Изначально, PWM неактивен. Этот вход не используется совсем. Подсветка управляется LED-A.


Если взглянуть на обратную сторону дисплея, можно найти область, подписанную как "Backlight control" . Здесь то мы и найдём эти самые входы. Для управления подсветкой методом ШИМ, необходимо сделать так, чтобы все было наоборот: LED-A - неактивен, PWM - активен. Для этого придётся перепаять перемычку. Вот фото того, что должно получиться:

Программная часть

Так как наша библиотека не может дать то, что нам надо, мы сами напишем нужную функцию. Для этого откроем документацию к контроллеру, управляющему дисплеем (SSD1963). Управление SSD1963 осуществляется с помощью специальных команд, которые передаются с Arduino через специальные выходы, которые описаны в документации:

Управление осуществляется следующим образом: Arduino выводит через RS (D/C в таблице) 0, если мы собираемся передавать команду, 1 - если данные. После передачи команды, RS переключается на 1, и далее передаются необходимые параметры. Все команды и параметры передаются через выходы D0-D7. Если у вас ATmega2560, то все эти восемь выходов объединены в порт C.

Итак, для начала, напишем функцию передачи данных по шине. Для удобства использования, я буду писать прямо в UTFT.h:

Void Lcd_Writ_Bus(uint8_t bla) { digitalWrite(WR,LOW); //Настраиваем SSD1963 на чтение digitalWrite(CS, LOW); PORTC = bla; //Передаем на шину данные в виде одного байта digitalWrite(CS,HIGH); digitalWrite(WR,HIGH); }

Также стоит обратить внимание на названия методов, так как в библиотеке уже могут встретиться функции с такими же именами.
Добавим две функции для вывода команд и данных:

Void Lcd_Write_Com(uint8_t data) { digitalWrite(RS,LOW); //Переключаем RS в режим чтения команды, то есть 0 Lcd_Writ_Bus(data); } void Lcd_Write_Data(uint8_t data) { digitalWrite(RS,HIGH); //Переключаем RS в режим чтения данных, то есть 1 Lcd_Writ_Bus(data); }

Теперь сама настройка подсветки. Чтобы узнать, как осуществить все это, открываем документацию и ищем команду для настройки PWM.

Примечание:
PWM может управляться, с помощью DBC - система динамической регулировки яркости, но я, для простоты, не стал её использовать. Вы же, если хотите, можете найти необходимую информацию в той же документации.

Итак, вот, что нам надо:

То есть, сначала мы должны передать команду «0xBE», а потом, в качестве 3-х параметров передать частоту сигнала, длительность рабочего цикла, а также третий параметр, который определяет, включен DBC или нет (0x01 - выключен, 0x09 - включен).

Для регулировки самой яркости, необходимо изменять лишь частоту рабочего цикла. Так как мы передаём данные в виде одного байта, то значения цикла могут быть от 0 до 255. Я решил определить 9 уровней яркости (от 0 до 8). Следовательно, все 256 значений нужно разбить на 9 ступеней. Но также стоит обратить внимание на то, что если ступени будут равными, то яркость будет изменяться не так плавно, как хотелось бы. То есть уже, к примеру, на 4-ой ступени, яркость будет почти максимальной, а с 4-ой по 8-ую ступень будет изменять почти незаметно. Учитывая это, я решил использовать геометрическую прогрессию со знаменателем 2. То есть яркость будет вычисляться по следующей формуле: (2 ^ lvl) - 1 , где lvl - уровень яркости от 0 до 8. Обратите внимание, что так как значения начинаются с нуля, то необходимо вычесть единицу. Конечно, вы можете выбрать ступени и их значения сами, но я привёл вот такой, довольно просто пример. Теперь сам код:

Void setBright(byte lvl) { byte brightness(1); for (byte i(1); i <= lvl; i++) //Возведение в степень brightness *= 2; Lcd_Write_Com(0xBE); //Вывод команды Lcd_Write_Data(0x01); //Ставим частоту 760Гц Lcd_Write_Data(brightness-1); //Выводим длину рабочего цикла Lcd_Write_Data(0x01); //Отключаем DBC }

Теперь можно использовать UTFT.setBright(byte lvl);

Пояснения к схеме

R2 – переменный резистор номиналом 10К. Центральный вывод резистора через НЧ-фильтр подключен к нулевому каналу АЦП. Напряжение на резисторе преобразуется в цифровой код, который записывается в регистр сравнения таймера Т0. Таким образом с помощью резистора осуществляется регулировка скважности ШИМ сигнала.

Led1 – мощный светодиод белого цвета. Падение напряжения на нем около 4 Вольт. Максимальный ток светодиода, задается резистором R5 и равен 100 мА.

(Uпит – Uled)/R5 = (5 - 4)/10 = 100 мА

IRLU024N – N–канальный полевой транзистор. Вместо него можно использовать любые полевые транзисторы серии IRL (L - это значит, что управляются логическим уровнем)

R3 – токоограничительный резистор на всякий случай. R4 – pull-down резистор. Затвор полевого транзистора нельзя оставлять "болтающимся в воздухе".

Программа

//************************************************************
// Учебный курс. Программирование микроконтроллеров AVR на Си
// Управление нагрузкой с помощью
// широтно-импульсной модуляции (ШИМ, PWM)

#include
#include

int main(void )
{
//инициализация портов
PORTB = 0;
DDRB = 0xff;

//инициализация таймера Т0
TIMSK = 0;
//реж. - fast pwm, вывод OC0 - неинверт. шим, clk/64
TCCR0 = (1< TCNT0 = 0;
OCR0 = 0;

//инициализируем АЦП
//ион - напряжение питания, выравнивание влево, нулевой канал

ADMUX = (0< //вкл. ацп, реж. непрерывн. преобр., разр. прерывания, частота преобр. = FCPU/128
ADCSRA = (1< //режим непрерывного преобразования
SFIOR = 0;

__enable_interrupt ();
while (1);
return 0;
}

//********************************
//прерывание АЦП
#pragma vector=ADC_vect
__interrupt void adc_my(void )
{

//считываем старший регистр АЦП и

//записываем в регистр сравнения

Пояснения к коду

Логика работы

Инициализация периферии - таймера Т0, модуля АЦП. Разрешение прерываний. Бесконечный цикл. В прерывании АЦП напряжение считанное с переменного резистора записывается в регистр сравнения OCR0. В микроконтроллере AVR для этого регистра организован буфер, и на самом деле запись происходит сначала в него. Из буфера же значение переписывается в OCR0, только когда происходит переполнение счетного регистра TCNT0. Параллельно циклу while и модулю АЦП, работает таймер Т0 и генерит на выводе OC0(PB2) ШИМ сигнал. Когда счетный регистр TCNT0 переполняется OC0 устанавливается в 1, когда значение счетного регистра совпадает с регистром сравнения OCR0, вывод устанавливается в 0.

Инициализация таймера

Прерывания таймера не используются, поэтому регистр TIMSK = 0.

За установки таймера Т0 отвечает конфигурационный регистр TCCR0.

Биты WGM01, WHM00 определяют режим работы. 11 - режим Fast PWM.

Биты CS02, CS01, CS00 - устанавливают коэффициент предделителя таймера. 011 - соответствует предделителю 64. Тактовая частота таймера/счетчика будет Fcpu/64

Биты COM01, COM00 -определяют поведение вывода OC0. 10 - соответствует неинвертированному ШИМ сигналу. 1 - при переполнении TCNT0, 0 - при совпадении TCNT0 c OCR0.

Инициализация АЦП

Пример №2. Генерация синусоидального сигнала с помощью ШИМ

Пояснение к схемe

Резистор R2 просто забыл выкинуть из схемы. R3 и C8 интегратор.

Программа

//************************************************************

// Генерация синуса с помощью

// широтно-импульсной модуляции (ШИМ, PWM)

//************************************************************

#include
#include
#include "TableSin.h"

int main(void )
{
//инициализация портов
PORTB = 0;
DDRB = 0xff;

//инициализация таймера Т0
TIMSK = (1< //реж. - fast pwm, вывод OC0 - неинверт. шим, предделитель - на 8
TCCR0 = (1< //обнуляем счетный регистр и регистр сравнения
TCNT0 = 0;
OCR0 = 0;

__enable_interrupt ();
while (1);
return 0;
}

//**********************************************************

//Прерывание таймера/счетчика Т0

#pragma vector = TIMER0_COMP_vect

__interrupt void Timer0CompVect(void )


В iPhone X компания Apple впервые использовала дисплейную панель, основанную на технологии органических светодиодов. И если достоинства OLED дисплеев очевидны, то об их недостатках пишут достаточно редко. Один из таких недостатков – мерцание экрана на низких уровнях яркости вследствие использования широтно-импульсной модуляции для управления свечением светодиодов на низких уровнях яркости. На языке обычного пользователя – в темноте экран мерцает.

Производители выбирают частоту мерцания экрана таким образом, чтобы большинство пользователей её не замечало. Чаще всего используется частота 240 Гц. Даже если отбросить значительную группу людей, которые замечают такое мерцание, остаются те, кто мерцания не замечает, но испытывает повышенную утомляемость, слезотечение, воспаление и покраснение глаз и даже мигрени. Таких людей не так уж мало: в зависимости от исследования и используемой методологии, их число составляет от 20 до 30% всех пользователей.

В устройствах под управлением Android проблему мерцания OLED-дисплеев часто можно решить установкой приложения, отображающего затемняющий фильтр; можно даже сохранить автоматическое управление яркостью, если для этого использовать приложение Lux Dash (о том, как его правильно настроить для отключения мерцания экранов, мы напишем в одной из следующих статей).

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

Почему OLED мерцают: управление яркостью посредством ШИМ

Мерцание экранов OLED – прямое следствие механизма, который используется для управления яркостью таких панелей. Для изменения яркости экрана (как обычного LCD, на которых основаны матрицы IPS, так и OLED) можно либо снизить напряжение, подаваемое на светодиоды подсветки (или индивидуальные светодиоды в случае OLED), либо использовать так называемую широтно-импульсную модуляцию.

Широтно-импульсная модуляция управляет яркостью изображения включая и выключая светодиоды. Чем короче период, на который включается подсветка, и чем длиннее паузы между включениями, тем ниже воспринимаемая человеческим глазом яркость. Вот как это выглядит:

Именно мерцание, а не зловредная синяя компонента цвета или перенасыщенные цвета OLED, вызывает повышенную утомляемость и мигрени. Наибольшую нагрузку на глаза мерцание даёт в темноте, когда зрачок максимально расширен и яркие вспышки буквально бомбардируют сетчатку.

OLED без мерцания: миф или реальность?

Можно ли собрать OLED экран без мерцания? Да, такое вполне возможно, но на практике используется нечасто. Одним из немногих примеров таких экранов является матрица P-OLED, которую устанавливали в смартфоны LG G Flex 2. Обратной стороной таких экранов были вот такие эффекты, проявляющиеся на минимальной яркости:

Ошибкой было бы полагать, что LG сделали матрицу настолько хуже конкурентов. Матрицы Samsung в те времена обладали похожим качеством – с одним крупным отличием. OLED панели, которые Samsung устанавливает во все свои флагманы, мерцают на всех уровнях яркости – даже на 100%. Почему же изображение на мерцающей матрице выглядит настолько чище, чем на OLED без мерцания?

Эффект связан с разбросом параметров между соседними светодиодами. Разброс присутствует на матрицах всех производителей, но проявляется он в большей степени тогда, когда на светодиоды подаётся низкое напряжение. Эффект чем-то похож на цифровые шумы при фотографировании в тёмных условиях с короткой выдержкой. Чем меньше фотонов попадает на светочувствительные элементы матрицы – и чем меньше фотонов излучается диодом OLED – тем больше вероятность возникновения погрешности, «цифрового шума наоборот».

В Samsung решили проблему, всегда подавая на светодиоды максимальное напряжение и регулируя яркость при помощи скважности импульсов. Но постоянное мерцание экрана по вкусу далеко не всем, и ряд производителей остановились на гибридном подходе: до некоторого значения яркость регулируется уменьшением напряжения светодиодов; после пересечения заданного минимального уровня (обычно 15-50%) дальнейшее понижение яркости достигается при помощи ШИМ.

Если говорить конкретно про iPhone X, то яркость в нём регулируется так (по данным iXBT):

До уровня 50% мерцания нет; дальнейшее снижение яркости достигается мерцанием на частоте 240 Гц. Убедительно продемонстрировали это немецкие обозреватели с сайта Notebookcheck.net:

Как проверить мерцание экрана

Мерцает ли экран вашего телефона? Даже если ваши глаза не замечают никакого мерцания, его наличие или отсутствие легко проверить дома без какого-либо специального оборудования. Достаточно открыть на смартфоне страницу с белым фоном (например, about:blank в браузере Safari или Chrome), поместить телефон в тёмную комнату, снизить яркость до минимума и навести на экран камеру другого смартфона. Если мы видим что-то похожее на показанное в видеоролике ниже, мерцание экрана присутствует:

Чтобы определить, на каком уровне яркости прекращается мерцание экрана, откроем центр управления и плавно сдвигаем ползунок яркости. Исчезновение диагональных полос означает отсутствие ШИМ на данном уровне яркости.

Экспериментально установлено, что у iPhone X минимальный уровень яркости без мерцания – 50%. Но если держать яркость на этом уровне, то в тёмных условиях устройством будет пользоваться некомфортно. Задача – понизить эффективный уровень яркости экрана, но не допустить мерцания.

Как оказалось, это вполне возможно. Для этого в настройках iOS предусмотрен специальный режим, который можно найти в настройках Accessibility > Display Accomodations.

В этом режиме экран затемняется программным фильтром. Чтобы включить этот режим, нужно зайти в Display Accomodations и активировать ползунок. Теперь нужно нажать на надпись Display Accomodations и активировать ползунок Reduce White Point (см. скриншот). Попробуйте начать со значения в промежутке от 85 до 100% и настройте до комфортного для ваших глаз уровня (яркость экрана в центре управления должна быть на уровне 50%).

Отключение ШИМ тремя кликами

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

На iPhone X включение и отключение фильтра можно реализовать по трёхкратному нажатию на боковую кнопку, в прошлых поколениях устройств выполняющую функции включения и выключения телефона. Для этого нужно найти в настройках опцию Accessibility Shortcut и назначить на неё включение и отключение функции Reduce White Point (см. скриншоты).

На том всё. После активации этой функции мы получили iPhone X, мерцание экрана которого можно быстро включать и отключать трёхкратным нажатием на боковую кнопку. Теперь вы сможете пользоваться устройством в темноте без переутомления и сможете быстро отключать тёмный фильтр трёхкратным нажатием боковой кнопки.

Дополнительная информация

И снова мы возьмем в руки датчик освещенности BH1750 . На этот раз предлагаю не просто снимать данные об уровне освещенности, а еще и относительно этих данных регулировать яркость какого-либо осветительного прибора. Наподобие регулировки яркости дисплея планшета или смартфона – чем ярче окружающее освещение, тем меньше требуется яркость освещения и наоборот чем темнее, тем ярче будет гореть светодиод, лампочки или что-нибудь еще. Да, это все можно организовать на основе фотодиода, фоторезистора, но такие приборы необходимо настраивать, калибровать и так далее. В случае же с применением цифрового датчика освещенности BH1750 эти действия осуществлять нет необходимости, так как все уже откалибровано до нас, по шине I2C передаются готовые данные, которые необходимо лишь сопоставить с условием. Простыми словами – включил, и все сразу заработало без лишних телодвижений. Если вам идея интересна, то продолжаем дальше.

Устройство построим по следующей схеме:

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

Здесь уже все привычно для нас. В качестве микроконтроллера используется Atmega8a как наиболее универсальный и популярный микроконтроллер. Использовать микроконтроллер можно в любом корпусе – разницы нет, кроме порядка расположения выводов на корпусах. Индикация осуществляется на ЖК экранчике на базе HD44780. В моем случае используется экран на 4 строки по 20 символов на каждую, однако можно использовать и размер 1602 – информации на экранчик выводится не много, поэтому все помещается. Переменный резистор R2 необходим для регулировки контраста символов на дисплее. Вращением движка этого резистора добиваемся наиболее четких для нас показаний на экране. Подсветка ЖК дисплея организована через вывод "А" и "К" на плате дисплея. Подсветка включается через резистор, ограничивающий ток - R1. Чем больше номинал, тем более тускло будет подсвечиваться дисплей. Однако пренебрегать этим резистором не стоит во избежание порчи подсветки. Сам дисплей подключается к микроконтроллеру по 4х битной схеме. Резистор R3 необходим для предотвращения самопроизвольного перезапускания микроконтроллера в случае появления случайных помех на выводе PC6. Резистор R3 подтягивает плюс питания к этому выводу, надежно создавая потенциал на нем. Резистор R4 подтягивает ножку датчика к земле, отвечающую за адрес микросхемы для I2C интерфейса, посмотреть все эти цифры можно в исходном коде, который расположен в конце статьи. Для правильной работы I2C интерфейса необходимы резисторы R7 и R8. С их помощью на линиях образуется логическая единица из-за того, что они подтянуты к плюсу питания. При формировании логического нуля линии прижимаются к земле посредством ведущего или ведомого (микроконтроллера или датчика).

Основное питание схемы составляет 3,3 вольта, что обусловлено электрическими параметрами датчика освещенности BH1750. 5 вольт необходимы лишь для питания дисплея, если применить, например экранчик от nokia 5110, для питания которого необходимо также 3,3 вольта, то можно опустить из схемы стабилизатор напряжения на 5 вольт. Стабилизаторы напряжения на 5 вольт и 3,3 вольта можно применить абсолютно любые на аналогичные напряжения, можно использовать как линейные стабилизаторы, так и импульсные.

Теперь по поводу модернизации схемы. Основой всегда будет сам датчик и микроконтроллер, а также схема питания. ЖК-дисплей, при данном функционале, лишь для отладки необходим по большому счету. Его можно исключить из схемы при автономном использовании. Это первое. Второе это выход схемы, то есть светодиод HL1 – одним светодиодом большое пространство не осветить и нужно использовать что-то более массивное – мощные светодиоды, лампы накаливания или еще что-нибудь. Так вот подобные осветительные приборы со сравнительно большим потреблением тока, напряжения просто к микроконтроллеру не подсоединить так просто, иначе мы просто сожжем микроконтроллер. Для этого необходимо использовать драйверы или другие схемы. Если лампочки накаливания используют переменное напряжение, нужно использовать оптосимисторную связку и мощный симистор для управления яркостью лампочки (нужно только подправить прошивку для управления оптосимистором). Для светодиодных ламп ШИМ можно подавать на затвор полевого транзистора и через него включать светодиоды (не забыв про ограничение или стабилизацию тока), либо использовать управляемые драйверы для них. В общем вариантов очень много – для каждого случая – свой. Представленная схема будет являться исходной с заложенным основным функционалам. Вот такая вот идея.

Собиралась и отлаживалась такая схема на отладочной плате в связке с модулем BH1750:

Логика работы не сложная – считываем значение освещенности из датчика и преобразуем это значение в ШИМ сигнал. Необходимо лишь подобрать соотношение ШИМ сигнала и уровня освещенности.

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

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

Кроме того, для программирования микроконтроллера в данном варианте необходимо знать конфигурацию фьюз битов:

А к статье прилагается прошивка для микроконтроллера по представленной схеме, исходный код в программе AVR Studio и демонстративное видео (плавное изменение яркости светодиода в зависимости от уровня освещенности, который регулируется карманным фонариком).

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
IC1 МК AVR 8-бит

ATmega8A

1 В блокнот
IC2 Датчик освещенности BH1750FVI-E 1 В блокнот
VR1 Линейный регулятор

L7805AB

1 В блокнот
VR2 Линейный регулятор

AMS1117-3.3

1 В блокнот
C1, C3, C5, C7 Конденсатор 100 нФ 4 В блокнот
C2 470 мкФ 1 В блокнот
C4 Электролитический конденсатор 220 мкФ 1 В блокнот
C6 Электролитический конденсатор 10 мкФ 1 В блокнот
R1 Резистор

22 Ом

1 В блокнот
R2 Подстроечный резистор 10 кОм 1 В блокнот
R3 Резистор

10 кОм

1 В блокнот
R4, R7, R8 Резистор

4.7 кОм

3 В блокнот
R5 Резистор