Відповісти

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

Назад

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

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 пише:

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

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

jokeer
2025-08-27 22:40:30

Пояснити, як працює код, коли не показано жодного рядка - це вищий пілотаж wink
Подивіться, який код згенеровано для обробки переривання. Все іде до того,  що пора добряче упоротись в архітектуру avr.

MikeM
2025-08-27 22:25:52

Закинув я подалі ідею підлаштовувати час виконання фрагментів програми і вирішив використати переривання від таймера. Працює, але не без несподіваного сюрприза. Раніше нормально виводилися 23 символи в рядку, з використанням переривання тільки 17. При спробі вивести 18й символ (а місце для нього є і на екрані, і на осциллограмі) таймер збивається. Таке враження, що, якщо обробка переривання не завершується за 4 мкс до наступного переривання, то це має негативний вплив. Хто-небудь може щось прояснити?

dimich
2025-08-27 16:54:03
jokeR пише:

Колись учили ассемблер x86, там все якось більш зрозуміло було wink

x86 CISC, регістрів менше, а інструкцій більше. А тут RISC, все навпаки smile

jokeR
2025-08-27 16:47:44

Точно wink Дякую.
Колись учили ассемблер x86, там все якось більш зрозуміло було wink

dimich
2025-08-27 14:46:00
jokeR пише:

Цікаві досліди winkЗаписуємо 0 після i14? наче в С коді не було. Чи це вже щось наступне?

Старший байт 16-бітної змінної. У автора там наче б то індекс для масиву зі шрифтом.

jokeR
2025-08-27 14:22:16

Цікаві досліди wink

        sts     0x0000, r1      ; 0x<__SREG__+0x7fffc1> R_AVR_16        i14+0x1

Записуємо 0 після i14? наче в С коді не було. Чи це вже щось наступне?

dimich
2025-08-27 12:54:35

Для демонстрації, мабуть, красивіше так:

$ avr-objdump -dzrwC --no-address --no-show-raw-insn --visualize-jumps test.o
...
<foo()>:
        lds     r24, 0x0000     ; 0x<__SREG__+0x7fffc1> R_AVR_16        serialBuf+0x1
        bst     r24, 2
        eor     r24, r24
        bld     r24, 0
        sts     0x0000, r24     ; 0x<__SREG__+0x7fffc1> R_AVR_16        i14
        sts     0x0000, r1      ; 0x<__SREG__+0x7fffc1> R_AVR_16        i14+0x1

Або просто продукувати лістинг компілятором

$ avr-g++ -mmcu=atmega328p -Os -S -o - test.cc

Але з -flto після лінковки кінцевий результат може бути зовсім іншим, так що це дуже приблизно.

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