Медленная работа LiquidCrystal_I2C

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Клапауций!

Вентилятор то есть? Со свои дерьмом приходить или на месте найти можно? Про лопату даже не спрашиваю.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

wdrakula пишет:

Клапауций!

Вентилятор то есть? Со свои дерьмом приходить или на месте найти можно? Про лопату даже не спрашиваю.

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

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Arhat109-2 пишет:

Wdrakula, Вы точно нашли ошибку? ...

wdrakula пишет:

Или Вы не согласны, что забыли волатиль? ...

Архат! Мне, правда, неловко спрашивать, но Вы точно не баба? Ровно, как в той памятной истории про преобразование к int. Это очень по-женски - ни-за-что не признать косяк... смешно, право-слово.

Ладно... тема полностью исчерпана. Было и правда интересно. Пределы скорости lcd1602 с i2c регистром PCF8574 мы определили весьма точно. Больше не выжать.

На самом деле - вот ради таких тем и нужен форум, а не новичкам про диод и мосфет объяснять. ИМХО.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

wdrakula пишет:

Пределы скорости lcd1602 с i2c регистром PCF8574 мы определили весьма точно. Больше не выжать.

мы не определили максимально комфортную для восприятия скорость передачи.

потому, как максимальная нафиг не нужна.

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

wdrakula пишет:

Ладно... тема полностью исчерпана. Было и правда интересно. Пределы скорости lcd1602 с i2c регистром PCF8574 мы определили весьма точно. Больше не выжать.

Замечательно.

Я, правда, из-за периодической путаницы милисекунд с микросекундами и кучи вообще безразмерных цифр общего порядка не уловил.

Точнее, не уверен, что уловил.

Если я правильно понял, предел составляет где-то 120 мкс на символ на скорости 800, это так?

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Wdrakula, вы все уже достали читать по диагоналям. Ещё раз: косяки и расхождения версий реализаций компиляторов, а равно объявленные "фичами" в стандартах против полноценно строгого описания а-ля Алгол-68 или Ада - я НЕ СЧИТАЮ ОШИБКАМИ программистов. Да, их НАДО учитывать и, тот кто этого не сделал - ошибся. В этом плане я свою ошибку признал и там и тут отписал уже: "если Вы считаете это ошибкой". Но все что в библиотеке написано - сделано СОЗНАТЕЛЬНО. И, если оно "где-то не работает" это проблема того самого "где-то", ибо библиотека НЕ АДАПТИРОВАЛАСЬ к различным версиям плат, компиляторов и т.д и т.п. Тот кому требуется адаптация вполне способен сделать её САМОСТОЯТЕЛЬНО (ибо писано не для идиотов). Библиотека писалась для вполне конкретного применения на конкретном камне - Atmega2560 и его клонов, слегка адаптирована к ATmega328p и его клонам. Но только "слегка".

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

Andriano, не удивительно что Вы ничего не поняли. Остальные участники точно столько же "не поняли", просто "сделали вид". Давайте подведу "итоги":

1. На какой конкретно частоте способен работать I2C у PCF8574 - тут НЕ ВЫЯСНЕНО, ибо I2C-slave способен тормозить шину для обеспечения нужной ЕМУ скорости. То, что Мега-мастер спосоден И пытается отдавать на 800кгц - да, может .. но, в общем-то, "ни о чем". Тут эта сторона вопроса не выяснялась и вовсе.

2. LCD1602 способен принимать свои полубайты - БЫСТРО. Так быстро как может PCF8574, но только на 1 символ. Соответственно, правильная работа с этим экраном по I2C - это отправка пачкой обоих полубайт, для каждого из которых требуется отправить 3 байта в PCF8574 и это МОЖНО СДЕЛАТЬ ОДНОЙ ПОСЫЛКОЙ. Это самый быстрый способ работы с этим дисплеем. Wiring так не умеет.

3. Задержка отрисовки одного символа на моем дисплее составляет 4.1мсек или 4100 микросекунд. Это та пауза, которую Вы обязаны обеспечить если хотите прочитать отправленное на экране. В даташите на МК дисплея указано 37 микросекунд - это теоретический миниум .. у меня такое НЕ РАБОТАЕТ, но возможно вам повезло.. вот у Wdrakula он явно реагирует шустрее ..

4. Поскольку сам дисплей "экранирован" чипом PCF8574, то способа "узнать" готов ли дисплей принять следующий символ у вас практически нет. Паузу вы должны отрабатывать самостоятельно. Можно в лоб delay() как в моем примере, можно через прерывания по таймеру, можно ещё 100500 разными способами. Зависит от вашей программы, знаний, умений и навыков. Тут возможно есть способ "задрочить" PCF8574 так, чтобы можно было и читать 1 ногу дисплея .. но я этого не пробовал.

5. Скорости работы моего теста для 100х10 символов + позиционирование курсора (отправка +1 команды на каждые 10 символов): Время выдерживания паузы между посылками = 5500мсек; Время подготовки данных для отправки = 17мсек; Время отправки данных - НЕ ИЗМЕРЯЛОСЬ ибо проводилось через прерывания в процессе отработки паузы. Теоретическое время отправки подсчитано Logik-ом = 70мсек. для 800кГц, но поскольку реальная скорость интерфейса тут не замерялась вовсе - то неизвестно.

Собственно, 5500-70 = 5430мсек - это то время, которое ваш скетч способен потратить на свое усмотрение пока дисплей рисует символ. На каждый символ. В отличии от прямого способа отправки ногодрыгом у Wdrakula или Logik.

Вот, так как-то.

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

Arhat109-2, перечитайте, пожалуйста, свое сообщение. У меня стойкое ощущение, что Вы путаете мсек с мксек.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Перечитал, все верно. 1мсек - 1 миллисекунда = 1/1000 сек; 1мксек - 1 микросекунда = 1/1000 миллисекунды.

В Даташите указано время отрисовки символа действительно в микросекундах, но на самом деле он отрисовывает символ за 4.1 миллисекунды или 4100 микросекунд. И преодолеть этот барьер - никак. Интерфейс вполне способен работать на частотах мастера в 800кгц: сам мастер - позволяет, а слейв может в любой момент притормозить прием до приемлемой для себя скорости. И расходы на передачу, в сравнении с временем отклика - невелики. В даташите на дисплей и вовсе указано что принимать он может на скоростях аж до 1..2Мгц .. там характерные времена сигналов - 250..500 наносекунд. Только "толку-то"?

800кгц = 1.25 микросекунды на передачу 1 бита. * 9 бит * 7 байт (6+адрес) = 78,75мксек на посылку. Тест отправляет 100х(10+1) посылок 78.75 *1100 = 86625 мксек или 86.6мсек .. Logik давал цифирьку в 70. Это более точный расчет работы по прерываниям.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

Arhat109-2 пишет:

Перечитал, все верно. 1мсек - 1 миллисекунда = 1/1000 сек; 1мксек - 1 микросекунда = 1/1000 миллисекунды.

f8d0209399282de8ea2c52452fe2eb56.jpg

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

:) Да, весело получилось. Это не "минус", а тире .. типа "это". :)

Logik
Offline
Зарегистрирован: 05.08.2014

Arhat109-2 пишет:

Собственно, 5500-70 = 5430мсек - это то время, которое ваш скетч способен потратить на свое усмотрение пока дисплей рисует символ. На каждый символ. В отличии от прямого способа отправки ногодрыгом у Wdrakula или Logik.

Ох уж эти сказки..

Как в нашем коде, вот этом -

om=millis();

for (int j=0; j< 100; j++){

  lcdSetCursor(0,0);

  lcdWrite(s+ (j%25),10);

  }

 
m=millis()-om;

реализовано "скетч способен потратить на свое усмотрение"? 

Особенно учитывая что функции lcd... у Вас блокирующие! Несмотря на то, что под ними неблокирующие лежат. И зачем они там? )))) И весь выиграш ,о котором Вы пишите, в этих блокирующих lcd.. и седается ))) 

А теперь к вопросу почму они у Вас блокирующие.  Потому что если сделать их неблокирующими, то возникнет проблема хранения информуции о типе вызова и параметрах (назовем это контекст) в промежутке времени между вызовом функциии lcd.. и фактическим выводом на экран. Теперь обратим внимание на то, что взовы  lcd.. могут ити подряд и много, какраз как в нашем примере, и станет ясно что контекст нужно хранить в очереди и её размер вобщем то не ограничен какими либо разумными доводами. Вот и получим сотни байт ОЗУ (а если экран графический то и тысячи и даже сотни тысяч байт) и издержки на работу с очередью контекста (а там потребуется синхронизация её использования из прерывания и основного потока, и комуто из них прийдется ждать пока другой загружает/выгребает контекст). Что в сумме окажется  хуже чем прямой вывод. И по скорости и по ОЗУ и по сложности реализации.  

Так то вот!

ПС. Постарайтесь понять написаное выше, несмотря на свои эмоции и гордыню. У Вас банальный неверный выбор архитектуры, непродуманый подход.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

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

P.S. Тема в том ракусе что была поднята - исчерпана чуть более чем полностью. А поддерживать ваш срач - у меня нет никакого желания. За сим, таки откланяюсь тут. Разбирайтесь, перечитывайте .. всё есть.

Logik
Offline
Зарегистрирован: 05.08.2014

Ну раз среди ночи Вам так неспалось, что захотелось мне похамить то выводы очевидны:

1. Вы таки поняли свой про..б в архитектуре.

2. Отсутствие аргументов - отсутствие выхода с тупика. 

Действительно, говорить не о чем. Тема исчерпана.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

:) Перечитывайте. Тут все уже есть. :)

Logik
Offline
Зарегистрирован: 05.08.2014

Ниче нихто перечитівать небудет. Нет у Вас там ничерта толкового. И сказать Вам больше нечего ))

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ну вот почто Вы такой упертый "нечитатель"? :)

Ладно, давайте помогу:

1. Logik, писал: "Как в нашем коде, вот этом - ... реализовано "скетч способен потратить на свое усмотрение"? "

Ответ: это НЕ МОЙ код, а Wdrakula, который решил проверить скорость отрисовки ваще-то. Конечно в нем - НИКАК. Но это же не "весь код применения", так ведь? :)

2. Logik, писал: "Особенно учитывая что функции lcd... у Вас блокирующие! "

Ответ: Если Вы откроете lcd1602.h из библиотеки, то сможете обнаружить что все функции lcd..() по большей части являются макроопределениями, которые в конечном счете вызывают единственную функцию lcdSend(), которая в свою очередь вызывает twiWrite(), которая .. упс НЕ ЯВЛЯЕТСЯ блокирующей даже с включенным delay() в lcdSend()? а тупо устанавливает область и размер передачи и запускает обработчик прерывания. :)

Синхронность обработчика прерывания и основного потока здесь обеспечивается только большой задержкой в lcdSend(), которая гарантирует что к моменту повторного вызова lcdSend() не только закончится предыдущая передача, но и дисплей уже отрисует символ. И это - никак НЕ блокирующая работа, заметьте.

Да, и про это было сразу сказано в посту №17, последним предложением, но .. Вы похоже его не читали.

3. Logik писал: "Несмотря на то, что под ними неблокирующие лежат. И зачем они там?"

Ответ: Вы были просто невнимательны. Весь тест Wdrakula, работает НЕБЛОКИРУЮЩЕ. Об чем вам и было посоветовано: перечитайте. а вы .. "не буду" .. прямо как дите малое.

4. Logik писал: "И весь выиграш ,о котором Вы пишите, в этих блокирующих lcd.. и седается )))"

Ответ: нет. Весь выигрыш тут съедается оператором delay() внутри lcdSend(). Это легко поправить для конкретного применения. Разъяснение дано выше.

5. Logik писал: "А теперь к вопросу почму они у Вас блокирующие." и далее весь остальной бред из-за "читать не буду"

Они НЕБЛОКИРУЮЩИЕ. Перечитайте. Они даже в этом тесте работают НЕБЛОКИРУЮЩЕ. :)

6. Logik писал: "то возникнет проблема хранения информуции о типе вызова и параметрах (назовем это контекст) в промежутке времени между вызовом функциии lcd.."

Ответ: это вовсе необязательно. Необходимо и достаточно иметь признак (1 бит/байт) завершен ли предыдущий вызов вывода на дисплей и соответствующие проверки в коде "если дисплей ещё занят, то ..". Обычное автоматное программирование.

Ну и процитирую вас же:

ПС. Постарайтесь понять написаное выше, несмотря на свои эмоции и гордыню. У Вас банальный неверный выбор архитектуры, непродуманый подход.

Удачи. :)

Logik
Offline
Зарегистрирован: 05.08.2014

 

 писал: "Как в нашем коде,

Arhat109-2 пишет:

Ответ: это НЕ МОЙ код,

Разумеется не Ваш ))  Вы здесь не одни.  Выделю жирным может так просчитаете верно. "Как в нашем коде"

2. Logik, писал: "Особенно учитывая что функции lcd... у Вас блокирующие! "

Arhat109-2 пишет:

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

Вы конечно можете черное белым называть сколько угодно, даже большими буквами,  но выполнение лупа заблокировано до момента когда "закончится предыдущая передача". Это типичный блокирующий код. Причем кривой как турецкая сабля, за синзронизацию выполнения потоков (основног и прерывания) на основе ожидания. Разрабам за такое дают по рукам. т.к. либо будет ждать лишнего либо может недождатся. А еще и с делеем - нет слов, уровень новичка которого посылают на блинкбезделея. Позорище.

 

Arhat109-2 пишет:
. Весь выигрыш тут съедается оператором delay(). 
Наконец признали очевидное. 

Arhat109-2 пишет:
. Это легко поправить для конкретного применения. Разъяснение дано выше. 

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

Arhat109-2 пишет:

Ответ: это вовсе необязательно. Необходимо и достаточно иметь признак (1 бит/байт) завершен ли предыдущий вызов вывода на дисплей и соответствующие проверки в коде "если дисплей ещё занят, то ..". Обычное автоматное программирование.

Вот это и была бы неблокирующая реализация. Но Вы как всегда не хотите продумывать последствия.

1. автоматное програмирование, в котором каждый вызов lcd... отдельное состояние - это мрак.

2. при таком подходе в реальном приложении скорость вывода еще ниже окажется, т.к. основной цикл совсем не обязательно прокрутится за 5мсек, там как повезет может 4, может 20 а то и 100.  И все отклонения будут означать простой вывода и снижение скорости.

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

Но вы делайте, делайте, потом еще поржем и мокнем.  

ssss
Offline
Зарегистрирован: 01.07.2016

Arhat109-2 пишет:

У Вас банальный неверный выбор архитектуры, непродуманый подход.

"Не ругайтесь, девочки!"(с) ))))))))

Берите правильную архитектуру, тулите полный буфер, заводите хардварный вывод и вопрос отпадёт сам собой. Это ж просто красота! Меняем буфер дисплея когда хотим, запускаем обновление экрана дисплея когда хотим. А вы тут со своими поднадоевшими микро и миллисекундами. Срамота! )))))))

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Окей, пусть будет " в нашем коде", тоже оказался невнимателен.

Вы конечно можете черное белым называть сколько угодно, даже выделять жирным,  но покажите где выполнение лупа заблокировано до момента когда "закончится предыдущая передача"? Именно до этого момента. :)

Logik, писал: "А еще и с делеем - нет слов, уровень новичка которого посылают на блинкбезделея. Позорище."

Вы наивны как дитё. Это - пример, и написан он был вовсе не для демонстрации работы с дисплеем, а как пример реализации работы с моим twi интерфейсом. Пример как раз для тех, кто пользуется делеем. Кстати, неблокирующее исполнение delay() есть в wiring .. функция yield() кажется так. Ознакомьтесь, иногда тоже полезна.

Но, в целом почти разобрались, уже лучше.

Самый простой неблокирующий подход тут - использование функции yield() для выполнения кода во время отработки паузы отрисовки. В моем delay() её нет, но никто не мешает дополнить. Этим легко и ненапряжно опровергается ваше утверждение про "блокирующую" работу теста, а заодно и все ваши попытки хамить от незнаний. Осваивайте, это просто один из 100500 способов как использовать время ожидания.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Кажется дошло. У вас неверное понимание "блокирующего" и "неблокирующего" кода. попробую помочь:

Любое последовательное исполнение - есть "блокирующий" код для всех последующих команд. Так например:

A=B+C*3  // разворачивается у компилятора в последовательность:

A=C;     // тут сложение блокировано умножением
A=C*3;   //  и реализовать его "до" завершения умножения никак нельзя
A=A+B;

// аналогично вывод:
print("ABC")

// в конечном счете при выполнении будет выглядеть так:
print("A");
print("B");
print("C");
// и выполнить вывод символа "С" не представляется возможным
// ранее вывода "B" и "A"

// это последовательное или блокирующее исполнение. Согласны?

Теперь смотрим на наш тест и видим, что ничего, кроме вывода строк символов он не делает, соответственно сам по себе тест является последовательным или блокирующим исполнением. Нельзя вывести второй символ ранее, чем выведен первый. Нельзя вывести следующую строку раньше чем выведена предыдущая. Согласны?

А вот давайте теперь посмотрим как происходит сам вывод И отправка данных в дисплей по I2C:

// пост №29:
for (int j=0; j< 500; j++)
{
  lcdSetCursor( 0,0 );         // отправка команды (1 посылка)
  lcdWrite( s+(j%25), 10 );  // отправка 10 символов (10 посылок)
}

// Разворачивая вызовы (см. пост №14), получаем для тела цикла:
lcdPrepare(LCD_SHOW_RAM, 0); // lcdsetCursor() -> lcdCommnad()
twiWrite(0x27, lcdBuffer, 6); // .. lcdSend()
delay(LCD_WAIT_1);

twiMode |= TWI_SEND_STOP;  // lcdWrite()
while(10--)                                // тут длина строки
{
  lcdPrepare( *b++, 0);               // .. lcdWrite1()
  twiWrite(0x27, lcdBuffer, 6);     // .. .. lcdSend()
  delay(LCD_WAIT_1);
}

Обратите внимание, что строго последовательная задача посимвольного ВЫВОДА решена строго последовательно. А вот сам "вывод данных" делается неблокирующее: через запуск автомата I2C функцией twiWrite(), которая .. ничего не ждет а тупо запускает автомат, если он свободен. Соответственно, Вы ошибаетесь и не единожды, повторяя "вывод на дисплей - блокирующий". Нет, работа интерфейса I2C, которая тут и есть "вывод на дисплей" - делается не блокирующее а "параллельно" с основным циклом.

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

/**
 * Замена для delay() -- вызывает опрос датчиков линии и главный мозг робота
 * пока идет отрисовка текущих параметров на экран
 * getSensors() -- получает и усредняет датчики "бегущим средним" по 20шт - папина
 * brain() -- мой главный мозг моего робота запускается каждые 2 милисек!
 */
#define delay(vrema)                               \
{                                                  \
  static uint8_t t = (uint8_t)millis();            \
  while( (uint8_t)millis() - t < ((uint8_t)vrema ) \
  {                                                \
    getSensors();                                  \
    brain();                                       \
  }                                                \
}

#include "lcd1602.h"

Уж не помню, с рабочей версии стащил или какой .. осваивайте "неблокирующую работу с дисплеем.

P.S. Забыл указать, что в loop() есть вызов WriteAll() -- функция которая периодически обновляет показания и состояния автоматов на дисплее. Ну и конечно же как и положено, все реализовано на широком применении глобалов. :)

Такой подход позволил ему видеть онлайн как реагирует робот на изменение обстановки при движении по линии на двух датчиках: верхняя строка дисплея показывала левый и правый датчики, а на нижней выводились скорости моторов. В правой части экрана на 2-х строках рисовался "символ" - решение мозга "куда едем" псевдографикой.

ssss
Offline
Зарегистрирован: 01.07.2016

Arhat109-2 пишет:

Нет, работа интерфейса I2C, которая тут и есть "вывод на дисплей" - делается не блокирующее а "параллельно" с основным циклом.

Вы реально бредите в попытке оправдаться и очень сильно. И с этим нужно что-то делать.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Гы. Ты сначала посмотри и дочитай что тут обсуждается а потом сри в тему. Ещё один говнокодер-проприетарщик.

Logik
Offline
Зарегистрирован: 05.08.2014

Arhat109-2 пишет:

Кажется дошло. У вас неверное понимание "блокирующего" и "неблокирующего" кода. попробую помочь:

ненадо. Мое понимание строго согласно https://ru.wikipedia.org/wiki/Асинхронный_ввод-вывод

Именно:

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

Т.е. в нашем случае lcd... должен завершаются до окончания вывода на экран, и существенно ранше. И независимо от того, что там выводится от предыдущих вызовов. Это принципиально возможно, но сложно, я выше писал как. 

Arhat109-2 пишет:

Нельзя вывести следующую строку раньше чем выведена предыдущая. Согласны?

Нет. На уровне вызовов это возможно lcd.., если работа неблокирующая асинхронная. Как будет разгребатся это Ваша либа - Ваши проблемы. Пользователям либы об этом знать не нужно.  И не надо тут левый код постить, разварачивать как в либе,  он не интересен  никому. Я и так понимаю лучше Вас с какими проблемами Вы там сталкиваетесь, даже знаю как их решать и что это в конце концов тупик.  Тест вывода, аналогичный сделаным еще может гляну.  А тем более постить откровенное шопопало:

/**
 * Замена для delay() -- вызывает опрос датчиков линии и главный мозг робота
 * пока идет отрисовка текущих параметров на экран
 * getSensors() -- получает и усредняет датчики "бегущим средним" по 20шт - папина
 * brain() -- мой главный мозг моего робота запускается каждые 2 милисек!
 */
#define delay(vrema)                               \
{                                                  \
  static uint8_t t = (uint8_t)millis();            \
  while( (uint8_t)millis() - t < ((uint8_t)vrema ) \
  {                                                \
    getSensors();                                  \
    brain();                                       \
  }                                                \
}

#include "lcd1602.h"

 

Где в этом коде вобще работа с экраном? Постите хуйню, в расчете что всем будет пофиг и зачтется? Вы в натуре - баба? Биологическая или так просто, по жизни? 

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Logik пишет:

Вы в натуре - баба? Биологическая или так просто, по жизни? 

Я уже спрашивал... он ушел от ответа ;) ;) ;)...

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

Да, к теме, а не к дерьму на вентиляторе, хотел добавить:

Я довел софварный вариант до 720 мс на 1000 символов. Но вот в чем прикол: получается, что PCF8574, все таки, не держит более 100 МГц. Странно, но я проверил код Архата. Он действительно устанавливает 800КГц в регистры. Нужно посмотреть осцилом, Я сам пока не могу - жду нового ослика с али. В софтовом варианте обмен прекращается на времени удежржания уровня SCL меньше 4.5 мкс.Это не столь важно для жизни, но просто интересно! ЧОрт возьми!

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Пипец как достали долбо*бы. Разберись сначала что выложено а потом поливай дерьмом, а не наоборот. Сами как бабы, ничего кроме как обосрать оппонента не умеют, а туда же! Вот уж действительно "метать бисер" .. перед кем?!?

.. афигеть! "он обнаружил" .. обнаружь и все остальное.

Разберетесь - пишите.

 

Logik
Offline
Зарегистрирован: 05.08.2014

wdrakula пишет:

Он действительно устанавливает 800КГц в регистры. Нужно посмотреть осцилом, 

Я особо и не сомниваюсь, что 800КГц использует. Просто данные идут не непрерывным потоком а с паузами, и эти паузы очень большие. А чтоб обеспечить непрерывный поток надо или буфер большой, т.е. много ОЗУ чего нету или непрерывно и быстро из основного потока получать, а значить основной поток не сможет заниматся другими задачами и просто исчезает смысл всей затеи с аппаратными 800КГц на прерываниях. То что было отличным решением на 100 стало не очень на 800 )))

Arhat109-2 пишет:

 ничего кроме как обосрать оппонента не умеют

еще умеем быстрый код писать ;) См. результаты тестов. 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Не умеете. Ничего не умеете кроме как срать на форуме и ногордыгами заниматься. Уже проверено неоднократно. Вы даже тут разобраться не способны, митрофанушки.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

нашел время, перепроверил ваш тест:

Какие же Вы, Logik и Wdrakula оба ЗАСРАНЦЫ! Слов нет. Мало того что Ваш тест не компилируется, ибо массив символов должен быть объявлен не в 10, а 11 символов, так ещё и врете. Это тест с другого дисплея (не моего). Задержка LCD_WAIT_1 установлена в 50 МИКРОСЕКУНД с заменой вызова delay() на delayMicroseconds()..

только бабы могут насрать и не извиниться. Ибо Женщина - всегда права. .. какие чмори.. слов нет.

Это - код вашего теста. Исправлена ошибка в размере массива.

#include "arhat.h"

//#define TWI_ON    15    // Debug: Включить все режимы обработчика I2C
//#include "arhat_twi.h"  // Debug: Подключить явно тут с заданным комплектом режимов

#define LCD_I2C_SPEED 800000 // Установить типовую скорость (внутри задано 800кгц!)
#include "lcd1602.h"

// ************* Example *********** //

void setup() {
    lcdSetup(0x27, 16, 2, 1);
    lcdWrite("Hello, Arhat!", 13);
    lcdSetCursor(0,1);
    lcdWrite("Bye, Bye..", 10);
}

void loop()  {
const char *s=(char *)"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char ss[11] = "          ";
uint32_t  m,om;
int l=0;

delay(3000);

    lcdSetCursor(0,1);
    lcdWrite("                ", 16);

om=millis();
for (int j=0; j< 100; j++){
  lcdSetCursor(0,0);
  lcdWrite(s+ (j%25),10);
  }

m=millis()-om;

itoa((int)m,ss,10);
  lcdSetCursor(0,1);
  while(ss[++l]);
  lcdWrite(ss,l);


}

А это код lcd1602.h с заменой константы и функции задержки:

/**
 * Набор примитивов для работы с дисплеем LCD1602 или иными на базе 1,2 контроллеров HD44780
 * 1 контроллер держит дисплеи 8х2 или 16х1; 2 контроллера держат дисплеи 16х2. Программируются вроде как одинаково.
 *
 * !!! Пока только базовые примимтивы .. в развитии.
 *
 * Тайминги циклов шины связи с МК HD44780:
 * 1. Предустановка RS,R/W:          >60нсек от фронта E (>80нсек х2)
 * 2. Длительность строба записи E:  >500нсек (x2 устр.: LCD1602!) с периодом >1мксек
 * 3. Предустановка данных D7..D0:   >300нсек от спада E
 * 4. Удержание D7..D0, RS, R/W:     >20нсек после спада E (1), >300нсек (x2 контроллера!)
 * 5. Время исполнения команд в среднем <=37мксек -- в даташите НЕВЕРНО! Реально около 4.1 миллисекунды !!!
 *
 * Длительность 1 передачи по I2C на 100кГц = 90мксек! В реальности тянет до 800кгц..
 * Нельзя ничего читать с дисплея (R/W===0)! А ведь есть BF - "бит готовности" ..
 *
 * Отсюда передача возможна только побайтная:
 * сразу готовим посылку каждой тетрады в виде 3-х байт: установка, строб, сброс строба,
 * и сразу же отправляем одной пачкой обе тетрады. Итого буфер на передачу 1 байта = 6.
 *
 * @author Arhat109-20160402. arhat109@mail.ru
 * @license:
 *   1. This is a free software for any using and distributing without any warranties.
 *   2. You should keep author tag with any changes. May be with adding.
 *   Это свободное ПО для любого использования без каких-либо гарантий и претензий.
 *   Требуется сохранять тег @author при любых изменениях. Можете дописать свой.
 */
#ifndef _LCD1602_H_

#ifndef _ARHAT_TWI_H_
  #define TWI_ON           TWI_MASTER_TX         // only Master-Transmit mode! Not use other mode in this!
  #include "arhat_twi.h"
#endif // _ARHAT_TWI_H_

#ifndef LCD_I2C_SPEED
  #define LCD_I2C_SPEED    800000
#endif

// LCD on chip HD44780 команды или их коды операций:
#define LCD_CLEAR          0x01
#define LCD_HOME           0x02
#define LCD_SHIFTS         0x04  // часть!
#define LCD_SHOWS          0x08  // часть!
#define LCD_MODES          0x20  // часть!
#define LCD_FONT_RAM       0x40  // адрес в таблицу шрифтов 6бит
#define LCD_SHOW_RAM       0x80  // адрес текущей позиции 7бит

#define LCD_CURSOR_LEFT    0x10  // команда сдвига курсора влево
#define LCD_CURSOR_RIGHT   0x14  // команда сдвига курсора вправо
#define LCD_ROW_LEFT       0x18  // команда сдвига всей строки влево
#define LCD_ROW_RIGHT      0x1C  // команда сдвига всей строки вправо

// for LCD_SHIFTS:
#define LCD_INC            0x02  // Инкремент: сдвиг вправо при записи байта
#define LCD_SHIFT_ON       0x01  // сдвиг и строки тоже при записи байта (в обратную сторону! Курсор как-бы на месте)

// for LCD_SHOWS:
#define LCD_SHOW_ON        0x04  // включить отображение
#define LCD_CURSOR_UL      0x02  // включить курсор подчерком
#define LCD_CURSOR_BLINK   0x01  // включить мигание курсора

// for LCD_MODES:
#define LCD_8BIT           0x10  // шина 8бит/4бит
#define LCD_2LINE          0x08  // память 2/1 строки
#define LCD_5x10           0x04  // фонт 5х10 / 5х8 точек

// Пустышки для полноты картины:
#define LCD_DEC        0 // сдвиг курсора/экрана влево
#define LCD_SHIFT_OFF  0 // сдвиг строки отключен
#define LCD_SHOW_OFF   0 // дисплей выключен
#define LCD_CURSOR_OFF 0 // курсор выключен
#define LCD_5x8        0
#define LCD_1LINE      0 // только одна строка (1 буфер 80символов, иначе 2х40)
#define LCD_4BIT       0 // 4бита: каждый байт идет 2 посылками "подряд" по линиям D7..D4

// Маски бит данных (D3,D2,D1,D0), управляющие сигналами напрямую:
#define LCD_BACK      B00001000  // bit D3 управляет подсветкой экрана при каждом выводе в I2C!
#define LCD_E         B00000100  // Enable bit - Strobe for read/write >230msec.
#define LCD_RW        B00000010  // Read/Write bit - ==0 всегда!
#define LCD_RS        B00000001  // Команда(0) или данные(1)

#define LCD_WAIT_BOOT0       15  // тиков Т0:  >15мсек пауза на включение
#define LCD_WAIT_BOOT1        5  // тиков Т0:  >4,1мсек пауза повторной 0x30
#define LCD_WAIT_BOOT2      400  // (по 250нсек) >100мксек пауза третьей 0x30
#define LCD_WAIT_1           50  // тиков Т0: 4мксек - мало! (>37мксек пауза на команду "в среднем")

/**
 * Отправка буфера дисплею по 4-битному протоколу с "ручным" стробированием
 * и типовой задержкой на выполнение команды
 */
#define lcdSend(len)                               \
{                                                  \
  twiWrite(lcdAddress, lcdBuffer, (uint8_t)(len)); \
  delayMicroseconds(LCD_WAIT_1);                               \
}

#define lcdWrite1(d)     {lcdPrepare((uint8_t)(d), 1); lcdSend(6);}
#define lcdCommand(p)    {lcdPrepare((uint8_t)(p), 0); lcdSend(6);}
#define lcdClear()       {lcdCommand(LCD_CLEAR); delay(16);}
#define lcdHome()         lcdCommand(LCD_HOME)
#define lcdCursorLeft()   lcdCommand(LCD_CURSOR_LEFT)
#define lcdCursorRight()  lcdCommand(LCD_CURSOR_RIGHT)
#define lcdRowLeft()      lcdCommand(LCD_ROW_LEFT)
#define lcdRowRight()     lcdCommand(LCD_ROW_RIGHT)
#define lcdFontAddress(f) lcdCommand(LCD_FONT_RAM | ((uint8_t)(f)&0x3F))
#define lcdShowAddress(a) lcdCommand(LCD_SHOW_RAM | ((uint8_t)(a)&0x7F))

/**
 * Установить абсолютную позицию курсора
 */
#define lcdSetCursor(_col,_row) \
    (lcdCommand(LCD_SHOW_RAM | ((((_row)? 0x40 : 0x00) + (uint8_t)(_col)) & 0x7f)))

/**
 * Установить позицию записи в память шрифтов
 */
#define lcdGoChar5x8(_ch) (lcdCommand(LCD_FONT_RAM | ((((uint8_t)(_ch))<<3)&0x3f)))

#ifdef __cplusplus
  extern "C" {
#endif

    uint8_t lcdModes  = LCD_MODES | LCD_8BIT;               // PowerON: 8bit mode, 1 line, 5x8 font
    uint8_t lcdShifts = LCD_SHIFTS | LCD_INC;               // PowerON: cursor shift to right, not screen!
    uint8_t lcdShows  = LCD_SHOWS;                          // PowerON: show off, cursor off, blink off
    uint8_t lcdBackLight = LCD_BACK;                        // PowerON: Backlight is ON

    uint8_t lcdAddress = 0x27;       // for myLCD1602 parameters as default
    uint8_t lcdCols = 16;
    uint8_t lcdRows = 2;
    uint8_t lcdBuffer[6];            // Буфер для потоковой тетрадной записи в дисплей

    /**
     * Подготовка байта в буфер потоковой записи
     * @param _rs=[0 -- команда,!0 -- данные]
     */
    void lcdPrepare(uint8_t _data, uint8_t _rs)
    {
        uint8_t nibble = (_data&0xf0) | lcdBackLight;

        if( _rs ) nibble |= LCD_RS;
        lcdBuffer[2] = lcdBuffer[0] = nibble;
        nibble |= LCD_E;
        lcdBuffer[1] = nibble;

        nibble = ((_data&0x0f)<<4) | lcdBackLight;

        if( _rs ) nibble |= LCD_RS;
        lcdBuffer[5] = lcdBuffer[3] = nibble;
        nibble |= LCD_E;
        lcdBuffer[4] = nibble;
    }

    /**
     * Вывод строки заданной длины (буфера) на экран.
     * Повторная установка скорости работы дисплея по I2C и режима (мало ли кто и как работает ещё)
     */
    void lcdWrite(const void *buf, uint8_t len)
    {
        uint8_t *_b = (uint8_t *)buf;
        uint8_t  _l = len;

        twiMode |= TWI_SEND_STOP;
        while(_l--)
            lcdWrite1(*_b++);
    }

    /**
     * Полная перенастройка экрана по текущим значениям режимов.
     */
    void lcdInit()
    {
        lcdCommand(lcdModes);           // повторяем режим: 4 бита, но уже + сколько строк и какой шрифт
        lcdCommand(lcdShows);           // включаем дисплей и курсор
        lcdCommand(lcdShifts);          // настройка режимов сдвига курсора/экрана
        lcdClear();                     // очень долго очищаем экран (>15.2мсек)
        lcdHome();
    }

    /**
     * for setup(): powerON initialization for LCD with Hitachi HD44780 (up to 40 cols, 2 rows)
     */
    void lcdSetup(uint8_t _address, uint8_t _cols, uint8_t _rows, uint8_t _backLight)
    {
        lcdAddress   = _address;
        lcdCols      = _cols;
        lcdRows      = _rows;
        lcdBackLight = (_backLight? LCD_BACK : 0);
        lcdModes     = LCD_MODES;
        lcdShifts    = LCD_SHIFTS | LCD_INC;
        lcdShows     = LCD_SHOWS  | LCD_SHOW_ON;

        #ifndef TWI_SETUP
        twiSetup(LCD_I2C_SPEED, TWI_READY|TWI_SEND_STOP); // только если не запущен ранее!
        #define TWI_SETUP "lcd1602.h"                     // фиксим, что I2C инициализируется этой библиотекой
        #endif

        if( _rows>1 )          { lcdModes |= LCD_2LINE; } // else 1 line

        // @see datasheet: power on sequence
        {
            lcdPrepare(0x30, 0);          // HD44780: RS=0 запись в регистр команд дисплея.

            delay(LCD_WAIT_BOOT0);        // powerOn it needs wait from 15 upto 50msec
            lcdSend(3);                   // 1x3 отправка: по включению режим 8-бит - вторая тетрада не нужна!
            delay(LCD_WAIT_BOOT1);        // ждем >4.1ms
            lcdSend(3);                   // вторая отправка согласно даташит 8-бит..
            delayMicro16(LCD_WAIT_BOOT2); // ждем >100us
            lcdSend(3);                   // третья отправка 8-бит..

            lcdPrepare(0x20, 0);          // и только теперь переводим в режим 4-бита
            lcdSend(3);                   // и только теперь режим 4-бита и отправляем тетрады попарно!
        }
        lcdInit();
        delay(500);                     // чтобы было заметно глазом.. один раз.
    }

#ifdef __cplusplus
  } // extern "C"
#endif

#endif // _LCD1602_H_

Можете перепроверить.

ssss
Offline
Зарегистрирован: 01.07.2016

******* // ждем >4.1ms

Это чё за хрень? )))))))))))))))

ssss
Offline
Зарегистрирован: 01.07.2016

Arhat109-2 пишет:

Не умеете. Ничего не умеете кроме как срать на форуме и ногордыгами заниматься.

А ардуина умеет по другому? Не пугайте не смешите, не врите! ))))))))

ssss
Offline
Зарегистрирован: 01.07.2016

Arhat109-2 пишет:

Любое последовательное исполнение - есть "блокирующий" код для всех последующих команд.

Башорг просто плачет от такого бреда! )))))))))))))))

ssss
Offline
Зарегистрирован: 01.07.2016

Arhat109-2 пишет:

Теперь смотрим на наш тест и видим, что ничего, кроме вывода строк символов он не делает, соответственно сам по себе тест является последовательным или блокирующим исполнением. Нельзя вывести второй символ ранее, чем выведен первый. Нельзя вывести следующую строку раньше чем выведена предыдущая. Согласны?

С вашим очередным бредом? Конечно нет! )))))))))))))))

В прерывании или через ДМА можно вывести что угодно и как угодно, хоть символ, хоть строку. Так что сам по себе последовательный код не является блокирующим, он просто выполняет последовательность команд согласно алгоритму, как ему и положено.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Архат! Ты псих совсем или ...нулся? Я давно на твоей библиотеке не 140, а 116 получил. Причесав ее. Я хоть раз писал, что мне твоя библиотека не нравится???? Совсем рехнулся? Пустырнику попей! У меня есть вопросы по релизу аппаратного и2с, но не к тебе, а к производителю. Например держит оно 800 или нет? В даташите написано до 400, потому и хочу осликом посмотреть. Но у тебя - все нормально и правильно, успокойся уже!

-----

Наезд был исключительно за твой тупой апломб и непризнание очевидных ошибок, которые пришлось править самому. Но неблокирующий мастер - очень хорош. Логик спорит о том,что он нахер никому не нужен. Возможно. Но, как чисто програмистская задача, ты написал хороший драйвер и2с.

Что тебя на говно пробило? Странный ты какой-то.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

А вот и нашлось объяснение "якобы моей ошибки", которую исправляет "волатил" у Wdrakula: http://stackoverflow.com/questions/28708234/how-to-fix-defined-in-discarded-section-linker-error - "compiler bug version 4.9.2"

Wdrakula, Вам ещё не стыдно? или таки "женщина всегда права"? :)

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Пи...дец! Я тебе про это и говорил, когда описывал ситуацию и просил погуглить текст ошибки. А как, по твоему, я про волатиль догадался, если не прочтя вот именно эту ссылку? Только вместо "юзед" для функции я поставил волатиль для переменной. Но это именно ошибка, если более строгий подход оптимизатора ее выявляет. Только мудак, из этой ссылки,  считает это багом. Если ты точно считаешь, что оптимизатор не должен трогать твой код, пометь его "волатиль" и "юзед".

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Да вот уж точно.

Во-первых, сестра перестаньте хамить, тыкать и матерится. Это плохо отражается на внешности.

А во-вторых, освежу вашу "девичью память", ибо Вы конкретно писали это:

1. "Писал народ про то,что с 4.9 опять оптимизатор стал выбрасывать переменные без волатиля и даже функции, без атрибут (юзед)."

2. "Советую гугль на слова из диагностики, там и найдете про avr-gcc 4.7, 4.8, 4.9, у них в этом и разница. На 4.8 ошибка как раз не должна проявляться."

И теперь, найдите в ссылке, указанной мною: "This seems to be a compiler bug that was fixed in gcc-4.7 and reapeared in gcc-4.8 (gcc bugreport for 4.6, reapearance in 4.8). A quick workaround is to mark the function used:", последняя фраза в переводе с нерусского означает:

Быстрый обходной путь

Кроме этого, там же можно найти и номер бага и данные о его всплытии и правках .. Вы не знаете зачем пишут баг-репорты, а особенно что исправляют, что вменили это мне?!? Вы все ещё продолжаете настаивать что это МОЯ ОШИБКА и я был просто обязан применять volatile или used?!?

Окей, продолжим. опция -flto - это указание ЛИНКОВЩИКУ (а не компилятору, как можно подумать исходя из вашей позиции), КАК оптимизировать сборку всего проекта. Так в частности у avr-gcc в хелпе даже прямо сказано:

Options starting with -g, -f, -m, -O, -W, or --param are automatically
 passed on to the various sub-processes invoked by avr-gcc.

И у обоих про опцию читаем: "-flto                       Enable link-time optimization."

Тут точно ТОЧКА. Ибо это - не моя ошибка, как бы Вам не хотелось и не чесалось, барышня.

Да, и перестаньте вилять задним проходом, ибо это тоже писали Вы:

"Вот ты, когда пишешь, на зрителя расчитываешь, или пиздоболишь просто из любви к искусству? У тебя в архат.х "сишным по белому" 6 байт высылаются.", .. "Я н е стану писать про ошибки,ни у всех есть. Но, млядть, пиздобольство раздражает.", .. "Архат! Тупите нарочно? ШЕСТЬ байт + адрес=7, против 12 байт стандартной библиотеки. Это быстрее, но не в три раза.", .. "и перепешите setCursor, стыдно, ей Б..гу! У всех 4-х строк - разные смещения начала. Вы б в даташит взглядывали иногда, что ли.", .. "Вы с утра совершенно охренели??? .. и пойду искать ошибку в Вашем, заметьте, сырье.", .. "То есть Вы написали библиотеку, котора включает в код всякую херню, даже если там ей не место?", .."Вы влезли в разговор о том, как ускорить вывод на экран. Вы, как еще один персонаж на форуме, стали размахивать своим архат.х, с криками о том, что с ним решатся все проблеммы (пройдут прыщи и геморрой втянется сам). Грубого моего намека, что Вы несете чушь, Вы не поняли. Про то, что драйвер ЛСД у вас написан с ошибками - тоже не поняли. На мое прямое замечание, что в 4-х строчных дисплеях начала строк идут не 0 и 0x40, а другие (см. даташит) - Вы слили фразой, что Вас 4-х строчные не интересуют. Вот Вы не баба, после этого?", .. "1. В вашем коде была ошибка. при объявлении переменной timer0_hook_run  не стоял волатиль. Поэтому и не компилировалось. Диагностика и Ваш код в сообщении 28. 2. Ваш драйвер лсд в два раза медленнее.", .. "Вы, Архат, удивительная комбинация понтов и амбиций!"

.. мало Вашего хамства и прямых оскорблений в теме, "девушка"? Извиняться - будем?

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

1.Тыкать или нет, уж прости - сам решать буду. Я, наверняка, старше, если нужно, то я 1969 г.р., а, следовательно буду тыкать.

2. по аглицки читаешь?  "seems to be a compiler bug" - значит КАЖЕТСЯ -баг компилятора. В баг репорте читаем про мемсет... ни слова про другой "дискард".

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

Для гармонии, пусть будет "Вы" ;), не жалко.

Вы добросоветсно перечислили все невежливые мои высказывания. Память - просто восторг! Я в ах...уе, дорогая редакция. Тем не мение, я - вероятно единственный оставшийся программист на форуме, который не считает Вас клоуном и дураком. Наезд был исключительно на абициозное заявление - "применяйте мой драйвер и все будет хорошо".  У всех есть и хорошие и плохие идеи и реализации, но: Найдите пример саморекламы у меня или у Евгения, или у Логика и т.д. Вот собственно и все, что нужно знать ...

Кстати, Ваш драйвер давал 5000 мс примерно,  я его причесал до 116 мс. Но это уже не совсем Ваш драйвер, не так ли?

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

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

Поясню, для читателей:

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

Этот "спициализд" набросился со своим срачем как базарная баба, сделав вид, что "даже не разбирался". Потом, когда ему я начал объяснять что к чему, он сделал вид что у меня в коде присутствует ошибка, которая как в конце выяснилось и вовсе не ошибка даже, а багрепорт. И все это продолжалось базарным срачем. После чего он же, выложил НЕРАБОТАЮЩИЙ код теста, с синтаксической ошибкой, и некими фотографиями по времени исполнения. То, что ОДНО НЕ СООТВЕТСТВУЕТ ДРУГОМУ, ибо просто не компилируется - ЕСТЬ ФАКТ. И каждый его способен проверить самостоятельно. Все коды выложены тут, в теме. После того, как был разобран тест .. этот "диверсант" ушел молча в кусты и молчал РОВНО ДО ТЕХ ПОР, пока я не выложил свою перепроверку его вранья.

.. далее, этот герой заявил, что он "даже улучшил" мой код и добился якобы 116мсек его исполнения, типа "причесав", но теперь оказывается что "это уже не мой код".. ВО ОНО КАК ОКАЗЫВАЕТСЯ!

Не, батенька, Вы - не баба, извиняюсь. Вы - ДИВЕРСАНТ, который СОЗНАТЕЛЬНО ВРЕТ ДЛЯ СОЗДАНИЯ ИМИДЖА. Вы такой же "искперд" как и те, кто настроил укроаинцев против русских, которым поверили Клапауции, и теперь настраивают нас против амеров и всех против РФ .. ЭТО БЛАГОДАРЯ ТАКИМ КАК ВЫ по миру льется Кровь.

Это то, что меня больше всего возмущает во всей этой теме. И теперь Вы продолжаете вилять задом "я один из тех кто к вам не относится" .. ВРЕТЕ. КРУГОМ ВРЕТЕ. Все ваши утверждения надо перепроверять на 146%.

Logik
Offline
Зарегистрирован: 05.08.2014

Не тема - просто поэма!

Еще немного и всемирное зло будет повержено ))))

По существу.  После допилинга Arhat109-2 привел скорость работы в разумные пределы, я думаю все Высокие Срущиеся Стороны приймут мысль что показания 140, 124 и 116 мсек можна считать с практической стороны равными. Из этого вывод простой - каким бы способом не производилась бы работа с экраном на i2c при частоте шины 800КГц достижима скорость порядка 50% от максимума.

В чем же состоит допилинг. Самое интересное здесь.

#define LCD_WAIT_1           50  // тиков Т0: 4мксек - мало! (>37мксек пауза на команду "в среднем")

/**
 * Отправка буфера дисплею по 4-битному протоколу с "ручным" стробированием
 * и типовой задержкой на выполнение команды
 */
#define lcdSend(len)                               \
{                                                  \
  twiWrite(lcdAddress, lcdBuffer, (uint8_t)(len)); \
  delayMicroseconds(LCD_WAIT_1);                               \
}

#define lcdWrite1(d)     {lcdPrepare((uint8_t)(d), 1); lcdSend(6);}

И видим здесь следующее: на один символ выводится 6 байт (не криминал, но люди разное говорят ;);  вывод данных запускается неблокирующим вызовом (Вах! Хараше! Всебы так - ан нет ;); делей блокирует выполнение кода на 50мксек в течени которых в подсистеме прерываний должна быть выполнена отправка 5 байтов и инициализирована отправка 6-го. А вот это уже лажа, Arhat109-2! Не, я верю что вы написали аккуратно обработчик прерываний i2c и  вобщем представляете верно: типа освободился регистр, взвелся флаг, вызвался обработчик, записали в регистр следующий байт... Проблема в том, что  между "взвелся флаг" и "вызвался обработчик" может пройти достаточно много времени. Если по несчастному стечению обстоятельств как раз попало другое прерывание, например таймера. И если оно не позволяет прервать себя - а это как правило так, то прийдется ждать его завершения не один десяток мксек. Как следствие - за 50мксек не успеваем отправить 5 байт и инициализирована отправка 6-го. Возвращаемся в цикл где вызов снова lcdWrite() а в нем сразу без проверок uint8_t *_b = (uint8_t *)buf;    uint8_t  _l = len; затем lcdPrepare() который и ломает данные в еще не полностью выведеном буфере.

Ваш код нестабилен. Иногда работает, иногда мусор на экране. 

Ну и как обычно, Вас об этом предупреждал "Причем кривой как турецкая сабля, за синзронизацию выполнения потоков (основног и прерывания) на основе ожидания. Разрабам за такое дают по рукам. т.к. либо будет ждать лишнего либо может недождатся." И как обычно Вы ничего не поняли. Пока паузы были здоровенные, они скрывали проблему, а теперь вот так оно...

     
Arhat109-2
Offline
Зарегистрирован: 24.09.2015

вы уже определитесь хоть куда-нибудь "блокирующее тут исполнение" или "исполнение неблокировано, а СИНХРОНИЗИРОВАНО" делеем. а то мечетесь то туда, то сюда .. а в целом сильно рекомендую РАЗОБРАТЬСЯ с аппаратным I2C у мег. Он не просто хорош, он замечателен! Ну и заодно код обработчика обнаружить в библиотеке, дабы не писать ляпов в очередной раз. А то вас не поймешь то он "очень хорош", то "нестабилен" .. вы уж там договоритесь как-то промеж себя предварительно, так сказать "согласуйте позиции". :)

P.S. Да, и я не Иисус Христос, чтобы в "меня верить". Есть библиотека, есть файлик arhat_twi.c откройте и ознакомьтесь наконец-то. Зачем свое неумение читать код выставлять настолько напоказ-то? :)

P.P.S. Да, и ещё: 140мсек - результат полученный и продемонстрированный тут. 124мсек - результат не относящийся к вопросу (дисплей иной), а 116мсек - результат заявлен и подлежит ПРОВЕРКЕ. на все 146%.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Логик! Оставим его.Он же уже спалил меня, как виновника всех бед....
То, что ты написал, происходит, если снизить скорость и2с. Если оставить 50мкс и скорость100000, например. Я много играл с Архатовским драйвером. Сочетание 800 и 50 мкс устойчиво. Устройство полсуток отработало три дня назад.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Тем не менее, он в этой части прав. Работа конкретно тут только СИНХРОНИЗИРОВАНА задержкой. Поставьте задержку в 2мксек и получите "банан". Кстати, на моем дисплее задержка менее 41 миллисекунды - дает точно такой же "банан". Не знаю почему так.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Кстати, как можно проверить ваши "улучшения", что аж код "теперь не совсем мой"? Или так, болтнули лишнего ..

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Andriano, в целом мой первый пост в этой теме как раз полностью решал вашу задачу:

Вот похоже что это максиум того, что можно выжать при моем подходе. Тут частота I2C установлена в 880кГц, буфер для lcdPrepare() укорочен до 4-х байт и передача идет порцией в 4 байта по 2 байта на полубайт данных. Строб устанавливается в первом байте и сбрасывается во втором. Да, и здесь подчищен код теста для повышения скорости его работы:

#include "arhat.h"

//#define TWI_ON    15    // Debug: Включить все режимы обработчика I2C
//#include "arhat_twi.h"  // Debug: Подключить явно тут с заданным комплектом режимов

/**
 * Замена для delay() -- вместо Serial.writeln(); можно воткнуть любой иной код
 * который будет работать пока вывод ждет готовности дисплея, если это долго..
 *//*
#define delay(vrema)                               \
{                                                  \
  static uint8_t t = (uint8_t)millis();            \
  while( (uint8_t)millis() - t < ((uint8_t)vrema)) \
  {                                                \
    Serial.println(vrema);                         \
  }                                                \
}
*/
#define LCD_I2C_SPEED 880000 // Установить типовую скорость (внутри задано 800кгц!)
#define LCD_WAIT_1  26
#include "lcd1602.h"

// ************* Example *********** //

void setup() {
    lcdSetup(0x27, 16, 2, 1);
//    lcdWrite("Hello, Arhat!", 13);
//    lcdSetCursor(0,1);
//    lcdWrite("Bye, Bye..", 10);
//    Serial.begin(115200);
}

void loop()
{
  const char *s=(char *)"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  char ss[16];
  uint32_t  m,om;
  int l;
  float mm;

  delay(1000);

  lcdSetCursor(0,1);
  lcdWrite("                ", 16);

  om=getOvfCount();
  for( uint8_t j=0,jj=0; j < 100; j++){
    lcdSetCursor(0,0);
    lcdWrite(s+jj, 10);
    if( jj++ > 24 ) jj=0;
  }
  m=getOvfCount()-om;

  itoa((int)m, ss, 10);

  lcdSetCursor(0,1);
  l=0;
  while(ss[++l]);
  lcdWrite(ss,l);
}

Изменения: отказ от операции %, которой нет в наборе команд и замена медленной у меня millis() на быструю getOvfCount(). Соответственно 95 - это количество тиков таймера по 1024мксек. В миллисекундах тест показал = 95/1.024 = 93 мсек. :)

В целом, возвращаясь к началу темы, ответ положительный. Можно быстро выводить на дисплей и при этом иметь возможность каждые 300мксек получать данные из сериала. Даже остается "запас" не менее 200 микросекунд.

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

Arhat109-2, спасибо (заодно скажу спасибо и за Вашу библиотеку, фрагментами которой пользуюсь).

Да, этот код, пожалуй, дает ответ на один из моих вопросов, а остальное обсуждение - и на остальные вопросы.

В результате полученной информации все больше склоняюсь к варианту установить на экран (а заодно и на несколько органов управления) отдельную Pro Mini. Возможно, там и воспользуюсь Вашим кодом. Использовать его на 2560 у меня не получится из-за дефицита свободных ног (могу подключить по i2c, но не хватает скорости, а 7 лишних ного - нет). И так запланировано 4 сдвиговых регистра - по 2 на вход и на выход. Ну и кроме опроса порта там для 2560 еще хватает другой работы.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

К сожалению, Вы не посвятили всех участников в детали проекта, кроме одной заметки за "надо опрашивать порт, хотя бы каждые 300мксек". Я конечно догадываюсь что 2560 можно нагрузить большим объемом работы, но хотелось бы прочитать за хотя бы какие-то детали.

Дело в том, что эти 5518, 1118, 140, 116 или даже 95 мсек исполнения этого теста .. никоим образом не есть ответ на ваш вопрос, ибо это вовсе НЕ время работы аппаратного драйвера I2C, а "всего лишь" время работы самого теста. Сколько и как долго работает аппаратный драйвер - тут ни разу не измерялось НИКЕМ и НИЧЕМ .. помнится я уже писал про это. А, впрочем .. оно и НЕ ВАЖНО, как ни странно.

Как пример, для выпуклого показу: Вы настраиваете таймер на аппаратный ШИМ, скажем типовые 490гц и далее, всего одной командой записи в регистр изменяете его скважность. Вопрос: сколько времени требуется аппаратному таймеру чтобы скважность изменилась? Ответ: "а это кого-то волнует?" :)

Так и тут. Достоинство аппаратного I2C в том, что совершенно не важно сколько он пашет на самом деле. Его пнули - он отдал в шину. Сказали байт - отдал 1 байт, сказали 8килов - отдаст все 8. И такое невозможно при "программной эмуляции" ногодрыгами: прилетело прерывание таймера, сериала и т.д. и усё .. поплыли длительности SCL,SDA, а то и надолго. Закрывать прерывания - не выход.

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

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

Вот этот подход и есть решение многих "проблем". То, что я Вам и хотел отписать своим первым постом.

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

Ну, в свое время размещал в "Проектах" http://arduino.ru/forum/proekty/analog-analogovogo-sintezatora

Вопрос в том, что нужно опрашивать MIDI-порт и выполнять MIDI-команды так, чтобы человек не ощутил какой-либо задержки, не говоря уже о пропуске команд. При этом программно генерировать все LFO и ADSR и управлять 4-мя собственными таймерами,  фильтром на MAX261 и цифровым регулятором громкости.

Цитата:
Достоинство аппаратного I2C в том, что совершенно не важно сколько он пашет на самом деле.

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

В целом, мысль интересная (и, возможно, перспективная), но, честно говоря, сейчас мне проще поставить дополнительную Pro Mini.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

В типовой реализации из LiquidCrystal_I2C все ещё хуже: там каждый полубайт мало того, что отправляется в своем пакете, так он ещё и каждый раз делает это через Wire.h, производя инициализацию передачи заново. И делает это через виртуальную связь любимой пачки классов от Stream или кто там из них "первее". После того, как мне удалось продраться сквозь всю эту белиберду и понять что на самом деле происходит .. сел и сваял свой драйвер I2C. С сериалом кстати ровно таже самая история. Надеюсь Вы пользуете свой драйвер UART/USART на аппаратных прерываниях, а не Serial.. .h?

В кратце, идея звучит банально просто: в loop() работает самый медленный поток обработки, всё что дергается аппаратно - работает из прерываний "быстро и шустро", а основной алгоритм работы вызывается из yield() или его аналога из под пауз, которые обязан отрабатывать самый медленный поток в loop().

Собственно таким измененным подходом и отличается программирование микроконтроллеров от "ЭВМ общего назначения".

В данном, конкретном случае ваще можно сделать такую вот "рыбу":

#include "arhat.h"

//
// тут подключаем всё, что нам понадобится ишо..
// .. и объявляем все глобалы для взаимодействия аппаратных потоков-обработчиков
// , "главного" loop-потока и основного потока управления из yield()
//
// На примере робота, бегающего по линии:
// uint8_t speedLeft, speedRight, .. и т.д.
//

/**
 * Замена для delay() -- (arhat.h не имеет yield()!) вызывает опрос датчиков линии и главный мозг робота
 * пока идет отрисовка текущих параметров на экран
 * getSensors() -- получает и усредняет датчики "бегущим средним" по 20шт - папина
 * brain() -- мой главный мозг моего робота запускается каждые 2 милисек!
 */
#define delay(vrema)                               \
{                                                  \
  static uint8_t t = (uint8_t)millis();            \
  while( (uint8_t)millis() - t < ((uint8_t)vrema ) \
  {                                                \
    getSensors();                                  \
    brain();                                       \
  }                                                \
}
#include "lcd1602.h"

// Если переопределение далее мешает, то можно сделать #undef ..

// Это и есть "главный loop-поток": отрисовка всего что требуется на экран из глобалов:
void lcdWriteAll(void)
{
  uint8_t strBuffer[16];
  uint8_t len;

  len = itoa(strBuffer, speedLeft, 10);
  strBuffer[len] = '___';
  len += itoa(strBuffer, speedRight, 10);
  strBuffer[len] = '___';
  lcdSetCursor(0,0);
  lcdWrite(strBuffer, len);
  // все остально что надо вывести на экран во вторую строку и остаток первой
}

void setup(void)
{
  // все инициализации как и раньше ..
}

void loop(void)
{
  lcdWriteAll(); // и всё! Первый раз отрисовываем стартовые значения ..
}

// собственно основной поток, вызываемый из под вывода, ибо он - медленный.
// тут и происходит "самое главное" .. :)
void Brain(void)
{
}
Arhat109-2
Offline
Зарегистрирован: 24.09.2015

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

Посмотрите внимательнее этот подход: просто, дешево и сердито. :)

Logik
Offline
Зарегистрирован: 05.08.2014

Arhat109-2 пишет:

Кстати, вынеся работу с экраном в отдельное устройство, Вы ни разу не решите проблему "медленного вывода". Оно все равно всплывет, но на следующем уровне абстракции. 

Да. 

Arhat109-2 пишет:

И тогда уже, Вам полноценно понадобится всё то, чего так испугался Logik: "семафоры, очереди и т.д. и т.п."

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

Что Вы предлогаете andriano, код с известной проблемой?

Arhat109-2 пишет:

Тем не менее, он в этой части прав. Работа конкретно тут только СИНХРОНИЗИРОВАНА задержкой. Поставьте задержку в 2мксек и получите "банан". Кстати, на моем дисплее задержка менее 41 миллисекунды - дает точно такой же "банан". Не знаю почему так.

Не знаете почему - так я ж выше разжевал. " делей блокирует выполнение кода на 50мксек в течени которых в подсистеме прерываний должна быть выполнена отправка 5 байтов и инициализирована отправка 6-го." Если за время делея действие не выполнено то и будет Ваш "банан". Вы это увидели сократив задержку. Но и при 50мс получится тоже самое если "левое" прервание, например таймера, произойдет в течении этих 50мс и задержит выполнение прерываний i2c. Сопоставив период системного таймера около 1000мксек и делея 50мксек, возникает подозрение что даже в Вашем тесте каждая 20 строка на экране битая (чтоб подозрение переросло в увереност надо знать время обработки прерывания системного таймера). Понятно что в тесте "созерцая" на экране строку аж 1мсек глазами это незаметить. Бага вылезит в реальном приложении позже. И чем интенсивней работают прерывапния - тем чаще.

И чего вы andriano, паритесь с надумаными сложностяи. Ну опрос порта  300мксек - в прерывание по таймеру, вывод на экран в основной цикл. Всех делов то. Очевидно после опроса порта надо чегото сделать с получеными данными. Так для этого есть минимум 300мксек (про максимум можно поговорить отдельно, там и 600 вероятно не проблема) до следующего прерывания, вполне достаточно я думаю.