Мега: присоединение Interrupt на выводе 18/19/20/21 не работает
Задача: Я пытаюсь создать управление вентилятором с помощью arduino mega. Я генерирую ШИМ-сигнал на контактах 6 и 7 и хочу использовать прерывания для измерения скорости вращения вентиляторов.
Проблема: На контактах 2 и 3 все работает нормально, но я не могу заставить его работать ни на одном из контактов 18-21. Цифры прыгают дико. НО: цифры верны для 0% и 100% нагрузки!
Что я уже пробовал:
- Изменил мой таймер с 1 на 4 (сейчас 4)
- Заменен дисплей, чтобы избавиться от U8g2lib.
- Изменение порядка подключения сигнальных кабелей (и, следовательно, подключенного вентилятора).
- Проверьте сигнал с помощью цифрового осциллографа (все порты выглядят нормально)
- Установите для глобальных переменных ISR значения volatile и byte, чтобы предотвратить опасность обновления.
- Бейся головой об стол.
Ничто не решило мою проблему. Что я упускаю?
#include <Arduino.h>
#include <U8g2lib.h>
#include <Bounce2.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
#define PIN_CONTROL_S 6
#define PIN_CONTROL_L 7
#define PIN_SENSE_1 18
#define PIN_SENSE_2 19
#define PIN_SENSE_3 3
#define PIN_SENSE_4 2
#define PIN_BUTTON 22
#define UPDATE_CYCLE 1000 // Интервал обновления отображения/измерения.
#define SIGNAL_PER_RND 2 // 2 сигнала на оборот
#define DUTY_INC 10 // Увеличение пошлины в % за нажатие кнопки
#define PWM_MAX 320 // режим ШИМ отсчитывает на 320 вверх, затем на 320 вниз (25 кГц)
#define lineOffset 10 // Смещение (y) для каждой строки на дисплее
word duty = 0; // 0-100 рабочий цикл
word dutyS = 0; // 0-320 = рабочий цикл 0-100% для небольших вентиляторов
word dutyL = 0; // 0-320 = рабочий цикл 0-100% для больших вентиляторов
volatile byte counterFan1 = 0;
volatile byte counterFan2 = 0;
volatile byte counterFan3 = 0;
volatile byte counterFan4 = 0;
unsigned long lastDraw = 0;
Bounce btnBouncer = Bounce(PIN_BUTTON, 50);
void setupPwm() {
// Штойерунг
pinMode(PIN_CONTROL_S, OUTPUT);
pinMode(PIN_CONTROL_L, OUTPUT);
// Сенсорен
pinMode(PIN_SENSE_1, INPUT_PULLUP);
pinMode(PIN_SENSE_2, INPUT_PULLUP);
pinMode(PIN_SENSE_3, INPUT_PULLUP);
pinMode(PIN_SENSE_4, INPUT_PULLUP);
// Очистить регистр таймера
TCCR4A = 0;
TCCR4B = 0;
TCCR4C = 0;
TCNT4 = 0;
TCCR4A |= _BV(WGM41); // Установите режим ШИМ, правильная фаза. ТОР - это ICR1. (Режим 10)
TCCR4B |= _BV(WGM43); // Начать BOTTOM, TOP от OCR1x до OCR1x.
TCCR4B |= _BV(CS40); // Пределитель 1:1
TCCR4A |= _BV(COM4A1); // Высокий выходной сигнал OC1A на compareMatch при прямом счете / низкий при обратном счете
TCCR4A |= _BV(COM4B1); // Высокий выходной сигнал OC1B на compareMatch при прямом счете / низкий при обратном счете
OCR4A = dutyS; // устанавливаем начальную пошлину
OCR4B = dutyL; // устанавливаем начальную пошлину
ICR4 = PWM_MAX; // ТОР для TCNTx. 320 => @ ЦП 16 МГц -> 25 кГц ШИМ
}
void setupDisplay() {
u8g2.begin();
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.setFontRefHeightExtendedText();
u8g2.setDrawColor(1);
u8g2.setFontPosTop();
u8g2.setFontDirection(0);
}
void attachInterrups() {
attachInterrupt(digitalPinToInterrupt(PIN_SENSE_1), isrFan1, RISING);
attachInterrupt(digitalPinToInterrupt(PIN_SENSE_2), isrFan2, RISING);
attachInterrupt(digitalPinToInterrupt(PIN_SENSE_3), isrFan3, RISING);
attachInterrupt(digitalPinToInterrupt(PIN_SENSE_4), isrFan4, RISING);
}
void detachInterrups() {
detachInterrupt(digitalPinToInterrupt(PIN_SENSE_1));
detachInterrupt(digitalPinToInterrupt(PIN_SENSE_2));
detachInterrupt(digitalPinToInterrupt(PIN_SENSE_3));
detachInterrupt(digitalPinToInterrupt(PIN_SENSE_4));
}
void setup() {
setupDisplay();
setupPwm();
attachInterrups();
pinMode(PIN_BUTTON, INPUT_PULLUP);
}
void draw(void) {
char output[22]; // В одной строке дисплея может отображаться 21 символ.
float uPerSekS1 = counterFan1; // * 60000/measureDuration/SIGNAL_PER_RND;
float uPerSekS2 = counterFan2; // * 60000/measureDuration/SIGNAL_PER_RND;
float uPerSekL1 = counterFan3; // * 60000/measureDuration/SIGNAL_PER_RND;
float uPerSekL2 = counterFan4; // * 60000/measureDuration/SIGNAL_PER_RND;
int y = 0;
sprintf(output, "Duty : %3d", duty);
u8g2.drawStr(0, y, output);
y += lineOffset;
sprintf(output, "U/sec S1: %6d", (int) uPerSekS1);
u8g2.drawStr(0, y, output);
y += lineOffset;
sprintf(output, "U/sec S2: %6d", (int) uPerSekS2);
u8g2.drawStr(0, y, output);
y += lineOffset;
sprintf(output, "U/sec L1: %6d", (int) uPerSekL1);
u8g2.drawStr(0, y, output);
y += lineOffset;
sprintf(output, "U/sec L2: %6d", (int) uPerSekL2);
u8g2.drawStr(0, y, output);
}
void measure() {
unsigned long measureDuration = millis() - lastDraw;
if (measureDuration >= UPDATE_CYCLE) {
// Деактивировать прерывание, пока мы вычисляем
detachInterrups();
u8g2.clearBuffer();
draw();
u8g2.sendBuffer();
// сбросить состояние
counterFan1 = 0;
counterFan2 = 0;
counterFan3 = 0;
counterFan4 = 0;
lastDraw = millis();
// Повторно активировать прерывание
attachInterrups();
}
}
void loop() {
measure();
btnBouncer.update();
if (btnBouncer.fell()) {
// Кнопка была нажата. Повысить пошлину.
if (duty > 90) {
duty = 0;
} else {
duty += DUTY_INC;
}
dutyS = duty * PWM_MAX / 100;
dutyL = duty * PWM_MAX / 100;
OCR4A = dutyS;
OCR4B = dutyL;
}
}
void isrFan1() {
counterFan1++;
}
void isrFan2() {
counterFan2++;
}
void isrFan3() {
counterFan3++;
}
void isrFan4() {
counterFan4++;
}
ОБНОВЛЕНО: Я изо всех сил старался создать удобочитаемую принципиальную схему. 4-контактные разъемы представляют собой вентиляторы.
@Tarkil, 👍2
Обсуждение1 ответ
Хорошо, я не решил проблему с контактами 18-21, но нашел другое решение: используя PinChangeInterrupts и библиотеку PinChangeInterrupt
Я ничего не менял в настройке оборудования, за исключением переноса проводов с контактов 18/19 на A8/A9. Я изменил свой код, чтобы использовать PinChangeInterrups для двух вентиляторов, возможно, я изменю все 4 вентилятора.
Вот новый код:
#include <Arduino.h>
#include <U8g2lib.h>
#include <Bounce2.h>
#include <PinChangeInterrupt.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
#define PIN_CONTROL_S 6
#define PIN_CONTROL_L 7
//#определить PIN_SENSE_1 18
//#определить PIN_SENSE_2 19
#define PIN_SENSE_1 A8
#define PIN_SENSE_2 A9
#define PIN_SENSE_3 3
#define PIN_SENSE_4 2
#define PIN_BUTTON 22
#define UPDATE_CYCLE 1000 // Интервал обновления отображения/измерения.
#define SIGNAL_PER_RND 2 // 2 сигнала на оборот
#define DUTY_INC 10 // Увеличение пошлины в % за нажатие кнопки
#define PWM_MAX 320 // режим ШИМ отсчитывает на 320 вверх, затем на 320 вниз (25 кГц)
#define lineOffset 10 // Смещение (y) для каждой строки на дисплее
word duty = 0; // 0-100 рабочий цикл
word dutyS = 0; // 0-320 = рабочий цикл 0-100% для небольших вентиляторов
word dutyL = 0; // 0-320 = рабочий цикл 0-100% для больших вентиляторов
volatile byte counterFan1 = 0;
volatile byte counterFan2 = 0;
volatile byte counterFan3 = 0;
volatile byte counterFan4 = 0;
unsigned long measureDuration;
unsigned long lastDraw = 0;
Bounce btnBouncer = Bounce(PIN_BUTTON, 50);
void setupPwm() {
// Штойерунг
pinMode(PIN_CONTROL_S, OUTPUT);
pinMode(PIN_CONTROL_L, OUTPUT);
// Сенсорен
pinMode(PIN_SENSE_1, INPUT_PULLUP);
pinMode(PIN_SENSE_2, INPUT_PULLUP);
pinMode(PIN_SENSE_3, INPUT_PULLUP);
pinMode(PIN_SENSE_4, INPUT_PULLUP);
// Очистить регистр таймера
TCCR4A = 0;
TCCR4B = 0;
TCCR4C = 0;
TCNT4 = 0;
TCCR4A |= _BV(WGM41); // Установите режим ШИМ, правильная фаза. ТОР - это ICR1. (Режим 10)
TCCR4B |= _BV(WGM43); // Начать BOTTOM, TOP от OCR1x до OCR1x.
TCCR4B |= _BV(CS40); // Пределитель 1:1
TCCR4A |= _BV(COM4A1); // Высокий выходной сигнал OC1A на compareMatch при прямом счете / низкий при обратном счете
TCCR4A |= _BV(COM4B1); // Высокий выходной сигнал OC1B на compareMatch при прямом счете / низкий при обратном счете
OCR4A = dutyS; // устанавливаем начальную пошлину
OCR4B = dutyL; // устанавливаем начальную пошлину
ICR4 = PWM_MAX; // ТОР для TCNTx. 320 => @ ЦП 16 МГц -> 25 кГц ШИМ
}
void setupDisplay() {
u8g2.begin();
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.setFontRefHeightExtendedText();
u8g2.setDrawColor(1);
u8g2.setFontPosTop();
u8g2.setFontDirection(0);
}
void attachInterrups() {
// attachInterrupt(digitalPinToInterrupt(PIN_SENSE_1), isrFan1, RISING);
attachPCINT(digitalPinToPCINT(PIN_SENSE_1), isrFan1, RISING);
// attachInterrupt(digitalPinToInterrupt(PIN_SENSE_2), isrFan2, RISING);
attachPCINT(digitalPinToPCINT(PIN_SENSE_2), isrFan2, RISING);
attachInterrupt(digitalPinToInterrupt(PIN_SENSE_3), isrFan3, RISING);
attachInterrupt(digitalPinToInterrupt(PIN_SENSE_4), isrFan4, RISING);
}
void detachInterrups() {
// detachInterrupt(digitalPinToInterrupt(PIN_SENSE_1));
detachPinChangeInterrupt(digitalPinToPCINT(PIN_SENSE_1));
// detachInterrupt(digitalPinToInterrupt(PIN_SENSE_2));
detachPinChangeInterrupt(digitalPinToPCINT(PIN_SENSE_2));
detachInterrupt(digitalPinToInterrupt(PIN_SENSE_3));
detachInterrupt(digitalPinToInterrupt(PIN_SENSE_4));
}
void setup() {
// кли();
setupDisplay();
setupPwm();
attachInterrups();
pinMode(PIN_BUTTON, INPUT_PULLUP);
// sei();
}
void draw() {
char output[22]; // В одной строке дисплея может отображаться 21 символ.
float uPerSekS1 = counterFan1 * 60000 / measureDuration / SIGNAL_PER_RND;
float uPerSekS2 = counterFan2 * 60000 / measureDuration / SIGNAL_PER_RND;
float uPerSekL1 = counterFan3 * 60000 / measureDuration / SIGNAL_PER_RND;
float uPerSekL2 = counterFan4 * 60000 / measureDuration / SIGNAL_PER_RND;
int y = 0;
sprintf(output, "Duty : %3d", duty);
u8g2.drawStr(0, y, output);
y += lineOffset;
sprintf(output, "U/sec S1: %6d", (int) uPerSekS1);
u8g2.drawStr(0, y, output);
y += lineOffset;
sprintf(output, "U/sec S2: %6d", (int) uPerSekS2);
u8g2.drawStr(0, y, output);
y += lineOffset;
sprintf(output, "U/sec L1: %6d", (int) uPerSekL1);
u8g2.drawStr(0, y, output);
y += lineOffset;
sprintf(output, "U/sec L2: %6d", (int) uPerSekL2);
u8g2.drawStr(0, y, output);
}
void measure() {
measureDuration = millis() - lastDraw;
if (measureDuration >= UPDATE_CYCLE) {
// Деактивировать прерывание, пока мы вычисляем
detachInterrups();
u8g2.clearBuffer();
draw();
u8g2.sendBuffer();
// сбросить состояние
counterFan1 = 0;
counterFan2 = 0;
counterFan3 = 0;
counterFan4 = 0;
lastDraw = millis();
// Повторно активировать прерывание
attachInterrups();
}
}
void loop() {
measure();
btnBouncer.update();
if (btnBouncer.fell()) {
// Кнопка была нажата. Повысить пошлину.
if (duty > 90) {
duty = 0;
} else {
duty += DUTY_INC;
}
dutyS = duty * PWM_MAX / 100;
dutyL = duty * PWM_MAX / 100;
OCR4A = dutyS;
OCR4B = dutyL;
}
}
void isrFan1() {
counterFan1++;
}
void isrFan2() {
counterFan2++;
}
void isrFan3() {
counterFan3++;
}
void isrFan4() {
counterFan4++;
}
- Как измерить ультразвуковой датчик без импульсного метода?
- Проблема прерывания библиотеки MPU6050 Arduino Jeff Rowberg
- Таймеры, выводы ШИМ и цифровые выходы на Arduino Mega
- генерировать два сдвинутых по фазе ШИМ-импульса, запускаемых внешним сигналом с частотным разделением, с помощью Arduino uno?
- ATmega328P - проблема с использованием таймера 2 для генерации тона
- Точность синхронизации Arduino nano
- Arduino Mega — включение режима CTC отключает таймер
- Считать данные датчика повторно через указанное время?
Хорошо, может проблема в электронике? Я только что перепроверил свои утверждения и одновременно подключил осциллограф к ардуино, и оказалось, что осци стабилизирует результаты... Я использую подтягивающее сопротивление 5 кОм. Это нормально?, @Tarkil
Я не могу понять, какое отношение рабочий цикл имеет к чтению импульсов. Что будет, если отключить 6 и 7 и повернуть вентиляторы рукой? Вы уверены, что 8-битные счетчики не переполняются при быстром вращении вентилятора?, @Edgar Bonet
@EdgarBonet Если я отключу 6/7, вентиляторы будут работать на 100%. Я забыл упомянуть, что использую 4-контактные вентиляторы для ПК. Они подключены к 12 В и регулируются ШИМ 25 кГц, который я генерирую на третьем выводе. Четвертый контакт является сигнальным контактом с двумя сигналами на раунд., @Tarkil
@EdgarBonet ... и 8 бит не переполняются. Интервал измерения в настоящее время составляет 1 секунду, и я получаю до 50 сигналов за цикл измерения., @Tarkil
что подключено к прерыванию контактов и как?, @Juraj