НЕ время работы аппаратного драйвера I2C, а "всего лишь" время работы самого теста. Сколько и как долго работает аппаратный драйвер - тут ни разу не измерялось НИКЕМ и НИЧЕМ .. помнится я уже писал про это. А, впрочем .. оно и НЕ ВАЖНО, как ни странно.
Не важно конечно как производительность потрачена внутри вызова. При использовании важно время выполнения вызова, при блокирующем оноже и время вывода. На верхнем уровне Ваш код такой же блокирующий как и остальные и такой же по скорости, только глюкавый слеганца ;)
Arhat109-2 пишет:
Как пример, для выпуклого показу:...
Arhat109-2 здесь все умные, прежде чем делится своими просветлениями, вспоминайте что к Вам они приходят последними.
Arhat109-2 пишет:
Достоинство аппаратного I2C в том, что совершенно не важно сколько он пашет на самом деле. Его пнули - он отдал в шину.
Важно. Важно чтоб не пнуть следующее пока не отдано предыдущее.
Arhat109-2 пишет:
Сказали байт - отдал 1 байт, сказали 8килов - отдаст все 8.
Хватит гнать пургу! Эти 8килов надо кудато положить чтоб драйвер отдавал, куда? В програмном выводе вопрос решается сам собой - по мере вывода строки её символы конвертируются в требуемые байты по одному, без буферизации вообще.
Arhat109-2 пишет:
И такое невозможно при "программной эмуляции" ногодрыгами: прилетело прерывание таймера, сериала и т.д. и усё .. поплыли длительности SCL,SDA, а то и надолго.
На время прерывания пауза на шине, закончилось продолжаем с того же места, шина синхронная, все ок. Если в Ваш код прилетело прерывание, из за отсутствия синхронизации глюк и мусор на экране. Если Вы сделаете синхронизацию у себя, то реакция на "прилетело прерывание" будет такая же - пауза на время чужего обработчика прерывания.
Arhat109-2 пишет:
А то что тут измеряли - это по сути длительность минимальной задержки в посылках, которые дисплей ещё способен переваривать. И только.
Хватит словоблудствовать. Измерялось время выполнения функций lcd.. при частоте шины 800КГц и выводе 100 строкк по 10 символов. Что там у функций внутри никого не интересует. Это чисто Ваши проблемы. Важно чтоб быстро работало и не глючило. Со вторым проблема у Вас. А по первому нет выиграша (то что Вы шину на 880 подразогнали я заметил ;)
Arhat109-2 пишет:
пример я приводил выше с макрозаменой функции delay() на полезный код.
Бредите?! Какой полезный код отработает за 50мксек, при том что в течении их 80% процессорного времени забирает обработчик прерывания (он вызывается каждые 10мксек). Вы предлагаете разбить задачу на куски по 100 тактов? А когда доделаете синхронизацию, то и того не будет.
Arhat109-2 пишет:
Вот этот подход и есть решение многих "проблем". То, что я Вам и хотел отписать своим первым постом.
По ходу темы переписали тормозной код в глюкавый и называете решением. Простописец..
Безнадега. Никто Вас никуда не провоцировал. На ваше высказывание была дана ссылка с моей оценкой написанного. ИМХО, знаете как расшифровывается? Вот, оно и есть. И перестаньте истерить по поводу и без такового. :)
Arhat109-2, что-то я не смог провести параллели между тем, что написано словами и тем, что написно в коде. Да и в коде далеко не все пронятно. Например, комментарии говорят, что brain() должен вызываться не реже раз в 2 мс. В коде я этого не увидел. (я так понимаю, что различие между brain() и Brain() получилось случайно и должно быть одно и то же)
В принципе то, что я реализовал у себя в программе близко к написаному у Вас словами: в прерывании таймера происходит управление всей периферией, а также анализ того, появились в очереди новые ноты или нет. А в loop() - опросы портов и периферии с приоитетом MIDI-порта. Фоновые задачи отсутствуют. Serial использую стандартный. Все управление периферией писал сам, сверяясь с дэйташитом и осциллографом. Общение между 2560 и промини также планирую через Serial. Правда, если ранее я думал лишь повесить на промини экран и несколько датчиков, то теперь, наоборот склоняюсь сделать ее как бы основной - она взаимодействует с органами управления, задающими глобальные режимы и передает команды на 2560, а последняя наблдюдает за своими локальными органами управления, принимает команды по MIDI и от промини, а также докладывает последней об изменениях в локальных органах управления - для индикации на дисплей.
Кстати, в качестве дисплея одним из вариантов предусматривал Nextion, но есть несколько "но":
И чего вы andriano, паритесь с надумаными сложностяи. Ну опрос порта 300мксек - в прерывание по таймеру, вывод на экран в основной цикл. Всех делов то. Очевидно после опроса порта надо чегото сделать с получеными данными. Так для этого есть минимум 300мксек (про максимум можно поговорить отдельно, там и 600 вероятно не проблема) до следующего прерывания, вполне достаточно я думаю.
Мне не нужно опрашивать порт КАЖДЫЕ 300 мкс, мне нужно, чтобы интервал опроса не превышал 300 мкс, по крайней мере, в критически важных случаях.
Сейчас у меня единственное прерывание от таймера раз в 1024 мкс, в котором я управляю периферией. Вот этот процесс должен быть строго регулярным.
В настоящее время это прерывание занимает примерно:
- 30 мкс, если не звучат ноты, т.е. только на LFO, проверки и накладные расходы,
- 130 мкс, если звучат ноты - добавляется вычисление ADSR и управление VCO,
- + 170 мкс на начало ноты, максимум 2 т.е. +340 мкс, итого примерно 470 мкс в худшем случае,
- по оценкам будет еще порядка +100 мкс на вычисление ADSR фильтра и управление фильтром.
итого: порядка 30-250 мкс в среднем, но со всплесками до 400 мкс при старте новой ноты и почти 600 мкс при старте двух нот одновременно. В принципе даже это устраивает, так как нота - это минтимум 2 байта. На каждый нужно по 300 мкс (при скорости MIDI 31250), значит, как только мы получили очередную ноту, следующая поступит не ранее 600 мкс. Просто считаем из буфера два байта один за другим, а задержки относительно прихода второго байта уже не будет. И, кстати, это тот случай, когда опрашивать нужно с интервалом меньше 300 мкс.
Но обмен с экраном как-то в эту картину не укладывается.
Вы правы, brain() и Brain() это одно и тоже. Тут его код не приведен вовсе, а комментарий скопипастился из рабочего скетча "робота по линии". Там, в его нутре есть первой строкой everyMillis(2, {}); которая делает сразу возврат из него, если от предыдущего вызова прошло менее 2миллисекунд.
Нам так понадобилось, потому что было выявлено, что управлять моторами чаще чем 1 раз в 8 мсек - по сути бессмысленно - инерция мотора не позволяет ему хоть как-то среагировать. Поэтому его Brain() при более частом вызове тупо делает return; и макрос, заменяющий delay() повторно вызывает getSensors() для накопления данных с датчиков и их усреднения. Датчики имеют свой everyMicros() и не позволяют вызывать себя чаще чем каждые 50мксек. Но и задержка на отрисовку у него там стоит 5 миллисекунд. Наш дисплей вот быстрее чем 4.1 - "никак". Получается тот же банан, когда он занят отрисовкой предыдущего символа, а программа ему шлет уже следующий .. имеем бардак на экране. Он его как-то совсем не так принимает.
Надеюсь понятно их местных тестов, что за 4 миллисекунды отправка предыдущего уже ДАВНО состоялась .. я к тому, что у дисплея есть его собственное время отрисовки и слать ему что-то чаще - бессмысленно и рушит выдачу, даже если ваш I2C и программа позволяют такое.
Я привел "рыбу". Что-то надергано из рабочего кода, что-то дописано тут-же "по памяти" .. это просто демонстрация идеи управления при наличии медленного вывода:
В loop() гоняем самый медленный поток, в данном случае - единую функцию отрисовки "всего и вся" на экране. Она может быть достаточно сложной, если требуетсярисовать "несколько экранов" .. сегодня один, а в этом режиме - другой. Сделайте внутри неё switch() по типам экранов... но идея в том, что ВСЯ ОТРИСОВКА делается только тут и больше нигде по коду. Для этого, имеем глобальную область видимости и в нужных местах кода присваиваем переменным отрисовки нужные значения .. или тупо выводим рабочие переменные как в этой рыбе.
Далее, поскольку вывод требует пауз:
а) Вы вполне можете поменять LCD_WAIT_1 на то значение, которое обеспечивает вам ещё приемлемую скорость отрисовки и оно больше минимально возможной паузы для вашего дисплея (у меня на одном экземпляре 4100мксек, а на другом - 50мксек);
б) Перед подключением lcd1602.h вставляете макрос, заменяющий вызов delay() на то что Вам требуется (у меня приведен пример вызова 2-х функций).
И это "то что требуется" у Вас будет отрабатываться пока отрисовка ожидает продолжения своей работы. А поскольку loop() больше ничем не занят, кроме как отрисовкой, то Вы спокойно можете рассчитать как частоту вызова макро-делей, так и времена работы того что требуется. Надежно. И, управляя размером паузы, вы просто регулируете что вам нужнее: ушустрить отрисовку и уменьшить долю времени исполнения основного кода ИЛИ наоборот: усложнить основной код за счет замедления отрисовки.
Этот подход оставляет Вам возможность использовать хук прерывания таймера для "более важных задач по расписанию" .. плюсом ко всем задачам отрабатываемым по прерываниям.
.. вот такой симбиоз и как-бы кооперативный и примитивный RTOS, тем не менее решающий большинство "проблем". :)
Мне не нужно опрашивать порт КАЖДЫЕ 300 мкс ... Сейчас у меня единственное прерывание от таймера раз в 1024 мкс, в котором я управляю периферией. Вот этот процесс должен быть строго регулярным.
В настоящее время это прерывание занимает примерно:
- 30 мкс, если не звучат ноты, т.е. только на LFO, проверки и накладные расходы,
- 130 мкс, если звучат ноты - добавляется вычисление ADSR и управление VCO,
- + 170 мкс на начало ноты, максимум 2 т.е. +340 мкс, итого примерно 470 мкс в худшем случае,
- по оценкам будет еще порядка +100 мкс на вычисление ADSR фильтра и управление фильтром.
итого: порядка 30-250 мкс в среднем, но со всплесками до 400 мкс при старте новой ноты и почти 600 мкс при старте двух нот одновременно.
1. Перетащите весь этот управляющий код в аналог Brain() и сделайте паузу на отрисовку 1 символа в районе 600мксек. Просто вместо millis() Вам придется вызывать micros() в макросе delay() рыбы.
2. Нарисуйте единую функцию отрисовки на дисплей, собирающую строки из глобалов и/или согласно состоянию своих флагов отрисовки, воткните её в loop() одну, единственную. Всё, что в нем ещё есть - добавьте в тот же Brain().
3. Прием от MIDI интерфейса посадите на прерывание RX нужного UART-а. Его задача принять байт в соответствующий глобал и установить флаг "принято" для Brain(). Можно даже завести пару или сколько там потребуется для приема нескольких нот аккордом с модификацией флага "принята 1,2 и т.д. ноты";
4. Ваш Brain() должен выродится в типовой конечный автомат: "если принята нота", "если нота начала звучать", "если звучит нота" и т.д. Надо просто очень аккуратно выписать все его состояния и возможные переходы между ними. Сильно уверен, что там много ортогональностей и сам код перехода между состояниями не окажется сколько-то сложным.
P.S. Да, и простейший обработчик UART без wiring есть в библиотеке Cyberlib для ATmega328p .. его переделка под мега2560 не составляет никакой проблемы. Да и из Wiring можно выдрать процедуры приема по прерыванию и причесать..
Интересно, а ведь если на I2C кроме этого одного дисплея нет ничего, то ведь строб "E" можно смело генерить детектируя завершение I2C транзации на SCL. То есть длительность высокого уровня на SCL при высоком уровне на SDA больше заданного RC цепочкой = строб )
/**
* Набор примитивов для работы с дисплеем 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мксек до 4100 мксек зависит от .. дисплея!!!
*
* Длительность 1 передачи по I2C на 100кГц = 90мксек! В реальности тянет до 880кгц .. отдельные экземпляры
* Нельзя ничего читать с дисплея (R/W===0)! А ведь есть BF - "бит готовности" ..
*
* Отсюда передача возможна только побайтная:
* сразу готовим посылку каждой тетрады в виде 2-х байт: установка + строб, сброс строба,
* и сразу же отправляем одной пачкой обе тетрады. Итого буфер на передачу 1 символа = 4 байта.
*
* @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
#ifndef LCD_WAIT_1
# define LCD_WAIT_1 50 // тиков Т0: 40мксек - мало! (>37мксек пауза на команду "в среднем")
#endif
#define LCD_BUF_LEN 4 // длина буфера для lcdPrepare()
/**
* Отправка буфера дисплею по 4-битному протоколу с "ручным" стробированием
* и типовой задержкой на выполнение команды
*/
#define lcdSend(len) \
{ \
twiWrite(lcdAddress, lcdBuffer, (uint8_t)(len)); \
delayMicroseconds(LCD_WAIT_1); \
}
#define lcdWrite1(d) {lcdPrepare((uint8_t)(d), 1); lcdSend(LCD_BUF_LEN);}
#define lcdCommand(p) {lcdPrepare((uint8_t)(p), 0); lcdSend(LCD_BUF_LEN);}
#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[LCD_BUF_LEN]; // Буфер для потоковой тетрадной записи в дисплей
/**
* Подготовка байта в буфер потоковой записи
* @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;
lcdBuffer[1] = nibble;
nibble |= LCD_E;
lcdBuffer[0] = nibble;
nibble = ((_data&0x0f)<<4) | lcdBackLight;
if( _rs ) nibble |= LCD_RS;
// lcdBuffer[5] = lcdBuffer[3] = nibble;
// nibble |= LCD_E;
// lcdBuffer[4] = nibble;
lcdBuffer[3] = nibble;
nibble |= LCD_E;
lcdBuffer[2] = 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(LCD_BUF_LEN/2); // 1xn отправка: по включению режим 8-бит - вторая тетрада не нужна!
delay(LCD_WAIT_BOOT1); // ждем >4.1ms
lcdSend(LCD_BUF_LEN/2); // вторая отправка согласно даташит 8-бит..
delayMicro16(LCD_WAIT_BOOT2); // ждем >100us
lcdSend(LCD_BUF_LEN/2); // третья отправка 8-бит..
lcdPrepare(0x20, 0); // и только теперь переводим в режим 4-бита
lcdSend(LCD_BUF_LEN/2); // и только теперь режим 4-бита и отправляем тетрады попарно!
}
lcdInit();
delay(500); // чтобы было заметно глазом.. один раз.
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _LCD1602_H_
Это корректированный lcd1602.h с константой "длина буфера" и измененным lcdPrepare(), lcdInit() под размер буфера в 4 байта. Дополнительно константа LCD_WAIT_1 обрамлена проверкой для её предустановки из скетча. Компилялось и на фото приведена проверка с этой версией файла.
Да, в посту №95 изменена скорость I2C до 880кГц. Мой - тянет, обратите внимание. :)
Ну и как показал замер на нескольких дисплеях - минимальные тайминги все-таки "плавают", но незначительно. Мой дисплей - тоже "ожил" .. тупо пропаял всю линейку контактов к переходнику I2C .. не знаю что там "было не так". Но быстрее 105мсек мой "итого" тоже не кажет.
В целом, вам как-бы "без разницы" сколько мелкосекунд оно выводит, при том подходе что я вам рекомендовал. Перечитайте.
Двигатель имеет некорою штангу с магнитом, который пролетает мимо двух Холлов, отрабатываю прерывания от каждого датчика
//_______Прерывание по началу возврата ЗК_______
void ZADNIK()
{ VZRT = millis();
Serial.print ("VZRT = "); Serial.println (VZRT);
}
//_______Прерывание по концу возврата ЗК _______
void PERED()
{VZRT = millis()- VZRT;
-- CYC;
Serial.print ("VZRT = "); Serial.println (VZRT);
}
(считаю некоторое время между срабатываниями Холлов, ну и заодно количество нужных оборотов CYC -1). Проблем нет все четко, на монитор порта вылетает это самое время возврата, все вижу, а вот написать на LCD1602 подключенный через I2C номер витка, к примеру, не получается - мотор просто теряет все скоростные характеристики (резко замедляется).
Andriano посоветовал суда заглянуть. Идею понял, но количество знаий языка не позволяет увидитьь это самое решение. Нужна доработанная библиотека? Или как-то с сетапе это устанавливается?
Вот это я не понял. Это влияет на скорость?
{ lcdSetCursor( 0,0 ); // отправка команды (1 посылка)
lcdWrite( s+(j%25), 10 ); // отправка 10 символов (10 посылок)
}
Для выввода на LCD экран через PCF8574 и2с регистр - самый быстрый вариант - библиотека Архата для хардварного и2с.
Но у нее есть сложности в использовании, так как у Архата - это побочный продукт идеи многопоточности о которую мы тут уже зубы сточили ;).
Еще особеннсть - библиотека Архата работает через прерывания, поэтому будет мешать иным синхронным процессам. Хотя сам и2с протокол - вообще не критичен к таймингам. Может нужно не быстро, а "в фоновом режиме"?
................
я не очень следил за форумом и не помню, откуда Вас сюда Андриано отправил. Вы задачу свою изложите. Есть много вариантов.
Например можно прекращать получение данных на время их обработки и отображения (так, например, цифровые осцилографы делают). Можно передать отображение - другому кристаллу, тем паче что они по 100-200р стоят. Что у Вас за задача?
Вообще контрноллеры программируют так:
1. выделяют самый критичный к синхронности процесс и цепляют его на прерывания от нужных событий (внешних или по времени);
2. сервисные процессы строят на некритичных к таймингам алгоритмах и протоколах, и запускают их в основном потоке времени.
3. если есть больше одного критичного к таймингам процесса - то возможно следует выбирать подходящий контроллер, который успеет. С приоритетами прерываний, очередями запросов и соответствующей частотой. На низких частотатх это можно эмулировать.
Задача: вращением шагового открывать защитный кожух пилы со скоростью не менее 10раз/мин. Затем штанга ротора в конечной точке открытого кожуха будет соскальзывать с захвата кожуха и он будет возвращаться в начало. Это время не должно превышать 0,3сек. Двигатель будет продолжать свое вращение, снова захватывать кожух открывать его , снова доводить его до полного открытия, соскальзывать и т.д. Количество открытий не менее 50000. Вот собственно и все. У меня все прекарсно работает, но вывести номер очередного оборота (открытия) не получается - двигатель резко притормаживает и вообще все зависает. Поэтому ограничился только вывеской "тест идет" и делаю остановку (кнопкой) чтобы посмотреть сколько циклов уже завершено.
Я пока вижу очень наивный код, в котором сочетается неотключенный (вне тестирования) вывод в сериал, который все равно тратит кучу времени и управление шаговым двигателем в основном потоке времени, вместо управления из прерывания, или вообще просто таймером. Тут я не знаю остального кода и чем могут быть заняты таймеры, даже не знаю, на каком контроллере все сделано.
Много просто ошибок: циклы 28-33, 38-43, 47-53, 64-6, и с 72 по 76 - это как? В цикле выводим на дисплей? сколько тысяч раз успеваем это сделать ;) ;) ;)? Ну эта ошибка просто самая веселая, она хоть работать не мешает, просто забавная. Не стану про остальные писать.
В таком коде как у Вас - действительно вывод добавить почти невозможно. Просто код не расширяемый.
Опишите словами ВСЕ задачи контроллера - я напишу, как нужно их решать. Не код напишу а объясню, как его написать самостоятельно.
Можно и код - но Вам это, надеюсь ;), не интересно будет.
EuBeginer, суде по приведенному тексту, Вам нужно совершать манипуляции с мотором раз в несколько сотен мкс, тогда как вывод строки на дисплей занимает порядка 25000-30000 мкс. Т.е. самые грубые прикидки говорят, что одновременно с построчным выводом текста мотор нормально работать не сможет.
Наиболее прямой метод порекомендовал wdrakula, но, опять же, нам неизвестно, какие процессы еще происходят в Вашем скетчяе, и как используются аппаратные ресурсы, в частности, таймеры.
В принципе можно организовать вывод текста и в общем цикле работы мотора, только нужно сделать две вещи:
1. Выводить текст не построчно, а по одной букве. Т.е. вклиниваем вывод очередной буквы в то место, где сейчас находятся delayMicroseconds(), уменьшая величину rpm на время вывода буквы. Или, лучше, заменяем delayMicroseconds() на micros() по принципу blink without delay.
2. В несколько раз ускоряем вывод символа, воспользовавшись приведенными в данной теме вариантаии кода. По идее должен занимать прмерно 100 мкс. Так что по идее можно даже выводить не по одному, а сразу порциями по 4 символа (400 мкс < 468 мкс).
Не. Писали работает на 800КГц. Значить 13мкс. И за 468мкс одна строка выводится свободно.
Делается так, перед выводом сохраняем время мксек, выводим строку и ждем пока время не дотикает до сохраненное+rpm. И это просто вместо делея.
В общем случае, если времени совсем мало, вывод строки пишем как машинку состояний, посимвольно за один вызов. Ничего сложого.
//нам неизвестно, какие процессы еще происходят в Вашем скетчяе
Какие еще задачи есть - совершенно не важно, если сидим в ожидании десятки или сотни мксек - то можно выводить какое то кол-во символов. Остальное дело техники аппаратного програмирования, если прерывания не трогаем и не мешаем быстрым процессам. Если нам они мешают (частые и долгие, занимают много процессорного времени) - ужимаемся хоть до побитового вывода, вывод одного бита раз в 100мкс (ну это если совсем плохо) даст 1000 байт в секунду. Дай бог прочитать столько с экрана ))
Andriano, как продвигается ваш проект? Что-то получилось сделать с выводом как я советовал? Вы как-то забросили тему, напоминаю что лично мне - все ещё интересно. :)
Wdrakula предложил вполне нормальное решение. Мне кажется что его надо просто слегка разжевать автору вопроса..
Arhat109-2, вот только полчаса назад проковырял дырку для дисплея.
То собирал на макете с 16-ю переменниками и 6-ю кнопками. Дисплей было некуда прикрепить. Теперь делаю уже полномасштабное шасси для полного набора как узлов, так и органов управления. В общем, в программировании уткнулся в то, что полностью исчерпал возможности урезанного макета. Так что пока занимаюсь шасси и корпусом (дрель, напильник...), потом перейду к пайке.
Ваш вариант я опробовал в виде отдельного скетча, о чем писал. Когда все соберется в корпусе, думаю, начну как раз с дисплея, а то с макета вся диагностика шла в Serial, а это и долго, и неудобно. Выводить планирую по одной букве, в свободное время, вклинившись в основной цикл опроса датчиков. Так что просто навесить сверху не получится - нужно довольно глубоко интегрировать в имеющийся код.
По срокам планирую закончить весь конструктив-электронику где-то на рождественских каникулах и перейти снова к программированию.
Я пока вижу очень наивный код, в котором сочетается неотключенный (вне тестирования) вывод в сериал, который все равно тратит кучу времени и управление шаговым двигателем в основном потоке времени, вместо управления из прерывания, или вообще просто таймером. Тут я не знаю остального кода и чем могут быть заняты таймеры, даже не знаю, на каком контроллере все сделано.
Много просто ошибок: циклы 28-33, 38-43, 47-53, 64-6, и с 72 по 76 - это как? В цикле выводим на дисплей? сколько тысяч раз успеваем это сделать ;) ;) ;)? Ну эта ошибка просто самая веселая, она хоть работать не мешает, просто забавная. Не стану про остальные писать.
За критику спасибо. Я и не претендую. Циклы..... дапросто заменил delay. Уроки поделал. Весело ,ну и хорошо... Все равно его не брошу, потому что он....
Там просто уже ничего делать не надо. Только на экран смотреть и читать.
Таймеры.... да, не знаю я их не задействовал. Просто не знаю как вращать мотор через прерывания.
Задача... да просто вращать мотор и выполнить пару прерываний.
даст 1000 байт в секунду. Дай бог прочитать столько с экрана ))
а, кто-то, вообще, смотрел в даташит экрана? - есть там параметр типа "скорость отклика жидких кристаллов экрана"?
Ну я смотрел, а чего? Имею право ;) Я там режими всякие скролинга вычитывал. Прикольные. Скорости отклика жидких кристаллов экрана там нету из-за отсутствия таковых. Он без жидких кристалов, он светодиодный и органический как навоз. Есть частота кадровой развертки, до сотни Гц вроде.
Ну я смотрел, а чего? Имею право ;) Я там режими всякие скролинга вычитывал. Прикольные. Скорости отклика жидких кристаллов экрана там нету из-за отсутствия таковых. Он без жидких кристалов, он светодиодный и органический как навоз. Есть частота кадровой развертки, до сотни Гц вроде.
Не. Писали работает на 800КГц. Значить 13мкс. И за 468мкс одна строка выводится свободно.
Тут 3 страницы копья ломали, увеличили скорость вывода в 15 раз, довели до 100 мкс и вдруг - на тебе! 13 мкс!
Код, подтверждающий эти слова, в студию!
Так смотрите в начале темы и код и скрин. 124мсек на тест. Возможности экрана упираются в возможности шины i2c. А на шине i2c удается реализовать вывод 100 строк по 10 символов в строке по 8 байт на символ (округляем до 10 т.к. команды адрес и т.д. по крайней мере у меня так в этом примере) итого грубо 10000 байт за 124мсек. Ну такой тест там, не я его даже придумал. Итого вывод байта - порядка 13мксек на байт. И еще два сотоварища в диспуте участвующие получили сходные результаты. И теория какбы намекает, что 800кГц - это период тактирования 1,25мкс, т.е.8 бит + АСК 11,25мкс плюс усушка утруска старты- стопы дето около 13мксек и выходит снова.
Возможно Вы посчитали что "Писали работает на 800КГц. Значить 13мкс." относится к времени вывода символа - так это не так, это на байт. А сколько байт для 1602 нужно на символ 4-5-6 я толком из этой темы и не понял, Вы там дискутировали много и гаряче, так озвучте четкий результат. Но даже при худшем: 6*13=78мс 468/78=6 символов, чего явно хватит для того чтоб "вывести номер очередного оборота" как хочет EuBeginer.
Ну я смотрел, а чего? Имею право ;) Я там режими всякие скролинга вычитывал. Прикольные. Скорости отклика жидких кристаллов экрана там нету из-за отсутствия таковых. Он без жидких кристалов, он светодиодный и органический как навоз. Есть частота кадровой развертки, до сотни Гц вроде.
ты название темы читал? - LiquidCrystal_I2C
LiquidCrystal, блин!
Не только читал, но и писал. Здесь уже давно не только про это несчастье речь, и даже скорей не о ней, а о скорости вывода на экран.
///скорость за которой не успевает жидкий кристалл.
Это те дальше на йух и буераками. Здесь три страницы про обмен с экраном по i2c накрапали, и без тебя справились. И достигнуты скорости вывода такие, что не то ше жидкий кристал не успевает, даже глаз человечий не различает. А им все мало!
Возможно Вы посчитали что "Писали работает на 800КГц. Значить 13мкс." относится к времени вывода символа - так это не так, это на байт. А сколько байт для 1602 нужно на символ 4-5-6 я толком из этой темы и не понял, Вы там дискутировали много и гаряче, так озвучте четкий результат. Но даже при худшем: 6*13=78мс 468/78=6 символов, чего явно хватит для того чтоб "вывести номер очередного оборота" как хочет EuBeginer.
Ну, вообще-то байтовая скорость в денной теме никакого иньтереса не представляет. Тем более, что в процессе обсуждения и экспериментоыв удалось уменьшить количество байтов, передаваемых на 1 символ, с 12 до 5.
Но даже если она кого-то интересует, притом, интересуют "грязные" байты, т.е. кроме данных включающие и служебную информацию, все равно меньше 21-25 мкс на байт не получается.
ну, так пиздуйте в тему про большие экраны, пока я здесь выясняю, накуя нужна скорость за которой не успевает жидкий кристалл.
Объясняю по буквам:
Д а л е к о н е в к а ж д о м п р о е к т е е д и н с т в е н н а я р а б о т а А р д у и н о - в ы д а в а т ь д а н н ы е н а э к р а н . Б ы в а е т , э к р а н - ч и с т о в с п о м о г а т е л ь н о е у с т р о й с т в о , а А р д у и н а з а н я т а д р у г и м б о л е е в а ж н ы м д е л о м . И т р е б у е т с я , ч т о б ы э к р а н н е о т н и м а л с л и ш к о м м н о г о в р е м е н и у о с н о в н о г о а л г о р и т м а .
Вместо срача с троллями, Вы бы лучше выложили что у Вас уже получилось. Интересно жеж каким путем Вы пошли в конце концов и куда дошли.. ;)
Ну, вообще-то сейчас как раз в процессе работы: поочередно то паяю очередную плату (дабы обеспечить ввод данных от 43 потенциометров, 22 кнопок и двух валкодеров), то вожусь с подключением этого всего, включая библиотеки (как они называются в Ардуино - а по простому - пары *.h и *.cpp). Увы, стандартного 20-санитиметрового дюпона немного не хватает, а при включеннии друг в друга последовательно, дисплей работает неустойчиво.
Но в целом идея такая: при возникновении потребности вывести строку на дисплей вызывается метод, запоминающий эту строку. А затем в цикле loop() вызывается метод run(), который проверяет, есть ли что на вывод, и если "да", выводит на экран очередную букву из запомненной строки.
Исходная библиотека LiquidCrystal_I2C не используется, из нее была вытащена, не вникая в подробности, инициализация дисплея и факически доприсаны 3 метода:
- прием и запоминание строки, которую нужно вывести,
- прием и запоминание числа, которое нужно вывести,
- вывод очередного символа из запомненной строки, буде такая обнаружится.
Т.е. основной алгоритм прерывается не более чем на 124 мкс.
Выяснил, что по крайней мере мой дисплей работает на 800 МГц неустойчиво - после прогрева то зависает, то не инициализируется после перезагрузки. И, подозрение, что блокирует контроллер, т.к. последний перестает реагировать на внешние воздействия.
Переключил на 400 МГц. Один символ выводится за 184 мкс. Вроде, работает устойчиво.
У меня таже проблема. Контроллер в основном цикле измерят частоту вращения двигателя и дополнительно каждые 0.5 сек передает данные ны дисплей за счет длинной передачи подтормаживает основной цикл. Скачал заплатку из поста 124 но она не компелируется из -за #include "MIDI_Disp.c". В инете ничего не нашел. Что это такое и как скомпелировать.
Используйте вместо него файл с теми строками, что нужны Вам.
PS. Посмотрел еще раз: в том же самом посте №124 лежит файл MIDI_disp.c целиком. Так что можете его оттуда скопировать и подключить. Если это Вам поможет.
жаль что регулятор частоты не может выдавать на индикатор саму частоту вращения. Я конечно выкрутился и отправляю но одному знаку F, “”,5,0,,,1,””,H,z но это как то не красиво.
жаль что регулятор частоты не может выдавать на индикатор саму частоту вращения.
Нет, на индикатор, конечно, можно выдать и саму частоту. Только, боюсь, он не будет знать, что с ней делать, т.к. привык получать на вход команды либо данные, но никак не частоту.
Еще раз спасибо за участие, но оно сподвигло меня решить эту проблему. Возможно коряво, но результат на лицо. В моей программе рассчитывается частота с датчика, который работает с 60-ти зубовым колесом и эта частота формата float
На индикатор должна выводиться частота генератора, которая = частоте датчика / 30, с точностью один знак после запятой. Я создал временную переменную формата int и поделил частоту не на 30, а на 3. В результате чего получил число формата int в 10 раз большее, чем частота генератора. Не 51,5 Гц, а 515. Далее я конвертирую это число с помощью оператора itoa в одномерную матрицу формат char. И это я проделываю 1 раз в секунду. Мне надо на индикатор передать такую строку F 50.1 Hz. Затем я делаю матрицу на 9 позиций, где третья, четвертая и шестая позиции числа из матрицы, полученной оператором itoa и я ее передаю на индикатор. Сам процесс передачи, наблюдаемый осциллографом занимает около 2.5 мс, что меня вполне устраивает. При обычной передаче это занимает в 3-4 раза больше время.
Меня еще интересует, все ли время занят контроллер этой передачей или само участие в этом процессе контроллера еще меньше.
P.S. кстати в строку можно преобразовать и число формата float, но выкрутасов будет значительно больше.
Опять Остапа-Arhat109-2 понесло в Новые Васюки.
НЕ время работы аппаратного драйвера I2C, а "всего лишь" время работы самого теста. Сколько и как долго работает аппаратный драйвер - тут ни разу не измерялось НИКЕМ и НИЧЕМ .. помнится я уже писал про это. А, впрочем .. оно и НЕ ВАЖНО, как ни странно.
Бредите?! Какой полезный код отработает за 50мксек, при том что в течении их 80% процессорного времени забирает обработчик прерывания (он вызывается каждые 10мксек). Вы предлагаете разбить задачу на куски по 100 тактов? А когда доделаете синхронизацию, то и того не будет.
Вот этот подход и есть решение многих "проблем". То, что я Вам и хотел отписать своим первым постом.
Безнадега. Никто Вас никуда не провоцировал. На ваше высказывание была дана ссылка с моей оценкой написанного. ИМХО, знаете как расшифровывается? Вот, оно и есть. И перестаньте истерить по поводу и без такового. :)
Arhat109-2, что-то я не смог провести параллели между тем, что написано словами и тем, что написно в коде. Да и в коде далеко не все пронятно. Например, комментарии говорят, что brain() должен вызываться не реже раз в 2 мс. В коде я этого не увидел. (я так понимаю, что различие между brain() и Brain() получилось случайно и должно быть одно и то же)
В принципе то, что я реализовал у себя в программе близко к написаному у Вас словами: в прерывании таймера происходит управление всей периферией, а также анализ того, появились в очереди новые ноты или нет. А в loop() - опросы портов и периферии с приоитетом MIDI-порта. Фоновые задачи отсутствуют. Serial использую стандартный. Все управление периферией писал сам, сверяясь с дэйташитом и осциллографом. Общение между 2560 и промини также планирую через Serial. Правда, если ранее я думал лишь повесить на промини экран и несколько датчиков, то теперь, наоборот склоняюсь сделать ее как бы основной - она взаимодействует с органами управления, задающими глобальные режимы и передает команды на 2560, а последняя наблдюдает за своими локальными органами управления, принимает команды по MIDI и от промини, а также докладывает последней об изменениях в локальных органах управления - для индикации на дисплей.
Кстати, в качестве дисплея одним из вариантов предусматривал Nextion, но есть несколько "но":
- медленный интерфейс 9600,
- медленные команды в текстовом формате,
- я вообще не вижу, чем он может быть лучше 1602.
И чего вы andriano, паритесь с надумаными сложностяи. Ну опрос порта 300мксек - в прерывание по таймеру, вывод на экран в основной цикл. Всех делов то. Очевидно после опроса порта надо чегото сделать с получеными данными. Так для этого есть минимум 300мксек (про максимум можно поговорить отдельно, там и 600 вероятно не проблема) до следующего прерывания, вполне достаточно я думаю.
Мне не нужно опрашивать порт КАЖДЫЕ 300 мкс, мне нужно, чтобы интервал опроса не превышал 300 мкс, по крайней мере, в критически важных случаях.
Сейчас у меня единственное прерывание от таймера раз в 1024 мкс, в котором я управляю периферией. Вот этот процесс должен быть строго регулярным.
В настоящее время это прерывание занимает примерно:
- 30 мкс, если не звучат ноты, т.е. только на LFO, проверки и накладные расходы,
- 130 мкс, если звучат ноты - добавляется вычисление ADSR и управление VCO,
- + 170 мкс на начало ноты, максимум 2 т.е. +340 мкс, итого примерно 470 мкс в худшем случае,
- по оценкам будет еще порядка +100 мкс на вычисление ADSR фильтра и управление фильтром.
итого: порядка 30-250 мкс в среднем, но со всплесками до 400 мкс при старте новой ноты и почти 600 мкс при старте двух нот одновременно. В принципе даже это устраивает, так как нота - это минтимум 2 байта. На каждый нужно по 300 мкс (при скорости MIDI 31250), значит, как только мы получили очередную ноту, следующая поступит не ранее 600 мкс. Просто считаем из буфера два байта один за другим, а задержки относительно прихода второго байта уже не будет. И, кстати, это тот случай, когда опрашивать нужно с интервалом меньше 300 мкс.
Но обмен с экраном как-то в эту картину не укладывается.
Вы правы, brain() и Brain() это одно и тоже. Тут его код не приведен вовсе, а комментарий скопипастился из рабочего скетча "робота по линии". Там, в его нутре есть первой строкой everyMillis(2, {}); которая делает сразу возврат из него, если от предыдущего вызова прошло менее 2миллисекунд.
Нам так понадобилось, потому что было выявлено, что управлять моторами чаще чем 1 раз в 8 мсек - по сути бессмысленно - инерция мотора не позволяет ему хоть как-то среагировать. Поэтому его Brain() при более частом вызове тупо делает return; и макрос, заменяющий delay() повторно вызывает getSensors() для накопления данных с датчиков и их усреднения. Датчики имеют свой everyMicros() и не позволяют вызывать себя чаще чем каждые 50мксек. Но и задержка на отрисовку у него там стоит 5 миллисекунд. Наш дисплей вот быстрее чем 4.1 - "никак". Получается тот же банан, когда он занят отрисовкой предыдущего символа, а программа ему шлет уже следующий .. имеем бардак на экране. Он его как-то совсем не так принимает.
Надеюсь понятно их местных тестов, что за 4 миллисекунды отправка предыдущего уже ДАВНО состоялась .. я к тому, что у дисплея есть его собственное время отрисовки и слать ему что-то чаще - бессмысленно и рушит выдачу, даже если ваш I2C и программа позволяют такое.
Я привел "рыбу". Что-то надергано из рабочего кода, что-то дописано тут-же "по памяти" .. это просто демонстрация идеи управления при наличии медленного вывода:
В loop() гоняем самый медленный поток, в данном случае - единую функцию отрисовки "всего и вся" на экране. Она может быть достаточно сложной, если требуетсярисовать "несколько экранов" .. сегодня один, а в этом режиме - другой. Сделайте внутри неё switch() по типам экранов... но идея в том, что ВСЯ ОТРИСОВКА делается только тут и больше нигде по коду. Для этого, имеем глобальную область видимости и в нужных местах кода присваиваем переменным отрисовки нужные значения .. или тупо выводим рабочие переменные как в этой рыбе.
Далее, поскольку вывод требует пауз:
а) Вы вполне можете поменять LCD_WAIT_1 на то значение, которое обеспечивает вам ещё приемлемую скорость отрисовки и оно больше минимально возможной паузы для вашего дисплея (у меня на одном экземпляре 4100мксек, а на другом - 50мксек);
б) Перед подключением lcd1602.h вставляете макрос, заменяющий вызов delay() на то что Вам требуется (у меня приведен пример вызова 2-х функций).
И это "то что требуется" у Вас будет отрабатываться пока отрисовка ожидает продолжения своей работы. А поскольку loop() больше ничем не занят, кроме как отрисовкой, то Вы спокойно можете рассчитать как частоту вызова макро-делей, так и времена работы того что требуется. Надежно. И, управляя размером паузы, вы просто регулируете что вам нужнее: ушустрить отрисовку и уменьшить долю времени исполнения основного кода ИЛИ наоборот: усложнить основной код за счет замедления отрисовки.
Этот подход оставляет Вам возможность использовать хук прерывания таймера для "более важных задач по расписанию" .. плюсом ко всем задачам отрабатываемым по прерываниям.
.. вот такой симбиоз и как-бы кооперативный и примитивный RTOS, тем не менее решающий большинство "проблем". :)
В настоящее время это прерывание занимает примерно:
- 30 мкс, если не звучат ноты, т.е. только на LFO, проверки и накладные расходы,
- 130 мкс, если звучат ноты - добавляется вычисление ADSR и управление VCO,
- + 170 мкс на начало ноты, максимум 2 т.е. +340 мкс, итого примерно 470 мкс в худшем случае,
- по оценкам будет еще порядка +100 мкс на вычисление ADSR фильтра и управление фильтром.
итого: порядка 30-250 мкс в среднем, но со всплесками до 400 мкс при старте новой ноты и почти 600 мкс при старте двух нот одновременно.
1. Перетащите весь этот управляющий код в аналог Brain() и сделайте паузу на отрисовку 1 символа в районе 600мксек. Просто вместо millis() Вам придется вызывать micros() в макросе delay() рыбы.
2. Нарисуйте единую функцию отрисовки на дисплей, собирающую строки из глобалов и/или согласно состоянию своих флагов отрисовки, воткните её в loop() одну, единственную. Всё, что в нем ещё есть - добавьте в тот же Brain().
3. Прием от MIDI интерфейса посадите на прерывание RX нужного UART-а. Его задача принять байт в соответствующий глобал и установить флаг "принято" для Brain(). Можно даже завести пару или сколько там потребуется для приема нескольких нот аккордом с модификацией флага "принята 1,2 и т.д. ноты";
4. Ваш Brain() должен выродится в типовой конечный автомат: "если принята нота", "если нота начала звучать", "если звучит нота" и т.д. Надо просто очень аккуратно выписать все его состояния и возможные переходы между ними. Сильно уверен, что там много ортогональностей и сам код перехода между состояниями не окажется сколько-то сложным.
P.S. Да, и простейший обработчик UART без wiring есть в библиотеке Cyberlib для ATmega328p .. его переделка под мега2560 не составляет никакой проблемы. Да и из Wiring можно выдрать процедуры приема по прерыванию и причесать..
andriano, Вы уже попробовали предложенный вариант? Отпишите, получилось и что. Интересно жеж. :)
Arhat109-2, думаю еще недельку повожусь с фильтром, заодно в голове устаканится распределение работы между ардуинами, тогда дойдут руки и до дисплея.
Интересно, а ведь если на I2C кроме этого одного дисплея нет ничего, то ведь строб "E" можно смело генерить детектируя завершение I2C транзации на SCL. То есть длительность высокого уровня на SCL при высоком уровне на SDA больше заданного RC цепочкой = строб )
Arhat109-2, у меня Ваш тест из 94 поста с библиотекой из 78 поста с переделанным на 4 байта буфером показывает 102 мкс/символ.
Это корректированный lcd1602.h с константой "длина буфера" и измененным lcdPrepare(), lcdInit() под размер буфера в 4 байта. Дополнительно константа LCD_WAIT_1 обрамлена проверкой для её предустановки из скетча. Компилялось и на фото приведена проверка с этой версией файла.
Да, в посту №95 изменена скорость I2C до 880кГц. Мой - тянет, обратите внимание. :)
Ну и как показал замер на нескольких дисплеях - минимальные тайминги все-таки "плавают", но незначительно. Мой дисплей - тоже "ожил" .. тупо пропаял всю линейку контактов к переходнику I2C .. не знаю что там "было не так". Но быстрее 105мсек мой "итого" тоже не кажет.
В целом, вам как-бы "без разницы" сколько мелкосекунд оно выводит, при том подходе что я вам рекомендовал. Перечитайте.
Ну думаю , что все уже поостыли и можно новичку задать дурной вопрос.
Я так и не уловил, что конкретно ускоряет шину. Программист я, можно сказать, хреновый, потому и не уловил.
У меня задача такая - вращаю шаговый двигатель вот этим:
использую Нано с АтМега 328
Двигатель имеет некорою штангу с магнитом, который пролетает мимо двух Холлов, отрабатываю прерывания от каждого датчика
(считаю некоторое время между срабатываниями Холлов, ну и заодно количество нужных оборотов CYC -1). Проблем нет все четко, на монитор порта вылетает это самое время возврата, все вижу, а вот написать на LCD1602 подключенный через I2C номер витка, к примеру, не получается - мотор просто теряет все скоростные характеристики (резко замедляется).
Andriano посоветовал суда заглянуть. Идею понял, но количество знаий языка не позволяет увидитьь это самое решение. Нужна доработанная библиотека? Или как-то с сетапе это устанавливается?
Вот это я не понял. Это влияет на скорость?
Для выввода на LCD экран через PCF8574 и2с регистр - самый быстрый вариант - библиотека Архата для хардварного и2с.
Но у нее есть сложности в использовании, так как у Архата - это побочный продукт идеи многопоточности о которую мы тут уже зубы сточили ;).
Еще особеннсть - библиотека Архата работает через прерывания, поэтому будет мешать иным синхронным процессам. Хотя сам и2с протокол - вообще не критичен к таймингам. Может нужно не быстро, а "в фоновом режиме"?
................
я не очень следил за форумом и не помню, откуда Вас сюда Андриано отправил. Вы задачу свою изложите. Есть много вариантов.
Например можно прекращать получение данных на время их обработки и отображения (так, например, цифровые осцилографы делают). Можно передать отображение - другому кристаллу, тем паче что они по 100-200р стоят. Что у Вас за задача?
Вообще контрноллеры программируют так:
1. выделяют самый критичный к синхронности процесс и цепляют его на прерывания от нужных событий (внешних или по времени);
2. сервисные процессы строят на некритичных к таймингам алгоритмах и протоколах, и запускают их в основном потоке времени.
3. если есть больше одного критичного к таймингам процесса - то возможно следует выбирать подходящий контроллер, который успеет. С приоритетами прерываний, очередями запросов и соответствующей частотой. На низких частотатх это можно эмулировать.
спасибо за быстрый отклик
Может нужно не быстро, а "в фоновом режиме"?
................
Ардуин постоянно занят вращением мотора, поэтому не представляю как организовать "фоновый режим".
А Андриано меня сюда порекомендовал заглянуть из обсуждения подключения матричной клавы к I2C вот отсюда :http://arduino.ru/forum/programmirovanie/podklyuchenie-matrichnoi-klaviatury-po-i2c
Задача: вращением шагового открывать защитный кожух пилы со скоростью не менее 10раз/мин. Затем штанга ротора в конечной точке открытого кожуха будет соскальзывать с захвата кожуха и он будет возвращаться в начало. Это время не должно превышать 0,3сек. Двигатель будет продолжать свое вращение, снова захватывать кожух открывать его , снова доводить его до полного открытия, соскальзывать и т.д. Количество открытий не менее 50000. Вот собственно и все. У меня все прекарсно работает, но вывести номер очередного оборота (открытия) не получается - двигатель резко притормаживает и вообще все зависает. Поэтому ограничился только вывеской "тест идет" и делаю остановку (кнопкой) чтобы посмотреть сколько циклов уже завершено.
Вот эта часть программы - процедура мотор:
Я пока вижу очень наивный код, в котором сочетается неотключенный (вне тестирования) вывод в сериал, который все равно тратит кучу времени и управление шаговым двигателем в основном потоке времени, вместо управления из прерывания, или вообще просто таймером. Тут я не знаю остального кода и чем могут быть заняты таймеры, даже не знаю, на каком контроллере все сделано.
Много просто ошибок: циклы 28-33, 38-43, 47-53, 64-6, и с 72 по 76 - это как? В цикле выводим на дисплей? сколько тысяч раз успеваем это сделать ;) ;) ;)? Ну эта ошибка просто самая веселая, она хоть работать не мешает, просто забавная. Не стану про остальные писать.
В таком коде как у Вас - действительно вывод добавить почти невозможно. Просто код не расширяемый.
Опишите словами ВСЕ задачи контроллера - я напишу, как нужно их решать. Не код напишу а объясню, как его написать самостоятельно.
Можно и код - но Вам это, надеюсь ;), не интересно будет.
В цикле выводим на дисплей? сколько тысяч раз успеваем это сделать ;) ;) ;)?
До тысяч не доходит, всего раз по 15-20.
EuBeginer, суде по приведенному тексту, Вам нужно совершать манипуляции с мотором раз в несколько сотен мкс, тогда как вывод строки на дисплей занимает порядка 25000-30000 мкс. Т.е. самые грубые прикидки говорят, что одновременно с построчным выводом текста мотор нормально работать не сможет.
Наиболее прямой метод порекомендовал wdrakula, но, опять же, нам неизвестно, какие процессы еще происходят в Вашем скетчяе, и как используются аппаратные ресурсы, в частности, таймеры.
В принципе можно организовать вывод текста и в общем цикле работы мотора, только нужно сделать две вещи:
1. Выводить текст не построчно, а по одной букве. Т.е. вклиниваем вывод очередной буквы в то место, где сейчас находятся delayMicroseconds(), уменьшая величину rpm на время вывода буквы. Или, лучше, заменяем delayMicroseconds() на micros() по принципу blink without delay.
2. В несколько раз ускоряем вывод символа, воспользовавшись приведенными в данной теме вариантаии кода. По идее должен занимать прмерно 100 мкс. Так что по идее можно даже выводить не по одному, а сразу порциями по 4 символа (400 мкс < 468 мкс).
//должен занимать прмерно 100 мкс
Не. Писали работает на 800КГц. Значить 13мкс. И за 468мкс одна строка выводится свободно.
Делается так, перед выводом сохраняем время мксек, выводим строку и ждем пока время не дотикает до сохраненное+rpm. И это просто вместо делея.
В общем случае, если времени совсем мало, вывод строки пишем как машинку состояний, посимвольно за один вызов. Ничего сложого.
//нам неизвестно, какие процессы еще происходят в Вашем скетчяе
Какие еще задачи есть - совершенно не важно, если сидим в ожидании десятки или сотни мксек - то можно выводить какое то кол-во символов. Остальное дело техники аппаратного програмирования, если прерывания не трогаем и не мешаем быстрым процессам. Если нам они мешают (частые и долгие, занимают много процессорного времени) - ужимаемся хоть до побитового вывода, вывод одного бита раз в 100мкс (ну это если совсем плохо) даст 1000 байт в секунду. Дай бог прочитать столько с экрана ))
даст 1000 байт в секунду. Дай бог прочитать столько с экрана ))
а, кто-то, вообще, смотрел в даташит экрана? - есть там параметр типа "скорость отклика жидких кристаллов экрана"?
*раньше всех это интересовало, когда жидкие кристаллы были визуально очень медленными.
//должен занимать прмерно 100 мкс
Не. Писали работает на 800КГц. Значить 13мкс. И за 468мкс одна строка выводится свободно.
Тут 3 страницы копья ломали, увеличили скорость вывода в 15 раз, довели до 100 мкс и вдруг - на тебе! 13 мкс!
Код, подтверждающий эти слова, в студию!
Andriano, как продвигается ваш проект? Что-то получилось сделать с выводом как я советовал? Вы как-то забросили тему, напоминаю что лично мне - все ещё интересно. :)
Wdrakula предложил вполне нормальное решение. Мне кажется что его надо просто слегка разжевать автору вопроса..
Arhat109-2, вот только полчаса назад проковырял дырку для дисплея.
То собирал на макете с 16-ю переменниками и 6-ю кнопками. Дисплей было некуда прикрепить. Теперь делаю уже полномасштабное шасси для полного набора как узлов, так и органов управления. В общем, в программировании уткнулся в то, что полностью исчерпал возможности урезанного макета. Так что пока занимаюсь шасси и корпусом (дрель, напильник...), потом перейду к пайке.
Ваш вариант я опробовал в виде отдельного скетча, о чем писал. Когда все соберется в корпусе, думаю, начну как раз с дисплея, а то с макета вся диагностика шла в Serial, а это и долго, и неудобно. Выводить планирую по одной букве, в свободное время, вклинившись в основной цикл опроса датчиков. Так что просто навесить сверху не получится - нужно довольно глубоко интегрировать в имеющийся код.
По срокам планирую закончить весь конструктив-электронику где-то на рождественских каникулах и перейти снова к программированию.
Последние фото макета выкладывал здесь: http://arduino.ru/forum/proekty/analog-analogovogo-sintezatora
Как видите, дисплея там пока нет.
Я пока вижу очень наивный код, в котором сочетается неотключенный (вне тестирования) вывод в сериал, который все равно тратит кучу времени и управление шаговым двигателем в основном потоке времени, вместо управления из прерывания, или вообще просто таймером. Тут я не знаю остального кода и чем могут быть заняты таймеры, даже не знаю, на каком контроллере все сделано.
Много просто ошибок: циклы 28-33, 38-43, 47-53, 64-6, и с 72 по 76 - это как? В цикле выводим на дисплей? сколько тысяч раз успеваем это сделать ;) ;) ;)? Ну эта ошибка просто самая веселая, она хоть работать не мешает, просто забавная. Не стану про остальные писать.
За критику спасибо. Я и не претендую. Циклы..... дапросто заменил delay. Уроки поделал. Весело ,ну и хорошо... Все равно его не брошу, потому что он....
Там просто уже ничего делать не надо. Только на экран смотреть и читать.
Таймеры.... да, не знаю я их не задействовал. Просто не знаю как вращать мотор через прерывания.
Задача... да просто вращать мотор и выполнить пару прерываний.
Вариант с маленькой заплаткой для LiquidCrystall_I2C
отображает символ примерно за 124 мкс, что в 5 раз быстрее стандартного, на 20% медленнее, чем у Arhat, но при этом использует стандартный Wire.
Ну и на всякий случай используемый здесь массив для отображения (названия инструментов General MIDI)
Andriano, спасибо за конкретное предложение.
Попробую, но в принципе все работает. Просто интересно было номер цикла видеть в момент вращения.
даст 1000 байт в секунду. Дай бог прочитать столько с экрана ))
а, кто-то, вообще, смотрел в даташит экрана? - есть там параметр типа "скорость отклика жидких кристаллов экрана"?
Ну я смотрел, а чего? Имею право ;) Я там режими всякие скролинга вычитывал. Прикольные. Скорости отклика жидких кристаллов экрана там нету из-за отсутствия таковых. Он без жидких кристалов, он светодиодный и органический как навоз. Есть частота кадровой развертки, до сотни Гц вроде.
Ну я смотрел, а чего? Имею право ;) Я там режими всякие скролинга вычитывал. Прикольные. Скорости отклика жидких кристаллов экрана там нету из-за отсутствия таковых. Он без жидких кристалов, он светодиодный и органический как навоз. Есть частота кадровой развертки, до сотни Гц вроде.
ты название темы читал? - LiquidCrystal_I2C
LiquidCrystal, блин!
//должен занимать прмерно 100 мкс
Не. Писали работает на 800КГц. Значить 13мкс. И за 468мкс одна строка выводится свободно.
Тут 3 страницы копья ломали, увеличили скорость вывода в 15 раз, довели до 100 мкс и вдруг - на тебе! 13 мкс!
Код, подтверждающий эти слова, в студию!
Так смотрите в начале темы и код и скрин. 124мсек на тест. Возможности экрана упираются в возможности шины i2c. А на шине i2c удается реализовать вывод 100 строк по 10 символов в строке по 8 байт на символ (округляем до 10 т.к. команды адрес и т.д. по крайней мере у меня так в этом примере) итого грубо 10000 байт за 124мсек. Ну такой тест там, не я его даже придумал. Итого вывод байта - порядка 13мксек на байт. И еще два сотоварища в диспуте участвующие получили сходные результаты. И теория какбы намекает, что 800кГц - это период тактирования 1,25мкс, т.е.8 бит + АСК 11,25мкс плюс усушка утруска старты- стопы дето около 13мксек и выходит снова.
Возможно Вы посчитали что "Писали работает на 800КГц. Значить 13мкс." относится к времени вывода символа - так это не так, это на байт. А сколько байт для 1602 нужно на символ 4-5-6 я толком из этой темы и не понял, Вы там дискутировали много и гаряче, так озвучте четкий результат. Но даже при худшем: 6*13=78мс 468/78=6 символов, чего явно хватит для того чтоб "вывести номер очередного оборота" как хочет EuBeginer.
Ну я смотрел, а чего? Имею право ;) Я там режими всякие скролинга вычитывал. Прикольные. Скорости отклика жидких кристаллов экрана там нету из-за отсутствия таковых. Он без жидких кристалов, он светодиодный и органический как навоз. Есть частота кадровой развертки, до сотни Гц вроде.
ты название темы читал? - LiquidCrystal_I2C
LiquidCrystal, блин!
Не только читал, но и писал. Здесь уже давно не только про это несчастье речь, и даже скорей не о ней, а о скорости вывода на экран.
Не только читал, но и писал. Здесь уже давно не только про это несчастье речь, и даже скорей не о ней, а о скорости вывода на экран.
ну, так пиздуйте в тему про большие экраны, пока я здесь выясняю, накуя нужна скорость за которой не успевает жидкий кристалл.
///скорость за которой не успевает жидкий кристалл.
Это те дальше на йух и буераками. Здесь три страницы про обмен с экраном по i2c накрапали, и без тебя справились. И достигнуты скорости вывода такие, что не то ше жидкий кристал не успевает, даже глаз человечий не различает. А им все мало!
Возможно Вы посчитали что "Писали работает на 800КГц. Значить 13мкс." относится к времени вывода символа - так это не так, это на байт. А сколько байт для 1602 нужно на символ 4-5-6 я толком из этой темы и не понял, Вы там дискутировали много и гаряче, так озвучте четкий результат. Но даже при худшем: 6*13=78мс 468/78=6 символов, чего явно хватит для того чтоб "вывести номер очередного оборота" как хочет EuBeginer.
Ну, вообще-то байтовая скорость в денной теме никакого иньтереса не представляет. Тем более, что в процессе обсуждения и экспериментоыв удалось уменьшить количество байтов, передаваемых на 1 символ, с 12 до 5.
Но даже если она кого-то интересует, притом, интересуют "грязные" байты, т.е. кроме данных включающие и служебную информацию, все равно меньше 21-25 мкс на байт не получается.
ну, так пиздуйте в тему про большие экраны, пока я здесь выясняю, накуя нужна скорость за которой не успевает жидкий кристалл.
Объясняю по буквам:
Д а л е к о н е в к а ж д о м п р о е к т е е д и н с т в е н н а я р а б о т а А р д у и н о - в ы д а в а т ь д а н н ы е н а э к р а н . Б ы в а е т , э к р а н - ч и с т о в с п о м о г а т е л ь н о е у с т р о й с т в о , а А р д у и н а з а н я т а д р у г и м б о л е е в а ж н ы м д е л о м . И т р е б у е т с я , ч т о б ы э к р а н н е о т н и м а л с л и ш к о м м н о г о в р е м е н и у о с н о в н о г о а л г о р и т м а .
Вместо срача с троллями, Вы бы лучше выложили что у Вас уже получилось. Интересно жеж каким путем Вы пошли в конце концов и куда дошли.. ;)
С наступающим Новым Годом всех участников!
Объясняю по буквам:
объясни мне по буквам, почему во время выдачи данных на экран, блокируется работа других "важных дел"?
*а, если экран способен принять 1 символ в минуту(механический экран, допустим, а весь интерфейс на концевиках), то что, блин?
Вместо срача с троллями, Вы бы лучше выложили что у Вас уже получилось. Интересно жеж каким путем Вы пошли в конце концов и куда дошли.. ;)
Ну, вообще-то сейчас как раз в процессе работы: поочередно то паяю очередную плату (дабы обеспечить ввод данных от 43 потенциометров, 22 кнопок и двух валкодеров), то вожусь с подключением этого всего, включая библиотеки (как они называются в Ардуино - а по простому - пары *.h и *.cpp). Увы, стандартного 20-санитиметрового дюпона немного не хватает, а при включеннии друг в друга последовательно, дисплей работает неустойчиво.
Но в целом идея такая: при возникновении потребности вывести строку на дисплей вызывается метод, запоминающий эту строку. А затем в цикле loop() вызывается метод run(), который проверяет, есть ли что на вывод, и если "да", выводит на экран очередную букву из запомненной строки.
Исходная библиотека LiquidCrystal_I2C не используется, из нее была вытащена, не вникая в подробности, инициализация дисплея и факически доприсаны 3 метода:
- прием и запоминание строки, которую нужно вывести,
- прием и запоминание числа, которое нужно вывести,
- вывод очередного символа из запомненной строки, буде такая обнаружится.
Т.е. основной алгоритм прерывается не более чем на 124 мкс.
Но все это пока довольно сырое.
объясни мне по буквам, почему во время выдачи данных на экран, блокируется работа других "важных дел"?
А он по-любому блокируется. Вопрос лишь, на какой интервал времени. Если до 300 мкс, меня это устраивает, если больше - нет.
В результате обсуждения данной темы:
- я отказался от идеи выводить строку целиком - вывожу по одному символу.
- время вывода одного символа удалось сократить с 1500 до 124 мкс, что меня вполне устраивает.
*а, если экран способен принять 1 символ в минуту(механический экран, допустим, а весь интерфейс на концевиках), то что, блин?
Вообще-то принять и визуализировать - это не одно и то же. Тот же ЖК способен принять за 124 мкс, а визуализироваь - десятки, если не сотни мс.
Но если он именно принимает 1 символ в минуту, то мне такой не подходит.
В принципе, если минуту на визуализацию - тоже не подходит. Человек должен своевременно получать на дисплее отклик на вращение ручки.
Выяснил, что по крайней мере мой дисплей работает на 800 МГц неустойчиво - после прогрева то зависает, то не инициализируется после перезагрузки. И, подозрение, что блокирует контроллер, т.к. последний перестает реагировать на внешние воздействия.
Переключил на 400 МГц. Один символ выводится за 184 мкс. Вроде, работает устойчиво.
У меня таже проблема. Контроллер в основном цикле измерят частоту вращения двигателя и дополнительно каждые 0.5 сек передает данные ны дисплей за счет длинной передачи подтормаживает основной цикл. Скачал заплатку из поста 124 но она не компелируется из -за #include "MIDI_Disp.c". В инете ничего не нашел. Что это такое и как скомпелировать.
В MIDI_disp.c лежат те строки, которые нужно выводить на экран.
Начало выглядит так:
Используйте вместо него файл с теми строками, что нужны Вам.
PS. Посмотрел еще раз: в том же самом посте №124 лежит файл MIDI_disp.c целиком. Так что можете его оттуда скопировать и подключить. Если это Вам поможет.
Спасибо за ответ.
Но прошу некоторые разяснения. Во первых у меня выводиться переменная float которая расчитывается каждые 10 мс, а во вторых как подключить файл.
Если это вопрос, то не могли бы Вы его переформулировать так, чтобы было понятно, о чем идет речь?
Как передавать на дисплей значения переменной (float) не из массива а полученные в программе. Если возможно то на примере.
На дисплей нельзя передать значение переменной float. На дисплей можно передать только:
1. Команду установки курсора.
2. Символ.
3. Строку.
Выбирайте, что Вам больше нравится.
Спасибо за разъяснения,
жаль что регулятор частоты не может выдавать на индикатор саму частоту вращения. Я конечно выкрутился и отправляю но одному знаку F, “”,5,0,,,1,””,H,z но это как то не красиво.
Я конечно выкрутился и отправляю но одному знаку F, “”,5,0,,,1,””,H,z но это как то не красиво.
что мешает сначала сформировать из символов строчку, а потом выводить?
Да, я как раз об этом читал, но никогда такими преобразованиями не занимался.
В строчке переменная с плавающей запятой.
жаль что регулятор частоты не может выдавать на индикатор саму частоту вращения.
Нет, на индикатор, конечно, можно выдать и саму частоту. Только, боюсь, он не будет знать, что с ней делать, т.к. привык получать на вход команды либо данные, но никак не частоту.
Еще раз спасибо за участие, но оно сподвигло меня решить эту проблему. Возможно коряво, но результат на лицо. В моей программе рассчитывается частота с датчика, который работает с 60-ти зубовым колесом и эта частота формата float
На индикатор должна выводиться частота генератора, которая = частоте датчика / 30, с точностью один знак после запятой. Я создал временную переменную формата int и поделил частоту не на 30, а на 3. В результате чего получил число формата int в 10 раз большее, чем частота генератора. Не 51,5 Гц, а 515. Далее я конвертирую это число с помощью оператора itoa в одномерную матрицу формат char. И это я проделываю 1 раз в секунду. Мне надо на индикатор передать такую строку F 50.1 Hz. Затем я делаю матрицу на 9 позиций, где третья, четвертая и шестая позиции числа из матрицы, полученной оператором itoa и я ее передаю на индикатор. Сам процесс передачи, наблюдаемый осциллографом занимает около 2.5 мс, что меня вполне устраивает. При обычной передаче это занимает в 3-4 раза больше время.
Меня еще интересует, все ли время занят контроллер этой передачей или само участие в этом процессе контроллера еще меньше.
P.S. кстати в строку можно преобразовать и число формата float, но выкрутасов будет значительно больше.