Добрый день. Какое-то время назад я прочёл, что какому-то человеку захотелось изучить поглубже вопрос про ePWM модуль на мк tms320f28xxx, поэтому я решил, почему бы и мне не написать статью на эту тему, в которой я постараюсь подробно разжевать этот модуль на примере tms320f28335.
Давайте приблизимся к основным блокам: из чего состоит ePWM модуль и к чему он подключен.
Как видно из рисунка, не так много и блоков находится в модуле ePWM. Поэтому, имеет смысл рассмотреть за что отвечает каждый модуль и начнём с сигналов.
Теперь перейдём к модулям
Time base (TB) модуль — отвечает за время события каждого модуля ePWM. Не будем сильно вдаваться во все настройки этого модуля, думаю достаточно обратить внимание на то, что есть 3 режима работы счётчика:
А так же есть настройка синхронности таймера через выставление бита TBCLKSYNC
Counter compare (CC) модуль — через него мы как-раз таки и задаём нашу скважность.
Action-Qualifier (AQ) модуль — через него можно настроить состояние на событие. А для выходов можно настроить следующие действия:
Dead-Band Submodule (DB) модуль — с помощью этого модуля можно настроить зоны нечувствительности для каналов ШИМа. Ни для кого не будет секретом то, что ключи транзисторов переключаются не мгновенно и для того, чтобы не было ситуации когда верхний ключ полумоста не успел закрыться, а нижний уже открыт, выставляют задержку на переключение в состояние HI и более раннее включение в состояние LO.
Trip-Zone Submodule (TZ) модуль — как говорилось выше, этот модуль связан с отработкой аварийных состояний. Тут мы можем выбрать 1 из 4 действий.
Событие, вызывающее действие TZ модуля может быть вызвано как программно, так и аппаратно. Кроме этого, предусмотрен вызов прерывания.
Для начала необходимо настроить GPIO на альтернативную функцию epwm
Далее, когда мы уже настроили GPIO, мы можем приступать к разным настройкам. Для настройки работы ШИМа надо определиться с тем, что мы хотим получить. Начнём с частоты TBCLK. Она определяется по формуле:
Тут необходимо обратить внимание на то, что CLKDIV по умолчанию равен 1, с HSPCLKDIV всё иначе, по умолчанию он равен 2. Это надо держать в уме, поскольку бывают случаи, когда люди об этом забывают. При загрузке программы в RAM часто HSPCLKDIV = 1, соответственно, эту проблему замечают не сразу.
С частотой тактирования TBCLK мы определились. Но надо бы выбрать, то, как у нас будет работать счётчик. По спаду, по нарастающей, а может и так и эдак, для этого надо настроить соответствующий регистр, например:
В дальнейшем, чтобы никто не пугался макросам, определимся откуда они вообще появляются. Эти дефайны определены в файле с названием DSP2833x_EPwm_defines.h.
После чего необходимо определиться с тем, как будут реагировать наши GPIO на достижение определённых значений TBCTR. Вариантов действий более чем достаточно. Они приведены в таблице ниже:
Затем необходимо определиться, какого поведения мы хотим от портов А и В, а именно, мы хотим, чтобы они были связаны друг с другом или же могли работать независимо. Если мы хотим, чтобы порт А был ведущим, то мы просто записываем для него действия, например (случай для отсчёта по нарастающей):
если же хотим независимости и для второго порта, то добавляем:
По подробнее о настройках необходимо обратиться к изображению выше за одним лишь дополнением, регистров AQCTLA чуть больше, чем изображено на таблице, сильно картину это не меняет, а лишь вносится конкретика касательно того, в каком случае достиг нужного значения счётчик, например, при отсчете в верх или отсчёте в низ. Более кратко с битами можно ознакомиться в вырезке из системного .h файла
Если у нас 2 порта ePWM работают зависимо и мы хотим выставить мёртвое время, то необходимо выставить регистр в нужное состояние, например:
Теперь, когда с описанием периферии определились, можно перейти к конкретным примерам.
Тут пример без мёртвого времени и порт A, и порт В работают зависимо. Когда A активен, B не активен.
На осцилограмме можно увидеть полученный результат:
Теперь можно попробовать добавить мёртвое время, для этого добавляем:
Отсчёт мёртвого времени производится аналогично частоте, по формуле:
А теперь мы получили мёртвое время таким, каким мы и хотели
А что делать, если нам надо развязать порт А и порт В? Такое тоже имеет место быть. Тут всё просто. Возвращаемся к первому примеру и удаляем последние 4 строчки, и записываем каждому скважность в следующие регистры.
Теперь мы имеем вот такую вот картину. Можно задавать скважность каждому каналу по отдельности.
Для режима по спаду всё примерно аналогично. Есть отличие с отсчётом в режиме up-down. Тут частота шима рассчитывается уже по формуле:
Примерно так же и для dead Time.
Наверное, единственно важное, что не было рассмотрено, так это настройка TZ, ну что ж, теперь остановимся на этом модуле чуть чуть по подробнее.
Для программного вызова события аварии достаточно настроить следующие регистры:
Вызов и обнуление аварии ШИМа можно осуществить с помощью следующих команд:
Если же мы хотим аппаратно вызвать сигнал TZ, то тут всё ещё проще, через регистр TZSEL выставляем нужный нам TZ, но вдобавок к этому необходимо настроить GPIO на TZ.
Если кому-то покажется данная статья интересной, то могу в более менее ускоренном порядке написать ещё пару статей. В планах имеется рассмотреть can модуль, хотелось бы dma, а еще может быть напишу небольшую статью по IQMath от ti с их библиотеками.
Возможности модуля ePWM
- Выходы epwmA epwmB могут работать как:
- single-edge operation
- dual-edge symmetric operation
- dual edge asymmetric opperation
- Может быть сконфигурировано мертвое время
- Может быть сконфигурирован TZ эвент и настроено логическое состояние выхода как HI так и LO.
- Может быть настроенно событие прерывания или событие SOC для ADC.
Давайте приблизимся к основным блокам: из чего состоит ePWM модуль и к чему он подключен.
Как видно из рисунка, не так много и блоков находится в модуле ePWM. Поэтому, имеет смысл рассмотреть за что отвечает каждый модуль и начнём с сигналов.
- EPWMxA и EPWMxB сигналы — наверное, самый очевидный выходной сигнал. Обычное логическое состояние либо HI, либо LO, в зависимости от того, как сконфигурировано выходное воздействие
- TZ1 — TZ6 сигналы — тут уже не совсем очевидные входные сигналы. Для тех, кто обладает около нулевыми знаниями в этой теме, как и я в своё время, подробно опишу это. Эти сигналы обычно используются для генерирования аварийных реакций, а именно вывода EPWMxA и EPWMxB выходов в определённое состояние. Зачастую, у драйверов силовых ключей есть возможность генерировать аварию по ключам, иногда схемотехники делают аппаратную защиту по уровню какого-то значения некой величины, например тока, при достижении которого генерируется некий логический уровень. Это мы сейчас говорили о физической реализации. Кроме физического получения этого сигнала, его можно генерировать и программно.
- EPWMxSYNCI и EPWMxSYNCO сигналы — тут мало что можно рассказать, это сигналы сихронизации, которые могут быть входом или выходом.
- EPWMxSOCA и EPWMxSOCB сигналы — тут всё более чем понятно из названия. Эти события могут задать события SOC для АЦП.
- EPWMxTZINT и EPWMxINT сигналы — тут событие прерываний на TZ и на события связанные с самим ШИМ, например генерирование прерывания по периоду ШИМ.
Теперь перейдём к модулям
Time base (TB) модуль — отвечает за время события каждого модуля ePWM. Не будем сильно вдаваться во все настройки этого модуля, думаю достаточно обратить внимание на то, что есть 3 режима работы счётчика:
- Up-Down-Count Mode
- Up-Count Mode
- Down-Count Mode
А так же есть настройка синхронности таймера через выставление бита TBCLKSYNC
Counter compare (CC) модуль — через него мы как-раз таки и задаём нашу скважность.
Action-Qualifier (AQ) модуль — через него можно настроить состояние на событие. А для выходов можно настроить следующие действия:
- Выставить в состояние HI
- Выставить в состояние LO
- Произвести инверсию состояния
- Ничего не делать
Dead-Band Submodule (DB) модуль — с помощью этого модуля можно настроить зоны нечувствительности для каналов ШИМа. Ни для кого не будет секретом то, что ключи транзисторов переключаются не мгновенно и для того, чтобы не было ситуации когда верхний ключ полумоста не успел закрыться, а нижний уже открыт, выставляют задержку на переключение в состояние HI и более раннее включение в состояние LO.
Trip-Zone Submodule (TZ) модуль — как говорилось выше, этот модуль связан с отработкой аварийных состояний. Тут мы можем выбрать 1 из 4 действий.
- Выставить в состояние HI
- Выставить в состояние LO
- Выставить High-impedance состояние
- Ничего не делать
Событие, вызывающее действие TZ модуля может быть вызвано как программно, так и аппаратно. Кроме этого, предусмотрен вызов прерывания.
Теперь перейдем от слов к практике
Для начала необходимо настроить GPIO на альтернативную функцию epwm
EALLOW;
// Включение pull-up
GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0x000;
GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0x000;
// Настройка GPIO как EPWM1A
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 0x001;
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 0x001;
EDIS;
Далее, когда мы уже настроили GPIO, мы можем приступать к разным настройкам. Для настройки работы ШИМа надо определиться с тем, что мы хотим получить. Начнём с частоты TBCLK. Она определяется по формуле:
Тут необходимо обратить внимание на то, что CLKDIV по умолчанию равен 1, с HSPCLKDIV всё иначе, по умолчанию он равен 2. Это надо держать в уме, поскольку бывают случаи, когда люди об этом забывают. При загрузке программы в RAM часто HSPCLKDIV = 1, соответственно, эту проблему замечают не сразу.
С частотой тактирования TBCLK мы определились. Но надо бы выбрать, то, как у нас будет работать счётчик. По спаду, по нарастающей, а может и так и эдак, для этого надо настроить соответствующий регистр, например:
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;
В дальнейшем, чтобы никто не пугался макросам, определимся откуда они вообще появляются. Эти дефайны определены в файле с названием DSP2833x_EPwm_defines.h.
После чего необходимо определиться с тем, как будут реагировать наши GPIO на достижение определённых значений TBCTR. Вариантов действий более чем достаточно. Они приведены в таблице ниже:
Затем необходимо определиться, какого поведения мы хотим от портов А и В, а именно, мы хотим, чтобы они были связаны друг с другом или же могли работать независимо. Если мы хотим, чтобы порт А был ведущим, то мы просто записываем для него действия, например (случай для отсчёта по нарастающей):
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
если же хотим независимости и для второго порта, то добавляем:
EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.ZRO = AQ_SET;
EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;
По подробнее о настройках необходимо обратиться к изображению выше за одним лишь дополнением, регистров AQCTLA чуть больше, чем изображено на таблице, сильно картину это не меняет, а лишь вносится конкретика касательно того, в каком случае достиг нужного значения счётчик, например, при отсчете в верх или отсчёте в низ. Более кратко с битами можно ознакомиться в вырезке из системного .h файла
struct AQCTL_BITS { // bits description
Uint16 ZRO:2; // 1:0 Action Counter = Zero
Uint16 PRD:2; // 3:2 Action Counter = Period
Uint16 CAU:2; // 5:4 Action Counter = Compare A up
Uint16 CAD:2; // 7:6 Action Counter = Compare A down
Uint16 CBU:2; // 9:8 Action Counter = Compare B up
Uint16 CBD:2; // 11:10 Action Counter = Compare B down
Uint16 rsvd:4; // 15:12 reserved
};
Если у нас 2 порта ePWM работают зависимо и мы хотим выставить мёртвое время, то необходимо выставить регистр в нужное состояние, например:
EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
Теперь, когда с описанием периферии определились, можно перейти к конкретным примерам.
Настройка ePWM в режиме отсчёта по нарастающей
Тут пример без мёртвого времени и порт A, и порт В работают зависимо. Когда A активен, B не активен.
EPwm1Regs.TBPRD = 150000 / 5; // т.к частота 150Мгц / 5000 Гц
// Выставляем скважность 50%
EPwm1Regs.CMPA.half.CMPA = EPwm1Regs.TBPRD / 2;
EPwm1Regs.TBPHS.half.TBPHS = 0;
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;
EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW;
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0;
EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
EPwm1Regs.AQCTLA.bit.PRD = AQ_CLEAR;
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
EPwm1Regs.AQCTLB.bit.PRD = AQ_SET;
EPwm1Regs.AQCTLB.bit.CAU = AQ_CLEAR;
На осцилограмме можно увидеть полученный результат:
Теперь можно попробовать добавить мёртвое время, для этого добавляем:
EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm1Regs.DBCTL.all = BP_ENABLE + POLSEL_ACTIVE_HI_CMP; // Настройка db
EPwm1Regs.DBFED = 300; // Мертвое время = 150Мгц * 2мкс = 300
EPwm1Regs.DBRED = 300;
Отсчёт мёртвого времени производится аналогично частоте, по формуле:
А теперь мы получили мёртвое время таким, каким мы и хотели
А что делать, если нам надо развязать порт А и порт В? Такое тоже имеет место быть. Тут всё просто. Возвращаемся к первому примеру и удаляем последние 4 строчки, и записываем каждому скважность в следующие регистры.
EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.ZRO = AQ_SET;
EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm1Regs.CMPA.half.CMPA = EPwm1Regs.TBPRD / 2; // Выставляем скважность 50% порта А
EPwm1Regs.CMPB = EPwm1Regs.TBPRD / 3; // Выставляем скважность 33% порта В
Теперь мы имеем вот такую вот картину. Можно задавать скважность каждому каналу по отдельности.
Для режима по спаду всё примерно аналогично. Есть отличие с отсчётом в режиме up-down. Тут частота шима рассчитывается уже по формуле:
Примерно так же и для dead Time.
Наверное, единственно важное, что не было рассмотрено, так это настройка TZ, ну что ж, теперь остановимся на этом модуле чуть чуть по подробнее.
Для программного вызова события аварии достаточно настроить следующие регистры:
EPwm1Regs.TZCTL.bit.TZA = TZ_FORCE_LO;
EPwm1Regs.TZCTL.bit.TZB = TZ_FORCE_LO;
Вызов и обнуление аварии ШИМа можно осуществить с помощью следующих команд:
//Вызов аварии
EALLOW;
EPwm1Regs.TZFRC.bit.OST = 0x001;
EDIS;
//Обнуление сигнала аварии
EALLOW;
EPwm1Regs.TZCLR.bit.OST = 0x0001;
EDIS;
Если же мы хотим аппаратно вызвать сигнал TZ, то тут всё ещё проще, через регистр TZSEL выставляем нужный нам TZ, но вдобавок к этому необходимо настроить GPIO на TZ.
Заключение
Если кому-то покажется данная статья интересной, то могу в более менее ускоренном порядке написать ещё пару статей. В планах имеется рассмотреть can модуль, хотелось бы dma, а еще может быть напишу небольшую статью по IQMath от ti с их библиотеками.