Ви не увійшли.
https://www.rcgroups.com/forums/showthread.php?1473207-Diy-osd-(Arduino-and-opensource) - тут чувак досить сильно упоровся в цю тему з формуванням OSD на ардуїні.
Звичайно, обробки, швидше ніж таблична, не існує.
Та не завжди. Залежить від алгоритму та особливостей архітектури. Хоча, спеціалізовані інструкції, по суті, на таких же "таблицях" реалізовані, тільки вони містяться на самому кристалі.
Звісно, в загальному випадку нічого швидше за O(1) не існує, але в конкретних випадках бувають нюанси. В мультизадачному середовищі взагалі, ще питання, що краще: виконати кілька додаткових інструкцій, чи лазити в таблицю через спільний кеш.
Однак, не варто було одразу викладати код. JOKEERу весь кайф обламали.
Вибачте, не хотів. Здавалось очевидним. Але там є ще простір для подальшої оптимізації
Класний варіант. Звичайно, обробки, швидше ніж таблична, не існує.
Однак, не варто було одразу викладати код. JOKEERу весь кайф обламали.
Звісно, BCD можна упакувати. Тоді таблиці будуть менші, але виконання трохи довшим.
А якщо правильно упакувати, то і таблиці в півтора раза менші, і виконання навіть швидше. Має вийти десь 5.5 мкс.
const uint16_t PROGMEM intr[512] = { 0x000, 0x001, ... 0x510, 0x511 };
const uint16_t PROGMEM frac[128] = { 0x000, 0x008, ... 0x984, 0x992 };
char out[6];
void convert(uint32_t n)
{
char *dst = out;
uint16_t d;
d = pgm_read_word(intr + ((n >> 19) & 511));
*dst++ = d >> 8;
*dst++ = (d >> 4) & 0xf;
*dst++ = d & 0xf;
d = pgm_read_word(frac + ((n >> 12) & 127));
*dst++ = d >> 8;
*dst++ = (d >> 4) & 0xf;
*dst = d & 0xf;
}
Прикольно я заходився гуглити швидкий алгоритм ділення на 5
але там дофіга асемблера, і враховувати такти не дуже й хотілось
У 32-розрядному слові в бітах з 12 по 27 міститься двійковий код 16-розрядного числа з фіксованою крапкою. Старші 9 бітів містять цілу частину, молодші 7 бітів - дробову. Потрібно перетворити його в 6-байтовий код для відображення на екран (по одній двійково-десятковій цифрі в байті).
Задача здається нескладною, але це все треба зробити швидше, ніж за 25 мікросекунд (на 16 МГц 8-бітному Arduino).
Якщо правильно зрозумів вхідний та вихідний формати, то дві таблиці з десятковими цифрами, 3 * (512 + 128) = 1920 байт флеша. Навіть "в лоб", без низькорівневої оптимізації:
const char PROGMEM intr[512 * 3] = {
0, 0, 0,
0, 0, 1,
...
5, 1, 0,
5, 1, 1
};
const char PROGMEM frac[128 * 3] = {
0, 0, 0,
0, 0, 8,
...
9, 8, 4,
9, 9, 2
};
char out[6];
void convert(uint32_t n)
{
char *dst = out;
const char *src = intr + 3*((n >> 19) & 511);
*dst++ = pgm_read_byte(src++);
*dst++ = pgm_read_byte(src++);
*dst++ = pgm_read_byte(src++);
src = frac + 3*((n >> 12) & 127);
*dst++ = pgm_read_byte(src++);
*dst++ = pgm_read_byte(src++);
*dst++ = pgm_read_byte(src++);
}
виходить щось біля сотні тактів, десь 7 мкс.
Звісно, BCD можна упакувати. Тоді таблиці будуть менші, але виконання трохи довшим.
Більше тижня нічого не писав, бо дуже ретельно працював над проектом. Схоже, те, що було потрібно, отримав. Вийшло виводити на екран 24 текстових рядків по 16 символів плюс 6 інтервалів. Є ідея, як подвоїти кількість символів в рядку за допомогою зовнішньої мікросхеми. Але реалізацію відклав з огляду на поточну неактуальність.
А от ще одну ідею реалізував і екран у мене тепер кольоровий. Що цікаво, навантаження на процессор практично не зросло.
Дякую усім, хто допомагав.
Найскладнішим виявився такий фрагмент:
У 32-розрядному слові в бітах з 12 по 27 міститься двійковий код 16-розрядного числа з фіксованою крапкою. Старші 9 бітів містять цілу частину, молодші 7 бітів - дробову. Потрібно перетворити його в 6-байтовий код для відображення на екран (по одній двійково-десятковій цифрі в байті).
Задача здається нескладною, але це все треба зробити швидше, ніж за 25 мікросекунд (на 16 МГц 8-бітному Arduino).
Це не прохання про допомогу (у мене вже все працює). Це ідея "задачі вихідного дня" для реально творчіх особистостей. Якщо хтось зацікавиться, спробуйте.
Цікаво, яким буде найменший час.
І ніяких бібліотек.
А SMCR, по-вашому, де визначено? На avr-libc і весь ардуіно фреймворк побудований, так що це як раз із бібліотекою. Тільки замість бібліотечної функції чи макроса прямий доступ до регістра. Цілком можна і так, якщо не планується компілювати для контролерів, у яких sleep modes визначаються не через SMCR.
Цікаво, що ще пару днів тому я так вже робив. Не запрацювало з-за того, що забув /n/t. Переглянувши бібліотеку зрозумів, у чому був косяк.
Дивно, бо для єдиної інструкції у виразі в asm це не має значення. А для кількох інструкцій без \n була б синтаксична помилка.
Не зрозумів роль /n/t
\n для асемблера, бо компілятор спочатку зклеює сусідні строкові літерали, потім передає асемблеру. А асемблеру потрібно розрізняти рядки з мнемоніками. Але для єдиної інструкції це не обовʼязково.
\t для краси, щоби при виводі асемблерного лістингу наступний рядок був з відступом.
Не запрацювало з-за того, що забув /n/t.
Не зрозумів роль /n/t
...
Дякую за розгорнуту відповідь. Даташит я читав (в моєму про це написано в розділі 14).
Мій працюючий варіант:
SMCR=1; // in setup()
__asm__("sleep/n/t") ; // in loop()
І ніяких бібліотек. Цікаво, що ще пару днів тому я так вже робив. Не запрацювало з-за того, що забув /n/t. Переглянувши бібліотеку зрозумів, у чому був косяк.
Хитро придумано
З ваших відповідей я зрозумів, чого не вистачає. Треба зупиняти процессор!
Так. Може це не єдиний, але, мабуть, найпростіший спосіб синхронізувати виконання коду з перериванням.
Обробник переривання не може почати виконуватись посередині 2- чи 3-тактової інструкції. А організувати цикл чи розгалуження без таких інструкцій неможливо. Щоб забезпечити незмінність затримки між виникненням запиту на переривання та виконанням коду, потрібно, щоб запит виникав, коли процесор знаходиться в режимі idle, тобто виконує інструкцію sleep.
Нове питання: як це зробити з Arduino IDE?
Що потрібно зробити, написано в даташиті, розділи 9.3 Idle Mode, 9.11.1 SMCR – Sleep Mode Control Register.
Як це можна зробити засобами avr-libc, написано в avr/sleep.h. Цей хідер для вашої версії avr-libc лежить у вас на диску в тулчейні.
Якщо коротко (по запису, не по виконанню):
// Один раз на початку:
set_sleep_mode(SLEEP_MODE_IDLE);
...
// Для входу в режим очікування:
sleep_mode();
Але це оверхед з перестраховкою. Можна простіше:
// Один раз на початку:
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
...
// Для входу в режим очікування:
sleep_cpu();
IDE тут ні до чого, код писати можна хоч у блокноті. Чи ви маєте на увазі, зробити засобами фреймворка ардуіно? По-перше, зручно використовувати 16-бітний TIMER1 в режимі CTC, бо при 16 МГц одна горизонтальна лінія відповідає циклу таймера в 508 тактів. По-друге, потрібно як мінімум вимкнути переривання від TIMER0, який ардуіно фреймворк використовує для своїх потреб.
Треба зупиняти процессор!
Навіщо? Ви плануєте заодно і в енергоспоживання гратись?
Дякую за оперативність. З ваших відповідей я зрозумів, чого не вистачає. Треба зупиняти процессор!
Нове питання: як це зробити з Arduino IDE?
При спробі вивести 18й символ (а місце для нього є і на екрані, і на осциллограмі) таймер збивається.
Що значить "збивається"?
Таке враження, що, якщо обробка переривання не завершується за 4 мкс до наступного переривання, то це має негативний вплив.
4 мкс - це 64 такта. Спробую вгадати, ви помістили в обробник чималий шматок коду, який потребує зберігання/відновлення всіх регістрів. 32 інструкції pop по 2 такта в епілозі обробника, от вам і 4 мкс.
По-хорошому, обробник має складатись з єдиної інструкції reti: тільки розбудити процесор зі сплячки і завершитись.
Покажіть же, як ініціалізуєте таймер, що виконується в обробнику, як синхронізуєте переривання з рештою коду.
Хто-небудь може щось прояснити?
При наявній на даний момент інформації - можуть хіба що екстрасенси-телепати.