целостность данных при слишком частых прерываниях
У меня есть кодер, который генерирует ~54000 прерываний в секунду. Это слишком быстро для моего Arduino Uno. Но я все равно пытаюсь понять, что я могу из этого получить. Для науки.
unsigned long tickACount = 0;
unsigned long tickBCount = 0;
void myInterrupt(){
tickACount++;
tickBCount++;
}
Затем в моем основном цикле каждую секунду я печатаю tickACount
и tickBCount
.
Я вижу, что мой arduino способен обрабатывать только около 2050 прерываний в секунду. Ничего удивительного.
Что меня удивляет, так это то, что tickACount
и tickBCount
имеют разные значения! tickBCount
постоянно выше, чем tickACount
.
Хорошо, теперь я переключаю их на volatile. И согласно документации, volatile имеет смысл только для 8-битных значений. Счетчики в конечном итоге переполнятся, но это не имеет значения. Я просто хочу посмотреть, все еще ли эти две переменные отличаются.
volatile uint8 tickACount = 0;
volatile uint8 tickBCount = 0;
void myInterrupt(){
tickACount++;
tickBCount++;
}
В результате эти два значения все равно различаются.
Кроме того, их обёртывание в какой-нибудь макрос ATOMIC
не помогает.
Из того, что я прочитал, прерывания прерывать нельзя.
И, кроме того, если бы мое прерывание было прервано на полпути, то tickACount
был бы больше, чем tickBCount
, а не наоборот.
Таким образом, как можно объяснить разницу между этими двумя переменными?
Спасибо за любую идею!
@OoDeLally, 👍1
Обсуждение1 ответ
54000 прерываний в секунду [...] слишком быстро для моего Arduino уно
Возможно, ваш тест некорректен. Мой Uno справляется с такой частотой прерываний без проблем. Смотрите мой тестовый код ниже.
мой arduino способен обрабатывать только около 2050 прерываний в секунду
Вероятно, вы делаете что-то неправильно, например, блокируете прерывания слишком долго. long или с помощью библиотеки, которая это делает.
Что меня удивляет, так это то, что
tickACount
иtickBCount
имеют разные ценности!
Как уже было сказано в комментариях, это не так. Вместо этого Счетчики были увеличены во время их печати.
И согласно документации, volatile имеет смысл только для 8-битных ценности.
Или документация совершенно неверна, или (что наиболее вероятно) у вас есть неправильно понял.
Из того, что я прочитал, прерывания прерывать нельзя.
Это действительно поведение по умолчанию. Вы можете изменить его, выполнив
interrupts()
внутри обработчика прерываний или путем объявления ISR как
ISR(XXX_vect, ISR_NOBLOCK)
{
...
}
но это делается редко.
Ниже мой тестовый код. Я использовал Timer 2 для генерации прямоугольной волны на контакт 3, который одновременно является контактом ШИМ (OC2B) и контактом прерывания (INT1). Частота ШИМ составляет около 54,054 кГц.
const uint32_t PRINT_PERIOD = 1e6; // выводить количество раз в секунду
struct TickCounts {
uint32_t tickA;
uint32_t tickB;
};
volatile TickCounts rawCounts;
void myInterrupt() {
rawCounts.tickA++;
rawCounts.tickB++;
}
// Атомарно считываем оба счетчика.
static TickCounts getCounts() {
TickCounts counts;
noInterrupts();
counts.tickA = rawCounts.tickA;
counts.tickB = rawCounts.tickB;
interrupts();
return counts;
}
void setup() {
// ШИМ на частоте 54 кГц на OC2B = PD3 = INT1
DDRD |= _BV(PD3); // вывод PD3 как выход
OCR2A = 37 - 1; // период = 37 * 0,5 мкс = 18,5 мкс
OCR2B = 37/2 - 1; // рабочий цикл ~ 50%
TCCR2A = _BV(COM2B1) // неинвертирующий ШИМ на OC2B
| _BV(WGM20) // режим 7: быстрый ШИМ, TOP = OCR2A
| _BV(WGM21); // то же самое
TCCR2B = _BV(WGM22) // то же самое
| _BV(CS21); // тактовая частота F_CPU/8 = 2 МГц
Serial.begin(9600);
attachInterrupt(1, myInterrupt, RISING);
}
void loop() {
static uint32_t last_time_printed;
if (micros() - last_time_printed >= PRINT_PERIOD) {
last_time_printed += PRINT_PERIOD;
TickCounts counts = getCounts();
Serial.print(counts.tickA);
Serial.print(" ");
Serial.println(counts.tickB);
}
}
И вот что получилось:
54053 54053
108107 108107
162161 162161
216215 216215
270269 270269
324324 324324
378377 378377
432431 432431
486485 486485
540539 540539
...
- Использование millis() и micros() внутри процедуры прерывания
- Подсчет импульсов с прерыванием
- Устранение дребезга кнопки с помощью прерывания
- Программа arduino выдаёт ошибку expected //primary-expression before ')' token error: //expected ';' before '}' token E
- Почему необходимо использовать ключевое слово volatile для глобальных переменных при обработке прерываний в ардуино?
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Использование поворотных энкодеров с прерываниями смены контактов
- Arduino непрерывно считывает значение АЦП с помощью прерывания
Они одинаковы. Но когда вы печатаете один, а затем печатаете другой, между ними есть прерывания. Сделайте полный набросок, который показывает проблему. С большим количеством оптимизаций плата uno может делать больше прерываний, чем 54 кГц, возможно, 100 кГц. Учебник по прерываниям: https://gammon.com.au/interrupts, @Jot
Вы можете сэкономить довольно много циклов ЦП, создав свой собственный ISR вместо того, чтобы полагаться на тот, что есть в ядре Arduino, для вызова вашего обработчика прерываний. См. этот ответ, раздел _Напишите свой собственный ISR_., @Edgar Bonet
Если вам нужен простой счетчик, почему бы вам не использовать Timer1 или Timer2 с внешним источником синхронизации?, @KIIV
Рассматривали бы вы использование дополнительного оборудования для снижения нагрузки на микроконтроллер? Как насчет использования регистров сдвига для подсчета импульсов., @MichaelT
Я был убежден, что 54 кГц слишком быстро для Arduino, которая работает на частоте 16 МГц. Это всего ~300 циклов на прерывание + ему нужно обработать обычный цикл. Я заменил прерывания библиотекой Encoder https://github.com/PaulStoffregen/Encoder, и теперь она, похоже, работает нормально. Я немного покопался в этой библиотеке, похоже, она делает примерно то же самое, что и я, но на ассемблере, возможно, с какой-то большой оптимизацией. Молодец, насчет
tickCountB
>tickCountA
. Возможность увеличенияtickCountB
во время двух отпечатков мне в голову не приходила. Думаю, это опыт!, @OoDeLally