Добрый день, необходимо сформировать сигнал (как на приложенном рисунке) на пине мк от прерывания. Т.е. по событию EXTI каждый раз на пине формируется сигнал постоянной формы (50мксек высокий уровень, 50 - низкий, 500 - высокий и потом низкий до следующего EXTI). Программно это сделать не проблема, завести таймер и в его прерываниях переключать пин. Но хотелось бы реализовать это аппаратно. Так и временные промежутки будут поточнее и время срабатывания.
Как это реализовать я пока не представляю. В самом сигнале самым важным является первый промежуток 50 мк сек и начало его срабатывания. Можно было бы включить один из таймеров в режиме One Pulse Mode, он бы отработал первые 50 мксек, а дальше вторым таймером в прерывании переключать оставшиеся промежутки, но при настройке пина в режим одиночного срабатывания PWM в дальнейшем у меня не получается менять его состояние, т.е. отработал он импульс, перешел в LOW и перевести его в HIGH вручную уже не получается.
Есть ли тут какие-либо варианты или же только переключать в прерываниях от таймера?
OPM режим с прерыванием по UEV: в прерывании EXTI запускаешь таймер, он дает тебе два первых импульса. Затем перенастраиваешь в прерывании UEV на 500мкс, опять запускаешь, уже отключив прерывание UEV. Тольког 50мкс - маловато, из-за накладных расходов второй импульс будет больше (но можно экспериментально определить нужные настройки, а прерыванию UEV поставить наивысший приоритет). Еще более разумный вариант — запусить передачу при помощи DMA, пусть он в CC1/ARR и пишет нужные значения. Тогда проблемы с длительностью будут минимальными. Вот так, например. В конкретном случае нет нужды ни CC1, ни ARR дергать: можно прескалером поиграться.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Интересная задачка, а что если такой вариант прикинуть (конечно, расход таймеров большой, но их вроде как много): объединить выход двух таймеров, первый настроить на двойной OPM (продвинутые так умеют) и период в 50мкс, второй сделать слейвом с делением на 2 и тоже OPM, тогда на второй переход первого таймера запустится второй, то есть вообще ничего изменять не надо?
OPM режим с прерыванием по UEV: в прерывании EXTI запускаешь таймер, он дает тебе два первых импульса. Затем перенастраиваешь в прерывании UEV на 500мкс, опять запускаешь, уже отключив прерывание UEV.
Идея понятна, но смущает ручной запуск таймера в EXTI ..., ну ладно, как вариант.
Но вопрос, если пин настроен как OPM таймера, то менять состояние этого пина по ходу программы я не могу?
Цитата:
Еще более разумный вариант — запусить передачу при помощи DMA, пусть он в CC1/ARR и пишет нужные значения. Тогда проблемы с длительностью будут минимальными.
Мне кажется вызов DMA или в прерывании перезаписать CC1/ARR примерно то на то и выйдет?
Тут еще минус в том, что сейчас таймер срабатывает от EXTI автоматически, а если делать на перезапуске в прерываниях, то придется перенастраивать на программный запуск таймера, что скорее всего скажется на времени старта первого импульса
В чём проблема? Берём XMC4500 или XMC4700 или XMC4800. Внешний сигнал заводим на вход любого таймера (CCU). На этот вход программируем функцию запуска таймера (по нужному событию: фронт/спад/фронт_или_спад). Пассивный уровень выхода таймера устанавливаем = LOW. Таймер настраиваем в режим PWM: период = 100мкс, длина импульса =50мкс. Также внутри этого периода настраиваем генерацию прерывания, в ISR которого будем производить запись новых периода/длины_импульса в теневые регистры. Во 2-м входе в ISR настроим новое прерывание на точку 500мкс от начала 2-го периода. В 3-м входе в ISR - выключим таймер. Всё! Все интервалы будут выдержаны с точностью до такта CPU, так как ни один не зависит от задержек выполнения CPU/DMA.
PS: Если сильно нужно, то операции производимые в ISR, можно заменить на DMA (направив сигналы прерываний на линии DMA-запросов). PPS: А если ещё немного подумать и немного доработать алгоритм, то даже целый CCU не нужен, можно обойтись его 1/4 частью.
jcxz, Ваша идея мне нравится, я даже не знал, что в режиме PWM можно генерировать прерывания. Только пока с реализацией не получилось. Использую stm32f7 и из-за некоторых соображений настройку делаю на HAL
В main запускаю:
Код:
HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_4); State = 1;
HAL_TIM_PWM_Stop_IT(&htim3, TIM_CHANNEL_4); State = 1;
break;
default: __NOP(); } } }
Если я не ошибаюсь, то прерывание генерируется по достижению счета ARR, поэтому в case 1 меняю значения ARR и CCR на более длительные, т.е. на этот момент у меня уже 2 тайминга по 50 мксек должно отработать. Дальше отрабатывают новые ARR и CCR, генерируется прерывание и в case 2 я возвращаю прошлые значения и останавливаю PWM.
Вот собака, он без HRTIM! Тогда как вариант использовать
Eddy_Em писал(а):
запусить передачу при помощи DMA, пусть он в CC1/ARR и пишет нужные значения.
Включить буферизацию регистров и сразу после запуска от внешнего события запихивать модулем ПДП в регистры настройки для второго импульса. Как толькотаймер отработает первый импульс, то значения обновятся и полетит второй импульс. Вероятно (не читал так подробно) можно устроить всё так, чтобы тот же ПДП во время второго импульса возвращал всё на круги своя. В этом случае ядро не будет отвлекаться вообще. Как всё это настраивать написано в документе RM0385 на странице 786.
jcxz, Ваша идея мне нравится, я даже не знал, что в режиме PWM можно генерировать прерывания.
У таймеров XMC регистр сравнения (который определяет точку переключения выхода PWM) точно также может на этом же значении вызывать генерацию прерывания (если соответствующий бит разрешения установлен), либо можно отдельно разрешить прерывания в конце и/или в середине периода PWM (для этого регистр сравнения не используется). А если использовать таймер с двумя CR (регистрами сравнения), то можно одним CR задать точку переключения выхода PWM, а вторым CR - точку генерации прерывания в произвольном месте периода (никак не привязанном к моментам переключения выхода PWM).
Итого: один CCU8 (таймер с двумя регистрами сравнения) позволяет генерировать до 6-ти прерываний на период в разных точках периода: начале/конце, в середине, по 1-му CR при счёте вверх и при счёте вниз, и то же самое - по 2-му CR. Для этих 6 источников прерываний можно назначить до двух разных сигналов прерывания к NVIC. Это не считая того, что для многих событий таймера (старт или стоп, захват, сигнал разрешения тактирования, изменения направления счёта, перезагрузка и др.) если они вызываются внешними сигналами, то по ним тоже таймер может дополнительно генерить прерывания по назначенным линиям NVIC.
>TEHb<, jcxz, Ваши два совета в целом похожи и в целом они работают. Настроить PWM на запуск от EXTI и в прерывании от таймера менять ARR и CCR, дальше останавливать таймер, возвращать ARR и CCR на место и разрешать повторное срабатывание по следующему EXTI.
Одна только проблема с этим: как только отрабатывают все 3 тайминга (50, 50 и 500 мксек), выставляем низкий уровень и при следующем EXTI у нас первые 50 мк сек опять должны быть HIGH, но они будут LOW, и из-за этого все уровни поменяются местами и так будет происходить циклично
Значит у вас неверно настроен таймер. Настраивайте однозначно в какой точке устанавливать высокий уровень, а в какой низкий. Никаких переключений, только жёстко вверх или вниз. Ну и я всё-таки предлагал с помощью DMA рассовывать данные в регистры, а не в прерывании.
DmitryR, предлагаю для начала от кала отказаться. Все равно ведь часть вещей делается напрямую регистрами! Ну и еще раз напомню о DMA. А также, видимо, мимо пролетело: было же предложение SPI или I2C использовать, чтобы данную последовательность импульсов воспроизвести. P.S. А код реально жуткий. Дрыгать ногодрыгом в прерывании от таймера, когда можно напрямую в ШИМ-режиме… Да еще и ты учти, что есть приличные накладные расходы на вход в прерывание и исполнение кода в нем. Таким образом честных 50мкс ты никак не получишь. Кстати, каловская HAL_GPIO_TogglePin - это тебе не макрос и даже не true_inline, а самая настоящая функция со всеми вытекающими!\ Если тебе так хочется кала, то напиши на С++ с шаблонами свою обертку, и будет тебе щассье. Так как минимум пара человек с форума делала.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Настраивайте однозначно в какой точке устанавливать высокий уровень, а в какой низкий
Дело в том, что там нет таких настроек, могу только выбрать с какого уровня будет это все стартовать CH Polarity. Хотя я тогда не понимаю, почему при повторном запуске он стартует не с указанного HIGH, а с предыдущего уровня
Eddy_Em, Там стоит FreeRTOS, но вроде как не планируется ее взаимодействие с этим таймером, поэтому можно и без HAL...
Цитата:
Ну и еще раз напомню о DMA
Попробую на DMA, но мне кажется тут дело не в том, как менять ARR и CRR
Цитата:
А также, видимо, мимо пролетело: было же предложение SPI или I2C использовать
Как-то диковато звучит реализовать функции таймера посредством SPI, но можно попробовать)
Добавлено after 3 minutes 44 seconds: Eddy_Em,
Цитата:
P.S. А код реально жуткий. Дрыгать ногодрыгом в прерывании от таймера, когда можно напрямую в ШИМ-режиме…
То не, это не тот пин, который должен сигнал формировать, это я просто поставил, чтобы отследить на осциллографе когда срабатывает прерывание и не убрал. А так в прерывании только ARR и CRR меняется, больше там ничего не делается, ну кроме State++
Ну вот, чтобы не было проблем, нужно делать все более-менее аппаратно. И, кстати, предложение Andrey_B использовать SPI - очень даже дельная идея (если есть в системе 1 ненужный SPI и его нога MOSI свободная). И таймер на это тратить не придется, и канал DMA. А ртось - это фу. Если приложение для МК невозможно без ртоси сделать, то дешевле купить одноплатник за две тысячи рублей и реализовать всю многозадачность на нормальном линуксе, а для RT выделить какой-нибудь недорогой камушек (зачастую даже F030 сгодится).
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
РТОС фу, HAL фу, LWIP фу, если так на все фукать на одних регистрах придется сидеть писать кучу времени))) Да там FreeRTOS поставлена чисто из-за LWIP (уж что, а ethernet я точно на регистрах писать не готов). На ОС я скидываю всякие медленные задачи не первой необходимости, а все RT делаю на приоритете выше чем у ОС и не взаимодействующими с ОС. Ну и стараюсь делать их аппаратно. Камень 746 - таймеров, SPI, DMA, всего полно.
Тут знаете, дело-то даже и не в регистрах, это уже довольно заезженная тема, отношение к HAL давно понятно... Тут скорее вопрос в принципе в функциональной возможности реализации тем или иным способом. Вот, например, сейчас задача практически решена, за исключением подмеса ARR и CRR переключения пина происходит аппаратно, но он начинает свой новый цикл со значения предыдущего. Предполагаю, что так построена архитектура, что что он при первом запуске выставляет настроенный уровень, а дальше делает toggle и когда вмешиваешься в этот процесс, система себя так ведет. Может тогда помимо старт/стоп надо еще делать какую-нибудь переинициализацию состояния пина... Добавить DMA можно, но на данном этапе это изменит лишь способ перезадания ARR и CRR
VladislavS, Видимо мне надо поглубже покапатьcя в Reference manual. Опять же, как Вы и написали, это настройки на событие сравнения, а по событию сравнения надо менять ARR и ССR. Не знаю даже... Надо мне почитать сначала
PS: Для себя я сделал вывод, что все, что не критично на HAL или LL, критичные функции на CMSIS или, в некоторых случаях, LL. Типа золотая середина по-моему мнению
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 15
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения