прерывание вывода 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 раз. У меня вопрос: почему иногда светодиод мигает быстрее во много раз, когда вывод прерывания ни к чему не подключен (открыт)? Разве у него не было внутреннего подтягивающего резистора или мне следует использовать внешний подтягивающий резистор в моей схеме?
Спасибо
@juliussin, 👍0
Обсуждение2 ответа
Лучший ответ:
Это не просто ответ, это некоторые комментарии и предложения по вашему вопросу. программа, которая, я надеюсь, поможет вам писать более чистый код:
Поскольку вы, похоже, избегаете использования базовой библиотеки Arduino, вы можете а также написать свой собственный
main()вместо того, чтобы полагаться на тот, что есть в ядро. Таким образом, у вас есть простая программа на языке C, которую можно скомпилировать либо внутри, либо вне фреймворка Arduino.Вместо непрозрачных констант, таких как
0b00100000, используйте имена битов. Эти названия определены в техническом описании и предоставлены avr-libc. Обычно они используются с макросом_BV(), который расширяется до(1<<(x)).Для тех из нас, у кого в голове нет полного технического описания, Полезны комментарии или два, описывающие, что делает каждый бит.
Чтобы изменить только один бит из порта, используйте синтаксис
PORTB |= ...;илиPORTB &= .... Компилятор достаточно умен, чтобы превратить это в отдельные инструкции по сборке, а именноsbiиcbi.Вместо того, чтобы повторять десять раз одни и те же строки, используйте цикл.
Применяя эти предложения, ваша программа будет выглядеть примерно так:
#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 в соответствующий бит в регистре 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
- PCINT0, PCINT1, PCINT2 и т. д. на ATtiny45/85
- Как назначить прерывание на нажатие кнопки с помощью ATtiny? (прерывание не срабатывает с моим кодом)
- Прерывание переполнения таймера AVR не работает
- ATtiny85 AC Phase Control для регулировки яркости лампочки
- Прерывать каждую секунду на ATTiny84 (и спать все остальное)
- Attiny85 Watchdog срабатывает только один раз
- Attiny85 Таймер 1 компаратор B не работает должным образом (в то время как A работает)
- ATtiny85 со сном и последовательным портом
Внутреннее подтягивание произойдет только в том случае, если вы активно включите это внутреннее подтягивание., @Majenko
@Majenko, так как включить внутреннюю подтяжку и использовать ее как вывод прерывания? Возможно ли это?, @juliussin
Функциональность прерываний полностью отделена от функциональности ввода-вывода. Убедитесь, что вывод является ВХОДОМ, и установите на нём ВЫСОКИЙ уровень, чтобы включить подтягивающий резистор. Конечно, вам нужно знать, какой DDR и PORT для вывода вы используете для прерывания., @Majenko
Вам действительно не следует создавать прерывания, выполнение которых занимает несколько *секунд*. Прерывания должны завершаться быстро, чтобы основная программа могла вернуться к выполнению., @RubberDuck