Ви не увійшли.
дофига не дофига, но я, например, окромя gcc пользовался еще codevision и iar. и как то кумарило, что попадались такие макросы, что свойственны конкретному компилятору.
![]()
в сторонніх лібах може бути що завгодно.
Неактивний
в сторонніх лібах може бути що завгодно.
Не в либах дело. Например, для размещения данных во флеш, у gcc используется PROGMEM, у IAR-а __flash, у CVAVR просто flash. Свои директивы. Это же касается и асм вставок, надо писать именно так, как хочет конкретный компилятор, и пофиг что у другого по другому, они никогда не будут совместимы в этом плане.
Неактивний
Kino пише:Посмотрел. Раскладывается в тот же #define bit(b) (1UL << (b)).
1UL. В avr-libc просто 1.
та дурня. UL это просто unsigned long, т.е. захватывает 4 байта. Если я напишу 1ULL это будет восемь байт, то шо, я еще круче?
Неактивний
Не в либах дело. Например, для размещения данных во флеш, у gcc используется PROGMEM
В gcc є підтримка named address spaces (ISO/IEC DTR 18037), так що в C __flash працює (для читання генерується lpm). В arduino, яке базується на C++, такого не буде.
Это же касается и асм вставок, надо писать именно так, как хочет конкретный компилятор, и пофиг что у другого по другому, они никогда не будут совместимы в этом плане.
Бо inline assembler не є частиною стандарту, це розширення конкретного компілятора.
Неактивний
та дурня. UL это просто unsigned long, т.е. захватывает 4 байта. Если я напишу 1ULL это будет восемь байт, то шо, я еще круче?
Суть не в long, а в unsigned. Поведінка (1 << 15) при 16-бітному int не визначена стандартом. Те ж саме з (1 << 31) на платформах з 32-бітним int.
Неактивний
В gcc є підтримка named address spaces (ISO/IEC DTR 18037), так що в C __flash працює (для читання генерується lpm). В arduino, яке базується на C++, такого не буде.
Та блин, эти стандарты уже так разрослись и отличаются, что уже не уследишь. )) Например, под тот же esp32, если написал достаточно объемный проект на родном фреймворке esp-idf, то адаптировать, чтобы он собрался на фрейме arduino тот еще гемор, проще переписать заново.
Неактивний
Поведінка (1 << 15) при 16-бітному int не визначена стандартом.
За стандарт не скажу, честно не знаю. Но помню книжка писала, что если не указано явно unsigned, то по умолчанию переменная всегда signed.
Неактивний
Та блин, эти стандарты уже так разрослись и отличаются, что уже не уследишь. ))
Стандартів для мов програмування не так уже й багато. Але так, вендори заліза клепають свої фреймворки з велосипедами на костилях. Тут нічого не поробиш, се ля ві.
Неактивний
За стандарт не скажу, честно не знаю. Но помню книжка писала, что если не указано явно unsigned, то по умолчанию переменная всегда signed.
Саме так. А при зсуві signed:
The result of E1 << E2 is E1 left-shifted E2 bit positions; ... If E1 has a signed type and nonnegative value, and E1 × 2^E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
Остання редакція dimich (2026-07-22 21:11:31)
Неактивний
Саме так. А при зсуві signed:
Хм. Надо почитать, никогда не задавался этим вопросом. Всегда казалось, что сдвиг это команда LSL (LSR) с установкой флага переноса и этому флагу ваще побоку знаковое число или нет, просто ячейка, что выпадает переносится во флаг и по нему уже идет анализ установлен был бит или нет. Понятно что по этому же флагу (carry) определяется знак, но в случае "обычного" сдвига как бы зачем?
Остання редакція Kino (2026-07-22 21:30:43)
Неактивний
Всегда казалось, что сдвиг это команда LSL (LSR) с установкой флага переноса
Це вже деталі імплементації для конкретної архітектури. Я маю на увазі з точки зору стандарту мови програмування в загальному. Він нічого "не знає" про біти, флаги та інструкції.
Остання редакція dimich (2026-07-22 21:37:15)
Неактивний
Я маю на увазі з точки зору стандарту мови програмування в загальному. Він нічого "не знає" про біти, флаги та інструкції.
Ну в целом логично, если число знаковое, то он не понимает как можно сдвинуть знак без самого, собственно, числа.
Неактивний
надо писать именно так, как хочет конкретный компилятор, и пофиг что у другого по другому, они никогда не будут совместимы в этом плане.
Кросплатформна/кроскомпіляторна розробка добавляє складності. Таке життя. Я більше про те що часто копіпастять код виду
(0 << CS10)взагалі не задумуючись. Правильні макроси спрощують життя.
Неактивний
взагалі не задумуючись. Правильні макроси спрощують життя.
Ну если всё записано в одну строчку, то в принципе нормально.
В принципе удобно записывать все биты регистра, т.к. визуально видно как они расположены в самом мк (по ДШ).
Неактивний
Ну если всё записано в одну строчку, то в принципе нормально.
Для сіоністів може і нормально, (1 << CS10)| (.... ![]()
Краще вже як у лінуксоїдів
#define R_OK 4 /* Test for read permission. */
#define W_OK 2 /* Test for write permission. */
#define X_OK 1 /* Test for execute permission. */
#define F_OK 0 /* Test for existence. */Неактивний
Правильні макроси спрощують життя.
Та з макросами теж можна намудрити, якщо зловживати. Хіба так багато місць у програмі де конфігурується таймер, чи потрібно перемикатись між декількома конфігураціями в рантаймі?
Якщо хочеться високорівневих абстракцій, то краще вже робити засобами C++. Ну то таке, якщо нема вимог до стилистики коду, то справа особистих вподобань.
Неактивний
№47 - може окремий if і виконується швидше(я спочатку їх і використав), але switch case дозволив обійтись без goto...
№49 - "для таких ПрофесіАналів у програмувані як я важливіше розуміти як воно працює"... Ваш шматок коду вигляда красиво, але якщо я ще приблизно розумію як він працює, то самостійно змінити за потреби 100% не зможу!!!
"потрібно оновлювати з вимкеними перериваннями" - детальніше можна, у поточній редакції word Serv_1_1
Неактивний
якщо я ще приблизно розумію як він працює, то самостійно змінити за потреби 100% не зможу!!!
Якщо що - питайте, поясню. Там лише C-шні масиви та арифметика.
Ваші окремі змінні Serv_1_1, Serv_1_2, Serv_2_1, Serv_1_2 і т.д. просто переїхали в масив. Тепер вони доступні як ocr[0][0], ocr[0][1], ocr[1][0], ocr[1][1] і т.д.
№47 - може окремий if і виконується швидше(я спочатку їх і використав), але switch case дозволив обійтись без goto...
Звідки там goto береться? У вас завжди лише одна гілка виконується, і нічого більше. Робіть return в кінці кожної гілки:
switch (Serv) {
case 1:
switch (Shag) {
case 1:
digitalWrite(2, HIGH);
OCR1A = Serv_1_1;,
Shag=2;
return;
case 2:
digitalWrite(2, LOW);
OCR1A = Serv_1_2;
Shag=1;
Serv=2;
return;
}
case 2:
...
case 3:
...
case 4:
...
}Але все одно, вважаю такий код - збочення.
"потрібно оновлювати з вимкеними перериваннями" - детальніше можна, у поточній редакції word Serv_1_1
word - це unsigned int, тобто 16 біт в AVR. 8-бітний процесор пише в 16-бітну змінну побайтово, двома інструкціями. Коли переривання виникає між цими двома інструкціями, змінна виявляється оновлена не повністю, і обробник переривання читає некоректне значення.
Щоб обробник гарантовано прочитав коректне значення, обробку переривань на момент запису потрібно вимкнути.
На рівні фреймворку:
noInterrupts();
Serv_1_1 = new_value;
interrupts();Або на рівні avr-libc:
cli();
Serv_1_1 = new_value;
sei();Або для універсального коду, який може виконуватись як в основному потоці, так і в контексті переривання:
uint8_t sreg = SREG;
cli();
Serv_1_1 = new_value;
SREG = sreg;Неактивний
Доповнення - обробку преривання довелося знов переписати, при тестуваны осцилографом виявилось що наступний імпульс формується відразу після закінчення формування попередн"ого.
Знов довелося перейти на switch case
Хто може ЛЮДСКОЮ мовою пояснити як працює цей код:
static const uint8_t pins[4] = { 2, 4, 7, 8 };
static volatile uint16_t ocr[4][2];
ISR ...
{
uint8_t idx = Serv;
Serv = (idx + 1) % 8;
uint8_t channel = idx / 2;
uint8_t state = idx % 2;
digitalWrite(pins[channel], !state);
OCR1A = ocr[channel][state];
}Бо моїх знань вистачіло щоб зрозуміти масив з номерами виводів, запис 0/1 на відповідний вивід і програмування таймеру...
Неактивний
№70 - Приблизно так я код і переписав.
З прериванями теж зрозуміло - де може вилізти боком теж, я маю надію схитрити з часовими затримками і перейти на byte - Serv_1_1..., поки що для тестування проканало і так.
Остання редакція AS31979 (2026-07-23 19:32:24)
Неактивний
виявилось що наступний імпульс формується відразу після закінчення формування попередн"ого.
А де ви обчислюєте значення для OCR після скидання піна, щоб наступне переривання виникало через потрібний проміжок часу?
Це якщо з одним перериванням, як в Servo, і як ви зараз робите. А можна з двома.
Щоб фаза для кожного канала була постійна, зробіть тайм-слот для кожного канала. Для 4 каналів рівномірно - по 5 мс виходить. На початку тайм-слота виставляєте пін у високий рівень та програмуйте OCR, в кінці тайм-слота скидайте пін у низький рівень.
Початок тайм слота - по перериванню COMPA, значення OCR1A визначає тривалість всього тайм-слота - 5 мс. Кінець - по COMPB. Значення OCR1B визначає тривалість високого рівня.
Там іще є варіанти, але поки не буду ускладнювати.
Знов довелося перейти на switch case
Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження. Щось там не так.
Хто може ЛЮДСКОЮ мовою пояснити як працює цей код
4 канала, для кожного канала пін потрібно змінювати 2 раза. Виходить 4*2 = 8 станів.
Serv на кожному виклику циклічно приймає значення від 0 до 7: 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, ...
Із цього значення обчислюєм номер канала, від 0 до 3:
uint8_t channel = idx / 2;І номер крока, від 0 до 1 (по факту - інвертований стан піна).
uint8_t state = idx % 2;Їхні значення виходять такі
Serv channel state
0 0 0
1 0 1
2 1 0
3 1 1
4 2 0
5 2 1
6 3 0
7 3 1
0 0 0
1 0 1
2 1 0
...Виставляєм пін, що відповідає поточному каналу, у відповідний стан:
digitalWrite(pins[channel], !state);Програмуєм OCR на наступну ітерацію:
OCR1A = ocr[channel][state];Тривалість високого рівня на каналі k визначається значенням ocr[k][0] (аналогічно як у вашому коді Serv_1_1, Serv_2_1 і т.д.), тривалість низького рівня - ocr[k][1] (у вашому коді Serv_1_2, Serv_2_2 і т.д.).
Остання редакція dimich (2026-07-23 20:14:05)
Неактивний
"Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження" - легко:
if (Serv==1) { // Проверка для какого канала управления производится формирование сигнала(канал №1)
if (Shag==1) { // Проверка что нужно сделать, шаг 1 установить высокий уровень и запрограмировать
OCR1A = Serv_1_1; // регистр совпадений для формирования требуемой длины управляющего импулься
digitalWrite(2, HIGH); // Установка на выводе 2 (канал №1) высокого уровня, начало формирования импульса
Shag=2; // Величина Serv_1_1, длина импульса вычисляется в основном цикле
goto Of_canel_1; // Для того чтобы при следующем прерывании перети к шагу 2
}
if (Shag==2) {
OCR1A = Serv_1_2; // Величина Serv_1_2, время до формирования на следуещем канале импульса вычисляется в основном цикле
digitalWrite(2, LOW); // Шаг 2 - устанавливаем низкий уровень на выводе 2(канал 1)
Shag=1; // Для того чтобы правильно сформировать начало импульса на следующем канале
Serv=2; // Для того чтобы перейти к формированию сигнала на выводе 4(канал №2)
}
} // Конец формирования сигнала для канала 1
Of_canel_1:
// Получился косяк который пришлось исправлять через GOTO, пришлось переписывать на Switch - case - break;
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
if (Serv==2) {.........Я просто протупив - if виконуються послідовно, а я у для Serv 2-му циклі робив Serv=2;, після чого шов оператор if (Serv==2) {......... який за задумом мав виконуватися лише при наступному преривані...
Тільки с"огодні дійшло що потрібно було не з першого канала почінати, а з 5...
Виходило що спочатку формуються 5 импульсів, а потім 1 довгий, поки дивився сигнал на одному каналі усе чудово, а потім почав вираховувати затримки для таймерів, а воно не стикується...
Дякую за роз"яснення по роботі коду(я так розумію це асемблер), не факт що використаю у остаточній версії, але 100% причеплю у кінець скетчу на усякий випадок!!!
Неактивний
"Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження" - легко
А, просто else забули.
Ну якщо Shag приймає тільки два значення, 1 і 2, то можна без другого if:
if (Shag == 1) {
...
} else {
} ...Або просто в кінці гілки робити return;
я так розумію це асемблер
Ні, там ніякого асемблера нема, тільки C++. Навіть ніяких особливостей від ++ не використовується, як C код він теж валідний.
Неактивний