Прерывания по таймеру 1
- Войдите на сайт для отправки комментариев
Вс, 08/03/2020 - 14:07
Добрый день, корифеи
Есть задача по каналу А таймера 1 менять переменную timerAс частотой 1 Гц, по каналу B менять переменную timerB с частотой ~500 Гц. Несмотря на значения OCR1A = 15624 и OCR1B = 30, прерывания по обоим каналам идут с частотой 1 Гц. Прошу подсказать в какую сторону копать:
volatile unsigned int timerA; volatile unsigned int timerB; ISR (TIMER1_COMPA_vect) { timerA++; } ISR (TIMER1_COMPB_vect) { timerB++; } void setup() { cli(); TCCR1B = 0; TCCR1A = 0; TCCR1B |= (1 << WGM12); // Режим CTC (сброс по совпадению) TCCR1B |= (1 << CS10) | (1 << CS12); // CLK/1024 OCR1A = 15624; // Частота прерываний A будет = 16.000.000/(1024*(1+15624)) = 1 гЦ OCR1B = 30; // Частота прерываний B будет = 16.000.000/(1024*(1+30)) = 504 гЦ TIMSK1 |= (1 << OCIE1A); // Разрешить прерывание по совпадению A TIMSK1 |= (1 << OCIE1B); // Разрешить прерывание по совпадению B sei(); Serial.begin(9600); Serial.println("Starting..."); }
По одному таймеру прерывания ВСЕГДА будут идти с одинаковой частотой по всем каналам.
С этим необходимо смириться и проектировать алгоритмы, исходя из этого очень неприятного обстоятельства.
Внезапно :( а в чем тогда практический смысл двух каналов (в раздельной настройке OCR1A и OCR1B)?
У таймера на оба канала один счётчик, который по CTC сбрасывается в 0 при достижении заданного TOP. Он физически не может досчитать до двух лимитов.
То есть можно использовать один канал по совпадению, а второй по переполнению?
Можно и так, но сложно рассчитывать параметры под частоты. В вашем случае я бы просто генерировал прерывание по наивысшей, а вторую получал через счётчик заходов в прерывание.
Как вариант, либо использовать под 500 гЦ таймер 2 (tone и pwm в проекте не задействованы). Насколько оправдано (правильно) использование двух таймеров? В предложенном Вами варианте не хочется unsigned int ворочать...
Зачем его ворочать? byte тоже умеет считать до двух. И boolean имеет два значения. Перекидывайте его логической инверсией и прибавляйте к основному счётчику.
Сорри, что-то я подумал, что там килогерц и 500 герц.
На 500гц можно и ансигнед инт ворочать, МК не вспотеет.
Можно, конечно, и два таймера использовать для инкремента переменных. Прикуривают же люди от долларовых банкнот.
Сделал на двух таймерах, выложу, может пригодится кому.
Вот и интересуюсь как более кошерно и почему? Мне казалось что ансигнед инт тяжелее ворочать чем bool перевернуть...
Тяжелее, но не настолько, чтобы 16мгц процессор загнулся на двубайтных операциях вместо однобайтных. Зато сыканомите на входе и выходе из второго обработчика. Может даже то на то и выйдет.
Ок, принял к сведению. Спасибо за консультацию. Но все равно хотелось бы понять логику - зачем два канала у таймера?
Почему два - надо спрашивать у разработчиков чипа. А используются они для PWM, к примеру.
Мммм, логично. про PWM вообще не подумал. Теперь все встало на свои места!
Внезапно :( а в чем тогда практический смысл двух каналов (в раздельной настройке OCR1A и OCR1B)?
Например, нужно сформировать полный телевизионный сигнал, тогда, если время начал строчного синхроимпульса принять на 0, то нужно обеспечить время окончания синхроимпульса, время окончания импульса гашения и время начала импульса гашения. И все это на одной и той же кадровой частоте. Вполне естественно для этой задачи использовать 4 канала одного таймера.
Мне вот интересно, если бы каналы можно было программировать независимо на разные частоты, какой бы смысл было делать несколько таймеров вместо одного со множеством каналов?
Другой пример, на том же таймере 1 делаю управление шаговиком на А4988. Один канал, понятно сам степ. А второй канал - енейбл, чтоб в паузах между шагами драйвер и движок меньше грелись. А период таймера (а это собственно скорость вращения) через ICR1 задаю.
А на каком микрошаге степпер ходит? Дрожание/биение не заметно? А то ведь вал при снятии тока может и немного назад откинуть.
без микрошага, на 1.откинет - не беда, енейбл подается раньше шага, сразу вернет на позицию, затем шаг на следующую и после паузы снимается енейбл. Вроде нормально, микрошаги заради интереса могу позже попробовать.
Без микрошага всё должно быть ОК.
При микрошаге ротор висит где-то между двумя зубцами статора (точная позиция зависит от соотношения тока на обмотках и ещё разных факторов). И как только удержание будет снято, ротор притянет к ближайшему зубцу - а он может быть "впереди", так и "позади". Подача энейбла не может гарантировать возврат статора в ту же позицию. Точно так же ротор скакнёт непредсказуемо (для человека) или вперёд или назад. Хотя я и интуитивно чувствую, что должен взад вернуть, если соотношение токов в обмотках восстановится. Однако это всё же вызовет микробиения.
Во всяком случае мои эксперименты с 7,5-градусным движком на A4988 с микрошагом 1/16 порождали разные эффекты... Вплоть до неравного микрошага - три мелких, один крупный, например. Хочу вот на TMC-шных ещё потестить...
На 1,8-градусном, наверное, это не так заметно будет. Повторять весь экспериментальный цикл что-то лень, поэтому и интересуюсь - как оно, чувствуется или нет?
Да. Юзаю только без микрошага и ОК. С изложенным согласен, на микрошаге снимать енейбл - проблемный вариант. Разве что на высокой скорости и инерционной нагрузке, может и не успеет отскочить. Тут пробовать и/или рассчитывать нужно.
Хмм... Можно в принципе обойтись одним таймером используя
То есть
Написанный код выше актуален не только для 16-битного таймера, но и 8-битного. Сам недавно начал изучать работу с регистрами AVR... Если есть ошибка в коде или недочет - пишите, буду исправляться))
Недочёты есть.
i я бы сделал static внутри обработчика, u вообще бы выкинул, а биты для OCR1A задавал через мнемонические макросы.
До static ещё не дошел в изучении. А биты это уже личное))) Мне в двоичной или в шестнадцатеричной системе использовать удобнее. Ну ещё юзаю в виде (1<<5), если не очень хочется затрагивать другие биты в порте.
Beijo2908,
А что сразу не написать 250, зачем этот макрос? И кстати использование "|=" без острой надобности в "OR" когда-нибудь до беды вас доведёт. Однажды в регистре будет что-то лежать, и вы к нему не глядя добавите новое значение.
То не макрос а двоичная константа. И действительно, просто присвоить OCR1A =250 выглядит разумней. Прерывания получаются 1КГц. Если уж так хочется иметь 500Гц и 1Гц, то зачем нам 1КГц. Сразу просто настраиваем таймер на 500Гц, это просто OCR1A =500, а 1Гц получаем пересчетом.
Okay, буду писать числа в десятичной системе. Про |= и например &= в курсе, но тоже возьму на заметку.
А если таймер 8-bit? 500 не влезет же.
Далее, к примеру, если МК 8-bit, таймер 16-bit. Получается 16-bit таймер состоит из двух 8-bit регистров OCR1AH и OCR1AL - старшая и младшая часть. Это лишние ограничения + "пара" строчек, имхо.
Если у восьмибитного таймера есть прескаллер - меняем его и пересчитываем значение. Например прескаллер выбрать 256, а значение выйдет 125. Про 16-битность регистров таймера - не переживайте излишне, там двойная буферизация. Простое присвоение отрабатывает корректно. А вот арифметические и логические действия напрямую на регистрах писать не стоит.