Ви не увійшли.
І ніяких бібліотек.
А 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: тільки розбудити процесор зі сплячки і завершитись.
Покажіть же, як ініціалізуєте таймер, що виконується в обробнику, як синхронізуєте переривання з рештою коду.
Хто-небудь може щось прояснити?
При наявній на даний момент інформації - можуть хіба що екстрасенси-телепати.
Пояснити, як працює код, коли не показано жодного рядка - це вищий пілотаж
Подивіться, який код згенеровано для обробки переривання. Все іде до того, що пора добряче упоротись в архітектуру avr.
Закинув я подалі ідею підлаштовувати час виконання фрагментів програми і вирішив використати переривання від таймера. Працює, але не без несподіваного сюрприза. Раніше нормально виводилися 23 символи в рядку, з використанням переривання тільки 17. При спробі вивести 18й символ (а місце для нього є і на екрані, і на осциллограмі) таймер збивається. Таке враження, що, якщо обробка переривання не завершується за 4 мкс до наступного переривання, то це має негативний вплив. Хто-небудь може щось прояснити?
Колись учили ассемблер x86, там все якось більш зрозуміло було
x86 CISC, регістрів менше, а інструкцій більше. А тут RISC, все навпаки
Точно Дякую.
Колись учили ассемблер x86, там все якось більш зрозуміло було
Цікаві досліди
Записуємо 0 після i14? наче в С коді не було. Чи це вже щось наступне?
Старший байт 16-бітної змінної. У автора там наче б то індекс для масиву зі шрифтом.
Цікаві досліди
sts 0x0000, r1 ; 0x<__SREG__+0x7fffc1> R_AVR_16 i14+0x1
Записуємо 0 після i14? наче в С коді не було. Чи це вже щось наступне?
Для демонстрації, мабуть, красивіше так:
$ 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 після лінковки кінцевий результат може бути зовсім іншим, так що це дуже приблизно.