OLED_I2C и millis() или 1000mls = 2.4 сек
- Войдите на сайт для отправки комментариев
Здравствуйте.
При работе с Arduino nano v.3.0, библиотекой OLED_I2C и дисплеем 128X64 OLED Display 0.96" I2C заметил большую задержку по времени работы скетча. Путём эксперементов упёрся в функцию millis();
Для теста залил скетч мигание светодиодом.
01 | /* Blink without Delay – мигаем без задержки :) |
02 | * |
03 | * Включение/выключение светодиода, подключённого к |
04 | * цифровому порту, без использования функции delay(). |
05 | * Это означает, что «одновременно» можно выполнять другой код, |
06 | * без прерывания на мигание светодиодом :) |
07 | * |
08 | * <a href="http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay" title="http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay" rel="nofollow">http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay</a> |
09 | */ |
10 | #include <OLED_I2C.h> |
11 |
12 | int ledPin = 13; // наш светодиод на 13 порту |
13 | int value = LOW; // предыдущее состояние светодиода |
14 | unsigned long previousMillis = 0; // здесь будет храниться время последнего изменения состояния светодиода |
15 | long interval = 1000; // интервал мигания в миллисекундах |
16 |
17 | void setup () |
18 | { |
19 | pinMode(ledPin, OUTPUT); // устанавливаем порт, как выход |
20 | } |
21 |
22 | void loop () |
23 | { |
24 | // проверяем – настало ли время изменить состояние светодиода |
25 | // для этого берём разность между текущим временем и |
26 | // временем последнего изменения состояния светодиода, |
27 | // а затем сверяем полученный интервал с нужным интервалом |
28 | // мигания – если больше – значит пора мигать ;) |
29 | if (millis() - previousMillis > interval) { |
30 | previousMillis = millis(); // запоминаем текущее время |
31 | // если светодиод был выключен – включаем и наоборот :) |
32 | if (value == LOW) |
33 | value = HIGH; |
34 | else |
35 | value = LOW; |
36 |
37 | digitalWrite(ledPin, value); |
38 | } |
39 | } |
Всё отлично, 10 переключений за 10 секунд.
Добавляем библиотеку OLED_I2C и подключаем дисплей:
01 | /* Blink without Delay – мигаем без задержки :) |
02 | * |
03 | * Включение/выключение светодиода, подключённого к |
04 | * цифровому порту, без использования функции delay(). |
05 | * Это означает, что «одновременно» можно выполнять другой код, |
06 | * без прерывания на мигание светодиодом :) |
07 | * |
08 | * <a href="http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay" title="http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay" rel="nofollow">http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay</a> |
09 | */ |
10 |
11 | #include <OLED_I2C.h> |
12 | OLED myOLED(SDA, SCL); |
13 | extern uint8_t SmallFont[]; |
14 |
15 | int ledPin = 13; // наш светодиод на 13 порту |
16 | int value = LOW; // предыдущее состояние светодиода |
17 | unsigned long previousMillis = 0; // здесь будет храниться время последнего изменения состояния светодиода |
18 | long interval = 1000; // интервал мигания в миллисекундах |
19 |
20 | void setup () |
21 | { |
22 | pinMode(ledPin, OUTPUT); // устанавливаем порт, как выход |
23 | |
24 | myOLED.begin(); |
25 | myOLED.setFont(SmallFont); |
26 | } |
27 |
28 | void loop () |
29 | { |
30 | myOLED.clrScr(); |
31 | myOLED.print( "QWERTY" , RIGHT, 0); |
32 | myOLED.update(); |
33 | |
34 | // здесь можно поместить свой код для постоянного выполнения |
35 |
36 | // проверяем – настало ли время изменить состояние светодиода |
37 | // для этого берём разность между текущим временем и |
38 | // временем последнего изменения состояния светодиода, |
39 | // а затем сверяем полученный интервал с нужным интервалом |
40 | // мигания – если больше – значит пора мигать ;) |
41 | if (millis() - previousMillis > interval) { |
42 | previousMillis = millis(); // запоминаем текущее время |
43 | // если светодиод был выключен – включаем и наоборот :) |
44 | if (value == LOW) |
45 | value = HIGH; |
46 | else |
47 | value = LOW; |
48 |
49 | digitalWrite(ledPin, value); |
50 | } |
51 | } |
И начинаются чудеса: 10 переключений за 24 секунды. Если удалить строку №32 myOLED.update(); всё работает нормально.
Если в loop добавить delay(200); то 10 переключений = 12 секунд 0_о
Залил этот скетч в Ардуино МЕГА 2560 ... 10 переключений светодиода за 62 секунды!
Не могу понять почему такой разброс по времени, почему delay влияет на millis()?
Поиск по "OLED_I2C и millis()" ничего не дал. Ткните пожалуйста где копать, это мой первый проект, знаний катастрофически не хватает.
Чудеса были бы если бы обновление дисплея происходило мгновенно.
а так не вижу чудес.
Если в loop добавить delay(200); то 10 переключений = 12 секунд 0_о
.. почему delay влияет на millis()?
Т.е. Реальное время работы == "Количество миллисекунд с момента начала выполнения программы" + время выполнения кода + время опроса-ответа устройств ?
Тогда почему строка delay(200); сокращает время работы на 12сек, в два раза?
Хотя должна увеличить на 0.2 сек.
Потому что видимо иногда ваш интервал равен заданому интервалу а не больше. И становится больше только на следующий круг.
хотите четко по времени, изучайте прерывания.
А если отключить обновление дисплея то сразу 0% совпадений интервала?
Нелогично.
Опять же delay(200)?
Как я понимаю millis() привязан к тактовой частоте, почему увеличивается период импульсов?
Читал что если процессор работает на 8 а не 16 мегагерцах, то это отражается и на millis() т.к. соотношение расчитано на 16. В два раза было бы понятно, но соотношение плавает и зависит от фазы луны.
И начинаются чудеса: 10 переключений за 24 секунды. Если удалить строку №32 myOLED.update(); всё работает нормально.
Аяя... диво дивное! Криворукое, жопомозгое! Очередной автор либы срал на вся и всё вокруг, запретил прерывания где смог достать и закомитил...
Код в студию.. Что там от noInterrupts() и до interrupts() по времени? Все прерывания таймера слиплись в одно, вот и время потерялось.
01
void
OLED::update()
02
{
03
noInterrupts();
04
_sendTWIcommand(SSD1306_SET_COLUMN_ADDR);
05
_sendTWIcommand(0);
06
_sendTWIcommand(127);
07
08
_sendTWIcommand(SSD1306_SET_PAGE_ADDR);
09
_sendTWIcommand(0);
10
_sendTWIcommand(7);
11
12
if
(_use_hw)
// Send TWI Start
13
{
14
// Send start address
15
TWCR = _BV(TWEN) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
16
while
((TWCR & _BV(TWINT)) == 0) {};
17
TWDR = SSD1306_ADDR<<1;
18
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA);
19
while
((TWCR & _BV(TWINT)) == 0) {};
20
TWDR = SSD1306_DATA_CONTINUE;
21
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA);
22
while
((TWCR & _BV(TWINT)) == 0) {};
23
}
24
else
25
{
26
_sendStart(SSD1306_ADDR<<1);
27
_waitForAck();
28
_writeByte(SSD1306_DATA_CONTINUE);
29
_waitForAck();
30
}
31
32
for
(
int
b=0; b<1024; b++)
// Send data
33
if
(_use_hw)
34
{
35
TWDR = scrbuf[b];
36
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA);
// Clear TWINT to proceed
37
while
((TWCR & _BV(TWINT)) == 0) {};
// Wait for TWI to be ready
38
}
39
else
40
{
41
_writeByte(scrbuf[b]);
42
_waitForAck();
43
}
44
45
if
(_use_hw)
// Send TWI Stop
46
TWCR = _BV(TWEN)| _BV(TWINT) | _BV(TWSTO);
// Send STOP
47
else
48
_sendStop();
49
interrupts();
50
}
Спасибо большое за помощь, как раз начал изучать прерывания, попробую разобраться зачем отключены прерывания.
P.S. Спасибо что вернули веру в специалистов, грешным делом подумал что все такие же "програмисты" как я и Puhlyaviy
Убрал строки noInterrupts() и interrupts() и чудо исчезло, теперь 1000мс == 1 сек.
Возможно автор библиотеки отключил прерывания чтоб небыло задержки передачи данных в дисплей в блоке "Send TWI"
Doozer, вооьще-то Вы изначально неправильно используете millis().
Для начала перепишите фрагмент кода примерно так:
1
long
CurrentTime = millis();
2
3
if
(CurrentTime - previousMillis > interval)
4
5
{
6
7
previousMillis += interval;
Doozer, вооьще-то Вы изначально неправильно используете millis().
Для начала перепишите фрагмент кода примерно так:
1
long
CurrentTime = millis();
2
3
if
(CurrentTime - previousMillis > interval)
4
5
{
6
7
previousMillis += interval;
Лучше попробуй остановить милис методом запрета прерываний. Я что-то нигде не нашел про такой фокус. А ардуино нет под рукой.
Пишут что прерывания запрещаются но не все. И милисс морозиться только при обработке прерываний.
Код, первый попавшийся в поиске по millis().
С вами согласен, ещё бы заменил if else на digitalWrite(ledPin, !digitalRead(ledPin));
Знаний не хватает для этого. Пока меня устраивает выход с отменой запрета. Код пишу для себя, поэтому сойдёт и так.
как я понимаю милс привязан к прерыванию по таймеру. если не ошибаюсь 1-го. а оно может быть иногда и запрещено локально(только оно) или глобально(все прерывания).
Я бы рекомендовал так. http://arduino.ru/forum/proekty/net-sosed-ili-domofon-avtomat
Пока меня устраивает выход с отменой запрета.
Код пишу для себя, поэтому сойдёт и так.
Код работает месяц. Глюков не замечено.
И слава Богу. Но понять его логику (для чего он это делал) всё же нелишне. Хотя, я бы на код посмотрел (сейчас смотреть не хочу - "не моя война"). Если код выглядит как профессиональный, то старался бы понять, если нет - не парился бы. Если писал непрофессионал, то вполне вероятно, что отключил безо всякой причины :)
Нормально там все без прерываний. Наверно его смущало что програмная реализация I2C и тайминги увеличатся при попадании прерывания на обмен. Но это для синхронной шины до лампочки. Я себе либку писал для этого экрана тоже с програмным I2C, без запрета прерываний и прямо щас крутится месяц в режиме 24*7 (это второй экран для фоторамки - на одном фотка а на этом надпись к ней ). Ни одного глюка. К стати по описанию I2C у экрана 400кГц а в реале он держит максимум возможного от ардуинки 16МГц, вобще без задержек. А это выходит около 1МГц на шине. Может экземляр такой, может на температуре повалится, но пока без замечаний.