attiny13 таймер то работает, то нет

Обсуждаем контроллеры компании Atmel.
Аватара пользователя
shonty
Мучитель микросхем
Сообщения: 473
Зарегистрирован: Ср янв 11, 2012 18:20:26

Re: attiny13 таймер то работает, то нет

Сообщение shonty »

oleg_4rk писал(а):Теперь вопрос, разве доступ к DDR* на чтение что-то меняет?
Направление вывода на выход должно быть настроено. Если вы про это.
Реклама
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

[uquote="shonty",url="/forum/viewtopic.php?p=4642322#p4642322"]
oleg_4rk писал(а):Теперь вопрос, разве доступ к DDR* на чтение что-то меняет?
Направление вывода на выход должно быть настроено. Если вы про это.[/uquote]

Это да. Но я не про это. Я про то, что, судя по всему, вот эта строка:

Код: Выделить всё

if (!(*pins[pin].type_reg & pins[pin].mask))
как раз и ломает всё. Где, pins[pin].type_reg = &DDRB.
Ардуинщик. Не шарю в электронике.
Реклама
Adrift
Вымогатель припоя
Сообщения: 543
Зарегистрирован: Вт окт 01, 2024 15:22:33

Re: attiny13 таймер то работает, то нет

Сообщение Adrift »

Oleg_4rk, у тебя в принципе не рабочий код, проблема как минимум в неатомарности записи в порт. Допустим в цикле происходит чтение из PORTB, модификация, затем вызывается прерывание в котором опять читается значение PORTB, тоглится бит и значение записывается обратно, после возврата из прерывания в PORTB пишется значение в котором бит затоглен еще не был. Следовательно нужно применять операции SBI/CBI, которые другие биты не затрагивают. Для тогла можно писать 1 в PINB. Работает не на всех AVR, но у tiny13 такое есть.
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

[uquote="Adrift",url="/forum/viewtopic.php?p=4642328#p4642328"]Oleg_4rk, у тебя в принципе не рабочий код, проблема как минимум в неатомарности записи в порт. Допустим в цикле происходит чтение из PORTB, модификация, затем вызывается прерывание в котором опять читается значение PORTB, тоглится бит и значение записывается обратно, после возврата из прерывания в PORTB пишется значение в котором бит затоглен еще не был. Следовательно нужно применять операции SBI/CBI, которые другие биты не затрагивают. Для тогла можно писать 1 в PINB. Работает не на всех AVR, но у tiny13 такое есть.[/uquote]

Блин. Точно. Похоже дело в этом. Сейчас попробую исправить.
Ардуинщик. Не шарю в электронике.
Реклама
Эиком - электронные компоненты и радиодетали
Огонёк
Опытный кот
Сообщения: 776
Зарегистрирован: Вт авг 27, 2024 19:11:47

Re: attiny13 таймер то работает, то нет

Сообщение Огонёк »

Забористо вас вкрячило. Тринадцатая тинька через ардуину... Тинька - это строго асм, даже Си тут излишен, чрезмерен и бесполезен. А уж абдурина...
Реклама
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

[uquote="Adrift",url="/forum/viewtopic.php?p=4642328#p4642328"]Oleg_4rk, у тебя в принципе не рабочий код, проблема как минимум в неатомарности записи в порт. Допустим в цикле происходит чтение из PORTB, модификация, затем вызывается прерывание в котором опять читается значение PORTB, тоглится бит и значение записывается обратно, после возврата из прерывания в PORTB пишется значение в котором бит затоглен еще не был. Следовательно нужно применять операции SBI/CBI, которые другие биты не затрагивают. Для тогла можно писать 1 в PINB. Работает не на всех AVR, но у tiny13 такое есть.[/uquote]

Чё-т фигня какая-то выходит. sbi/cbi хотят константы. Т.е. мне в pin_write() их не получается применить. Ничего с переменными не нашёл, поэтому всё свелось с манипуляциями с SREG:

Код: Выделить всё

void
pin_write(uint8_t pin, uint8_t val)
{
    uint8_t sreg_prev;

    if (!(*pins[pin].type_reg & pins[pin].mask))
        return;
    
    sreg_prev = SREG;
    cli();
    if (val)
        *pins[pin].out_reg |= pins[pin].mask;
    else
        *pins[pin].out_reg &= ~pins[pin].mask;
    SREG = sreg_prev;
}
Но не работает чё-т. Добавил в код включение 3й ноги до while и внутри включение 1й ноги если SREG I-бит выставлен. ВОт так конец main выглядит:

Код: Выделить всё

    // ~100us
    OCR0A = 15;
    sei();
    PORTB |= _BV(3);
    while (1) {
        if (bit_is_set(SREG, 7))
            PORTB |= _BV(1);
        else
            PORTB &= ~(_BV(1));

        if (pwm_width > pwm_width_on)
            pin_write(PIN_D2, 1);
        else if (pwm_width < pwm_width_off)
            pin_write(PIN_D2, 0);
    }
Вырубаю питание и смотрю что при включении. И вот такая забавная фигня выходит - Изображение

Первая строка это 0й пин, который по таймеру меняется; вторая - меняется при SREG; третья - до while.
Вложения
screenshot.png
(4.98 КБ) 327 скачиваний
Ардуинщик. Не шарю в электронике.
Реклама
Adrift
Вымогатель припоя
Сообщения: 543
Зарегистрирован: Вт окт 01, 2024 15:22:33

Re: attiny13 таймер то работает, то нет

Сообщение Adrift »

[uquote="oleg_4rk",url="/forum/viewtopic.php?p=4642392#p4642392"]ВОт так конец main выглядит:

Код: Выделить всё

    // ~100us
    OCR0A = 15;
    sei();
    PORTB |= _BV(3);
    while (1) {
        if (bit_is_set(SREG, 7))
            PORTB |= _BV(1);
        else
            PORTB &= ~(_BV(1));

        if (pwm_width > pwm_width_on)
            pin_write(PIN_D2, 1);
        else if (pwm_width < pwm_width_off)
            pin_write(PIN_D2, 0);
    }
[/uquote]
Так ничего не поменялось, теперь в pin_write() есть отключение прерываний, но добавились всякие PORTB |= _BV(1), где никакой защиты нет )
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

Да блин :-). Точно... Чё-т торможу под вечер уже. Ща.

Добавлено after 6 minutes 7 seconds:
Не помогло :-). ВОт такой код сейчас:

Код: Выделить всё

    // ~100us
    OCR0A = 15;
    sei();
    asm("sbi %[io], %[bit]" :: [io] "I" (_SFR_IO_ADDR(PORTB)), [bit] "I" (3));
    while (1) {
        if (bit_is_set(SREG, 7))
            asm("sbi %[io], %[bit]" :: [io] "I" (_SFR_IO_ADDR(PORTB)), [bit] "I" (1));
        else
            asm("cbi %[io], %[bit]" :: [io] "I" (_SFR_IO_ADDR(PORTB)), [bit] "I" (1));

        if (pwm_width > pwm_width_on)
            pin_write(PIN_D2, 1);
        else if (pwm_width < pwm_width_off)
            pin_write(PIN_D2, 0);
    }

Картина та же.
Запутался я уже. Оно и до этого не работало в общем, т.к. я ж эти дополнительные выводы 0/1 на ноги начал добавлять именно из-за того, что манипуляции с SREG не сработали. Или где-то ещё проблема.
Ардуинщик. Не шарю в электронике.
Adrift
Вымогатель припоя
Сообщения: 543
Зарегистрирован: Вт окт 01, 2024 15:22:33

Re: attiny13 таймер то работает, то нет

Сообщение Adrift »

Я не могу проверить, потому что AVR давно не занимаюсь. Если не работает, нужно упрощать... С пустым циклом все нормально? А если в цикле будут только SBI/CBI c задержками между ними?
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

Кстати, PORTB |= компилятором и так переводится в sbi.

Код: Выделить всё

  97:test.c        ****                 if (bit_is_set(SREG, 7))
 181                            .loc 1 97 3 view .LVU37
 182                            .loc 1 97 6 is_stmt 0 view .LVU38
 183 0044 0FB6                  in __tmp_reg__,__SREG__
 184 0046 07FE                  sbrs __tmp_reg__,7
 185 0048 00C0                  rjmp .L4
  98:test.c        ****                         PORTB |= _BV(1);
 186                            .loc 1 98 4 is_stmt 1 view .LVU39
 187                            .loc 1 98 10 is_stmt 0 view .LVU40
 188 004a C19A                  sbi 0x18,1
 189                    .L5:
  99:test.c        ****                 else
 100:test.c        ****                         PORTB &= ~(_BV(1));
 101:test.c        **** 

...
 214                    .L4:
 100:test.c        **** 
 215                            .loc 1 100 4 view .LVU50
 100:test.c        **** 
 216                            .loc 1 100 10 is_stmt 0 view .LVU51
 217 006e C198                  cbi 0x18,1
 218 0070 00C0                  rjmp .L5
Вот здесь об этом написано - https://www.nongnu.org/avr-libc/user-ma ... __sfr.html

Добавлено after 7 minutes 14 seconds:
[uquote="Adrift",url="/forum/viewtopic.php?p=4642411#p4642411"]Я не могу проверить, потому что AVR давно не занимаюсь. Если не работает, нужно упрощать... С пустым циклом все нормально?[/uquote]

Так да.
А если в цикле будут только SBI/CBI c задержками между ними?
Так тоже. Код:

Код: Выделить всё

        while (1) {
                PORTB |= _BV(1);
                _delay_us(200);
                PORTB &= ~(_BV(1));
        }
Изображение
Вложения
screenshot.png
(9.66 КБ) 284 скачивания
Ардуинщик. Не шарю в электронике.
Adrift
Вымогатель припоя
Сообщения: 543
Зарегистрирован: Вт окт 01, 2024 15:22:33

Re: attiny13 таймер то работает, то нет

Сообщение Adrift »

[uquote="oleg_4rk",url="/forum/viewtopic.php?p=4642413#p4642413"]Кстати, PORTB |= компилятором и так переводится в sbi.[/uquote]
Не обязательно. Кстати, возможно именно поэтому убирание проверки DDR из начала pin_write() влияет на результат. Лишний код может заставить компилятор оставить функцию таковой, то есть не инлайнить ее и в таком случае там будет универсальный код работающий с адресами памяти, а не SBI/CBI.

Добавлено after 1 minute 55 seconds:
[uquote="oleg_4rk",url="/forum/viewtopic.php?p=4642413#p4642413"]Так тоже. Код:

Код: Выделить всё

        while (1) {
                PORTB |= _BV(1);
                _delay_us(200);
                PORTB &= ~(_BV(1));
        }
[/uquote]
Так ничего не увидишь, большая пауза минимизирует вероятность того, что прерывание произойдет в момент когда регистр PORTB был прочитан, но еще не записан. Пауза должна быть в несколько NOP. И опять же тут компилятор SBI/CBI наверняка вставит, если он умеет это делать...
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

[uquote="Adrift",url="/forum/viewtopic.php?p=4642417#p4642417"][uquote="oleg_4rk",url="/forum/viewtopic.php?p=4642413#p4642413"]Кстати, PORTB |= компилятором и так переводится в sbi.[/uquote]
Не обязательно. Кстати, возможно именно поэтому убирание проверки DDR из начала pin_write() влияет на результат. Лишний код может заставить компилятор оставить функцию таковой, то есть не инлайнить ее и в таком случае там будет универсальный код работающий с адресами памяти, а не SBI/CBI.[/uquote]

Ну хз. Так в доке написано. Давай верну как было с asm-вставками.
Так ничего не увидишь, большая пауза минимизирует вероятность того, что прерывание произойдет в момент когда регистр PORTB был прочитан, но еще не записан. Пауза должна быть в несколько NOP.
Так тоже работает норм таймер. Но 1й пин теперь вначале часто дёргается, а потом реже :-).
Изображение

Код:

Код: Выделить всё

        while (1) {
                asm("sbi %[io], %[bit]" :: [io] "I" (_SFR_IO_ADDR(PORTB)), [bit] "I" (1));
                asm("nop");
                asm("nop");
                asm("nop");
                asm("cbi %[io], %[bit]" :: [io] "I" (_SFR_IO_ADDR(PORTB)), [bit] "I" (1));
        }
Добавлено after 9 minutes 30 seconds:
[uquote="Adrift",url="/forum/viewtopic.php?p=4642328#p4642328"]Следовательно нужно применять операции SBI/CBI, которые другие биты не затрагивают. Для тогла можно писать 1 в PINB. Работает не на всех AVR, но у tiny13 такое есть.[/uquote]

Сейчас глянул, кстати, насчёт этого. Удобная штука. В attiny25/45/85, atmega48/88/168/328/16u4/32u4 оно есть.
Вложения
screenshot.png
(8.01 КБ) 262 скачивания
Ардуинщик. Не шарю в электронике.
Adrift
Вымогатель припоя
Сообщения: 543
Зарегистрирован: Вт окт 01, 2024 15:22:33

Re: attiny13 таймер то работает, то нет

Сообщение Adrift »

Запустил код в Compiler Explorer, для pin_write(), даже без проверки DDRB, генерит без SBI/CBI(-mmcu=attiny13 -Og):

Код: Выделить всё

ldd r25,Z+6
or r24,r25
st X,r24
Даже если инлайнить SBI не появляются. А для простых PORTB |= 4 они уже есть.

Похоже простейший цикл в котором будет RMW над портом, как и в pin_write(), должен выглядеть так:

Код: Выделить всё

    volatile uint16_t portb = 0x38;
    while(1)
    {
        *(volatile uint8_t*)portb |= 1;
        *(volatile uint8_t*)portb &= ~1;
    }
Из-за volatile компилятор не знает во время компиляции, что имеет дело с PORTB и на SBI/CBI ничего не заменяет.
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

Согласно доке PORTB |= 4; всегда будет заменяться на sbi и такое же с cbi. А дальше, насколько у оптимизатора получается вычислить константы на этапе компиляции. Но это сейчас не важно. Теперь-то код такой:

Код: Выделить всё

void
pin_write(uint8_t pin, uint8_t val)
{
        uint8_t sreg_prev;

        if (!(*pins[pin].type_reg & pins[pin].mask))
                return;
        
        sreg_prev = SREG;
        cli();
        if (val)
                *pins[pin].out_reg |= pins[pin].mask;
        else
                *pins[pin].out_reg &= ~pins[pin].mask;
        SREG = sreg_prev;
}
И по идее перезапись не должна пересекаться с прерыванием. Но чё-т не то.
Ардуинщик. Не шарю в электронике.
Аватара пользователя
shonty
Мучитель микросхем
Сообщения: 473
Зарегистрирован: Ср янв 11, 2012 18:20:26

Re: attiny13 таймер то работает, то нет

Сообщение shonty »

Не по теме
СпойлерКогда читаю подобные изыскания, невольно задумываюсь..
если asm это ногодрыг, то си и выше это чего_дрыг?
Adrift
Вымогатель припоя
Сообщения: 543
Зарегистрирован: Вт окт 01, 2024 15:22:33

Re: attiny13 таймер то работает, то нет

Сообщение Adrift »

[uquote="oleg_4rk",url="/forum/viewtopic.php?p=4642656#p4642656"]И по идее перезапись не должна пересекаться с прерыванием. Но чё-т не то.[/uquote]
Хедер для cli() точно подключен? Можно написать cli555() и будет лишь предупреждение аж до gcc14, где это уже ошибка. Вместо cli555() компилятор вставляет непонятную хрень "rcall cli555" )

Попробуй так:

Код: Выделить всё

    volatile uint16_t portb = 0x38;
    while(1)
    {
        uint8_t sreg = SREG;
        asm("cli");
        *(volatile uint8_t*)portb |= 2;
        *(volatile uint8_t*)portb &= ~2;
        SREG = sreg;
    }
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

[uquote="Adrift",url="/forum/viewtopic.php?p=4642667#p4642667"]Хедер для cli() точно подключен? Можно написать cli555() и будет лишь предупреждение аж до gcc14, где это уже ошибка. Вместо cli555() компилятор вставляет непонятную хрень "rcall cli555" )[/uquote]

Точно :-):

Код: Выделить всё

  13:pin.c         ****         cli();
  58                            .loc 1 13 2 is_stmt 1 view .LVU12
  59                    /* #APP */
  60                     ;  13 "pin.c" 1
  61 0026 F894                  cli
  62                     ;  0 "" 2
  14:pin.c         ****         if (val)
Не знаю насчёт cli555(), а с cli() всё конкректно в avr/interrupt.h прописано:

Код: Выделить всё

# define cli()  __asm__ __volatile__ ("cli" ::: "memory")
Ардуинщик. Не шарю в электронике.
Adrift
Вымогатель припоя
Сообщения: 543
Зарегистрирован: Вт окт 01, 2024 15:22:33

Re: attiny13 таймер то работает, то нет

Сообщение Adrift »

Oleg_4rk, что с моим примером, тоже не работает? Собственно из-за озвученной мной проблемы сплошные нули все равно не должно давать на пинах, значит есть что-то еще. Может и с линковкой что-то, например, в pin_write() вызывается __mulhi3, это длинное умножение и если оно вдруг от другой архитектуры, то там внутри может быть инструкция MUL, которой нет у tiny13 и мк, как вариант, просто виснет и никакой код не выполняется вообще, тогда таки будут одни нули ) А на mega328 все относительно нормально, хотя там, конечно, из-за неатомарности глючит тоже, просто этого можно и не заметить.
oleg_4rk
Встал на лапы
Сообщения: 141
Зарегистрирован: Чт сен 26, 2019 20:42:21

Re: attiny13 таймер то работает, то нет

Сообщение oleg_4rk »

[uquote="Adrift",url="/forum/viewtopic.php?p=4642681#p4642681"]Oleg_4rk, что с моим примером, тоже не работает?[/uquote]

Вот этот код:

Код: Выделить всё

volatile uint16_t portb = 0x38;
    while(1)
    {
        uint8_t sreg = SREG;
        asm("cli");
        *(volatile uint8_t*)portb |= 2;
        *(volatile uint8_t*)portb &= ~2;
        SREG = sreg;
    }
Работает. И даёт такую картину:

Изображение
Собственно из-за озвученной мной проблемы сплошные нули все равно не должно давать на пинах, значит есть что-то еще. Может и с линковкой что-то,
Похоже на то. Хз. Если всё вставляю в программу и собираю без своей библиотеки, то получается так:
Изображение

Вроде работает, но какие-то провалы присутствуют. Но хотя бы работает таймер :-).
Вложения
screenshot.png
(5.38 КБ) 117 скачиваний
screenshot.png
(11.24 КБ) 111 скачиваний
Ардуинщик. Не шарю в электронике.
Adrift
Вымогатель припоя
Сообщения: 543
Зарегистрирован: Вт окт 01, 2024 15:22:33

Re: attiny13 таймер то работает, то нет

Сообщение Adrift »

[uquote="oleg_4rk",url="/forum/viewtopic.php?p=4642699#p4642699"]Вроде работает, но какие-то провалы присутствуют. Но хотя бы работает таймер :-).[/uquote]
Провалы присутствуют потому что прерывания отключаются и хотя флаг таймер всегда выставляет через одинаковое время в обработчик периодически будет попадать с опозданием, не раньше момента включения прерываний.
Ответить

Вернуться в «AVR»