Очень странное поведение DUE на прерываниях
- Войдите на сайт для отправки комментариев
Вообщем код запускает ШИМ на двух каналах:
#define BTN1 21 #define BTN2 20 #define BTN3 19 uint16_t duty = 21; uint16_t freq = 42; uint16_t n_impulse = 4; void setup() { Serial.begin(9600); pinMode(BTN1, INPUT); pinMode(BTN2, INPUT); pinMode(BTN3, INPUT); REG_PMC_PCER1 |= PMC_PCER1_PID33; REG_PIOC_ABSR |= PIO_ABSR_P26 | PIO_ABSR_P25; REG_PIOC_PDR |= PIO_PDR_P26 | PIO_PDR_P25; REG_TC2_CMR0 = TC_CMR_BCPC_CLEAR | TC_CMR_ACPC_CLEAR | TC_CMR_BCPB_SET | TC_CMR_ACPA_SET | TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_EEVT_XC0 | TC_CMR_TCCLKS_TIMER_CLOCK1; REG_TC2_RC0 = freq; REG_TC2_IER0 = 0b00010000; REG_TC2_IDR0 = 0b11101111; NVIC_EnableIRQ(TC6_IRQn); REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN; } void loop() { if (digitalRead(BTN1) == 0) { Serial.println("BTN1"); } if (digitalRead(BTN2) == 0) { Serial.println("BTN2"); } if (digitalRead(BTN3) == 0) { Serial.println("BTN3"); } } void TC6_Handler(){ static int i = 0; i++; uint16_t dummy = REG_TC2_SR0; REG_TC2_RB0 = freq; REG_TC2_RA0 = duty; if (i >= n_impulse) { REG_TC2_RA0 = freq; REG_TC2_RB0 = duty; i = 0; } }
В обработчике события TC6_Handler() я формирую нужные мне формы сигнала на двух выводах. Частота freq и скважность импульса duty задаются отдельной функцие. В данном случае код максимально упрощен, но баг отлавливается. Значит первая страннойсть. Если в обработчике TC6_Handler() параметр REG_TC2_RA0 выглядит так как в коде выше, то есть:
REG_TC2_RA0 = duty;
То функция loop не работает от слова совсем. Если я параметр укажу прямо:
REG_TC2_RA0 = 21;
То функция loop работает условно замечательно. Почему условно. А здесь второй баг. Если я оставлю один обработчик кнопок, скажем так:
void loop() { if (digitalRead(BTN1) == 0) { Serial.println("BTN1"); } }
То функция не работает ни в случае прямой передачи переменной duty в REG_TC2_RA0=21, ни в случае REG_TC2_RA0 = 21.
Кто знает в чем дело? Хелп плиз
Глобальные переменные которые используются в прерывании должны быть объявлены как volatile иначе оптимизатор может напортачить.
Ах вот оно что! Спасибо огромное за помощь. Два дня потратил отлавливая баг)
Есть ещё небольшая вероятность, что компилятор может строчку uint16_t dummy = REG_TC2_SR0 выкинуть (т.к. её результат нигде не используется) , тогда прерывание будет вечным, и до лупа очередь не дойдёт... :)
Вы тут один из самых гуристых гуру, может сможете подсказать. У меня в данном коде формируется сигнал на двух выходах с одной частотой, но с определенными пропусками. Примерно так:
Выход 1: _П_П_П___П_П_П___П_П_П_
Выход 2: _______П________П_______
То есть пропущенный импульс на выходе 1, формируется на выходе 2 с той же фазой, скважностью и частотой. Частоту, скважность и количество импульсов для пропуска я задаю сам. Но если я в функции loop занимаюсь опросом кнопок, у меня постоянно скачет количество пропущенных импульсов. Примерно так:
Выход 1: _П_П____П_П_П_П___П_П___П_П____П_П_П_П___П_П___
Выход 2: ______П__________П_____П______П__________П_____П_
Если я ставлю прерывания, то ситуация чуть лучше. Вот пробую разные варианты типов встроенной переменной-счетчика отвечающей за количество импульсов:
static uint16_t i = 0;
volatile uint16_t i = 0;
volatile static uint16_t i = 0;
Так же ее выношу и вношу за пределы Слушателя TC6_Handler()
Во многом тыкаю пальцем в небо без особого понимания того, что делаю. Спасибо
Есть ещё небольшая вероятность, что компилятор может строчку uint16_t dummy = REG_TC2_SR0 выкинуть (т.к. её результат нигде не используется) , тогда прерывание будет вечным, и до лупа очередь не дойдёт... :)
Volatile же исправляет ситуацию?
Есть ли в DUE понятие приоритет прерываний? Дело в том, что такой стиль програмировария очень плох - смешивание ардуино и CMSYSа. Ардуина вешает свои прерывания, которые Вы не контролируете, и они могут вредить Вашим. И можно долго искать глюки. Поробуйте не использовать loop setup a main while. При этом не будет инициализироваться другая переферия и прерывания, кроме Ваших.
А как это должно выглядеть? можно простенький пример? Спасибо!
Первая ссылка из гугля https://ph0en1x.net/80-simple-program-for-avr-microcontroller-c-language...
И как только нажимается кнопка, начинается вывод в сериал. Если приоритет прерываний сериал выше чем таймера, будет та фигня, что Вы описали. Измените скорость вывода на 115200 и если дергаться будет меньше, значит так и есть. Надо приоритет своего прерывания ставить выше.
Mulin.by, для переменной i внутри прерывания ничего менять не нужно. Нужно только глобальные freq duty сделать volatile. Ещё мне не нравится содержание прерывания. Получается регистры RB0/RA0 вы сначала программируете одним, но если это был четвёртый вход -то тут же переписывате другим -так не делается.
Если всё равно проблема не ушла, то нужно посмотреть что во флаге dummy, сделайте его тоже глобальным, и в loop прочитайте. Ещё не помещает в прерывании дёргать какую-нибудь ногу, и осциллографом/частометром посмотреть с какой частотой происходит вход в прерывание, соответствует ли это тому, что задумано.
Получается регистры RB0/RA0 вы сначала программируете одним, но если это был четвёртый вход -то тут же переписываете другим -так не делается.
Такой код вроде правильнее, насколько я понял
Вы используете NVIC для поднятия прерывания. У NVIC есть другие функции, позволяющие установить приоритет прерывания до его поднятия. Посмотрите в описании и примените.
Mulin.by, вы проверили работу после изменения переменных на volatile , какие флаги в dummy ?
Mulin.by, вы проверили работу после изменения переменных на volatile , какие флаги в dummy ?
Dummy я вывел в Serial. Плавает в трех значениях 6556 65556 65560. Как это интерпретировать не совсем понимаю. Вообщем по осциллографу пропуски импульсов происходят на определенной частоте +/-. Причем вот этот код работает условно нормально (пропуски на определенной частоте)
А вот этот уже не отрабатывает второй if
Но если инкремент воткнуть во второй if как здесь:
То опять начинаются пропуски, но уже во втором канале
Serial вообще отключал, все равно пропуски бывают. Такое ощущение, что не всегда при условии происходит присвоение значения REG_TC2_RA0 и REG_TC2_RB0. Еще замечено, что влияет на пропуски последовательность записи REG_TC2_RB0 и REG_TC2_RA0 то есть что стоит первое.
Mulin.by, выложите полный скетч.
Убрал все лишнее оставил суть. Когда я докручиваю энкодером до частоты 525000 и до примерно 444000 начинается нестабильная работа например так:
Вот фото с осциллографа

Приоритет лучше задавать до запуска, а так все в порядке. Нужно посчитать сколько тактов живёт прерывание, со всеми входами и выходами. Укладывается в 2 микросекунды?
Такты прописаны в регистре REG_TC2_SR0 ? Если так, то 6556 65556 65560. Значение плавает
Заметил, что проблемная частота лежит в пределах freq = 84/85 duty = 42
Mulin.by, вам стоило сразу написать про частоту. МК не может выполнять прерывания с такой высокой частотой, как я понял по умолчанию прерывания у вас в программе строчат с частотой 1млн раз в секунду. Вернее выполнить прерывание именно Due сможет, но ни на что другое времени просто не остаётся. Думаю не стоит настраивать прерывания чаще, чем 100 000 раз в секунду.
Mulin.by, вам стоило сразу написать про частоту. МК не может выполнять прерывания с такой высокой частотой, как я понял по умолчанию прерывания у вас в программе строчат с частотой 1млн раз в секунду. Вернее выполнить прерывание именно Due сможет, но ни на что другое времени просто не остаётся. Думаю не стоит настраивать прерывания чаще, чем 100 000 раз в секунду.
Mulin.by, если использовать такие высокие частоты нужно принципиально, то придётся отказаться от прерываний совсем. Есть другой способ управлять сигналами - модуляция/стробирование. В общем связка двух и более таймеров, когда один управляет другим. Не уверен, что это возможно на Due, может есть смысл сразу перейти на stm32, там такие вещи возможны.
Mulin.by, если использовать такие высокие частоты нужно принципиально, то придётся отказаться от прерываний совсем. Есть другой способ управлять сигналами - модуляция/стробирование. В общем связка двух и более таймеров, когда один управляет другим. Не уверен, что это возможно на Due, может есть смысл сразу перейти на stm32, там такие вещи возможны.
Но и там есть некоторые проблемы
У меня есть еще один вариант. В котором я запускаю два таймера. Первый дает постоянный меандр, второй с пропуском:
Выход 1: _П_П_П_П_П_П_П_П_П_П
Выход 2: _____П______П_____П_
Фронты на самом деле совпадают идеально. Может есть какой способ принудительно, не останавливая первый таймер, устанавливать его выход на 0 при единице на выходе 2? Здесь у меня совсем все просто и топорно. Зато работает стабильно, при чем верчу кручу скважностью импульсами частотами в реалайме
А далее какой-нибудь обработчик вроде:
Вообщем кому интересно поставил на выходе логический исключающее-или SN74HC86 с фронтами 17нс, что является эквивалентом ~5мГц. На выходе небольшие пульсации сгладил пикофарадным конденсатором.