Arduino/Attiny85: задержка в ISR и манипулирование портом

Я пытаюсь иметь на выводе инвертированный выход, связанный с другим выходом, воспроизводящим tone(), но есть задержка 35 мкс при частоте 8 МГц или 158 мкс при частоте 1 МГц. Кажется, есть фиксированная задержка 16 мкс +142 тактовых цикла, потому что задержка не обратно пропорциональна тактовой частоте. Они очень-очень длинные! Почему?

Это код:

/* Per ATTINY85:
1: I/O5 PB5 A0 RS 
2: I/O3 PB3 A3          - Geiger inverted earphone out
3: I/O4 PB4 A2          - Geiger earphone out
4: GND
5: I/O0 PB0    MOSI SDA - Battery test/Geiger LED out
6: I/O1 PB1    MISO     
7: I/O2 PB2 A1 SCLK SCL INT0 - Geiger probe in (via NPN transistor)
8: Vcc
*/

#include<avr/sleep.h>

byte state;
volatile byte P;
int B;
int Bo;
byte LED=0;
unsigned long t=0;

void particella() 
{
    P=1;
}

ISR(PCINT0_vect)
{
    if (!(PINB & (1<<PB4)))
        PORTB |= (1<<PB3);
    else 
        PORTB &= ~(1<<PB3);
}

void setup()
{
    pinMode(0, OUTPUT); // Al LED.
    pinMode(2, INPUT); // Dal transistor dal tubo Geiger.
    pinMode(2, INPUT_PULLUP); // Pull-up per il collettore del transistor.
    pinMode(4, OUTPUT); // All'auricolare.
    pinMode(3, OUTPUT); // All'auricolare (copia invertita del 4).

    GIMSK = 0x60;    // turns on external and pin change interrupts.
    PCMSK = 0x10;    // turn on interrupts on pin PB4.
    sei();           // set interrupts (enable).

    tone(4,2000,100); // Power on beep
    PORTB|=0b00000001;
    delay(700);
    PORTB&=0b11111110;
    delay(1000);
    readVcc();

    for(byte n=1; n<=state; n++)
    {
        PORTB|=0b00000001; // Accende il LED su I/O0 = PB0
        tone(4,2000,25); // Bip acuto a ogni lampo.
        delay(30);
        PORTB&=0b11111110; //  Spegne il LED su I/O0 = PB0
        delay(250);
    }
    delay(350);

    attachInterrupt(0, particella, FALLING);
}


void loop()
{
    if(P)
    {
        P=0;
        t=millis();
        PORTB|=0b00000001; // Accende il LED su I/O0 = PB0
        LED=1;
        tone(4,1000,5); // Fa TIC nell'auricolare.
    }

    if(millis()-t>=10 && LED==1) 
    {
        LED=0; PORTB&=0b11111110; //  I/O0 = PB0 LED OFF after 10ms.
    }
}

person Gianluca    schedule 18.09.2018    source источник
comment
1) задержка между чем и чем? 2) Как узнать, что есть задержка? 3) Почему вы смешиваете обработку прерываний на основе Arduino (attachInterrupt) и собственные ISR?   -  person AterLux    schedule 19.09.2018


Ответы (1)


Существует много задержек перед выполнением кода ISR.

Во-первых, это аппаратные задержки. Они задокументированы в таблицах данных и включают время, необходимое для того, чтобы заметить и зафиксировать изменение ввода, завершить текущую инструкцию, поместить указатель инструкции в стек и выполнить переход к ISR. введите здесь описание изображения введите здесь описание изображения

Далее идут программные задержки. Сначала происходит переход от вектора прерывания к адресу IST, затем компилятор помещает серию инструкций в начало ISR, чтобы убедиться, что все регистры сохранены, а также имеют правильные ожидаемые значения. Это называется «преамбула».

https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

Вы мало что можете сделать с аппаратной задержкой, но вы можете значительно уменьшить задержку программного обеспечения, особенно для очень простого ISR, такого как ваш. Одним из способов было бы создать «голый» ISR, который сохраняет только те регистры, которые вы изменяете. Для вашего кода выше вы, вероятно, могли бы получить, изменив только один регистр.

Я думаю, что также возможно выполнить вашу функцию ISR без использования каких-либо регистров данных, используя трюк записи в регистр PIN, чтобы немного перевернуть. введите здесь описание изображения

Вы сможете найти больше информации обо всем этом, погуглив, или сообщить здесь, если вам нужно какое-то конкретное руководство по любому из этих пунктов!

Альтернативный подход – использовать встроенный аппаратно инвертированный выход.

Таймер 1 на этой микросхеме имеет встроенный инвертирующий выход, который автоматически выводит инвертированный сигнал, который вы ищете, и задержка между инвертированным и неинвертированным выходами будет намного меньше 1 цикла (при условии, что вы намеренно не добавляете dead time). между переходами).

введите здесь описание изображения

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

person bigjosh    schedule 18.09.2018
comment
Спасибо за ваш ответ. Это нормально, но я вижу задержку в 16 мкс + 142 тактовых цикла или 158 мкс при частоте 1 МГц... Это очень большая задержка! - person Gianluca; 19.09.2018
comment
Задержка постоянна каждый раз? Также имейте в виду, что библиотеки Arduino часто отключают глобальные прерывания. И как выглядит ваша схема? Рассматривали ли вы просто использование сгенерированного аппаратным обеспечением инвертированного вывода внутри попытки сделать это в программном обеспечении? Разница между аппаратными прямыми и инвертированными выходами составляет менее 1 цикла. - person bigjosh; 19.09.2018
comment
Да, задержка постоянная. Тон () переключает булавку; изменение контакта вызывает прерывание; он запускает if PIN B4... (затем) PORT B3..., иначе PORT B3... и B3 переключается в 16us+142 тактовых цикла. - person Gianluca; 21.09.2018
comment
Есть ли причина, по которой вы не хотите использовать аппаратный инвертированный вывод вместо программного? - person bigjosh; 22.09.2018
comment
В предыдущем устройстве я использовал CD40106 для мостового управления пьезоизлучателем. На этот раз я подумал, что было бы намного проще сделать это с помощью программного обеспечения. Я думал, что другой выход мог быть переключен через несколько тактов... - person Gianluca; 22.09.2018
comment
Я думаю, аппаратная часть здесь проще. Установите пару регистров, и вы получите идеальный инвертированный сигнал всегда и автоматически. - person bigjosh; 23.09.2018