Відповісти

Введіть повідомлення і натисніть Надіслати
Параметри

Назад

Огляд теми (нові повідомленні вгорі)

jokeR
2025-09-11 09:41:52

https://www.rcgroups.com/forums/showthread.php?1473207-Diy-osd-(Arduino-and-opensource) - тут чувак досить сильно упоровся в цю тему з формуванням OSD на ардуїні.

dimich
2025-09-06 16:58:24
MikeM пише:

Звичайно, обробки, швидше ніж таблична, не існує.

Та не завжди. Залежить від алгоритму та особливостей архітектури. Хоча, спеціалізовані інструкції, по суті, на таких же "таблицях" реалізовані, тільки вони містяться на самому кристалі.
Звісно, в загальному випадку нічого швидше за O(1) не існує, але в конкретних випадках бувають нюанси. В мультизадачному середовищі взагалі, ще питання, що краще: виконати кілька додаткових інструкцій, чи лазити в таблицю через спільний кеш.

MikeM пише:

Однак, не варто було одразу викладати код. JOKEERу весь кайф обламали.

Вибачте, не хотів. Здавалось очевидним. Але там є ще простір для подальшої оптимізації wink

MikeM
2025-09-06 15:06:33

Класний варіант. Звичайно, обробки, швидше ніж таблична, не існує.
Однак, не варто було одразу викладати код. JOKEERу весь кайф обламали.

dimich
2025-09-06 12:00:16
dimich пише:

Звісно, 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;
}
jokeer
2025-09-06 10:57:00

Прикольно wink я заходився гуглити швидкий алгоритм ділення на 5 wink але там дофіга асемблера,  і враховувати такти не дуже й хотілось

dimich
2025-09-06 02:26:12
MikeM пише:

У 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 можна упакувати. Тоді таблиці будуть менші, але виконання трохи довшим.

MikeM
2025-09-05 21:57:38

Більше тижня нічого не писав, бо дуже ретельно працював над проектом. Схоже, те, що було потрібно, отримав. Вийшло виводити на екран 24 текстових рядків по 16 символів плюс 6 інтервалів. Є ідея, як подвоїти кількість символів в рядку за допомогою зовнішньої мікросхеми. Але реалізацію відклав з огляду на поточну неактуальність.
А от ще одну ідею реалізував і екран у мене тепер кольоровий. Що цікаво, навантаження на процессор практично не зросло.
Дякую усім, хто допомагав.
Найскладнішим виявився такий фрагмент:
У 32-розрядному слові в бітах з 12 по 27 міститься двійковий код 16-розрядного числа з фіксованою крапкою. Старші 9 бітів містять цілу частину, молодші 7 бітів - дробову. Потрібно перетворити його в 6-байтовий код для відображення на екран (по одній двійково-десятковій цифрі в байті).
Задача здається нескладною, але це все треба зробити швидше, ніж за 25 мікросекунд (на 16 МГц 8-бітному Arduino).
Це не прохання про допомогу (у мене вже все працює). Це ідея "задачі вихідного дня" для реально творчіх особистостей. Якщо хтось зацікавиться, спробуйте.
Цікаво, яким буде найменший час.

dimich
2025-08-28 12:33:45
MikeM пише:

І ніяких бібліотек.

А SMCR, по-вашому, де визначено? wink На avr-libc і весь ардуіно фреймворк побудований, так що це як раз із бібліотекою. Тільки замість бібліотечної функції чи макроса прямий доступ до регістра. Цілком можна і так, якщо не планується компілювати для контролерів, у яких sleep modes визначаються не через SMCR.

MikeM пише:

Цікаво, що ще пару днів тому я так вже робив. Не запрацювало з-за того, що забув /n/t. Переглянувши бібліотеку зрозумів, у чому був косяк.

Дивно, бо для єдиної інструкції у виразі в asm це не має значення. А для кількох інструкцій без \n була б синтаксична помилка.

jokeer пише:

Не зрозумів роль /n/t

\n для асемблера, бо компілятор спочатку зклеює сусідні строкові літерали, потім передає асемблеру. А асемблеру потрібно розрізняти рядки з мнемоніками. Але для єдиної інструкції це не обовʼязково.
\t для краси, щоби при виводі асемблерного лістингу наступний рядок був з відступом.

jokeer
2025-08-28 11:51:27

Не запрацювало з-за того, що забув /n/t.

Не зрозумів роль /n/t

MikeM
2025-08-28 11:28:53
dimich пише:

...

Дякую за розгорнуту відповідь. Даташит я читав (в моєму про це написано в розділі 14).
Мій працюючий варіант:

SMCR=1; // in setup()

__asm__("sleep/n/t") ; // in loop()

І ніяких бібліотек. Цікаво, що ще пару днів тому я так вже робив. Не запрацювало з-за того, що забув /n/t. Переглянувши бібліотеку зрозумів, у чому був косяк.

jokeer
2025-08-28 08:27:50

Хитро придумано wink

dimich
2025-08-28 05:44:59
MikeM пише:

З ваших відповідей я зрозумів, чого не вистачає. Треба зупиняти процессор!

Так. Може це не єдиний, але, мабуть, найпростіший спосіб синхронізувати виконання коду з перериванням.

Обробник переривання не може почати виконуватись посередині 2- чи 3-тактової інструкції. А організувати цикл чи розгалуження без таких інструкцій неможливо. Щоб забезпечити незмінність затримки між виникненням запиту на переривання та виконанням коду, потрібно, щоб запит виникав, коли процесор знаходиться в режимі idle, тобто виконує інструкцію sleep.

MikeM пише:

Нове питання: як це зробити з 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, який ардуіно фреймворк використовує для своїх потреб.

jokeer
2025-08-28 04:38:09

Треба зупиняти процессор!

Навіщо? Ви плануєте заодно і в енергоспоживання гратись?

MikeM
2025-08-27 23:58:34

Дякую за оперативність. З ваших відповідей я зрозумів, чого не вистачає. Треба зупиняти процессор!
Нове питання: як це зробити з Arduino IDE?

dimich
2025-08-27 23:30:40
MikeM пише:

При спробі вивести 18й символ (а місце для нього є і на екрані, і на осциллограмі) таймер збивається.

Що значить "збивається"?

MikeM пише:

Таке враження, що, якщо обробка переривання не завершується за 4 мкс до наступного переривання, то це має негативний вплив.

4 мкс - це 64 такта. Спробую вгадати, ви помістили в обробник чималий шматок коду, який потребує зберігання/відновлення всіх регістрів. 32 інструкції pop по 2 такта в епілозі обробника, от вам і 4 мкс.
По-хорошому, обробник має складатись з єдиної інструкції reti: тільки розбудити процесор зі сплячки і завершитись.

Покажіть же, як ініціалізуєте таймер, що виконується в обробнику, як синхронізуєте переривання з рештою коду.

MikeM пише:

Хто-небудь може щось прояснити?

При наявній на даний момент інформації - можуть хіба що екстрасенси-телепати.

Підвал форуму