Программирование ARM nRF52x: периферийное устройство PWM Tue, January 25 2022  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

nRF52x: периферийное устройство PWM Печать
Добавил(а) microsin   

Аппаратный модуль PWM позволяет генерировать сигналы широтно-импульсной модуляции (ШИМ, pulse width modulation, PWM) на ножках портов GPIO. Реализован счетчик с возможностью счета вверх и вниз, с 4 каналами PWM, которые управляют назначенными выводами портов GPIO.

Три блока модуля PWM могут предоставить до 12 каналов PWM с индивидуальным управлением частотой в группах, в которых находятся до 4 каналов. Кроме того, встроенный декодер и функция EasyDMA делают возможным манипулирование скважностью PWM без вмешательства CPU. Произвольные последовательности скважностей считываются из Data RAM, и могут быть выстроены в цепочку, чтобы организовать двойную буферизацию (ping-pong buffering) или повторение сложных циклов из буферов.

Основные возможность модуля PWM:

• Фиксированная базовая частота PWM с программируемым делителем частоты тактов.
• До 4 каналов PWM с индивидуально настроенной полярностью и значениями скважности импульсов.
• Формирование на каналах PWM импульсов, выровненных по началу периода (edge aligned), либо с выровненных симметрично (center aligned).
• Несколько массивов скважностей (последовательности из буферов) определенные в Data RAM.
• Автономное, без дрожания фронтов импульсов, обновление значений скважности непосредственно из памяти с помощью EasyDMA.
• Изменение полярности, скважности и базовой частоты может быть реализовано в каждом периоде PWM.
• Последовательности из Data RAM могут быть повторены или объединены в циклы.

nRF52 PWM block diagram fig01

Рис. 1. Внутренняя структура аппаратного модуля PWM.

[Счетчик]

Счетчик PWM (wave counter) отвечает за генерацию импульсов со скважностью, которая зависит от значений сравнения (COMP0 .. COMP3) и от частоты PWM, которая зависит от предела счета COUNTERTOP.

Имеется один общий 15-разрядный счетчик с 4 каналами сравнения. Таким образом, все эти 4 канала работают с общим периодом (на общей частоте PWM), однако у каждого из них можно настроить индивидуальную скважность и полярность импульсов PWM. Полярность устанавливается значением, прочитанным из RAM (см. рис. 4), в то время как регистр MODE управляет режимом счета - либо счет вверх, либо счет вверх и вниз. Верхнее значение счета задает регистр COUNTERTOP. Значение этого регистра вместе с выбранным значением для PRESCALER частоты тактов PWM_CLK определяют период PWM. Значение COUNTERTOP меньшее, чем значение для сравнения, приведет к отсутствию генерации импульсов PWM. Соответственно на OUT[n] будет удерживаться постоянная лог. 1, если полярность установлена на FallingEdge. Все регистры сравнения внутренние, и могут конфигурироваться только через декодер, представленный далее.

Регистр COUNTERTOP можно безопасно записать в любой момент времени. Его значение анализируется и учитывается сразу после задачи запуска (START task). Если в DECODER.LOAD записано любое значение, отличающееся от WaveForm, то COUNTERTOP также будет анализироваться после задачи STARTSEQ[n], и тогда будет загружено новое значение из RAM во время проигрывания последовательности скважностей. Если DECODER.LOAD=WaveForm, то значение регистра COUNTERTOP игнорируется, и вместо этого принимаются значения из RAM (см. далее "Декодер + EasyDMA").

Рис. 2 показывает работу счетчика в режиме счета вверх (MODE=PWM_MODE_Up) с тремя каналами PWM, работающими на одной частоте, но с разными скважностями. Счетчик автоматически сбрасывается в 0, когда достигает значения COUNTERTOP, и уровень на OUT[n] будет инвертироваться. OUT[n] удерживается в лог. 0, если значение сравнения 0, и удерживается в лог. 1 соответственно, если установлено значение COUNTERTOP, когда полярность установлена в FallingEdge. Режим со счетом вверх генерирует импульсы ШИМ с выравниванием по началу периода (PWM edge-aligned). Ниже приведен пример кода:

uint16_t pwm_seq[4] = {PWM_CH0_DUTY, PWM_CH1_DUTY, PWM_CH2_DUTY, PWM_CH3_DUTY};
 
NRF_PWM0->PSEL.OUT[0] = (first_pin << PWM_PSEL_OUT_PIN_Pos) | 
                        (PWM_PSEL_OUT_CONNECT_Connected <<
                                                 PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[1] = (second_pin << PWM_PSEL_OUT_PIN_Pos) | 
                        (PWM_PSEL_OUT_CONNECT_Connected <<
                                                 PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->ENABLE      = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
NRF_PWM0->MODE        = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
NRF_PWM0->PRESCALER   = (PWM_PRESCALER_PRESCALER_DIV_1 <<
                                                 PWM_PRESCALER_PRESCALER_Pos);
NRF_PWM0->COUNTERTOP  = (16000 << PWM_COUNTERTOP_COUNTERTOP_Pos); //1 msec
NRF_PWM0->LOOP        = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
NRF_PWM0->DECODER   = (PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos) | 
                      (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
NRF_PWM0->SEQ[0].PTR  = ((uint32_t)(pwm_seq) << PWM_SEQ_PTR_PTR_Pos);
NRF_PWM0->SEQ[0].CNT  = ((sizeof(pwm_seq) / sizeof(uint16_t)) <<
                                                 PWM_SEQ_CNT_CNT_Pos);
NRF_PWM0->SEQ[0].REFRESH  = 0;
NRF_PWM0->SEQ[0].ENDDELAY = 0;
NRF_PWM0->TASKS_SEQSTART[0] = 1;

nRF52 PWM up counter example fig02

Рис. 2. Пример PWM со счетом вверх, полярность FallingEdge.

Для вычисления периода PWM TPWM(Up) и разрешающей способности по длительности импульсов Tsteps можно использовать формулы:

TPWM(Up) = TPWM_CLK * COUNTERTOP
Tsteps = TPWM_CLK

На рис. 3 показан режим работы, когда счетчик считает вверх и вниз (от предыдущего примера отличается только настройкой MODE=PWM_MODE_UpAndDown), с двумя каналами PWM, работающими на одной частоте, но с разными скважностями и полярностью импульсов. Счетчик начинает декрементироваться до 0, когда при счете вверх достигнуто значение COUNTERTOP, и инвертируется OUT[n], когда значение сравнения достигнуто второй раз. В результате получаются импульсы PWM, длительность которых меняется с выравниванием посередине (PWM center-aligned).

uint16_t pwm_seq[4] = {PWM_CH0_DUTY, PWM_CH1_DUTY, PWM_CH2_DUTY, PWM_CH3_DUTY};
 
NRF_PWM0->PSEL.OUT[0] = (first_pin << PWM_PSEL_OUT_PIN_Pos) | 
                        (PWM_PSEL_OUT_CONNECT_Connected <<
                                                 PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[1] = (second_pin << PWM_PSEL_OUT_PIN_Pos) | 
                        (PWM_PSEL_OUT_CONNECT_Connected <<
                                                 PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->ENABLE      = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
NRF_PWM0->MODE        = (PWM_MODE_UPDOWN_UpAndDown << PWM_MODE_UPDOWN_Pos);
NRF_PWM0->PRESCALER   = (PWM_PRESCALER_PRESCALER_DIV_1 <<
                                                 PWM_PRESCALER_PRESCALER_Pos);
NRF_PWM0->COUNTERTOP  = (16000 << PWM_COUNTERTOP_COUNTERTOP_Pos); //1 msec
NRF_PWM0->LOOP        = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
NRF_PWM0->DECODER   = (PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos) | 
                      (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
NRF_PWM0->SEQ[0].PTR  = ((uint32_t)(pwm_seq) << PWM_SEQ_PTR_PTR_Pos);
NRF_PWM0->SEQ[0].CNT  = ((sizeof(pwm_seq) / sizeof(uint16_t)) <<
                                                 PWM_SEQ_CNT_CNT_Pos);
NRF_PWM0->SEQ[0].REFRESH  = 0;
NRF_PWM0->SEQ[0].ENDDELAY = 0;
NRF_PWM0->TASKS_SEQSTART[0] = 1;

nRF52 PWM up and down counter example fig03

Рис. 3. Пример PWM со счетом вверх/вниз.

В режиме счета вверх/вниз для вычисления периода PWM TPWM(Up And Down) и разрешающей способности по длительности импульсов Tsteps можно использовать формулы:

TPWM(Up And Down) = TPWM_CLK * 2 * COUNTERTOP
Tsteps = TPWM_CLK * 2

[Декодер + EasyDMA]

Декодер использует EasyDMA для выборки параметров PWM, сохраненных в Data RAM, и обновления внутренних регистров сравнения, основываясь на режиме работы.

Упомянутые параметры PWM организованы в последовательность, содержащую как минимум половину слова (16 бит). Самый старший значащий бит здесь (MSB, bit[15]) обозначает полярность OUT[n], в то время как остальные bit[14:0] представляют собой 15-разрядное значение для сравнения:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                 B A A A A A A A A A A A A A A A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW COMPARE Настройка скважности - значение, загружаемое во внутренний регистр сравнения.
B RW POLARITY Устанавливает полярность генерации импульсов PWM на выходе.
0: RisingEdge, первый перепад в периоде импульсов - нарастание уровня (0 -> 1).
1: FallingEdge, первый перепад в периоде импульсов - спад уровня (1 -> 0).

Регистр DECODER управляет, каким образом содержимое RAM интерпретируется и загружается во внутренние регистры сравнения. Поле LOAD может использоваться для управления загрузкой - загружаются ли значения RAM во все каналы сравнения, или же альтернативно будет обновляться группа, либо все каналы будут обновляться индивидуальными значениями. Рис. 4 иллюстрирует, как организованы параметры, сохраненные в RAM, и как они перенаправляются в различные каналы сравнения в разных режимах.

nRF52 PWM Decoder memory access modes fig04

Рис. 4. Режимы доступа к памяти со стороны декодера.

Доступен специальный режим работы, когда DECODER.LOAD установлен в WaveForm. В этом режиме можно разрешить до 3 каналов PWM - OUT[0], OUT[1] и OUT[2]. В RAM загружаются за один раз 4 значения: первая, вторая и третья ячейки RAM используются для загрузки значений регистров сравнения, и четвертая используется для загрузки регистра COUNTERTOP. Это дает возможность получить до 3 каналов PWM, при котором может меняться базовая частота PWM. Этот режим работы полезен для генерации сигналов произвольной формы в таких приложениях, как светодиодное освещение.

Регистр SEQ[n].REFRESH=N (один на последовательность n=0 или 1) будет инструктировать о новой величине ширины импульса, сохраненной в RAM, на каждом (N+1)-ом периоде PWM. Установка этого регистра в 0 приведет к новой смене рабочего цикла для каждого периода PWM до тех пор, пока наблюдается минимальный период PWM.

Обратите внимание, что регистры SEQ[n].REFRESH и SEQ[n].ENDDELAY игнорируются, когда DECODER.MODE=NextStep. Следующее значение загружается при получении каждой новой задачи следующего шага (NEXTSTEP task).

SEQ[n].PTR это указатель, используемый для выборки значений COMPARE из RAM. Если SEQ[n].PTR не указывает на область Data RAM, то транзакция EasyDMA может вызвать HardFault или повреждение содержимого RAM. Для дополнительной информации по разным регионам памяти см. [2].

После установки указателя SEQ[n].PTR на желаемую область RAM, в регистре SEQ[n].CNT должно быть установлено количество половинок слов (порций данных по 16-бит), находящихся в последовательности. Важно отметить, что режимы Grouped и Single требуют одну половинку слова на группу, либо либо одну половинку слова на канал соответственно, что увеличивает расход RAM. Если в этот момент генерация PWM еще не запущена, отправка SEQSTART[n] task загрузит первое значение из RAM, затем запустит генерацию PWM. Будет сгенерировано событие SEQSTARTED[n], как только EasyDMA прочитал первый параметр PWM из RAM, и счетчик начал его выполнять. Когда LOOP.CNT=0, последовательность n=0 или 1 проигрывается один раз. После того, как последнее значение из последовательности было загружено и запущено на выполнение, генерируется событие SEQEND[n]. Генерация The PWM продолжится с последним загруженным значением. См. рис. 5 для примера такого простого проигрывания.

Чтобы полностью остановить генерацию PWM и принудительно перевести соответствующие ножки в определенное состояние, можно в любой момент запустить STOP task. Сгенерируется событие STOPPED, года генерация PWM остановилась в конце текущего формируемого периода PWM, и ножки перейдут в состояние ожидания, как определено в GPIO->OUT. Затем генерация PWM может быть запущена заново выдачей SEQSTART[n] task. SEQSTART[n] возобновит генерацию PWM после того, как загрузится первое значение из буфера RAM, который определен регистром SEQ[n].PTR.

В таблице ниже показано, когда аппаратно анализируются определенные регистры. Следует проявлять осторожность, когда обновляются эти регистры, чтобы избежать ситуации, когда значения применяются раньше, чем это ожидалось.

Таблица 1. Когда можно безопасно обновлять регистры PWM.

Регистр Учитывается в аппаратуре Рекомендуемый (безопасный) момент обновления
SEQ[n].PTR Когда посылается SEQSTART[n] task. После получения события SEQSTARTED[n].
SEQ[n].CNT
SEQ[0].ENDDELAY Когда посылается SEQSTART[0] task.

Каждый раз, когда новое значение из последовательности 0 было загружено из RAM, и было принято Wave Counter (индицируется событием PWMPERIODEND).
Перед запуском последовательности 0 путем активации SEQSTART[0] task.

Когда больше нет значений из последовательности 0, которые могут быть загружены из RAM (показывается событием SEQEND[0]).

В любой момент времени, когда выполняется последовательность 1 (которая стартует, когда сгенерировано событие SEQSTARTED[1]).
SEQ[1].ENDDELAY Когда посылается SEQSTART[1] task.

Каждый раз, когда новое значение из последовательности 1 было загружено из RAM, и было принято Wave Counter (индицируется событием PWMPERIODEND).
Перед запуском последовательности 1 путем активации SEQSTART[1] task.

Когда больше нет значений из последовательности 1, которые могут быть загружены из RAM (показывается событием SEQEND[1]).

В любой момент времени, когда выполняется последовательность 0 (которая стартует, когда сгенерировано событие SEQSTARTED[0]).
SEQ[0].REFRESH Когда посылается SEQSTART[0] task. Каждый раз, когда новое значение последовательности 0 было загружено из RAM, и было принято Wave Counter (индицируется событием PWMPERIODEND).

Перед запуском последовательности 0 путем активации SEQSTART[0] task.

В любой момент времени, когда выполняется последовательность 1 (которая стартует при генерации события SEQSTARTED[1]).
SEQ[1].REFRESH Когда посылается SEQSTART[1] task. Каждый раз, когда новое значение последовательности 1 было загружено из RAM, и было принято Wave Counter (индицируется событием PWMPERIODEND).

Перед запуском последовательности 1 путем активации SEQSTART[1] task.

В любой момент времени, когда выполняется последовательность 0 (которая стартует при генерации события SEQSTARTED[0]).
COUNTERTOP При DECODER.LOAD=WaveForm этот регистр игнорируется.

Во всех других режимах LOAD: по окончании текущего периода PWM (индицируется событием PWMPERIODEND).
Перед запуском генерации PWM путем активации SEQSTART[n] task.

После того, как была активирована STOP task, и когда было принято событие STOPPED.
MODE Немедленно
DECODER
PRESCALER
LOOP
PSEL.OUT[n]

Важное замечание: SEQ[n].REFRESH и SEQ[n].ENDDELAY игнорируются в конце сложной последовательности, о чем показывает событие LOOPSDONE. Причина этого в том, что последнее загруженное из RAM значение остается действующим, пока программа не предпримет дальнейшие действия (пока не перезапустит последовательность, либо не остановит генерацию PWM).

На рис. 5 показана работа следующего кода, используемого для конфигурации и параметров времени в последовательности, где используется только последовательность 0, и она запускается только один раз с новым значением скважности PWM для каждого периода.

NRF_PWM0->PSEL.OUT[0] = (first_pin << PWM_PSEL_OUT_PIN_Pos) | 
                        (PWM_PSEL_OUT_CONNECT_Connected <<
                                                 PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->ENABLE      = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
NRF_PWM0->MODE        = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
NRF_PWM0->PRESCALER   = (PWM_PRESCALER_PRESCALER_DIV_1 <<
                                                 PWM_PRESCALER_PRESCALER_Pos);
NRF_PWM0->COUNTERTOP  = (16000 << PWM_COUNTERTOP_COUNTERTOP_Pos); //1 msec
NRF_PWM0->LOOP        = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
NRF_PWM0->DECODER   = (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos) | 
                      (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
NRF_PWM0->SEQ[0].PTR  = ((uint32_t)(seq0_ram) << PWM_SEQ_PTR_PTR_Pos);
NRF_PWM0->SEQ[0].CNT  = ((sizeof(seq0_ram) / sizeof(uint16_t)) <<
                                                 PWM_SEQ_CNT_CNT_Pos);
NRF_PWM0->SEQ[0].REFRESH  = 0;
NRF_PWM0->SEQ[0].ENDDELAY = 0;
NRF_PWM0->TASKS_SEQSTART[0] = 1;

nRF52 PWM Simple sequence example fig05

Рис. 5. Пример запуска простой последовательности.

Более сложный пример показан на рис. 6, где LOOP.CNT > 0 . В этом случае осуществляется автоматическое проигрывание, состоящее из последовательности SEQ[0], задержки delay 0, последовательности SEQ[1], задержки delay 1, и затем снова по кругу SEQ[0], и так далее. Пользователь может выбрать запуск сложной последовательности с SEQ[0] или SEQ[1] путем активации либо SEQSTART[0], либо SEQSTART[1] task.

Сложное проигрывание всегда завершается с delay 1.

Две последовательности 0 и 1 определены адресами таблиц значений в Data RAM (указываются SEQ[n].PTR) и соответствующими размерами буфера (SEQ[n].CNT). Скорость, с которой новое значение загружается, определяется индивидуально для каждой последовательности через SEQ[n].REFRESH. Переход от проигрывания последовательности 1 к проигрыванию последовательности 0 неявный, регистр LOOP.CNT позволяет задать количество таких переходов (повторных запусков). Другими словами, LOOP.CNT задает количество полностью автоматических повторов проигрывания сложной последовательности.

В примере ниже последовательность 0 определена с SEQ[0].REFRESH, установленным в 1 - это значит, что новый цикл скважности PWM инициируется на каждом втором периоде PWM. Эта сложная последовательность запускается активацией SEQSTART[0], поэтому сначала проигрывается SEQ[0]. Поскольку SEQ[0].ENDDELAY=1, здесь будет задержка на 1 период PWM между последним периодом последовательности 0 и первым периодом последовательности 1. Поскольку SEQ[1].ENDDELAY=0, здесь нет задержки delay 1, так что SEQ[0] запустится сразу по окончанию SEQ[1]. Однако, поскольку LOOP.CNT = 1, проигрывание остановится после однократного проигрывания SEQ[1], и сгенерируются оба события SEQEND[1] и LOOPSDONE (в этом случае порядок возникновения этих событий не гарантируется).

NRF_PWM0->PSEL.OUT[0] = (first_pin << PWM_PSEL_OUT_PIN_Pos) | 
                        (PWM_PSEL_OUT_CONNECT_Connected <<
                                                 PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->ENABLE      = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
NRF_PWM0->MODE        = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
NRF_PWM0->PRESCALER   = (PWM_PRESCALER_PRESCALER_DIV_1 <<
                                                 PWM_PRESCALER_PRESCALER_Pos);
NRF_PWM0->COUNTERTOP  = (16000 << PWM_COUNTERTOP_COUNTERTOP_Pos); //1 msec
NRF_PWM0->LOOP        = (1 << PWM_LOOP_CNT_Pos);
NRF_PWM0->DECODER   = (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos) | 
                      (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
NRF_PWM0->SEQ[0].PTR  = ((uint32_t)(seq0_ram) << PWM_SEQ_PTR_PTR_Pos);
NRF_PWM0->SEQ[0].CNT  = ((sizeof(seq0_ram) / sizeof(uint16_t)) <<
                                                 PWM_SEQ_CNT_CNT_Pos);
NRF_PWM0->SEQ[0].REFRESH  = 1;
NRF_PWM0->SEQ[0].ENDDELAY = 1;
NRF_PWM0->SEQ[1].PTR  = ((uint32_t)(seq1_ram) << PWM_SEQ_PTR_PTR_Pos);
NRF_PWM0->SEQ[1].CNT  = ((sizeof(seq1_ram) / sizeof(uint16_t)) <<
                                                 PWM_SEQ_CNT_CNT_Pos);
NRF_PWM0->SEQ[1].REFRESH  = 0;
NRF_PWM0->SEQ[1].ENDDELAY = 0;
NRF_PWM0->TASKS_SEQSTART[0] = 1;

nRF52 PWM using two sequences example fig06

Рис. 6. Пример использования двух последовательностей, проигрываемых друг за другом.

Декодер может быть также сконфигурирован для асинхронной загрузки новой скважности PWM. Если установлено DECODER.MODE = NextStep, то активация NEXTSTEP task приведет к обновлению внутренних регистров сравнения на следующем периоде PWM.

Рисунки ниже дают обзор каждой части произвольной последовательности в различных режимах (LOOP.CNT=0 и LOOP.CNT>0). В частности представлено следующее:

• Начальный и конечный цикл скважности на выходе (выходах) PWM.
• Соединение в цепочку SEQ[0] и SEQ[1], если LOOP.CNT>0.
• Влияние регистров на последовательность.
• События, генерируемые во время последовательности.
• Активность DMA: загрузка следующего значения и применение его на выходе (выходах).

Обратите внимание, что пример однократного проигрывания применим также и к SEQ[1], здесь SEQ[0] представлена только для упрощения.

nRF52 PWM Single shot LOOP CNT 0 fig07

Рис. 7. Однократное проигрывание (LOOP.CNT=0).

nRF52 PWM Complex sequence example fig08

Рис. 8. Сложная последовательность (LOOP.CNT>0), начинающаяся с SEQ[0].

nRF52 PWM Complex sequence example fig09

Рис. 9. Сложная последовательность (LOOP.CNT>0), начинающаяся с SEQ[1].

Имейте в виду, что если используется простая или сложная последовательность, у неё должна быть длина SEQ[n].CNT>0 .

[Ограничения]

Предыдущее значение сравнения будет повторено, если выбран период PWM короче, чем время, требуемое для EasyDMA, чтобы сделать выборку из RAM и обновить внутренние регистры сравнения.

Это реализовано для работы без паразитных импульсов, даже если выбраны очень короткие периоды PWM.

[Конфигурация ножек портов]

Сигналы OUT[n] (n=0..3), связанные с каждым каналом модуля PWM, отображаются на физические выводы портов в соответствии с конфигурацией, указанной соответствующими регистрами PSEL.OUT[n]. Если SEL.OUT[n].CONNECT установлено в Disconnected, соответствующий сигнал модуля PWM не подключен к любому физическому выводу.

Регистры PSEL.OUT[n] и их конфигурации используются только пока модуль PWM разрешен, и активна генерация PWM (счетчик запущен), и это сохраняется пока устройство находится в режиме System ON, см. описание модуля POWER [3] для дополнительной информации о режимах энергопотребления.

Для гарантии корректного поведения модуля PWM выводы, которые PWM использует, должны быть сконфигурированы в периферийном устройстве GPIO [4], как это описано в таблице 2, и это должно быть сделано перед разрешением модуля PWM. Исходное состояние выводов, когда они находятся в состоянии ожидания (pins idle state) определяется регистрами OUT в модуле GPIO. Это гарантирует, что выводы, используемые модулем PWM, переключаются корректно, если генерация PWM остановлена активацией STOP task, если сам модуль PWM временно запрещен, или устройство временно вошло в System OFF. Эта конфигурация должна сохраняться в GPIO для выбранных ножек IO при условии, что модуль PWM подключенным к внешней схеме PWM (например силовые ключи).

В любой момент времени может быть назначено только одно периферийное устройство для переключения определенной ножки GPIO. Если не выполнить это условие, то возможно непредсказуемое поведение.

Таблица 2. Рекомендуемая конфигурация GPIO перед запуском генерации PWM.

Сигнал PWM Ножка порта PWM Направление Значение на выходе Комментарий
OUT[n] Как указано в PSEL.OUT[n], n=0..3 Выход 0 Состояние отсутствия активности определяется значением GPIO->OUT.

[Регистры PWM]

Таблица 3. Экземпляры PWM, базовые адреса начала адресного пространства регистров.

Баз. адрес Периф. устройство Экз. Описание Конфигурация
0x4001C000 PWM PWM0 Блок широтно-импульсной модуляции 0  
0x40021000 PWM1 Блок широтно-импульсной модуляции 1  
0x40022000 PWM2 Блок широтно-импульсной модуляции 2  

Таблица 4. Обзор регистров PWM.

Регистр Смещ. Описание
TASKS_STOP 0x004 Останавливает генерацию импульса PWM на всех каналах по окончании текущего периода PWM, и останавливает последовательность проигрывания.
TASKS_SEQSTART[0] 0x008 Загружает первое значение PWM на всех разрешенных каналах из последовательности 0, и запускает проигрывание последовательности со скоростью, которая определяется в SEQ[0].REFRESH и/или DECODER.MODE. В результате запускается генерация PWM, если она не была уже запущена.
TASKS_SEQSTART[1] 0x00C Загружает первое значение PWM на всех разрешенных каналах из последовательности 1, и запускает проигрывание последовательности со скоростью, которая определяется в SEQ[1].REFRESH и/или DECODER.MODE. В результате запускается генерация PWM, если она не была уже запущена.
TASKS_NEXTSTEP 0x010 Выполняет 1 шаг вперед на одно значение в текущей последовательности на всех разрешенных каналах, если DECODER.MODE=NextStep. Не запускает генерацию PWM, если она не была запущена.
EVENTS_STOPPED 0x104 Ответ на STOP task, генерируется, когда перестали генерироваться импульсы PWM.
EVENTS_SEQSTARTED[0] 0x108 Начал генерироваться первый период в последовательности 0.
EVENTS_SEQSTARTED[1] 0x10C Начал генерироваться первый период в последовательности 1.
EVENTS_SEQEND[0] 0x110 Генерируется при каждом окончании последовательности 0, когда её последнее значение было извлечено из RAM и было принято Wave Counter.
EVENTS_SEQEND[1] 0x114 Генерируется при каждом окончании последовательности 1, когда её последнее значение было извлечено из RAM и было принято Wave Counter.
EVENTS_PWMPERIODEND 0x118 Генерируется по окончанию формирования каждого периода PWM.
EVENTS_LOOPSDONE 0x11C Завершено LOOP.CNT повторений соединенных в цепочку последовательностей.
SHORTS 0x200 Регистр шортката.
INTEN 0x300 Разрешение и запрет прерываний.
INTENSET 0x304 Разрешение прерываний.
INTENCLR 0x308 Запрет прерываний.
ENABLE 0x500 Регистр разрешения модуля PWM.
MODE 0x504 Выбор рабочего режима Wave Counter.
COUNTERTOP 0x508 Значение, до которого инкрементируется счетчик.
PRESCALER 0x50C Конфигурация для PWM_CLK.
DECODER 0x510 Конфигурация декодера.
LOOP 0x514 Количество повторов циклов проигрывания.
SEQ[0].PTR 0x520 Начало последовательности 0 в Data RAM.
SEQ[0].CNT 0x524 Количество значений (скважностей) в последовательности 0.
SEQ[0].REFRESH 0x528 Количество дополнительных периодов PWM между выборками, загружаемыми в регистр сравнения при проигрывании последовательности 0.
SEQ[0].ENDDELAY 0x52C Время, добавляемое в конце последовательности 0.
SEQ[1].PTR 0x540 Начало последовательности 1 в Data RAM.
SEQ[1].CNT 0x544 Количество значений (скважностей) в последовательности 1.
SEQ[1].REFRESH 0x548 Количество дополнительных периодов PWM между выборками, загружаемыми в регистр сравнения при проигрывании последовательности 1.
SEQ[1].ENDDELAY 0x54C Время, добавляемое в конце последовательности 1.
PSEL.OUT[0] 0x560 Выбор выходной ножки для канала 0 PWM.
PSEL.OUT[1] 0x564 Выбор выходной ножки для канала 1 PWM.
PSEL.OUT[2] 0x568 Выбор выходной ножки для канала 2 PWM.
PSEL.OUT[3] 0x56C Выбор выходной ножки для канала 3 PWM.

Смещение адреса: 0x200. Запись 1 разрешает шорткат, запись 0 запрещает. Чтение показывает текущее состояние.

Биты регистра SHORTS:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                                       E D C B A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW SEQEND0_STOP Шорткат между событием EVENTS_SEQEND[0] и задачей TASKS_STOP.
B RW SEQEND1_STOP Шорткат между событием EVENTS_SEQEND[1] и задачей TASKS_STOP.
C RW LOOPSDONE_SEQSTART0 Шорткат между событием LOOPSDONE и задачей TASKS_SEQSTART[0].
D RW LOOPSDONE_SEQSTART1 Шорткат между событием LOOPSDONE и задачей TASKS_SEQSTART[1].
E RW LOOPSDONE_STOP Шорткат между событием LOOPSDONE и задачей TASKS_STOP.

Смещение адреса: 0x300. Запись 1 разрешает соответствующее прерывание, запись 0 запрещает. Чтение показывает состояние прерывания: 0 прерывание запрещено, 1 разрешено.

Биты регистра INTEN:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                                 H G F E D C B  
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
B RW STOPPED Разрешение прерывания для события STOPPED. См. EVENTS_STOPPED.
C RW SEQSTARTED0 Разрешение прерывания для события SEQSTARTED[0]. См. EVENTS_SEQSTARTED[0].
D RW SEQSTARTED1 Разрешение прерывания для события SEQSTARTED[1]. См. EVENTS_SEQSTARTED[1].
E RW SEQEND0 Разрешение прерывания для события SEQEND[0]. См. EVENTS_SEQEND[0].
F RW SEQEND1 Разрешение прерывания для события SEQEND[1]. См. EVENTS_SEQEND[1].
G RW PWMPERIODEND Разрешение прерывания для события PWMPERIODEND. См. EVENTS_PWMPERIODEND.
H RW LOOPSDONE Разрешение прерывания для события LOOPSDONE. См. EVENTS_LOOPSDONE.

Смещение адреса: 0x304. Запись 1 разрешает соответствующее прерывание. Запись 0 не оказывает никакого влияния. Чтение показывает состояние прерывания: 0 прерывание запрещено, 1 разрешено.

Биты регистра INTENSET:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                                 H G F E D C B  
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
B RW STOPPED Разрешение прерывания для события STOPPED. См. EVENTS_STOPPED.
C RW SEQSTARTED0 Разрешение прерывания для события SEQSTARTED[0]. См. EVENTS_SEQSTARTED[0].
D RW SEQSTARTED1 Разрешение прерывания для события SEQSTARTED[1]. См. EVENTS_SEQSTARTED[1].
E RW SEQEND0 Разрешение прерывания для события SEQEND[0]. См. EVENTS_SEQEND[0].
F RW SEQEND1 Разрешение прерывания для события SEQEND[1]. См. EVENTS_SEQEND[1].
G RW PWMPERIODEND Разрешение прерывания для события PWMPERIODEND. См. EVENTS_PWMPERIODEND.
H RW LOOPSDONE Разрешение прерывания для события LOOPSDONE. См. EVENTS_LOOPSDONE.

Смещение адреса: 0x308. Запись 1 запрещает соответствующее прерывание. Запись 0 не оказывает никакого влияния. Чтение показывает состояние прерывания: 0 прерывание запрещено, 1 разрешено.

Биты регистра INTENCLR:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                                 H G F E D C B  
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
B RW STOPPED Запрет прерывания для события STOPPED. См. EVENTS_STOPPED.
C RW SEQSTARTED0 Запрет прерывания для события SEQSTARTED[0]. См. EVENTS_SEQSTARTED[0].
D RW SEQSTARTED1 Запрет прерывания для события SEQSTARTED[1]. См. EVENTS_SEQSTARTED[1].
E RW SEQEND0 Запрет прерывания для события SEQEND[0]. См. EVENTS_SEQEND[0].
F RW SEQEND1 Запрет прерывания для события SEQEND[1]. См. EVENTS_SEQEND[1].
G RW PWMPERIODEND Запрет прерывания для события PWMPERIODEND. См. EVENTS_PWMPERIODEND.
H RW LOOPSDONE Запрет прерывания для события LOOPSDONE. См. EVENTS_LOOPSDONE.

Смещение адреса: 0x500. Запись 1 разрешает работу модуля PWM. Чтение показывает состояние: 0 работа аппаратного модуля PWM запрещена, 1 разрешена.

Биты регистра ENABLE:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                                               A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW ENABLE Разрешение или запрет аппаратного модуля PWM.

Смещение адреса: 0x504. Запись выбирает режим счета, чтение показывает текущий выбранный режим.

Биты регистра MODE:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                                               A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW MODE 0: Up, счет вверх. Генерация быстрых импульсов ШИМ с выравниванием по началу периода (edge-aligned PWM).
1: UpAndDown, счет вверх и вниз. Генерация импульсов ШИМ на частоте в 2 раза меньшей, но зато выровненных посередине (center-aligned PWM).

Смещение адреса: 0x508. Верхнее значение, до которого считает счетчик.

Биты регистра COUNTERTOP:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                   A A A A A A A A A A A A A A A
Reset 0x000003FF 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1

Назначение бит:

Id RW Поле Описание
A RW COUNTERTOP Значение в диапазоне 3..32767. Предельное значение, до которого доходит счет вверх. Этот регистр игнорируется, когда DECODER.MODE = WaveForm, и используются только значения из RAM.

Смещение адреса: 0x50C.

Биты регистра PRESCALER:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                                           A A A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW PRESCALER 0: DIV_1, делитель частоты не используется (PWM_CLK = 16 МГц).
1: DIV_2, деление частоты на 2 (PWM_CLK = 8 МГц).
2: DIV_4, деление частоты на 4 (PWM_CLK = 4 МГц).
3: DIV_8, деление частоты на 8 (PWM_CLK = 2 МГц).
4: DIV_16, деление частоты на 16 (PWM_CLK = 1 МГц).
5: DIV_32, деление частоты на 32 (PWM_CLK = 500 кГц).
6: DIV_64, деление частоты на 64 (PWM_CLK = 250 кГц).
7: DIV_128, деление частоты на 128 (PWM_CLK = 125 кГц).

Смещение адреса: 0x510.

Биты регистра DECODER:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                               B             A A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW LOAD Определяет, как последовательность считывается из RAM, и как передается в регистр сравнения.
0: Common, первая половинка слова (16 бит) используется для всех каналов 0..3.
1: Grouped, первая половинка слова (16 бит) используется в каналах 0 и 1, вторая в каналах 2 и 3.
2: Individual, первая половинка слова (16 бит) используется в канале 0, вторая в канале 1, третья в канале 2, четвертая в канале 3.
3: WaveForm, первая половинка слова (16 бит) используется в канале 0, вторая в канале 1, третья в канале 2, четвертая в COUNTERTOP.
B RW MODE Выбирает источник для итерации по последовательности.
0: RefreshCount, используется SWQ[n].REFRESH для определения интервала загрузки внутренних регистров сравнения.
1: NextStep, активация NEXTSTEP task используется для загрузки нового значения во внутренние регистры сравнения.

Смещение адреса: 0x514.

Биты регистра LOOP:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                 A A A A A A A A A A A A A A A A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW CNT Количество повторов последовательностей. Если 0, то повторы запрещены (произойдет остановка в конце последовательности).

Смещение адреса: 0x520 + (n * 0x20).

Биты регистра SEQ[n].PTR:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW PTR Адрес начала буфера последовательности в Data RAM.

Смещение адреса: 0x524 + (n * 0x20).

Биты регистра SEQ[n].CNT:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                                   A A A A A A A A A A A A A A A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW CNT Количество значений (скважностей) буфера последовательности в Data RAM. Если 0, то последовательность запрещена и не должна запускаться, поскольку она пустая.

Смещение адреса: 0x528 + (n * 0x20).

Биты регистра SEQ[n].REFRESH:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                 A A A A A A A A A A A A A A A A A A A A A A A A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW CNT Количество дополнительных периодов PWM между загрузками выборок в регистр сравнения (загрузка каждые REFRESH.CNT+1 периодов PWM). Если 0 (Continuous), то обновление в каждом периоде PWM.

Смещение адреса: 0x52C + (n * 0x20).

Биты регистра SEQ[n].ENDDELAY:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id                 A A A A A A A A A A A A A A A A A A A A A A A A
Reset 0x00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Назначение бит:

Id RW Поле Описание
A RW CNT Время в количестве периодов PWM, добавляемых в конце последовательности.

Смещение адреса: 0x560 + (n * 0x04).

Биты регистра PSEL.OUT[n]:

№ бита 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
Id C                                                     A A A A A
Reset 0xFFFFFFFF 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

Назначение бит:

Id RW Поле Описание
A RW PIN Номер разряда порта [0..31].
C RW CONNECT 0: Connected
1: Disconnected

[Электрические параметры PWM]

Симв. Описание min Typ. MAX Ед.
IPWM,16MHz Ток потребления работающего PWM, прескалер установлен на DIV_1 (16 МГц), без учета DMA и GPIO.   200   мкА
IPWM,8MHz Ток потребления работающего PWM, прескалер установлен на DIV_2 (8 МГц), без учета DMA и GPIO.   150  
IPWM,125kHz Ток потребления работающего PWM, прескалер установлен на DIV_128 (125 кГц), без учета DMA и GPIO.   150  

[Ссылки]

1. nRF52832 PWM Pulse width modulation site:nordicsemi.com.
2. nRF52: память.
3. nRF52: источники питания.
4. nRF5x: порты GPIO.

 

Добавить комментарий


Защитный код
Обновить

Top of Page