Синхронизация I2C с INT

XOBIT
Offline
Зарегистрирован: 23.11.2018

Всем привет.  Прошу помощи у знатоков)))

Реализовал  Многоканальный Dimmer, работающий в фоновом режиме.
В качестве нагрузки подключил лампочку. Отлично работает.

Добавить OLED дисплей SSD1306 i2c, для отображения информации. Инфа на дисплее отображается, но вот незадача лампочка стала мерцать. Взял исходный код и методом научного тыка выяснилось, что мерцание возникает, если в коде обновляю экран display.display().

Ни как не могу понять, что да как. Конфликт I2C с прерыванием INT? Таймер в какой момент не считает? Подскажите,  как правильно реализовать совместную работу диммера и дисплея.

 

#include <CyberLib.h> //Библиотека от Cyber-Place.ru
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


volatile uint8_t tic, Dimmer1, Dimmer2, Dimmer3;
uint8_t data;

unsigned long curTime, loopTime; // Время работы программы 
volatile int Hz = 0;            // Назовем частотой в сети переменного тока . 


#define SCREEN_WIDTH 128              // OLED дисплей ширина, пикселей
#define SCREEN_HEIGHT 64              // OLED дисплей высота, пикселей 

// Декларация для дисплея SSD1306, подключенного к I2C (контакты SDA, SCL)
#define OLED_RESET     7              // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() 
{ 
  D4_Out; //Настраиваем порты на выход
  D4_Low; //установить на выходах низкий уровень сигнала
  D2_In;  //настраиваем порт на вход для отслеживания прохождения сигнала через ноль  

//CHANGE – прерывание вызывается при любом изменении значения на входе; 
//RISING – вызов прерывания при изменении уровня напряжения с низкого (Low) на высокий(HIGH) 
//FALLING – вызов прерывания при изменении уровня напряжения с высокого (HIGH) на низкий (Low) 
    attachInterrupt(0, detect_up, LOW);  // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
    StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
    StopTimer1(); //остановить таймер
    UART_Init(115200); //инициализация порта
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3D for 128x64 // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
}
//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{ 
  tic++;  //счетчик  
  if(Dimmer1 < tic ) D4_High; //управляем выходом
 
}

void  detect_up()  // обработка внешнего прерывания. Сработает по переднему фронту
{  
 tic=0;             //обнулить счетчик
 ResumeTimer1();   //запустить таймер
 attachInterrupt(0, detect_down, HIGH);  //перепрограммировать прерывание на другой обработчик
}  

void  detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту
{   
 StopTimer1(); //остановить таймер
 D4_Low; //логический ноль на выходы
 tic=0;       //обнулить счетчик
 attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
}  

void loop() 
{
  Dimmer1=160;  

  display.clearDisplay();             // Сlear display
  display.setTextSize(2);             // Normal 1:1 pixel scale
  display.setTextColor(WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner

  display.print(Hz++);   
  
  display.display();   

  delay(500); 
  
}  

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

дак выводи на дисплей только когда что-то реально меняется а не постоянно в лупе

XOBIT
Offline
Зарегистрирован: 23.11.2018

Пробовал, по всякому. По времени, по изменению.
Все равно мерцает. Правда реже. В момент обновления экрана)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

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

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

Пробовал, по всякому. По времени, по изменению.
Все равно мерцает. Правда реже. В момент обновления экрана)

Хобит, во-первых, вот эти три строчки вызывать каждый раз в ЛУПе ни к чему:

display.clearDisplay();             // Сlear display
display.setTextSize(2);             // Normal 1:1 pixel scale
display.setTextColor(WHITE);        // Draw white text

вставьте их в Setup

Во-вторых, насколько я помню display.dispay() нужно только для графики а у вас только текст. Попробуйте вовсе выкинуть эту строчку.

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

XOBIT
Offline
Зарегистрирован: 23.11.2018
  if (millis() - time_oled_update > 500)
  {
  display.print(Hz++);     
  display.display();   
  time_oled_update = millis();
  

Мерцает 2  раза в секунду.

Пробовал, по всякому. По времени, по изменению.
Хоть так хоть этак. Мерцание в момент обновления.

В этой библиотеке:
display.clearDisplay();             // Очистить дисплей.
display.display();                    // Обновить дисплей.

 

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

я бы еще попробовал библиотеку "полегче" для вывода на экран.

mykaida
mykaida аватар
Онлайн
Зарегистрирован: 12.07.2018

"Лампочку" запитай отдельным пином без i2c

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

В этой библиотеке:

display.clearDisplay();             // Очистить дисплей.
display.display();                    // Обновить дисплей.

 

Я вам сказал три строчки убрать в Сетап, а последнюю просто закомментировать. Вы попробовали или просто теоретизируете? - попробуйте, не бойтесь, сломать этим вы ничего не можете

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

XOBIT,

я понимаю, что код Вы скачали, но ответьте мне, Вы понимаете что там написано и можете осмысленно менять? Или нет? Только честно. Если первое, я расскажу Вам что делать. Если второе. то мой рассказ Вам не поможет, я только пальцы об клавиши зря бить буду.

XOBIT
Offline
Зарегистрирован: 23.11.2018

Да, понимаю что там написано и могу осмысленно менять.
Пробовал обновлять дисплей, когда нет прерывания, когда счетчик не считает, когда выход = 0.
Так ничего и не получилось. 

 

XOBIT
Offline
Зарегистрирован: 23.11.2018

b707 пишет:

XOBIT пишет:

В этой библиотеке:

display.clearDisplay();             // Очистить дисплей.
display.display();                    // Обновить дисплей.

 

Я вам сказал три строчки убрать в Сетап, а последнюю просто закомментировать. Вы попробовали или просто теоретизируете? - попробуйте, не бойтесь, сломать этим вы ничего не можете

Теоретизирую для вас теорию. )
Специально по вашей просьбе попробовал.
И...
О чудо! Мерцание пропало. Спасибо.
НО...
Перестал работать дисплей.
И все почему? Потому что:
В этой библиотеке:
display.clearDisplay();             // Очистить дисплей.
display.display();                    // Обновить дисплей.

P.S. вы были правы, я ничего не сломал)

 

AlexanderNO
Offline
Зарегистрирован: 08.11.2018

Проверить напряжение на оптроне (D4) осциллографом с экраном (в момент включения) и без экрана.

 

nik182
Онлайн
Зарегистрирован: 04.05.2015

Деда в #1 посте правильно сказал. Заведи себе флаг и взводи его после смены прерывания. У тебя будет 10 мс на прорисовку. Начинай рисовать только после флага. И делей надо заменить на кошерный миллис как в#5 предложили. Если после этого останется мерцание воспользуйся советом из #6.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

XOBIT пишет:

Да, понимаю что там написано и могу осмысленно менять.

Ну, значит, будем надеяться. что я не зря потерял час на написание этого поста.

XOBIT пишет:

Пробовал обновлять дисплей, когда нет прерывания, когда счетчик не считает, когда выход = 0.
Так ничего и не получилось. 

Вы не показывали как Вы пробовали, потому ничего сказать не могу.

Ну, давайте рассуждать.

Всё, что Вам говорили коллеги (про вынос инициализации дисплея в setup(), и т.п.) всё это верно. Т.е. инициализацию дисплея выносим в setup и это не обсуждается.

Давайте поговорим по алгоритму.

Сейчас Вы делаете следующее  

  1. при пересечении 0 снизу вверх (функция detect_up ()) Вы обнуляете счётчик tic и включаете таймер. На самом деле, несколько позже, чем точно при пересечении 0).
  2. Таймер тикает раз в 40мкс (строка 32). Когда натикает Dimmer1 тиков, Вы включаете HIGH на D4.
  3. При пересечении 0 сверху вниз (функция detect_down()) Вы выключаете таймер и ставите LOW на D4. При этом также обнуляете счётчик tic, но неясно зачем, таймер-то выключен, а перед включением (в функции detect_up()) Вы его ещё раз обнулите!

Что имеем.

  1. Если Ваши detect_up () и detect_down() срабатывают по сетевому напряжению, то между ними проходит полпериода сетевого напряжения, а именно 10мс.
  2. Лампой Вы управляете на отрезке между detect_up () и detect_down(). На втором отрезке (между detect_down() и detect_up ()) Ваша лампа всегда выключена (или всегда включена, в общем на D4 всегда LOW).
  3. Т.е. лампа у Вас управляется не «от 0 до максимума» а либо от нуля до «полнакала» (если она светится при HIGH на D4), либо от «полнакала» до максимума (если она светится при LOW на D4).
  4. За время между detect_up () и detect_down(), прерывание таймера (функция halfcycle()) успевает сработать 10000/40 = 250 раз!
  5. А вот нафига? Вы ведь заранее знаете и частоту ШИМ и коэффициент заполнения! Т.е. Вы вполне могли бы просто посчитать длительность HIGH и LOW на D4 и включать таймер точно на это время! Тогда у Вас функция halfcycle()  срабатывала бы только тогда, когда реально нужно переключить с HIGH на LOW или обратно, т.е. 2 раза за период, а не 250 раз! Можно даже обратно не переключать (это можно делать в Вашей detect_down()), тогда таймер вообще будет срабатывать 1 раз за период, а не 250! Это к вопросу о ненужной загрузке процессора.
  6. Теперь про вывод на экран. Вы делаете это «когда попало», никак не привязываясь к ритму Вашего таймера, вот оно и сбивается.
  7. Ну, и последнее. Какова частота Вашего ШИМ? Очевидно, что она равна частоте сети, т.е. 50Гц.
  8. Не знаю, что у Вас там за лампа (насколько она инерционная), но скорее всего, Вы сделали специальный прибор для угробления зрения и введения человека в состояние тревожности и депрессии. Такие пульсации освещённости (огромные просадки на такой низкой частоте) санитарно-эпидемические нормы допускают только для уличного освещения, или для освещения мест, где люди не находятся постоянно и лишь эпизодически проходят. Для освещения жилых помещений или, тем более, рабочих мест это категорически запрещено.

Итак, что теперь делать.

Про п.3 думайте сами. Может Вас это устраивает, я не знаю. Если не устраивает, то можно, напроимер, просто убрать одну из функций detect_up () или detect_down(), и работать только по одной из ник, считая при этом, что максимальный коэффициент заполнения ШИМ не 250 как сейчас, а 500.

Я буду исходить из того, что п.3 Вы оставляете как есть, а расскажу Вам как бороться с п.п. 5 и 6, т.к. борьба с п.8 требует не переделывания этого кода, а выбрасывания его и переписывания совсем по-другому.

По п.5. делаете следующее.

  1. В самом начале, а также после каждого изменения процента заполнения Dimmer1 (Вы же планируете добавить возможность его менять, правда?), вычисляете время HIGH. Оно равно 40 * Dimmer1 микросекунд (40 это если сохранять всё как у Вас есть).
  2. В detect_up() запускаете таймер на рассчитанное время
  3. При срабатывании таймера, взводите D4 в HIGH и выключаете таймер
  4. В detect_down() LOW ставите D4 в LOW

Всё, таймер у Вас работает 1 раз за период вместо нынешних 250! Функциональность при этом не поменялась (см. примечание * в конце)

По п.6. делаете следующее

Для начала заметьте, что между detect_down() и detect_up() у Вас таймер выключен, и вообще ничего не происходит, т.е. у Вас есть целых 10мс свободного времени, когда Вы можете вывести на экран что хотите и это никому не помешает.

Поэтому, Вы можете, например, прямо в конце функции detect_down() вставить проверку, что с последнего вывода прошло 500мс (через millis()) и если прошло, то прямо там вызывать display.print(Hz++); (только не забудьте разрешить прерывания, а то I2C не сработает). Тогда loop() у Вас останется просто пустым. А чему там ещё быть?

При таком подходе Вы гарантируете, что вывод на экран не пересечётся по времени ни с какими другими действиями.

Можно сделать то же самое и ещё 100500 способами, в любом случае – Вы должны привязать вывод к свободному времени, а не выводить «когда придётся» как Вы это делаете сейчас.

---------------------------

Примечание (*). Это один из тех случаев, когда Вы экономите время на совершеннейших «спичках» (используете Cyberlib) и при этом, там где надо бы экономить – в алгоритме, делаете 250 раз то, что можно сделать 1 раз. У меня на эту тему была здесь статья, посмотрите. Если интересно.

 

ВН
Offline
Зарегистрирован: 25.02.2016

парочка замечаний по диммеру

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

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

т.е. процесс может выглядеть примерно так:

- ловим переход напряжения из нуля в верх;

- формируем задержку до включения;

-формируем имульс запуска и вывод на экран; 

все, цикл 

нет необходимости ловить еще какие-то фронты.

т.е. тут остается открытым вопрос, сколько времени занимает вывод на экран.

 

 

 

XOBIT
Offline
Зарегистрирован: 23.11.2018

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

Вы правильно описали алгоритм работы.
Пока решил оставить алгоритм работы диммера, как есть.
250 раз вместо 1 раз, ни и... 
Хочу все таки в первую очередь отладить работу экрана.

Т.е. сразу перешел к п.№6. По вашему совету попробовал в конце функции detect_down() добавить вывод на экран.

#include  //Библиотека от Cyber-Place.ru
#include 
#include 
#include 
#include 


volatile uint8_t tic, Dimmer1, UpF;

volatile unsigned long time_oled_update; 

volatile int Hz = 0;                  // Назовем частотой в сети переменного тока . 

#define SCREEN_WIDTH 128              // OLED дисплей ширина, пикселей
#define SCREEN_HEIGHT 64              // OLED дисплей высота, пикселей 

// Декларация для дисплея SSD1306, подключенного к I2C (контакты SDA, SCL)
#define OLED_RESET     7              // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() 
{ 
  D4_Out; //Настраиваем порты на выход
  D4_Low; //установить на выходах низкий уровень сигнала
  D2_In;  //настраиваем порт на вход для отслеживания прохождения сигнала через ноль  

//CHANGE – прерывание вызывается при любом изменении значения на входе; 
//RISING – вызов прерывания при изменении уровня напряжения с низкого (Low) на высокий(HIGH) 
//FALLING – вызов прерывания при изменении уровня напряжения с высокого (HIGH) на низкий (Low) 
    attachInterrupt(0, detect_up, LOW);  // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
    StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
    StopTimer1(); //остановить таймер
    UART_Init(115200); //инициализация порта
    
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3D for 128x64 // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setTextColor(WHITE);        // Draw white text
}
//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{ 
  tic++;  //счетчик  
  if(Dimmer1 < tic ) D4_High; //управляем выходом
}

void  detect_up()  // обработка внешнего прерывания. Сработает по переднему фронту
{  
 tic=0;             //обнулить счетчик
 ResumeTimer1();   //запустить таймер
 attachInterrupt(0, detect_down, HIGH);  //перепрограммировать прерывание на другой обработчик
}  

void  detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту
{   
 StopTimer1(); //остановить таймер
 D4_Low; //логический ноль на выходы
 tic=0;       //обнулить счетчик
 attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик

  if ((millis() - time_oled_update > 500))
  {
  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  display.display(); 
  time_oled_update = millis();
  }

}  

void loop() 
{
  Dimmer1=160;  
  //delay(500);
}  

Как результат, лампочка включается и через ~1сек гаснет.
Экран отображает число 144. И все. После перезапуска все повторяется.

Попробовал убрать delay и через промежуточную переменную oled_update обновлять экран.

#include  //Библиотека от Cyber-Place.ru
#include 
#include 
#include 
#include 


volatile uint8_t tic, Dimmer1, oled_update;

volatile unsigned long time_oled_update; 

volatile int Hz = 0;                  // Назовем частотой в сети переменного тока . 

#define SCREEN_WIDTH 128              // OLED дисплей ширина, пикселей
#define SCREEN_HEIGHT 64              // OLED дисплей высота, пикселей 

// Декларация для дисплея SSD1306, подключенного к I2C (контакты SDA, SCL)
#define OLED_RESET     7              // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() 
{ 
  D4_Out; //Настраиваем порты на выход
  D4_Low; //установить на выходах низкий уровень сигнала
  D2_In;  //настраиваем порт на вход для отслеживания прохождения сигнала через ноль  

//CHANGE – прерывание вызывается при любом изменении значения на входе; 
//RISING – вызов прерывания при изменении уровня напряжения с низкого (Low) на высокий(HIGH) 
//FALLING – вызов прерывания при изменении уровня напряжения с высокого (HIGH) на низкий (Low) 
    attachInterrupt(0, detect_up, LOW);  // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
    StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
    StopTimer1(); //остановить таймер
    UART_Init(115200); //инициализация порта
    
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3D for 128x64 // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setTextColor(WHITE);        // Draw white text
}
//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{ 
  oled_update = 0;   //нельзя обновлять дисплей
  tic++;  //счетчик  
  if(Dimmer1 < tic ) D4_High; //управляем выходом
}

void  detect_up()  // обработка внешнего прерывания. Сработает по переднему фронту
{  
 tic=0;             //обнулить счетчик
 ResumeTimer1();   //запустить таймер
 attachInterrupt(0, detect_down, HIGH);  //перепрограммировать прерывание на другой обработчик
 oled_update = 0;   //нельзя обновлять дисплей
}  

void  detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту
{   
 StopTimer1();  //остановить таймер
 D4_Low;        //логический ноль на выходы
 tic=0;         //обнулить счетчик
 attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
 oled_update = 1;   //можно обновить дисплей
}  

void loop() 
{
  Dimmer1=160;  
  
  if ((millis() - time_oled_update > 500) && oled_update==1)
  {
  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  display.display(); 
  time_oled_update = millis();
  }

  //delay(500);
}  

Мерцает стабильно в момент обновления экрана.

Возможно ВН правильно мыслит?
"т.е. тут остается открытым вопрос, сколько времени занимает вывод на экран."
Не успевает. Хотя OLED 400kHz. Прерывания внутри прерывания запрещены. 
Выполняются по очереди. Отсюда и просадка, мерцание.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

А если посмотреть сколько времени занимает вывод на экран?
 

void loop() 
{
  Dimmer1=160;  
  
  if ((millis() - time_oled_update > 500) && oled_update==1)
  {
time_oled_update = millis();  // Время до начала процедуры вывода на экран

  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  display.display(); 

Serial.println(millis()-time_oled_update); // Время выволнения процедуры в миллисекундах

  time_oled_update = millis();


  }

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ведь я Вам писал, что "не забудьте разрешить прерывания". Вы не забыли?

XOBIT пишет:

Возможно ВН правильно мыслит?
"т.е. тут остается открытым вопрос, сколько времени занимает вывод на экран."

Да, мыслит то он правильно, только 10мс - так много, что вопрос так не стоит.

Ну, напечатайте скорость вывода, чтобы успокоиться, делов-то. Запомните перед выводом значение micros и сравните с ним же после вывода - получите время.

ВН
Offline
Зарегистрирован: 25.02.2016

ЕвгенийП пишет:
Ну, напечатайте скорость вывода, чтобы успокоиться, делов-то. Запомните перед выводом значение micros и сравните с ним же после вывода - получите время.

да, тогда будет понятно, в какое из времен этот вывод можно помещать, и какие ограничения при этом могут возникнуть на работу диммера 

XOBIT
Offline
Зарегистрирован: 23.11.2018
void loop() 
{  
  Dimmer1=160;  

  time_1 = micros();
  
  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  display.display(); 
  
  Serial.print("Time_oled1:");  Serial.println(micros() - time_1); // Время 
}  

Результат: 

Time_oled1:39088
Time_oled1:38920
Time_oled1:39176
Time_oled1:39060
Time_oled1:40016
Time_oled1:39840
Time_oled1:39836

~40мс. не многовато?

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

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

XOBIT
Offline
Зарегистрирован: 23.11.2018
void loop() 
{  
  Dimmer1=160;  

  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  
  time_1 = micros();
  
  display.display(); 
  
  Serial.print("Time_oled1:");  Serial.println(micros() - time_1); // Время 
}  

Результат:

Time_oled1:37156
Time_oled1:37164
Time_oled1:37176
Time_oled1:37156
Time_oled1:37152

~38мс.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

из любопытства решил сам попробовать скорость вывода

#include <Wire.h>
#include <OzOLED.h>


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Wire.begin();
  OzOled.init();  //initialze OLED display
  OzOled.setNormalDisplay();       //Set display to Normal mode
  OzOled.setHorizontalMode();      //Set addressing mode to Horizontal Mode
  OzOled.setPowerOn();
  OzOled.clearDisplay(); //clear the screen and set start position to top left corner
}

void loop() {
  long xxx = 0;
  // put your main code here, to run repeatedly:
  unsigned long ttt = micros();
  while (xxx < 1000) {
    OzOled.setCursorXY(0, 0);
    OzOled.printNumber((long)xxx);
    ++xxx;
  }
  unsigned long yyy = micros() - ttt;
  Serial.print("1000 numbers time = "); Serial.print(yyy, DEC);
  yyy /= 1000;
  Serial.print("4 chars time = "); Serial.println(yyy, DEC);
}

получилось на один символ менее 1 миллисекунды.

1000 numbers time = 2991684    4 chars time = 2991
1000 numbers time = 2991620    4 chars time = 2991
1000 numbers time = 2991728    4 chars time = 2991
1000 numbers time = 2991560    4 chars time = 2991
1000 numbers time = 2991596    4 chars time = 2991

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

тупо вывод строки получается ~ 0.92 миллисекунды на один символ

#include <Wire.h>
#include <OzOLED.h>


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Wire.begin();
  OzOled.init();  //initialze OLED display
  OzOled.setNormalDisplay();       //Set display to Normal mode
  OzOled.setHorizontalMode();      //Set addressing mode to Horizontal Mode
  OzOled.setPowerOn();
  OzOled.clearDisplay(); //clear the screen and set start position to top left corner
}

void loop() {
  long xxx = 0;
  // put your main code here, to run repeatedly:
  unsigned long ttt = micros();
  /*while (xxx < 1000) {
    OzOled.setCursorXY(0, 0);
    OzOled.printNumber((long)xxx);
    ++xxx;
  }*/
  OzOled.setCursorXY(0, 0);
  OzOled.printString("0test1char2");
  unsigned long yyy = micros() - ttt;
  Serial.print("1000 numbers time = "); Serial.print(yyy, DEC); Serial.print("    ");
  yyy /= 1000;
  Serial.print("4 chars time = "); Serial.println(yyy, DEC);
}


1000 numbers time = 10104    4 chars time = 10
1000 numbers time = 10096    4 chars time = 10
1000 numbers time = 10116    4 chars time = 10
1000 numbers time = 10084    4 chars time = 10
1000 numbers time = 10092    4 chars time = 10
1000 numbers time = 10116    4 chars time = 10
1000 numbers time = 10096    4 chars time = 10
1000 numbers time = 10108    4 chars time = 10
1000 numbers time = 10124    4 chars time = 10
1000 numbers time = 10092    4 chars time = 10
1000 numbers time = 10112    4 chars time = 10
1000 numbers time = 10096    4 chars time = 10

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

XOBIT пишет:

void loop() 
{  
  Dimmer1=160;  

  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  
  time_1 = micros();
  
  display.display(); 
  
  Serial.print("Time_oled1:");  Serial.println(micros() - time_1); // Время 
}  

Результат:

Time_oled1:37156
Time_oled1:37164
Time_oled1:37176
Time_oled1:37156
Time_oled1:37152

~38мс.

9 строку на 4ое место вставьте - будут более честные результаты

XOBIT
Offline
Зарегистрирован: 23.11.2018

В #20 посте, так делал.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Сейчас не могу посмотреть, посмотрите сами. Почти уверен, что ВСЕ операции, кроме display.display() не работают с экраном, а работаю только с буфером. Поэтому измерять надо только эту операцию.

Такое время меня удивило - ни в какие ворота не лезет. Попробуйте не чистить весь экран, а работать только с нужной областью. Если не поможет, надо смотреть что там в библиотеке так тормозит (например, display.display() может всегда работать только с полным экраном) и улучшать её. Этот дисплей позволяет выводить за раз по 8 пикселей, так что фигачить весь экран нет никакой нужды.

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

Результат:

Time_oled1:37156
Time_oled1:37164
Time_oled1:37176
Time_oled1:37156
Time_oled1:37152

~38мс.

жуть!

Посмотрел исходники библиотеки - написано не слишком эффективно. Чтобы обновить 1 пиксель на экране - она каждый раз выплевыет в дисплей весь буфер. Но, по-моему все равно долго, 38мс - это просто вечность.

Возьмите другую библиотеку, их для SSD1306 как минимум десяток в сети.

b707
Offline
Зарегистрирован: 26.05.2017

ЕвгенийП пишет:

Такое время меня удивило - ни в какие ворота не лезет. Попробуйте не чистить весь экран, а работать только с нужной областью. Если не поможет, надо смотреть что там в библиотеке так тормозит (например, display.display() может всегда работать только с полным экраном)

там так написано, что все сначала рисуется в буфере, а потом буфер ЦЕЛИКОМ выплевывается на экран. Возможности обновлять экран по частям в библиотеке нет (ну или я не нашел - смотрел по диагонали).

ИМХО, "улучшать" эту библиотеку нет смысла, "проще сделать новую" :) - просто взять другую, благо их в сети несколько

XOBIT
Offline
Зарегистрирован: 23.11.2018

b707 пишет:

ИМХО, "улучшать" эту библиотеку нет смысла, "проще сделать новую" :) - просто взять другую, благо их в сети несколько

Может сразу конкретную посоветуете? Чтобы время не терять. 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

XOBIT пишет:

b707 пишет:

ИМХО, "улучшать" эту библиотеку нет смысла, "проще сделать новую" :) - просто взять другую, благо их в сети несколько

Может сразу конкретную посоветуете? Чтобы время не терять. 

их реально много - поиск вам в помощь по словам ss1306 library, я например пробовал штук пять, остановился на OzOled (пример выше), но в любом случае дело вкуса.

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

Может сразу конкретную посоветуете? Чтобы время не терять. 

например, в последнем проекте использовал вот эту:

https://github.com/greiman/SSD1306Ascii

она маленькая, не использует буфер и быстрая. Но, как видно из названия - совсем без графики, только текст

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

b707 пишет:
буфер ЦЕЛИКОМ выплевывается на экран.
Хреново.

Могу предложить то, что сам использую, но там нет шрифтов (и буфера тоже нет). Есть только вывод битмапа. Что мне надо я рисую руками и вывожу точно туда, куда надо и ни на байт больше, чем реально надо не вывожу. Не только в 328, но и в тиньке 45-ой на ура работает (I2C там через USI делаю).

b707 пишет:
совсем без графики, только текст

А у меня совсем без текста, только графика. Цифры (огромного размера) приделал когда надо было, как пачку битмапов в прогмеме :)

XOBIT
Offline
Зарегистрирован: 23.11.2018

Планирую использовать дисплей для вывода информации.
Так что мне и текст и цифирки) нужны. Графика - нет.

b707 подскажи что не так?

// Simple I2C test for ebay 128x64 oled.
// Use smaller faster AvrI2c class in place of Wire.
// Edit AVRI2C_FASTMODE in SSD1306Ascii.h to change the default I2C frequency.
//
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

// 0X3C+SA0 - 0x3C or 0x3D
#define I2C_ADDRESS 0x3C

// Define proper RST_PIN if required.
#define RST_PIN -1

SSD1306AsciiAvrI2c oled;
//------------------------------------------------------------------------------
void setup() {

#if RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
#else // RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // RST_PIN >= 0
 
  // Call oled.setI2cClock(frequency) to change from the default frequency.

   oled.setFont(Adafruit5x7);
 
}
//------------------------------------------------------------------------------
void loop() 
{
  oled.clear();
  oled.print("Hello world!");
  delay(1000);
}

Мерцает раз в секунду.

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

b707 подскажи что не так?

// Simple I2C test for ebay 128x64 oled.
// Use smaller faster AvrI2c class in place of Wire.
// Edit AVRI2C_FASTMODE in SSD1306Ascii.h to change the default I2C frequency.
//
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

// 0X3C+SA0 - 0x3C or 0x3D
#define I2C_ADDRESS 0x3C

// Define proper RST_PIN if required.
#define RST_PIN -1

SSD1306AsciiAvrI2c oled;
//------------------------------------------------------------------------------
void setup() {

#if RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
#else // RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // RST_PIN >= 0
 
  // Call oled.setI2cClock(frequency) to change from the default frequency.

   oled.setFont(Adafruit5x7);
 
}
//------------------------------------------------------------------------------
void loop() 
{
  oled.clear();
  oled.print("Hello world!");
  delay(1000);
}

Мерцает раз в секунду.

а если oled.clear() в строке 32 закомментировать?

XOBIT
Offline
Зарегистрирован: 23.11.2018
void loop() 
{
  //oled.clear();
  oled.print("1");
  delay(1000);
}

Выводит 1, через секунду 11, 111,1111 и т.д.
за облать экрана уходит...

 

 

SLKH
Offline
Зарегистрирован: 17.08.2015

ЕвгенийП пишет:

Сейчас не могу посмотреть, посмотрите сами. Почти уверен, что ВСЕ операции, кроме display.display() не работают с экраном, а работаю только с буфером. Поэтому измерять надо только эту операцию.

Такое время меня удивило - ни в какие ворота не лезет. 

Ещё бы оно влезло, если длительность сериалпринта измерять.

09   time_1 = micros();
10   
11   display.display();
12   
13   Serial.print("Time_oled1:");  Serial.println(micros() - time_1); // Время

 

b707
Offline
Зарегистрирован: 26.05.2017

Хобит, попробуйте так

void loop() 
{
  for (int i =0; i< 100; i ++) {
  oled.setCursor(0,0);
  oled.print(i);
  delay(1000);
}
}

 

b707
Offline
Зарегистрирован: 26.05.2017

SLKH пишет:

Ещё бы оно влезло, если длительность сериалпринта измерять.

при скорости 9600 вывод одного символа порядка 1мс, у автора их менее двадцати, так что на очистку дисплея все равно тратится примерно 20мс - много

XOBIT
Offline
Зарегистрирован: 23.11.2018
 void loop() 
{  
  Dimmer1=160;  

  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  
  time_1 = micros();
  
  display.display(); 

  time_2 = micros() - time_1;
  Serial.print("Time_oled1:");  Serial.println(time_2); // Время 
  

  //delay(500);
}  

Результат:

Time_oled1:38388
Time_oled1:38364
Time_oled1:38328
Time_oled1:38372
Time_oled1:38380
Time_oled1:38420
Time_oled1:38360
Time_oled1:38372

~39мс.

XOBIT
Offline
Зарегистрирован: 23.11.2018

b707 пишет:

Хобит, попробуйте так

void loop() 
{
  for (int i =0; i< 100; i ++) {
  oled.setCursor(0,0);
  oled.print(i);
  delay(1000);
}
}

 

Норм

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

 void loop() 
{  
  Dimmer1=160;  

  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  
  time_1 = micros();
  
  display.display(); 

  time_2 = micros() - time_1;
  Serial.print("Time_oled1:");  Serial.println(time_2); // Время 
  

  //delay(500);
}  

Результат:

Time_oled1:38388
Time_oled1:38364
Time_oled1:38328
Time_oled1:38372
Time_oled1:38380
Time_oled1:38420
Time_oled1:38360
Time_oled1:38372

~39мс.

фигня какая-то, быть такого не может. Скорость Сериал какая?

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

b707 пишет:

Хобит, попробуйте так

void loop() 
{
  for (int i =0; i< 100; i ++) {
  oled.setCursor(0,0);
  oled.print(i);
  delay(1000);
}
}

 

Норм

а поподробнее? :) - мерцает, нет?

XOBIT
Offline
Зарегистрирован: 23.11.2018
#include <CyberLib.h> //Библиотека от Cyber-Place.ru
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


volatile uint8_t tic, Dimmer1, oled_update;

volatile unsigned long time_oled_update, time_1, time_2; 


volatile int Hz = 0;                  // Назовем частотой в сети переменного тока . 

#define SCREEN_WIDTH 128              // OLED дисплей ширина, пикселей
#define SCREEN_HEIGHT 64              // OLED дисплей высота, пикселей 

// Декларация для дисплея SSD1306, подключенного к I2C (контакты SDA, SCL)
#define OLED_RESET     7              // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() 
{ 
  D4_Out; //Настраиваем порты на выход
  D4_Low; //установить на выходах низкий уровень сигнала
  D2_In;  //настраиваем порт на вход для отслеживания прохождения сигнала через ноль  

//CHANGE – прерывание вызывается при любом изменении значения на входе; 
//RISING – вызов прерывания при изменении уровня напряжения с низкого (Low) на высокий(HIGH) 
//FALLING – вызов прерывания при изменении уровня напряжения с высокого (HIGH) на низкий (Low) 
    attachInterrupt(0, detect_up, LOW);  // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
    StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
    StopTimer1(); //остановить таймер
    UART_Init(115200); //инициализация порта
    
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3D for 128x64 // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setTextColor(WHITE);        // Draw white text
}
//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{ 
  oled_update = 0;   //нельзя обновлять дисплей
  tic++;  //счетчик  
  if(Dimmer1 < tic ) D4_High; //управляем выходом
}

void  detect_up()  // обработка внешнего прерывания. Сработает по переднему фронту
{  
 tic=0;             //обнулить счетчик
 ResumeTimer1();   //запустить таймер
 attachInterrupt(0, detect_down, HIGH);  //перепрограммировать прерывание на другой обработчик
 oled_update = 0;   //нельзя обновлять дисплей
}  

void  detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту
{   
 StopTimer1();  //остановить таймер
 D4_Low;        //логический ноль на выходы
 tic=0;         //обнулить счетчик
 attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
 oled_update = 1;   //можно обновить дисплей
}  

  void loop() 
{  
  Dimmer1=160;  

  display.clearDisplay();             // Сlear display
  display.setCursor(0,0);             // Start at top-left corner
  display.print(Hz++);     
  
  time_1 = micros();
  
  display.display(); 

  time_2 = micros() - time_1;
  
  Serial.print("Time_oled1:");  Serial.println(time_2); // Время 
  
 delay(500);
}  

 

Если не измерять длительность сериалпринта.

XOBIT
Offline
Зарегистрирован: 23.11.2018

нет, не мерцает.

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

нет, не мерцает.

проблема решена?

XOBIT
Offline
Зарегистрирован: 23.11.2018

b707 пишет:

XOBIT пишет:

b707 подскажи что не так?

// Simple I2C test for ebay 128x64 oled.
// Use smaller faster AvrI2c class in place of Wire.
// Edit AVRI2C_FASTMODE in SSD1306Ascii.h to change the default I2C frequency.
//
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

// 0X3C+SA0 - 0x3C or 0x3D
#define I2C_ADDRESS 0x3C

// Define proper RST_PIN if required.
#define RST_PIN -1

SSD1306AsciiAvrI2c oled;
//------------------------------------------------------------------------------
void setup() {

#if RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
#else // RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // RST_PIN >= 0
 
  // Call oled.setI2cClock(frequency) to change from the default frequency.

   oled.setFont(Adafruit5x7);
 
}
//------------------------------------------------------------------------------
void loop() 
{
  oled.clear();
  oled.print("Hello world!");
  delay(1000);
}

Мерцает раз в секунду.

а если oled.clear() в строке 32 закомментировать?

Надо было не закомментировать, а перенести в Setup.

b707
Offline
Зарегистрирован: 26.05.2017

XOBIT пишет:

Надо было не закомментировать, а перенести в Setup.

это вы мне советуете? :)

В Сетапе она нафик не нужна, так как метод begin() сам чистит экран.

У меня такое впечатление. что вы смысла всех этих команд не очень понимаете :) И на вопрос не ответили

XOBIT
Offline
Зарегистрирован: 23.11.2018

Да не очень. Спсб.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Вообще-то 38 мс на передачу всего экрана - это нормально.

Считаем: частота - 400 МГц. Если включать всю служебную информацию, паузы, сигналы начыала и окончагия передачи и пр, это 50 кбайт/с. Объем буфера 1 киб = 1.024 кб. Т.е. если бы мы передавали только нужную информацию, то скорость могла бы быть 1000/50*1.024=20.5 мс. Ну а если добавить служебную, паузы и т.п. - 38 вполне реально.

В качестве альтерниативы рискну предложить на пробу свою библиотечку: http://arduino.ru/forum/proekty/asoled-kompaktnaya-biblioteka-dlya-oled-displeya-128kh64-s-kirillitsei-utf-8