прерывание вывода attiny85 нестабильно

Я пытаюсь научиться программировать MC, так что, возможно, я не очень много знаю о MC. Я попытался проверить прерывание вывода ATtiny85 с помощью этого простого кода

#include <avr/io.h>
#include <avr/interrupt.h>

void setup()
{
  GIMSK = 0b00100000;
  PCMSK = 0b00000001;
  sei();
  DDRB = 0b00010000;
}

void loop()
{
  PORTB = 0b00010000;
  _delay_ms(500);
  PORTB = 0b00000000;
  _delay_ms(500);
}

ISR(PCINT0_vect)
{
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);
  PORTB = 0b00010000;
  _delay_ms(100);
  PORTB = 0b00000000;
  _delay_ms(100);

}

Это просто мигающий код с прерыванием, чтобы заставить светодиод мигать быстрее в 10 раз. У меня вопрос: почему иногда светодиод мигает быстрее во много раз, когда вывод прерывания ни к чему не подключен (открыт)? Разве у него не было внутреннего подтягивающего резистора или мне следует использовать внешний подтягивающий резистор в моей схеме?

Спасибо

, 👍0

Обсуждение

Внутреннее подтягивание произойдет только в том случае, если вы активно включите это внутреннее подтягивание., @Majenko

@Majenko, так как включить внутреннюю подтяжку и использовать ее как вывод прерывания? Возможно ли это?, @juliussin

Функциональность прерываний полностью отделена от функциональности ввода-вывода. Убедитесь, что вывод является ВХОДОМ, и установите на нём ВЫСОКИЙ уровень, чтобы включить подтягивающий резистор. Конечно, вам нужно знать, какой DDR и PORT для вывода вы используете для прерывания., @Majenko

Вам действительно не следует создавать прерывания, выполнение которых занимает несколько *секунд*. Прерывания должны завершаться быстро, чтобы основная программа могла вернуться к выполнению., @RubberDuck


2 ответа


Лучший ответ:

0

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

  1. Поскольку вы, похоже, избегаете использования базовой библиотеки Arduino, вы можете а также написать свой собственный main() вместо того, чтобы полагаться на тот, что есть в ядро. Таким образом, у вас есть простая программа на языке C, которую можно скомпилировать либо внутри, либо вне фреймворка Arduino.

  2. Вместо непрозрачных констант, таких как 0b00100000, используйте имена битов. Эти названия определены в техническом описании и предоставлены avr-libc. Обычно они используются с макросом _BV(), который расширяется до (1<<(x)).

  3. Для тех из нас, у кого в голове нет полного технического описания, Полезны комментарии или два, описывающие, что делает каждый бит.

  4. Чтобы изменить только один бит из порта, используйте синтаксис PORTB |= ...; или PORTB &= .... Компилятор достаточно умен, чтобы превратить это в отдельные инструкции по сборке, а именно sbi и cbi.

  5. Вместо того, чтобы повторять десять раз одни и те же строки, используйте цикл.

Применяя эти предложения, ваша программа будет выглядеть примерно так:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

ISR(PCINT0_vect)
{
    for (int i = 0; i < 10; i++) {
        PORTB |= _BV(PB4);
        _delay_ms(100);
        PORTB &= ~_BV(PB4);
        _delay_ms(100);
    }
}

int main(void)
{
    PORTB = _BV(PB0);     // внутренний подтягивающий резистор на PB0
    GIMSK = _BV(PCIE);    // включить прерывание по смене контакта
    PCMSK = _BV(PCINT0);  // определение изменения контакта на PCINT0 = PB0
    DDRB  = _BV(PB4);     // вывод PB4 как выход
    for (;;) {
        PORTB |= _BV(PB4);
        _delay_ms(500);
        PORTB &= ~_BV(PB4);
        _delay_ms(500);
    }
}
,

Ого, спасибо большое, это очень полезно. Очень полезно для новичков. А для чего нужны две точки с запятой? Это как while(1)? Поскольку существует так много руководств по библиотеке Arduino, довольно сложно найти примеры кода на чистом C (или нет?)., @juliussin

for(;;) — это бесконечный цикл, как и while(1)., @chrisl

Решил ли этот код вашу проблему с двойным выполнением, о которой вы упомянули ниже?, @chrisl

@juliussin: Как заметил @chrisl, for (;;) вводит бесконечный цикл. Я предпочитаю его вместо while (true), потому что мне кажется естественным читать его как «for (ever)»., @Edgar Bonet

@chrisl Я только что попробовал этот код, и он не решил проблему с двойным ISR, если я подключил вывод прерывания к земле перемычкой (я использую макетную плату для подключения ATtiny85), и с повторным ISR, если я коснулся перемычки. И, к сожалению, у меня нет осциллографа, чтобы проверить сигнал на выводе прерывания :(, @juliussin

Это из-за того, что кнопка тактильно отскакивает? Кстати, это просто моё мнение., @juliussin

Я думал об этом, но ваш ISR гораздо длиннее, чем должен быть любой дребезг от кнопки. Во время ISR никакое другое прерывание того же типа не может быть поставлено в очередь, я думаю., @chrisl


1

Внутренний подтягивающий резистор активируется только в том случае, если вы активируете его в своем коде. Для этого вам нужно записать 1 в соответствующий бит в регистре PORTB, в то время как регистр DDRB настроен на то, чтобы вывод был входом.

Таким образом, прерывание срабатывает случайным образом, поскольку вывод был плавающим.

Поскольку вы в настоящее время всегда пишете во весь PORTB, вам нужно изменить последний бит на 1 при каждой записи PORTB. Или вы можете записать 1 в него в setup() и изменить только один выходной бит в PORTB с помощью

PORTB ^= 0b00010000;

Это переключит только ваш выходной контакт.

,

Ого, спасибо! Я использую PORTB, потому что хочу понять, как работают регистры, и избежать зависимости от библиотеки. Поэтому, следуя вашему совету, я изменил последний бит на 1 для каждого PORTB, который я написал. Думаю, это работает довольно хорошо. Но если я коснусь контакта прерывания рукой, запуск ISR повторится. Это связано с тем, что триггер нестабилен, если я его коснусь?, @juliussin

Этого не должно происходить, если вы активировали внутренний подтягивающий резистор. Я попробовал это с моим ATTiny85, и всё работает отлично., @chrisl

Я активировал внутреннюю подтяжку, как вы и сказали, изменив последний бит на 1 на каждом PORTB, который я записал. Но с помощью человека он всё ещё нестабилен. И ещё одна проблема: если я подключаю вывод прерывания к VCC, ничего не происходит (ведь он подтянут, верно?), но если я подключаю его к GND, то ISR выполняется дважды, а затем я отпускаю его, и ISR выполняется один раз., @juliussin

Странное поведение (при двойном запуске). Мне не удалось воспроизвести это на моём оборудовании. Возможно, у вас что-то не так с проводкой., @chrisl