Відповісти

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

Назад

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

jokeR
Вчора 18:10:04

потрібно було не з першого канала почінати, а з 5...

Або return в кожний if
Взагалі по можливості треба уникати копіпасти з вкладеними if ами. Потім складно зрозуміти що там взагалі відбувається. Особливо якщо ні з debug_ером ні з print не підлізти.

dimich
Вчора 17:49:40
AS31979 пише:

"Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження" - легко

А, просто else забули.
Ну якщо Shag приймає тільки два значення, 1 і 2, то можна без другого if:

if (Shag == 1) {
    ...
} else {
} ...

Або просто в кінці гілки робити return;

AS31979 пише:

я так розумію це асемблер

Ні, там ніякого асемблера нема, тільки C++. Навіть ніяких особливостей від ++ не використовується, як C код він теж валідний.

AS31979
Вчора 16:59:59

"Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження" - легко:

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% причеплю у кінець скетчу на усякий випадок!!!

dimich
2026-07-23 20:10:47
AS31979 пише:

виявилось що наступний імпульс формується відразу після закінчення формування попередн"ого.

А де ви обчислюєте значення для OCR після скидання піна, щоб наступне переривання виникало через потрібний проміжок часу?

Це якщо з одним перериванням, як в Servo, і як ви зараз робите. А можна з двома.
Щоб фаза для кожного канала була постійна, зробіть тайм-слот для кожного канала. Для 4 каналів рівномірно - по 5 мс виходить. На початку тайм-слота виставляєте пін у високий рівень та програмуйте OCR, в кінці тайм-слота скидайте пін у низький рівень.
Початок тайм слота - по перериванню COMPA, значення OCR1A визначає тривалість всього тайм-слота - 5 мс.  Кінець - по COMPB. Значення OCR1B визначає тривалість високого рівня.
Там іще є варіанти, але поки не буду ускладнювати.

AS31979 пише:

Знов довелося перейти на switch    case

Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження. Щось там не так.

AS31979 пише:

Хто може ЛЮДСКОЮ мовою пояснити як працює цей код

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 і т.д.).

AS31979
2026-07-23 19:32:03

№70 - Приблизно так я код і переписав.
         З прериванями теж зрозуміло - де може вилізти боком теж, я маю надію схитрити з часовими затримками і перейти на byte - Serv_1_1..., поки що для тестування проканало і так.

AS31979
2026-07-23 19:22:30

Доповнення - обробку преривання довелося знов переписати, при тестуваны осцилографом виявилось що наступний імпульс формується відразу після закінчення формування попередн"ого.
Знов довелося перейти на 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 на відповідний вивід і програмування таймеру...

dimich
2026-07-23 17:32:57
AS31979 пише:

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

AS31979 пише:

№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:
      ...
  }

Але все одно, вважаю такий код - збочення.

AS31979 пише:

"потрібно оновлювати з вимкеними перериваннями" - детальніше можна, у поточній редакції  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;
AS31979
2026-07-23 16:27:41

№47 - може окремий if  і виконується швидше(я спочатку їх і використав), але   switch    case дозволив обійтись без goto...

№49 - "для таких ПрофесіАналів у програмувані як я важливіше розуміти як воно працює"... Ваш шматок коду вигляда красиво, але якщо я ще приблизно розумію як він працює, то самостійно змінити за потреби 100% не зможу!!!

"потрібно оновлювати з вимкеними перериваннями" - детальніше можна, у поточній редакції  word Serv_1_1

dimich
2026-07-23 16:01:22
jokeR пише:

Правильні макроси спрощують життя.

Та з макросами теж можна намудрити, якщо зловживати. Хіба так багато місць у програмі де конфігурується таймер, чи потрібно перемикатись між декількома конфігураціями в рантаймі?
Якщо хочеться високорівневих абстракцій, то краще вже робити засобами C++. Ну то таке, якщо нема вимог до стилистики коду, то справа особистих вподобань.

jokeR
2026-07-23 14:13:12

Ну если всё записано в одну строчку, то в принципе нормально.

Для сіоністів може і нормально, (1 << CS10)| (.... wink

Краще вже як у лінуксоїдів

#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.  */
Kino
2026-07-23 12:50:03
jokeR пише:

взагалі не задумуючись. Правильні макроси спрощують життя.

Ну если всё записано в одну строчку, то в принципе нормально.
В принципе удобно записывать все биты регистра, т.к. визуально видно как они расположены в самом мк (по ДШ).

jokeR
2026-07-23 08:25:27

надо писать именно так, как хочет конкретный компилятор, и пофиг что у другого по другому, они никогда не будут совместимы в этом плане.

Кросплатформна/кроскомпіляторна розробка добавляє складності. Таке життя. Я більше про те що часто копіпастять код виду

(0 << CS10)

взагалі не задумуючись. Правильні макроси спрощують життя.

Kino
2026-07-22 21:44:40
dimich пише:

Я маю на увазі з точки зору стандарту мови програмування в загальному. Він нічого "не знає" про біти, флаги та інструкції.

Ну в целом логично, если число знаковое, то он не понимает как можно сдвинуть знак без самого, собственно, числа.

dimich
2026-07-22 21:35:38
Kino пише:

Всегда казалось, что сдвиг это команда LSL (LSR) с установкой флага переноса

Це вже деталі імплементації для конкретної архітектури. Я маю на увазі з точки зору стандарту мови програмування в загальному. Він нічого "не знає" про біти, флаги та інструкції.

Kino
2026-07-22 21:23:07
dimich пише:

Саме так. А при зсуві signed:

Хм. Надо почитать, никогда не задавался этим вопросом. Всегда казалось, что сдвиг это команда LSL (LSR) с установкой флага переноса и этому флагу ваще побоку знаковое число или нет, просто ячейка, что выпадает переносится во флаг и по нему уже идет анализ установлен был бит или нет. Понятно что по этому же флагу (carry) определяется знак, но в случае "обычного" сдвига как бы зачем?

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